@notionx/core 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (208) hide show
  1. package/dist/admin/index.d.ts +137 -0
  2. package/dist/admin/index.js +206 -0
  3. package/dist/admin/index.js.map +1 -0
  4. package/dist/admin/pages/index.d.ts +324 -0
  5. package/dist/admin/pages/index.js +827 -0
  6. package/dist/admin/pages/index.js.map +1 -0
  7. package/dist/auth/auth-pages/forgot-password.d.ts +20 -0
  8. package/dist/auth/auth-pages/forgot-password.js +70 -0
  9. package/dist/auth/auth-pages/forgot-password.js.map +1 -0
  10. package/dist/auth/auth-pages/index.d.ts +6 -0
  11. package/dist/auth/auth-pages/index.js +342 -0
  12. package/dist/auth/auth-pages/index.js.map +1 -0
  13. package/dist/auth/auth-pages/login.d.ts +30 -0
  14. package/dist/auth/auth-pages/login.js +125 -0
  15. package/dist/auth/auth-pages/login.js.map +1 -0
  16. package/dist/auth/auth-pages/register.d.ts +17 -0
  17. package/dist/auth/auth-pages/register.js +81 -0
  18. package/dist/auth/auth-pages/register.js.map +1 -0
  19. package/dist/auth/auth-pages/reset-password.d.ts +18 -0
  20. package/dist/auth/auth-pages/reset-password.js +72 -0
  21. package/dist/auth/auth-pages/reset-password.js.map +1 -0
  22. package/dist/auth/index.d.ts +72 -0
  23. package/dist/auth/index.js +1011 -0
  24. package/dist/auth/index.js.map +1 -0
  25. package/dist/auth/passwords.d.ts +6 -0
  26. package/dist/auth/passwords.js +79 -0
  27. package/dist/auth/passwords.js.map +1 -0
  28. package/dist/auth/rate-limit.d.ts +28 -0
  29. package/dist/auth/rate-limit.js +245 -0
  30. package/dist/auth/rate-limit.js.map +1 -0
  31. package/dist/auth/routes/google-callback.d.ts +6 -0
  32. package/dist/auth/routes/google-callback.js +404 -0
  33. package/dist/auth/routes/google-callback.js.map +1 -0
  34. package/dist/auth/routes/google.d.ts +6 -0
  35. package/dist/auth/routes/google.js +250 -0
  36. package/dist/auth/routes/google.js.map +1 -0
  37. package/dist/auth/routes/index.d.ts +22 -0
  38. package/dist/auth/routes/index.js +619 -0
  39. package/dist/auth/routes/index.js.map +1 -0
  40. package/dist/auth/routes/verify-email.d.ts +6 -0
  41. package/dist/auth/routes/verify-email.js +317 -0
  42. package/dist/auth/routes/verify-email.js.map +1 -0
  43. package/dist/auth/routes/viewer.d.ts +6 -0
  44. package/dist/auth/routes/viewer.js +372 -0
  45. package/dist/auth/routes/viewer.js.map +1 -0
  46. package/dist/auth/session.d.ts +9 -0
  47. package/dist/auth/session.js +1 -0
  48. package/dist/auth/session.js.map +1 -0
  49. package/dist/auth/turnstile.d.ts +20 -0
  50. package/dist/auth/turnstile.js +301 -0
  51. package/dist/auth/turnstile.js.map +1 -0
  52. package/dist/auth/user-session.d.ts +42 -0
  53. package/dist/auth/user-session.js +419 -0
  54. package/dist/auth/user-session.js.map +1 -0
  55. package/dist/auth/users.d.ts +112 -0
  56. package/dist/auth/users.js +558 -0
  57. package/dist/auth/users.js.map +1 -0
  58. package/dist/bootstrap-CN2g76M6.d.ts +67 -0
  59. package/dist/cache/index.d.ts +6 -0
  60. package/dist/cache/index.js +47 -0
  61. package/dist/cache/index.js.map +1 -0
  62. package/dist/content/admin-summary.d.ts +24 -0
  63. package/dist/content/admin-summary.js +36 -0
  64. package/dist/content/admin-summary.js.map +1 -0
  65. package/dist/content/index.d.ts +9 -0
  66. package/dist/content/index.js +473 -0
  67. package/dist/content/index.js.map +1 -0
  68. package/dist/content/models.d.ts +69 -0
  69. package/dist/content/models.js +24 -0
  70. package/dist/content/models.js.map +1 -0
  71. package/dist/content/prewarm.d.ts +28 -0
  72. package/dist/content/prewarm.js +56 -0
  73. package/dist/content/prewarm.js.map +1 -0
  74. package/dist/content/revalidate.d.ts +37 -0
  75. package/dist/content/revalidate.js +170 -0
  76. package/dist/content/revalidate.js.map +1 -0
  77. package/dist/content/search-index.d.ts +54 -0
  78. package/dist/content/search-index.js +172 -0
  79. package/dist/content/search-index.js.map +1 -0
  80. package/dist/content/search.d.ts +8 -0
  81. package/dist/content/search.js +57 -0
  82. package/dist/content/search.js.map +1 -0
  83. package/dist/doctor/cli.d.ts +1 -0
  84. package/dist/doctor/cli.js +360 -0
  85. package/dist/doctor/cli.js.map +1 -0
  86. package/dist/doctor/index.d.ts +139 -0
  87. package/dist/doctor/index.js +289 -0
  88. package/dist/doctor/index.js.map +1 -0
  89. package/dist/email/index.d.ts +38 -0
  90. package/dist/email/index.js +126 -0
  91. package/dist/email/index.js.map +1 -0
  92. package/dist/env-C5qu-0R-.d.ts +35 -0
  93. package/dist/hooks/index.d.ts +2 -0
  94. package/dist/hooks/index.js +1 -0
  95. package/dist/hooks/index.js.map +1 -0
  96. package/dist/i18n/index.d.ts +26 -0
  97. package/dist/i18n/index.js +73 -0
  98. package/dist/i18n/index.js.map +1 -0
  99. package/dist/index.d.ts +8 -0
  100. package/dist/index.js +1281 -0
  101. package/dist/index.js.map +1 -0
  102. package/dist/internal/admin/index.d.ts +75 -0
  103. package/dist/internal/admin/index.js +365 -0
  104. package/dist/internal/admin/index.js.map +1 -0
  105. package/dist/media/index.d.ts +24 -0
  106. package/dist/media/index.js +86 -0
  107. package/dist/media/index.js.map +1 -0
  108. package/dist/media/routes/index.d.ts +1 -0
  109. package/dist/media/routes/index.js +585 -0
  110. package/dist/media/routes/index.js.map +1 -0
  111. package/dist/media/routes/notion-media.d.ts +19 -0
  112. package/dist/media/routes/notion-media.js +588 -0
  113. package/dist/media/routes/notion-media.js.map +1 -0
  114. package/dist/middleware.d.ts +95 -0
  115. package/dist/middleware.js +79 -0
  116. package/dist/middleware.js.map +1 -0
  117. package/dist/notion/block-text.d.ts +5 -0
  118. package/dist/notion/block-text.js +37 -0
  119. package/dist/notion/block-text.js.map +1 -0
  120. package/dist/notion/blocks.d.ts +24 -0
  121. package/dist/notion/blocks.js +46 -0
  122. package/dist/notion/blocks.js.map +1 -0
  123. package/dist/notion/client.d.ts +7 -0
  124. package/dist/notion/client.js +13 -0
  125. package/dist/notion/client.js.map +1 -0
  126. package/dist/notion/config.d.ts +25 -0
  127. package/dist/notion/config.js +147 -0
  128. package/dist/notion/config.js.map +1 -0
  129. package/dist/notion/content-cache.d.ts +45 -0
  130. package/dist/notion/content-cache.js +166 -0
  131. package/dist/notion/content-cache.js.map +1 -0
  132. package/dist/notion/generic-source.d.ts +61 -0
  133. package/dist/notion/generic-source.js +408 -0
  134. package/dist/notion/generic-source.js.map +1 -0
  135. package/dist/notion/index.d.ts +13 -0
  136. package/dist/notion/index.js +1278 -0
  137. package/dist/notion/index.js.map +1 -0
  138. package/dist/notion/mappers.d.ts +1 -0
  139. package/dist/notion/mappers.js +152 -0
  140. package/dist/notion/mappers.js.map +1 -0
  141. package/dist/notion/media.d.ts +22 -0
  142. package/dist/notion/media.js +209 -0
  143. package/dist/notion/media.js.map +1 -0
  144. package/dist/notion/property-mappers.d.ts +24 -0
  145. package/dist/notion/property-mappers.js +152 -0
  146. package/dist/notion/property-mappers.js.map +1 -0
  147. package/dist/notion/routes/index.d.ts +8 -0
  148. package/dist/notion/routes/index.js +428 -0
  149. package/dist/notion/routes/index.js.map +1 -0
  150. package/dist/notion/routes/webhook.d.ts +98 -0
  151. package/dist/notion/routes/webhook.js +428 -0
  152. package/dist/notion/routes/webhook.js.map +1 -0
  153. package/dist/notion/types.d.ts +152 -0
  154. package/dist/notion/types.js +1 -0
  155. package/dist/notion/types.js.map +1 -0
  156. package/dist/notion/webhook.d.ts +83 -0
  157. package/dist/notion/webhook.js +490 -0
  158. package/dist/notion/webhook.js.map +1 -0
  159. package/dist/platform/capabilities.d.ts +34 -0
  160. package/dist/platform/capabilities.js +42 -0
  161. package/dist/platform/capabilities.js.map +1 -0
  162. package/dist/platform/current.d.ts +13 -0
  163. package/dist/platform/current.js +181 -0
  164. package/dist/platform/current.js.map +1 -0
  165. package/dist/platform/index.d.ts +5 -0
  166. package/dist/platform/index.js +269 -0
  167. package/dist/platform/index.js.map +1 -0
  168. package/dist/platform/runtime.d.ts +118 -0
  169. package/dist/platform/runtime.js +160 -0
  170. package/dist/platform/runtime.js.map +1 -0
  171. package/dist/platform/selection.d.ts +10 -0
  172. package/dist/platform/selection.js +22 -0
  173. package/dist/platform/selection.js.map +1 -0
  174. package/dist/storage/index.d.ts +17 -0
  175. package/dist/storage/index.js +218 -0
  176. package/dist/storage/index.js.map +1 -0
  177. package/dist/storage/routes/cdn.d.ts +19 -0
  178. package/dist/storage/routes/cdn.js +289 -0
  179. package/dist/storage/routes/cdn.js.map +1 -0
  180. package/dist/storage/routes/files.d.ts +27 -0
  181. package/dist/storage/routes/files.js +216 -0
  182. package/dist/storage/routes/files.js.map +1 -0
  183. package/dist/storage/routes/index.d.ts +2 -0
  184. package/dist/storage/routes/index.js +352 -0
  185. package/dist/storage/routes/index.js.map +1 -0
  186. package/dist/types-BsAcZSNX.d.ts +94 -0
  187. package/dist/types.d.ts +78 -0
  188. package/dist/types.js +1 -0
  189. package/dist/types.js.map +1 -0
  190. package/dist/util/index.d.ts +18 -0
  191. package/dist/util/index.js +48 -0
  192. package/dist/util/index.js.map +1 -0
  193. package/dist/worker/index.d.ts +6 -0
  194. package/dist/worker/index.js +1026 -0
  195. package/dist/worker/index.js.map +1 -0
  196. package/dist/worker/routes/content-prewarm.d.ts +34 -0
  197. package/dist/worker/routes/content-prewarm.js +38 -0
  198. package/dist/worker/routes/content-prewarm.js.map +1 -0
  199. package/dist/worker/routes/content-revalidate.d.ts +81 -0
  200. package/dist/worker/routes/content-revalidate.js +64 -0
  201. package/dist/worker/routes/content-revalidate.js.map +1 -0
  202. package/dist/worker/routes/health.d.ts +14 -0
  203. package/dist/worker/routes/health.js +278 -0
  204. package/dist/worker/routes/health.js.map +1 -0
  205. package/dist/worker/routes/index.d.ts +6 -0
  206. package/dist/worker/routes/index.js +373 -0
  207. package/dist/worker/routes/index.js.map +1 -0
  208. package/package.json +124 -0
