/** * Notion page shapes exchanged over the custom block bridge. * * These mirror a narrow subset of Notion's public `POST /v1/pages` API, since that's the bridge * shape the host delivers for messages like `createPageResult`. */ import * as v from "valibot" import { notionDataSourceIdSchema, notionPageIdSchema } from "../ids.js" import { notionParentSchema } from "../parent.js" export type { NotionPageId } from "../ids.js" export const customBlockPageSchema = v.object({ id: notionPageIdSchema, }) export type CustomBlockPage = v.InferOutput /** * Parent reference accepted by `pages.create` / `pages.update` inputs. * * Mirrors the parent shape Notion's public `POST /v1/pages` API accepts: `page_id` for a * page-parented child, `data_source_id` for a database row. `data_source_id` is the internal * collection ID. */ export const notionPageParentInputSchema = v.variant("type", [ v.object({ type: v.literal("page_id"), page_id: notionPageIdSchema }), v.object({ type: v.literal("data_source_id"), data_source_id: notionDataSourceIdSchema, }), ]) export type NotionPageParentInput = v.InferOutput< typeof notionPageParentInputSchema > export const notionEmojiIconSchema = v.object({ type: v.literal("emoji"), emoji: v.string(), }) export const notionCustomEmojiIconSchema = v.object({ type: v.literal("custom_emoji"), custom_emoji: v.object({ id: v.string(), name: v.optional(v.string()), url: v.optional(v.string()), }), }) export const notionExternalFileSchema = v.object({ type: v.literal("external"), external: v.object({ url: v.string() }), }) export const notionHostedFileSchema = v.object({ type: v.literal("file"), file: v.object({ url: v.string(), expiry_time: v.optional(v.string()), }), }) // TODO(custom-blocks): Add file upload support. export const notionPageIconSchema = v.variant("type", [ notionEmojiIconSchema, notionCustomEmojiIconSchema, notionExternalFileSchema, notionHostedFileSchema, ]) export type NotionPageIcon = v.InferOutput export const notionPageCoverSchema = v.variant("type", [ notionExternalFileSchema, notionHostedFileSchema, ]) export type NotionPageCover = v.InferOutput const nullableStringSchema = v.nullable(v.string()) const notionRichTextItemSchema = v.record(v.string(), v.unknown()) export type NotionRichTextItem = v.InferOutput const notionSelectOptionInputSchema = v.union([ v.object({ id: v.string(), name: v.optional(v.string()), color: v.optional(v.string()), description: v.optional(nullableStringSchema), }), v.object({ name: v.string(), id: v.optional(v.string()), color: v.optional(v.string()), description: v.optional(nullableStringSchema), }), ]) export type NotionSelectOptionInput = v.InferOutput< typeof notionSelectOptionInputSchema > const notionDateInputSchema = v.object({ start: v.string(), end: v.optional(nullableStringSchema), time_zone: v.optional(nullableStringSchema), }) export type NotionDateInput = v.InferOutput const notionUserInputSchema = v.union([ v.object({ object: v.optional(v.literal("user")), id: v.string(), }), v.object({ object: v.literal("group"), id: v.string(), name: v.optional(nullableStringSchema), }), ]) export type NotionUserInput = v.InferOutput const notionRelationInputSchema = v.object({ id: v.string() }) export type NotionRelationInput = v.InferOutput< typeof notionRelationInputSchema > const notionFileInputSchema = v.union([ v.object({ type: v.literal("external"), name: v.string(), external: v.object({ url: v.string() }), }), v.object({ type: v.literal("file"), name: v.string(), file: v.object({ url: v.string(), expiry_time: v.optional(v.string()), }), }), ]) export type NotionFileInput = v.InferOutput const notionPlaceInputSchema = v.object({ lat: v.number(), lon: v.number(), name: v.optional(nullableStringSchema), address: v.optional(nullableStringSchema), aws_place_id: v.optional(nullableStringSchema), google_place_id: v.optional(nullableStringSchema), }) export type NotionPlaceInput = v.InferOutput const pagePropertyBaseSchema = { id: v.string(), } /** * A page property value. */ export const notionPagePropertyValueSchema = v.variant("type", [ v.object({ ...pagePropertyBaseSchema, type: v.literal("title"), title: v.array(notionRichTextItemSchema), }), v.object({ ...pagePropertyBaseSchema, type: v.literal("rich_text"), rich_text: v.array(notionRichTextItemSchema), }), v.object({ ...pagePropertyBaseSchema, type: v.literal("number"), number: v.nullable(v.number()), }), v.object({ ...pagePropertyBaseSchema, type: v.literal("url"), url: nullableStringSchema, }), v.object({ ...pagePropertyBaseSchema, type: v.literal("email"), email: nullableStringSchema, }), v.object({ ...pagePropertyBaseSchema, type: v.literal("phone_number"), phone_number: nullableStringSchema, }), v.object({ ...pagePropertyBaseSchema, type: v.literal("checkbox"), checkbox: v.boolean(), }), v.object({ ...pagePropertyBaseSchema, type: v.literal("select"), select: v.nullable(notionSelectOptionInputSchema), }), v.object({ ...pagePropertyBaseSchema, type: v.literal("status"), status: v.nullable(notionSelectOptionInputSchema), }), v.object({ ...pagePropertyBaseSchema, type: v.literal("multi_select"), multi_select: v.array(notionSelectOptionInputSchema), }), v.object({ ...pagePropertyBaseSchema, type: v.literal("date"), date: v.nullable(notionDateInputSchema), }), v.object({ ...pagePropertyBaseSchema, type: v.literal("people"), people: v.array(notionUserInputSchema), }), v.object({ ...pagePropertyBaseSchema, type: v.literal("relation"), has_more: v.optional(v.boolean()), relation: v.array(notionRelationInputSchema), }), v.object({ ...pagePropertyBaseSchema, type: v.literal("files"), files: v.array(notionFileInputSchema), }), v.object({ ...pagePropertyBaseSchema, type: v.literal("place"), place: v.nullable(notionPlaceInputSchema), }), ]) export type NotionPagePropertyValue = v.InferOutput< typeof notionPagePropertyValueSchema > type WithOptionalPropertyId = T extends { id: string } ? Omit & { id?: string } : never /** * Public SDK page property write input. Keys may be raw property IDs or data-source * property keys. `id` is optional because the SDK resolves the final raw property ID * from the map key before sending the bridge message. */ export type NotionPagePropertyInputValue = WithOptionalPropertyId export type NotionPagePropertyInputMap = { [propertyIdOrKey: string]: NotionPagePropertyInputValue } export const notionPagePropertyWriteMapSchema = v.record( v.string(), notionPagePropertyValueSchema, ) export type NotionPagePropertyWriteMap = v.InferOutput< typeof notionPagePropertyWriteMapSchema > /** * The `Page` object returned in `createPageResult` messages. */ export const notionPageSchema = v.object({ object: v.literal("page"), id: notionPageIdSchema, parent: notionParentSchema, properties: notionPagePropertyWriteMapSchema, created_time: v.optional(v.string()), last_edited_time: v.optional(v.string()), icon: v.optional(notionPageIconSchema), cover: v.optional(notionPageCoverSchema), url: v.optional(v.string()), public_url: v.optional(v.string()), in_trash: v.optional(v.boolean()), }) export type NotionPage = v.InferOutput