@notionx/core 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/notion/generic-source.d.ts +1 -1
- package/dist/notion/generic-source.js +39 -2
- package/dist/notion/generic-source.js.map +1 -1
- package/dist/notion/index.js +22 -2
- package/dist/notion/index.js.map +1 -1
- package/dist/pages/index.d.ts +7 -0
- package/dist/pages/index.js +27 -0
- package/dist/pages/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -18,7 +18,7 @@ type GenericContentListItem = {
|
|
|
18
18
|
coverImage: string | null;
|
|
19
19
|
published: boolean;
|
|
20
20
|
editUrl: string | null;
|
|
21
|
-
properties: Record<string, string | string[]>;
|
|
21
|
+
properties: Record<string, string | string[] | number | boolean>;
|
|
22
22
|
};
|
|
23
23
|
type GenericContentDetail = GenericContentListItem & {
|
|
24
24
|
blocks: NotionBlock[];
|
|
@@ -226,6 +226,23 @@ function getDateProperty(properties, key) {
|
|
|
226
226
|
const date = property.date;
|
|
227
227
|
return String(date?.start ?? "").trim();
|
|
228
228
|
}
|
|
229
|
+
function getSelectProperty(properties, key) {
|
|
230
|
+
const property = getProperty(properties, key);
|
|
231
|
+
if (property?.type !== "select") return "";
|
|
232
|
+
const select = property.select;
|
|
233
|
+
return String(select?.name ?? "").trim();
|
|
234
|
+
}
|
|
235
|
+
function getCheckboxProperty(properties, key) {
|
|
236
|
+
const property = getProperty(properties, key);
|
|
237
|
+
if (property?.type !== "checkbox") return false;
|
|
238
|
+
return Boolean(property.checkbox);
|
|
239
|
+
}
|
|
240
|
+
function getNumberProperty(properties, key, fallback = 0) {
|
|
241
|
+
const property = getProperty(properties, key);
|
|
242
|
+
if (property?.type !== "number") return fallback;
|
|
243
|
+
const value = Number(property.number);
|
|
244
|
+
return Number.isFinite(value) ? value : fallback;
|
|
245
|
+
}
|
|
229
246
|
function getTagsProperty(properties, key) {
|
|
230
247
|
const property = getProperty(properties, key);
|
|
231
248
|
if (property?.type === "multi_select" && Array.isArray(property.multi_select)) {
|
|
@@ -299,8 +316,28 @@ function mapExtraProperties(properties, fields) {
|
|
|
299
316
|
for (const [key, value] of Object.entries(fields)) {
|
|
300
317
|
const field = firstFieldName(value);
|
|
301
318
|
if (!field) continue;
|
|
302
|
-
const
|
|
303
|
-
|
|
319
|
+
const property = properties[field];
|
|
320
|
+
if (!property?.type) {
|
|
321
|
+
result[key] = getRichTextProperty(properties, field);
|
|
322
|
+
continue;
|
|
323
|
+
}
|
|
324
|
+
if (property.type === "multi_select") {
|
|
325
|
+
result[key] = getTagsProperty(properties, field);
|
|
326
|
+
continue;
|
|
327
|
+
}
|
|
328
|
+
if (property.type === "select" || property.type === "status") {
|
|
329
|
+
result[key] = getSelectProperty(properties, field);
|
|
330
|
+
continue;
|
|
331
|
+
}
|
|
332
|
+
if (property.type === "number") {
|
|
333
|
+
result[key] = getNumberProperty(properties, field);
|
|
334
|
+
continue;
|
|
335
|
+
}
|
|
336
|
+
if (property.type === "checkbox") {
|
|
337
|
+
result[key] = getCheckboxProperty(properties, field);
|
|
338
|
+
continue;
|
|
339
|
+
}
|
|
340
|
+
result[key] = getRichTextProperty(properties, field);
|
|
304
341
|
}
|
|
305
342
|
return result;
|
|
306
343
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/notion/generic-source.ts","../../src/notion/blocks.ts","../../src/notion/client.ts","../../src/notion/config.ts","../../src/notion/media.ts","../../src/notion/property-mappers.ts"],"sourcesContent":["import { cache } from \"react\";\nimport { listBlockChildrenDeep, type NotionBlockClient } from \"./blocks\";\nimport { createNotionClient } from \"./client\";\nimport { getNotionConfigForModel, hasNotionModelConfig } from \"./config\";\nimport { coverImageUrlForPage } from \"./media\";\nimport {\n getDateProperty,\n getRichTextProperty,\n getTagsProperty,\n isValidPublicSlug,\n notionPageEditUrl,\n pickDescriptionFallback,\n} from \"./property-mappers\";\nimport type {\n NotionBlock,\n NotionFieldMap,\n NotionGenericContentModel,\n NotionPageLike,\n} from \"./types\";\n\ntype PropertyMap = Record<string, unknown>;\n\ntype DataSourceQueryResponse = {\n results?: unknown[];\n has_more?: boolean;\n next_cursor?: string | null;\n};\n\ntype QueryDataSourceInput = {\n startCursor?: string;\n};\n\nexport type GenericContentListItem = {\n pageId: string;\n slug: string;\n title: string;\n description: string;\n date: string;\n tags: string[];\n coverImage: string | null;\n published: boolean;\n editUrl: string | null;\n properties: Record<string, string | string[]>;\n};\n\nexport type GenericContentDetail = GenericContentListItem & {\n blocks: NotionBlock[];\n};\n\nexport type GenericContentSourceDeps<\n TFields extends NotionFieldMap = NotionFieldMap,\n> = {\n model: NotionGenericContentModel & { source: { fields: TFields } };\n dataSourceId: string;\n queryDataSource: (\n input?: QueryDataSourceInput\n ) => Promise<DataSourceQueryResponse>;\n getPageBlocks: (pageId: string) => Promise<NotionBlock[]>;\n editBaseUrl?: string;\n};\n\nfunction normalizePage(input: unknown): NotionPageLike | null {\n if (!input || typeof input !== \"object\") return null;\n const page = input as NotionPageLike;\n return page.id ? page : null;\n}\n\nfunction firstFieldName(value: string | readonly string[] | undefined) {\n if (Array.isArray(value)) return value[0];\n return value;\n}\n\nfunction getFieldName(fields: NotionFieldMap, key: string) {\n return firstFieldName(fields[key]);\n}\n\nfunction pickPublishedFlagForModel(\n properties: PropertyMap,\n fields: NotionFieldMap\n) {\n const publishedField = getFieldName(fields, \"published\") ?? \"Published\";\n const published = properties[publishedField] as\n | Record<string, unknown>\n | undefined;\n if (published?.type === \"checkbox\") return Boolean(published.checkbox);\n\n const statusField = getFieldName(fields, \"status\") ?? \"Status\";\n const status = properties[statusField] as Record<string, unknown> | undefined;\n if (status?.type === \"status\") {\n const value = status.status as { name?: string } | null | undefined;\n return String(value?.name ?? \"\").trim().toLowerCase() === \"published\";\n }\n if (status?.type === \"select\") {\n const value = status.select as { name?: string } | null | undefined;\n return String(value?.name ?? \"\").trim().toLowerCase() === \"published\";\n }\n\n return true;\n}\n\nfunction coverImageUrlForModel(page: NotionPageLike, fields: NotionFieldMap) {\n const coverField = fields.cover;\n if (Array.isArray(coverField)) {\n for (const field of coverField) {\n const imageUrl = coverImageUrlForPage(page, field);\n if (imageUrl) return imageUrl;\n }\n }\n if (typeof coverField === \"string\") {\n return coverImageUrlForPage(page, coverField);\n }\n return coverImageUrlForPage(page);\n}\n\nfunction mapExtraProperties(properties: PropertyMap, fields: NotionFieldMap) {\n const result: Record<string, string | string[]> = {};\n for (const [key, value] of Object.entries(fields)) {\n const field = firstFieldName(value);\n if (!field) continue;\n const tags = getTagsProperty(properties, field);\n result[key] = tags.length > 0 ? tags : getRichTextProperty(properties, field);\n }\n return result;\n}\n\nexport function mapNotionPageToGenericContentItem<\n TFields extends NotionFieldMap,\n>(\n model: { id: string; source: { fields: TFields } },\n page: NotionPageLike,\n options?: { editBaseUrl?: string }\n): GenericContentListItem {\n const fields = model.source.fields;\n const properties = isRecord(page.properties) ? page.properties : {};\n const titleField = getFieldName(fields, \"title\");\n const slugField = getFieldName(fields, \"slug\");\n const descriptionField = getFieldName(fields, \"description\");\n const dateField = getFieldName(fields, \"date\");\n const tagsField = getFieldName(fields, \"tags\");\n\n const title = titleField ? getRichTextProperty(properties, titleField) : \"\";\n const slug = slugField\n ? getRichTextProperty(properties, slugField).toLowerCase()\n : page.id.replaceAll(\"-\", \"\").toLowerCase();\n const description = pickDescriptionFallback(\n descriptionField ? getRichTextProperty(properties, descriptionField) : \"\",\n title\n );\n\n return {\n pageId: page.id,\n slug,\n title,\n description,\n date: dateField ? getDateProperty(properties, dateField) : \"\",\n tags: tagsField ? getTagsProperty(properties, tagsField) : [],\n coverImage: coverImageUrlForModel(page, fields),\n published: pickPublishedFlagForModel(properties, fields),\n editUrl: notionPageEditUrl(page.id, options?.editBaseUrl),\n properties: mapExtraProperties(properties, fields),\n };\n}\n\nexport function isRenderableGenericContentItem(item: GenericContentListItem) {\n return Boolean(item.published && item.title && item.slug && isValidPublicSlug(item.slug));\n}\n\nexport function createGenericNotionContentSource<\n TFields extends NotionFieldMap,\n>(deps: GenericContentSourceDeps<TFields>) {\n return {\n async listItems(): Promise<GenericContentListItem[]> {\n const pages: NotionPageLike[] = [];\n let cursor: string | undefined;\n\n do {\n const response = await deps.queryDataSource({ startCursor: cursor });\n for (const item of response.results ?? []) {\n const page = normalizePage(item);\n if (page) pages.push(page);\n }\n\n cursor = response.next_cursor ?? undefined;\n if (!response.has_more) break;\n } while (cursor);\n\n return pages\n .map((page) =>\n mapNotionPageToGenericContentItem(deps.model, page, {\n editBaseUrl: deps.editBaseUrl,\n })\n )\n .filter(isRenderableGenericContentItem)\n .sort((a, b) => b.date.localeCompare(a.date));\n },\n\n async getItemBySlug(slug: string): Promise<GenericContentDetail | null> {\n const items = await this.listItems();\n const item = items.find((candidate) => candidate.slug === slug);\n if (!item) return null;\n return {\n ...item,\n blocks: await deps.getPageBlocks(item.pageId),\n };\n },\n };\n}\n\nasync function createDefaultGenericSource<\n TFields extends NotionFieldMap,\n>(model: NotionGenericContentModel & { source: { fields: TFields } }) {\n if (!(await hasNotionModelConfig(model))) return null;\n\n const config = await getNotionConfigForModel(model);\n const client = createNotionClient(config);\n return createGenericNotionContentSource({\n model,\n dataSourceId: config.dataSourceId,\n editBaseUrl: config.editBaseUrl,\n queryDataSource: async ({ startCursor } = {}) =>\n client.dataSources.query({\n data_source_id: config.dataSourceId,\n page_size: model.source.query.pageSize,\n sorts: model.source.query.sorts\n ? [...model.source.query.sorts]\n : undefined,\n filter_properties: model.source.query.filterProperties\n ? [...model.source.query.filterProperties]\n : undefined,\n ...(startCursor ? { start_cursor: startCursor } : {}),\n }),\n getPageBlocks: (pageId) =>\n listBlockChildrenDeep(client as unknown as NotionBlockClient, pageId),\n });\n}\n\nconst sourceCache = cache(createDefaultGenericSource);\n\nexport async function listGenericNotionContent<\n TFields extends NotionFieldMap,\n>(model: NotionGenericContentModel & { source: { fields: TFields } }) {\n const source = await sourceCache(model);\n if (!source) return [];\n return source.listItems();\n}\n\nexport async function getGenericNotionContentBySlug<\n TFields extends NotionFieldMap,\n>(model: NotionGenericContentModel & { source: { fields: TFields } }, slug: string) {\n const source = await sourceCache(model);\n if (!source) return null;\n return source.getItemBySlug(slug);\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return Boolean(value && typeof value === \"object\");\n}\n","import type { NotionBlock } from \"./types\";\n\ntype BlockChildrenListResponse = {\n results?: unknown[];\n has_more?: boolean;\n next_cursor?: string | null;\n};\n\nexport type NotionBlockClient = {\n blocks: {\n children: {\n list: (args: {\n block_id: string;\n page_size?: number;\n start_cursor?: string;\n }) => Promise<BlockChildrenListResponse>;\n };\n };\n};\n\nfunction normalizeBlock(input: unknown): NotionBlock | null {\n if (!input || typeof input !== \"object\") return null;\n const block = input as NotionBlock;\n return block.id && block.type ? block : null;\n}\n\nexport async function listBlockChildren(\n client: NotionBlockClient,\n blockId: string\n): Promise<NotionBlock[]> {\n const results: NotionBlock[] = [];\n let cursor: string | undefined;\n\n do {\n const response = await client.blocks.children.list({\n block_id: blockId,\n page_size: 100,\n ...(cursor ? { start_cursor: cursor } : {}),\n });\n\n for (const item of response.results ?? []) {\n const block = normalizeBlock(item);\n if (block) results.push(block);\n }\n\n cursor = response.next_cursor ?? undefined;\n if (!response.has_more) break;\n } while (cursor);\n\n return results;\n}\n\nexport async function listBlockChildrenDeep(\n client: NotionBlockClient,\n blockId: string,\n options?: { maxDepth?: number }\n): Promise<NotionBlock[]> {\n const maxDepth = options?.maxDepth ?? 6;\n\n async function visit(id: string, depth: number): Promise<NotionBlock[]> {\n const children = await listBlockChildren(client, id);\n if (depth >= maxDepth) return children;\n\n return Promise.all(\n children.map(async (block) => {\n if (!block.has_children) return block;\n return {\n ...block,\n children: await visit(block.id, depth + 1),\n };\n })\n );\n }\n\n return visit(blockId, 0);\n}\n","import { Client } from \"@notionhq/client\";\nimport type { NotionClientConfig } from \"./config\";\n\nexport function createNotionClient(config: NotionClientConfig) {\n return new Client({\n auth: config.token,\n baseUrl: config.apiBaseUrl,\n notionVersion: \"2026-03-11\",\n });\n}\n","import type { NotionContentModelLike } from \"./types\";\n\ntype NotionEnv = {\n NOTION_TOKEN?: string;\n NOTION_DATA_SOURCE_ID?: string;\n NOTION_API_BASE_URL?: string;\n NOTION_EDIT_BASE_URL?: string;\n NOTION_WEBHOOK_VERIFICATION_TOKEN?: string;\n [key: string]: string | undefined;\n};\n\nexport type NotionClientConfig = {\n token: string;\n apiBaseUrl?: string;\n};\n\nexport type NotionConfig = {\n token: string;\n dataSourceId: string;\n apiBaseUrl?: string;\n editBaseUrl?: string;\n webhookVerificationToken?: string;\n};\n\nfunction readProcessEnv(): NotionEnv {\n const env: NotionEnv = {\n NOTION_TOKEN: process.env.NOTION_TOKEN,\n NOTION_DATA_SOURCE_ID: process.env.NOTION_DATA_SOURCE_ID,\n NOTION_API_BASE_URL: process.env.NOTION_API_BASE_URL,\n NOTION_EDIT_BASE_URL: process.env.NOTION_EDIT_BASE_URL,\n NOTION_WEBHOOK_VERIFICATION_TOKEN:\n process.env.NOTION_WEBHOOK_VERIFICATION_TOKEN,\n };\n\n for (const [key, value] of Object.entries(process.env)) {\n if (key.startsWith(\"NOTION_\") && typeof value === \"string\") {\n env[key] = value;\n }\n }\n\n return env;\n}\n\nasync function readWorkerEnv(): Promise<NotionEnv> {\n try {\n const mod = (await import(\n /* webpackIgnore: true */ \"cloudflare:workers\"\n )) as unknown as { env?: Record<string, unknown> };\n const env: NotionEnv = {};\n for (const [key, value] of Object.entries(mod.env ?? {})) {\n if (key.startsWith(\"NOTION_\") && typeof value === \"string\") {\n env[key] = value;\n }\n }\n return env;\n } catch {\n return {};\n }\n}\n\nfunction readString(source: NotionEnv, name: string): string | undefined {\n const value = String(source[name] ?? \"\").trim();\n return value || undefined;\n}\n\nfunction mergeEnv(...sources: NotionEnv[]): NotionEnv {\n const merged: NotionEnv = {};\n\n for (const source of sources) {\n for (const name of Object.keys(source)) {\n if (!name.startsWith(\"NOTION_\")) continue;\n const value = readString(source, name);\n if (value) merged[name] = value;\n }\n }\n\n return merged;\n}\n\nasync function readEnv(): Promise<NotionEnv> {\n const processEnv = readProcessEnv();\n return mergeEnv(await readWorkerEnv(), processEnv);\n}\n\nfunction readRequired(\n source: NotionEnv,\n name: string\n): string {\n const value = readString(source, name);\n if (!value) {\n throw new Error(`Missing required Notion env: ${name}`);\n }\n return value;\n}\n\nexport function getNotionEditBaseUrl(): string {\n return readString(readProcessEnv(), \"NOTION_EDIT_BASE_URL\") ?? \"https://www.notion.so\";\n}\n\nexport async function hasNotionConfig(): Promise<boolean> {\n const env = await readEnv();\n return Boolean(\n readString(env, \"NOTION_TOKEN\") && readString(env, \"NOTION_DATA_SOURCE_ID\")\n );\n}\n\nexport async function hasNotionModelConfig(\n model: NotionContentModelLike\n): Promise<boolean> {\n const env = await readEnv();\n return Boolean(\n readString(env, \"NOTION_TOKEN\") &&\n (readString(env, model.source.dataSourceEnv) ||\n model.source.defaultDataSourceId)\n );\n}\n\nexport async function getNotionClientConfig(): Promise<NotionClientConfig> {\n const env = await readEnv();\n return {\n token: readRequired(env, \"NOTION_TOKEN\"),\n apiBaseUrl: readString(env, \"NOTION_API_BASE_URL\"),\n };\n}\n\nexport async function getNotionConfig(): Promise<NotionConfig> {\n const env = await readEnv();\n return {\n token: readRequired(env, \"NOTION_TOKEN\"),\n dataSourceId: readRequired(env, \"NOTION_DATA_SOURCE_ID\"),\n apiBaseUrl: readString(env, \"NOTION_API_BASE_URL\"),\n editBaseUrl: readString(env, \"NOTION_EDIT_BASE_URL\"),\n webhookVerificationToken: readString(\n env,\n \"NOTION_WEBHOOK_VERIFICATION_TOKEN\"\n ),\n };\n}\n\nexport async function getNotionWebhookVerificationToken(): Promise<\n string | undefined\n> {\n const env = await readEnv();\n return readString(env, \"NOTION_WEBHOOK_VERIFICATION_TOKEN\");\n}\n\nexport async function getNotionConfigForModel(\n model: NotionContentModelLike\n): Promise<NotionConfig> {\n const env = await readEnv();\n const dataSourceId =\n readString(env, model.source.dataSourceEnv) ??\n model.source.defaultDataSourceId;\n if (!dataSourceId) {\n throw new Error(`Missing required Notion env: ${model.source.dataSourceEnv}`);\n }\n\n return {\n token: readRequired(env, model.source.tokenEnv),\n dataSourceId,\n apiBaseUrl: readString(env, \"NOTION_API_BASE_URL\"),\n editBaseUrl: readString(env, \"NOTION_EDIT_BASE_URL\"),\n webhookVerificationToken: readString(\n env,\n \"NOTION_WEBHOOK_VERIFICATION_TOKEN\"\n ),\n };\n}\n","import type { NotionBlock, NotionFileSource, NotionPageLike } from \"./types\";\n\ntype FileLike = {\n type?: string;\n external?: { url?: string };\n file?: { url?: string; expiry_time?: string };\n name?: string;\n};\n\nfunction stripLeadingSlash(value: string) {\n return value.startsWith(\"/\") ? value.slice(1) : value;\n}\n\nfunction encodePathPart(value: string) {\n return encodeURIComponent(stripLeadingSlash(value));\n}\n\nfunction appendVersion(path: string, version?: string) {\n const value = String(version ?? \"\").trim();\n if (!value) return path;\n return `${path}?${new URLSearchParams({ v: value })}`;\n}\n\nfunction blockVersion(block: NotionBlock): string | undefined {\n return typeof block.last_edited_time === \"string\"\n ? block.last_edited_time\n : undefined;\n}\n\nexport function notionPageCoverMediaPath(pageId: string): string {\n return `/api/notion/media/page/${encodePathPart(pageId)}/cover`;\n}\n\nexport function notionPagePropertyMediaPath(\n pageId: string,\n propertyName: string\n): string {\n return `/api/notion/media/page/${encodePathPart(pageId)}/property/${encodePathPart(propertyName)}`;\n}\n\nexport function notionBlockMediaPath(blockId: string): string {\n return `/api/notion/media/block/${encodePathPart(blockId)}`;\n}\n\nexport function normalizeNotionFileSource(input: unknown): NotionFileSource | null {\n const file = input as FileLike | null | undefined;\n if (!file || typeof file !== \"object\") return null;\n\n if (file.type === \"external\") {\n const url = String(file.external?.url ?? \"\").trim();\n return url ? { type: \"external\", url } : null;\n }\n\n if (file.type === \"file\") {\n const url = String(file.file?.url ?? \"\").trim();\n if (!url) return null;\n return {\n type: \"file\",\n url,\n expiryTime: String(file.file?.expiry_time ?? \"\").trim() || null,\n };\n }\n\n return null;\n}\n\nexport function resolveNotionFileUrl(input: unknown): string | null {\n return normalizeNotionFileSource(input)?.url ?? null;\n}\n\nexport function isNotionHostedFile(input: unknown): boolean {\n return normalizeNotionFileSource(input)?.type === \"file\";\n}\n\nexport function pickFirstFilesPropertyValue(property: unknown): unknown | null {\n const value = property as { type?: string; files?: unknown[] } | null | undefined;\n if (!value || value.type !== \"files\" || !Array.isArray(value.files)) {\n return null;\n }\n return value.files[0] ?? null;\n}\n\nexport function pickPageCoverFile(page: NotionPageLike): unknown | null {\n return page.cover ?? null;\n}\n\nexport function coverImageUrlForPage(\n page: NotionPageLike,\n coverPropertyName = \"Cover\"\n): string | null {\n const propertyFile = pickFirstFilesPropertyValue(\n page.properties?.[coverPropertyName]\n );\n const propertySource = normalizeNotionFileSource(propertyFile);\n if (propertySource) {\n return appendVersion(\n notionPagePropertyMediaPath(page.id, coverPropertyName),\n page.last_edited_time\n );\n }\n\n const coverSource = normalizeNotionFileSource(pickPageCoverFile(page));\n if (coverSource) {\n return appendVersion(notionPageCoverMediaPath(page.id), page.last_edited_time);\n }\n\n return null;\n}\n\nexport function fileObjectForMediaBlock(block: NotionBlock): unknown | null {\n const typed = block[block.type] as Record<string, unknown> | undefined;\n if (!typed || typeof typed !== \"object\") return null;\n\n if (\n block.type === \"image\" ||\n block.type === \"video\" ||\n block.type === \"file\" ||\n block.type === \"pdf\" ||\n block.type === \"audio\"\n ) {\n return typed;\n }\n\n return null;\n}\n\nexport function mediaUrlForBlock(block: NotionBlock): string | null {\n const source = normalizeNotionFileSource(fileObjectForMediaBlock(block));\n if (!source) return null;\n if (source.type === \"external\" && block.type !== \"image\") {\n return source.url;\n }\n return appendVersion(notionBlockMediaPath(block.id), blockVersion(block));\n}\n\nexport function firstImageUrlFromBlocks(blocks: NotionBlock[]): string | null {\n for (const block of blocks) {\n if (block.type === \"image\") {\n const imageUrl = mediaUrlForBlock(block);\n if (imageUrl) return imageUrl;\n }\n\n const nested = block.children?.length\n ? firstImageUrlFromBlocks(block.children)\n : null;\n if (nested) return nested;\n }\n\n return null;\n}\n\nexport function publicMediaBlockForApi(block: NotionBlock): NotionBlock {\n const value = block[block.type];\n const source = normalizeNotionFileSource(value);\n const children = block.children?.map(publicMediaBlockForApi);\n\n if (!source) {\n return children ? { ...block, children } : { ...block };\n }\n\n const path = appendVersion(notionBlockMediaPath(block.id), blockVersion(block));\n const publicValue =\n source.type === \"external\"\n ? block.type === \"image\"\n ? {\n ...(value as Record<string, unknown>),\n external: { url: path },\n }\n : value\n : {\n ...(value as Record<string, unknown>),\n file: {\n url: path,\n expiry_time: null,\n },\n };\n\n return {\n ...block,\n [block.type]: publicValue,\n ...(children ? { children } : {}),\n };\n}\n\nexport function gatedMediaBlockForApi(\n block: NotionBlock,\n options?: { accessUrlForBlock?: (block: NotionBlock) => string | null }\n): NotionBlock {\n const value = block[block.type];\n const source = normalizeNotionFileSource(value);\n const children = block.children?.map((child) =>\n gatedMediaBlockForApi(child, options)\n );\n\n if (block.type !== \"video\" || !source) {\n const publicBlock = publicMediaBlockForApi(block);\n return children ? { ...publicBlock, children } : publicBlock;\n }\n\n const gatedValue: Record<string, unknown> = {\n ...(value as Record<string, unknown>),\n gated: true,\n access_url: options?.accessUrlForBlock?.(block) ?? null,\n };\n\n if (source.type === \"external\") {\n gatedValue.external = { url: null };\n } else {\n gatedValue.file = {\n url: null,\n expiry_time: null,\n };\n }\n\n return {\n ...block,\n video: gatedValue,\n ...(children ? { children } : {}),\n };\n}\n\nexport function isDirectVideoUrl(url: string): boolean {\n try {\n const parsed = new URL(url);\n return /\\.(mp4|webm|mov|m4v)(?:$|\\?)/i.test(parsed.pathname);\n } catch {\n return false;\n }\n}\n\nexport function videoEmbedUrl(url: string): string | null {\n try {\n const parsed = new URL(url);\n const host = parsed.hostname.replace(/^www\\./, \"\");\n\n if (host === \"youtube.com\" || host === \"m.youtube.com\") {\n const id = parsed.searchParams.get(\"v\");\n return id ? `https://www.youtube.com/embed/${encodeURIComponent(id)}` : null;\n }\n\n if (host === \"youtu.be\") {\n const id = parsed.pathname.split(\"/\").filter(Boolean)[0];\n return id ? `https://www.youtube.com/embed/${encodeURIComponent(id)}` : null;\n }\n\n if (host === \"youtube-nocookie.com\") {\n return parsed.toString();\n }\n\n if (host === \"vimeo.com\") {\n const id = parsed.pathname.split(\"/\").filter(Boolean)[0];\n return id ? `https://player.vimeo.com/video/${encodeURIComponent(id)}` : null;\n }\n\n if (host === \"player.vimeo.com\") {\n return parsed.toString();\n }\n } catch {\n return null;\n }\n\n return null;\n}\n","type PropertyMap = Record<string, unknown>;\n\ntype TextPart = {\n plain_text?: string;\n};\n\nexport function isRecord(value: unknown): value is Record<string, unknown> {\n return Boolean(value && typeof value === \"object\");\n}\n\nfunction getPlainText(parts: unknown): string {\n if (!Array.isArray(parts)) return \"\";\n return parts\n .map((part: TextPart) => part.plain_text ?? \"\")\n .join(\"\")\n .trim();\n}\n\nfunction getProperty(properties: PropertyMap, key: string) {\n return properties[key] as Record<string, unknown> | undefined;\n}\n\nfunction firstPropertyOfType(properties: PropertyMap, type: string) {\n return Object.values(properties).find(\n (property): property is Record<string, unknown> =>\n isRecord(property) && property.type === type\n );\n}\n\nexport function getFirstTitleProperty(properties: PropertyMap): string {\n const property = firstPropertyOfType(properties, \"title\");\n return property ? getPlainText(property.title) : \"\";\n}\n\nexport function getRichTextProperty(properties: PropertyMap, key: string): string {\n const property = getProperty(properties, key);\n if (!property) return \"\";\n\n if (property.type === \"title\") return getPlainText(property.title);\n if (property.type === \"rich_text\") return getPlainText(property.rich_text);\n if (property.type === \"url\") return String(property.url ?? \"\").trim();\n if (property.type === \"email\") return String(property.email ?? \"\").trim();\n if (property.type === \"phone_number\") {\n return String(property.phone_number ?? \"\").trim();\n }\n\n return \"\";\n}\n\nexport function getDateProperty(properties: PropertyMap, key: string): string {\n const property = getProperty(properties, key);\n if (property?.type !== \"date\") return \"\";\n const date = property.date as { start?: string } | null | undefined;\n return String(date?.start ?? \"\").trim();\n}\n\nexport function getFirstDateProperty(properties: PropertyMap): string {\n const property = firstPropertyOfType(properties, \"date\");\n if (!property) return \"\";\n const date = property.date as { start?: string } | null | undefined;\n return String(date?.start ?? \"\").trim();\n}\n\nexport function getSelectProperty(properties: PropertyMap, key: string): string {\n const property = getProperty(properties, key);\n if (property?.type !== \"select\") return \"\";\n const select = property.select as { name?: string } | null | undefined;\n return String(select?.name ?? \"\").trim();\n}\n\nexport function getCheckboxProperty(properties: PropertyMap, key: string): boolean {\n const property = getProperty(properties, key);\n if (property?.type !== \"checkbox\") return false;\n return Boolean(property.checkbox);\n}\n\nexport function getNumberProperty(\n properties: PropertyMap,\n key: string,\n fallback = 0\n): number {\n const property = getProperty(properties, key);\n if (property?.type !== \"number\") return fallback;\n const value = Number(property.number);\n return Number.isFinite(value) ? value : fallback;\n}\n\nexport function getRelationPageIds(properties: PropertyMap, key: string): string[] {\n const property = getProperty(properties, key);\n if (property?.type !== \"relation\" || !Array.isArray(property.relation)) {\n return [];\n }\n\n return property.relation\n .map((item: { id?: string }) => String(item.id ?? \"\").trim())\n .filter(Boolean);\n}\n\nexport function getTagsProperty(properties: PropertyMap, key: string): string[] {\n const property = getProperty(properties, key);\n if (property?.type === \"multi_select\" && Array.isArray(property.multi_select)) {\n return property.multi_select\n .map((item: { name?: string }) => String(item.name ?? \"\").trim())\n .filter(Boolean);\n }\n\n if (property?.type === \"select\") {\n const select = property.select as { name?: string } | null | undefined;\n const name = String(select?.name ?? \"\").trim();\n return name ? [name] : [];\n }\n\n return [];\n}\n\nexport function getFirstTagsProperty(properties: PropertyMap): string[] {\n const multiSelect = firstPropertyOfType(properties, \"multi_select\");\n if (multiSelect && Array.isArray(multiSelect.multi_select)) {\n return multiSelect.multi_select\n .map((item: { name?: string }) => String(item.name ?? \"\").trim())\n .filter(Boolean);\n }\n\n const select = firstPropertyOfType(properties, \"select\");\n const name = String((select?.select as { name?: string } | null)?.name ?? \"\").trim();\n return name ? [name] : [];\n}\n\nexport function getAuthorProperty(properties: PropertyMap, key: string): string {\n const property = getProperty(properties, key);\n if (!property) return \"\";\n\n if (property.type === \"people\" && Array.isArray(property.people)) {\n return property.people\n .map((person: { name?: string; person?: { email?: string } }) =>\n String(person.name ?? person.person?.email ?? \"\").trim()\n )\n .filter(Boolean)\n .join(\", \");\n }\n\n return getRichTextProperty(properties, key);\n}\n\nexport function getFirstPeopleProperty(properties: PropertyMap): string {\n const property = firstPropertyOfType(properties, \"people\");\n if (!property || !Array.isArray(property.people)) return \"\";\n\n return property.people\n .map((person: { name?: string; person?: { email?: string } }) =>\n String(person.name ?? person.person?.email ?? \"\").trim()\n )\n .filter(Boolean)\n .join(\", \");\n}\n\nexport function pickPublishedFlag(properties: PropertyMap): boolean {\n const published = getProperty(properties, \"Published\");\n if (published?.type === \"checkbox\") {\n return Boolean(published.checkbox);\n }\n\n const status = getProperty(properties, \"Status\");\n if (status?.type === \"status\") {\n const statusValue = status.status as { name?: string } | null | undefined;\n return String(statusValue?.name ?? \"\").trim().toLowerCase() === \"published\";\n }\n\n if (status?.type === \"select\") {\n const statusValue = status.select as { name?: string } | null | undefined;\n return String(statusValue?.name ?? \"\").trim().toLowerCase() === \"published\";\n }\n\n return false;\n}\n\nexport function pickDescriptionFallback(description: string, title: string): string {\n return description.trim() || title.trim();\n}\n\nexport function isValidPublicSlug(slug: string): boolean {\n return /^[a-z0-9][a-z0-9-]{0,79}$/.test(slug);\n}\n\nexport function notionPageEditUrl(pageId: string, editBaseUrl?: string): string {\n const compactPageId = pageId.replaceAll(\"-\", \"\");\n if (editBaseUrl?.includes(\"{pageId}\")) {\n return editBaseUrl.replaceAll(\"{pageId}\", compactPageId);\n }\n return `https://www.notion.so/${compactPageId}`;\n}\n\n/**\n * Normalize a Notion page id (with or without dashes) to a compact lowercase\n * string. Used as a stable identifier in URLs and cache keys.\n */\nexport function compactNotionId(id: string): string {\n return id.replaceAll(\"-\", \"\").toLowerCase();\n}\n"],"mappings":";AAAA,SAAS,aAAa;;;ACoBtB,SAAS,eAAe,OAAoC;AAC1D,MAAI,CAAC,SAAS,OAAO,UAAU,SAAU,QAAO;AAChD,QAAM,QAAQ;AACd,SAAO,MAAM,MAAM,MAAM,OAAO,QAAQ;AAC1C;AAEA,eAAsB,kBACpB,QACA,SACwB;AACxB,QAAM,UAAyB,CAAC;AAChC,MAAI;AAEJ,KAAG;AACD,UAAM,WAAW,MAAM,OAAO,OAAO,SAAS,KAAK;AAAA,MACjD,UAAU;AAAA,MACV,WAAW;AAAA,MACX,GAAI,SAAS,EAAE,cAAc,OAAO,IAAI,CAAC;AAAA,IAC3C,CAAC;AAED,eAAW,QAAQ,SAAS,WAAW,CAAC,GAAG;AACzC,YAAM,QAAQ,eAAe,IAAI;AACjC,UAAI,MAAO,SAAQ,KAAK,KAAK;AAAA,IAC/B;AAEA,aAAS,SAAS,eAAe;AACjC,QAAI,CAAC,SAAS,SAAU;AAAA,EAC1B,SAAS;AAET,SAAO;AACT;AAEA,eAAsB,sBACpB,QACA,SACA,SACwB;AACxB,QAAM,WAAW,SAAS,YAAY;AAEtC,iBAAe,MAAM,IAAY,OAAuC;AACtE,UAAM,WAAW,MAAM,kBAAkB,QAAQ,EAAE;AACnD,QAAI,SAAS,SAAU,QAAO;AAE9B,WAAO,QAAQ;AAAA,MACb,SAAS,IAAI,OAAO,UAAU;AAC5B,YAAI,CAAC,MAAM,aAAc,QAAO;AAChC,eAAO;AAAA,UACL,GAAG;AAAA,UACH,UAAU,MAAM,MAAM,MAAM,IAAI,QAAQ,CAAC;AAAA,QAC3C;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO,MAAM,SAAS,CAAC;AACzB;;;AC3EA,SAAS,cAAc;AAGhB,SAAS,mBAAmB,QAA4B;AAC7D,SAAO,IAAI,OAAO;AAAA,IAChB,MAAM,OAAO;AAAA,IACb,SAAS,OAAO;AAAA,IAChB,eAAe;AAAA,EACjB,CAAC;AACH;;;ACeA,SAAS,iBAA4B;AACnC,QAAM,MAAiB;AAAA,IACrB,cAAc,QAAQ,IAAI;AAAA,IAC1B,uBAAuB,QAAQ,IAAI;AAAA,IACnC,qBAAqB,QAAQ,IAAI;AAAA,IACjC,sBAAsB,QAAQ,IAAI;AAAA,IAClC,mCACE,QAAQ,IAAI;AAAA,EAChB;AAEA,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,QAAQ,GAAG,GAAG;AACtD,QAAI,IAAI,WAAW,SAAS,KAAK,OAAO,UAAU,UAAU;AAC1D,UAAI,GAAG,IAAI;AAAA,IACb;AAAA,EACF;AAEA,SAAO;AACT;AAEA,eAAe,gBAAoC;AACjD,MAAI;AACF,UAAM,MAAO,MAAM;AAAA;AAAA,MACS;AAAA,IAC5B;AACA,UAAM,MAAiB,CAAC;AACxB,eAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,IAAI,OAAO,CAAC,CAAC,GAAG;AACxD,UAAI,IAAI,WAAW,SAAS,KAAK,OAAO,UAAU,UAAU;AAC1D,YAAI,GAAG,IAAI;AAAA,MACb;AAAA,IACF;AACA,WAAO;AAAA,EACT,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;AAEA,SAAS,WAAW,QAAmB,MAAkC;AACvE,QAAM,QAAQ,OAAO,OAAO,IAAI,KAAK,EAAE,EAAE,KAAK;AAC9C,SAAO,SAAS;AAClB;AAEA,SAAS,YAAY,SAAiC;AACpD,QAAM,SAAoB,CAAC;AAE3B,aAAW,UAAU,SAAS;AAC5B,eAAW,QAAQ,OAAO,KAAK,MAAM,GAAG;AACtC,UAAI,CAAC,KAAK,WAAW,SAAS,EAAG;AACjC,YAAM,QAAQ,WAAW,QAAQ,IAAI;AACrC,UAAI,MAAO,QAAO,IAAI,IAAI;AAAA,IAC5B;AAAA,EACF;AAEA,SAAO;AACT;AAEA,eAAe,UAA8B;AAC3C,QAAM,aAAa,eAAe;AAClC,SAAO,SAAS,MAAM,cAAc,GAAG,UAAU;AACnD;AAEA,SAAS,aACP,QACA,MACQ;AACR,QAAM,QAAQ,WAAW,QAAQ,IAAI;AACrC,MAAI,CAAC,OAAO;AACV,UAAM,IAAI,MAAM,gCAAgC,IAAI,EAAE;AAAA,EACxD;AACA,SAAO;AACT;AAaA,eAAsB,qBACpB,OACkB;AAClB,QAAM,MAAM,MAAM,QAAQ;AAC1B,SAAO;AAAA,IACL,WAAW,KAAK,cAAc,MAC3B,WAAW,KAAK,MAAM,OAAO,aAAa,KACzC,MAAM,OAAO;AAAA,EACnB;AACF;AA+BA,eAAsB,wBACpB,OACuB;AACvB,QAAM,MAAM,MAAM,QAAQ;AAC1B,QAAM,eACJ,WAAW,KAAK,MAAM,OAAO,aAAa,KAC1C,MAAM,OAAO;AACf,MAAI,CAAC,cAAc;AACjB,UAAM,IAAI,MAAM,gCAAgC,MAAM,OAAO,aAAa,EAAE;AAAA,EAC9E;AAEA,SAAO;AAAA,IACL,OAAO,aAAa,KAAK,MAAM,OAAO,QAAQ;AAAA,IAC9C;AAAA,IACA,YAAY,WAAW,KAAK,qBAAqB;AAAA,IACjD,aAAa,WAAW,KAAK,sBAAsB;AAAA,IACnD,0BAA0B;AAAA,MACxB;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACF;;;AC9JA,SAAS,kBAAkB,OAAe;AACxC,SAAO,MAAM,WAAW,GAAG,IAAI,MAAM,MAAM,CAAC,IAAI;AAClD;AAEA,SAAS,eAAe,OAAe;AACrC,SAAO,mBAAmB,kBAAkB,KAAK,CAAC;AACpD;AAEA,SAAS,cAAc,MAAc,SAAkB;AACrD,QAAM,QAAQ,OAAO,WAAW,EAAE,EAAE,KAAK;AACzC,MAAI,CAAC,MAAO,QAAO;AACnB,SAAO,GAAG,IAAI,IAAI,IAAI,gBAAgB,EAAE,GAAG,MAAM,CAAC,CAAC;AACrD;AAQO,SAAS,yBAAyB,QAAwB;AAC/D,SAAO,0BAA0B,eAAe,MAAM,CAAC;AACzD;AAEO,SAAS,4BACd,QACA,cACQ;AACR,SAAO,0BAA0B,eAAe,MAAM,CAAC,aAAa,eAAe,YAAY,CAAC;AAClG;AAMO,SAAS,0BAA0B,OAAyC;AACjF,QAAM,OAAO;AACb,MAAI,CAAC,QAAQ,OAAO,SAAS,SAAU,QAAO;AAE9C,MAAI,KAAK,SAAS,YAAY;AAC5B,UAAM,MAAM,OAAO,KAAK,UAAU,OAAO,EAAE,EAAE,KAAK;AAClD,WAAO,MAAM,EAAE,MAAM,YAAY,IAAI,IAAI;AAAA,EAC3C;AAEA,MAAI,KAAK,SAAS,QAAQ;AACxB,UAAM,MAAM,OAAO,KAAK,MAAM,OAAO,EAAE,EAAE,KAAK;AAC9C,QAAI,CAAC,IAAK,QAAO;AACjB,WAAO;AAAA,MACL,MAAM;AAAA,MACN;AAAA,MACA,YAAY,OAAO,KAAK,MAAM,eAAe,EAAE,EAAE,KAAK,KAAK;AAAA,IAC7D;AAAA,EACF;AAEA,SAAO;AACT;AAUO,SAAS,4BAA4B,UAAmC;AAC7E,QAAM,QAAQ;AACd,MAAI,CAAC,SAAS,MAAM,SAAS,WAAW,CAAC,MAAM,QAAQ,MAAM,KAAK,GAAG;AACnE,WAAO;AAAA,EACT;AACA,SAAO,MAAM,MAAM,CAAC,KAAK;AAC3B;AAEO,SAAS,kBAAkB,MAAsC;AACtE,SAAO,KAAK,SAAS;AACvB;AAEO,SAAS,qBACd,MACA,oBAAoB,SACL;AACf,QAAM,eAAe;AAAA,IACnB,KAAK,aAAa,iBAAiB;AAAA,EACrC;AACA,QAAM,iBAAiB,0BAA0B,YAAY;AAC7D,MAAI,gBAAgB;AAClB,WAAO;AAAA,MACL,4BAA4B,KAAK,IAAI,iBAAiB;AAAA,MACtD,KAAK;AAAA,IACP;AAAA,EACF;AAEA,QAAM,cAAc,0BAA0B,kBAAkB,IAAI,CAAC;AACrE,MAAI,aAAa;AACf,WAAO,cAAc,yBAAyB,KAAK,EAAE,GAAG,KAAK,gBAAgB;AAAA,EAC/E;AAEA,SAAO;AACT;;;ACjGA,SAAS,aAAa,OAAwB;AAC5C,MAAI,CAAC,MAAM,QAAQ,KAAK,EAAG,QAAO;AAClC,SAAO,MACJ,IAAI,CAAC,SAAmB,KAAK,cAAc,EAAE,EAC7C,KAAK,EAAE,EACP,KAAK;AACV;AAEA,SAAS,YAAY,YAAyB,KAAa;AACzD,SAAO,WAAW,GAAG;AACvB;AAcO,SAAS,oBAAoB,YAAyB,KAAqB;AAChF,QAAM,WAAW,YAAY,YAAY,GAAG;AAC5C,MAAI,CAAC,SAAU,QAAO;AAEtB,MAAI,SAAS,SAAS,QAAS,QAAO,aAAa,SAAS,KAAK;AACjE,MAAI,SAAS,SAAS,YAAa,QAAO,aAAa,SAAS,SAAS;AACzE,MAAI,SAAS,SAAS,MAAO,QAAO,OAAO,SAAS,OAAO,EAAE,EAAE,KAAK;AACpE,MAAI,SAAS,SAAS,QAAS,QAAO,OAAO,SAAS,SAAS,EAAE,EAAE,KAAK;AACxE,MAAI,SAAS,SAAS,gBAAgB;AACpC,WAAO,OAAO,SAAS,gBAAgB,EAAE,EAAE,KAAK;AAAA,EAClD;AAEA,SAAO;AACT;AAEO,SAAS,gBAAgB,YAAyB,KAAqB;AAC5E,QAAM,WAAW,YAAY,YAAY,GAAG;AAC5C,MAAI,UAAU,SAAS,OAAQ,QAAO;AACtC,QAAM,OAAO,SAAS;AACtB,SAAO,OAAO,MAAM,SAAS,EAAE,EAAE,KAAK;AACxC;AA4CO,SAAS,gBAAgB,YAAyB,KAAuB;AAC9E,QAAM,WAAW,YAAY,YAAY,GAAG;AAC5C,MAAI,UAAU,SAAS,kBAAkB,MAAM,QAAQ,SAAS,YAAY,GAAG;AAC7E,WAAO,SAAS,aACb,IAAI,CAAC,SAA4B,OAAO,KAAK,QAAQ,EAAE,EAAE,KAAK,CAAC,EAC/D,OAAO,OAAO;AAAA,EACnB;AAEA,MAAI,UAAU,SAAS,UAAU;AAC/B,UAAM,SAAS,SAAS;AACxB,UAAM,OAAO,OAAO,QAAQ,QAAQ,EAAE,EAAE,KAAK;AAC7C,WAAO,OAAO,CAAC,IAAI,IAAI,CAAC;AAAA,EAC1B;AAEA,SAAO,CAAC;AACV;AA+DO,SAAS,wBAAwB,aAAqB,OAAuB;AAClF,SAAO,YAAY,KAAK,KAAK,MAAM,KAAK;AAC1C;AAEO,SAAS,kBAAkB,MAAuB;AACvD,SAAO,4BAA4B,KAAK,IAAI;AAC9C;AAEO,SAAS,kBAAkB,QAAgB,aAA8B;AAC9E,QAAM,gBAAgB,OAAO,WAAW,KAAK,EAAE;AAC/C,MAAI,aAAa,SAAS,UAAU,GAAG;AACrC,WAAO,YAAY,WAAW,YAAY,aAAa;AAAA,EACzD;AACA,SAAO,yBAAyB,aAAa;AAC/C;;;ALjIA,SAAS,cAAc,OAAuC;AAC5D,MAAI,CAAC,SAAS,OAAO,UAAU,SAAU,QAAO;AAChD,QAAM,OAAO;AACb,SAAO,KAAK,KAAK,OAAO;AAC1B;AAEA,SAAS,eAAe,OAA+C;AACrE,MAAI,MAAM,QAAQ,KAAK,EAAG,QAAO,MAAM,CAAC;AACxC,SAAO;AACT;AAEA,SAAS,aAAa,QAAwB,KAAa;AACzD,SAAO,eAAe,OAAO,GAAG,CAAC;AACnC;AAEA,SAAS,0BACP,YACA,QACA;AACA,QAAM,iBAAiB,aAAa,QAAQ,WAAW,KAAK;AAC5D,QAAM,YAAY,WAAW,cAAc;AAG3C,MAAI,WAAW,SAAS,WAAY,QAAO,QAAQ,UAAU,QAAQ;AAErE,QAAM,cAAc,aAAa,QAAQ,QAAQ,KAAK;AACtD,QAAM,SAAS,WAAW,WAAW;AACrC,MAAI,QAAQ,SAAS,UAAU;AAC7B,UAAM,QAAQ,OAAO;AACrB,WAAO,OAAO,OAAO,QAAQ,EAAE,EAAE,KAAK,EAAE,YAAY,MAAM;AAAA,EAC5D;AACA,MAAI,QAAQ,SAAS,UAAU;AAC7B,UAAM,QAAQ,OAAO;AACrB,WAAO,OAAO,OAAO,QAAQ,EAAE,EAAE,KAAK,EAAE,YAAY,MAAM;AAAA,EAC5D;AAEA,SAAO;AACT;AAEA,SAAS,sBAAsB,MAAsB,QAAwB;AAC3E,QAAM,aAAa,OAAO;AAC1B,MAAI,MAAM,QAAQ,UAAU,GAAG;AAC7B,eAAW,SAAS,YAAY;AAC9B,YAAM,WAAW,qBAAqB,MAAM,KAAK;AACjD,UAAI,SAAU,QAAO;AAAA,IACvB;AAAA,EACF;AACA,MAAI,OAAO,eAAe,UAAU;AAClC,WAAO,qBAAqB,MAAM,UAAU;AAAA,EAC9C;AACA,SAAO,qBAAqB,IAAI;AAClC;AAEA,SAAS,mBAAmB,YAAyB,QAAwB;AAC3E,QAAM,SAA4C,CAAC;AACnD,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,MAAM,GAAG;AACjD,UAAM,QAAQ,eAAe,KAAK;AAClC,QAAI,CAAC,MAAO;AACZ,UAAM,OAAO,gBAAgB,YAAY,KAAK;AAC9C,WAAO,GAAG,IAAI,KAAK,SAAS,IAAI,OAAO,oBAAoB,YAAY,KAAK;AAAA,EAC9E;AACA,SAAO;AACT;AAEO,SAAS,kCAGd,OACA,MACA,SACwB;AACxB,QAAM,SAAS,MAAM,OAAO;AAC5B,QAAM,aAAa,SAAS,KAAK,UAAU,IAAI,KAAK,aAAa,CAAC;AAClE,QAAM,aAAa,aAAa,QAAQ,OAAO;AAC/C,QAAM,YAAY,aAAa,QAAQ,MAAM;AAC7C,QAAM,mBAAmB,aAAa,QAAQ,aAAa;AAC3D,QAAM,YAAY,aAAa,QAAQ,MAAM;AAC7C,QAAM,YAAY,aAAa,QAAQ,MAAM;AAE7C,QAAM,QAAQ,aAAa,oBAAoB,YAAY,UAAU,IAAI;AACzE,QAAM,OAAO,YACT,oBAAoB,YAAY,SAAS,EAAE,YAAY,IACvD,KAAK,GAAG,WAAW,KAAK,EAAE,EAAE,YAAY;AAC5C,QAAM,cAAc;AAAA,IAClB,mBAAmB,oBAAoB,YAAY,gBAAgB,IAAI;AAAA,IACvE;AAAA,EACF;AAEA,SAAO;AAAA,IACL,QAAQ,KAAK;AAAA,IACb;AAAA,IACA;AAAA,IACA;AAAA,IACA,MAAM,YAAY,gBAAgB,YAAY,SAAS,IAAI;AAAA,IAC3D,MAAM,YAAY,gBAAgB,YAAY,SAAS,IAAI,CAAC;AAAA,IAC5D,YAAY,sBAAsB,MAAM,MAAM;AAAA,IAC9C,WAAW,0BAA0B,YAAY,MAAM;AAAA,IACvD,SAAS,kBAAkB,KAAK,IAAI,SAAS,WAAW;AAAA,IACxD,YAAY,mBAAmB,YAAY,MAAM;AAAA,EACnD;AACF;AAEO,SAAS,+BAA+B,MAA8B;AAC3E,SAAO,QAAQ,KAAK,aAAa,KAAK,SAAS,KAAK,QAAQ,kBAAkB,KAAK,IAAI,CAAC;AAC1F;AAEO,SAAS,iCAEd,MAAyC;AACzC,SAAO;AAAA,IACL,MAAM,YAA+C;AACnD,YAAM,QAA0B,CAAC;AACjC,UAAI;AAEJ,SAAG;AACD,cAAM,WAAW,MAAM,KAAK,gBAAgB,EAAE,aAAa,OAAO,CAAC;AACnE,mBAAW,QAAQ,SAAS,WAAW,CAAC,GAAG;AACzC,gBAAM,OAAO,cAAc,IAAI;AAC/B,cAAI,KAAM,OAAM,KAAK,IAAI;AAAA,QAC3B;AAEA,iBAAS,SAAS,eAAe;AACjC,YAAI,CAAC,SAAS,SAAU;AAAA,MAC1B,SAAS;AAET,aAAO,MACJ;AAAA,QAAI,CAAC,SACJ,kCAAkC,KAAK,OAAO,MAAM;AAAA,UAClD,aAAa,KAAK;AAAA,QACpB,CAAC;AAAA,MACH,EACC,OAAO,8BAA8B,EACrC,KAAK,CAAC,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,IAAI,CAAC;AAAA,IAChD;AAAA,IAEA,MAAM,cAAc,MAAoD;AACtE,YAAM,QAAQ,MAAM,KAAK,UAAU;AACnC,YAAM,OAAO,MAAM,KAAK,CAAC,cAAc,UAAU,SAAS,IAAI;AAC9D,UAAI,CAAC,KAAM,QAAO;AAClB,aAAO;AAAA,QACL,GAAG;AAAA,QACH,QAAQ,MAAM,KAAK,cAAc,KAAK,MAAM;AAAA,MAC9C;AAAA,IACF;AAAA,EACF;AACF;AAEA,eAAe,2BAEb,OAAoE;AACpE,MAAI,CAAE,MAAM,qBAAqB,KAAK,EAAI,QAAO;AAEjD,QAAM,SAAS,MAAM,wBAAwB,KAAK;AAClD,QAAM,SAAS,mBAAmB,MAAM;AACxC,SAAO,iCAAiC;AAAA,IACtC;AAAA,IACA,cAAc,OAAO;AAAA,IACrB,aAAa,OAAO;AAAA,IACpB,iBAAiB,OAAO,EAAE,YAAY,IAAI,CAAC,MACzC,OAAO,YAAY,MAAM;AAAA,MACvB,gBAAgB,OAAO;AAAA,MACvB,WAAW,MAAM,OAAO,MAAM;AAAA,MAC9B,OAAO,MAAM,OAAO,MAAM,QACtB,CAAC,GAAG,MAAM,OAAO,MAAM,KAAK,IAC5B;AAAA,MACJ,mBAAmB,MAAM,OAAO,MAAM,mBAClC,CAAC,GAAG,MAAM,OAAO,MAAM,gBAAgB,IACvC;AAAA,MACJ,GAAI,cAAc,EAAE,cAAc,YAAY,IAAI,CAAC;AAAA,IACrD,CAAC;AAAA,IACH,eAAe,CAAC,WACd,sBAAsB,QAAwC,MAAM;AAAA,EACxE,CAAC;AACH;AAEA,IAAM,cAAc,MAAM,0BAA0B;AAEpD,eAAsB,yBAEpB,OAAoE;AACpE,QAAM,SAAS,MAAM,YAAY,KAAK;AACtC,MAAI,CAAC,OAAQ,QAAO,CAAC;AACrB,SAAO,OAAO,UAAU;AAC1B;AAEA,eAAsB,8BAEpB,OAAoE,MAAc;AAClF,QAAM,SAAS,MAAM,YAAY,KAAK;AACtC,MAAI,CAAC,OAAQ,QAAO;AACpB,SAAO,OAAO,cAAc,IAAI;AAClC;AAEA,SAAS,SAAS,OAAkD;AAClE,SAAO,QAAQ,SAAS,OAAO,UAAU,QAAQ;AACnD;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/notion/generic-source.ts","../../src/notion/blocks.ts","../../src/notion/client.ts","../../src/notion/config.ts","../../src/notion/media.ts","../../src/notion/property-mappers.ts"],"sourcesContent":["import { cache } from \"react\";\nimport { listBlockChildrenDeep, type NotionBlockClient } from \"./blocks\";\nimport { createNotionClient } from \"./client\";\nimport { getNotionConfigForModel, hasNotionModelConfig } from \"./config\";\nimport { coverImageUrlForPage } from \"./media\";\nimport {\n getCheckboxProperty,\n getDateProperty,\n getNumberProperty,\n getRichTextProperty,\n getSelectProperty,\n getTagsProperty,\n isValidPublicSlug,\n notionPageEditUrl,\n pickDescriptionFallback,\n} from \"./property-mappers\";\nimport type {\n NotionBlock,\n NotionFieldMap,\n NotionGenericContentModel,\n NotionPageLike,\n} from \"./types\";\n\ntype PropertyMap = Record<string, unknown>;\n\ntype DataSourceQueryResponse = {\n results?: unknown[];\n has_more?: boolean;\n next_cursor?: string | null;\n};\n\ntype QueryDataSourceInput = {\n startCursor?: string;\n};\n\nexport type GenericContentListItem = {\n pageId: string;\n slug: string;\n title: string;\n description: string;\n date: string;\n tags: string[];\n coverImage: string | null;\n published: boolean;\n editUrl: string | null;\n properties: Record<string, string | string[] | number | boolean>;\n};\n\nexport type GenericContentDetail = GenericContentListItem & {\n blocks: NotionBlock[];\n};\n\nexport type GenericContentSourceDeps<\n TFields extends NotionFieldMap = NotionFieldMap,\n> = {\n model: NotionGenericContentModel & { source: { fields: TFields } };\n dataSourceId: string;\n queryDataSource: (\n input?: QueryDataSourceInput\n ) => Promise<DataSourceQueryResponse>;\n getPageBlocks: (pageId: string) => Promise<NotionBlock[]>;\n editBaseUrl?: string;\n};\n\nfunction normalizePage(input: unknown): NotionPageLike | null {\n if (!input || typeof input !== \"object\") return null;\n const page = input as NotionPageLike;\n return page.id ? page : null;\n}\n\nfunction firstFieldName(value: string | readonly string[] | undefined) {\n if (Array.isArray(value)) return value[0];\n return value;\n}\n\nfunction getFieldName(fields: NotionFieldMap, key: string) {\n return firstFieldName(fields[key]);\n}\n\nfunction pickPublishedFlagForModel(\n properties: PropertyMap,\n fields: NotionFieldMap\n) {\n const publishedField = getFieldName(fields, \"published\") ?? \"Published\";\n const published = properties[publishedField] as\n | Record<string, unknown>\n | undefined;\n if (published?.type === \"checkbox\") return Boolean(published.checkbox);\n\n const statusField = getFieldName(fields, \"status\") ?? \"Status\";\n const status = properties[statusField] as Record<string, unknown> | undefined;\n if (status?.type === \"status\") {\n const value = status.status as { name?: string } | null | undefined;\n return String(value?.name ?? \"\").trim().toLowerCase() === \"published\";\n }\n if (status?.type === \"select\") {\n const value = status.select as { name?: string } | null | undefined;\n return String(value?.name ?? \"\").trim().toLowerCase() === \"published\";\n }\n\n return true;\n}\n\nfunction coverImageUrlForModel(page: NotionPageLike, fields: NotionFieldMap) {\n const coverField = fields.cover;\n if (Array.isArray(coverField)) {\n for (const field of coverField) {\n const imageUrl = coverImageUrlForPage(page, field);\n if (imageUrl) return imageUrl;\n }\n }\n if (typeof coverField === \"string\") {\n return coverImageUrlForPage(page, coverField);\n }\n return coverImageUrlForPage(page);\n}\n\nfunction mapExtraProperties(properties: PropertyMap, fields: NotionFieldMap) {\n const result: Record<string, string | string[] | number | boolean> = {};\n for (const [key, value] of Object.entries(fields)) {\n const field = firstFieldName(value);\n if (!field) continue;\n const property = properties[field] as Record<string, unknown> | undefined;\n if (!property?.type) {\n result[key] = getRichTextProperty(properties, field);\n continue;\n }\n\n if (property.type === \"multi_select\") {\n result[key] = getTagsProperty(properties, field);\n continue;\n }\n if (property.type === \"select\" || property.type === \"status\") {\n result[key] = getSelectProperty(properties, field);\n continue;\n }\n if (property.type === \"number\") {\n result[key] = getNumberProperty(properties, field);\n continue;\n }\n if (property.type === \"checkbox\") {\n result[key] = getCheckboxProperty(properties, field);\n continue;\n }\n\n result[key] = getRichTextProperty(properties, field);\n }\n return result;\n}\n\nexport function mapNotionPageToGenericContentItem<\n TFields extends NotionFieldMap,\n>(\n model: { id: string; source: { fields: TFields } },\n page: NotionPageLike,\n options?: { editBaseUrl?: string }\n): GenericContentListItem {\n const fields = model.source.fields;\n const properties = isRecord(page.properties) ? page.properties : {};\n const titleField = getFieldName(fields, \"title\");\n const slugField = getFieldName(fields, \"slug\");\n const descriptionField = getFieldName(fields, \"description\");\n const dateField = getFieldName(fields, \"date\");\n const tagsField = getFieldName(fields, \"tags\");\n\n const title = titleField ? getRichTextProperty(properties, titleField) : \"\";\n const slug = slugField\n ? getRichTextProperty(properties, slugField).toLowerCase()\n : page.id.replaceAll(\"-\", \"\").toLowerCase();\n const description = pickDescriptionFallback(\n descriptionField ? getRichTextProperty(properties, descriptionField) : \"\",\n title\n );\n\n return {\n pageId: page.id,\n slug,\n title,\n description,\n date: dateField ? getDateProperty(properties, dateField) : \"\",\n tags: tagsField ? getTagsProperty(properties, tagsField) : [],\n coverImage: coverImageUrlForModel(page, fields),\n published: pickPublishedFlagForModel(properties, fields),\n editUrl: notionPageEditUrl(page.id, options?.editBaseUrl),\n properties: mapExtraProperties(properties, fields),\n };\n}\n\nexport function isRenderableGenericContentItem(item: GenericContentListItem) {\n return Boolean(item.published && item.title && item.slug && isValidPublicSlug(item.slug));\n}\n\nexport function createGenericNotionContentSource<\n TFields extends NotionFieldMap,\n>(deps: GenericContentSourceDeps<TFields>) {\n return {\n async listItems(): Promise<GenericContentListItem[]> {\n const pages: NotionPageLike[] = [];\n let cursor: string | undefined;\n\n do {\n const response = await deps.queryDataSource({ startCursor: cursor });\n for (const item of response.results ?? []) {\n const page = normalizePage(item);\n if (page) pages.push(page);\n }\n\n cursor = response.next_cursor ?? undefined;\n if (!response.has_more) break;\n } while (cursor);\n\n return pages\n .map((page) =>\n mapNotionPageToGenericContentItem(deps.model, page, {\n editBaseUrl: deps.editBaseUrl,\n })\n )\n .filter(isRenderableGenericContentItem)\n .sort((a, b) => b.date.localeCompare(a.date));\n },\n\n async getItemBySlug(slug: string): Promise<GenericContentDetail | null> {\n const items = await this.listItems();\n const item = items.find((candidate) => candidate.slug === slug);\n if (!item) return null;\n return {\n ...item,\n blocks: await deps.getPageBlocks(item.pageId),\n };\n },\n };\n}\n\nasync function createDefaultGenericSource<\n TFields extends NotionFieldMap,\n>(model: NotionGenericContentModel & { source: { fields: TFields } }) {\n if (!(await hasNotionModelConfig(model))) return null;\n\n const config = await getNotionConfigForModel(model);\n const client = createNotionClient(config);\n return createGenericNotionContentSource({\n model,\n dataSourceId: config.dataSourceId,\n editBaseUrl: config.editBaseUrl,\n queryDataSource: async ({ startCursor } = {}) =>\n client.dataSources.query({\n data_source_id: config.dataSourceId,\n page_size: model.source.query.pageSize,\n sorts: model.source.query.sorts\n ? [...model.source.query.sorts]\n : undefined,\n filter_properties: model.source.query.filterProperties\n ? [...model.source.query.filterProperties]\n : undefined,\n ...(startCursor ? { start_cursor: startCursor } : {}),\n }),\n getPageBlocks: (pageId) =>\n listBlockChildrenDeep(client as unknown as NotionBlockClient, pageId),\n });\n}\n\nconst sourceCache = cache(createDefaultGenericSource);\n\nexport async function listGenericNotionContent<\n TFields extends NotionFieldMap,\n>(model: NotionGenericContentModel & { source: { fields: TFields } }) {\n const source = await sourceCache(model);\n if (!source) return [];\n return source.listItems();\n}\n\nexport async function getGenericNotionContentBySlug<\n TFields extends NotionFieldMap,\n>(model: NotionGenericContentModel & { source: { fields: TFields } }, slug: string) {\n const source = await sourceCache(model);\n if (!source) return null;\n return source.getItemBySlug(slug);\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return Boolean(value && typeof value === \"object\");\n}\n","import type { NotionBlock } from \"./types\";\n\ntype BlockChildrenListResponse = {\n results?: unknown[];\n has_more?: boolean;\n next_cursor?: string | null;\n};\n\nexport type NotionBlockClient = {\n blocks: {\n children: {\n list: (args: {\n block_id: string;\n page_size?: number;\n start_cursor?: string;\n }) => Promise<BlockChildrenListResponse>;\n };\n };\n};\n\nfunction normalizeBlock(input: unknown): NotionBlock | null {\n if (!input || typeof input !== \"object\") return null;\n const block = input as NotionBlock;\n return block.id && block.type ? block : null;\n}\n\nexport async function listBlockChildren(\n client: NotionBlockClient,\n blockId: string\n): Promise<NotionBlock[]> {\n const results: NotionBlock[] = [];\n let cursor: string | undefined;\n\n do {\n const response = await client.blocks.children.list({\n block_id: blockId,\n page_size: 100,\n ...(cursor ? { start_cursor: cursor } : {}),\n });\n\n for (const item of response.results ?? []) {\n const block = normalizeBlock(item);\n if (block) results.push(block);\n }\n\n cursor = response.next_cursor ?? undefined;\n if (!response.has_more) break;\n } while (cursor);\n\n return results;\n}\n\nexport async function listBlockChildrenDeep(\n client: NotionBlockClient,\n blockId: string,\n options?: { maxDepth?: number }\n): Promise<NotionBlock[]> {\n const maxDepth = options?.maxDepth ?? 6;\n\n async function visit(id: string, depth: number): Promise<NotionBlock[]> {\n const children = await listBlockChildren(client, id);\n if (depth >= maxDepth) return children;\n\n return Promise.all(\n children.map(async (block) => {\n if (!block.has_children) return block;\n return {\n ...block,\n children: await visit(block.id, depth + 1),\n };\n })\n );\n }\n\n return visit(blockId, 0);\n}\n","import { Client } from \"@notionhq/client\";\nimport type { NotionClientConfig } from \"./config\";\n\nexport function createNotionClient(config: NotionClientConfig) {\n return new Client({\n auth: config.token,\n baseUrl: config.apiBaseUrl,\n notionVersion: \"2026-03-11\",\n });\n}\n","import type { NotionContentModelLike } from \"./types\";\n\ntype NotionEnv = {\n NOTION_TOKEN?: string;\n NOTION_DATA_SOURCE_ID?: string;\n NOTION_API_BASE_URL?: string;\n NOTION_EDIT_BASE_URL?: string;\n NOTION_WEBHOOK_VERIFICATION_TOKEN?: string;\n [key: string]: string | undefined;\n};\n\nexport type NotionClientConfig = {\n token: string;\n apiBaseUrl?: string;\n};\n\nexport type NotionConfig = {\n token: string;\n dataSourceId: string;\n apiBaseUrl?: string;\n editBaseUrl?: string;\n webhookVerificationToken?: string;\n};\n\nfunction readProcessEnv(): NotionEnv {\n const env: NotionEnv = {\n NOTION_TOKEN: process.env.NOTION_TOKEN,\n NOTION_DATA_SOURCE_ID: process.env.NOTION_DATA_SOURCE_ID,\n NOTION_API_BASE_URL: process.env.NOTION_API_BASE_URL,\n NOTION_EDIT_BASE_URL: process.env.NOTION_EDIT_BASE_URL,\n NOTION_WEBHOOK_VERIFICATION_TOKEN:\n process.env.NOTION_WEBHOOK_VERIFICATION_TOKEN,\n };\n\n for (const [key, value] of Object.entries(process.env)) {\n if (key.startsWith(\"NOTION_\") && typeof value === \"string\") {\n env[key] = value;\n }\n }\n\n return env;\n}\n\nasync function readWorkerEnv(): Promise<NotionEnv> {\n try {\n const mod = (await import(\n /* webpackIgnore: true */ \"cloudflare:workers\"\n )) as unknown as { env?: Record<string, unknown> };\n const env: NotionEnv = {};\n for (const [key, value] of Object.entries(mod.env ?? {})) {\n if (key.startsWith(\"NOTION_\") && typeof value === \"string\") {\n env[key] = value;\n }\n }\n return env;\n } catch {\n return {};\n }\n}\n\nfunction readString(source: NotionEnv, name: string): string | undefined {\n const value = String(source[name] ?? \"\").trim();\n return value || undefined;\n}\n\nfunction mergeEnv(...sources: NotionEnv[]): NotionEnv {\n const merged: NotionEnv = {};\n\n for (const source of sources) {\n for (const name of Object.keys(source)) {\n if (!name.startsWith(\"NOTION_\")) continue;\n const value = readString(source, name);\n if (value) merged[name] = value;\n }\n }\n\n return merged;\n}\n\nasync function readEnv(): Promise<NotionEnv> {\n const processEnv = readProcessEnv();\n return mergeEnv(await readWorkerEnv(), processEnv);\n}\n\nfunction readRequired(\n source: NotionEnv,\n name: string\n): string {\n const value = readString(source, name);\n if (!value) {\n throw new Error(`Missing required Notion env: ${name}`);\n }\n return value;\n}\n\nexport function getNotionEditBaseUrl(): string {\n return readString(readProcessEnv(), \"NOTION_EDIT_BASE_URL\") ?? \"https://www.notion.so\";\n}\n\nexport async function hasNotionConfig(): Promise<boolean> {\n const env = await readEnv();\n return Boolean(\n readString(env, \"NOTION_TOKEN\") && readString(env, \"NOTION_DATA_SOURCE_ID\")\n );\n}\n\nexport async function hasNotionModelConfig(\n model: NotionContentModelLike\n): Promise<boolean> {\n const env = await readEnv();\n return Boolean(\n readString(env, \"NOTION_TOKEN\") &&\n (readString(env, model.source.dataSourceEnv) ||\n model.source.defaultDataSourceId)\n );\n}\n\nexport async function getNotionClientConfig(): Promise<NotionClientConfig> {\n const env = await readEnv();\n return {\n token: readRequired(env, \"NOTION_TOKEN\"),\n apiBaseUrl: readString(env, \"NOTION_API_BASE_URL\"),\n };\n}\n\nexport async function getNotionConfig(): Promise<NotionConfig> {\n const env = await readEnv();\n return {\n token: readRequired(env, \"NOTION_TOKEN\"),\n dataSourceId: readRequired(env, \"NOTION_DATA_SOURCE_ID\"),\n apiBaseUrl: readString(env, \"NOTION_API_BASE_URL\"),\n editBaseUrl: readString(env, \"NOTION_EDIT_BASE_URL\"),\n webhookVerificationToken: readString(\n env,\n \"NOTION_WEBHOOK_VERIFICATION_TOKEN\"\n ),\n };\n}\n\nexport async function getNotionWebhookVerificationToken(): Promise<\n string | undefined\n> {\n const env = await readEnv();\n return readString(env, \"NOTION_WEBHOOK_VERIFICATION_TOKEN\");\n}\n\nexport async function getNotionConfigForModel(\n model: NotionContentModelLike\n): Promise<NotionConfig> {\n const env = await readEnv();\n const dataSourceId =\n readString(env, model.source.dataSourceEnv) ??\n model.source.defaultDataSourceId;\n if (!dataSourceId) {\n throw new Error(`Missing required Notion env: ${model.source.dataSourceEnv}`);\n }\n\n return {\n token: readRequired(env, model.source.tokenEnv),\n dataSourceId,\n apiBaseUrl: readString(env, \"NOTION_API_BASE_URL\"),\n editBaseUrl: readString(env, \"NOTION_EDIT_BASE_URL\"),\n webhookVerificationToken: readString(\n env,\n \"NOTION_WEBHOOK_VERIFICATION_TOKEN\"\n ),\n };\n}\n","import type { NotionBlock, NotionFileSource, NotionPageLike } from \"./types\";\n\ntype FileLike = {\n type?: string;\n external?: { url?: string };\n file?: { url?: string; expiry_time?: string };\n name?: string;\n};\n\nfunction stripLeadingSlash(value: string) {\n return value.startsWith(\"/\") ? value.slice(1) : value;\n}\n\nfunction encodePathPart(value: string) {\n return encodeURIComponent(stripLeadingSlash(value));\n}\n\nfunction appendVersion(path: string, version?: string) {\n const value = String(version ?? \"\").trim();\n if (!value) return path;\n return `${path}?${new URLSearchParams({ v: value })}`;\n}\n\nfunction blockVersion(block: NotionBlock): string | undefined {\n return typeof block.last_edited_time === \"string\"\n ? block.last_edited_time\n : undefined;\n}\n\nexport function notionPageCoverMediaPath(pageId: string): string {\n return `/api/notion/media/page/${encodePathPart(pageId)}/cover`;\n}\n\nexport function notionPagePropertyMediaPath(\n pageId: string,\n propertyName: string\n): string {\n return `/api/notion/media/page/${encodePathPart(pageId)}/property/${encodePathPart(propertyName)}`;\n}\n\nexport function notionBlockMediaPath(blockId: string): string {\n return `/api/notion/media/block/${encodePathPart(blockId)}`;\n}\n\nexport function normalizeNotionFileSource(input: unknown): NotionFileSource | null {\n const file = input as FileLike | null | undefined;\n if (!file || typeof file !== \"object\") return null;\n\n if (file.type === \"external\") {\n const url = String(file.external?.url ?? \"\").trim();\n return url ? { type: \"external\", url } : null;\n }\n\n if (file.type === \"file\") {\n const url = String(file.file?.url ?? \"\").trim();\n if (!url) return null;\n return {\n type: \"file\",\n url,\n expiryTime: String(file.file?.expiry_time ?? \"\").trim() || null,\n };\n }\n\n return null;\n}\n\nexport function resolveNotionFileUrl(input: unknown): string | null {\n return normalizeNotionFileSource(input)?.url ?? null;\n}\n\nexport function isNotionHostedFile(input: unknown): boolean {\n return normalizeNotionFileSource(input)?.type === \"file\";\n}\n\nexport function pickFirstFilesPropertyValue(property: unknown): unknown | null {\n const value = property as { type?: string; files?: unknown[] } | null | undefined;\n if (!value || value.type !== \"files\" || !Array.isArray(value.files)) {\n return null;\n }\n return value.files[0] ?? null;\n}\n\nexport function pickPageCoverFile(page: NotionPageLike): unknown | null {\n return page.cover ?? null;\n}\n\nexport function coverImageUrlForPage(\n page: NotionPageLike,\n coverPropertyName = \"Cover\"\n): string | null {\n const propertyFile = pickFirstFilesPropertyValue(\n page.properties?.[coverPropertyName]\n );\n const propertySource = normalizeNotionFileSource(propertyFile);\n if (propertySource) {\n return appendVersion(\n notionPagePropertyMediaPath(page.id, coverPropertyName),\n page.last_edited_time\n );\n }\n\n const coverSource = normalizeNotionFileSource(pickPageCoverFile(page));\n if (coverSource) {\n return appendVersion(notionPageCoverMediaPath(page.id), page.last_edited_time);\n }\n\n return null;\n}\n\nexport function fileObjectForMediaBlock(block: NotionBlock): unknown | null {\n const typed = block[block.type] as Record<string, unknown> | undefined;\n if (!typed || typeof typed !== \"object\") return null;\n\n if (\n block.type === \"image\" ||\n block.type === \"video\" ||\n block.type === \"file\" ||\n block.type === \"pdf\" ||\n block.type === \"audio\"\n ) {\n return typed;\n }\n\n return null;\n}\n\nexport function mediaUrlForBlock(block: NotionBlock): string | null {\n const source = normalizeNotionFileSource(fileObjectForMediaBlock(block));\n if (!source) return null;\n if (source.type === \"external\" && block.type !== \"image\") {\n return source.url;\n }\n return appendVersion(notionBlockMediaPath(block.id), blockVersion(block));\n}\n\nexport function firstImageUrlFromBlocks(blocks: NotionBlock[]): string | null {\n for (const block of blocks) {\n if (block.type === \"image\") {\n const imageUrl = mediaUrlForBlock(block);\n if (imageUrl) return imageUrl;\n }\n\n const nested = block.children?.length\n ? firstImageUrlFromBlocks(block.children)\n : null;\n if (nested) return nested;\n }\n\n return null;\n}\n\nexport function publicMediaBlockForApi(block: NotionBlock): NotionBlock {\n const value = block[block.type];\n const source = normalizeNotionFileSource(value);\n const children = block.children?.map(publicMediaBlockForApi);\n\n if (!source) {\n return children ? { ...block, children } : { ...block };\n }\n\n const path = appendVersion(notionBlockMediaPath(block.id), blockVersion(block));\n const publicValue =\n source.type === \"external\"\n ? block.type === \"image\"\n ? {\n ...(value as Record<string, unknown>),\n external: { url: path },\n }\n : value\n : {\n ...(value as Record<string, unknown>),\n file: {\n url: path,\n expiry_time: null,\n },\n };\n\n return {\n ...block,\n [block.type]: publicValue,\n ...(children ? { children } : {}),\n };\n}\n\nexport function gatedMediaBlockForApi(\n block: NotionBlock,\n options?: { accessUrlForBlock?: (block: NotionBlock) => string | null }\n): NotionBlock {\n const value = block[block.type];\n const source = normalizeNotionFileSource(value);\n const children = block.children?.map((child) =>\n gatedMediaBlockForApi(child, options)\n );\n\n if (block.type !== \"video\" || !source) {\n const publicBlock = publicMediaBlockForApi(block);\n return children ? { ...publicBlock, children } : publicBlock;\n }\n\n const gatedValue: Record<string, unknown> = {\n ...(value as Record<string, unknown>),\n gated: true,\n access_url: options?.accessUrlForBlock?.(block) ?? null,\n };\n\n if (source.type === \"external\") {\n gatedValue.external = { url: null };\n } else {\n gatedValue.file = {\n url: null,\n expiry_time: null,\n };\n }\n\n return {\n ...block,\n video: gatedValue,\n ...(children ? { children } : {}),\n };\n}\n\nexport function isDirectVideoUrl(url: string): boolean {\n try {\n const parsed = new URL(url);\n return /\\.(mp4|webm|mov|m4v)(?:$|\\?)/i.test(parsed.pathname);\n } catch {\n return false;\n }\n}\n\nexport function videoEmbedUrl(url: string): string | null {\n try {\n const parsed = new URL(url);\n const host = parsed.hostname.replace(/^www\\./, \"\");\n\n if (host === \"youtube.com\" || host === \"m.youtube.com\") {\n const id = parsed.searchParams.get(\"v\");\n return id ? `https://www.youtube.com/embed/${encodeURIComponent(id)}` : null;\n }\n\n if (host === \"youtu.be\") {\n const id = parsed.pathname.split(\"/\").filter(Boolean)[0];\n return id ? `https://www.youtube.com/embed/${encodeURIComponent(id)}` : null;\n }\n\n if (host === \"youtube-nocookie.com\") {\n return parsed.toString();\n }\n\n if (host === \"vimeo.com\") {\n const id = parsed.pathname.split(\"/\").filter(Boolean)[0];\n return id ? `https://player.vimeo.com/video/${encodeURIComponent(id)}` : null;\n }\n\n if (host === \"player.vimeo.com\") {\n return parsed.toString();\n }\n } catch {\n return null;\n }\n\n return null;\n}\n","type PropertyMap = Record<string, unknown>;\n\ntype TextPart = {\n plain_text?: string;\n};\n\nexport function isRecord(value: unknown): value is Record<string, unknown> {\n return Boolean(value && typeof value === \"object\");\n}\n\nfunction getPlainText(parts: unknown): string {\n if (!Array.isArray(parts)) return \"\";\n return parts\n .map((part: TextPart) => part.plain_text ?? \"\")\n .join(\"\")\n .trim();\n}\n\nfunction getProperty(properties: PropertyMap, key: string) {\n return properties[key] as Record<string, unknown> | undefined;\n}\n\nfunction firstPropertyOfType(properties: PropertyMap, type: string) {\n return Object.values(properties).find(\n (property): property is Record<string, unknown> =>\n isRecord(property) && property.type === type\n );\n}\n\nexport function getFirstTitleProperty(properties: PropertyMap): string {\n const property = firstPropertyOfType(properties, \"title\");\n return property ? getPlainText(property.title) : \"\";\n}\n\nexport function getRichTextProperty(properties: PropertyMap, key: string): string {\n const property = getProperty(properties, key);\n if (!property) return \"\";\n\n if (property.type === \"title\") return getPlainText(property.title);\n if (property.type === \"rich_text\") return getPlainText(property.rich_text);\n if (property.type === \"url\") return String(property.url ?? \"\").trim();\n if (property.type === \"email\") return String(property.email ?? \"\").trim();\n if (property.type === \"phone_number\") {\n return String(property.phone_number ?? \"\").trim();\n }\n\n return \"\";\n}\n\nexport function getDateProperty(properties: PropertyMap, key: string): string {\n const property = getProperty(properties, key);\n if (property?.type !== \"date\") return \"\";\n const date = property.date as { start?: string } | null | undefined;\n return String(date?.start ?? \"\").trim();\n}\n\nexport function getFirstDateProperty(properties: PropertyMap): string {\n const property = firstPropertyOfType(properties, \"date\");\n if (!property) return \"\";\n const date = property.date as { start?: string } | null | undefined;\n return String(date?.start ?? \"\").trim();\n}\n\nexport function getSelectProperty(properties: PropertyMap, key: string): string {\n const property = getProperty(properties, key);\n if (property?.type !== \"select\") return \"\";\n const select = property.select as { name?: string } | null | undefined;\n return String(select?.name ?? \"\").trim();\n}\n\nexport function getCheckboxProperty(properties: PropertyMap, key: string): boolean {\n const property = getProperty(properties, key);\n if (property?.type !== \"checkbox\") return false;\n return Boolean(property.checkbox);\n}\n\nexport function getNumberProperty(\n properties: PropertyMap,\n key: string,\n fallback = 0\n): number {\n const property = getProperty(properties, key);\n if (property?.type !== \"number\") return fallback;\n const value = Number(property.number);\n return Number.isFinite(value) ? value : fallback;\n}\n\nexport function getRelationPageIds(properties: PropertyMap, key: string): string[] {\n const property = getProperty(properties, key);\n if (property?.type !== \"relation\" || !Array.isArray(property.relation)) {\n return [];\n }\n\n return property.relation\n .map((item: { id?: string }) => String(item.id ?? \"\").trim())\n .filter(Boolean);\n}\n\nexport function getTagsProperty(properties: PropertyMap, key: string): string[] {\n const property = getProperty(properties, key);\n if (property?.type === \"multi_select\" && Array.isArray(property.multi_select)) {\n return property.multi_select\n .map((item: { name?: string }) => String(item.name ?? \"\").trim())\n .filter(Boolean);\n }\n\n if (property?.type === \"select\") {\n const select = property.select as { name?: string } | null | undefined;\n const name = String(select?.name ?? \"\").trim();\n return name ? [name] : [];\n }\n\n return [];\n}\n\nexport function getFirstTagsProperty(properties: PropertyMap): string[] {\n const multiSelect = firstPropertyOfType(properties, \"multi_select\");\n if (multiSelect && Array.isArray(multiSelect.multi_select)) {\n return multiSelect.multi_select\n .map((item: { name?: string }) => String(item.name ?? \"\").trim())\n .filter(Boolean);\n }\n\n const select = firstPropertyOfType(properties, \"select\");\n const name = String((select?.select as { name?: string } | null)?.name ?? \"\").trim();\n return name ? [name] : [];\n}\n\nexport function getAuthorProperty(properties: PropertyMap, key: string): string {\n const property = getProperty(properties, key);\n if (!property) return \"\";\n\n if (property.type === \"people\" && Array.isArray(property.people)) {\n return property.people\n .map((person: { name?: string; person?: { email?: string } }) =>\n String(person.name ?? person.person?.email ?? \"\").trim()\n )\n .filter(Boolean)\n .join(\", \");\n }\n\n return getRichTextProperty(properties, key);\n}\n\nexport function getFirstPeopleProperty(properties: PropertyMap): string {\n const property = firstPropertyOfType(properties, \"people\");\n if (!property || !Array.isArray(property.people)) return \"\";\n\n return property.people\n .map((person: { name?: string; person?: { email?: string } }) =>\n String(person.name ?? person.person?.email ?? \"\").trim()\n )\n .filter(Boolean)\n .join(\", \");\n}\n\nexport function pickPublishedFlag(properties: PropertyMap): boolean {\n const published = getProperty(properties, \"Published\");\n if (published?.type === \"checkbox\") {\n return Boolean(published.checkbox);\n }\n\n const status = getProperty(properties, \"Status\");\n if (status?.type === \"status\") {\n const statusValue = status.status as { name?: string } | null | undefined;\n return String(statusValue?.name ?? \"\").trim().toLowerCase() === \"published\";\n }\n\n if (status?.type === \"select\") {\n const statusValue = status.select as { name?: string } | null | undefined;\n return String(statusValue?.name ?? \"\").trim().toLowerCase() === \"published\";\n }\n\n return false;\n}\n\nexport function pickDescriptionFallback(description: string, title: string): string {\n return description.trim() || title.trim();\n}\n\nexport function isValidPublicSlug(slug: string): boolean {\n return /^[a-z0-9][a-z0-9-]{0,79}$/.test(slug);\n}\n\nexport function notionPageEditUrl(pageId: string, editBaseUrl?: string): string {\n const compactPageId = pageId.replaceAll(\"-\", \"\");\n if (editBaseUrl?.includes(\"{pageId}\")) {\n return editBaseUrl.replaceAll(\"{pageId}\", compactPageId);\n }\n return `https://www.notion.so/${compactPageId}`;\n}\n\n/**\n * Normalize a Notion page id (with or without dashes) to a compact lowercase\n * string. Used as a stable identifier in URLs and cache keys.\n */\nexport function compactNotionId(id: string): string {\n return id.replaceAll(\"-\", \"\").toLowerCase();\n}\n"],"mappings":"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","names":[]}
|
package/dist/notion/index.js
CHANGED
|
@@ -733,8 +733,28 @@ function mapExtraProperties(properties, fields) {
|
|
|
733
733
|
for (const [key, value] of Object.entries(fields)) {
|
|
734
734
|
const field = firstFieldName(value);
|
|
735
735
|
if (!field) continue;
|
|
736
|
-
const
|
|
737
|
-
|
|
736
|
+
const property = properties[field];
|
|
737
|
+
if (!property?.type) {
|
|
738
|
+
result[key] = getRichTextProperty(properties, field);
|
|
739
|
+
continue;
|
|
740
|
+
}
|
|
741
|
+
if (property.type === "multi_select") {
|
|
742
|
+
result[key] = getTagsProperty(properties, field);
|
|
743
|
+
continue;
|
|
744
|
+
}
|
|
745
|
+
if (property.type === "select" || property.type === "status") {
|
|
746
|
+
result[key] = getSelectProperty(properties, field);
|
|
747
|
+
continue;
|
|
748
|
+
}
|
|
749
|
+
if (property.type === "number") {
|
|
750
|
+
result[key] = getNumberProperty(properties, field);
|
|
751
|
+
continue;
|
|
752
|
+
}
|
|
753
|
+
if (property.type === "checkbox") {
|
|
754
|
+
result[key] = getCheckboxProperty(properties, field);
|
|
755
|
+
continue;
|
|
756
|
+
}
|
|
757
|
+
result[key] = getRichTextProperty(properties, field);
|
|
738
758
|
}
|
|
739
759
|
return result;
|
|
740
760
|
}
|
package/dist/notion/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/notion/client.ts","../../src/notion/config.ts","../../src/notion/blocks.ts","../../src/notion/block-text.ts","../../src/notion/content-cache.ts","../../src/notion/media.ts","../../src/notion/generic-source.ts","../../src/notion/property-mappers.ts","../../src/notion/webhook.ts"],"sourcesContent":["import { Client } from \"@notionhq/client\";\nimport type { NotionClientConfig } from \"./config\";\n\nexport function createNotionClient(config: NotionClientConfig) {\n return new Client({\n auth: config.token,\n baseUrl: config.apiBaseUrl,\n notionVersion: \"2026-03-11\",\n });\n}\n","import type { NotionContentModelLike } from \"./types\";\n\ntype NotionEnv = {\n NOTION_TOKEN?: string;\n NOTION_DATA_SOURCE_ID?: string;\n NOTION_API_BASE_URL?: string;\n NOTION_EDIT_BASE_URL?: string;\n NOTION_WEBHOOK_VERIFICATION_TOKEN?: string;\n [key: string]: string | undefined;\n};\n\nexport type NotionClientConfig = {\n token: string;\n apiBaseUrl?: string;\n};\n\nexport type NotionConfig = {\n token: string;\n dataSourceId: string;\n apiBaseUrl?: string;\n editBaseUrl?: string;\n webhookVerificationToken?: string;\n};\n\nfunction readProcessEnv(): NotionEnv {\n const env: NotionEnv = {\n NOTION_TOKEN: process.env.NOTION_TOKEN,\n NOTION_DATA_SOURCE_ID: process.env.NOTION_DATA_SOURCE_ID,\n NOTION_API_BASE_URL: process.env.NOTION_API_BASE_URL,\n NOTION_EDIT_BASE_URL: process.env.NOTION_EDIT_BASE_URL,\n NOTION_WEBHOOK_VERIFICATION_TOKEN:\n process.env.NOTION_WEBHOOK_VERIFICATION_TOKEN,\n };\n\n for (const [key, value] of Object.entries(process.env)) {\n if (key.startsWith(\"NOTION_\") && typeof value === \"string\") {\n env[key] = value;\n }\n }\n\n return env;\n}\n\nasync function readWorkerEnv(): Promise<NotionEnv> {\n try {\n const mod = (await import(\n /* webpackIgnore: true */ \"cloudflare:workers\"\n )) as unknown as { env?: Record<string, unknown> };\n const env: NotionEnv = {};\n for (const [key, value] of Object.entries(mod.env ?? {})) {\n if (key.startsWith(\"NOTION_\") && typeof value === \"string\") {\n env[key] = value;\n }\n }\n return env;\n } catch {\n return {};\n }\n}\n\nfunction readString(source: NotionEnv, name: string): string | undefined {\n const value = String(source[name] ?? \"\").trim();\n return value || undefined;\n}\n\nfunction mergeEnv(...sources: NotionEnv[]): NotionEnv {\n const merged: NotionEnv = {};\n\n for (const source of sources) {\n for (const name of Object.keys(source)) {\n if (!name.startsWith(\"NOTION_\")) continue;\n const value = readString(source, name);\n if (value) merged[name] = value;\n }\n }\n\n return merged;\n}\n\nasync function readEnv(): Promise<NotionEnv> {\n const processEnv = readProcessEnv();\n return mergeEnv(await readWorkerEnv(), processEnv);\n}\n\nfunction readRequired(\n source: NotionEnv,\n name: string\n): string {\n const value = readString(source, name);\n if (!value) {\n throw new Error(`Missing required Notion env: ${name}`);\n }\n return value;\n}\n\nexport function getNotionEditBaseUrl(): string {\n return readString(readProcessEnv(), \"NOTION_EDIT_BASE_URL\") ?? \"https://www.notion.so\";\n}\n\nexport async function hasNotionConfig(): Promise<boolean> {\n const env = await readEnv();\n return Boolean(\n readString(env, \"NOTION_TOKEN\") && readString(env, \"NOTION_DATA_SOURCE_ID\")\n );\n}\n\nexport async function hasNotionModelConfig(\n model: NotionContentModelLike\n): Promise<boolean> {\n const env = await readEnv();\n return Boolean(\n readString(env, \"NOTION_TOKEN\") &&\n (readString(env, model.source.dataSourceEnv) ||\n model.source.defaultDataSourceId)\n );\n}\n\nexport async function getNotionClientConfig(): Promise<NotionClientConfig> {\n const env = await readEnv();\n return {\n token: readRequired(env, \"NOTION_TOKEN\"),\n apiBaseUrl: readString(env, \"NOTION_API_BASE_URL\"),\n };\n}\n\nexport async function getNotionConfig(): Promise<NotionConfig> {\n const env = await readEnv();\n return {\n token: readRequired(env, \"NOTION_TOKEN\"),\n dataSourceId: readRequired(env, \"NOTION_DATA_SOURCE_ID\"),\n apiBaseUrl: readString(env, \"NOTION_API_BASE_URL\"),\n editBaseUrl: readString(env, \"NOTION_EDIT_BASE_URL\"),\n webhookVerificationToken: readString(\n env,\n \"NOTION_WEBHOOK_VERIFICATION_TOKEN\"\n ),\n };\n}\n\nexport async function getNotionWebhookVerificationToken(): Promise<\n string | undefined\n> {\n const env = await readEnv();\n return readString(env, \"NOTION_WEBHOOK_VERIFICATION_TOKEN\");\n}\n\nexport async function getNotionConfigForModel(\n model: NotionContentModelLike\n): Promise<NotionConfig> {\n const env = await readEnv();\n const dataSourceId =\n readString(env, model.source.dataSourceEnv) ??\n model.source.defaultDataSourceId;\n if (!dataSourceId) {\n throw new Error(`Missing required Notion env: ${model.source.dataSourceEnv}`);\n }\n\n return {\n token: readRequired(env, model.source.tokenEnv),\n dataSourceId,\n apiBaseUrl: readString(env, \"NOTION_API_BASE_URL\"),\n editBaseUrl: readString(env, \"NOTION_EDIT_BASE_URL\"),\n webhookVerificationToken: readString(\n env,\n \"NOTION_WEBHOOK_VERIFICATION_TOKEN\"\n ),\n };\n}\n","import type { NotionBlock } from \"./types\";\n\ntype BlockChildrenListResponse = {\n results?: unknown[];\n has_more?: boolean;\n next_cursor?: string | null;\n};\n\nexport type NotionBlockClient = {\n blocks: {\n children: {\n list: (args: {\n block_id: string;\n page_size?: number;\n start_cursor?: string;\n }) => Promise<BlockChildrenListResponse>;\n };\n };\n};\n\nfunction normalizeBlock(input: unknown): NotionBlock | null {\n if (!input || typeof input !== \"object\") return null;\n const block = input as NotionBlock;\n return block.id && block.type ? block : null;\n}\n\nexport async function listBlockChildren(\n client: NotionBlockClient,\n blockId: string\n): Promise<NotionBlock[]> {\n const results: NotionBlock[] = [];\n let cursor: string | undefined;\n\n do {\n const response = await client.blocks.children.list({\n block_id: blockId,\n page_size: 100,\n ...(cursor ? { start_cursor: cursor } : {}),\n });\n\n for (const item of response.results ?? []) {\n const block = normalizeBlock(item);\n if (block) results.push(block);\n }\n\n cursor = response.next_cursor ?? undefined;\n if (!response.has_more) break;\n } while (cursor);\n\n return results;\n}\n\nexport async function listBlockChildrenDeep(\n client: NotionBlockClient,\n blockId: string,\n options?: { maxDepth?: number }\n): Promise<NotionBlock[]> {\n const maxDepth = options?.maxDepth ?? 6;\n\n async function visit(id: string, depth: number): Promise<NotionBlock[]> {\n const children = await listBlockChildren(client, id);\n if (depth >= maxDepth) return children;\n\n return Promise.all(\n children.map(async (block) => {\n if (!block.has_children) return block;\n return {\n ...block,\n children: await visit(block.id, depth + 1),\n };\n })\n );\n }\n\n return visit(blockId, 0);\n}\n","import type { NotionBlock, NotionRichTextPart } from \"./types\";\n\ntype RichTextContainer = {\n rich_text?: NotionRichTextPart[];\n caption?: NotionRichTextPart[];\n title?: NotionRichTextPart[];\n cells?: NotionRichTextPart[][];\n expression?: string;\n};\n\nfunction typedValue(block: NotionBlock): RichTextContainer {\n return (block[block.type] ?? {}) as RichTextContainer;\n}\n\nfunction richTextPlainText(parts: unknown) {\n if (!Array.isArray(parts)) return \"\";\n return parts\n .map((part: NotionRichTextPart) =>\n part.type === \"equation\"\n ? part.equation?.expression ?? \"\"\n : part.plain_text ?? part.text?.content ?? \"\"\n )\n .join(\"\");\n}\n\nfunction blockPlainText(block: NotionBlock) {\n const value = typedValue(block);\n const text = [\n richTextPlainText(value.rich_text),\n richTextPlainText(value.caption),\n richTextPlainText(value.title),\n value.expression ?? \"\",\n ...(value.cells ?? []).map(richTextPlainText),\n ];\n\n return text.filter(Boolean).join(\" \");\n}\n\nexport function flattenNotionBlockText(blocks: readonly NotionBlock[]): string {\n const parts: string[] = [];\n\n function visit(block: NotionBlock) {\n const text = blockPlainText(block).trim();\n if (text) parts.push(text);\n for (const child of block.children ?? []) {\n visit(child);\n }\n }\n\n for (const block of blocks) visit(block);\n\n return parts\n .join(\" \")\n .normalize(\"NFKC\")\n .replace(/\\s+/g, \" \")\n .trim();\n}\n","import type { KeyValueCacheAdapter } from \"../platform/runtime\";\nimport type { NotionBlock } from \"./types\";\n\nconst CACHE_VERSION = \"v2\";\nexport const NOTION_LIST_CACHE_TTL_SECONDS = 300;\nexport const NOTION_BLOCKS_CACHE_TTL_SECONDS = 60 * 60 * 24;\nconst CONTENT_CACHE_READ_TTL_SECONDS = 60;\n\nexport type NotionContentCacheDeleteResult = {\n ok: boolean;\n skipped: boolean;\n deleted: string[];\n failed: Array<{ key?: string; error: string }>;\n};\n\ntype CacheableValue = Record<string, unknown> | unknown[] | null;\n\nfunction modelPrefix(modelId: string) {\n return `notion:${CACHE_VERSION}:${modelId}`;\n}\n\nexport function notionModelListCacheKey(modelId: string) {\n return `${modelPrefix(modelId)}:list`;\n}\n\nfunction versionSegment(value?: string) {\n const version = String(value ?? \"\").trim();\n return version ? `:v:${encodeURIComponent(version)}` : \"\";\n}\n\nexport function notionPageBlocksCacheKey(\n modelId: string,\n pageId: string,\n cacheVersion?: string\n) {\n return `${modelPrefix(modelId)}:page:${pageId}:blocks${versionSegment(cacheVersion)}`;\n}\n\nexport function notionModelCachePrefix(modelId: string) {\n return `${modelPrefix(modelId)}:`;\n}\n\nexport function notionPageCachePrefix(modelId: string, pageId: string) {\n return `${modelPrefix(modelId)}:page:${pageId}:`;\n}\n\nfunction logNotionContentCache(fields: Record<string, unknown>) {\n try {\n console.log(JSON.stringify({ tag: \"notion_content_cache\", ...fields }));\n } catch {\n // Ignore logging serialization errors.\n }\n}\n\nexport async function getCachedNotionValue<T extends CacheableValue>(\n cache: KeyValueCacheAdapter | null | undefined,\n key: string\n): Promise<T | null> {\n if (!cache) return null;\n try {\n const value = await cache.get<T>(key, {\n cacheTtl: CONTENT_CACHE_READ_TTL_SECONDS,\n });\n logNotionContentCache({\n op: \"get\",\n key,\n hit: value !== null,\n cache: cache.kind,\n });\n return value;\n } catch (error) {\n logNotionContentCache({\n op: \"get_error\",\n key,\n cache: cache.kind,\n message: error instanceof Error ? error.message : String(error),\n });\n return null;\n }\n}\n\nexport async function putCachedNotionValue<T extends CacheableValue>(\n cache: KeyValueCacheAdapter | null | undefined,\n key: string,\n value: T,\n options?: { expirationTtl?: number }\n) {\n if (!cache) return;\n try {\n await cache.put(key, value, {\n expirationTtl: options?.expirationTtl ?? NOTION_BLOCKS_CACHE_TTL_SECONDS,\n metadata: {\n source: \"notion\",\n cachedAt: new Date().toISOString(),\n },\n });\n logNotionContentCache({\n op: \"put\",\n key,\n cache: cache.kind,\n });\n } catch (error) {\n logNotionContentCache({\n op: \"put_error\",\n key,\n cache: cache.kind,\n message: error instanceof Error ? error.message : String(error),\n });\n }\n}\n\nexport async function getCachedNotionBlocks(\n cache: KeyValueCacheAdapter | null | undefined,\n input: { modelId: string; pageId: string; cacheVersion?: string }\n) {\n return getCachedNotionValue<NotionBlock[]>(\n cache,\n notionPageBlocksCacheKey(input.modelId, input.pageId, input.cacheVersion)\n );\n}\n\nexport async function putCachedNotionBlocks(\n cache: KeyValueCacheAdapter | null | undefined,\n input: {\n modelId: string;\n pageId: string;\n cacheVersion?: string;\n blocks: NotionBlock[];\n }\n) {\n await putCachedNotionValue(\n cache,\n notionPageBlocksCacheKey(input.modelId, input.pageId, input.cacheVersion),\n input.blocks,\n { expirationTtl: NOTION_BLOCKS_CACHE_TTL_SECONDS }\n );\n}\n\nexport async function deleteNotionContentCache(input: {\n modelId: string;\n pageId?: string;\n routeId?: string;\n previousRouteId?: string;\n cache?: KeyValueCacheAdapter | null;\n getCache?: () => KeyValueCacheAdapter | null;\n}): Promise<NotionContentCacheDeleteResult> {\n let cache: KeyValueCacheAdapter | null;\n try {\n cache = input.cache ?? input.getCache?.() ?? null;\n } catch (error) {\n return {\n ok: false,\n skipped: true,\n deleted: [],\n failed: [\n {\n error: error instanceof Error ? error.message : String(error),\n },\n ],\n };\n }\n\n if (!cache) {\n return { ok: true, skipped: true, deleted: [], failed: [] };\n }\n\n const prefixes = new Set<string>();\n if (input.pageId) {\n prefixes.add(notionPageCachePrefix(input.modelId, input.pageId));\n } else {\n prefixes.add(notionModelCachePrefix(input.modelId));\n }\n\n prefixes.add(notionModelListCacheKey(input.modelId));\n\n const deleted: string[] = [];\n const failed: Array<{ key?: string; error: string }> = [];\n\n for (const prefix of prefixes) {\n let cursor: string | undefined;\n do {\n try {\n const result = await cache.list({ prefix, cursor, limit: 100 });\n for (const { name } of result.keys) {\n try {\n await cache.delete(name);\n deleted.push(name);\n } catch (error) {\n failed.push({\n key: name,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n cursor = result.listComplete ? undefined : result.cursor;\n } catch (error) {\n failed.push({\n key: prefix,\n error: error instanceof Error ? error.message : String(error),\n });\n cursor = undefined;\n }\n } while (cursor);\n }\n\n return {\n ok: failed.length === 0,\n skipped: false,\n deleted,\n failed,\n };\n}\n","import type { NotionBlock, NotionFileSource, NotionPageLike } from \"./types\";\n\ntype FileLike = {\n type?: string;\n external?: { url?: string };\n file?: { url?: string; expiry_time?: string };\n name?: string;\n};\n\nfunction stripLeadingSlash(value: string) {\n return value.startsWith(\"/\") ? value.slice(1) : value;\n}\n\nfunction encodePathPart(value: string) {\n return encodeURIComponent(stripLeadingSlash(value));\n}\n\nfunction appendVersion(path: string, version?: string) {\n const value = String(version ?? \"\").trim();\n if (!value) return path;\n return `${path}?${new URLSearchParams({ v: value })}`;\n}\n\nfunction blockVersion(block: NotionBlock): string | undefined {\n return typeof block.last_edited_time === \"string\"\n ? block.last_edited_time\n : undefined;\n}\n\nexport function notionPageCoverMediaPath(pageId: string): string {\n return `/api/notion/media/page/${encodePathPart(pageId)}/cover`;\n}\n\nexport function notionPagePropertyMediaPath(\n pageId: string,\n propertyName: string\n): string {\n return `/api/notion/media/page/${encodePathPart(pageId)}/property/${encodePathPart(propertyName)}`;\n}\n\nexport function notionBlockMediaPath(blockId: string): string {\n return `/api/notion/media/block/${encodePathPart(blockId)}`;\n}\n\nexport function normalizeNotionFileSource(input: unknown): NotionFileSource | null {\n const file = input as FileLike | null | undefined;\n if (!file || typeof file !== \"object\") return null;\n\n if (file.type === \"external\") {\n const url = String(file.external?.url ?? \"\").trim();\n return url ? { type: \"external\", url } : null;\n }\n\n if (file.type === \"file\") {\n const url = String(file.file?.url ?? \"\").trim();\n if (!url) return null;\n return {\n type: \"file\",\n url,\n expiryTime: String(file.file?.expiry_time ?? \"\").trim() || null,\n };\n }\n\n return null;\n}\n\nexport function resolveNotionFileUrl(input: unknown): string | null {\n return normalizeNotionFileSource(input)?.url ?? null;\n}\n\nexport function isNotionHostedFile(input: unknown): boolean {\n return normalizeNotionFileSource(input)?.type === \"file\";\n}\n\nexport function pickFirstFilesPropertyValue(property: unknown): unknown | null {\n const value = property as { type?: string; files?: unknown[] } | null | undefined;\n if (!value || value.type !== \"files\" || !Array.isArray(value.files)) {\n return null;\n }\n return value.files[0] ?? null;\n}\n\nexport function pickPageCoverFile(page: NotionPageLike): unknown | null {\n return page.cover ?? null;\n}\n\nexport function coverImageUrlForPage(\n page: NotionPageLike,\n coverPropertyName = \"Cover\"\n): string | null {\n const propertyFile = pickFirstFilesPropertyValue(\n page.properties?.[coverPropertyName]\n );\n const propertySource = normalizeNotionFileSource(propertyFile);\n if (propertySource) {\n return appendVersion(\n notionPagePropertyMediaPath(page.id, coverPropertyName),\n page.last_edited_time\n );\n }\n\n const coverSource = normalizeNotionFileSource(pickPageCoverFile(page));\n if (coverSource) {\n return appendVersion(notionPageCoverMediaPath(page.id), page.last_edited_time);\n }\n\n return null;\n}\n\nexport function fileObjectForMediaBlock(block: NotionBlock): unknown | null {\n const typed = block[block.type] as Record<string, unknown> | undefined;\n if (!typed || typeof typed !== \"object\") return null;\n\n if (\n block.type === \"image\" ||\n block.type === \"video\" ||\n block.type === \"file\" ||\n block.type === \"pdf\" ||\n block.type === \"audio\"\n ) {\n return typed;\n }\n\n return null;\n}\n\nexport function mediaUrlForBlock(block: NotionBlock): string | null {\n const source = normalizeNotionFileSource(fileObjectForMediaBlock(block));\n if (!source) return null;\n if (source.type === \"external\" && block.type !== \"image\") {\n return source.url;\n }\n return appendVersion(notionBlockMediaPath(block.id), blockVersion(block));\n}\n\nexport function firstImageUrlFromBlocks(blocks: NotionBlock[]): string | null {\n for (const block of blocks) {\n if (block.type === \"image\") {\n const imageUrl = mediaUrlForBlock(block);\n if (imageUrl) return imageUrl;\n }\n\n const nested = block.children?.length\n ? firstImageUrlFromBlocks(block.children)\n : null;\n if (nested) return nested;\n }\n\n return null;\n}\n\nexport function publicMediaBlockForApi(block: NotionBlock): NotionBlock {\n const value = block[block.type];\n const source = normalizeNotionFileSource(value);\n const children = block.children?.map(publicMediaBlockForApi);\n\n if (!source) {\n return children ? { ...block, children } : { ...block };\n }\n\n const path = appendVersion(notionBlockMediaPath(block.id), blockVersion(block));\n const publicValue =\n source.type === \"external\"\n ? block.type === \"image\"\n ? {\n ...(value as Record<string, unknown>),\n external: { url: path },\n }\n : value\n : {\n ...(value as Record<string, unknown>),\n file: {\n url: path,\n expiry_time: null,\n },\n };\n\n return {\n ...block,\n [block.type]: publicValue,\n ...(children ? { children } : {}),\n };\n}\n\nexport function gatedMediaBlockForApi(\n block: NotionBlock,\n options?: { accessUrlForBlock?: (block: NotionBlock) => string | null }\n): NotionBlock {\n const value = block[block.type];\n const source = normalizeNotionFileSource(value);\n const children = block.children?.map((child) =>\n gatedMediaBlockForApi(child, options)\n );\n\n if (block.type !== \"video\" || !source) {\n const publicBlock = publicMediaBlockForApi(block);\n return children ? { ...publicBlock, children } : publicBlock;\n }\n\n const gatedValue: Record<string, unknown> = {\n ...(value as Record<string, unknown>),\n gated: true,\n access_url: options?.accessUrlForBlock?.(block) ?? null,\n };\n\n if (source.type === \"external\") {\n gatedValue.external = { url: null };\n } else {\n gatedValue.file = {\n url: null,\n expiry_time: null,\n };\n }\n\n return {\n ...block,\n video: gatedValue,\n ...(children ? { children } : {}),\n };\n}\n\nexport function isDirectVideoUrl(url: string): boolean {\n try {\n const parsed = new URL(url);\n return /\\.(mp4|webm|mov|m4v)(?:$|\\?)/i.test(parsed.pathname);\n } catch {\n return false;\n }\n}\n\nexport function videoEmbedUrl(url: string): string | null {\n try {\n const parsed = new URL(url);\n const host = parsed.hostname.replace(/^www\\./, \"\");\n\n if (host === \"youtube.com\" || host === \"m.youtube.com\") {\n const id = parsed.searchParams.get(\"v\");\n return id ? `https://www.youtube.com/embed/${encodeURIComponent(id)}` : null;\n }\n\n if (host === \"youtu.be\") {\n const id = parsed.pathname.split(\"/\").filter(Boolean)[0];\n return id ? `https://www.youtube.com/embed/${encodeURIComponent(id)}` : null;\n }\n\n if (host === \"youtube-nocookie.com\") {\n return parsed.toString();\n }\n\n if (host === \"vimeo.com\") {\n const id = parsed.pathname.split(\"/\").filter(Boolean)[0];\n return id ? `https://player.vimeo.com/video/${encodeURIComponent(id)}` : null;\n }\n\n if (host === \"player.vimeo.com\") {\n return parsed.toString();\n }\n } catch {\n return null;\n }\n\n return null;\n}\n","import { cache } from \"react\";\nimport { listBlockChildrenDeep, type NotionBlockClient } from \"./blocks\";\nimport { createNotionClient } from \"./client\";\nimport { getNotionConfigForModel, hasNotionModelConfig } from \"./config\";\nimport { coverImageUrlForPage } from \"./media\";\nimport {\n getDateProperty,\n getRichTextProperty,\n getTagsProperty,\n isValidPublicSlug,\n notionPageEditUrl,\n pickDescriptionFallback,\n} from \"./property-mappers\";\nimport type {\n NotionBlock,\n NotionFieldMap,\n NotionGenericContentModel,\n NotionPageLike,\n} from \"./types\";\n\ntype PropertyMap = Record<string, unknown>;\n\ntype DataSourceQueryResponse = {\n results?: unknown[];\n has_more?: boolean;\n next_cursor?: string | null;\n};\n\ntype QueryDataSourceInput = {\n startCursor?: string;\n};\n\nexport type GenericContentListItem = {\n pageId: string;\n slug: string;\n title: string;\n description: string;\n date: string;\n tags: string[];\n coverImage: string | null;\n published: boolean;\n editUrl: string | null;\n properties: Record<string, string | string[]>;\n};\n\nexport type GenericContentDetail = GenericContentListItem & {\n blocks: NotionBlock[];\n};\n\nexport type GenericContentSourceDeps<\n TFields extends NotionFieldMap = NotionFieldMap,\n> = {\n model: NotionGenericContentModel & { source: { fields: TFields } };\n dataSourceId: string;\n queryDataSource: (\n input?: QueryDataSourceInput\n ) => Promise<DataSourceQueryResponse>;\n getPageBlocks: (pageId: string) => Promise<NotionBlock[]>;\n editBaseUrl?: string;\n};\n\nfunction normalizePage(input: unknown): NotionPageLike | null {\n if (!input || typeof input !== \"object\") return null;\n const page = input as NotionPageLike;\n return page.id ? page : null;\n}\n\nfunction firstFieldName(value: string | readonly string[] | undefined) {\n if (Array.isArray(value)) return value[0];\n return value;\n}\n\nfunction getFieldName(fields: NotionFieldMap, key: string) {\n return firstFieldName(fields[key]);\n}\n\nfunction pickPublishedFlagForModel(\n properties: PropertyMap,\n fields: NotionFieldMap\n) {\n const publishedField = getFieldName(fields, \"published\") ?? \"Published\";\n const published = properties[publishedField] as\n | Record<string, unknown>\n | undefined;\n if (published?.type === \"checkbox\") return Boolean(published.checkbox);\n\n const statusField = getFieldName(fields, \"status\") ?? \"Status\";\n const status = properties[statusField] as Record<string, unknown> | undefined;\n if (status?.type === \"status\") {\n const value = status.status as { name?: string } | null | undefined;\n return String(value?.name ?? \"\").trim().toLowerCase() === \"published\";\n }\n if (status?.type === \"select\") {\n const value = status.select as { name?: string } | null | undefined;\n return String(value?.name ?? \"\").trim().toLowerCase() === \"published\";\n }\n\n return true;\n}\n\nfunction coverImageUrlForModel(page: NotionPageLike, fields: NotionFieldMap) {\n const coverField = fields.cover;\n if (Array.isArray(coverField)) {\n for (const field of coverField) {\n const imageUrl = coverImageUrlForPage(page, field);\n if (imageUrl) return imageUrl;\n }\n }\n if (typeof coverField === \"string\") {\n return coverImageUrlForPage(page, coverField);\n }\n return coverImageUrlForPage(page);\n}\n\nfunction mapExtraProperties(properties: PropertyMap, fields: NotionFieldMap) {\n const result: Record<string, string | string[]> = {};\n for (const [key, value] of Object.entries(fields)) {\n const field = firstFieldName(value);\n if (!field) continue;\n const tags = getTagsProperty(properties, field);\n result[key] = tags.length > 0 ? tags : getRichTextProperty(properties, field);\n }\n return result;\n}\n\nexport function mapNotionPageToGenericContentItem<\n TFields extends NotionFieldMap,\n>(\n model: { id: string; source: { fields: TFields } },\n page: NotionPageLike,\n options?: { editBaseUrl?: string }\n): GenericContentListItem {\n const fields = model.source.fields;\n const properties = isRecord(page.properties) ? page.properties : {};\n const titleField = getFieldName(fields, \"title\");\n const slugField = getFieldName(fields, \"slug\");\n const descriptionField = getFieldName(fields, \"description\");\n const dateField = getFieldName(fields, \"date\");\n const tagsField = getFieldName(fields, \"tags\");\n\n const title = titleField ? getRichTextProperty(properties, titleField) : \"\";\n const slug = slugField\n ? getRichTextProperty(properties, slugField).toLowerCase()\n : page.id.replaceAll(\"-\", \"\").toLowerCase();\n const description = pickDescriptionFallback(\n descriptionField ? getRichTextProperty(properties, descriptionField) : \"\",\n title\n );\n\n return {\n pageId: page.id,\n slug,\n title,\n description,\n date: dateField ? getDateProperty(properties, dateField) : \"\",\n tags: tagsField ? getTagsProperty(properties, tagsField) : [],\n coverImage: coverImageUrlForModel(page, fields),\n published: pickPublishedFlagForModel(properties, fields),\n editUrl: notionPageEditUrl(page.id, options?.editBaseUrl),\n properties: mapExtraProperties(properties, fields),\n };\n}\n\nexport function isRenderableGenericContentItem(item: GenericContentListItem) {\n return Boolean(item.published && item.title && item.slug && isValidPublicSlug(item.slug));\n}\n\nexport function createGenericNotionContentSource<\n TFields extends NotionFieldMap,\n>(deps: GenericContentSourceDeps<TFields>) {\n return {\n async listItems(): Promise<GenericContentListItem[]> {\n const pages: NotionPageLike[] = [];\n let cursor: string | undefined;\n\n do {\n const response = await deps.queryDataSource({ startCursor: cursor });\n for (const item of response.results ?? []) {\n const page = normalizePage(item);\n if (page) pages.push(page);\n }\n\n cursor = response.next_cursor ?? undefined;\n if (!response.has_more) break;\n } while (cursor);\n\n return pages\n .map((page) =>\n mapNotionPageToGenericContentItem(deps.model, page, {\n editBaseUrl: deps.editBaseUrl,\n })\n )\n .filter(isRenderableGenericContentItem)\n .sort((a, b) => b.date.localeCompare(a.date));\n },\n\n async getItemBySlug(slug: string): Promise<GenericContentDetail | null> {\n const items = await this.listItems();\n const item = items.find((candidate) => candidate.slug === slug);\n if (!item) return null;\n return {\n ...item,\n blocks: await deps.getPageBlocks(item.pageId),\n };\n },\n };\n}\n\nasync function createDefaultGenericSource<\n TFields extends NotionFieldMap,\n>(model: NotionGenericContentModel & { source: { fields: TFields } }) {\n if (!(await hasNotionModelConfig(model))) return null;\n\n const config = await getNotionConfigForModel(model);\n const client = createNotionClient(config);\n return createGenericNotionContentSource({\n model,\n dataSourceId: config.dataSourceId,\n editBaseUrl: config.editBaseUrl,\n queryDataSource: async ({ startCursor } = {}) =>\n client.dataSources.query({\n data_source_id: config.dataSourceId,\n page_size: model.source.query.pageSize,\n sorts: model.source.query.sorts\n ? [...model.source.query.sorts]\n : undefined,\n filter_properties: model.source.query.filterProperties\n ? [...model.source.query.filterProperties]\n : undefined,\n ...(startCursor ? { start_cursor: startCursor } : {}),\n }),\n getPageBlocks: (pageId) =>\n listBlockChildrenDeep(client as unknown as NotionBlockClient, pageId),\n });\n}\n\nconst sourceCache = cache(createDefaultGenericSource);\n\nexport async function listGenericNotionContent<\n TFields extends NotionFieldMap,\n>(model: NotionGenericContentModel & { source: { fields: TFields } }) {\n const source = await sourceCache(model);\n if (!source) return [];\n return source.listItems();\n}\n\nexport async function getGenericNotionContentBySlug<\n TFields extends NotionFieldMap,\n>(model: NotionGenericContentModel & { source: { fields: TFields } }, slug: string) {\n const source = await sourceCache(model);\n if (!source) return null;\n return source.getItemBySlug(slug);\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return Boolean(value && typeof value === \"object\");\n}\n","type PropertyMap = Record<string, unknown>;\n\ntype TextPart = {\n plain_text?: string;\n};\n\nexport function isRecord(value: unknown): value is Record<string, unknown> {\n return Boolean(value && typeof value === \"object\");\n}\n\nfunction getPlainText(parts: unknown): string {\n if (!Array.isArray(parts)) return \"\";\n return parts\n .map((part: TextPart) => part.plain_text ?? \"\")\n .join(\"\")\n .trim();\n}\n\nfunction getProperty(properties: PropertyMap, key: string) {\n return properties[key] as Record<string, unknown> | undefined;\n}\n\nfunction firstPropertyOfType(properties: PropertyMap, type: string) {\n return Object.values(properties).find(\n (property): property is Record<string, unknown> =>\n isRecord(property) && property.type === type\n );\n}\n\nexport function getFirstTitleProperty(properties: PropertyMap): string {\n const property = firstPropertyOfType(properties, \"title\");\n return property ? getPlainText(property.title) : \"\";\n}\n\nexport function getRichTextProperty(properties: PropertyMap, key: string): string {\n const property = getProperty(properties, key);\n if (!property) return \"\";\n\n if (property.type === \"title\") return getPlainText(property.title);\n if (property.type === \"rich_text\") return getPlainText(property.rich_text);\n if (property.type === \"url\") return String(property.url ?? \"\").trim();\n if (property.type === \"email\") return String(property.email ?? \"\").trim();\n if (property.type === \"phone_number\") {\n return String(property.phone_number ?? \"\").trim();\n }\n\n return \"\";\n}\n\nexport function getDateProperty(properties: PropertyMap, key: string): string {\n const property = getProperty(properties, key);\n if (property?.type !== \"date\") return \"\";\n const date = property.date as { start?: string } | null | undefined;\n return String(date?.start ?? \"\").trim();\n}\n\nexport function getFirstDateProperty(properties: PropertyMap): string {\n const property = firstPropertyOfType(properties, \"date\");\n if (!property) return \"\";\n const date = property.date as { start?: string } | null | undefined;\n return String(date?.start ?? \"\").trim();\n}\n\nexport function getSelectProperty(properties: PropertyMap, key: string): string {\n const property = getProperty(properties, key);\n if (property?.type !== \"select\") return \"\";\n const select = property.select as { name?: string } | null | undefined;\n return String(select?.name ?? \"\").trim();\n}\n\nexport function getCheckboxProperty(properties: PropertyMap, key: string): boolean {\n const property = getProperty(properties, key);\n if (property?.type !== \"checkbox\") return false;\n return Boolean(property.checkbox);\n}\n\nexport function getNumberProperty(\n properties: PropertyMap,\n key: string,\n fallback = 0\n): number {\n const property = getProperty(properties, key);\n if (property?.type !== \"number\") return fallback;\n const value = Number(property.number);\n return Number.isFinite(value) ? value : fallback;\n}\n\nexport function getRelationPageIds(properties: PropertyMap, key: string): string[] {\n const property = getProperty(properties, key);\n if (property?.type !== \"relation\" || !Array.isArray(property.relation)) {\n return [];\n }\n\n return property.relation\n .map((item: { id?: string }) => String(item.id ?? \"\").trim())\n .filter(Boolean);\n}\n\nexport function getTagsProperty(properties: PropertyMap, key: string): string[] {\n const property = getProperty(properties, key);\n if (property?.type === \"multi_select\" && Array.isArray(property.multi_select)) {\n return property.multi_select\n .map((item: { name?: string }) => String(item.name ?? \"\").trim())\n .filter(Boolean);\n }\n\n if (property?.type === \"select\") {\n const select = property.select as { name?: string } | null | undefined;\n const name = String(select?.name ?? \"\").trim();\n return name ? [name] : [];\n }\n\n return [];\n}\n\nexport function getFirstTagsProperty(properties: PropertyMap): string[] {\n const multiSelect = firstPropertyOfType(properties, \"multi_select\");\n if (multiSelect && Array.isArray(multiSelect.multi_select)) {\n return multiSelect.multi_select\n .map((item: { name?: string }) => String(item.name ?? \"\").trim())\n .filter(Boolean);\n }\n\n const select = firstPropertyOfType(properties, \"select\");\n const name = String((select?.select as { name?: string } | null)?.name ?? \"\").trim();\n return name ? [name] : [];\n}\n\nexport function getAuthorProperty(properties: PropertyMap, key: string): string {\n const property = getProperty(properties, key);\n if (!property) return \"\";\n\n if (property.type === \"people\" && Array.isArray(property.people)) {\n return property.people\n .map((person: { name?: string; person?: { email?: string } }) =>\n String(person.name ?? person.person?.email ?? \"\").trim()\n )\n .filter(Boolean)\n .join(\", \");\n }\n\n return getRichTextProperty(properties, key);\n}\n\nexport function getFirstPeopleProperty(properties: PropertyMap): string {\n const property = firstPropertyOfType(properties, \"people\");\n if (!property || !Array.isArray(property.people)) return \"\";\n\n return property.people\n .map((person: { name?: string; person?: { email?: string } }) =>\n String(person.name ?? person.person?.email ?? \"\").trim()\n )\n .filter(Boolean)\n .join(\", \");\n}\n\nexport function pickPublishedFlag(properties: PropertyMap): boolean {\n const published = getProperty(properties, \"Published\");\n if (published?.type === \"checkbox\") {\n return Boolean(published.checkbox);\n }\n\n const status = getProperty(properties, \"Status\");\n if (status?.type === \"status\") {\n const statusValue = status.status as { name?: string } | null | undefined;\n return String(statusValue?.name ?? \"\").trim().toLowerCase() === \"published\";\n }\n\n if (status?.type === \"select\") {\n const statusValue = status.select as { name?: string } | null | undefined;\n return String(statusValue?.name ?? \"\").trim().toLowerCase() === \"published\";\n }\n\n return false;\n}\n\nexport function pickDescriptionFallback(description: string, title: string): string {\n return description.trim() || title.trim();\n}\n\nexport function isValidPublicSlug(slug: string): boolean {\n return /^[a-z0-9][a-z0-9-]{0,79}$/.test(slug);\n}\n\nexport function notionPageEditUrl(pageId: string, editBaseUrl?: string): string {\n const compactPageId = pageId.replaceAll(\"-\", \"\");\n if (editBaseUrl?.includes(\"{pageId}\")) {\n return editBaseUrl.replaceAll(\"{pageId}\", compactPageId);\n }\n return `https://www.notion.so/${compactPageId}`;\n}\n\n/**\n * Normalize a Notion page id (with or without dashes) to a compact lowercase\n * string. Used as a stable identifier in URLs and cache keys.\n */\nexport function compactNotionId(id: string): string {\n return id.replaceAll(\"-\", \"\").toLowerCase();\n}\n","import type { KeyValueCacheAdapter } from \"../platform/runtime\";\nimport { createNotionClient } from \"./client\";\nimport { getNotionConfigForModel } from \"./config\";\nimport {\n compactNotionId,\n getRichTextProperty,\n getSelectProperty,\n isValidPublicSlug,\n} from \"./property-mappers\";\nimport type {\n NotionFieldMap,\n NotionGenericContentModel,\n NotionPageLike,\n} from \"./types\";\n\ntype JsonRecord = Record<string, unknown>;\n\ntype StoredWebhookVerificationToken = {\n token: string;\n updatedAt: string;\n};\n\nconst WEBHOOK_VERIFICATION_TOKEN_CACHE_KEY =\n \"notion:webhook:verification-token:v1\";\n\nexport type NotionWebhookParseResult =\n | { type: \"verification\"; verificationToken: string }\n | { type: \"events\"; events: NotionWebhookEvent[] };\n\nexport type NotionWebhookEvent = {\n id?: string;\n eventType: string;\n modelId: string;\n pageId?: string;\n dataSourceId?: string;\n routeId?: string;\n locale?: string;\n kind: \"publish\" | \"update\" | \"delete\";\n includeApi: boolean;\n reason: \"page\" | \"data_source\";\n};\n\n/**\n * Generic shape that callers (i.e. the starter) supply so the webhook can\n * route events to the correct content model. Each entry must expose the\n * Notion data source id that the model is bound to, and a hook to derive a\n * route id and locale from the Notion page payload.\n */\nexport type NotionWebhookModelRegistration<\n TFields extends NotionFieldMap = NotionFieldMap,\n> = NotionGenericContentModel & {\n source: { fields: TFields };\n resolveRouteId?: (page: JsonRecord) => string;\n resolveLocale?: (page: JsonRecord) => string;\n};\n\nexport type NotionPageRetriever = (\n pageId: string,\n model: NotionGenericContentModel\n) => Promise<NotionPageLike | null>;\n\nexport type NotionWebhookParseOptions<\n TFields extends NotionFieldMap = NotionFieldMap,\n> = {\n models: ReadonlyArray<NotionWebhookModelRegistration<TFields>>;\n /**\n * Optional override for resolving the data source id of a model. Defaults\n * to `process.env[model.source.dataSourceEnv] ?? model.source.defaultDataSourceId`.\n */\n getModelDataSourceId?: (model: NotionWebhookModelRegistration<TFields>) => string | null;\n};\n\nexport type InvalidationKind = \"publish\" | \"update\" | \"delete\";\n\n/**\n * Request shape consumed by the revalidation pipeline. Mirrors the starter's\n * `ContentRevalidateRequest`; duplicated here so the package does not need\n * a runtime dependency on the starter.\n */\nexport type NotionWebhookRevalidateRequest = {\n modelId: string;\n pageId?: string;\n routeId?: string;\n previousRouteId?: string;\n locale?: string;\n kind?: InvalidationKind;\n includeApi?: boolean;\n};\n\nfunction isRecord(value: unknown): value is JsonRecord {\n return Boolean(value && typeof value === \"object\" && !Array.isArray(value));\n}\n\nfunction readString(source: unknown, key: string) {\n if (!isRecord(source)) return \"\";\n const value = source[key];\n return typeof value === \"string\" ? value.trim() : \"\";\n}\n\nfunction asRecords(value: unknown) {\n if (Array.isArray(value)) return value.filter(isRecord);\n if (isRecord(value)) return [value];\n return [];\n}\n\nfunction nestedRecords(source: JsonRecord, ...keys: string[]) {\n const records: JsonRecord[] = [];\n for (const key of keys) {\n const value = source[key];\n if (isRecord(value)) records.push(value);\n }\n return records;\n}\n\nfunction firstString(...values: string[]) {\n return values.find(Boolean) ?? \"\";\n}\n\nfunction normalizeId(value: string) {\n return value.replaceAll(\"-\", \"\").toLowerCase();\n}\n\nfunction findIdByType(input: JsonRecord, type: string): string {\n const stack = [input];\n const seen = new Set<JsonRecord>();\n\n while (stack.length > 0) {\n const current = stack.pop();\n if (!current || seen.has(current)) continue;\n seen.add(current);\n\n if (readString(current, \"type\") === type) {\n const id = readString(current, \"id\");\n if (id) return id;\n }\n\n for (const value of Object.values(current)) {\n if (isRecord(value)) stack.push(value);\n if (Array.isArray(value)) {\n for (const item of value) {\n if (isRecord(item)) stack.push(item);\n }\n }\n }\n }\n\n return \"\";\n}\n\nfunction findDataSourceId(input: JsonRecord): string {\n const stack = [input];\n const seen = new Set<JsonRecord>();\n\n while (stack.length > 0) {\n const current = stack.pop();\n if (!current || seen.has(current)) continue;\n seen.add(current);\n\n const direct = firstString(\n readString(current, \"data_source_id\"),\n readString(current, \"source_id\")\n );\n if (direct) return direct;\n\n const type = readString(current, \"type\");\n if (type === \"data_source\") {\n const id = readString(current, \"id\");\n if (id) return id;\n }\n\n for (const value of Object.values(current)) {\n if (isRecord(value)) stack.push(value);\n if (Array.isArray(value)) {\n for (const item of value) {\n if (isRecord(item)) stack.push(item);\n }\n }\n }\n }\n\n return \"\";\n}\n\nfunction firstFieldName(value: string | readonly string[] | undefined) {\n if (Array.isArray(value)) return value[0];\n return value;\n}\n\nfunction defaultRouteIdFromProperties(\n model: NotionGenericContentModel,\n page: JsonRecord\n) {\n const properties = isRecord(page.properties) ? page.properties : {};\n const slugField = firstFieldName(model.source.fields.slug);\n const slug = slugField\n ? getRichTextProperty(properties, slugField).toLowerCase()\n : \"\";\n return isValidPublicSlug(slug) ? slug : \"\";\n}\n\nfunction defaultLocaleFromProperties(model: NotionGenericContentModel, page: JsonRecord) {\n const properties = isRecord(page.properties) ? page.properties : {};\n const localeField = firstFieldName(model.source.fields.locale);\n return localeField ? getSelectProperty(properties, localeField) : \"\";\n}\n\nfunction eventKind(eventType: string): \"publish\" | \"update\" | \"delete\" {\n if (eventType.includes(\".deleted\")) return \"delete\";\n if (eventType.includes(\".created\") || eventType.includes(\".undeleted\")) {\n return \"publish\";\n }\n return \"update\";\n}\n\nfunction defaultGetModelDataSourceId(\n model: NotionGenericContentModel\n): string | null {\n const fromEnv = process.env[model.source.dataSourceEnv];\n if (fromEnv) return fromEnv;\n return model.source.defaultDataSourceId ?? null;\n}\n\nfunction matchModelByDataSourceId<\n TFields extends NotionFieldMap,\n>(\n models: ReadonlyArray<NotionWebhookModelRegistration<TFields>>,\n dataSourceId: string,\n getDataSourceId: (model: NotionWebhookModelRegistration<TFields>) => string | null\n) {\n if (!dataSourceId) return null;\n const normalized = normalizeId(dataSourceId);\n return (\n models.find((model) => {\n const configured = getDataSourceId(model);\n return configured ? normalizeId(configured) === normalized : false;\n }) ?? null\n );\n}\n\nfunction findModelForEvent<\n TFields extends NotionFieldMap,\n>(\n models: ReadonlyArray<NotionWebhookModelRegistration<TFields>>,\n event: JsonRecord,\n page: JsonRecord | undefined,\n getDataSourceId: (model: NotionWebhookModelRegistration<TFields>) => string | null\n) {\n const modelId = firstString(\n readString(event, \"modelId\"),\n readString(event, \"model_id\"),\n readString(event.data, \"modelId\"),\n readString(event.data, \"model_id\")\n );\n if (modelId) {\n return models.find((model) => model.id === modelId) ?? null;\n }\n\n const dataSourceId = firstString(\n findDataSourceId(event),\n findIdByType(event, \"data_source\"),\n readString(event, \"data_source_id\"),\n readString(event.data, \"data_source_id\"),\n readString(page?.parent, \"data_source_id\"),\n readString(page?.parent, \"database_id\")\n );\n return matchModelByDataSourceId(models, dataSourceId, getDataSourceId);\n}\n\nfunction pageIdForEvent(event: JsonRecord, page?: JsonRecord) {\n return firstString(\n readString(page, \"id\"),\n findIdByType(event, \"page\"),\n readString(event, \"page_id\"),\n readString(event.data, \"page_id\")\n );\n}\n\nfunction pageForEvent(event: JsonRecord) {\n let fallback: JsonRecord | null = null;\n for (const record of [\n ...nestedRecords(isRecord(event.data) ? event.data : {}, \"page\", \"entity\"),\n ...nestedRecords(event, \"page\", \"entity\"),\n event,\n ]) {\n const type = readString(record, \"type\");\n if (type === \"page\" || readString(record, \"object\") === \"page\") {\n if (!readString(record, \"id\")) continue;\n if (isRecord(record.properties) || isRecord(record.parent)) return record;\n fallback ??= record;\n }\n }\n return fallback;\n}\n\nexport function parseNotionWebhookPayload<\n TFields extends NotionFieldMap = NotionFieldMap,\n>(\n payload: unknown,\n options: NotionWebhookParseOptions<TFields>\n): NotionWebhookParseResult {\n if (!isRecord(payload)) return { type: \"events\", events: [] };\n\n const verificationToken = readString(payload, \"verification_token\");\n if (verificationToken) {\n return { type: \"verification\", verificationToken };\n }\n\n const getDataSourceId = options.getModelDataSourceId ?? defaultGetModelDataSourceId;\n\n const events = asRecords(payload.events ?? payload.event ?? payload)\n .map((event) =>\n parseEvent(event, options.models, getDataSourceId)\n )\n .filter((event): event is NotionWebhookEvent => Boolean(event));\n\n return { type: \"events\", events };\n}\n\nfunction parseEvent<\n TFields extends NotionFieldMap,\n>(\n event: JsonRecord,\n models: ReadonlyArray<NotionWebhookModelRegistration<TFields>>,\n getDataSourceId: (model: NotionWebhookModelRegistration<TFields>) => string | null\n): NotionWebhookEvent | null {\n const eventType = firstString(readString(event, \"type\"), readString(event, \"event\"));\n if (!eventType) return null;\n const page = pageForEvent(event);\n const model = findModelForEvent(\n models,\n event,\n page ?? undefined,\n getDataSourceId\n );\n if (!model) return null;\n\n const routeId = page\n ? (model.resolveRouteId?.(page) ??\n defaultRouteIdFromProperties(model, page))\n : \"\";\n const locale = page\n ? (model.resolveLocale?.(page) ?? defaultLocaleFromProperties(model, page))\n : \"\";\n const dataSourceId = firstString(\n findDataSourceId(event),\n findIdByType(event, \"data_source\"),\n readString(event, \"data_source_id\"),\n readString(event.data, \"data_source_id\"),\n readString(page?.parent, \"data_source_id\"),\n readString(page?.parent, \"database_id\")\n );\n const dataSourceEvent =\n eventType.startsWith(\"data_source.\") || eventType.startsWith(\"database.\");\n return {\n id: readString(event, \"id\") || undefined,\n eventType,\n modelId: model.id,\n pageId: pageIdForEvent(event, page ?? undefined) || undefined,\n dataSourceId: dataSourceId || undefined,\n routeId: routeId || undefined,\n locale: locale || undefined,\n kind: eventKind(eventType),\n includeApi: true,\n reason: page && routeId ? \"page\" : dataSourceEvent ? \"data_source\" : \"page\",\n };\n}\n\nasync function defaultRetrieveNotionPage(\n pageId: string,\n model: NotionGenericContentModel\n) {\n const config = await getNotionConfigForModel(model);\n const client = createNotionClient(config);\n const page = await client.pages.retrieve({ page_id: pageId });\n return page as NotionPageLike;\n}\n\nfunction resolveWebhookEventRoute<\n TFields extends NotionFieldMap,\n>(\n event: NotionWebhookEvent,\n models: ReadonlyArray<NotionWebhookModelRegistration<TFields>>,\n retrievePage: NotionPageRetriever\n): Promise<NotionWebhookEvent> {\n if (event.routeId || !event.pageId) return Promise.resolve(event);\n if (event.kind === \"delete\") return Promise.resolve(event);\n\n const model = models.find((m) => m.id === event.modelId);\n if (!model) return Promise.resolve(event);\n\n return retrievePage(event.pageId, model)\n .then((page) => {\n if (!page) return event;\n const pageRecord = page as unknown as JsonRecord;\n const routeId =\n model.resolveRouteId?.(pageRecord) ??\n defaultRouteIdFromProperties(model, pageRecord);\n const locale =\n model.resolveLocale?.(pageRecord) ??\n defaultLocaleFromProperties(model, pageRecord);\n if (!routeId) return event;\n return {\n ...event,\n routeId,\n locale: locale || event.locale,\n reason: \"page\" as const,\n };\n })\n .catch((error) => {\n const err = error as { code?: string; status?: number; message?: string };\n console.warn(\n JSON.stringify({\n tag: \"notion_webhook_page_lookup_failed\",\n eventId: event.id,\n eventType: event.eventType,\n modelId: event.modelId,\n pageId: event.pageId,\n code: err?.code,\n status: err?.status,\n message: err?.message ?? String(error),\n })\n );\n return event;\n });\n}\n\nexport async function parseNotionWebhookPayloadWithPageLookup<\n TFields extends NotionFieldMap = NotionFieldMap,\n>(\n payload: unknown,\n options: NotionWebhookParseOptions<TFields> & {\n retrievePage?: NotionPageRetriever;\n lookupPages?: boolean;\n }\n): Promise<NotionWebhookParseResult> {\n const parsed = parseNotionWebhookPayload(payload, options);\n if (parsed.type === \"verification\") return parsed;\n if (options?.lookupPages === false) return parsed;\n\n const retrievePage = options?.retrievePage ?? defaultRetrieveNotionPage;\n return {\n type: \"events\",\n events: await Promise.all(\n parsed.events.map((event) =>\n resolveWebhookEventRoute(event, options.models, retrievePage)\n )\n ),\n };\n}\n\nexport function notionWebhookEventToRevalidateRequest(\n event: NotionWebhookEvent\n): NotionWebhookRevalidateRequest {\n return {\n modelId: event.modelId,\n pageId: event.pageId,\n routeId: event.routeId,\n locale: event.locale,\n kind: event.kind,\n includeApi: event.includeApi,\n };\n}\n\nexport async function signNotionWebhookBody(body: string, verificationToken: string) {\n const key = await crypto.subtle.importKey(\n \"raw\",\n new TextEncoder().encode(verificationToken),\n { name: \"HMAC\", hash: \"SHA-256\" },\n false,\n [\"sign\"]\n );\n const signature = await crypto.subtle.sign(\n \"HMAC\",\n key,\n new TextEncoder().encode(body)\n );\n return Array.from(new Uint8Array(signature))\n .map((byte) => byte.toString(16).padStart(2, \"0\"))\n .join(\"\");\n}\n\nfunction normalizeNotionSignature(signature: string | null) {\n const value = String(signature ?? \"\").trim();\n const prefixed = value.match(/^sha256=(.+)$/i);\n return prefixed && prefixed[1] ? prefixed[1].trim() : value;\n}\n\nexport async function verifyNotionWebhookSignature(input: {\n body: string;\n signature: string | null;\n verificationToken?: string | null;\n}) {\n const token = String(input.verificationToken ?? \"\").trim();\n const signature = normalizeNotionSignature(input.signature);\n if (!token || !signature) return false;\n const expected = await signNotionWebhookBody(input.body, token);\n if (expected.length !== signature.length) return false;\n\n let diff = 0;\n for (let index = 0; index < expected.length; index += 1) {\n diff |= expected.charCodeAt(index) ^ signature.charCodeAt(index);\n }\n return diff === 0;\n}\n\nexport async function verifyNotionWebhookSignatureWithTokens(input: {\n body: string;\n signature: string | null;\n verificationTokens: Array<string | null | undefined>;\n}) {\n const tokens = Array.from(\n new Set(input.verificationTokens.map((token) => String(token ?? \"\").trim()))\n ).filter(Boolean);\n\n for (const token of tokens) {\n if (\n await verifyNotionWebhookSignature({\n body: input.body,\n signature: input.signature,\n verificationToken: token,\n })\n ) {\n return true;\n }\n }\n\n return false;\n}\n\nfunction readStoredWebhookVerificationToken(value: unknown) {\n if (typeof value === \"string\") return value.trim() || null;\n if (!isRecord(value)) return null;\n\n const token = readString(value, \"token\");\n return token || null;\n}\n\nexport async function getStoredNotionWebhookVerificationToken(\n cache: KeyValueCacheAdapter | null | undefined\n) {\n if (!cache) return null;\n\n try {\n const value = await cache.get<StoredWebhookVerificationToken | string>(\n WEBHOOK_VERIFICATION_TOKEN_CACHE_KEY,\n { cacheTtl: 60 }\n );\n return readStoredWebhookVerificationToken(value);\n } catch (error) {\n console.warn(\n JSON.stringify({\n tag: \"notion_webhook_token_lookup_failed\",\n message: error instanceof Error ? error.message : String(error),\n })\n );\n return null;\n }\n}\n\nexport async function putStoredNotionWebhookVerificationToken(\n cache: KeyValueCacheAdapter | null | undefined,\n token: string\n) {\n const normalized = token.trim();\n if (!cache || !normalized) return false;\n\n await cache.put<StoredWebhookVerificationToken>(\n WEBHOOK_VERIFICATION_TOKEN_CACHE_KEY,\n {\n token: normalized,\n updatedAt: new Date().toISOString(),\n },\n {\n metadata: {\n source: \"notion-webhook\",\n },\n }\n );\n return true;\n}\n\n// Re-exported for backwards compatibility with the starter.\nexport { compactNotionId };\n"],"mappings":"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","names":["cache","isRecord","isRecord","readString","firstFieldName","cache"]}
|
|
1
|
+
{"version":3,"sources":["../../src/notion/client.ts","../../src/notion/config.ts","../../src/notion/blocks.ts","../../src/notion/block-text.ts","../../src/notion/content-cache.ts","../../src/notion/media.ts","../../src/notion/generic-source.ts","../../src/notion/property-mappers.ts","../../src/notion/webhook.ts"],"sourcesContent":["import { Client } from \"@notionhq/client\";\nimport type { NotionClientConfig } from \"./config\";\n\nexport function createNotionClient(config: NotionClientConfig) {\n return new Client({\n auth: config.token,\n baseUrl: config.apiBaseUrl,\n notionVersion: \"2026-03-11\",\n });\n}\n","import type { NotionContentModelLike } from \"./types\";\n\ntype NotionEnv = {\n NOTION_TOKEN?: string;\n NOTION_DATA_SOURCE_ID?: string;\n NOTION_API_BASE_URL?: string;\n NOTION_EDIT_BASE_URL?: string;\n NOTION_WEBHOOK_VERIFICATION_TOKEN?: string;\n [key: string]: string | undefined;\n};\n\nexport type NotionClientConfig = {\n token: string;\n apiBaseUrl?: string;\n};\n\nexport type NotionConfig = {\n token: string;\n dataSourceId: string;\n apiBaseUrl?: string;\n editBaseUrl?: string;\n webhookVerificationToken?: string;\n};\n\nfunction readProcessEnv(): NotionEnv {\n const env: NotionEnv = {\n NOTION_TOKEN: process.env.NOTION_TOKEN,\n NOTION_DATA_SOURCE_ID: process.env.NOTION_DATA_SOURCE_ID,\n NOTION_API_BASE_URL: process.env.NOTION_API_BASE_URL,\n NOTION_EDIT_BASE_URL: process.env.NOTION_EDIT_BASE_URL,\n NOTION_WEBHOOK_VERIFICATION_TOKEN:\n process.env.NOTION_WEBHOOK_VERIFICATION_TOKEN,\n };\n\n for (const [key, value] of Object.entries(process.env)) {\n if (key.startsWith(\"NOTION_\") && typeof value === \"string\") {\n env[key] = value;\n }\n }\n\n return env;\n}\n\nasync function readWorkerEnv(): Promise<NotionEnv> {\n try {\n const mod = (await import(\n /* webpackIgnore: true */ \"cloudflare:workers\"\n )) as unknown as { env?: Record<string, unknown> };\n const env: NotionEnv = {};\n for (const [key, value] of Object.entries(mod.env ?? {})) {\n if (key.startsWith(\"NOTION_\") && typeof value === \"string\") {\n env[key] = value;\n }\n }\n return env;\n } catch {\n return {};\n }\n}\n\nfunction readString(source: NotionEnv, name: string): string | undefined {\n const value = String(source[name] ?? \"\").trim();\n return value || undefined;\n}\n\nfunction mergeEnv(...sources: NotionEnv[]): NotionEnv {\n const merged: NotionEnv = {};\n\n for (const source of sources) {\n for (const name of Object.keys(source)) {\n if (!name.startsWith(\"NOTION_\")) continue;\n const value = readString(source, name);\n if (value) merged[name] = value;\n }\n }\n\n return merged;\n}\n\nasync function readEnv(): Promise<NotionEnv> {\n const processEnv = readProcessEnv();\n return mergeEnv(await readWorkerEnv(), processEnv);\n}\n\nfunction readRequired(\n source: NotionEnv,\n name: string\n): string {\n const value = readString(source, name);\n if (!value) {\n throw new Error(`Missing required Notion env: ${name}`);\n }\n return value;\n}\n\nexport function getNotionEditBaseUrl(): string {\n return readString(readProcessEnv(), \"NOTION_EDIT_BASE_URL\") ?? \"https://www.notion.so\";\n}\n\nexport async function hasNotionConfig(): Promise<boolean> {\n const env = await readEnv();\n return Boolean(\n readString(env, \"NOTION_TOKEN\") && readString(env, \"NOTION_DATA_SOURCE_ID\")\n );\n}\n\nexport async function hasNotionModelConfig(\n model: NotionContentModelLike\n): Promise<boolean> {\n const env = await readEnv();\n return Boolean(\n readString(env, \"NOTION_TOKEN\") &&\n (readString(env, model.source.dataSourceEnv) ||\n model.source.defaultDataSourceId)\n );\n}\n\nexport async function getNotionClientConfig(): Promise<NotionClientConfig> {\n const env = await readEnv();\n return {\n token: readRequired(env, \"NOTION_TOKEN\"),\n apiBaseUrl: readString(env, \"NOTION_API_BASE_URL\"),\n };\n}\n\nexport async function getNotionConfig(): Promise<NotionConfig> {\n const env = await readEnv();\n return {\n token: readRequired(env, \"NOTION_TOKEN\"),\n dataSourceId: readRequired(env, \"NOTION_DATA_SOURCE_ID\"),\n apiBaseUrl: readString(env, \"NOTION_API_BASE_URL\"),\n editBaseUrl: readString(env, \"NOTION_EDIT_BASE_URL\"),\n webhookVerificationToken: readString(\n env,\n \"NOTION_WEBHOOK_VERIFICATION_TOKEN\"\n ),\n };\n}\n\nexport async function getNotionWebhookVerificationToken(): Promise<\n string | undefined\n> {\n const env = await readEnv();\n return readString(env, \"NOTION_WEBHOOK_VERIFICATION_TOKEN\");\n}\n\nexport async function getNotionConfigForModel(\n model: NotionContentModelLike\n): Promise<NotionConfig> {\n const env = await readEnv();\n const dataSourceId =\n readString(env, model.source.dataSourceEnv) ??\n model.source.defaultDataSourceId;\n if (!dataSourceId) {\n throw new Error(`Missing required Notion env: ${model.source.dataSourceEnv}`);\n }\n\n return {\n token: readRequired(env, model.source.tokenEnv),\n dataSourceId,\n apiBaseUrl: readString(env, \"NOTION_API_BASE_URL\"),\n editBaseUrl: readString(env, \"NOTION_EDIT_BASE_URL\"),\n webhookVerificationToken: readString(\n env,\n \"NOTION_WEBHOOK_VERIFICATION_TOKEN\"\n ),\n };\n}\n","import type { NotionBlock } from \"./types\";\n\ntype BlockChildrenListResponse = {\n results?: unknown[];\n has_more?: boolean;\n next_cursor?: string | null;\n};\n\nexport type NotionBlockClient = {\n blocks: {\n children: {\n list: (args: {\n block_id: string;\n page_size?: number;\n start_cursor?: string;\n }) => Promise<BlockChildrenListResponse>;\n };\n };\n};\n\nfunction normalizeBlock(input: unknown): NotionBlock | null {\n if (!input || typeof input !== \"object\") return null;\n const block = input as NotionBlock;\n return block.id && block.type ? block : null;\n}\n\nexport async function listBlockChildren(\n client: NotionBlockClient,\n blockId: string\n): Promise<NotionBlock[]> {\n const results: NotionBlock[] = [];\n let cursor: string | undefined;\n\n do {\n const response = await client.blocks.children.list({\n block_id: blockId,\n page_size: 100,\n ...(cursor ? { start_cursor: cursor } : {}),\n });\n\n for (const item of response.results ?? []) {\n const block = normalizeBlock(item);\n if (block) results.push(block);\n }\n\n cursor = response.next_cursor ?? undefined;\n if (!response.has_more) break;\n } while (cursor);\n\n return results;\n}\n\nexport async function listBlockChildrenDeep(\n client: NotionBlockClient,\n blockId: string,\n options?: { maxDepth?: number }\n): Promise<NotionBlock[]> {\n const maxDepth = options?.maxDepth ?? 6;\n\n async function visit(id: string, depth: number): Promise<NotionBlock[]> {\n const children = await listBlockChildren(client, id);\n if (depth >= maxDepth) return children;\n\n return Promise.all(\n children.map(async (block) => {\n if (!block.has_children) return block;\n return {\n ...block,\n children: await visit(block.id, depth + 1),\n };\n })\n );\n }\n\n return visit(blockId, 0);\n}\n","import type { NotionBlock, NotionRichTextPart } from \"./types\";\n\ntype RichTextContainer = {\n rich_text?: NotionRichTextPart[];\n caption?: NotionRichTextPart[];\n title?: NotionRichTextPart[];\n cells?: NotionRichTextPart[][];\n expression?: string;\n};\n\nfunction typedValue(block: NotionBlock): RichTextContainer {\n return (block[block.type] ?? {}) as RichTextContainer;\n}\n\nfunction richTextPlainText(parts: unknown) {\n if (!Array.isArray(parts)) return \"\";\n return parts\n .map((part: NotionRichTextPart) =>\n part.type === \"equation\"\n ? part.equation?.expression ?? \"\"\n : part.plain_text ?? part.text?.content ?? \"\"\n )\n .join(\"\");\n}\n\nfunction blockPlainText(block: NotionBlock) {\n const value = typedValue(block);\n const text = [\n richTextPlainText(value.rich_text),\n richTextPlainText(value.caption),\n richTextPlainText(value.title),\n value.expression ?? \"\",\n ...(value.cells ?? []).map(richTextPlainText),\n ];\n\n return text.filter(Boolean).join(\" \");\n}\n\nexport function flattenNotionBlockText(blocks: readonly NotionBlock[]): string {\n const parts: string[] = [];\n\n function visit(block: NotionBlock) {\n const text = blockPlainText(block).trim();\n if (text) parts.push(text);\n for (const child of block.children ?? []) {\n visit(child);\n }\n }\n\n for (const block of blocks) visit(block);\n\n return parts\n .join(\" \")\n .normalize(\"NFKC\")\n .replace(/\\s+/g, \" \")\n .trim();\n}\n","import type { KeyValueCacheAdapter } from \"../platform/runtime\";\nimport type { NotionBlock } from \"./types\";\n\nconst CACHE_VERSION = \"v2\";\nexport const NOTION_LIST_CACHE_TTL_SECONDS = 300;\nexport const NOTION_BLOCKS_CACHE_TTL_SECONDS = 60 * 60 * 24;\nconst CONTENT_CACHE_READ_TTL_SECONDS = 60;\n\nexport type NotionContentCacheDeleteResult = {\n ok: boolean;\n skipped: boolean;\n deleted: string[];\n failed: Array<{ key?: string; error: string }>;\n};\n\ntype CacheableValue = Record<string, unknown> | unknown[] | null;\n\nfunction modelPrefix(modelId: string) {\n return `notion:${CACHE_VERSION}:${modelId}`;\n}\n\nexport function notionModelListCacheKey(modelId: string) {\n return `${modelPrefix(modelId)}:list`;\n}\n\nfunction versionSegment(value?: string) {\n const version = String(value ?? \"\").trim();\n return version ? `:v:${encodeURIComponent(version)}` : \"\";\n}\n\nexport function notionPageBlocksCacheKey(\n modelId: string,\n pageId: string,\n cacheVersion?: string\n) {\n return `${modelPrefix(modelId)}:page:${pageId}:blocks${versionSegment(cacheVersion)}`;\n}\n\nexport function notionModelCachePrefix(modelId: string) {\n return `${modelPrefix(modelId)}:`;\n}\n\nexport function notionPageCachePrefix(modelId: string, pageId: string) {\n return `${modelPrefix(modelId)}:page:${pageId}:`;\n}\n\nfunction logNotionContentCache(fields: Record<string, unknown>) {\n try {\n console.log(JSON.stringify({ tag: \"notion_content_cache\", ...fields }));\n } catch {\n // Ignore logging serialization errors.\n }\n}\n\nexport async function getCachedNotionValue<T extends CacheableValue>(\n cache: KeyValueCacheAdapter | null | undefined,\n key: string\n): Promise<T | null> {\n if (!cache) return null;\n try {\n const value = await cache.get<T>(key, {\n cacheTtl: CONTENT_CACHE_READ_TTL_SECONDS,\n });\n logNotionContentCache({\n op: \"get\",\n key,\n hit: value !== null,\n cache: cache.kind,\n });\n return value;\n } catch (error) {\n logNotionContentCache({\n op: \"get_error\",\n key,\n cache: cache.kind,\n message: error instanceof Error ? error.message : String(error),\n });\n return null;\n }\n}\n\nexport async function putCachedNotionValue<T extends CacheableValue>(\n cache: KeyValueCacheAdapter | null | undefined,\n key: string,\n value: T,\n options?: { expirationTtl?: number }\n) {\n if (!cache) return;\n try {\n await cache.put(key, value, {\n expirationTtl: options?.expirationTtl ?? NOTION_BLOCKS_CACHE_TTL_SECONDS,\n metadata: {\n source: \"notion\",\n cachedAt: new Date().toISOString(),\n },\n });\n logNotionContentCache({\n op: \"put\",\n key,\n cache: cache.kind,\n });\n } catch (error) {\n logNotionContentCache({\n op: \"put_error\",\n key,\n cache: cache.kind,\n message: error instanceof Error ? error.message : String(error),\n });\n }\n}\n\nexport async function getCachedNotionBlocks(\n cache: KeyValueCacheAdapter | null | undefined,\n input: { modelId: string; pageId: string; cacheVersion?: string }\n) {\n return getCachedNotionValue<NotionBlock[]>(\n cache,\n notionPageBlocksCacheKey(input.modelId, input.pageId, input.cacheVersion)\n );\n}\n\nexport async function putCachedNotionBlocks(\n cache: KeyValueCacheAdapter | null | undefined,\n input: {\n modelId: string;\n pageId: string;\n cacheVersion?: string;\n blocks: NotionBlock[];\n }\n) {\n await putCachedNotionValue(\n cache,\n notionPageBlocksCacheKey(input.modelId, input.pageId, input.cacheVersion),\n input.blocks,\n { expirationTtl: NOTION_BLOCKS_CACHE_TTL_SECONDS }\n );\n}\n\nexport async function deleteNotionContentCache(input: {\n modelId: string;\n pageId?: string;\n routeId?: string;\n previousRouteId?: string;\n cache?: KeyValueCacheAdapter | null;\n getCache?: () => KeyValueCacheAdapter | null;\n}): Promise<NotionContentCacheDeleteResult> {\n let cache: KeyValueCacheAdapter | null;\n try {\n cache = input.cache ?? input.getCache?.() ?? null;\n } catch (error) {\n return {\n ok: false,\n skipped: true,\n deleted: [],\n failed: [\n {\n error: error instanceof Error ? error.message : String(error),\n },\n ],\n };\n }\n\n if (!cache) {\n return { ok: true, skipped: true, deleted: [], failed: [] };\n }\n\n const prefixes = new Set<string>();\n if (input.pageId) {\n prefixes.add(notionPageCachePrefix(input.modelId, input.pageId));\n } else {\n prefixes.add(notionModelCachePrefix(input.modelId));\n }\n\n prefixes.add(notionModelListCacheKey(input.modelId));\n\n const deleted: string[] = [];\n const failed: Array<{ key?: string; error: string }> = [];\n\n for (const prefix of prefixes) {\n let cursor: string | undefined;\n do {\n try {\n const result = await cache.list({ prefix, cursor, limit: 100 });\n for (const { name } of result.keys) {\n try {\n await cache.delete(name);\n deleted.push(name);\n } catch (error) {\n failed.push({\n key: name,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n cursor = result.listComplete ? undefined : result.cursor;\n } catch (error) {\n failed.push({\n key: prefix,\n error: error instanceof Error ? error.message : String(error),\n });\n cursor = undefined;\n }\n } while (cursor);\n }\n\n return {\n ok: failed.length === 0,\n skipped: false,\n deleted,\n failed,\n };\n}\n","import type { NotionBlock, NotionFileSource, NotionPageLike } from \"./types\";\n\ntype FileLike = {\n type?: string;\n external?: { url?: string };\n file?: { url?: string; expiry_time?: string };\n name?: string;\n};\n\nfunction stripLeadingSlash(value: string) {\n return value.startsWith(\"/\") ? value.slice(1) : value;\n}\n\nfunction encodePathPart(value: string) {\n return encodeURIComponent(stripLeadingSlash(value));\n}\n\nfunction appendVersion(path: string, version?: string) {\n const value = String(version ?? \"\").trim();\n if (!value) return path;\n return `${path}?${new URLSearchParams({ v: value })}`;\n}\n\nfunction blockVersion(block: NotionBlock): string | undefined {\n return typeof block.last_edited_time === \"string\"\n ? block.last_edited_time\n : undefined;\n}\n\nexport function notionPageCoverMediaPath(pageId: string): string {\n return `/api/notion/media/page/${encodePathPart(pageId)}/cover`;\n}\n\nexport function notionPagePropertyMediaPath(\n pageId: string,\n propertyName: string\n): string {\n return `/api/notion/media/page/${encodePathPart(pageId)}/property/${encodePathPart(propertyName)}`;\n}\n\nexport function notionBlockMediaPath(blockId: string): string {\n return `/api/notion/media/block/${encodePathPart(blockId)}`;\n}\n\nexport function normalizeNotionFileSource(input: unknown): NotionFileSource | null {\n const file = input as FileLike | null | undefined;\n if (!file || typeof file !== \"object\") return null;\n\n if (file.type === \"external\") {\n const url = String(file.external?.url ?? \"\").trim();\n return url ? { type: \"external\", url } : null;\n }\n\n if (file.type === \"file\") {\n const url = String(file.file?.url ?? \"\").trim();\n if (!url) return null;\n return {\n type: \"file\",\n url,\n expiryTime: String(file.file?.expiry_time ?? \"\").trim() || null,\n };\n }\n\n return null;\n}\n\nexport function resolveNotionFileUrl(input: unknown): string | null {\n return normalizeNotionFileSource(input)?.url ?? null;\n}\n\nexport function isNotionHostedFile(input: unknown): boolean {\n return normalizeNotionFileSource(input)?.type === \"file\";\n}\n\nexport function pickFirstFilesPropertyValue(property: unknown): unknown | null {\n const value = property as { type?: string; files?: unknown[] } | null | undefined;\n if (!value || value.type !== \"files\" || !Array.isArray(value.files)) {\n return null;\n }\n return value.files[0] ?? null;\n}\n\nexport function pickPageCoverFile(page: NotionPageLike): unknown | null {\n return page.cover ?? null;\n}\n\nexport function coverImageUrlForPage(\n page: NotionPageLike,\n coverPropertyName = \"Cover\"\n): string | null {\n const propertyFile = pickFirstFilesPropertyValue(\n page.properties?.[coverPropertyName]\n );\n const propertySource = normalizeNotionFileSource(propertyFile);\n if (propertySource) {\n return appendVersion(\n notionPagePropertyMediaPath(page.id, coverPropertyName),\n page.last_edited_time\n );\n }\n\n const coverSource = normalizeNotionFileSource(pickPageCoverFile(page));\n if (coverSource) {\n return appendVersion(notionPageCoverMediaPath(page.id), page.last_edited_time);\n }\n\n return null;\n}\n\nexport function fileObjectForMediaBlock(block: NotionBlock): unknown | null {\n const typed = block[block.type] as Record<string, unknown> | undefined;\n if (!typed || typeof typed !== \"object\") return null;\n\n if (\n block.type === \"image\" ||\n block.type === \"video\" ||\n block.type === \"file\" ||\n block.type === \"pdf\" ||\n block.type === \"audio\"\n ) {\n return typed;\n }\n\n return null;\n}\n\nexport function mediaUrlForBlock(block: NotionBlock): string | null {\n const source = normalizeNotionFileSource(fileObjectForMediaBlock(block));\n if (!source) return null;\n if (source.type === \"external\" && block.type !== \"image\") {\n return source.url;\n }\n return appendVersion(notionBlockMediaPath(block.id), blockVersion(block));\n}\n\nexport function firstImageUrlFromBlocks(blocks: NotionBlock[]): string | null {\n for (const block of blocks) {\n if (block.type === \"image\") {\n const imageUrl = mediaUrlForBlock(block);\n if (imageUrl) return imageUrl;\n }\n\n const nested = block.children?.length\n ? firstImageUrlFromBlocks(block.children)\n : null;\n if (nested) return nested;\n }\n\n return null;\n}\n\nexport function publicMediaBlockForApi(block: NotionBlock): NotionBlock {\n const value = block[block.type];\n const source = normalizeNotionFileSource(value);\n const children = block.children?.map(publicMediaBlockForApi);\n\n if (!source) {\n return children ? { ...block, children } : { ...block };\n }\n\n const path = appendVersion(notionBlockMediaPath(block.id), blockVersion(block));\n const publicValue =\n source.type === \"external\"\n ? block.type === \"image\"\n ? {\n ...(value as Record<string, unknown>),\n external: { url: path },\n }\n : value\n : {\n ...(value as Record<string, unknown>),\n file: {\n url: path,\n expiry_time: null,\n },\n };\n\n return {\n ...block,\n [block.type]: publicValue,\n ...(children ? { children } : {}),\n };\n}\n\nexport function gatedMediaBlockForApi(\n block: NotionBlock,\n options?: { accessUrlForBlock?: (block: NotionBlock) => string | null }\n): NotionBlock {\n const value = block[block.type];\n const source = normalizeNotionFileSource(value);\n const children = block.children?.map((child) =>\n gatedMediaBlockForApi(child, options)\n );\n\n if (block.type !== \"video\" || !source) {\n const publicBlock = publicMediaBlockForApi(block);\n return children ? { ...publicBlock, children } : publicBlock;\n }\n\n const gatedValue: Record<string, unknown> = {\n ...(value as Record<string, unknown>),\n gated: true,\n access_url: options?.accessUrlForBlock?.(block) ?? null,\n };\n\n if (source.type === \"external\") {\n gatedValue.external = { url: null };\n } else {\n gatedValue.file = {\n url: null,\n expiry_time: null,\n };\n }\n\n return {\n ...block,\n video: gatedValue,\n ...(children ? { children } : {}),\n };\n}\n\nexport function isDirectVideoUrl(url: string): boolean {\n try {\n const parsed = new URL(url);\n return /\\.(mp4|webm|mov|m4v)(?:$|\\?)/i.test(parsed.pathname);\n } catch {\n return false;\n }\n}\n\nexport function videoEmbedUrl(url: string): string | null {\n try {\n const parsed = new URL(url);\n const host = parsed.hostname.replace(/^www\\./, \"\");\n\n if (host === \"youtube.com\" || host === \"m.youtube.com\") {\n const id = parsed.searchParams.get(\"v\");\n return id ? `https://www.youtube.com/embed/${encodeURIComponent(id)}` : null;\n }\n\n if (host === \"youtu.be\") {\n const id = parsed.pathname.split(\"/\").filter(Boolean)[0];\n return id ? `https://www.youtube.com/embed/${encodeURIComponent(id)}` : null;\n }\n\n if (host === \"youtube-nocookie.com\") {\n return parsed.toString();\n }\n\n if (host === \"vimeo.com\") {\n const id = parsed.pathname.split(\"/\").filter(Boolean)[0];\n return id ? `https://player.vimeo.com/video/${encodeURIComponent(id)}` : null;\n }\n\n if (host === \"player.vimeo.com\") {\n return parsed.toString();\n }\n } catch {\n return null;\n }\n\n return null;\n}\n","import { cache } from \"react\";\nimport { listBlockChildrenDeep, type NotionBlockClient } from \"./blocks\";\nimport { createNotionClient } from \"./client\";\nimport { getNotionConfigForModel, hasNotionModelConfig } from \"./config\";\nimport { coverImageUrlForPage } from \"./media\";\nimport {\n getCheckboxProperty,\n getDateProperty,\n getNumberProperty,\n getRichTextProperty,\n getSelectProperty,\n getTagsProperty,\n isValidPublicSlug,\n notionPageEditUrl,\n pickDescriptionFallback,\n} from \"./property-mappers\";\nimport type {\n NotionBlock,\n NotionFieldMap,\n NotionGenericContentModel,\n NotionPageLike,\n} from \"./types\";\n\ntype PropertyMap = Record<string, unknown>;\n\ntype DataSourceQueryResponse = {\n results?: unknown[];\n has_more?: boolean;\n next_cursor?: string | null;\n};\n\ntype QueryDataSourceInput = {\n startCursor?: string;\n};\n\nexport type GenericContentListItem = {\n pageId: string;\n slug: string;\n title: string;\n description: string;\n date: string;\n tags: string[];\n coverImage: string | null;\n published: boolean;\n editUrl: string | null;\n properties: Record<string, string | string[] | number | boolean>;\n};\n\nexport type GenericContentDetail = GenericContentListItem & {\n blocks: NotionBlock[];\n};\n\nexport type GenericContentSourceDeps<\n TFields extends NotionFieldMap = NotionFieldMap,\n> = {\n model: NotionGenericContentModel & { source: { fields: TFields } };\n dataSourceId: string;\n queryDataSource: (\n input?: QueryDataSourceInput\n ) => Promise<DataSourceQueryResponse>;\n getPageBlocks: (pageId: string) => Promise<NotionBlock[]>;\n editBaseUrl?: string;\n};\n\nfunction normalizePage(input: unknown): NotionPageLike | null {\n if (!input || typeof input !== \"object\") return null;\n const page = input as NotionPageLike;\n return page.id ? page : null;\n}\n\nfunction firstFieldName(value: string | readonly string[] | undefined) {\n if (Array.isArray(value)) return value[0];\n return value;\n}\n\nfunction getFieldName(fields: NotionFieldMap, key: string) {\n return firstFieldName(fields[key]);\n}\n\nfunction pickPublishedFlagForModel(\n properties: PropertyMap,\n fields: NotionFieldMap\n) {\n const publishedField = getFieldName(fields, \"published\") ?? \"Published\";\n const published = properties[publishedField] as\n | Record<string, unknown>\n | undefined;\n if (published?.type === \"checkbox\") return Boolean(published.checkbox);\n\n const statusField = getFieldName(fields, \"status\") ?? \"Status\";\n const status = properties[statusField] as Record<string, unknown> | undefined;\n if (status?.type === \"status\") {\n const value = status.status as { name?: string } | null | undefined;\n return String(value?.name ?? \"\").trim().toLowerCase() === \"published\";\n }\n if (status?.type === \"select\") {\n const value = status.select as { name?: string } | null | undefined;\n return String(value?.name ?? \"\").trim().toLowerCase() === \"published\";\n }\n\n return true;\n}\n\nfunction coverImageUrlForModel(page: NotionPageLike, fields: NotionFieldMap) {\n const coverField = fields.cover;\n if (Array.isArray(coverField)) {\n for (const field of coverField) {\n const imageUrl = coverImageUrlForPage(page, field);\n if (imageUrl) return imageUrl;\n }\n }\n if (typeof coverField === \"string\") {\n return coverImageUrlForPage(page, coverField);\n }\n return coverImageUrlForPage(page);\n}\n\nfunction mapExtraProperties(properties: PropertyMap, fields: NotionFieldMap) {\n const result: Record<string, string | string[] | number | boolean> = {};\n for (const [key, value] of Object.entries(fields)) {\n const field = firstFieldName(value);\n if (!field) continue;\n const property = properties[field] as Record<string, unknown> | undefined;\n if (!property?.type) {\n result[key] = getRichTextProperty(properties, field);\n continue;\n }\n\n if (property.type === \"multi_select\") {\n result[key] = getTagsProperty(properties, field);\n continue;\n }\n if (property.type === \"select\" || property.type === \"status\") {\n result[key] = getSelectProperty(properties, field);\n continue;\n }\n if (property.type === \"number\") {\n result[key] = getNumberProperty(properties, field);\n continue;\n }\n if (property.type === \"checkbox\") {\n result[key] = getCheckboxProperty(properties, field);\n continue;\n }\n\n result[key] = getRichTextProperty(properties, field);\n }\n return result;\n}\n\nexport function mapNotionPageToGenericContentItem<\n TFields extends NotionFieldMap,\n>(\n model: { id: string; source: { fields: TFields } },\n page: NotionPageLike,\n options?: { editBaseUrl?: string }\n): GenericContentListItem {\n const fields = model.source.fields;\n const properties = isRecord(page.properties) ? page.properties : {};\n const titleField = getFieldName(fields, \"title\");\n const slugField = getFieldName(fields, \"slug\");\n const descriptionField = getFieldName(fields, \"description\");\n const dateField = getFieldName(fields, \"date\");\n const tagsField = getFieldName(fields, \"tags\");\n\n const title = titleField ? getRichTextProperty(properties, titleField) : \"\";\n const slug = slugField\n ? getRichTextProperty(properties, slugField).toLowerCase()\n : page.id.replaceAll(\"-\", \"\").toLowerCase();\n const description = pickDescriptionFallback(\n descriptionField ? getRichTextProperty(properties, descriptionField) : \"\",\n title\n );\n\n return {\n pageId: page.id,\n slug,\n title,\n description,\n date: dateField ? getDateProperty(properties, dateField) : \"\",\n tags: tagsField ? getTagsProperty(properties, tagsField) : [],\n coverImage: coverImageUrlForModel(page, fields),\n published: pickPublishedFlagForModel(properties, fields),\n editUrl: notionPageEditUrl(page.id, options?.editBaseUrl),\n properties: mapExtraProperties(properties, fields),\n };\n}\n\nexport function isRenderableGenericContentItem(item: GenericContentListItem) {\n return Boolean(item.published && item.title && item.slug && isValidPublicSlug(item.slug));\n}\n\nexport function createGenericNotionContentSource<\n TFields extends NotionFieldMap,\n>(deps: GenericContentSourceDeps<TFields>) {\n return {\n async listItems(): Promise<GenericContentListItem[]> {\n const pages: NotionPageLike[] = [];\n let cursor: string | undefined;\n\n do {\n const response = await deps.queryDataSource({ startCursor: cursor });\n for (const item of response.results ?? []) {\n const page = normalizePage(item);\n if (page) pages.push(page);\n }\n\n cursor = response.next_cursor ?? undefined;\n if (!response.has_more) break;\n } while (cursor);\n\n return pages\n .map((page) =>\n mapNotionPageToGenericContentItem(deps.model, page, {\n editBaseUrl: deps.editBaseUrl,\n })\n )\n .filter(isRenderableGenericContentItem)\n .sort((a, b) => b.date.localeCompare(a.date));\n },\n\n async getItemBySlug(slug: string): Promise<GenericContentDetail | null> {\n const items = await this.listItems();\n const item = items.find((candidate) => candidate.slug === slug);\n if (!item) return null;\n return {\n ...item,\n blocks: await deps.getPageBlocks(item.pageId),\n };\n },\n };\n}\n\nasync function createDefaultGenericSource<\n TFields extends NotionFieldMap,\n>(model: NotionGenericContentModel & { source: { fields: TFields } }) {\n if (!(await hasNotionModelConfig(model))) return null;\n\n const config = await getNotionConfigForModel(model);\n const client = createNotionClient(config);\n return createGenericNotionContentSource({\n model,\n dataSourceId: config.dataSourceId,\n editBaseUrl: config.editBaseUrl,\n queryDataSource: async ({ startCursor } = {}) =>\n client.dataSources.query({\n data_source_id: config.dataSourceId,\n page_size: model.source.query.pageSize,\n sorts: model.source.query.sorts\n ? [...model.source.query.sorts]\n : undefined,\n filter_properties: model.source.query.filterProperties\n ? [...model.source.query.filterProperties]\n : undefined,\n ...(startCursor ? { start_cursor: startCursor } : {}),\n }),\n getPageBlocks: (pageId) =>\n listBlockChildrenDeep(client as unknown as NotionBlockClient, pageId),\n });\n}\n\nconst sourceCache = cache(createDefaultGenericSource);\n\nexport async function listGenericNotionContent<\n TFields extends NotionFieldMap,\n>(model: NotionGenericContentModel & { source: { fields: TFields } }) {\n const source = await sourceCache(model);\n if (!source) return [];\n return source.listItems();\n}\n\nexport async function getGenericNotionContentBySlug<\n TFields extends NotionFieldMap,\n>(model: NotionGenericContentModel & { source: { fields: TFields } }, slug: string) {\n const source = await sourceCache(model);\n if (!source) return null;\n return source.getItemBySlug(slug);\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return Boolean(value && typeof value === \"object\");\n}\n","type PropertyMap = Record<string, unknown>;\n\ntype TextPart = {\n plain_text?: string;\n};\n\nexport function isRecord(value: unknown): value is Record<string, unknown> {\n return Boolean(value && typeof value === \"object\");\n}\n\nfunction getPlainText(parts: unknown): string {\n if (!Array.isArray(parts)) return \"\";\n return parts\n .map((part: TextPart) => part.plain_text ?? \"\")\n .join(\"\")\n .trim();\n}\n\nfunction getProperty(properties: PropertyMap, key: string) {\n return properties[key] as Record<string, unknown> | undefined;\n}\n\nfunction firstPropertyOfType(properties: PropertyMap, type: string) {\n return Object.values(properties).find(\n (property): property is Record<string, unknown> =>\n isRecord(property) && property.type === type\n );\n}\n\nexport function getFirstTitleProperty(properties: PropertyMap): string {\n const property = firstPropertyOfType(properties, \"title\");\n return property ? getPlainText(property.title) : \"\";\n}\n\nexport function getRichTextProperty(properties: PropertyMap, key: string): string {\n const property = getProperty(properties, key);\n if (!property) return \"\";\n\n if (property.type === \"title\") return getPlainText(property.title);\n if (property.type === \"rich_text\") return getPlainText(property.rich_text);\n if (property.type === \"url\") return String(property.url ?? \"\").trim();\n if (property.type === \"email\") return String(property.email ?? \"\").trim();\n if (property.type === \"phone_number\") {\n return String(property.phone_number ?? \"\").trim();\n }\n\n return \"\";\n}\n\nexport function getDateProperty(properties: PropertyMap, key: string): string {\n const property = getProperty(properties, key);\n if (property?.type !== \"date\") return \"\";\n const date = property.date as { start?: string } | null | undefined;\n return String(date?.start ?? \"\").trim();\n}\n\nexport function getFirstDateProperty(properties: PropertyMap): string {\n const property = firstPropertyOfType(properties, \"date\");\n if (!property) return \"\";\n const date = property.date as { start?: string } | null | undefined;\n return String(date?.start ?? \"\").trim();\n}\n\nexport function getSelectProperty(properties: PropertyMap, key: string): string {\n const property = getProperty(properties, key);\n if (property?.type !== \"select\") return \"\";\n const select = property.select as { name?: string } | null | undefined;\n return String(select?.name ?? \"\").trim();\n}\n\nexport function getCheckboxProperty(properties: PropertyMap, key: string): boolean {\n const property = getProperty(properties, key);\n if (property?.type !== \"checkbox\") return false;\n return Boolean(property.checkbox);\n}\n\nexport function getNumberProperty(\n properties: PropertyMap,\n key: string,\n fallback = 0\n): number {\n const property = getProperty(properties, key);\n if (property?.type !== \"number\") return fallback;\n const value = Number(property.number);\n return Number.isFinite(value) ? value : fallback;\n}\n\nexport function getRelationPageIds(properties: PropertyMap, key: string): string[] {\n const property = getProperty(properties, key);\n if (property?.type !== \"relation\" || !Array.isArray(property.relation)) {\n return [];\n }\n\n return property.relation\n .map((item: { id?: string }) => String(item.id ?? \"\").trim())\n .filter(Boolean);\n}\n\nexport function getTagsProperty(properties: PropertyMap, key: string): string[] {\n const property = getProperty(properties, key);\n if (property?.type === \"multi_select\" && Array.isArray(property.multi_select)) {\n return property.multi_select\n .map((item: { name?: string }) => String(item.name ?? \"\").trim())\n .filter(Boolean);\n }\n\n if (property?.type === \"select\") {\n const select = property.select as { name?: string } | null | undefined;\n const name = String(select?.name ?? \"\").trim();\n return name ? [name] : [];\n }\n\n return [];\n}\n\nexport function getFirstTagsProperty(properties: PropertyMap): string[] {\n const multiSelect = firstPropertyOfType(properties, \"multi_select\");\n if (multiSelect && Array.isArray(multiSelect.multi_select)) {\n return multiSelect.multi_select\n .map((item: { name?: string }) => String(item.name ?? \"\").trim())\n .filter(Boolean);\n }\n\n const select = firstPropertyOfType(properties, \"select\");\n const name = String((select?.select as { name?: string } | null)?.name ?? \"\").trim();\n return name ? [name] : [];\n}\n\nexport function getAuthorProperty(properties: PropertyMap, key: string): string {\n const property = getProperty(properties, key);\n if (!property) return \"\";\n\n if (property.type === \"people\" && Array.isArray(property.people)) {\n return property.people\n .map((person: { name?: string; person?: { email?: string } }) =>\n String(person.name ?? person.person?.email ?? \"\").trim()\n )\n .filter(Boolean)\n .join(\", \");\n }\n\n return getRichTextProperty(properties, key);\n}\n\nexport function getFirstPeopleProperty(properties: PropertyMap): string {\n const property = firstPropertyOfType(properties, \"people\");\n if (!property || !Array.isArray(property.people)) return \"\";\n\n return property.people\n .map((person: { name?: string; person?: { email?: string } }) =>\n String(person.name ?? person.person?.email ?? \"\").trim()\n )\n .filter(Boolean)\n .join(\", \");\n}\n\nexport function pickPublishedFlag(properties: PropertyMap): boolean {\n const published = getProperty(properties, \"Published\");\n if (published?.type === \"checkbox\") {\n return Boolean(published.checkbox);\n }\n\n const status = getProperty(properties, \"Status\");\n if (status?.type === \"status\") {\n const statusValue = status.status as { name?: string } | null | undefined;\n return String(statusValue?.name ?? \"\").trim().toLowerCase() === \"published\";\n }\n\n if (status?.type === \"select\") {\n const statusValue = status.select as { name?: string } | null | undefined;\n return String(statusValue?.name ?? \"\").trim().toLowerCase() === \"published\";\n }\n\n return false;\n}\n\nexport function pickDescriptionFallback(description: string, title: string): string {\n return description.trim() || title.trim();\n}\n\nexport function isValidPublicSlug(slug: string): boolean {\n return /^[a-z0-9][a-z0-9-]{0,79}$/.test(slug);\n}\n\nexport function notionPageEditUrl(pageId: string, editBaseUrl?: string): string {\n const compactPageId = pageId.replaceAll(\"-\", \"\");\n if (editBaseUrl?.includes(\"{pageId}\")) {\n return editBaseUrl.replaceAll(\"{pageId}\", compactPageId);\n }\n return `https://www.notion.so/${compactPageId}`;\n}\n\n/**\n * Normalize a Notion page id (with or without dashes) to a compact lowercase\n * string. Used as a stable identifier in URLs and cache keys.\n */\nexport function compactNotionId(id: string): string {\n return id.replaceAll(\"-\", \"\").toLowerCase();\n}\n","import type { KeyValueCacheAdapter } from \"../platform/runtime\";\nimport { createNotionClient } from \"./client\";\nimport { getNotionConfigForModel } from \"./config\";\nimport {\n compactNotionId,\n getRichTextProperty,\n getSelectProperty,\n isValidPublicSlug,\n} from \"./property-mappers\";\nimport type {\n NotionFieldMap,\n NotionGenericContentModel,\n NotionPageLike,\n} from \"./types\";\n\ntype JsonRecord = Record<string, unknown>;\n\ntype StoredWebhookVerificationToken = {\n token: string;\n updatedAt: string;\n};\n\nconst WEBHOOK_VERIFICATION_TOKEN_CACHE_KEY =\n \"notion:webhook:verification-token:v1\";\n\nexport type NotionWebhookParseResult =\n | { type: \"verification\"; verificationToken: string }\n | { type: \"events\"; events: NotionWebhookEvent[] };\n\nexport type NotionWebhookEvent = {\n id?: string;\n eventType: string;\n modelId: string;\n pageId?: string;\n dataSourceId?: string;\n routeId?: string;\n locale?: string;\n kind: \"publish\" | \"update\" | \"delete\";\n includeApi: boolean;\n reason: \"page\" | \"data_source\";\n};\n\n/**\n * Generic shape that callers (i.e. the starter) supply so the webhook can\n * route events to the correct content model. Each entry must expose the\n * Notion data source id that the model is bound to, and a hook to derive a\n * route id and locale from the Notion page payload.\n */\nexport type NotionWebhookModelRegistration<\n TFields extends NotionFieldMap = NotionFieldMap,\n> = NotionGenericContentModel & {\n source: { fields: TFields };\n resolveRouteId?: (page: JsonRecord) => string;\n resolveLocale?: (page: JsonRecord) => string;\n};\n\nexport type NotionPageRetriever = (\n pageId: string,\n model: NotionGenericContentModel\n) => Promise<NotionPageLike | null>;\n\nexport type NotionWebhookParseOptions<\n TFields extends NotionFieldMap = NotionFieldMap,\n> = {\n models: ReadonlyArray<NotionWebhookModelRegistration<TFields>>;\n /**\n * Optional override for resolving the data source id of a model. Defaults\n * to `process.env[model.source.dataSourceEnv] ?? model.source.defaultDataSourceId`.\n */\n getModelDataSourceId?: (model: NotionWebhookModelRegistration<TFields>) => string | null;\n};\n\nexport type InvalidationKind = \"publish\" | \"update\" | \"delete\";\n\n/**\n * Request shape consumed by the revalidation pipeline. Mirrors the starter's\n * `ContentRevalidateRequest`; duplicated here so the package does not need\n * a runtime dependency on the starter.\n */\nexport type NotionWebhookRevalidateRequest = {\n modelId: string;\n pageId?: string;\n routeId?: string;\n previousRouteId?: string;\n locale?: string;\n kind?: InvalidationKind;\n includeApi?: boolean;\n};\n\nfunction isRecord(value: unknown): value is JsonRecord {\n return Boolean(value && typeof value === \"object\" && !Array.isArray(value));\n}\n\nfunction readString(source: unknown, key: string) {\n if (!isRecord(source)) return \"\";\n const value = source[key];\n return typeof value === \"string\" ? value.trim() : \"\";\n}\n\nfunction asRecords(value: unknown) {\n if (Array.isArray(value)) return value.filter(isRecord);\n if (isRecord(value)) return [value];\n return [];\n}\n\nfunction nestedRecords(source: JsonRecord, ...keys: string[]) {\n const records: JsonRecord[] = [];\n for (const key of keys) {\n const value = source[key];\n if (isRecord(value)) records.push(value);\n }\n return records;\n}\n\nfunction firstString(...values: string[]) {\n return values.find(Boolean) ?? \"\";\n}\n\nfunction normalizeId(value: string) {\n return value.replaceAll(\"-\", \"\").toLowerCase();\n}\n\nfunction findIdByType(input: JsonRecord, type: string): string {\n const stack = [input];\n const seen = new Set<JsonRecord>();\n\n while (stack.length > 0) {\n const current = stack.pop();\n if (!current || seen.has(current)) continue;\n seen.add(current);\n\n if (readString(current, \"type\") === type) {\n const id = readString(current, \"id\");\n if (id) return id;\n }\n\n for (const value of Object.values(current)) {\n if (isRecord(value)) stack.push(value);\n if (Array.isArray(value)) {\n for (const item of value) {\n if (isRecord(item)) stack.push(item);\n }\n }\n }\n }\n\n return \"\";\n}\n\nfunction findDataSourceId(input: JsonRecord): string {\n const stack = [input];\n const seen = new Set<JsonRecord>();\n\n while (stack.length > 0) {\n const current = stack.pop();\n if (!current || seen.has(current)) continue;\n seen.add(current);\n\n const direct = firstString(\n readString(current, \"data_source_id\"),\n readString(current, \"source_id\")\n );\n if (direct) return direct;\n\n const type = readString(current, \"type\");\n if (type === \"data_source\") {\n const id = readString(current, \"id\");\n if (id) return id;\n }\n\n for (const value of Object.values(current)) {\n if (isRecord(value)) stack.push(value);\n if (Array.isArray(value)) {\n for (const item of value) {\n if (isRecord(item)) stack.push(item);\n }\n }\n }\n }\n\n return \"\";\n}\n\nfunction firstFieldName(value: string | readonly string[] | undefined) {\n if (Array.isArray(value)) return value[0];\n return value;\n}\n\nfunction defaultRouteIdFromProperties(\n model: NotionGenericContentModel,\n page: JsonRecord\n) {\n const properties = isRecord(page.properties) ? page.properties : {};\n const slugField = firstFieldName(model.source.fields.slug);\n const slug = slugField\n ? getRichTextProperty(properties, slugField).toLowerCase()\n : \"\";\n return isValidPublicSlug(slug) ? slug : \"\";\n}\n\nfunction defaultLocaleFromProperties(model: NotionGenericContentModel, page: JsonRecord) {\n const properties = isRecord(page.properties) ? page.properties : {};\n const localeField = firstFieldName(model.source.fields.locale);\n return localeField ? getSelectProperty(properties, localeField) : \"\";\n}\n\nfunction eventKind(eventType: string): \"publish\" | \"update\" | \"delete\" {\n if (eventType.includes(\".deleted\")) return \"delete\";\n if (eventType.includes(\".created\") || eventType.includes(\".undeleted\")) {\n return \"publish\";\n }\n return \"update\";\n}\n\nfunction defaultGetModelDataSourceId(\n model: NotionGenericContentModel\n): string | null {\n const fromEnv = process.env[model.source.dataSourceEnv];\n if (fromEnv) return fromEnv;\n return model.source.defaultDataSourceId ?? null;\n}\n\nfunction matchModelByDataSourceId<\n TFields extends NotionFieldMap,\n>(\n models: ReadonlyArray<NotionWebhookModelRegistration<TFields>>,\n dataSourceId: string,\n getDataSourceId: (model: NotionWebhookModelRegistration<TFields>) => string | null\n) {\n if (!dataSourceId) return null;\n const normalized = normalizeId(dataSourceId);\n return (\n models.find((model) => {\n const configured = getDataSourceId(model);\n return configured ? normalizeId(configured) === normalized : false;\n }) ?? null\n );\n}\n\nfunction findModelForEvent<\n TFields extends NotionFieldMap,\n>(\n models: ReadonlyArray<NotionWebhookModelRegistration<TFields>>,\n event: JsonRecord,\n page: JsonRecord | undefined,\n getDataSourceId: (model: NotionWebhookModelRegistration<TFields>) => string | null\n) {\n const modelId = firstString(\n readString(event, \"modelId\"),\n readString(event, \"model_id\"),\n readString(event.data, \"modelId\"),\n readString(event.data, \"model_id\")\n );\n if (modelId) {\n return models.find((model) => model.id === modelId) ?? null;\n }\n\n const dataSourceId = firstString(\n findDataSourceId(event),\n findIdByType(event, \"data_source\"),\n readString(event, \"data_source_id\"),\n readString(event.data, \"data_source_id\"),\n readString(page?.parent, \"data_source_id\"),\n readString(page?.parent, \"database_id\")\n );\n return matchModelByDataSourceId(models, dataSourceId, getDataSourceId);\n}\n\nfunction pageIdForEvent(event: JsonRecord, page?: JsonRecord) {\n return firstString(\n readString(page, \"id\"),\n findIdByType(event, \"page\"),\n readString(event, \"page_id\"),\n readString(event.data, \"page_id\")\n );\n}\n\nfunction pageForEvent(event: JsonRecord) {\n let fallback: JsonRecord | null = null;\n for (const record of [\n ...nestedRecords(isRecord(event.data) ? event.data : {}, \"page\", \"entity\"),\n ...nestedRecords(event, \"page\", \"entity\"),\n event,\n ]) {\n const type = readString(record, \"type\");\n if (type === \"page\" || readString(record, \"object\") === \"page\") {\n if (!readString(record, \"id\")) continue;\n if (isRecord(record.properties) || isRecord(record.parent)) return record;\n fallback ??= record;\n }\n }\n return fallback;\n}\n\nexport function parseNotionWebhookPayload<\n TFields extends NotionFieldMap = NotionFieldMap,\n>(\n payload: unknown,\n options: NotionWebhookParseOptions<TFields>\n): NotionWebhookParseResult {\n if (!isRecord(payload)) return { type: \"events\", events: [] };\n\n const verificationToken = readString(payload, \"verification_token\");\n if (verificationToken) {\n return { type: \"verification\", verificationToken };\n }\n\n const getDataSourceId = options.getModelDataSourceId ?? defaultGetModelDataSourceId;\n\n const events = asRecords(payload.events ?? payload.event ?? payload)\n .map((event) =>\n parseEvent(event, options.models, getDataSourceId)\n )\n .filter((event): event is NotionWebhookEvent => Boolean(event));\n\n return { type: \"events\", events };\n}\n\nfunction parseEvent<\n TFields extends NotionFieldMap,\n>(\n event: JsonRecord,\n models: ReadonlyArray<NotionWebhookModelRegistration<TFields>>,\n getDataSourceId: (model: NotionWebhookModelRegistration<TFields>) => string | null\n): NotionWebhookEvent | null {\n const eventType = firstString(readString(event, \"type\"), readString(event, \"event\"));\n if (!eventType) return null;\n const page = pageForEvent(event);\n const model = findModelForEvent(\n models,\n event,\n page ?? undefined,\n getDataSourceId\n );\n if (!model) return null;\n\n const routeId = page\n ? (model.resolveRouteId?.(page) ??\n defaultRouteIdFromProperties(model, page))\n : \"\";\n const locale = page\n ? (model.resolveLocale?.(page) ?? defaultLocaleFromProperties(model, page))\n : \"\";\n const dataSourceId = firstString(\n findDataSourceId(event),\n findIdByType(event, \"data_source\"),\n readString(event, \"data_source_id\"),\n readString(event.data, \"data_source_id\"),\n readString(page?.parent, \"data_source_id\"),\n readString(page?.parent, \"database_id\")\n );\n const dataSourceEvent =\n eventType.startsWith(\"data_source.\") || eventType.startsWith(\"database.\");\n return {\n id: readString(event, \"id\") || undefined,\n eventType,\n modelId: model.id,\n pageId: pageIdForEvent(event, page ?? undefined) || undefined,\n dataSourceId: dataSourceId || undefined,\n routeId: routeId || undefined,\n locale: locale || undefined,\n kind: eventKind(eventType),\n includeApi: true,\n reason: page && routeId ? \"page\" : dataSourceEvent ? \"data_source\" : \"page\",\n };\n}\n\nasync function defaultRetrieveNotionPage(\n pageId: string,\n model: NotionGenericContentModel\n) {\n const config = await getNotionConfigForModel(model);\n const client = createNotionClient(config);\n const page = await client.pages.retrieve({ page_id: pageId });\n return page as NotionPageLike;\n}\n\nfunction resolveWebhookEventRoute<\n TFields extends NotionFieldMap,\n>(\n event: NotionWebhookEvent,\n models: ReadonlyArray<NotionWebhookModelRegistration<TFields>>,\n retrievePage: NotionPageRetriever\n): Promise<NotionWebhookEvent> {\n if (event.routeId || !event.pageId) return Promise.resolve(event);\n if (event.kind === \"delete\") return Promise.resolve(event);\n\n const model = models.find((m) => m.id === event.modelId);\n if (!model) return Promise.resolve(event);\n\n return retrievePage(event.pageId, model)\n .then((page) => {\n if (!page) return event;\n const pageRecord = page as unknown as JsonRecord;\n const routeId =\n model.resolveRouteId?.(pageRecord) ??\n defaultRouteIdFromProperties(model, pageRecord);\n const locale =\n model.resolveLocale?.(pageRecord) ??\n defaultLocaleFromProperties(model, pageRecord);\n if (!routeId) return event;\n return {\n ...event,\n routeId,\n locale: locale || event.locale,\n reason: \"page\" as const,\n };\n })\n .catch((error) => {\n const err = error as { code?: string; status?: number; message?: string };\n console.warn(\n JSON.stringify({\n tag: \"notion_webhook_page_lookup_failed\",\n eventId: event.id,\n eventType: event.eventType,\n modelId: event.modelId,\n pageId: event.pageId,\n code: err?.code,\n status: err?.status,\n message: err?.message ?? String(error),\n })\n );\n return event;\n });\n}\n\nexport async function parseNotionWebhookPayloadWithPageLookup<\n TFields extends NotionFieldMap = NotionFieldMap,\n>(\n payload: unknown,\n options: NotionWebhookParseOptions<TFields> & {\n retrievePage?: NotionPageRetriever;\n lookupPages?: boolean;\n }\n): Promise<NotionWebhookParseResult> {\n const parsed = parseNotionWebhookPayload(payload, options);\n if (parsed.type === \"verification\") return parsed;\n if (options?.lookupPages === false) return parsed;\n\n const retrievePage = options?.retrievePage ?? defaultRetrieveNotionPage;\n return {\n type: \"events\",\n events: await Promise.all(\n parsed.events.map((event) =>\n resolveWebhookEventRoute(event, options.models, retrievePage)\n )\n ),\n };\n}\n\nexport function notionWebhookEventToRevalidateRequest(\n event: NotionWebhookEvent\n): NotionWebhookRevalidateRequest {\n return {\n modelId: event.modelId,\n pageId: event.pageId,\n routeId: event.routeId,\n locale: event.locale,\n kind: event.kind,\n includeApi: event.includeApi,\n };\n}\n\nexport async function signNotionWebhookBody(body: string, verificationToken: string) {\n const key = await crypto.subtle.importKey(\n \"raw\",\n new TextEncoder().encode(verificationToken),\n { name: \"HMAC\", hash: \"SHA-256\" },\n false,\n [\"sign\"]\n );\n const signature = await crypto.subtle.sign(\n \"HMAC\",\n key,\n new TextEncoder().encode(body)\n );\n return Array.from(new Uint8Array(signature))\n .map((byte) => byte.toString(16).padStart(2, \"0\"))\n .join(\"\");\n}\n\nfunction normalizeNotionSignature(signature: string | null) {\n const value = String(signature ?? \"\").trim();\n const prefixed = value.match(/^sha256=(.+)$/i);\n return prefixed && prefixed[1] ? prefixed[1].trim() : value;\n}\n\nexport async function verifyNotionWebhookSignature(input: {\n body: string;\n signature: string | null;\n verificationToken?: string | null;\n}) {\n const token = String(input.verificationToken ?? \"\").trim();\n const signature = normalizeNotionSignature(input.signature);\n if (!token || !signature) return false;\n const expected = await signNotionWebhookBody(input.body, token);\n if (expected.length !== signature.length) return false;\n\n let diff = 0;\n for (let index = 0; index < expected.length; index += 1) {\n diff |= expected.charCodeAt(index) ^ signature.charCodeAt(index);\n }\n return diff === 0;\n}\n\nexport async function verifyNotionWebhookSignatureWithTokens(input: {\n body: string;\n signature: string | null;\n verificationTokens: Array<string | null | undefined>;\n}) {\n const tokens = Array.from(\n new Set(input.verificationTokens.map((token) => String(token ?? \"\").trim()))\n ).filter(Boolean);\n\n for (const token of tokens) {\n if (\n await verifyNotionWebhookSignature({\n body: input.body,\n signature: input.signature,\n verificationToken: token,\n })\n ) {\n return true;\n }\n }\n\n return false;\n}\n\nfunction readStoredWebhookVerificationToken(value: unknown) {\n if (typeof value === \"string\") return value.trim() || null;\n if (!isRecord(value)) return null;\n\n const token = readString(value, \"token\");\n return token || null;\n}\n\nexport async function getStoredNotionWebhookVerificationToken(\n cache: KeyValueCacheAdapter | null | undefined\n) {\n if (!cache) return null;\n\n try {\n const value = await cache.get<StoredWebhookVerificationToken | string>(\n WEBHOOK_VERIFICATION_TOKEN_CACHE_KEY,\n { cacheTtl: 60 }\n );\n return readStoredWebhookVerificationToken(value);\n } catch (error) {\n console.warn(\n JSON.stringify({\n tag: \"notion_webhook_token_lookup_failed\",\n message: error instanceof Error ? error.message : String(error),\n })\n );\n return null;\n }\n}\n\nexport async function putStoredNotionWebhookVerificationToken(\n cache: KeyValueCacheAdapter | null | undefined,\n token: string\n) {\n const normalized = token.trim();\n if (!cache || !normalized) return false;\n\n await cache.put<StoredWebhookVerificationToken>(\n WEBHOOK_VERIFICATION_TOKEN_CACHE_KEY,\n {\n token: normalized,\n updatedAt: new Date().toISOString(),\n },\n {\n metadata: {\n source: \"notion-webhook\",\n },\n }\n );\n return true;\n}\n\n// Re-exported for backwards compatibility with the starter.\nexport { compactNotionId };\n"],"mappings":"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","names":["cache","isRecord","isRecord","readString","firstFieldName","cache"]}
|
package/dist/pages/index.d.ts
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { NotionBlock, NotionPageLike } from '../notion/types.js';
|
|
2
2
|
|
|
3
3
|
type SitePageLayout = "home" | "default" | "legal" | "content-list";
|
|
4
|
+
type SitePageBlockRef = {
|
|
5
|
+
slug: string;
|
|
6
|
+
variant?: string;
|
|
7
|
+
order?: number;
|
|
8
|
+
};
|
|
4
9
|
type SitePageFields = {
|
|
5
10
|
title: string;
|
|
6
11
|
key: string;
|
|
@@ -20,6 +25,7 @@ type SitePageFields = {
|
|
|
20
25
|
footerGroup: string;
|
|
21
26
|
footerOrder: string;
|
|
22
27
|
contentSource: string;
|
|
28
|
+
blocks: string;
|
|
23
29
|
cover: string;
|
|
24
30
|
};
|
|
25
31
|
type SitePage = {
|
|
@@ -45,6 +51,7 @@ type SitePage = {
|
|
|
45
51
|
contentSource: string;
|
|
46
52
|
coverImage: string | null;
|
|
47
53
|
editUrl: string | null;
|
|
54
|
+
structuredBlocks: SitePageBlockRef[];
|
|
48
55
|
blocks: NotionBlock[];
|
|
49
56
|
};
|
|
50
57
|
type SitePageNavItem = {
|
package/dist/pages/index.js
CHANGED
|
@@ -18,6 +18,7 @@ var defaultSitePageFields = {
|
|
|
18
18
|
footerGroup: "Footer Group",
|
|
19
19
|
footerOrder: "Footer Order",
|
|
20
20
|
contentSource: "Content Source",
|
|
21
|
+
blocks: "Blocks",
|
|
21
22
|
cover: "Cover"
|
|
22
23
|
};
|
|
23
24
|
var defaultPagesDataSourceEnv = "NOTION_PAGES_DATA_SOURCE_ID";
|
|
@@ -316,6 +317,29 @@ function normalizeLayout(value) {
|
|
|
316
317
|
if (normalized === "content-list") return "content-list";
|
|
317
318
|
return "default";
|
|
318
319
|
}
|
|
320
|
+
function parseStructuredBlockRefs(raw) {
|
|
321
|
+
if (!raw.trim()) return [];
|
|
322
|
+
try {
|
|
323
|
+
const parsed = JSON.parse(raw);
|
|
324
|
+
if (!Array.isArray(parsed)) return [];
|
|
325
|
+
return parsed.map((item, index) => {
|
|
326
|
+
if (typeof item === "string") {
|
|
327
|
+
const slug2 = normalizePageSlug(item);
|
|
328
|
+
return slug2 ? { slug: slug2, order: index } : null;
|
|
329
|
+
}
|
|
330
|
+
if (!isRecord(item) || typeof item.slug !== "string") return null;
|
|
331
|
+
const slug = normalizePageSlug(item.slug);
|
|
332
|
+
if (!slug) return null;
|
|
333
|
+
return {
|
|
334
|
+
slug,
|
|
335
|
+
variant: typeof item.variant === "string" && item.variant.trim() ? item.variant.trim() : void 0,
|
|
336
|
+
order: typeof item.order === "number" && Number.isFinite(item.order) ? item.order : index
|
|
337
|
+
};
|
|
338
|
+
}).filter((item) => Boolean(item)).sort((a, b) => (a.order ?? 0) - (b.order ?? 0));
|
|
339
|
+
} catch {
|
|
340
|
+
return [];
|
|
341
|
+
}
|
|
342
|
+
}
|
|
319
343
|
function mapNotionPageToSitePage(page, fields, blocks = [], options) {
|
|
320
344
|
const properties = isRecord(page.properties) ? page.properties : {};
|
|
321
345
|
const title = getRichTextProperty(properties, fields.title);
|
|
@@ -359,6 +383,9 @@ function mapNotionPageToSitePage(page, fields, blocks = [], options) {
|
|
|
359
383
|
contentSource: getRichTextProperty(properties, fields.contentSource),
|
|
360
384
|
coverImage: coverImageUrlForPage(page, fields.cover),
|
|
361
385
|
editUrl: notionPageEditUrl(page.id, options?.editBaseUrl),
|
|
386
|
+
structuredBlocks: parseStructuredBlockRefs(
|
|
387
|
+
getRichTextProperty(properties, fields.blocks)
|
|
388
|
+
),
|
|
362
389
|
blocks
|
|
363
390
|
};
|
|
364
391
|
}
|
package/dist/pages/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/pages/model.ts","../../src/pages/source.ts","../../src/notion/blocks.ts","../../src/notion/client.ts","../../src/notion/config.ts","../../src/notion/media.ts","../../src/notion/property-mappers.ts"],"sourcesContent":["import type {\n DefinedSitePageModel,\n SitePageFields,\n SitePageModel,\n} from \"./types\";\n\nexport const defaultSitePageFields: SitePageFields = {\n title: \"Name\",\n key: \"Key\",\n slug: \"Slug\",\n status: \"Status\",\n layout: \"Layout\",\n description: \"Description\",\n seoTitle: \"SEO Title\",\n seoDescription: \"SEO Description\",\n showHeader: \"Show Header\",\n showFooter: \"Show Footer\",\n showInNav: \"Show in Nav\",\n navLabel: \"Nav Label\",\n navOrder: \"Nav Order\",\n showInFooter: \"Show in Footer\",\n footerLabel: \"Footer Label\",\n footerGroup: \"Footer Group\",\n footerOrder: \"Footer Order\",\n contentSource: \"Content Source\",\n cover: \"Cover\",\n};\n\nexport const defaultPagesDataSourceEnv = \"NOTION_PAGES_DATA_SOURCE_ID\";\n\nexport function defineSitePageModel(model: SitePageModel): DefinedSitePageModel {\n return {\n ...model,\n source: {\n ...model.source,\n fields: {\n ...defaultSitePageFields,\n ...(model.source.fields ?? {}),\n },\n query: {\n pageSize: 100,\n ...(model.source.query ?? {}),\n },\n },\n };\n}\n","import { cache } from \"react\";\nimport { listBlockChildrenDeep, type NotionBlockClient } from \"../notion/blocks\";\nimport { createNotionClient } from \"../notion/client\";\nimport { getNotionConfigForModel, hasNotionModelConfig } from \"../notion/config\";\nimport { coverImageUrlForPage } from \"../notion/media\";\nimport {\n getCheckboxProperty,\n getNumberProperty,\n getRichTextProperty,\n getSelectProperty,\n isRecord,\n notionPageEditUrl,\n} from \"../notion/property-mappers\";\nimport type { NotionBlock, NotionPageLike } from \"../notion/types\";\nimport { defineSitePageModel } from \"./model\";\nimport type {\n SitePage,\n SitePageFields,\n SitePageFooterGroup,\n SitePageModel,\n SitePageNavItem,\n SitePageSourceDeps,\n SitePageLayout,\n} from \"./types\";\n\nfunction normalizePage(input: unknown): NotionPageLike | null {\n if (!input || typeof input !== \"object\") return null;\n const page = input as NotionPageLike;\n return page.id ? page : null;\n}\n\nexport function slugToHref(slug: string) {\n const normalized = slug.trim().replace(/^\\/+|\\/+$/g, \"\");\n return normalized ? `/${normalized}` : \"/\";\n}\n\nexport function normalizePageSlug(slug: string) {\n return slug.trim().replace(/^\\/+|\\/+$/g, \"\").toLowerCase();\n}\n\nfunction isPublishedStatus(value: string) {\n return value.trim().toLowerCase() === \"published\";\n}\n\nfunction getCheckboxPropertyWithFallback(\n properties: Record<string, unknown>,\n key: string,\n fallback: boolean\n) {\n const property = properties[key] as Record<string, unknown> | undefined;\n if (property?.type !== \"checkbox\") return fallback;\n return getCheckboxProperty(properties, key);\n}\n\nfunction normalizeLayout(value: string): SitePageLayout {\n const normalized = value.trim().toLowerCase();\n if (normalized === \"home\") return \"home\";\n if (normalized === \"legal\") return \"legal\";\n if (normalized === \"content-list\") return \"content-list\";\n return \"default\";\n}\n\nexport function mapNotionPageToSitePage(\n page: NotionPageLike,\n fields: SitePageFields,\n blocks: NotionBlock[] = [],\n options?: { editBaseUrl?: string }\n): SitePage | null {\n const properties = isRecord(page.properties) ? page.properties : {};\n const title = getRichTextProperty(properties, fields.title);\n const key = getRichTextProperty(properties, fields.key).toLowerCase();\n const slug = normalizePageSlug(getRichTextProperty(properties, fields.slug));\n const status = getSelectProperty(properties, fields.status);\n if (!title || !key || !isPublishedStatus(status)) return null;\n\n const description = getRichTextProperty(properties, fields.description);\n const seoTitle = getRichTextProperty(properties, fields.seoTitle) || title;\n const seoDescription =\n getRichTextProperty(properties, fields.seoDescription) || description;\n const navLabel = getRichTextProperty(properties, fields.navLabel) || title;\n const footerLabel =\n getRichTextProperty(properties, fields.footerLabel) || navLabel;\n\n return {\n pageId: page.id,\n key,\n slug,\n href: slugToHref(slug),\n title,\n description,\n seoTitle,\n seoDescription,\n layout: normalizeLayout(getSelectProperty(properties, fields.layout)),\n published: true,\n showHeader: getCheckboxPropertyWithFallback(\n properties,\n fields.showHeader,\n true\n ),\n showFooter: getCheckboxPropertyWithFallback(\n properties,\n fields.showFooter,\n true\n ),\n showInNav: getCheckboxProperty(properties, fields.showInNav),\n navLabel,\n navOrder: getNumberProperty(properties, fields.navOrder, 100),\n showInFooter: getCheckboxProperty(properties, fields.showInFooter),\n footerLabel,\n footerGroup: getSelectProperty(properties, fields.footerGroup) || \"Company\",\n footerOrder: getNumberProperty(properties, fields.footerOrder, 100),\n contentSource: getRichTextProperty(properties, fields.contentSource),\n coverImage: coverImageUrlForPage(page, fields.cover),\n editUrl: notionPageEditUrl(page.id, options?.editBaseUrl),\n blocks,\n };\n}\n\nexport function createSitePageSource(\n modelInput: SitePageModel,\n deps: SitePageSourceDeps\n) {\n const model = defineSitePageModel(modelInput);\n const fields = model.source.fields;\n\n return {\n async listPages(): Promise<SitePage[]> {\n const pages: SitePage[] = [];\n let cursor: string | undefined;\n\n do {\n const response = await deps.queryDataSource({ startCursor: cursor });\n for (const item of response.results ?? []) {\n const page = normalizePage(item);\n if (!page) continue;\n const mapped = mapNotionPageToSitePage(\n page,\n fields,\n await deps.getPageBlocks(page.id),\n { editBaseUrl: deps.editBaseUrl }\n );\n if (mapped) pages.push(mapped);\n }\n\n cursor = response.next_cursor ?? undefined;\n if (!response.has_more) break;\n } while (cursor);\n\n return pages.sort((a, b) => a.navOrder - b.navOrder || a.title.localeCompare(b.title));\n },\n };\n}\n\nasync function createDefaultSitePageSource(modelInput: SitePageModel) {\n const model = defineSitePageModel(modelInput);\n if (!(await hasNotionModelConfig(model))) return null;\n\n const config = await getNotionConfigForModel(model);\n const client = createNotionClient(config);\n return createSitePageSource(model, {\n editBaseUrl: config.editBaseUrl,\n queryDataSource: async ({ startCursor } = {}) =>\n client.dataSources.query({\n data_source_id: config.dataSourceId,\n page_size: model.source.query.pageSize,\n sorts: [{ property: model.source.fields.navOrder, direction: \"ascending\" }],\n ...(startCursor ? { start_cursor: startCursor } : {}),\n }),\n getPageBlocks: (pageId) =>\n listBlockChildrenDeep(client as unknown as NotionBlockClient, pageId),\n });\n}\n\nconst defaultSourceCache = cache(createDefaultSitePageSource);\n\nexport function createSitePagesApi(input: {\n model: SitePageModel;\n fallbackPages?: readonly SitePage[];\n}) {\n const fallbackPages = [...(input.fallbackPages ?? [])];\n\n const listSitePages = cache(async (): Promise<SitePage[]> => {\n try {\n const source = await defaultSourceCache(input.model);\n const pages = source ? await source.listPages() : [];\n return pages.length ? pages : fallbackPages;\n } catch {\n return fallbackPages;\n }\n });\n\n return {\n listSitePages,\n async getSitePageByKey(key: string) {\n const pages = await listSitePages();\n return pages.find((page) => page.key === key.toLowerCase()) ?? null;\n },\n async getSitePageBySlug(slug: string) {\n const normalized = normalizePageSlug(slug);\n const pages = await listSitePages();\n return pages.find((page) => page.slug === normalized) ?? null;\n },\n async getSitePageForContentSource(sourceId: string) {\n const pages = await listSitePages();\n return (\n pages.find(\n (page) =>\n page.layout === \"content-list\" && page.contentSource === sourceId\n ) ?? null\n );\n },\n async getSiteNavigation(): Promise<SitePageNavItem[]> {\n const pages = await listSitePages();\n return deriveSiteNavigation(pages);\n },\n async getSiteFooterGroups(): Promise<SitePageFooterGroup[]> {\n const pages = await listSitePages();\n return deriveSiteFooterGroups(pages);\n },\n };\n}\n\nexport function deriveSiteNavigation(\n pages: readonly SitePage[]\n): SitePageNavItem[] {\n return pages\n .filter((page) => page.showInNav)\n .map((page) => ({\n label: page.navLabel,\n href: page.href,\n order: page.navOrder,\n pageKey: page.key,\n }))\n .sort((a, b) => a.order - b.order || a.label.localeCompare(b.label));\n}\n\nexport function deriveSiteFooterGroups(\n pages: readonly SitePage[]\n): SitePageFooterGroup[] {\n const groups = new Map<string, SitePageNavItem[]>();\n\n for (const page of pages.filter((candidate) => candidate.showInFooter)) {\n const label = page.footerGroup || \"Company\";\n const items = groups.get(label) ?? [];\n items.push({\n label: page.footerLabel,\n href: page.href,\n order: page.footerOrder,\n pageKey: page.key,\n });\n groups.set(label, items);\n }\n\n return Array.from(groups.entries()).map(([label, items]) => ({\n label,\n items: items.sort(\n (a, b) => a.order - b.order || a.label.localeCompare(b.label)\n ),\n }));\n}\n","import type { NotionBlock } from \"./types\";\n\ntype BlockChildrenListResponse = {\n results?: unknown[];\n has_more?: boolean;\n next_cursor?: string | null;\n};\n\nexport type NotionBlockClient = {\n blocks: {\n children: {\n list: (args: {\n block_id: string;\n page_size?: number;\n start_cursor?: string;\n }) => Promise<BlockChildrenListResponse>;\n };\n };\n};\n\nfunction normalizeBlock(input: unknown): NotionBlock | null {\n if (!input || typeof input !== \"object\") return null;\n const block = input as NotionBlock;\n return block.id && block.type ? block : null;\n}\n\nexport async function listBlockChildren(\n client: NotionBlockClient,\n blockId: string\n): Promise<NotionBlock[]> {\n const results: NotionBlock[] = [];\n let cursor: string | undefined;\n\n do {\n const response = await client.blocks.children.list({\n block_id: blockId,\n page_size: 100,\n ...(cursor ? { start_cursor: cursor } : {}),\n });\n\n for (const item of response.results ?? []) {\n const block = normalizeBlock(item);\n if (block) results.push(block);\n }\n\n cursor = response.next_cursor ?? undefined;\n if (!response.has_more) break;\n } while (cursor);\n\n return results;\n}\n\nexport async function listBlockChildrenDeep(\n client: NotionBlockClient,\n blockId: string,\n options?: { maxDepth?: number }\n): Promise<NotionBlock[]> {\n const maxDepth = options?.maxDepth ?? 6;\n\n async function visit(id: string, depth: number): Promise<NotionBlock[]> {\n const children = await listBlockChildren(client, id);\n if (depth >= maxDepth) return children;\n\n return Promise.all(\n children.map(async (block) => {\n if (!block.has_children) return block;\n return {\n ...block,\n children: await visit(block.id, depth + 1),\n };\n })\n );\n }\n\n return visit(blockId, 0);\n}\n","import { Client } from \"@notionhq/client\";\nimport type { NotionClientConfig } from \"./config\";\n\nexport function createNotionClient(config: NotionClientConfig) {\n return new Client({\n auth: config.token,\n baseUrl: config.apiBaseUrl,\n notionVersion: \"2026-03-11\",\n });\n}\n","import type { NotionContentModelLike } from \"./types\";\n\ntype NotionEnv = {\n NOTION_TOKEN?: string;\n NOTION_DATA_SOURCE_ID?: string;\n NOTION_API_BASE_URL?: string;\n NOTION_EDIT_BASE_URL?: string;\n NOTION_WEBHOOK_VERIFICATION_TOKEN?: string;\n [key: string]: string | undefined;\n};\n\nexport type NotionClientConfig = {\n token: string;\n apiBaseUrl?: string;\n};\n\nexport type NotionConfig = {\n token: string;\n dataSourceId: string;\n apiBaseUrl?: string;\n editBaseUrl?: string;\n webhookVerificationToken?: string;\n};\n\nfunction readProcessEnv(): NotionEnv {\n const env: NotionEnv = {\n NOTION_TOKEN: process.env.NOTION_TOKEN,\n NOTION_DATA_SOURCE_ID: process.env.NOTION_DATA_SOURCE_ID,\n NOTION_API_BASE_URL: process.env.NOTION_API_BASE_URL,\n NOTION_EDIT_BASE_URL: process.env.NOTION_EDIT_BASE_URL,\n NOTION_WEBHOOK_VERIFICATION_TOKEN:\n process.env.NOTION_WEBHOOK_VERIFICATION_TOKEN,\n };\n\n for (const [key, value] of Object.entries(process.env)) {\n if (key.startsWith(\"NOTION_\") && typeof value === \"string\") {\n env[key] = value;\n }\n }\n\n return env;\n}\n\nasync function readWorkerEnv(): Promise<NotionEnv> {\n try {\n const mod = (await import(\n /* webpackIgnore: true */ \"cloudflare:workers\"\n )) as unknown as { env?: Record<string, unknown> };\n const env: NotionEnv = {};\n for (const [key, value] of Object.entries(mod.env ?? {})) {\n if (key.startsWith(\"NOTION_\") && typeof value === \"string\") {\n env[key] = value;\n }\n }\n return env;\n } catch {\n return {};\n }\n}\n\nfunction readString(source: NotionEnv, name: string): string | undefined {\n const value = String(source[name] ?? \"\").trim();\n return value || undefined;\n}\n\nfunction mergeEnv(...sources: NotionEnv[]): NotionEnv {\n const merged: NotionEnv = {};\n\n for (const source of sources) {\n for (const name of Object.keys(source)) {\n if (!name.startsWith(\"NOTION_\")) continue;\n const value = readString(source, name);\n if (value) merged[name] = value;\n }\n }\n\n return merged;\n}\n\nasync function readEnv(): Promise<NotionEnv> {\n const processEnv = readProcessEnv();\n return mergeEnv(await readWorkerEnv(), processEnv);\n}\n\nfunction readRequired(\n source: NotionEnv,\n name: string\n): string {\n const value = readString(source, name);\n if (!value) {\n throw new Error(`Missing required Notion env: ${name}`);\n }\n return value;\n}\n\nexport function getNotionEditBaseUrl(): string {\n return readString(readProcessEnv(), \"NOTION_EDIT_BASE_URL\") ?? \"https://www.notion.so\";\n}\n\nexport async function hasNotionConfig(): Promise<boolean> {\n const env = await readEnv();\n return Boolean(\n readString(env, \"NOTION_TOKEN\") && readString(env, \"NOTION_DATA_SOURCE_ID\")\n );\n}\n\nexport async function hasNotionModelConfig(\n model: NotionContentModelLike\n): Promise<boolean> {\n const env = await readEnv();\n return Boolean(\n readString(env, \"NOTION_TOKEN\") &&\n (readString(env, model.source.dataSourceEnv) ||\n model.source.defaultDataSourceId)\n );\n}\n\nexport async function getNotionClientConfig(): Promise<NotionClientConfig> {\n const env = await readEnv();\n return {\n token: readRequired(env, \"NOTION_TOKEN\"),\n apiBaseUrl: readString(env, \"NOTION_API_BASE_URL\"),\n };\n}\n\nexport async function getNotionConfig(): Promise<NotionConfig> {\n const env = await readEnv();\n return {\n token: readRequired(env, \"NOTION_TOKEN\"),\n dataSourceId: readRequired(env, \"NOTION_DATA_SOURCE_ID\"),\n apiBaseUrl: readString(env, \"NOTION_API_BASE_URL\"),\n editBaseUrl: readString(env, \"NOTION_EDIT_BASE_URL\"),\n webhookVerificationToken: readString(\n env,\n \"NOTION_WEBHOOK_VERIFICATION_TOKEN\"\n ),\n };\n}\n\nexport async function getNotionWebhookVerificationToken(): Promise<\n string | undefined\n> {\n const env = await readEnv();\n return readString(env, \"NOTION_WEBHOOK_VERIFICATION_TOKEN\");\n}\n\nexport async function getNotionConfigForModel(\n model: NotionContentModelLike\n): Promise<NotionConfig> {\n const env = await readEnv();\n const dataSourceId =\n readString(env, model.source.dataSourceEnv) ??\n model.source.defaultDataSourceId;\n if (!dataSourceId) {\n throw new Error(`Missing required Notion env: ${model.source.dataSourceEnv}`);\n }\n\n return {\n token: readRequired(env, model.source.tokenEnv),\n dataSourceId,\n apiBaseUrl: readString(env, \"NOTION_API_BASE_URL\"),\n editBaseUrl: readString(env, \"NOTION_EDIT_BASE_URL\"),\n webhookVerificationToken: readString(\n env,\n \"NOTION_WEBHOOK_VERIFICATION_TOKEN\"\n ),\n };\n}\n","import type { NotionBlock, NotionFileSource, NotionPageLike } from \"./types\";\n\ntype FileLike = {\n type?: string;\n external?: { url?: string };\n file?: { url?: string; expiry_time?: string };\n name?: string;\n};\n\nfunction stripLeadingSlash(value: string) {\n return value.startsWith(\"/\") ? value.slice(1) : value;\n}\n\nfunction encodePathPart(value: string) {\n return encodeURIComponent(stripLeadingSlash(value));\n}\n\nfunction appendVersion(path: string, version?: string) {\n const value = String(version ?? \"\").trim();\n if (!value) return path;\n return `${path}?${new URLSearchParams({ v: value })}`;\n}\n\nfunction blockVersion(block: NotionBlock): string | undefined {\n return typeof block.last_edited_time === \"string\"\n ? block.last_edited_time\n : undefined;\n}\n\nexport function notionPageCoverMediaPath(pageId: string): string {\n return `/api/notion/media/page/${encodePathPart(pageId)}/cover`;\n}\n\nexport function notionPagePropertyMediaPath(\n pageId: string,\n propertyName: string\n): string {\n return `/api/notion/media/page/${encodePathPart(pageId)}/property/${encodePathPart(propertyName)}`;\n}\n\nexport function notionBlockMediaPath(blockId: string): string {\n return `/api/notion/media/block/${encodePathPart(blockId)}`;\n}\n\nexport function normalizeNotionFileSource(input: unknown): NotionFileSource | null {\n const file = input as FileLike | null | undefined;\n if (!file || typeof file !== \"object\") return null;\n\n if (file.type === \"external\") {\n const url = String(file.external?.url ?? \"\").trim();\n return url ? { type: \"external\", url } : null;\n }\n\n if (file.type === \"file\") {\n const url = String(file.file?.url ?? \"\").trim();\n if (!url) return null;\n return {\n type: \"file\",\n url,\n expiryTime: String(file.file?.expiry_time ?? \"\").trim() || null,\n };\n }\n\n return null;\n}\n\nexport function resolveNotionFileUrl(input: unknown): string | null {\n return normalizeNotionFileSource(input)?.url ?? null;\n}\n\nexport function isNotionHostedFile(input: unknown): boolean {\n return normalizeNotionFileSource(input)?.type === \"file\";\n}\n\nexport function pickFirstFilesPropertyValue(property: unknown): unknown | null {\n const value = property as { type?: string; files?: unknown[] } | null | undefined;\n if (!value || value.type !== \"files\" || !Array.isArray(value.files)) {\n return null;\n }\n return value.files[0] ?? null;\n}\n\nexport function pickPageCoverFile(page: NotionPageLike): unknown | null {\n return page.cover ?? null;\n}\n\nexport function coverImageUrlForPage(\n page: NotionPageLike,\n coverPropertyName = \"Cover\"\n): string | null {\n const propertyFile = pickFirstFilesPropertyValue(\n page.properties?.[coverPropertyName]\n );\n const propertySource = normalizeNotionFileSource(propertyFile);\n if (propertySource) {\n return appendVersion(\n notionPagePropertyMediaPath(page.id, coverPropertyName),\n page.last_edited_time\n );\n }\n\n const coverSource = normalizeNotionFileSource(pickPageCoverFile(page));\n if (coverSource) {\n return appendVersion(notionPageCoverMediaPath(page.id), page.last_edited_time);\n }\n\n return null;\n}\n\nexport function fileObjectForMediaBlock(block: NotionBlock): unknown | null {\n const typed = block[block.type] as Record<string, unknown> | undefined;\n if (!typed || typeof typed !== \"object\") return null;\n\n if (\n block.type === \"image\" ||\n block.type === \"video\" ||\n block.type === \"file\" ||\n block.type === \"pdf\" ||\n block.type === \"audio\"\n ) {\n return typed;\n }\n\n return null;\n}\n\nexport function mediaUrlForBlock(block: NotionBlock): string | null {\n const source = normalizeNotionFileSource(fileObjectForMediaBlock(block));\n if (!source) return null;\n if (source.type === \"external\" && block.type !== \"image\") {\n return source.url;\n }\n return appendVersion(notionBlockMediaPath(block.id), blockVersion(block));\n}\n\nexport function firstImageUrlFromBlocks(blocks: NotionBlock[]): string | null {\n for (const block of blocks) {\n if (block.type === \"image\") {\n const imageUrl = mediaUrlForBlock(block);\n if (imageUrl) return imageUrl;\n }\n\n const nested = block.children?.length\n ? firstImageUrlFromBlocks(block.children)\n : null;\n if (nested) return nested;\n }\n\n return null;\n}\n\nexport function publicMediaBlockForApi(block: NotionBlock): NotionBlock {\n const value = block[block.type];\n const source = normalizeNotionFileSource(value);\n const children = block.children?.map(publicMediaBlockForApi);\n\n if (!source) {\n return children ? { ...block, children } : { ...block };\n }\n\n const path = appendVersion(notionBlockMediaPath(block.id), blockVersion(block));\n const publicValue =\n source.type === \"external\"\n ? block.type === \"image\"\n ? {\n ...(value as Record<string, unknown>),\n external: { url: path },\n }\n : value\n : {\n ...(value as Record<string, unknown>),\n file: {\n url: path,\n expiry_time: null,\n },\n };\n\n return {\n ...block,\n [block.type]: publicValue,\n ...(children ? { children } : {}),\n };\n}\n\nexport function gatedMediaBlockForApi(\n block: NotionBlock,\n options?: { accessUrlForBlock?: (block: NotionBlock) => string | null }\n): NotionBlock {\n const value = block[block.type];\n const source = normalizeNotionFileSource(value);\n const children = block.children?.map((child) =>\n gatedMediaBlockForApi(child, options)\n );\n\n if (block.type !== \"video\" || !source) {\n const publicBlock = publicMediaBlockForApi(block);\n return children ? { ...publicBlock, children } : publicBlock;\n }\n\n const gatedValue: Record<string, unknown> = {\n ...(value as Record<string, unknown>),\n gated: true,\n access_url: options?.accessUrlForBlock?.(block) ?? null,\n };\n\n if (source.type === \"external\") {\n gatedValue.external = { url: null };\n } else {\n gatedValue.file = {\n url: null,\n expiry_time: null,\n };\n }\n\n return {\n ...block,\n video: gatedValue,\n ...(children ? { children } : {}),\n };\n}\n\nexport function isDirectVideoUrl(url: string): boolean {\n try {\n const parsed = new URL(url);\n return /\\.(mp4|webm|mov|m4v)(?:$|\\?)/i.test(parsed.pathname);\n } catch {\n return false;\n }\n}\n\nexport function videoEmbedUrl(url: string): string | null {\n try {\n const parsed = new URL(url);\n const host = parsed.hostname.replace(/^www\\./, \"\");\n\n if (host === \"youtube.com\" || host === \"m.youtube.com\") {\n const id = parsed.searchParams.get(\"v\");\n return id ? `https://www.youtube.com/embed/${encodeURIComponent(id)}` : null;\n }\n\n if (host === \"youtu.be\") {\n const id = parsed.pathname.split(\"/\").filter(Boolean)[0];\n return id ? `https://www.youtube.com/embed/${encodeURIComponent(id)}` : null;\n }\n\n if (host === \"youtube-nocookie.com\") {\n return parsed.toString();\n }\n\n if (host === \"vimeo.com\") {\n const id = parsed.pathname.split(\"/\").filter(Boolean)[0];\n return id ? `https://player.vimeo.com/video/${encodeURIComponent(id)}` : null;\n }\n\n if (host === \"player.vimeo.com\") {\n return parsed.toString();\n }\n } catch {\n return null;\n }\n\n return null;\n}\n","type PropertyMap = Record<string, unknown>;\n\ntype TextPart = {\n plain_text?: string;\n};\n\nexport function isRecord(value: unknown): value is Record<string, unknown> {\n return Boolean(value && typeof value === \"object\");\n}\n\nfunction getPlainText(parts: unknown): string {\n if (!Array.isArray(parts)) return \"\";\n return parts\n .map((part: TextPart) => part.plain_text ?? \"\")\n .join(\"\")\n .trim();\n}\n\nfunction getProperty(properties: PropertyMap, key: string) {\n return properties[key] as Record<string, unknown> | undefined;\n}\n\nfunction firstPropertyOfType(properties: PropertyMap, type: string) {\n return Object.values(properties).find(\n (property): property is Record<string, unknown> =>\n isRecord(property) && property.type === type\n );\n}\n\nexport function getFirstTitleProperty(properties: PropertyMap): string {\n const property = firstPropertyOfType(properties, \"title\");\n return property ? getPlainText(property.title) : \"\";\n}\n\nexport function getRichTextProperty(properties: PropertyMap, key: string): string {\n const property = getProperty(properties, key);\n if (!property) return \"\";\n\n if (property.type === \"title\") return getPlainText(property.title);\n if (property.type === \"rich_text\") return getPlainText(property.rich_text);\n if (property.type === \"url\") return String(property.url ?? \"\").trim();\n if (property.type === \"email\") return String(property.email ?? \"\").trim();\n if (property.type === \"phone_number\") {\n return String(property.phone_number ?? \"\").trim();\n }\n\n return \"\";\n}\n\nexport function getDateProperty(properties: PropertyMap, key: string): string {\n const property = getProperty(properties, key);\n if (property?.type !== \"date\") return \"\";\n const date = property.date as { start?: string } | null | undefined;\n return String(date?.start ?? \"\").trim();\n}\n\nexport function getFirstDateProperty(properties: PropertyMap): string {\n const property = firstPropertyOfType(properties, \"date\");\n if (!property) return \"\";\n const date = property.date as { start?: string } | null | undefined;\n return String(date?.start ?? \"\").trim();\n}\n\nexport function getSelectProperty(properties: PropertyMap, key: string): string {\n const property = getProperty(properties, key);\n if (property?.type !== \"select\") return \"\";\n const select = property.select as { name?: string } | null | undefined;\n return String(select?.name ?? \"\").trim();\n}\n\nexport function getCheckboxProperty(properties: PropertyMap, key: string): boolean {\n const property = getProperty(properties, key);\n if (property?.type !== \"checkbox\") return false;\n return Boolean(property.checkbox);\n}\n\nexport function getNumberProperty(\n properties: PropertyMap,\n key: string,\n fallback = 0\n): number {\n const property = getProperty(properties, key);\n if (property?.type !== \"number\") return fallback;\n const value = Number(property.number);\n return Number.isFinite(value) ? value : fallback;\n}\n\nexport function getRelationPageIds(properties: PropertyMap, key: string): string[] {\n const property = getProperty(properties, key);\n if (property?.type !== \"relation\" || !Array.isArray(property.relation)) {\n return [];\n }\n\n return property.relation\n .map((item: { id?: string }) => String(item.id ?? \"\").trim())\n .filter(Boolean);\n}\n\nexport function getTagsProperty(properties: PropertyMap, key: string): string[] {\n const property = getProperty(properties, key);\n if (property?.type === \"multi_select\" && Array.isArray(property.multi_select)) {\n return property.multi_select\n .map((item: { name?: string }) => String(item.name ?? \"\").trim())\n .filter(Boolean);\n }\n\n if (property?.type === \"select\") {\n const select = property.select as { name?: string } | null | undefined;\n const name = String(select?.name ?? \"\").trim();\n return name ? [name] : [];\n }\n\n return [];\n}\n\nexport function getFirstTagsProperty(properties: PropertyMap): string[] {\n const multiSelect = firstPropertyOfType(properties, \"multi_select\");\n if (multiSelect && Array.isArray(multiSelect.multi_select)) {\n return multiSelect.multi_select\n .map((item: { name?: string }) => String(item.name ?? \"\").trim())\n .filter(Boolean);\n }\n\n const select = firstPropertyOfType(properties, \"select\");\n const name = String((select?.select as { name?: string } | null)?.name ?? \"\").trim();\n return name ? [name] : [];\n}\n\nexport function getAuthorProperty(properties: PropertyMap, key: string): string {\n const property = getProperty(properties, key);\n if (!property) return \"\";\n\n if (property.type === \"people\" && Array.isArray(property.people)) {\n return property.people\n .map((person: { name?: string; person?: { email?: string } }) =>\n String(person.name ?? person.person?.email ?? \"\").trim()\n )\n .filter(Boolean)\n .join(\", \");\n }\n\n return getRichTextProperty(properties, key);\n}\n\nexport function getFirstPeopleProperty(properties: PropertyMap): string {\n const property = firstPropertyOfType(properties, \"people\");\n if (!property || !Array.isArray(property.people)) return \"\";\n\n return property.people\n .map((person: { name?: string; person?: { email?: string } }) =>\n String(person.name ?? person.person?.email ?? \"\").trim()\n )\n .filter(Boolean)\n .join(\", \");\n}\n\nexport function pickPublishedFlag(properties: PropertyMap): boolean {\n const published = getProperty(properties, \"Published\");\n if (published?.type === \"checkbox\") {\n return Boolean(published.checkbox);\n }\n\n const status = getProperty(properties, \"Status\");\n if (status?.type === \"status\") {\n const statusValue = status.status as { name?: string } | null | undefined;\n return String(statusValue?.name ?? \"\").trim().toLowerCase() === \"published\";\n }\n\n if (status?.type === \"select\") {\n const statusValue = status.select as { name?: string } | null | undefined;\n return String(statusValue?.name ?? \"\").trim().toLowerCase() === \"published\";\n }\n\n return false;\n}\n\nexport function pickDescriptionFallback(description: string, title: string): string {\n return description.trim() || title.trim();\n}\n\nexport function isValidPublicSlug(slug: string): boolean {\n return /^[a-z0-9][a-z0-9-]{0,79}$/.test(slug);\n}\n\nexport function notionPageEditUrl(pageId: string, editBaseUrl?: string): string {\n const compactPageId = pageId.replaceAll(\"-\", \"\");\n if (editBaseUrl?.includes(\"{pageId}\")) {\n return editBaseUrl.replaceAll(\"{pageId}\", compactPageId);\n }\n return `https://www.notion.so/${compactPageId}`;\n}\n\n/**\n * Normalize a Notion page id (with or without dashes) to a compact lowercase\n * string. Used as a stable identifier in URLs and cache keys.\n */\nexport function compactNotionId(id: string): string {\n return id.replaceAll(\"-\", \"\").toLowerCase();\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/pages/model.ts","../../src/pages/source.ts","../../src/notion/blocks.ts","../../src/notion/client.ts","../../src/notion/config.ts","../../src/notion/media.ts","../../src/notion/property-mappers.ts"],"sourcesContent":["import type {\n DefinedSitePageModel,\n SitePageFields,\n SitePageModel,\n} from \"./types\";\n\nexport const defaultSitePageFields: SitePageFields = {\n title: \"Name\",\n key: \"Key\",\n slug: \"Slug\",\n status: \"Status\",\n layout: \"Layout\",\n description: \"Description\",\n seoTitle: \"SEO Title\",\n seoDescription: \"SEO Description\",\n showHeader: \"Show Header\",\n showFooter: \"Show Footer\",\n showInNav: \"Show in Nav\",\n navLabel: \"Nav Label\",\n navOrder: \"Nav Order\",\n showInFooter: \"Show in Footer\",\n footerLabel: \"Footer Label\",\n footerGroup: \"Footer Group\",\n footerOrder: \"Footer Order\",\n contentSource: \"Content Source\",\n blocks: \"Blocks\",\n cover: \"Cover\",\n};\n\nexport const defaultPagesDataSourceEnv = \"NOTION_PAGES_DATA_SOURCE_ID\";\n\nexport function defineSitePageModel(model: SitePageModel): DefinedSitePageModel {\n return {\n ...model,\n source: {\n ...model.source,\n fields: {\n ...defaultSitePageFields,\n ...(model.source.fields ?? {}),\n },\n query: {\n pageSize: 100,\n ...(model.source.query ?? {}),\n },\n },\n };\n}\n","import { cache } from \"react\";\nimport { listBlockChildrenDeep, type NotionBlockClient } from \"../notion/blocks\";\nimport { createNotionClient } from \"../notion/client\";\nimport { getNotionConfigForModel, hasNotionModelConfig } from \"../notion/config\";\nimport { coverImageUrlForPage } from \"../notion/media\";\nimport {\n getCheckboxProperty,\n getNumberProperty,\n getRichTextProperty,\n getSelectProperty,\n isRecord,\n notionPageEditUrl,\n} from \"../notion/property-mappers\";\nimport type { NotionBlock, NotionPageLike } from \"../notion/types\";\nimport { defineSitePageModel } from \"./model\";\nimport type {\n SitePage,\n SitePageBlockRef,\n SitePageFields,\n SitePageFooterGroup,\n SitePageModel,\n SitePageNavItem,\n SitePageSourceDeps,\n SitePageLayout,\n} from \"./types\";\n\nfunction normalizePage(input: unknown): NotionPageLike | null {\n if (!input || typeof input !== \"object\") return null;\n const page = input as NotionPageLike;\n return page.id ? page : null;\n}\n\nexport function slugToHref(slug: string) {\n const normalized = slug.trim().replace(/^\\/+|\\/+$/g, \"\");\n return normalized ? `/${normalized}` : \"/\";\n}\n\nexport function normalizePageSlug(slug: string) {\n return slug.trim().replace(/^\\/+|\\/+$/g, \"\").toLowerCase();\n}\n\nfunction isPublishedStatus(value: string) {\n return value.trim().toLowerCase() === \"published\";\n}\n\nfunction getCheckboxPropertyWithFallback(\n properties: Record<string, unknown>,\n key: string,\n fallback: boolean\n) {\n const property = properties[key] as Record<string, unknown> | undefined;\n if (property?.type !== \"checkbox\") return fallback;\n return getCheckboxProperty(properties, key);\n}\n\nfunction normalizeLayout(value: string): SitePageLayout {\n const normalized = value.trim().toLowerCase();\n if (normalized === \"home\") return \"home\";\n if (normalized === \"legal\") return \"legal\";\n if (normalized === \"content-list\") return \"content-list\";\n return \"default\";\n}\n\nfunction parseStructuredBlockRefs(raw: string): SitePageBlockRef[] {\n if (!raw.trim()) return [];\n\n try {\n const parsed = JSON.parse(raw) as unknown;\n if (!Array.isArray(parsed)) return [];\n\n return parsed\n .map((item, index): SitePageBlockRef | null => {\n if (typeof item === \"string\") {\n const slug = normalizePageSlug(item);\n return slug ? { slug, order: index } : null;\n }\n if (!isRecord(item) || typeof item.slug !== \"string\") return null;\n\n const slug = normalizePageSlug(item.slug);\n if (!slug) return null;\n\n return {\n slug,\n variant:\n typeof item.variant === \"string\" && item.variant.trim()\n ? item.variant.trim()\n : undefined,\n order:\n typeof item.order === \"number\" && Number.isFinite(item.order)\n ? item.order\n : index,\n };\n })\n .filter((item): item is SitePageBlockRef => Boolean(item))\n .sort((a, b) => (a.order ?? 0) - (b.order ?? 0));\n } catch {\n return [];\n }\n}\n\nexport function mapNotionPageToSitePage(\n page: NotionPageLike,\n fields: SitePageFields,\n blocks: NotionBlock[] = [],\n options?: { editBaseUrl?: string }\n): SitePage | null {\n const properties = isRecord(page.properties) ? page.properties : {};\n const title = getRichTextProperty(properties, fields.title);\n const key = getRichTextProperty(properties, fields.key).toLowerCase();\n const slug = normalizePageSlug(getRichTextProperty(properties, fields.slug));\n const status = getSelectProperty(properties, fields.status);\n if (!title || !key || !isPublishedStatus(status)) return null;\n\n const description = getRichTextProperty(properties, fields.description);\n const seoTitle = getRichTextProperty(properties, fields.seoTitle) || title;\n const seoDescription =\n getRichTextProperty(properties, fields.seoDescription) || description;\n const navLabel = getRichTextProperty(properties, fields.navLabel) || title;\n const footerLabel =\n getRichTextProperty(properties, fields.footerLabel) || navLabel;\n\n return {\n pageId: page.id,\n key,\n slug,\n href: slugToHref(slug),\n title,\n description,\n seoTitle,\n seoDescription,\n layout: normalizeLayout(getSelectProperty(properties, fields.layout)),\n published: true,\n showHeader: getCheckboxPropertyWithFallback(\n properties,\n fields.showHeader,\n true\n ),\n showFooter: getCheckboxPropertyWithFallback(\n properties,\n fields.showFooter,\n true\n ),\n showInNav: getCheckboxProperty(properties, fields.showInNav),\n navLabel,\n navOrder: getNumberProperty(properties, fields.navOrder, 100),\n showInFooter: getCheckboxProperty(properties, fields.showInFooter),\n footerLabel,\n footerGroup: getSelectProperty(properties, fields.footerGroup) || \"Company\",\n footerOrder: getNumberProperty(properties, fields.footerOrder, 100),\n contentSource: getRichTextProperty(properties, fields.contentSource),\n coverImage: coverImageUrlForPage(page, fields.cover),\n editUrl: notionPageEditUrl(page.id, options?.editBaseUrl),\n structuredBlocks: parseStructuredBlockRefs(\n getRichTextProperty(properties, fields.blocks)\n ),\n blocks,\n };\n}\n\nexport function createSitePageSource(\n modelInput: SitePageModel,\n deps: SitePageSourceDeps\n) {\n const model = defineSitePageModel(modelInput);\n const fields = model.source.fields;\n\n return {\n async listPages(): Promise<SitePage[]> {\n const pages: SitePage[] = [];\n let cursor: string | undefined;\n\n do {\n const response = await deps.queryDataSource({ startCursor: cursor });\n for (const item of response.results ?? []) {\n const page = normalizePage(item);\n if (!page) continue;\n const mapped = mapNotionPageToSitePage(\n page,\n fields,\n await deps.getPageBlocks(page.id),\n { editBaseUrl: deps.editBaseUrl }\n );\n if (mapped) pages.push(mapped);\n }\n\n cursor = response.next_cursor ?? undefined;\n if (!response.has_more) break;\n } while (cursor);\n\n return pages.sort((a, b) => a.navOrder - b.navOrder || a.title.localeCompare(b.title));\n },\n };\n}\n\nasync function createDefaultSitePageSource(modelInput: SitePageModel) {\n const model = defineSitePageModel(modelInput);\n if (!(await hasNotionModelConfig(model))) return null;\n\n const config = await getNotionConfigForModel(model);\n const client = createNotionClient(config);\n return createSitePageSource(model, {\n editBaseUrl: config.editBaseUrl,\n queryDataSource: async ({ startCursor } = {}) =>\n client.dataSources.query({\n data_source_id: config.dataSourceId,\n page_size: model.source.query.pageSize,\n sorts: [{ property: model.source.fields.navOrder, direction: \"ascending\" }],\n ...(startCursor ? { start_cursor: startCursor } : {}),\n }),\n getPageBlocks: (pageId) =>\n listBlockChildrenDeep(client as unknown as NotionBlockClient, pageId),\n });\n}\n\nconst defaultSourceCache = cache(createDefaultSitePageSource);\n\nexport function createSitePagesApi(input: {\n model: SitePageModel;\n fallbackPages?: readonly SitePage[];\n}) {\n const fallbackPages = [...(input.fallbackPages ?? [])];\n\n const listSitePages = cache(async (): Promise<SitePage[]> => {\n try {\n const source = await defaultSourceCache(input.model);\n const pages = source ? await source.listPages() : [];\n return pages.length ? pages : fallbackPages;\n } catch {\n return fallbackPages;\n }\n });\n\n return {\n listSitePages,\n async getSitePageByKey(key: string) {\n const pages = await listSitePages();\n return pages.find((page) => page.key === key.toLowerCase()) ?? null;\n },\n async getSitePageBySlug(slug: string) {\n const normalized = normalizePageSlug(slug);\n const pages = await listSitePages();\n return pages.find((page) => page.slug === normalized) ?? null;\n },\n async getSitePageForContentSource(sourceId: string) {\n const pages = await listSitePages();\n return (\n pages.find(\n (page) =>\n page.layout === \"content-list\" && page.contentSource === sourceId\n ) ?? null\n );\n },\n async getSiteNavigation(): Promise<SitePageNavItem[]> {\n const pages = await listSitePages();\n return deriveSiteNavigation(pages);\n },\n async getSiteFooterGroups(): Promise<SitePageFooterGroup[]> {\n const pages = await listSitePages();\n return deriveSiteFooterGroups(pages);\n },\n };\n}\n\nexport function deriveSiteNavigation(\n pages: readonly SitePage[]\n): SitePageNavItem[] {\n return pages\n .filter((page) => page.showInNav)\n .map((page) => ({\n label: page.navLabel,\n href: page.href,\n order: page.navOrder,\n pageKey: page.key,\n }))\n .sort((a, b) => a.order - b.order || a.label.localeCompare(b.label));\n}\n\nexport function deriveSiteFooterGroups(\n pages: readonly SitePage[]\n): SitePageFooterGroup[] {\n const groups = new Map<string, SitePageNavItem[]>();\n\n for (const page of pages.filter((candidate) => candidate.showInFooter)) {\n const label = page.footerGroup || \"Company\";\n const items = groups.get(label) ?? [];\n items.push({\n label: page.footerLabel,\n href: page.href,\n order: page.footerOrder,\n pageKey: page.key,\n });\n groups.set(label, items);\n }\n\n return Array.from(groups.entries()).map(([label, items]) => ({\n label,\n items: items.sort(\n (a, b) => a.order - b.order || a.label.localeCompare(b.label)\n ),\n }));\n}\n","import type { NotionBlock } from \"./types\";\n\ntype BlockChildrenListResponse = {\n results?: unknown[];\n has_more?: boolean;\n next_cursor?: string | null;\n};\n\nexport type NotionBlockClient = {\n blocks: {\n children: {\n list: (args: {\n block_id: string;\n page_size?: number;\n start_cursor?: string;\n }) => Promise<BlockChildrenListResponse>;\n };\n };\n};\n\nfunction normalizeBlock(input: unknown): NotionBlock | null {\n if (!input || typeof input !== \"object\") return null;\n const block = input as NotionBlock;\n return block.id && block.type ? block : null;\n}\n\nexport async function listBlockChildren(\n client: NotionBlockClient,\n blockId: string\n): Promise<NotionBlock[]> {\n const results: NotionBlock[] = [];\n let cursor: string | undefined;\n\n do {\n const response = await client.blocks.children.list({\n block_id: blockId,\n page_size: 100,\n ...(cursor ? { start_cursor: cursor } : {}),\n });\n\n for (const item of response.results ?? []) {\n const block = normalizeBlock(item);\n if (block) results.push(block);\n }\n\n cursor = response.next_cursor ?? undefined;\n if (!response.has_more) break;\n } while (cursor);\n\n return results;\n}\n\nexport async function listBlockChildrenDeep(\n client: NotionBlockClient,\n blockId: string,\n options?: { maxDepth?: number }\n): Promise<NotionBlock[]> {\n const maxDepth = options?.maxDepth ?? 6;\n\n async function visit(id: string, depth: number): Promise<NotionBlock[]> {\n const children = await listBlockChildren(client, id);\n if (depth >= maxDepth) return children;\n\n return Promise.all(\n children.map(async (block) => {\n if (!block.has_children) return block;\n return {\n ...block,\n children: await visit(block.id, depth + 1),\n };\n })\n );\n }\n\n return visit(blockId, 0);\n}\n","import { Client } from \"@notionhq/client\";\nimport type { NotionClientConfig } from \"./config\";\n\nexport function createNotionClient(config: NotionClientConfig) {\n return new Client({\n auth: config.token,\n baseUrl: config.apiBaseUrl,\n notionVersion: \"2026-03-11\",\n });\n}\n","import type { NotionContentModelLike } from \"./types\";\n\ntype NotionEnv = {\n NOTION_TOKEN?: string;\n NOTION_DATA_SOURCE_ID?: string;\n NOTION_API_BASE_URL?: string;\n NOTION_EDIT_BASE_URL?: string;\n NOTION_WEBHOOK_VERIFICATION_TOKEN?: string;\n [key: string]: string | undefined;\n};\n\nexport type NotionClientConfig = {\n token: string;\n apiBaseUrl?: string;\n};\n\nexport type NotionConfig = {\n token: string;\n dataSourceId: string;\n apiBaseUrl?: string;\n editBaseUrl?: string;\n webhookVerificationToken?: string;\n};\n\nfunction readProcessEnv(): NotionEnv {\n const env: NotionEnv = {\n NOTION_TOKEN: process.env.NOTION_TOKEN,\n NOTION_DATA_SOURCE_ID: process.env.NOTION_DATA_SOURCE_ID,\n NOTION_API_BASE_URL: process.env.NOTION_API_BASE_URL,\n NOTION_EDIT_BASE_URL: process.env.NOTION_EDIT_BASE_URL,\n NOTION_WEBHOOK_VERIFICATION_TOKEN:\n process.env.NOTION_WEBHOOK_VERIFICATION_TOKEN,\n };\n\n for (const [key, value] of Object.entries(process.env)) {\n if (key.startsWith(\"NOTION_\") && typeof value === \"string\") {\n env[key] = value;\n }\n }\n\n return env;\n}\n\nasync function readWorkerEnv(): Promise<NotionEnv> {\n try {\n const mod = (await import(\n /* webpackIgnore: true */ \"cloudflare:workers\"\n )) as unknown as { env?: Record<string, unknown> };\n const env: NotionEnv = {};\n for (const [key, value] of Object.entries(mod.env ?? {})) {\n if (key.startsWith(\"NOTION_\") && typeof value === \"string\") {\n env[key] = value;\n }\n }\n return env;\n } catch {\n return {};\n }\n}\n\nfunction readString(source: NotionEnv, name: string): string | undefined {\n const value = String(source[name] ?? \"\").trim();\n return value || undefined;\n}\n\nfunction mergeEnv(...sources: NotionEnv[]): NotionEnv {\n const merged: NotionEnv = {};\n\n for (const source of sources) {\n for (const name of Object.keys(source)) {\n if (!name.startsWith(\"NOTION_\")) continue;\n const value = readString(source, name);\n if (value) merged[name] = value;\n }\n }\n\n return merged;\n}\n\nasync function readEnv(): Promise<NotionEnv> {\n const processEnv = readProcessEnv();\n return mergeEnv(await readWorkerEnv(), processEnv);\n}\n\nfunction readRequired(\n source: NotionEnv,\n name: string\n): string {\n const value = readString(source, name);\n if (!value) {\n throw new Error(`Missing required Notion env: ${name}`);\n }\n return value;\n}\n\nexport function getNotionEditBaseUrl(): string {\n return readString(readProcessEnv(), \"NOTION_EDIT_BASE_URL\") ?? \"https://www.notion.so\";\n}\n\nexport async function hasNotionConfig(): Promise<boolean> {\n const env = await readEnv();\n return Boolean(\n readString(env, \"NOTION_TOKEN\") && readString(env, \"NOTION_DATA_SOURCE_ID\")\n );\n}\n\nexport async function hasNotionModelConfig(\n model: NotionContentModelLike\n): Promise<boolean> {\n const env = await readEnv();\n return Boolean(\n readString(env, \"NOTION_TOKEN\") &&\n (readString(env, model.source.dataSourceEnv) ||\n model.source.defaultDataSourceId)\n );\n}\n\nexport async function getNotionClientConfig(): Promise<NotionClientConfig> {\n const env = await readEnv();\n return {\n token: readRequired(env, \"NOTION_TOKEN\"),\n apiBaseUrl: readString(env, \"NOTION_API_BASE_URL\"),\n };\n}\n\nexport async function getNotionConfig(): Promise<NotionConfig> {\n const env = await readEnv();\n return {\n token: readRequired(env, \"NOTION_TOKEN\"),\n dataSourceId: readRequired(env, \"NOTION_DATA_SOURCE_ID\"),\n apiBaseUrl: readString(env, \"NOTION_API_BASE_URL\"),\n editBaseUrl: readString(env, \"NOTION_EDIT_BASE_URL\"),\n webhookVerificationToken: readString(\n env,\n \"NOTION_WEBHOOK_VERIFICATION_TOKEN\"\n ),\n };\n}\n\nexport async function getNotionWebhookVerificationToken(): Promise<\n string | undefined\n> {\n const env = await readEnv();\n return readString(env, \"NOTION_WEBHOOK_VERIFICATION_TOKEN\");\n}\n\nexport async function getNotionConfigForModel(\n model: NotionContentModelLike\n): Promise<NotionConfig> {\n const env = await readEnv();\n const dataSourceId =\n readString(env, model.source.dataSourceEnv) ??\n model.source.defaultDataSourceId;\n if (!dataSourceId) {\n throw new Error(`Missing required Notion env: ${model.source.dataSourceEnv}`);\n }\n\n return {\n token: readRequired(env, model.source.tokenEnv),\n dataSourceId,\n apiBaseUrl: readString(env, \"NOTION_API_BASE_URL\"),\n editBaseUrl: readString(env, \"NOTION_EDIT_BASE_URL\"),\n webhookVerificationToken: readString(\n env,\n \"NOTION_WEBHOOK_VERIFICATION_TOKEN\"\n ),\n };\n}\n","import type { NotionBlock, NotionFileSource, NotionPageLike } from \"./types\";\n\ntype FileLike = {\n type?: string;\n external?: { url?: string };\n file?: { url?: string; expiry_time?: string };\n name?: string;\n};\n\nfunction stripLeadingSlash(value: string) {\n return value.startsWith(\"/\") ? value.slice(1) : value;\n}\n\nfunction encodePathPart(value: string) {\n return encodeURIComponent(stripLeadingSlash(value));\n}\n\nfunction appendVersion(path: string, version?: string) {\n const value = String(version ?? \"\").trim();\n if (!value) return path;\n return `${path}?${new URLSearchParams({ v: value })}`;\n}\n\nfunction blockVersion(block: NotionBlock): string | undefined {\n return typeof block.last_edited_time === \"string\"\n ? block.last_edited_time\n : undefined;\n}\n\nexport function notionPageCoverMediaPath(pageId: string): string {\n return `/api/notion/media/page/${encodePathPart(pageId)}/cover`;\n}\n\nexport function notionPagePropertyMediaPath(\n pageId: string,\n propertyName: string\n): string {\n return `/api/notion/media/page/${encodePathPart(pageId)}/property/${encodePathPart(propertyName)}`;\n}\n\nexport function notionBlockMediaPath(blockId: string): string {\n return `/api/notion/media/block/${encodePathPart(blockId)}`;\n}\n\nexport function normalizeNotionFileSource(input: unknown): NotionFileSource | null {\n const file = input as FileLike | null | undefined;\n if (!file || typeof file !== \"object\") return null;\n\n if (file.type === \"external\") {\n const url = String(file.external?.url ?? \"\").trim();\n return url ? { type: \"external\", url } : null;\n }\n\n if (file.type === \"file\") {\n const url = String(file.file?.url ?? \"\").trim();\n if (!url) return null;\n return {\n type: \"file\",\n url,\n expiryTime: String(file.file?.expiry_time ?? \"\").trim() || null,\n };\n }\n\n return null;\n}\n\nexport function resolveNotionFileUrl(input: unknown): string | null {\n return normalizeNotionFileSource(input)?.url ?? null;\n}\n\nexport function isNotionHostedFile(input: unknown): boolean {\n return normalizeNotionFileSource(input)?.type === \"file\";\n}\n\nexport function pickFirstFilesPropertyValue(property: unknown): unknown | null {\n const value = property as { type?: string; files?: unknown[] } | null | undefined;\n if (!value || value.type !== \"files\" || !Array.isArray(value.files)) {\n return null;\n }\n return value.files[0] ?? null;\n}\n\nexport function pickPageCoverFile(page: NotionPageLike): unknown | null {\n return page.cover ?? null;\n}\n\nexport function coverImageUrlForPage(\n page: NotionPageLike,\n coverPropertyName = \"Cover\"\n): string | null {\n const propertyFile = pickFirstFilesPropertyValue(\n page.properties?.[coverPropertyName]\n );\n const propertySource = normalizeNotionFileSource(propertyFile);\n if (propertySource) {\n return appendVersion(\n notionPagePropertyMediaPath(page.id, coverPropertyName),\n page.last_edited_time\n );\n }\n\n const coverSource = normalizeNotionFileSource(pickPageCoverFile(page));\n if (coverSource) {\n return appendVersion(notionPageCoverMediaPath(page.id), page.last_edited_time);\n }\n\n return null;\n}\n\nexport function fileObjectForMediaBlock(block: NotionBlock): unknown | null {\n const typed = block[block.type] as Record<string, unknown> | undefined;\n if (!typed || typeof typed !== \"object\") return null;\n\n if (\n block.type === \"image\" ||\n block.type === \"video\" ||\n block.type === \"file\" ||\n block.type === \"pdf\" ||\n block.type === \"audio\"\n ) {\n return typed;\n }\n\n return null;\n}\n\nexport function mediaUrlForBlock(block: NotionBlock): string | null {\n const source = normalizeNotionFileSource(fileObjectForMediaBlock(block));\n if (!source) return null;\n if (source.type === \"external\" && block.type !== \"image\") {\n return source.url;\n }\n return appendVersion(notionBlockMediaPath(block.id), blockVersion(block));\n}\n\nexport function firstImageUrlFromBlocks(blocks: NotionBlock[]): string | null {\n for (const block of blocks) {\n if (block.type === \"image\") {\n const imageUrl = mediaUrlForBlock(block);\n if (imageUrl) return imageUrl;\n }\n\n const nested = block.children?.length\n ? firstImageUrlFromBlocks(block.children)\n : null;\n if (nested) return nested;\n }\n\n return null;\n}\n\nexport function publicMediaBlockForApi(block: NotionBlock): NotionBlock {\n const value = block[block.type];\n const source = normalizeNotionFileSource(value);\n const children = block.children?.map(publicMediaBlockForApi);\n\n if (!source) {\n return children ? { ...block, children } : { ...block };\n }\n\n const path = appendVersion(notionBlockMediaPath(block.id), blockVersion(block));\n const publicValue =\n source.type === \"external\"\n ? block.type === \"image\"\n ? {\n ...(value as Record<string, unknown>),\n external: { url: path },\n }\n : value\n : {\n ...(value as Record<string, unknown>),\n file: {\n url: path,\n expiry_time: null,\n },\n };\n\n return {\n ...block,\n [block.type]: publicValue,\n ...(children ? { children } : {}),\n };\n}\n\nexport function gatedMediaBlockForApi(\n block: NotionBlock,\n options?: { accessUrlForBlock?: (block: NotionBlock) => string | null }\n): NotionBlock {\n const value = block[block.type];\n const source = normalizeNotionFileSource(value);\n const children = block.children?.map((child) =>\n gatedMediaBlockForApi(child, options)\n );\n\n if (block.type !== \"video\" || !source) {\n const publicBlock = publicMediaBlockForApi(block);\n return children ? { ...publicBlock, children } : publicBlock;\n }\n\n const gatedValue: Record<string, unknown> = {\n ...(value as Record<string, unknown>),\n gated: true,\n access_url: options?.accessUrlForBlock?.(block) ?? null,\n };\n\n if (source.type === \"external\") {\n gatedValue.external = { url: null };\n } else {\n gatedValue.file = {\n url: null,\n expiry_time: null,\n };\n }\n\n return {\n ...block,\n video: gatedValue,\n ...(children ? { children } : {}),\n };\n}\n\nexport function isDirectVideoUrl(url: string): boolean {\n try {\n const parsed = new URL(url);\n return /\\.(mp4|webm|mov|m4v)(?:$|\\?)/i.test(parsed.pathname);\n } catch {\n return false;\n }\n}\n\nexport function videoEmbedUrl(url: string): string | null {\n try {\n const parsed = new URL(url);\n const host = parsed.hostname.replace(/^www\\./, \"\");\n\n if (host === \"youtube.com\" || host === \"m.youtube.com\") {\n const id = parsed.searchParams.get(\"v\");\n return id ? `https://www.youtube.com/embed/${encodeURIComponent(id)}` : null;\n }\n\n if (host === \"youtu.be\") {\n const id = parsed.pathname.split(\"/\").filter(Boolean)[0];\n return id ? `https://www.youtube.com/embed/${encodeURIComponent(id)}` : null;\n }\n\n if (host === \"youtube-nocookie.com\") {\n return parsed.toString();\n }\n\n if (host === \"vimeo.com\") {\n const id = parsed.pathname.split(\"/\").filter(Boolean)[0];\n return id ? `https://player.vimeo.com/video/${encodeURIComponent(id)}` : null;\n }\n\n if (host === \"player.vimeo.com\") {\n return parsed.toString();\n }\n } catch {\n return null;\n }\n\n return null;\n}\n","type PropertyMap = Record<string, unknown>;\n\ntype TextPart = {\n plain_text?: string;\n};\n\nexport function isRecord(value: unknown): value is Record<string, unknown> {\n return Boolean(value && typeof value === \"object\");\n}\n\nfunction getPlainText(parts: unknown): string {\n if (!Array.isArray(parts)) return \"\";\n return parts\n .map((part: TextPart) => part.plain_text ?? \"\")\n .join(\"\")\n .trim();\n}\n\nfunction getProperty(properties: PropertyMap, key: string) {\n return properties[key] as Record<string, unknown> | undefined;\n}\n\nfunction firstPropertyOfType(properties: PropertyMap, type: string) {\n return Object.values(properties).find(\n (property): property is Record<string, unknown> =>\n isRecord(property) && property.type === type\n );\n}\n\nexport function getFirstTitleProperty(properties: PropertyMap): string {\n const property = firstPropertyOfType(properties, \"title\");\n return property ? getPlainText(property.title) : \"\";\n}\n\nexport function getRichTextProperty(properties: PropertyMap, key: string): string {\n const property = getProperty(properties, key);\n if (!property) return \"\";\n\n if (property.type === \"title\") return getPlainText(property.title);\n if (property.type === \"rich_text\") return getPlainText(property.rich_text);\n if (property.type === \"url\") return String(property.url ?? \"\").trim();\n if (property.type === \"email\") return String(property.email ?? \"\").trim();\n if (property.type === \"phone_number\") {\n return String(property.phone_number ?? \"\").trim();\n }\n\n return \"\";\n}\n\nexport function getDateProperty(properties: PropertyMap, key: string): string {\n const property = getProperty(properties, key);\n if (property?.type !== \"date\") return \"\";\n const date = property.date as { start?: string } | null | undefined;\n return String(date?.start ?? \"\").trim();\n}\n\nexport function getFirstDateProperty(properties: PropertyMap): string {\n const property = firstPropertyOfType(properties, \"date\");\n if (!property) return \"\";\n const date = property.date as { start?: string } | null | undefined;\n return String(date?.start ?? \"\").trim();\n}\n\nexport function getSelectProperty(properties: PropertyMap, key: string): string {\n const property = getProperty(properties, key);\n if (property?.type !== \"select\") return \"\";\n const select = property.select as { name?: string } | null | undefined;\n return String(select?.name ?? \"\").trim();\n}\n\nexport function getCheckboxProperty(properties: PropertyMap, key: string): boolean {\n const property = getProperty(properties, key);\n if (property?.type !== \"checkbox\") return false;\n return Boolean(property.checkbox);\n}\n\nexport function getNumberProperty(\n properties: PropertyMap,\n key: string,\n fallback = 0\n): number {\n const property = getProperty(properties, key);\n if (property?.type !== \"number\") return fallback;\n const value = Number(property.number);\n return Number.isFinite(value) ? value : fallback;\n}\n\nexport function getRelationPageIds(properties: PropertyMap, key: string): string[] {\n const property = getProperty(properties, key);\n if (property?.type !== \"relation\" || !Array.isArray(property.relation)) {\n return [];\n }\n\n return property.relation\n .map((item: { id?: string }) => String(item.id ?? \"\").trim())\n .filter(Boolean);\n}\n\nexport function getTagsProperty(properties: PropertyMap, key: string): string[] {\n const property = getProperty(properties, key);\n if (property?.type === \"multi_select\" && Array.isArray(property.multi_select)) {\n return property.multi_select\n .map((item: { name?: string }) => String(item.name ?? \"\").trim())\n .filter(Boolean);\n }\n\n if (property?.type === \"select\") {\n const select = property.select as { name?: string } | null | undefined;\n const name = String(select?.name ?? \"\").trim();\n return name ? [name] : [];\n }\n\n return [];\n}\n\nexport function getFirstTagsProperty(properties: PropertyMap): string[] {\n const multiSelect = firstPropertyOfType(properties, \"multi_select\");\n if (multiSelect && Array.isArray(multiSelect.multi_select)) {\n return multiSelect.multi_select\n .map((item: { name?: string }) => String(item.name ?? \"\").trim())\n .filter(Boolean);\n }\n\n const select = firstPropertyOfType(properties, \"select\");\n const name = String((select?.select as { name?: string } | null)?.name ?? \"\").trim();\n return name ? [name] : [];\n}\n\nexport function getAuthorProperty(properties: PropertyMap, key: string): string {\n const property = getProperty(properties, key);\n if (!property) return \"\";\n\n if (property.type === \"people\" && Array.isArray(property.people)) {\n return property.people\n .map((person: { name?: string; person?: { email?: string } }) =>\n String(person.name ?? person.person?.email ?? \"\").trim()\n )\n .filter(Boolean)\n .join(\", \");\n }\n\n return getRichTextProperty(properties, key);\n}\n\nexport function getFirstPeopleProperty(properties: PropertyMap): string {\n const property = firstPropertyOfType(properties, \"people\");\n if (!property || !Array.isArray(property.people)) return \"\";\n\n return property.people\n .map((person: { name?: string; person?: { email?: string } }) =>\n String(person.name ?? person.person?.email ?? \"\").trim()\n )\n .filter(Boolean)\n .join(\", \");\n}\n\nexport function pickPublishedFlag(properties: PropertyMap): boolean {\n const published = getProperty(properties, \"Published\");\n if (published?.type === \"checkbox\") {\n return Boolean(published.checkbox);\n }\n\n const status = getProperty(properties, \"Status\");\n if (status?.type === \"status\") {\n const statusValue = status.status as { name?: string } | null | undefined;\n return String(statusValue?.name ?? \"\").trim().toLowerCase() === \"published\";\n }\n\n if (status?.type === \"select\") {\n const statusValue = status.select as { name?: string } | null | undefined;\n return String(statusValue?.name ?? \"\").trim().toLowerCase() === \"published\";\n }\n\n return false;\n}\n\nexport function pickDescriptionFallback(description: string, title: string): string {\n return description.trim() || title.trim();\n}\n\nexport function isValidPublicSlug(slug: string): boolean {\n return /^[a-z0-9][a-z0-9-]{0,79}$/.test(slug);\n}\n\nexport function notionPageEditUrl(pageId: string, editBaseUrl?: string): string {\n const compactPageId = pageId.replaceAll(\"-\", \"\");\n if (editBaseUrl?.includes(\"{pageId}\")) {\n return editBaseUrl.replaceAll(\"{pageId}\", compactPageId);\n }\n return `https://www.notion.so/${compactPageId}`;\n}\n\n/**\n * Normalize a Notion page id (with or without dashes) to a compact lowercase\n * string. Used as a stable identifier in URLs and cache keys.\n */\nexport function compactNotionId(id: string): string {\n return id.replaceAll(\"-\", \"\").toLowerCase();\n}\n"],"mappings":"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","names":["slug"]}
|