@notionx/core 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/content/index.d.ts +1 -0
- package/dist/content/index.js +168 -0
- package/dist/content/index.js.map +1 -1
- package/dist/content/localized.d.ts +67 -0
- package/dist/content/localized.js +170 -0
- package/dist/content/localized.js.map +1 -0
- package/dist/notion/generic-source.js.map +1 -1
- package/dist/notion/index.d.ts +1 -1
- package/dist/notion/index.js +7 -0
- package/dist/notion/index.js.map +1 -1
- package/dist/notion/mappers.js.map +1 -1
- package/dist/notion/property-mappers.d.ts +2 -1
- package/dist/notion/property-mappers.js +7 -0
- package/dist/notion/property-mappers.js.map +1 -1
- package/dist/notion/webhook.js.map +1 -1
- package/dist/pages/index.d.ts +117 -0
- package/dist/pages/index.js +488 -0
- package/dist/pages/index.js.map +1 -0
- package/package.json +14 -1
|
@@ -0,0 +1,488 @@
|
|
|
1
|
+
// src/pages/model.ts
|
|
2
|
+
var defaultSitePageFields = {
|
|
3
|
+
title: "Name",
|
|
4
|
+
key: "Key",
|
|
5
|
+
slug: "Slug",
|
|
6
|
+
status: "Status",
|
|
7
|
+
layout: "Layout",
|
|
8
|
+
description: "Description",
|
|
9
|
+
seoTitle: "SEO Title",
|
|
10
|
+
seoDescription: "SEO Description",
|
|
11
|
+
showHeader: "Show Header",
|
|
12
|
+
showFooter: "Show Footer",
|
|
13
|
+
showInNav: "Show in Nav",
|
|
14
|
+
navLabel: "Nav Label",
|
|
15
|
+
navOrder: "Nav Order",
|
|
16
|
+
showInFooter: "Show in Footer",
|
|
17
|
+
footerLabel: "Footer Label",
|
|
18
|
+
footerGroup: "Footer Group",
|
|
19
|
+
footerOrder: "Footer Order",
|
|
20
|
+
contentSource: "Content Source",
|
|
21
|
+
cover: "Cover"
|
|
22
|
+
};
|
|
23
|
+
var defaultPagesDataSourceEnv = "NOTION_PAGES_DATA_SOURCE_ID";
|
|
24
|
+
function defineSitePageModel(model) {
|
|
25
|
+
return {
|
|
26
|
+
...model,
|
|
27
|
+
source: {
|
|
28
|
+
...model.source,
|
|
29
|
+
fields: {
|
|
30
|
+
...defaultSitePageFields,
|
|
31
|
+
...model.source.fields ?? {}
|
|
32
|
+
},
|
|
33
|
+
query: {
|
|
34
|
+
pageSize: 100,
|
|
35
|
+
...model.source.query ?? {}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// src/pages/source.ts
|
|
42
|
+
import { cache } from "react";
|
|
43
|
+
|
|
44
|
+
// src/notion/blocks.ts
|
|
45
|
+
function normalizeBlock(input) {
|
|
46
|
+
if (!input || typeof input !== "object") return null;
|
|
47
|
+
const block = input;
|
|
48
|
+
return block.id && block.type ? block : null;
|
|
49
|
+
}
|
|
50
|
+
async function listBlockChildren(client, blockId) {
|
|
51
|
+
const results = [];
|
|
52
|
+
let cursor;
|
|
53
|
+
do {
|
|
54
|
+
const response = await client.blocks.children.list({
|
|
55
|
+
block_id: blockId,
|
|
56
|
+
page_size: 100,
|
|
57
|
+
...cursor ? { start_cursor: cursor } : {}
|
|
58
|
+
});
|
|
59
|
+
for (const item of response.results ?? []) {
|
|
60
|
+
const block = normalizeBlock(item);
|
|
61
|
+
if (block) results.push(block);
|
|
62
|
+
}
|
|
63
|
+
cursor = response.next_cursor ?? void 0;
|
|
64
|
+
if (!response.has_more) break;
|
|
65
|
+
} while (cursor);
|
|
66
|
+
return results;
|
|
67
|
+
}
|
|
68
|
+
async function listBlockChildrenDeep(client, blockId, options) {
|
|
69
|
+
const maxDepth = options?.maxDepth ?? 6;
|
|
70
|
+
async function visit(id, depth) {
|
|
71
|
+
const children = await listBlockChildren(client, id);
|
|
72
|
+
if (depth >= maxDepth) return children;
|
|
73
|
+
return Promise.all(
|
|
74
|
+
children.map(async (block) => {
|
|
75
|
+
if (!block.has_children) return block;
|
|
76
|
+
return {
|
|
77
|
+
...block,
|
|
78
|
+
children: await visit(block.id, depth + 1)
|
|
79
|
+
};
|
|
80
|
+
})
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
return visit(blockId, 0);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// src/notion/client.ts
|
|
87
|
+
import { Client } from "@notionhq/client";
|
|
88
|
+
function createNotionClient(config) {
|
|
89
|
+
return new Client({
|
|
90
|
+
auth: config.token,
|
|
91
|
+
baseUrl: config.apiBaseUrl,
|
|
92
|
+
notionVersion: "2026-03-11"
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// src/notion/config.ts
|
|
97
|
+
function readProcessEnv() {
|
|
98
|
+
const env = {
|
|
99
|
+
NOTION_TOKEN: process.env.NOTION_TOKEN,
|
|
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
|
+
NOTION_API_BASE_URL: process.env.NOTION_API_BASE_URL,
|
|
103
|
+
NOTION_EDIT_BASE_URL: process.env.NOTION_EDIT_BASE_URL,
|
|
104
|
+
NOTION_WEBHOOK_VERIFICATION_TOKEN: process.env.NOTION_WEBHOOK_VERIFICATION_TOKEN
|
|
105
|
+
};
|
|
106
|
+
for (const [key, value] of Object.entries(process.env)) {
|
|
107
|
+
if (key.startsWith("NOTION_") && typeof value === "string") {
|
|
108
|
+
env[key] = value;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return env;
|
|
112
|
+
}
|
|
113
|
+
async function readWorkerEnv() {
|
|
114
|
+
try {
|
|
115
|
+
const mod = await import(
|
|
116
|
+
/* webpackIgnore: true */
|
|
117
|
+
"cloudflare:workers"
|
|
118
|
+
);
|
|
119
|
+
const env = {};
|
|
120
|
+
for (const [key, value] of Object.entries(mod.env ?? {})) {
|
|
121
|
+
if (key.startsWith("NOTION_") && typeof value === "string") {
|
|
122
|
+
env[key] = value;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return env;
|
|
126
|
+
} catch {
|
|
127
|
+
return {};
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
function readString(source, name) {
|
|
131
|
+
const value = String(source[name] ?? "").trim();
|
|
132
|
+
return value || void 0;
|
|
133
|
+
}
|
|
134
|
+
function mergeEnv(...sources) {
|
|
135
|
+
const merged = {};
|
|
136
|
+
for (const source of sources) {
|
|
137
|
+
for (const name of Object.keys(source)) {
|
|
138
|
+
if (!name.startsWith("NOTION_")) continue;
|
|
139
|
+
const value = readString(source, name);
|
|
140
|
+
if (value) merged[name] = value;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return merged;
|
|
144
|
+
}
|
|
145
|
+
async function readEnv() {
|
|
146
|
+
const processEnv = readProcessEnv();
|
|
147
|
+
return mergeEnv(await readWorkerEnv(), processEnv);
|
|
148
|
+
}
|
|
149
|
+
function readRequired(source, name) {
|
|
150
|
+
const value = readString(source, name);
|
|
151
|
+
if (!value) {
|
|
152
|
+
throw new Error(`Missing required Notion env: ${name}`);
|
|
153
|
+
}
|
|
154
|
+
return value;
|
|
155
|
+
}
|
|
156
|
+
async function hasNotionModelConfig(model) {
|
|
157
|
+
const env = await readEnv();
|
|
158
|
+
return Boolean(
|
|
159
|
+
readString(env, "NOTION_TOKEN") && (readString(env, model.source.dataSourceEnv) || model.source.defaultDataSourceId)
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
async function getNotionConfigForModel(model) {
|
|
163
|
+
const env = await readEnv();
|
|
164
|
+
const dataSourceId = readString(env, model.source.dataSourceEnv) ?? model.source.defaultDataSourceId;
|
|
165
|
+
if (!dataSourceId) {
|
|
166
|
+
throw new Error(`Missing required Notion env: ${model.source.dataSourceEnv}`);
|
|
167
|
+
}
|
|
168
|
+
return {
|
|
169
|
+
token: readRequired(env, model.source.tokenEnv),
|
|
170
|
+
dataSourceId,
|
|
171
|
+
apiBaseUrl: readString(env, "NOTION_API_BASE_URL"),
|
|
172
|
+
editBaseUrl: readString(env, "NOTION_EDIT_BASE_URL"),
|
|
173
|
+
webhookVerificationToken: readString(
|
|
174
|
+
env,
|
|
175
|
+
"NOTION_WEBHOOK_VERIFICATION_TOKEN"
|
|
176
|
+
)
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// src/notion/media.ts
|
|
181
|
+
function stripLeadingSlash(value) {
|
|
182
|
+
return value.startsWith("/") ? value.slice(1) : value;
|
|
183
|
+
}
|
|
184
|
+
function encodePathPart(value) {
|
|
185
|
+
return encodeURIComponent(stripLeadingSlash(value));
|
|
186
|
+
}
|
|
187
|
+
function appendVersion(path, version) {
|
|
188
|
+
const value = String(version ?? "").trim();
|
|
189
|
+
if (!value) return path;
|
|
190
|
+
return `${path}?${new URLSearchParams({ v: value })}`;
|
|
191
|
+
}
|
|
192
|
+
function notionPageCoverMediaPath(pageId) {
|
|
193
|
+
return `/api/notion/media/page/${encodePathPart(pageId)}/cover`;
|
|
194
|
+
}
|
|
195
|
+
function notionPagePropertyMediaPath(pageId, propertyName) {
|
|
196
|
+
return `/api/notion/media/page/${encodePathPart(pageId)}/property/${encodePathPart(propertyName)}`;
|
|
197
|
+
}
|
|
198
|
+
function normalizeNotionFileSource(input) {
|
|
199
|
+
const file = input;
|
|
200
|
+
if (!file || typeof file !== "object") return null;
|
|
201
|
+
if (file.type === "external") {
|
|
202
|
+
const url = String(file.external?.url ?? "").trim();
|
|
203
|
+
return url ? { type: "external", url } : null;
|
|
204
|
+
}
|
|
205
|
+
if (file.type === "file") {
|
|
206
|
+
const url = String(file.file?.url ?? "").trim();
|
|
207
|
+
if (!url) return null;
|
|
208
|
+
return {
|
|
209
|
+
type: "file",
|
|
210
|
+
url,
|
|
211
|
+
expiryTime: String(file.file?.expiry_time ?? "").trim() || null
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
return null;
|
|
215
|
+
}
|
|
216
|
+
function pickFirstFilesPropertyValue(property) {
|
|
217
|
+
const value = property;
|
|
218
|
+
if (!value || value.type !== "files" || !Array.isArray(value.files)) {
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
return value.files[0] ?? null;
|
|
222
|
+
}
|
|
223
|
+
function pickPageCoverFile(page) {
|
|
224
|
+
return page.cover ?? null;
|
|
225
|
+
}
|
|
226
|
+
function coverImageUrlForPage(page, coverPropertyName = "Cover") {
|
|
227
|
+
const propertyFile = pickFirstFilesPropertyValue(
|
|
228
|
+
page.properties?.[coverPropertyName]
|
|
229
|
+
);
|
|
230
|
+
const propertySource = normalizeNotionFileSource(propertyFile);
|
|
231
|
+
if (propertySource) {
|
|
232
|
+
return appendVersion(
|
|
233
|
+
notionPagePropertyMediaPath(page.id, coverPropertyName),
|
|
234
|
+
page.last_edited_time
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
const coverSource = normalizeNotionFileSource(pickPageCoverFile(page));
|
|
238
|
+
if (coverSource) {
|
|
239
|
+
return appendVersion(notionPageCoverMediaPath(page.id), page.last_edited_time);
|
|
240
|
+
}
|
|
241
|
+
return null;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// src/notion/property-mappers.ts
|
|
245
|
+
function isRecord(value) {
|
|
246
|
+
return Boolean(value && typeof value === "object");
|
|
247
|
+
}
|
|
248
|
+
function getPlainText(parts) {
|
|
249
|
+
if (!Array.isArray(parts)) return "";
|
|
250
|
+
return parts.map((part) => part.plain_text ?? "").join("").trim();
|
|
251
|
+
}
|
|
252
|
+
function getProperty(properties, key) {
|
|
253
|
+
return properties[key];
|
|
254
|
+
}
|
|
255
|
+
function getRichTextProperty(properties, key) {
|
|
256
|
+
const property = getProperty(properties, key);
|
|
257
|
+
if (!property) return "";
|
|
258
|
+
if (property.type === "title") return getPlainText(property.title);
|
|
259
|
+
if (property.type === "rich_text") return getPlainText(property.rich_text);
|
|
260
|
+
if (property.type === "url") return String(property.url ?? "").trim();
|
|
261
|
+
if (property.type === "email") return String(property.email ?? "").trim();
|
|
262
|
+
if (property.type === "phone_number") {
|
|
263
|
+
return String(property.phone_number ?? "").trim();
|
|
264
|
+
}
|
|
265
|
+
return "";
|
|
266
|
+
}
|
|
267
|
+
function getSelectProperty(properties, key) {
|
|
268
|
+
const property = getProperty(properties, key);
|
|
269
|
+
if (property?.type !== "select") return "";
|
|
270
|
+
const select = property.select;
|
|
271
|
+
return String(select?.name ?? "").trim();
|
|
272
|
+
}
|
|
273
|
+
function getCheckboxProperty(properties, key) {
|
|
274
|
+
const property = getProperty(properties, key);
|
|
275
|
+
if (property?.type !== "checkbox") return false;
|
|
276
|
+
return Boolean(property.checkbox);
|
|
277
|
+
}
|
|
278
|
+
function getNumberProperty(properties, key, fallback = 0) {
|
|
279
|
+
const property = getProperty(properties, key);
|
|
280
|
+
if (property?.type !== "number") return fallback;
|
|
281
|
+
const value = Number(property.number);
|
|
282
|
+
return Number.isFinite(value) ? value : fallback;
|
|
283
|
+
}
|
|
284
|
+
function notionPageEditUrl(pageId, editBaseUrl) {
|
|
285
|
+
const compactPageId = pageId.replaceAll("-", "");
|
|
286
|
+
if (editBaseUrl?.includes("{pageId}")) {
|
|
287
|
+
return editBaseUrl.replaceAll("{pageId}", compactPageId);
|
|
288
|
+
}
|
|
289
|
+
return `https://www.notion.so/${compactPageId}`;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// src/pages/source.ts
|
|
293
|
+
function normalizePage(input) {
|
|
294
|
+
if (!input || typeof input !== "object") return null;
|
|
295
|
+
const page = input;
|
|
296
|
+
return page.id ? page : null;
|
|
297
|
+
}
|
|
298
|
+
function slugToHref(slug) {
|
|
299
|
+
const normalized = slug.trim().replace(/^\/+|\/+$/g, "");
|
|
300
|
+
return normalized ? `/${normalized}` : "/";
|
|
301
|
+
}
|
|
302
|
+
function normalizePageSlug(slug) {
|
|
303
|
+
return slug.trim().replace(/^\/+|\/+$/g, "").toLowerCase();
|
|
304
|
+
}
|
|
305
|
+
function isPublishedStatus(value) {
|
|
306
|
+
return value.trim().toLowerCase() === "published";
|
|
307
|
+
}
|
|
308
|
+
function getCheckboxPropertyWithFallback(properties, key, fallback) {
|
|
309
|
+
const property = properties[key];
|
|
310
|
+
if (property?.type !== "checkbox") return fallback;
|
|
311
|
+
return getCheckboxProperty(properties, key);
|
|
312
|
+
}
|
|
313
|
+
function normalizeLayout(value) {
|
|
314
|
+
const normalized = value.trim().toLowerCase();
|
|
315
|
+
if (normalized === "home") return "home";
|
|
316
|
+
if (normalized === "legal") return "legal";
|
|
317
|
+
if (normalized === "content-list") return "content-list";
|
|
318
|
+
return "default";
|
|
319
|
+
}
|
|
320
|
+
function mapNotionPageToSitePage(page, fields, blocks = [], options) {
|
|
321
|
+
const properties = isRecord(page.properties) ? page.properties : {};
|
|
322
|
+
const title = getRichTextProperty(properties, fields.title);
|
|
323
|
+
const key = getRichTextProperty(properties, fields.key).toLowerCase();
|
|
324
|
+
const slug = normalizePageSlug(getRichTextProperty(properties, fields.slug));
|
|
325
|
+
const status = getSelectProperty(properties, fields.status);
|
|
326
|
+
if (!title || !key || !isPublishedStatus(status)) return null;
|
|
327
|
+
const description = getRichTextProperty(properties, fields.description);
|
|
328
|
+
const seoTitle = getRichTextProperty(properties, fields.seoTitle) || title;
|
|
329
|
+
const seoDescription = getRichTextProperty(properties, fields.seoDescription) || description;
|
|
330
|
+
const navLabel = getRichTextProperty(properties, fields.navLabel) || title;
|
|
331
|
+
const footerLabel = getRichTextProperty(properties, fields.footerLabel) || navLabel;
|
|
332
|
+
return {
|
|
333
|
+
pageId: page.id,
|
|
334
|
+
key,
|
|
335
|
+
slug,
|
|
336
|
+
href: slugToHref(slug),
|
|
337
|
+
title,
|
|
338
|
+
description,
|
|
339
|
+
seoTitle,
|
|
340
|
+
seoDescription,
|
|
341
|
+
layout: normalizeLayout(getSelectProperty(properties, fields.layout)),
|
|
342
|
+
published: true,
|
|
343
|
+
showHeader: getCheckboxPropertyWithFallback(
|
|
344
|
+
properties,
|
|
345
|
+
fields.showHeader,
|
|
346
|
+
true
|
|
347
|
+
),
|
|
348
|
+
showFooter: getCheckboxPropertyWithFallback(
|
|
349
|
+
properties,
|
|
350
|
+
fields.showFooter,
|
|
351
|
+
true
|
|
352
|
+
),
|
|
353
|
+
showInNav: getCheckboxProperty(properties, fields.showInNav),
|
|
354
|
+
navLabel,
|
|
355
|
+
navOrder: getNumberProperty(properties, fields.navOrder, 100),
|
|
356
|
+
showInFooter: getCheckboxProperty(properties, fields.showInFooter),
|
|
357
|
+
footerLabel,
|
|
358
|
+
footerGroup: getSelectProperty(properties, fields.footerGroup) || "Company",
|
|
359
|
+
footerOrder: getNumberProperty(properties, fields.footerOrder, 100),
|
|
360
|
+
contentSource: getRichTextProperty(properties, fields.contentSource),
|
|
361
|
+
coverImage: coverImageUrlForPage(page, fields.cover),
|
|
362
|
+
editUrl: notionPageEditUrl(page.id, options?.editBaseUrl),
|
|
363
|
+
blocks
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
function createSitePageSource(modelInput, deps) {
|
|
367
|
+
const model = defineSitePageModel(modelInput);
|
|
368
|
+
const fields = model.source.fields;
|
|
369
|
+
return {
|
|
370
|
+
async listPages() {
|
|
371
|
+
const pages = [];
|
|
372
|
+
let cursor;
|
|
373
|
+
do {
|
|
374
|
+
const response = await deps.queryDataSource({ startCursor: cursor });
|
|
375
|
+
for (const item of response.results ?? []) {
|
|
376
|
+
const page = normalizePage(item);
|
|
377
|
+
if (!page) continue;
|
|
378
|
+
const mapped = mapNotionPageToSitePage(
|
|
379
|
+
page,
|
|
380
|
+
fields,
|
|
381
|
+
await deps.getPageBlocks(page.id),
|
|
382
|
+
{ editBaseUrl: deps.editBaseUrl }
|
|
383
|
+
);
|
|
384
|
+
if (mapped) pages.push(mapped);
|
|
385
|
+
}
|
|
386
|
+
cursor = response.next_cursor ?? void 0;
|
|
387
|
+
if (!response.has_more) break;
|
|
388
|
+
} while (cursor);
|
|
389
|
+
return pages.sort((a, b) => a.navOrder - b.navOrder || a.title.localeCompare(b.title));
|
|
390
|
+
}
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
async function createDefaultSitePageSource(modelInput) {
|
|
394
|
+
const model = defineSitePageModel(modelInput);
|
|
395
|
+
if (!await hasNotionModelConfig(model)) return null;
|
|
396
|
+
const config = await getNotionConfigForModel(model);
|
|
397
|
+
const client = createNotionClient(config);
|
|
398
|
+
return createSitePageSource(model, {
|
|
399
|
+
editBaseUrl: config.editBaseUrl,
|
|
400
|
+
queryDataSource: async ({ startCursor } = {}) => client.dataSources.query({
|
|
401
|
+
data_source_id: config.dataSourceId,
|
|
402
|
+
page_size: model.source.query.pageSize,
|
|
403
|
+
sorts: [{ property: model.source.fields.navOrder, direction: "ascending" }],
|
|
404
|
+
...startCursor ? { start_cursor: startCursor } : {}
|
|
405
|
+
}),
|
|
406
|
+
getPageBlocks: (pageId) => listBlockChildrenDeep(client, pageId)
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
var defaultSourceCache = cache(createDefaultSitePageSource);
|
|
410
|
+
function createSitePagesApi(input) {
|
|
411
|
+
const fallbackPages = [...input.fallbackPages ?? []];
|
|
412
|
+
const listSitePages = cache(async () => {
|
|
413
|
+
try {
|
|
414
|
+
const source = await defaultSourceCache(input.model);
|
|
415
|
+
const pages = source ? await source.listPages() : [];
|
|
416
|
+
return pages.length ? pages : fallbackPages;
|
|
417
|
+
} catch {
|
|
418
|
+
return fallbackPages;
|
|
419
|
+
}
|
|
420
|
+
});
|
|
421
|
+
return {
|
|
422
|
+
listSitePages,
|
|
423
|
+
async getSitePageByKey(key) {
|
|
424
|
+
const pages = await listSitePages();
|
|
425
|
+
return pages.find((page) => page.key === key.toLowerCase()) ?? null;
|
|
426
|
+
},
|
|
427
|
+
async getSitePageBySlug(slug) {
|
|
428
|
+
const normalized = normalizePageSlug(slug);
|
|
429
|
+
const pages = await listSitePages();
|
|
430
|
+
return pages.find((page) => page.slug === normalized) ?? null;
|
|
431
|
+
},
|
|
432
|
+
async getSitePageForContentSource(sourceId) {
|
|
433
|
+
const pages = await listSitePages();
|
|
434
|
+
return pages.find(
|
|
435
|
+
(page) => page.layout === "content-list" && page.contentSource === sourceId
|
|
436
|
+
) ?? null;
|
|
437
|
+
},
|
|
438
|
+
async getSiteNavigation() {
|
|
439
|
+
const pages = await listSitePages();
|
|
440
|
+
return deriveSiteNavigation(pages);
|
|
441
|
+
},
|
|
442
|
+
async getSiteFooterGroups() {
|
|
443
|
+
const pages = await listSitePages();
|
|
444
|
+
return deriveSiteFooterGroups(pages);
|
|
445
|
+
}
|
|
446
|
+
};
|
|
447
|
+
}
|
|
448
|
+
function deriveSiteNavigation(pages) {
|
|
449
|
+
return pages.filter((page) => page.showInNav).map((page) => ({
|
|
450
|
+
label: page.navLabel,
|
|
451
|
+
href: page.href,
|
|
452
|
+
order: page.navOrder,
|
|
453
|
+
pageKey: page.key
|
|
454
|
+
})).sort((a, b) => a.order - b.order || a.label.localeCompare(b.label));
|
|
455
|
+
}
|
|
456
|
+
function deriveSiteFooterGroups(pages) {
|
|
457
|
+
const groups = /* @__PURE__ */ new Map();
|
|
458
|
+
for (const page of pages.filter((candidate) => candidate.showInFooter)) {
|
|
459
|
+
const label = page.footerGroup || "Company";
|
|
460
|
+
const items = groups.get(label) ?? [];
|
|
461
|
+
items.push({
|
|
462
|
+
label: page.footerLabel,
|
|
463
|
+
href: page.href,
|
|
464
|
+
order: page.footerOrder,
|
|
465
|
+
pageKey: page.key
|
|
466
|
+
});
|
|
467
|
+
groups.set(label, items);
|
|
468
|
+
}
|
|
469
|
+
return Array.from(groups.entries()).map(([label, items]) => ({
|
|
470
|
+
label,
|
|
471
|
+
items: items.sort(
|
|
472
|
+
(a, b) => a.order - b.order || a.label.localeCompare(b.label)
|
|
473
|
+
)
|
|
474
|
+
}));
|
|
475
|
+
}
|
|
476
|
+
export {
|
|
477
|
+
createSitePageSource,
|
|
478
|
+
createSitePagesApi,
|
|
479
|
+
defaultPagesDataSourceEnv,
|
|
480
|
+
defaultSitePageFields,
|
|
481
|
+
defineSitePageModel,
|
|
482
|
+
deriveSiteFooterGroups,
|
|
483
|
+
deriveSiteNavigation,
|
|
484
|
+
mapNotionPageToSitePage,
|
|
485
|
+
normalizePageSlug,
|
|
486
|
+
slugToHref
|
|
487
|
+
};
|
|
488
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +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":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@notionx/core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -19,13 +19,26 @@
|
|
|
19
19
|
"./platform/selection": "./dist/platform/selection.js",
|
|
20
20
|
"./platform/capabilities": "./dist/platform/capabilities.js",
|
|
21
21
|
"./cache": "./dist/cache/index.js",
|
|
22
|
+
"./pages": "./dist/pages/index.js",
|
|
22
23
|
"./notion": "./dist/notion/index.js",
|
|
24
|
+
"./notion/client": "./dist/notion/client.js",
|
|
25
|
+
"./notion/config": "./dist/notion/config.js",
|
|
26
|
+
"./notion/blocks": "./dist/notion/blocks.js",
|
|
27
|
+
"./notion/block-text": "./dist/notion/block-text.js",
|
|
28
|
+
"./notion/content-cache": "./dist/notion/content-cache.js",
|
|
29
|
+
"./notion/media": "./dist/notion/media.js",
|
|
30
|
+
"./notion/generic-source": "./dist/notion/generic-source.js",
|
|
31
|
+
"./notion/property-mappers": "./dist/notion/property-mappers.js",
|
|
32
|
+
"./notion/types": "./dist/notion/types.js",
|
|
33
|
+
"./notion/webhook": "./dist/notion/webhook.js",
|
|
34
|
+
"./notion/mappers": "./dist/notion/mappers.js",
|
|
23
35
|
"./notion/*": "./dist/notion/*.js",
|
|
24
36
|
"./content": "./dist/content/index.js",
|
|
25
37
|
"./content/models": "./dist/content/models.js",
|
|
26
38
|
"./content/revalidate": "./dist/content/revalidate.js",
|
|
27
39
|
"./content/search": "./dist/content/search.js",
|
|
28
40
|
"./content/search-index": "./dist/content/search-index.js",
|
|
41
|
+
"./content/localized": "./dist/content/localized.js",
|
|
29
42
|
"./content/prewarm": "./dist/content/prewarm.js",
|
|
30
43
|
"./content/admin-summary": "./dist/content/admin-summary.js",
|
|
31
44
|
"./auth": "./dist/auth/index.js",
|