import { APPS_WORKSPACE_RESOURCE_ID, type PageIntent, type PageCoverReference, type Parent, type ResourceId, } from "./intents.js"; import { createChildDatabase, type ChildDatabaseFactory } from "./database.js"; import { assertPageIcon, type PageIcon } from "./page-icon.js"; import { recordIntent } from "./recorder.js"; import { assertParent, assertUserResourceId } from "./resource.js"; /** * A single text token: plain text as `[string]`, or text with annotations as * `[string, ...annotations]`. */ export type SimpleTextToken = [string] | [string, ...unknown[]]; /** * The JSON-serializable text format returned by Notion-as-Code text helpers. */ export type TextValue = SimpleTextToken[]; export type VerificationInput = | { state: "verified"; /** Set an expiration with `notion.datetime(...)`; omit it for indefinite verification. */ datetime?: TextValue; } | { state: "unverified" }; export type VerificationPropertyValue = VerificationInput & { type: "verification"; }; /** * A page property value accepted by Notion as Code. * * Use helpers from `@notionhq/apps/notion-as-code` to construct rich values * such as text, dates, checkboxes, and URLs. */ export type PropertyValue = | string | number | TextValue | string[] | VerificationPropertyValue | undefined; /** * Arguments for declaring a page. * * For a regular page, set its title through * `properties.title: notion.text("My Page Title")`. For a database page, use * the title property's schema name instead of `title`. */ export type PageArgs = { resourceId: ResourceId; /** Omit to create a private top-level page in the Apps workspace. */ parent?: Parent; /** A standard emoji or Notion icon. Workspace custom emoji and file icons are not supported. */ icon?: PageIcon; cover?: PageCoverReference; /** * Page properties, including the page title. * * @example Regular page * properties: { title: notion.text("My Page Title") } * * @example Database page * properties: { Name: notion.text("Task Name"), Status: "In Progress" } */ properties?: Record; /** App custom block key to render as a childless custom page instead of Markdown. */ customBlockKey?: string; /** * Page body in Notion Markdown vNext. * * Resource mentions use `Label`. * Place a declared App custom block with ``. * Use `definition-id` instead to reference an existing definition in this workspace. * * Markdown headings create child blocks. They do not set the page title; * set a regular page's title with `properties.title`. */ content?: string; fullWidth?: boolean; }; export type ChildPageArgs = Omit; export type PageHandle = { readonly resourceType: "page"; readonly resourceId: string; addDatabase: ChildDatabaseFactory; addPage: (args: ChildPageArgs) => PageHandle; }; /** Declare a page, defaulting to a private top-level page in the Apps workspace. */ export function page(args: PageArgs): PageHandle { if (args.parent) { assertParent(args.parent); } const parent = args.parent ?? { type: "resourceId", resourceId: APPS_WORKSPACE_RESOURCE_ID, }; return createPage(args, parent); } /** Record a page declaration beneath its parent resource. */ export function createPage(args: ChildPageArgs | PageArgs, parent: Parent): PageHandle { assertUserResourceId(args.resourceId); if (args.icon !== undefined) { assertPageIcon(args.icon); } recordIntent({ type: "page", ...args, parent, } satisfies { type: "page" } & PageIntent); return { resourceType: "page", resourceId: args.resourceId, addDatabase: createChildDatabase({ type: "resourceId", resourceId: args.resourceId }), addPage(pageArgs) { return createPage(pageArgs, { type: "resourceId", resourceId: args.resourceId }); }, }; }