@notionx/core 0.1.2 → 0.1.3

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.
Files changed (80) hide show
  1. package/dist/auth/index.d.ts +1 -1
  2. package/dist/auth/index.js.map +1 -1
  3. package/dist/auth/rate-limit.js.map +1 -1
  4. package/dist/auth/routes/google-callback.js.map +1 -1
  5. package/dist/auth/routes/google.js.map +1 -1
  6. package/dist/auth/routes/index.js.map +1 -1
  7. package/dist/auth/routes/verify-email.js.map +1 -1
  8. package/dist/auth/routes/viewer.js.map +1 -1
  9. package/dist/auth/turnstile.js.map +1 -1
  10. package/dist/auth/user-session.d.ts +1 -1
  11. package/dist/auth/user-session.js.map +1 -1
  12. package/dist/auth/users.js.map +1 -1
  13. package/dist/content/index.d.ts +2 -2
  14. package/dist/content/index.js +8 -60
  15. package/dist/content/index.js.map +1 -1
  16. package/dist/content/revalidate.d.ts +2 -1
  17. package/dist/content/revalidate.js +5 -28
  18. package/dist/content/revalidate.js.map +1 -1
  19. package/dist/content/search-index.d.ts +1 -1
  20. package/dist/content/search-index.js.map +1 -1
  21. package/dist/content/search.d.ts +2 -5
  22. package/dist/content/search.js +3 -32
  23. package/dist/content/search.js.map +1 -1
  24. package/dist/email/index.js.map +1 -1
  25. package/dist/{env-C5qu-0R-.d.ts → env-hoez1e-n.d.ts} +0 -4
  26. package/dist/i18n/index.d.ts +18 -24
  27. package/dist/i18n/index.js +29 -54
  28. package/dist/i18n/index.js.map +1 -1
  29. package/dist/index.js +0 -1
  30. package/dist/index.js.map +1 -1
  31. package/dist/internal/admin/index.js.map +1 -1
  32. package/dist/media/index.js +3 -2
  33. package/dist/media/index.js.map +1 -1
  34. package/dist/media/routes/index.js +0 -1
  35. package/dist/media/routes/index.js.map +1 -1
  36. package/dist/media/routes/notion-media.js +0 -1
  37. package/dist/media/routes/notion-media.js.map +1 -1
  38. package/dist/notion/config.d.ts +1 -4
  39. package/dist/notion/config.js +1 -23
  40. package/dist/notion/config.js.map +1 -1
  41. package/dist/notion/content-cache.d.ts +1 -1
  42. package/dist/notion/generic-source.js +0 -1
  43. package/dist/notion/generic-source.js.map +1 -1
  44. package/dist/notion/index.d.ts +3 -3
  45. package/dist/notion/index.js +1 -23
  46. package/dist/notion/index.js.map +1 -1
  47. package/dist/notion/media.d.ts +1 -1
  48. package/dist/notion/media.js +1 -1
  49. package/dist/notion/media.js.map +1 -1
  50. package/dist/notion/routes/index.d.ts +1 -1
  51. package/dist/notion/routes/index.js +0 -1
  52. package/dist/notion/routes/index.js.map +1 -1
  53. package/dist/notion/routes/webhook.d.ts +1 -1
  54. package/dist/notion/routes/webhook.js +0 -1
  55. package/dist/notion/routes/webhook.js.map +1 -1
  56. package/dist/notion/types.d.ts +1 -73
  57. package/dist/notion/webhook.d.ts +1 -1
  58. package/dist/notion/webhook.js +0 -1
  59. package/dist/notion/webhook.js.map +1 -1
  60. package/dist/pages/index.js +0 -1
  61. package/dist/pages/index.js.map +1 -1
  62. package/dist/platform/current.d.ts +1 -1
  63. package/dist/platform/current.js.map +1 -1
  64. package/dist/platform/index.d.ts +1 -1
  65. package/dist/platform/index.js.map +1 -1
  66. package/dist/platform/runtime.d.ts +1 -1
  67. package/dist/storage/index.js.map +1 -1
  68. package/dist/storage/routes/cdn.js.map +1 -1
  69. package/dist/storage/routes/files.js.map +1 -1
  70. package/dist/storage/routes/index.js.map +1 -1
  71. package/dist/util/index.d.ts +1 -1
  72. package/dist/util/index.js +1 -2
  73. package/dist/util/index.js.map +1 -1
  74. package/dist/worker/index.js +0 -1
  75. package/dist/worker/index.js.map +1 -1
  76. package/dist/worker/routes/content-revalidate.d.ts +1 -1
  77. package/dist/worker/routes/health.js.map +1 -1
  78. package/dist/worker/routes/index.d.ts +1 -1
  79. package/dist/worker/routes/index.js.map +1 -1
  80. package/package.json +1 -1