@@ -0,0 +1,408 @@
1
+ // src/notion/generic-source.ts
2
+ import { cache } from "react";
3
+
4
+ // src/notion/blocks.ts
5
+ function normalizeBlock(input) {
6
+ if (!input || typeof input !== "object") return null;
7
+ const block = input;
8
+ return block.id && block.type ? block : null;
9
+ }
10
+ async function listBlockChildren(client, blockId) {
11
+ const results = [];
12
+ let cursor;
13
+ do {
14
+ const response = await client.blocks.children.list({
15
+ block_id: blockId,
16
+ page_size: 100,
17
+ ...cursor ? { start_cursor: cursor } : {}
18
+ });
19
+ for (const item of response.results ?? []) {
20
+ const block = normalizeBlock(item);
21
+ if (block) results.push(block);
22
+ }
23
+ cursor = response.next_cursor ?? void 0;
24
+ if (!response.has_more) break;
25
+ } while (cursor);
26
+ return results;
27
+ }
28
+ async function listBlockChildrenDeep(client, blockId, options) {
29
+ const maxDepth = options?.maxDepth ?? 6;
30
+ async function visit(id, depth) {
31
+ const children = await listBlockChildren(client, id);
32
+ if (depth >= maxDepth) return children;
33
+ return Promise.all(
34
+ children.map(async (block) => {
35
+ if (!block.has_children) return block;
36
+ return {
37
+ ...block,
38
+ children: await visit(block.id, depth + 1)
39
+ };
40
+ })
41
+ );
42
+ }
43
+ return visit(blockId, 0);
44
+ }
45
+
46
+ // src/notion/client.ts
47
+ import { Client } from "@notionhq/client";
48
+ function createNotionClient(config) {
49
+ return new Client({
50
+ auth: config.token,
51
+ baseUrl: config.apiBaseUrl,
52
+ notionVersion: "2026-03-11"
53
+ });
54
+ }
55
+
56
+ // src/notion/config.ts
57
+ function readProcessEnv() {
58
+ const env = {
59
+ NOTION_TOKEN: process.env.NOTION_TOKEN,
60
+ NOTION_DATA_SOURCE_ID: process.env.NOTION_DATA_SOURCE_ID,
61
+ NOTION_MOVIES_DATA_SOURCE_ID: process.env.NOTION_MOVIES_DATA_SOURCE_ID,
62
+ NOTION_API_BASE_URL: process.env.NOTION_API_BASE_URL,
63
+ NOTION_EDIT_BASE_URL: process.env.NOTION_EDIT_BASE_URL,
64
+ NOTION_WEBHOOK_VERIFICATION_TOKEN: process.env.NOTION_WEBHOOK_VERIFICATION_TOKEN
65
+ };
66
+ for (const [key, value] of Object.entries(process.env)) {
67
+ if (key.startsWith("NOTION_") && typeof value === "string") {
68
+ env[key] = value;
69
+ }
70
+ }
71
+ return env;
72
+ }
73
+ async function readWorkerEnv() {
74
+ try {
75
+ const mod = await import(
76
+ /* webpackIgnore: true */
77
+ "cloudflare:workers"
78
+ );
79
+ const env = {};
80
+ for (const [key, value] of Object.entries(mod.env ?? {})) {
81
+ if (key.startsWith("NOTION_") && typeof value === "string") {
82
+ env[key] = value;
83
+ }
84
+ }
85
+ return env;
86
+ } catch {
87
+ return {};
88
+ }
89
+ }
90
+ function readString(source, name) {
91
+ const value = String(source[name] ?? "").trim();
92
+ return value || void 0;
93
+ }
94
+ function mergeEnv(...sources) {
95
+ const merged = {};
96
+ for (const source of sources) {
97
+ for (const name of Object.keys(source)) {
98
+ if (!name.startsWith("NOTION_")) continue;
99
+ const value = readString(source, name);
100
+ if (value) merged[name] = value;
101
+ }
102
+ }
103
+ return merged;
104
+ }
105
+ async function readEnv() {
106
+ const processEnv = readProcessEnv();
107
+ return mergeEnv(await readWorkerEnv(), processEnv);
108
+ }
109
+ function readRequired(source, name) {
110
+ const value = readString(source, name);
111
+ if (!value) {
112
+ throw new Error(`Missing required Notion env: ${name}`);
113
+ }
114
+ return value;
115
+ }
116
+ async function hasNotionModelConfig(model) {
117
+ const env = await readEnv();
118
+ return Boolean(
119
+ readString(env, "NOTION_TOKEN") && (readString(env, model.source.dataSourceEnv) || model.source.defaultDataSourceId)
120
+ );
121
+ }
122
+ async function getNotionConfigForModel(model) {
123
+ const env = await readEnv();
124
+ const dataSourceId = readString(env, model.source.dataSourceEnv) ?? model.source.defaultDataSourceId;
125
+ if (!dataSourceId) {
126
+ throw new Error(`Missing required Notion env: ${model.source.dataSourceEnv}`);
127
+ }
128
+ return {
129
+ token: readRequired(env, model.source.tokenEnv),
130
+ dataSourceId,
131
+ apiBaseUrl: readString(env, "NOTION_API_BASE_URL"),
132
+ editBaseUrl: readString(env, "NOTION_EDIT_BASE_URL"),
133
+ webhookVerificationToken: readString(
134
+ env,
135
+ "NOTION_WEBHOOK_VERIFICATION_TOKEN"
136
+ )
137
+ };
138
+ }
139
+
140
+ // src/notion/media.ts
141
+ function stripLeadingSlash(value) {
142
+ return value.startsWith("/") ? value.slice(1) : value;
143
+ }
144
+ function encodePathPart(value) {
145
+ return encodeURIComponent(stripLeadingSlash(value));
146
+ }
147
+ function appendVersion(path, version) {
148
+ const value = String(version ?? "").trim();
149
+ if (!value) return path;
150
+ return `${path}?${new URLSearchParams({ v: value })}`;
151
+ }
152
+ function notionPageCoverMediaPath(pageId) {
153
+ return `/api/notion/media/page/${encodePathPart(pageId)}/cover`;
154
+ }
155
+ function notionPagePropertyMediaPath(pageId, propertyName) {
156
+ return `/api/notion/media/page/${encodePathPart(pageId)}/property/${encodePathPart(propertyName)}`;
157
+ }
158
+ function normalizeNotionFileSource(input) {
159
+ const file = input;
160
+ if (!file || typeof file !== "object") return null;
161
+ if (file.type === "external") {
162
+ const url = String(file.external?.url ?? "").trim();
163
+ return url ? { type: "external", url } : null;
164
+ }
165
+ if (file.type === "file") {
166
+ const url = String(file.file?.url ?? "").trim();
167
+ if (!url) return null;
168
+ return {
169
+ type: "file",
170
+ url,
171
+ expiryTime: String(file.file?.expiry_time ?? "").trim() || null
172
+ };
173
+ }
174
+ return null;
175
+ }
176
+ function pickFirstFilesPropertyValue(property) {
177
+ const value = property;
178
+ if (!value || value.type !== "files" || !Array.isArray(value.files)) {
179
+ return null;
180
+ }
181
+ return value.files[0] ?? null;
182
+ }
183
+ function pickPageCoverFile(page) {
184
+ return page.cover ?? null;
185
+ }
186
+ function coverImageUrlForPage(page, coverPropertyName = "Cover") {
187
+ const propertyFile = pickFirstFilesPropertyValue(
188
+ page.properties?.[coverPropertyName]
189
+ );
190
+ const propertySource = normalizeNotionFileSource(propertyFile);
191
+ if (propertySource) {
192
+ return appendVersion(
193
+ notionPagePropertyMediaPath(page.id, coverPropertyName),
194
+ page.last_edited_time
195
+ );
196
+ }
197
+ const coverSource = normalizeNotionFileSource(pickPageCoverFile(page));
198
+ if (coverSource) {
199
+ return appendVersion(notionPageCoverMediaPath(page.id), page.last_edited_time);
200
+ }
201
+ return null;
202
+ }
203
+
204
+ // src/notion/property-mappers.ts
205
+ function getPlainText(parts) {
206
+ if (!Array.isArray(parts)) return "";
207
+ return parts.map((part) => part.plain_text ?? "").join("").trim();
208
+ }
209
+ function getProperty(properties, key) {
210
+ return properties[key];
211
+ }
212
+ function getRichTextProperty(properties, key) {
213
+ const property = getProperty(properties, key);
214
+ if (!property) return "";
215
+ if (property.type === "title") return getPlainText(property.title);
216
+ if (property.type === "rich_text") return getPlainText(property.rich_text);
217
+ if (property.type === "url") return String(property.url ?? "").trim();
218
+ if (property.type === "email") return String(property.email ?? "").trim();
219
+ if (property.type === "phone_number") {
220
+ return String(property.phone_number ?? "").trim();
221
+ }
222
+ return "";
223
+ }
224
+ function getDateProperty(properties, key) {
225
+ const property = getProperty(properties, key);
226
+ if (property?.type !== "date") return "";
227
+ const date = property.date;
228
+ return String(date?.start ?? "").trim();
229
+ }
230
+ function getTagsProperty(properties, key) {
231
+ const property = getProperty(properties, key);
232
+ if (property?.type === "multi_select" && Array.isArray(property.multi_select)) {
233
+ return property.multi_select.map((item) => String(item.name ?? "").trim()).filter(Boolean);
234
+ }
235
+ if (property?.type === "select") {
236
+ const select = property.select;
237
+ const name = String(select?.name ?? "").trim();
238
+ return name ? [name] : [];
239
+ }
240
+ return [];
241
+ }
242
+ function pickDescriptionFallback(description, title) {
243
+ return description.trim() || title.trim();
244
+ }
245
+ function isValidPublicSlug(slug) {
246
+ return /^[a-z0-9][a-z0-9-]{0,79}$/.test(slug);
247
+ }
248
+ function notionPageEditUrl(pageId, editBaseUrl) {
249
+ const compactPageId = pageId.replaceAll("-", "");
250
+ if (editBaseUrl?.includes("{pageId}")) {
251
+ return editBaseUrl.replaceAll("{pageId}", compactPageId);
252
+ }
253
+ return `https://www.notion.so/${compactPageId}`;
254
+ }
255
+
256
+ // src/notion/generic-source.ts
257
+ function normalizePage(input) {
258
+ if (!input || typeof input !== "object") return null;
259
+ const page = input;
260
+ return page.id ? page : null;
261
+ }
262
+ function firstFieldName(value) {
263
+ if (Array.isArray(value)) return value[0];
264
+ return value;
265
+ }
266
+ function getFieldName(fields, key) {
267
+ return firstFieldName(fields[key]);
268
+ }
269
+ function pickPublishedFlagForModel(properties, fields) {
270
+ const publishedField = getFieldName(fields, "published") ?? "Published";
271
+ const published = properties[publishedField];
272
+ if (published?.type === "checkbox") return Boolean(published.checkbox);
273
+ const statusField = getFieldName(fields, "status") ?? "Status";
274
+ const status = properties[statusField];
275
+ if (status?.type === "status") {
276
+ const value = status.status;
277
+ return String(value?.name ?? "").trim().toLowerCase() === "published";
278
+ }
279
+ if (status?.type === "select") {
280
+ const value = status.select;
281
+ return String(value?.name ?? "").trim().toLowerCase() === "published";
282
+ }
283
+ return true;
284
+ }
285
+ function coverImageUrlForModel(page, fields) {
286
+ const coverField = fields.cover;
287
+ if (Array.isArray(coverField)) {
288
+ for (const field of coverField) {
289
+ const imageUrl = coverImageUrlForPage(page, field);
290
+ if (imageUrl) return imageUrl;
291
+ }
292
+ }
293
+ if (typeof coverField === "string") {
294
+ return coverImageUrlForPage(page, coverField);
295
+ }
296
+ return coverImageUrlForPage(page);
297
+ }
298
+ function mapExtraProperties(properties, fields) {
299
+ const result = {};
300
+ for (const [key, value] of Object.entries(fields)) {
301
+ const field = firstFieldName(value);
302
+ if (!field) continue;
303
+ const tags = getTagsProperty(properties, field);
304
+ result[key] = tags.length > 0 ? tags : getRichTextProperty(properties, field);
305
+ }
306
+ return result;
307
+ }
308
+ function mapNotionPageToGenericContentItem(model, page, options) {
309
+ const fields = model.source.fields;
310
+ const properties = isRecord(page.properties) ? page.properties : {};
311
+ const titleField = getFieldName(fields, "title");
312
+ const slugField = getFieldName(fields, "slug");
313
+ const descriptionField = getFieldName(fields, "description");
314
+ const dateField = getFieldName(fields, "date");
315
+ const tagsField = getFieldName(fields, "tags");
316
+ const title = titleField ? getRichTextProperty(properties, titleField) : "";
317
+ const slug = slugField ? getRichTextProperty(properties, slugField).toLowerCase() : page.id.replaceAll("-", "").toLowerCase();
318
+ const description = pickDescriptionFallback(
319
+ descriptionField ? getRichTextProperty(properties, descriptionField) : "",
320
+ title
321
+ );
322
+ return {
323
+ pageId: page.id,
324
+ slug,
325
+ title,
326
+ description,
327
+ date: dateField ? getDateProperty(properties, dateField) : "",
328
+ tags: tagsField ? getTagsProperty(properties, tagsField) : [],
329
+ coverImage: coverImageUrlForModel(page, fields),
330
+ published: pickPublishedFlagForModel(properties, fields),
331
+ editUrl: notionPageEditUrl(page.id, options?.editBaseUrl),
332
+ properties: mapExtraProperties(properties, fields)
333
+ };
334
+ }
335
+ function isRenderableGenericContentItem(item) {
336
+ return Boolean(item.published && item.title && item.slug && isValidPublicSlug(item.slug));
337
+ }
338
+ function createGenericNotionContentSource(deps) {
339
+ return {
340
+ async listItems() {
341
+ const pages = [];
342
+ let cursor;
343
+ do {
344
+ const response = await deps.queryDataSource({ startCursor: cursor });
345
+ for (const item of response.results ?? []) {
346
+ const page = normalizePage(item);
347
+ if (page) pages.push(page);
348
+ }
349
+ cursor = response.next_cursor ?? void 0;
350
+ if (!response.has_more) break;
351
+ } while (cursor);
352
+ return pages.map(
353
+ (page) => mapNotionPageToGenericContentItem(deps.model, page, {
354
+ editBaseUrl: deps.editBaseUrl
355
+ })
356
+ ).filter(isRenderableGenericContentItem).sort((a, b) => b.date.localeCompare(a.date));
357
+ },
358
+ async getItemBySlug(slug) {
359
+ const items = await this.listItems();
360
+ const item = items.find((candidate) => candidate.slug === slug);
361
+ if (!item) return null;
362
+ return {
363
+ ...item,
364
+ blocks: await deps.getPageBlocks(item.pageId)
365
+ };
366
+ }
367
+ };
368
+ }
369
+ async function createDefaultGenericSource(model) {
370
+ if (!await hasNotionModelConfig(model)) return null;
371
+ const config = await getNotionConfigForModel(model);
372
+ const client = createNotionClient(config);
373
+ return createGenericNotionContentSource({
374
+ model,
375
+ dataSourceId: config.dataSourceId,
376
+ editBaseUrl: config.editBaseUrl,
377
+ queryDataSource: async ({ startCursor } = {}) => client.dataSources.query({
378
+ data_source_id: config.dataSourceId,
379
+ page_size: model.source.query.pageSize,
380
+ sorts: model.source.query.sorts ? [...model.source.query.sorts] : void 0,
381
+ filter_properties: model.source.query.filterProperties ? [...model.source.query.filterProperties] : void 0,
382
+ ...startCursor ? { start_cursor: startCursor } : {}
383
+ }),
384
+ getPageBlocks: (pageId) => listBlockChildrenDeep(client, pageId)
385
+ });
386
+ }
387
+ var sourceCache = cache(createDefaultGenericSource);
388
+ async function listGenericNotionContent(model) {
389
+ const source = await sourceCache(model);
390
+ if (!source) return [];
391
+ return source.listItems();
392
+ }
393
+ async function getGenericNotionContentBySlug(model, slug) {
394
+ const source = await sourceCache(model);
395
+ if (!source) return null;
396
+ return source.getItemBySlug(slug);
397
+ }
398
+ function isRecord(value) {
399
+ return Boolean(value && typeof value === "object");
400
+ }
401
+ export {
402
+ createGenericNotionContentSource,
403
+ getGenericNotionContentBySlug,
404
+ isRenderableGenericContentItem,
405
+ listGenericNotionContent,
406
+ mapNotionPageToGenericContentItem
407
+ };
408
+ //# sourceMappingURL=generic-source.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/notion/generic-source.ts","../../src/notion/blocks.ts","../../src/notion/client.ts","../../src/notion/config.ts","../../src/notion/media.ts","../../src/notion/property-mappers.ts"],"sourcesContent":["import { cache } from \"react\";\nimport { listBlockChildrenDeep, type NotionBlockClient } from \"./blocks\";\nimport { createNotionClient } from \"./client\";\nimport { getNotionConfigForModel, hasNotionModelConfig } from \"./config\";\nimport { coverImageUrlForPage } from \"./media\";\nimport {\n getDateProperty,\n getRichTextProperty,\n getTagsProperty,\n isValidPublicSlug,\n notionPageEditUrl,\n pickDescriptionFallback,\n} from \"./property-mappers\";\nimport type {\n NotionBlock,\n NotionFieldMap,\n NotionGenericContentModel,\n NotionPageLike,\n} from \"./types\";\n\ntype PropertyMap = Record<string, unknown>;\n\ntype DataSourceQueryResponse = {\n results?: unknown[];\n has_more?: boolean;\n next_cursor?: string | null;\n};\n\ntype QueryDataSourceInput = {\n startCursor?: string;\n};\n\nexport type GenericContentListItem = {\n pageId: string;\n slug: string;\n title: string;\n description: string;\n date: string;\n tags: string[];\n coverImage: string | null;\n published: boolean;\n editUrl: string | null;\n properties: Record<string, string | string[]>;\n};\n\nexport type GenericContentDetail = GenericContentListItem & {\n blocks: NotionBlock[];\n};\n\nexport type GenericContentSourceDeps<\n TFields extends NotionFieldMap = NotionFieldMap,\n> = {\n model: NotionGenericContentModel & { source: { fields: TFields } };\n dataSourceId: string;\n queryDataSource: (\n input?: QueryDataSourceInput\n ) => Promise<DataSourceQueryResponse>;\n getPageBlocks: (pageId: string) => Promise<NotionBlock[]>;\n editBaseUrl?: string;\n};\n\nfunction normalizePage(input: unknown): NotionPageLike | null {\n if (!input || typeof input !== \"object\") return null;\n const page = input as NotionPageLike;\n return page.id ? page : null;\n}\n\nfunction firstFieldName(value: string | readonly string[] | undefined) {\n if (Array.isArray(value)) return value[0];\n return value;\n}\n\nfunction getFieldName(fields: NotionFieldMap, key: string) {\n return firstFieldName(fields[key]);\n}\n\nfunction pickPublishedFlagForModel(\n properties: PropertyMap,\n fields: NotionFieldMap\n) {\n const publishedField = getFieldName(fields, \"published\") ?? \"Published\";\n const published = properties[publishedField] as\n | Record<string, unknown>\n | undefined;\n if (published?.type === \"checkbox\") return Boolean(published.checkbox);\n\n const statusField = getFieldName(fields, \"status\") ?? \"Status\";\n const status = properties[statusField] as Record<string, unknown> | undefined;\n if (status?.type === \"status\") {\n const value = status.status as { name?: string } | null | undefined;\n return String(value?.name ?? \"\").trim().toLowerCase() === \"published\";\n }\n if (status?.type === \"select\") {\n const value = status.select as { name?: string } | null | undefined;\n return String(value?.name ?? \"\").trim().toLowerCase() === \"published\";\n }\n\n return true;\n}\n\nfunction coverImageUrlForModel(page: NotionPageLike, fields: NotionFieldMap) {\n const coverField = fields.cover;\n if (Array.isArray(coverField)) {\n for (const field of coverField) {\n const imageUrl = coverImageUrlForPage(page, field);\n if (imageUrl) return imageUrl;\n }\n }\n if (typeof coverField === \"string\") {\n return coverImageUrlForPage(page, coverField);\n }\n return coverImageUrlForPage(page);\n}\n\nfunction mapExtraProperties(properties: PropertyMap, fields: NotionFieldMap) {\n const result: Record<string, string | string[]> = {};\n for (const [key, value] of Object.entries(fields)) {\n const field = firstFieldName(value);\n if (!field) continue;\n const tags = getTagsProperty(properties, field);\n result[key] = tags.length > 0 ? tags : getRichTextProperty(properties, field);\n }\n return result;\n}\n\nexport function mapNotionPageToGenericContentItem<\n TFields extends NotionFieldMap,\n>(\n model: { id: string; source: { fields: TFields } },\n page: NotionPageLike,\n options?: { editBaseUrl?: string }\n): GenericContentListItem {\n const fields = model.source.fields;\n const properties = isRecord(page.properties) ? page.properties : {};\n const titleField = getFieldName(fields, \"title\");\n const slugField = getFieldName(fields, \"slug\");\n const descriptionField = getFieldName(fields, \"description\");\n const dateField = getFieldName(fields, \"date\");\n const tagsField = getFieldName(fields, \"tags\");\n\n const title = titleField ? getRichTextProperty(properties, titleField) : \"\";\n const slug = slugField\n ? getRichTextProperty(properties, slugField).toLowerCase()\n : page.id.replaceAll(\"-\", \"\").toLowerCase();\n const description = pickDescriptionFallback(\n descriptionField ? getRichTextProperty(properties, descriptionField) : \"\",\n title\n );\n\n return {\n pageId: page.id,\n slug,\n title,\n description,\n date: dateField ? getDateProperty(properties, dateField) : \"\",\n tags: tagsField ? getTagsProperty(properties, tagsField) : [],\n coverImage: coverImageUrlForModel(page, fields),\n published: pickPublishedFlagForModel(properties, fields),\n editUrl: notionPageEditUrl(page.id, options?.editBaseUrl),\n properties: mapExtraProperties(properties, fields),\n };\n}\n\nexport function isRenderableGenericContentItem(item: GenericContentListItem) {\n return Boolean(item.published && item.title && item.slug && isValidPublicSlug(item.slug));\n}\n\nexport function createGenericNotionContentSource<\n TFields extends NotionFieldMap,\n>(deps: GenericContentSourceDeps<TFields>) {\n return {\n async listItems(): Promise<GenericContentListItem[]> {\n const pages: NotionPageLike[] = [];\n let cursor: string | undefined;\n\n do {\n const response = await deps.queryDataSource({ startCursor: cursor });\n for (const item of response.results ?? []) {\n const page = normalizePage(item);\n if (page) pages.push(page);\n }\n\n cursor = response.next_cursor ?? undefined;\n if (!response.has_more) break;\n } while (cursor);\n\n return pages\n .map((page) =>\n mapNotionPageToGenericContentItem(deps.model, page, {\n editBaseUrl: deps.editBaseUrl,\n })\n )\n .filter(isRenderableGenericContentItem)\n .sort((a, b) => b.date.localeCompare(a.date));\n },\n\n async getItemBySlug(slug: string): Promise<GenericContentDetail | null> {\n const items = await this.listItems();\n const item = items.find((candidate) => candidate.slug === slug);\n if (!item) return null;\n return {\n ...item,\n blocks: await deps.getPageBlocks(item.pageId),\n };\n },\n };\n}\n\nasync function createDefaultGenericSource<\n TFields extends NotionFieldMap,\n>(model: NotionGenericContentModel & { source: { fields: TFields } }) {\n if (!(await hasNotionModelConfig(model))) return null;\n\n const config = await getNotionConfigForModel(model);\n const client = createNotionClient(config);\n return createGenericNotionContentSource({\n model,\n dataSourceId: config.dataSourceId,\n editBaseUrl: config.editBaseUrl,\n queryDataSource: async ({ startCursor } = {}) =>\n client.dataSources.query({\n data_source_id: config.dataSourceId,\n page_size: model.source.query.pageSize,\n sorts: model.source.query.sorts\n ? [...model.source.query.sorts]\n : undefined,\n filter_properties: model.source.query.filterProperties\n ? [...model.source.query.filterProperties]\n : undefined,\n ...(startCursor ? { start_cursor: startCursor } : {}),\n }),\n getPageBlocks: (pageId) =>\n listBlockChildrenDeep(client as unknown as NotionBlockClient, pageId),\n });\n}\n\nconst sourceCache = cache(createDefaultGenericSource);\n\nexport async function listGenericNotionContent<\n TFields extends NotionFieldMap,\n>(model: NotionGenericContentModel & { source: { fields: TFields } }) {\n const source = await sourceCache(model);\n if (!source) return [];\n return source.listItems();\n}\n\nexport async function getGenericNotionContentBySlug<\n TFields extends NotionFieldMap,\n>(model: NotionGenericContentModel & { source: { fields: TFields } }, slug: string) {\n const source = await sourceCache(model);\n if (!source) return null;\n return source.getItemBySlug(slug);\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return Boolean(value && typeof value === \"object\");\n}\n","import type { NotionBlock } from \"./types\";\n\ntype BlockChildrenListResponse = {\n results?: unknown[];\n has_more?: boolean;\n next_cursor?: string | null;\n};\n\nexport type NotionBlockClient = {\n blocks: {\n children: {\n list: (args: {\n block_id: string;\n page_size?: number;\n start_cursor?: string;\n }) => Promise<BlockChildrenListResponse>;\n };\n };\n};\n\nfunction normalizeBlock(input: unknown): NotionBlock | null {\n if (!input || typeof input !== \"object\") return null;\n const block = input as NotionBlock;\n return block.id && block.type ? block : null;\n}\n\nexport async function listBlockChildren(\n client: NotionBlockClient,\n blockId: string\n): Promise<NotionBlock[]> {\n const results: NotionBlock[] = [];\n let cursor: string | undefined;\n\n do {\n const response = await client.blocks.children.list({\n block_id: blockId,\n page_size: 100,\n ...(cursor ? { start_cursor: cursor } : {}),\n });\n\n for (const item of response.results ?? []) {\n const block = normalizeBlock(item);\n if (block) results.push(block);\n }\n\n cursor = response.next_cursor ?? undefined;\n if (!response.has_more) break;\n } while (cursor);\n\n return results;\n}\n\nexport async function listBlockChildrenDeep(\n client: NotionBlockClient,\n blockId: string,\n options?: { maxDepth?: number }\n): Promise<NotionBlock[]> {\n const maxDepth = options?.maxDepth ?? 6;\n\n async function visit(id: string, depth: number): Promise<NotionBlock[]> {\n const children = await listBlockChildren(client, id);\n if (depth >= maxDepth) return children;\n\n return Promise.all(\n children.map(async (block) => {\n if (!block.has_children) return block;\n return {\n ...block,\n children: await visit(block.id, depth + 1),\n };\n })\n );\n }\n\n return visit(blockId, 0);\n}\n","import { Client } from \"@notionhq/client\";\nimport type { NotionClientConfig } from \"./config\";\n\nexport function createNotionClient(config: NotionClientConfig) {\n return new Client({\n auth: config.token,\n baseUrl: config.apiBaseUrl,\n notionVersion: \"2026-03-11\",\n });\n}\n","import type { NotionContentModelLike } from \"./types\";\n\ntype NotionEnv = {\n NOTION_TOKEN?: string;\n NOTION_DATA_SOURCE_ID?: string;\n NOTION_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 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":[]}
@@ -0,0 +1,13 @@
1
+ export { createNotionClient } from './client.js';
2
+ export { DEFAULT_NOTION_MOVIES_DATA_SOURCE_ID, NotionClientConfig, NotionConfig, getNotionClientConfig, getNotionConfig, getNotionConfigForModel, getNotionEditBaseUrl, getNotionMovieConfig, getNotionWebhookVerificationToken, hasNotionConfig, hasNotionModelConfig, hasNotionMovieConfig } from './config.js';
3
+ export { NotionBlockClient, listBlockChildren, listBlockChildrenDeep } from './blocks.js';
4
+ export { flattenNotionBlockText } from './block-text.js';
5
+ export { NOTION_BLOCKS_CACHE_TTL_SECONDS, NOTION_LIST_CACHE_TTL_SECONDS, NotionContentCacheDeleteResult, deleteNotionContentCache, getCachedNotionBlocks, getCachedNotionValue, notionModelCachePrefix, notionModelListCacheKey, notionPageBlocksCacheKey, notionPageCachePrefix, putCachedNotionBlocks, putCachedNotionValue } from './content-cache.js';
6
+ export { coverImageUrlForPage, fileObjectForMediaBlock, firstImageUrlFromBlocks, gatedMediaBlockForApi, isDirectVideoUrl, isNotionHostedFile, mediaUrlForBlock, normalizeNotionFileSource, notionBlockMediaPath, notionPageCoverMediaPath, notionPagePropertyMediaPath, pickFirstFilesPropertyValue, pickPageCoverFile, publicMediaBlockForApi, resolveNotionFileUrl, videoEmbedUrl } from './media.js';
7
+ export { GenericContentDetail, GenericContentListItem, GenericContentSourceDeps, createGenericNotionContentSource, getGenericNotionContentBySlug, isRenderableGenericContentItem, listGenericNotionContent, mapNotionPageToGenericContentItem } from './generic-source.js';
8
+ export { compactNotionId, getAuthorProperty, getCheckboxProperty, getDateProperty, getFirstDateProperty, getFirstPeopleProperty, getFirstTagsProperty, getFirstTitleProperty, getRelationPageIds, getRichTextProperty, getSelectProperty, getTagsProperty, isRecord, isValidPublicSlug, notionPageEditUrl, pickDescriptionFallback, pickPublishedFlag } from './property-mappers.js';
9
+ export { LocalizedPublicMovieDetail, LocalizedPublicMovieListItem, NotionBlock, NotionContentModelLike, NotionFieldMap, NotionFileSource, NotionGenericContentModel, NotionMovieDetail, NotionMovieDownloadInfo, NotionMovieListItem, NotionMovieTranslation, NotionPageLike, NotionPostDetail, NotionPostListItem, NotionRichTextPart, NotionSort, NotionSortDirection, PublicNotionMovieDetail, PublicNotionMovieListItem } from './types.js';
10
+ export { InvalidationKind, NotionPageRetriever, NotionWebhookEvent, NotionWebhookModelRegistration, NotionWebhookParseOptions, NotionWebhookParseResult, NotionWebhookRevalidateRequest, getStoredNotionWebhookVerificationToken, notionWebhookEventToRevalidateRequest, parseNotionWebhookPayload, parseNotionWebhookPayloadWithPageLookup, putStoredNotionWebhookVerificationToken, signNotionWebhookBody, verifyNotionWebhookSignature, verifyNotionWebhookSignatureWithTokens } from './webhook.js';
11
+ import '@notionhq/client';
12
+ import '../platform/runtime.js';
13
+ import '../env-C5qu-0R-.js';