@@ -6,78 +6,6 @@ type NotionFileSource = {
6
6
  url: string;
7
7
  expiryTime: string | null;
8
8
  };
9
- type NotionPostListItem = {
10
- pageId: string;
11
- updatedAt?: string;
12
- slug: string;
13
- title: string;
14
- description: string;
15
- date: string;
16
- author: string;
17
- tags: string[];
18
- coverImage: string | null;
19
- published: boolean;
20
- editUrl: string | null;
21
- };
22
- type NotionPostDetail = NotionPostListItem & {
23
- blocks: NotionBlock[];
24
- };
25
- type NotionMovieListItem = {
26
- pageId: string;
27
- updatedAt?: string;
28
- routeId: string;
29
- title: string;
30
- releaseDate: string;
31
- director: string;
32
- actors: string;
33
- summary: string;
34
- genres: string[];
35
- downloadText: string;
36
- downloadUrl: string | null;
37
- extractionCode: string;
38
- hasDownloadInfo: boolean;
39
- coverImage: string | null;
40
- editUrl: string | null;
41
- sourceUrl: string | null;
42
- };
43
- type PublicNotionMovieListItem = Omit<NotionMovieListItem, "downloadText" | "downloadUrl" | "extractionCode"> & {
44
- downloadText: "";
45
- downloadUrl: null;
46
- extractionCode: "";
47
- };
48
- type NotionMovieDownloadInfo = Pick<NotionMovieListItem, "routeId" | "title" | "downloadUrl" | "extractionCode" | "hasDownloadInfo">;
49
- type NotionMovieDetail = NotionMovieListItem & {
50
- blocks: NotionBlock[];
51
- };
52
- type PublicNotionMovieDetail = PublicNotionMovieListItem & {
53
- blocks: NotionBlock[];
54
- };
55
- type NotionMovieTranslation = {
56
- pageId: string;
57
- updatedAt?: string;
58
- moviePageId: string;
59
- locale: string;
60
- slug: string;
61
- title: string;
62
- director: string;
63
- actors: string;
64
- summary: string;
65
- genres: string[];
66
- seoTitle: string;
67
- seoDescription: string;
68
- published: boolean;
69
- editUrl: string | null;
70
- sourceUrl: string | null;
71
- };
72
- type LocalizedPublicMovieListItem = PublicNotionMovieListItem & {
73
- locale: string;
74
- slug: string;
75
- seoTitle: string;
76
- seoDescription: string;
77
- };
78
- type LocalizedPublicMovieDetail = LocalizedPublicMovieListItem & {
79
- blocks: NotionBlock[];
80
- };
81
9
  type NotionRichTextPart = {
82
10
  plain_text?: string;
83
11
  href?: string | null;
@@ -149,4 +77,4 @@ type NotionGenericContentModel = {
149
77
  };
150
78
  };
151
79
 
152
- export type { LocalizedPublicMovieDetail, LocalizedPublicMovieListItem, NotionBlock, NotionContentModelLike, NotionFieldMap, NotionFileSource, NotionGenericContentModel, NotionMovieDetail, NotionMovieDownloadInfo, NotionMovieListItem, NotionMovieTranslation, NotionPageLike, NotionPostDetail, NotionPostListItem, NotionRichTextPart, NotionSort, NotionSortDirection, PublicNotionMovieDetail, PublicNotionMovieListItem };
80
+ export type { NotionBlock, NotionContentModelLike, NotionFieldMap, NotionFileSource, NotionGenericContentModel, NotionPageLike, NotionRichTextPart, NotionSort, NotionSortDirection };
@@ -1,7 +1,7 @@
1
1
  import { KeyValueCacheAdapter } from '../platform/runtime.js';
2
2
  export { compactNotionId } from './property-mappers.js';
3
3
  import { NotionGenericContentModel, NotionPageLike, NotionFieldMap } from './types.js';
4
- import '../env-C5qu-0R-.js';
4
+ import '../env-hoez1e-n.js';
5
5
 
6
6
  type JsonRecord = Record<string, unknown>;
7
7
  type NotionWebhookParseResult = {
@@ -13,7 +13,6 @@ function readProcessEnv() {
13
13
  const env = {
14
14
  NOTION_TOKEN: process.env.NOTION_TOKEN,
15
15
  NOTION_DATA_SOURCE_ID: process.env.NOTION_DATA_SOURCE_ID,
16
- NOTION_MOVIES_DATA_SOURCE_ID: process.env.NOTION_MOVIES_DATA_SOURCE_ID,
17
16
  NOTION_API_BASE_URL: process.env.NOTION_API_BASE_URL,
18
17
  NOTION_EDIT_BASE_URL: process.env.NOTION_EDIT_BASE_URL,
19
18
  NOTION_WEBHOOK_VERIFICATION_TOKEN: process.env.NOTION_WEBHOOK_VERIFICATION_TOKEN
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/notion/client.ts","../../src/notion/config.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_MOVIES_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 const DEFAULT_NOTION_MOVIES_DATA_SOURCE_ID =\n \"371dc62d-0738-8015-a601-000bc3944fcb\";\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_MOVIES_DATA_SOURCE_ID: process.env.NOTION_MOVIES_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 hasNotionMovieConfig(): Promise<boolean> {\n const env = await readEnv();\n return Boolean(readString(env, \"NOTION_TOKEN\"));\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 getNotionMovieConfig(): Promise<NotionConfig> {\n const env = await readEnv();\n return {\n token: readRequired(env, \"NOTION_TOKEN\"),\n dataSourceId:\n readString(env, \"NOTION_MOVIES_DATA_SOURCE_ID\") ??\n DEFAULT_NOTION_MOVIES_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 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","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":["readString"]}
1
+ {"version":3,"sources":["../../src/notion/client.ts","../../src/notion/config.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","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":";AAAA,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;AAqDA,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;;;AC7JA,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;AAgBO,SAAS,kBAAkB,YAAyB,KAAqB;AAC9E,QAAM,WAAW,YAAY,YAAY,GAAG;AAC5C,MAAI,UAAU,SAAS,SAAU,QAAO;AACxC,QAAM,SAAS,SAAS;AACxB,SAAO,OAAO,QAAQ,QAAQ,EAAE,EAAE,KAAK;AACzC;AAgHO,SAAS,kBAAkB,MAAuB;AACvD,SAAO,4BAA4B,KAAK,IAAI;AAC9C;AAcO,SAAS,gBAAgB,IAAoB;AAClD,SAAO,GAAG,WAAW,KAAK,EAAE,EAAE,YAAY;AAC5C;;;AChLA,IAAM,uCACJ;AAkEF,SAAS,SAAS,OAAqC;AACrD,SAAO,QAAQ,SAAS,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK,CAAC;AAC5E;AAEA,SAASA,YAAW,QAAiB,KAAa;AAChD,MAAI,CAAC,SAAS,MAAM,EAAG,QAAO;AAC9B,QAAM,QAAQ,OAAO,GAAG;AACxB,SAAO,OAAO,UAAU,WAAW,MAAM,KAAK,IAAI;AACpD;AAEA,SAAS,UAAU,OAAgB;AACjC,MAAI,MAAM,QAAQ,KAAK,EAAG,QAAO,MAAM,OAAO,QAAQ;AACtD,MAAI,SAAS,KAAK,EAAG,QAAO,CAAC,KAAK;AAClC,SAAO,CAAC;AACV;AAEA,SAAS,cAAc,WAAuB,MAAgB;AAC5D,QAAM,UAAwB,CAAC;AAC/B,aAAW,OAAO,MAAM;AACtB,UAAM,QAAQ,OAAO,GAAG;AACxB,QAAI,SAAS,KAAK,EAAG,SAAQ,KAAK,KAAK;AAAA,EACzC;AACA,SAAO;AACT;AAEA,SAAS,eAAe,QAAkB;AACxC,SAAO,OAAO,KAAK,OAAO,KAAK;AACjC;AAEA,SAAS,YAAY,OAAe;AAClC,SAAO,MAAM,WAAW,KAAK,EAAE,EAAE,YAAY;AAC/C;AAEA,SAAS,aAAa,OAAmB,MAAsB;AAC7D,QAAM,QAAQ,CAAC,KAAK;AACpB,QAAM,OAAO,oBAAI,IAAgB;AAEjC,SAAO,MAAM,SAAS,GAAG;AACvB,UAAM,UAAU,MAAM,IAAI;AAC1B,QAAI,CAAC,WAAW,KAAK,IAAI,OAAO,EAAG;AACnC,SAAK,IAAI,OAAO;AAEhB,QAAIA,YAAW,SAAS,MAAM,MAAM,MAAM;AACxC,YAAM,KAAKA,YAAW,SAAS,IAAI;AACnC,UAAI,GAAI,QAAO;AAAA,IACjB;AAEA,eAAW,SAAS,OAAO,OAAO,OAAO,GAAG;AAC1C,UAAI,SAAS,KAAK,EAAG,OAAM,KAAK,KAAK;AACrC,UAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,mBAAW,QAAQ,OAAO;AACxB,cAAI,SAAS,IAAI,EAAG,OAAM,KAAK,IAAI;AAAA,QACrC;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAEA,SAAS,iBAAiB,OAA2B;AACnD,QAAM,QAAQ,CAAC,KAAK;AACpB,QAAM,OAAO,oBAAI,IAAgB;AAEjC,SAAO,MAAM,SAAS,GAAG;AACvB,UAAM,UAAU,MAAM,IAAI;AAC1B,QAAI,CAAC,WAAW,KAAK,IAAI,OAAO,EAAG;AACnC,SAAK,IAAI,OAAO;AAEhB,UAAM,SAAS;AAAA,MACbA,YAAW,SAAS,gBAAgB;AAAA,MACpCA,YAAW,SAAS,WAAW;AAAA,IACjC;AACA,QAAI,OAAQ,QAAO;AAEnB,UAAM,OAAOA,YAAW,SAAS,MAAM;AACvC,QAAI,SAAS,eAAe;AAC1B,YAAM,KAAKA,YAAW,SAAS,IAAI;AACnC,UAAI,GAAI,QAAO;AAAA,IACjB;AAEA,eAAW,SAAS,OAAO,OAAO,OAAO,GAAG;AAC1C,UAAI,SAAS,KAAK,EAAG,OAAM,KAAK,KAAK;AACrC,UAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,mBAAW,QAAQ,OAAO;AACxB,cAAI,SAAS,IAAI,EAAG,OAAM,KAAK,IAAI;AAAA,QACrC;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAEA,SAAS,eAAe,OAA+C;AACrE,MAAI,MAAM,QAAQ,KAAK,EAAG,QAAO,MAAM,CAAC;AACxC,SAAO;AACT;AAEA,SAAS,6BACP,OACA,MACA;AACA,QAAM,aAAa,SAAS,KAAK,UAAU,IAAI,KAAK,aAAa,CAAC;AAClE,QAAM,YAAY,eAAe,MAAM,OAAO,OAAO,IAAI;AACzD,QAAM,OAAO,YACT,oBAAoB,YAAY,SAAS,EAAE,YAAY,IACvD;AACJ,SAAO,kBAAkB,IAAI,IAAI,OAAO;AAC1C;AAEA,SAAS,4BAA4B,OAAkC,MAAkB;AACvF,QAAM,aAAa,SAAS,KAAK,UAAU,IAAI,KAAK,aAAa,CAAC;AAClE,QAAM,cAAc,eAAe,MAAM,OAAO,OAAO,MAAM;AAC7D,SAAO,cAAc,kBAAkB,YAAY,WAAW,IAAI;AACpE;AAEA,SAAS,UAAU,WAAoD;AACrE,MAAI,UAAU,SAAS,UAAU,EAAG,QAAO;AAC3C,MAAI,UAAU,SAAS,UAAU,KAAK,UAAU,SAAS,YAAY,GAAG;AACtE,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAEA,SAAS,4BACP,OACe;AACf,QAAM,UAAU,QAAQ,IAAI,MAAM,OAAO,aAAa;AACtD,MAAI,QAAS,QAAO;AACpB,SAAO,MAAM,OAAO,uBAAuB;AAC7C;AAEA,SAAS,yBAGP,QACA,cACA,iBACA;AACA,MAAI,CAAC,aAAc,QAAO;AAC1B,QAAM,aAAa,YAAY,YAAY;AAC3C,SACE,OAAO,KAAK,CAAC,UAAU;AACrB,UAAM,aAAa,gBAAgB,KAAK;AACxC,WAAO,aAAa,YAAY,UAAU,MAAM,aAAa;AAAA,EAC/D,CAAC,KAAK;AAEV;AAEA,SAAS,kBAGP,QACA,OACA,MACA,iBACA;AACA,QAAM,UAAU;AAAA,IACdA,YAAW,OAAO,SAAS;AAAA,IAC3BA,YAAW,OAAO,UAAU;AAAA,IAC5BA,YAAW,MAAM,MAAM,SAAS;AAAA,IAChCA,YAAW,MAAM,MAAM,UAAU;AAAA,EACnC;AACA,MAAI,SAAS;AACX,WAAO,OAAO,KAAK,CAAC,UAAU,MAAM,OAAO,OAAO,KAAK;AAAA,EACzD;AAEA,QAAM,eAAe;AAAA,IACnB,iBAAiB,KAAK;AAAA,IACtB,aAAa,OAAO,aAAa;AAAA,IACjCA,YAAW,OAAO,gBAAgB;AAAA,IAClCA,YAAW,MAAM,MAAM,gBAAgB;AAAA,IACvCA,YAAW,MAAM,QAAQ,gBAAgB;AAAA,IACzCA,YAAW,MAAM,QAAQ,aAAa;AAAA,EACxC;AACA,SAAO,yBAAyB,QAAQ,cAAc,eAAe;AACvE;AAEA,SAAS,eAAe,OAAmB,MAAmB;AAC5D,SAAO;AAAA,IACLA,YAAW,MAAM,IAAI;AAAA,IACrB,aAAa,OAAO,MAAM;AAAA,IAC1BA,YAAW,OAAO,SAAS;AAAA,IAC3BA,YAAW,MAAM,MAAM,SAAS;AAAA,EAClC;AACF;AAEA,SAAS,aAAa,OAAmB;AACvC,MAAI,WAA8B;AAClC,aAAW,UAAU;AAAA,IACnB,GAAG,cAAc,SAAS,MAAM,IAAI,IAAI,MAAM,OAAO,CAAC,GAAG,QAAQ,QAAQ;AAAA,IACzE,GAAG,cAAc,OAAO,QAAQ,QAAQ;AAAA,IACxC;AAAA,EACF,GAAG;AACD,UAAM,OAAOA,YAAW,QAAQ,MAAM;AACtC,QAAI,SAAS,UAAUA,YAAW,QAAQ,QAAQ,MAAM,QAAQ;AAC9D,UAAI,CAACA,YAAW,QAAQ,IAAI,EAAG;AAC/B,UAAI,SAAS,OAAO,UAAU,KAAK,SAAS,OAAO,MAAM,EAAG,QAAO;AACnE,mBAAa;AAAA,IACf;AAAA,EACF;AACA,SAAO;AACT;AAEO,SAAS,0BAGd,SACA,SAC0B;AAC1B,MAAI,CAAC,SAAS,OAAO,EAAG,QAAO,EAAE,MAAM,UAAU,QAAQ,CAAC,EAAE;AAE5D,QAAM,oBAAoBA,YAAW,SAAS,oBAAoB;AAClE,MAAI,mBAAmB;AACrB,WAAO,EAAE,MAAM,gBAAgB,kBAAkB;AAAA,EACnD;AAEA,QAAM,kBAAkB,QAAQ,wBAAwB;AAExD,QAAM,SAAS,UAAU,QAAQ,UAAU,QAAQ,SAAS,OAAO,EAChE;AAAA,IAAI,CAAC,UACJ,WAAW,OAAO,QAAQ,QAAQ,eAAe;AAAA,EACnD,EACC,OAAO,CAAC,UAAuC,QAAQ,KAAK,CAAC;AAEhE,SAAO,EAAE,MAAM,UAAU,OAAO;AAClC;AAEA,SAAS,WAGP,OACA,QACA,iBAC2B;AAC3B,QAAM,YAAY,YAAYA,YAAW,OAAO,MAAM,GAAGA,YAAW,OAAO,OAAO,CAAC;AACnF,MAAI,CAAC,UAAW,QAAO;AACvB,QAAM,OAAO,aAAa,KAAK;AAC/B,QAAM,QAAQ;AAAA,IACZ;AAAA,IACA;AAAA,IACA,QAAQ;AAAA,IACR;AAAA,EACF;AACA,MAAI,CAAC,MAAO,QAAO;AAEnB,QAAM,UAAU,OACX,MAAM,iBAAiB,IAAI,KAC1B,6BAA6B,OAAO,IAAI,IAC1C;AACJ,QAAM,SAAS,OACV,MAAM,gBAAgB,IAAI,KAAK,4BAA4B,OAAO,IAAI,IACvE;AACJ,QAAM,eAAe;AAAA,IACnB,iBAAiB,KAAK;AAAA,IACtB,aAAa,OAAO,aAAa;AAAA,IACjCA,YAAW,OAAO,gBAAgB;AAAA,IAClCA,YAAW,MAAM,MAAM,gBAAgB;AAAA,IACvCA,YAAW,MAAM,QAAQ,gBAAgB;AAAA,IACzCA,YAAW,MAAM,QAAQ,aAAa;AAAA,EACxC;AACA,QAAM,kBACJ,UAAU,WAAW,cAAc,KAAK,UAAU,WAAW,WAAW;AAC1E,SAAO;AAAA,IACL,IAAIA,YAAW,OAAO,IAAI,KAAK;AAAA,IAC/B;AAAA,IACA,SAAS,MAAM;AAAA,IACf,QAAQ,eAAe,OAAO,QAAQ,MAAS,KAAK;AAAA,IACpD,cAAc,gBAAgB;AAAA,IAC9B,SAAS,WAAW;AAAA,IACpB,QAAQ,UAAU;AAAA,IAClB,MAAM,UAAU,SAAS;AAAA,IACzB,YAAY;AAAA,IACZ,QAAQ,QAAQ,UAAU,SAAS,kBAAkB,gBAAgB;AAAA,EACvE;AACF;AAEA,eAAe,0BACb,QACA,OACA;AACA,QAAM,SAAS,MAAM,wBAAwB,KAAK;AAClD,QAAM,SAAS,mBAAmB,MAAM;AACxC,QAAM,OAAO,MAAM,OAAO,MAAM,SAAS,EAAE,SAAS,OAAO,CAAC;AAC5D,SAAO;AACT;AAEA,SAAS,yBAGP,OACA,QACA,cAC6B;AAC7B,MAAI,MAAM,WAAW,CAAC,MAAM,OAAQ,QAAO,QAAQ,QAAQ,KAAK;AAChE,MAAI,MAAM,SAAS,SAAU,QAAO,QAAQ,QAAQ,KAAK;AAEzD,QAAM,QAAQ,OAAO,KAAK,CAAC,MAAM,EAAE,OAAO,MAAM,OAAO;AACvD,MAAI,CAAC,MAAO,QAAO,QAAQ,QAAQ,KAAK;AAExC,SAAO,aAAa,MAAM,QAAQ,KAAK,EACpC,KAAK,CAAC,SAAS;AACd,QAAI,CAAC,KAAM,QAAO;AAClB,UAAM,aAAa;AACnB,UAAM,UACJ,MAAM,iBAAiB,UAAU,KACjC,6BAA6B,OAAO,UAAU;AAChD,UAAM,SACJ,MAAM,gBAAgB,UAAU,KAChC,4BAA4B,OAAO,UAAU;AAC/C,QAAI,CAAC,QAAS,QAAO;AACrB,WAAO;AAAA,MACL,GAAG;AAAA,MACH;AAAA,MACA,QAAQ,UAAU,MAAM;AAAA,MACxB,QAAQ;AAAA,IACV;AAAA,EACF,CAAC,EACA,MAAM,CAAC,UAAU;AAChB,UAAM,MAAM;AACZ,YAAQ;AAAA,MACN,KAAK,UAAU;AAAA,QACb,KAAK;AAAA,QACL,SAAS,MAAM;AAAA,QACf,WAAW,MAAM;AAAA,QACjB,SAAS,MAAM;AAAA,QACf,QAAQ,MAAM;AAAA,QACd,MAAM,KAAK;AAAA,QACX,QAAQ,KAAK;AAAA,QACb,SAAS,KAAK,WAAW,OAAO,KAAK;AAAA,MACvC,CAAC;AAAA,IACH;AACA,WAAO;AAAA,EACT,CAAC;AACL;AAEA,eAAsB,wCAGpB,SACA,SAImC;AACnC,QAAM,SAAS,0BAA0B,SAAS,OAAO;AACzD,MAAI,OAAO,SAAS,eAAgB,QAAO;AAC3C,MAAI,SAAS,gBAAgB,MAAO,QAAO;AAE3C,QAAM,eAAe,SAAS,gBAAgB;AAC9C,SAAO;AAAA,IACL,MAAM;AAAA,IACN,QAAQ,MAAM,QAAQ;AAAA,MACpB,OAAO,OAAO;AAAA,QAAI,CAAC,UACjB,yBAAyB,OAAO,QAAQ,QAAQ,YAAY;AAAA,MAC9D;AAAA,IACF;AAAA,EACF;AACF;AAEO,SAAS,sCACd,OACgC;AAChC,SAAO;AAAA,IACL,SAAS,MAAM;AAAA,IACf,QAAQ,MAAM;AAAA,IACd,SAAS,MAAM;AAAA,IACf,QAAQ,MAAM;AAAA,IACd,MAAM,MAAM;AAAA,IACZ,YAAY,MAAM;AAAA,EACpB;AACF;AAEA,eAAsB,sBAAsB,MAAc,mBAA2B;AACnF,QAAM,MAAM,MAAM,OAAO,OAAO;AAAA,IAC9B;AAAA,IACA,IAAI,YAAY,EAAE,OAAO,iBAAiB;AAAA,IAC1C,EAAE,MAAM,QAAQ,MAAM,UAAU;AAAA,IAChC;AAAA,IACA,CAAC,MAAM;AAAA,EACT;AACA,QAAM,YAAY,MAAM,OAAO,OAAO;AAAA,IACpC;AAAA,IACA;AAAA,IACA,IAAI,YAAY,EAAE,OAAO,IAAI;AAAA,EAC/B;AACA,SAAO,MAAM,KAAK,IAAI,WAAW,SAAS,CAAC,EACxC,IAAI,CAAC,SAAS,KAAK,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC,EAChD,KAAK,EAAE;AACZ;AAEA,SAAS,yBAAyB,WAA0B;AAC1D,QAAM,QAAQ,OAAO,aAAa,EAAE,EAAE,KAAK;AAC3C,QAAM,WAAW,MAAM,MAAM,gBAAgB;AAC7C,SAAO,YAAY,SAAS,CAAC,IAAI,SAAS,CAAC,EAAE,KAAK,IAAI;AACxD;AAEA,eAAsB,6BAA6B,OAIhD;AACD,QAAM,QAAQ,OAAO,MAAM,qBAAqB,EAAE,EAAE,KAAK;AACzD,QAAM,YAAY,yBAAyB,MAAM,SAAS;AAC1D,MAAI,CAAC,SAAS,CAAC,UAAW,QAAO;AACjC,QAAM,WAAW,MAAM,sBAAsB,MAAM,MAAM,KAAK;AAC9D,MAAI,SAAS,WAAW,UAAU,OAAQ,QAAO;AAEjD,MAAI,OAAO;AACX,WAAS,QAAQ,GAAG,QAAQ,SAAS,QAAQ,SAAS,GAAG;AACvD,YAAQ,SAAS,WAAW,KAAK,IAAI,UAAU,WAAW,KAAK;AAAA,EACjE;AACA,SAAO,SAAS;AAClB;AAEA,eAAsB,uCAAuC,OAI1D;AACD,QAAM,SAAS,MAAM;AAAA,IACnB,IAAI,IAAI,MAAM,mBAAmB,IAAI,CAAC,UAAU,OAAO,SAAS,EAAE,EAAE,KAAK,CAAC,CAAC;AAAA,EAC7E,EAAE,OAAO,OAAO;AAEhB,aAAW,SAAS,QAAQ;AAC1B,QACE,MAAM,6BAA6B;AAAA,MACjC,MAAM,MAAM;AAAA,MACZ,WAAW,MAAM;AAAA,MACjB,mBAAmB;AAAA,IACrB,CAAC,GACD;AACA,aAAO;AAAA,IACT;AAAA,EACF;AAEA,SAAO;AACT;AAEA,SAAS,mCAAmC,OAAgB;AAC1D,MAAI,OAAO,UAAU,SAAU,QAAO,MAAM,KAAK,KAAK;AACtD,MAAI,CAAC,SAAS,KAAK,EAAG,QAAO;AAE7B,QAAM,QAAQA,YAAW,OAAO,OAAO;AACvC,SAAO,SAAS;AAClB;AAEA,eAAsB,wCACpB,OACA;AACA,MAAI,CAAC,MAAO,QAAO;AAEnB,MAAI;AACF,UAAM,QAAQ,MAAM,MAAM;AAAA,MACxB;AAAA,MACA,EAAE,UAAU,GAAG;AAAA,IACjB;AACA,WAAO,mCAAmC,KAAK;AAAA,EACjD,SAAS,OAAO;AACd,YAAQ;AAAA,MACN,KAAK,UAAU;AAAA,QACb,KAAK;AAAA,QACL,SAAS,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAAA,MAChE,CAAC;AAAA,IACH;AACA,WAAO;AAAA,EACT;AACF;AAEA,eAAsB,wCACpB,OACA,OACA;AACA,QAAM,aAAa,MAAM,KAAK;AAC9B,MAAI,CAAC,SAAS,CAAC,WAAY,QAAO;AAElC,QAAM,MAAM;AAAA,IACV;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,IACpC;AAAA,IACA;AAAA,MACE,UAAU;AAAA,QACR,QAAQ;AAAA,MACV;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;","names":["readString"]}
@@ -98,7 +98,6 @@ function readProcessEnv() {
98
98
  const env = {
99
99
  NOTION_TOKEN: process.env.NOTION_TOKEN,
100
100
  NOTION_DATA_SOURCE_ID: process.env.NOTION_DATA_SOURCE_ID,
101
- NOTION_MOVIES_DATA_SOURCE_ID: process.env.NOTION_MOVIES_DATA_SOURCE_ID,
102
101
  NOTION_API_BASE_URL: process.env.NOTION_API_BASE_URL,
103
102
  NOTION_EDIT_BASE_URL: process.env.NOTION_EDIT_BASE_URL,
104
103
  NOTION_WEBHOOK_VERIFICATION_TOKEN: process.env.NOTION_WEBHOOK_VERIFICATION_TOKEN
@@ -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_MOVIES_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 const DEFAULT_NOTION_MOVIES_DATA_SOURCE_ID =\n \"371dc62d-0738-8015-a601-000bc3944fcb\";\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_MOVIES_DATA_SOURCE_ID: process.env.NOTION_MOVIES_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 hasNotionMovieConfig(): Promise<boolean> {\n const env = await readEnv();\n return Boolean(readString(env, \"NOTION_TOKEN\"));\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 getNotionMovieConfig(): Promise<NotionConfig> {\n const env = await readEnv();\n return {\n token: readRequired(env, \"NOTION_TOKEN\"),\n dataSourceId:\n readString(env, \"NOTION_MOVIES_DATA_SOURCE_ID\") ??\n DEFAULT_NOTION_MOVIES_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 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?: { movieId?: string }\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?.movieId\n ? `/api/movies/${encodePathPart(options.movieId)}/video/${encodePathPart(block.id)}`\n : 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 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,6 +1,6 @@
1
1
  import { SqlDatabaseAdapter, KeyValueCacheAdapter, PublicCacheAdapter, RuntimePlatform } from './runtime.js';
2
2
  import { currentRuntimeId } from './selection.js';
3
- import '../env-C5qu-0R-.js';
3
+ import '../env-hoez1e-n.js';
4
4
 
5
5
  declare function getRuntimePlatform(): RuntimePlatform;
6
6
  declare function getDatabase(): SqlDatabaseAdapter;