noblog 0.3.0-beta.1 → 0.3.0-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -129,6 +129,7 @@ output:
129
129
  storePath: .noblog/pages/
130
130
  layoutPath: ../../layouts/MarkdownPostLayout.astro
131
131
  recursive: true
132
+ childPageMarker: "⸎"
132
133
  frontmatter:
133
134
  fields:
134
135
  title:
@@ -151,6 +152,19 @@ frontmatter:
151
152
  value: true
152
153
  ```
153
154
 
155
+ `output.childPageMarker` sets the prefix on child-page links. It defaults to `⸎` (U+2E0E EDITORIAL CORONIS) and must be a non-empty string. Set it to a marker your theme recognizes.
156
+
157
+ `output.childPageWrapper` optionally wraps the child-page link instead:
158
+
159
+ ```yaml
160
+ output:
161
+ childPageWrapper:
162
+ head: "<noblog-subpage>"
163
+ tail: "</noblog-subpage>"
164
+ ```
165
+
166
+ When configured, the wrapper overrides `output.childPageMarker`. Both strings must be non-empty and single-line. The wrapper is emitted as raw content, so use it with an HTML-capable Markdown renderer.
167
+
154
168
  The keys under `notion.properties` are logical source names; their values must exactly match Notion property names. Custom sources such as `series` must be declared there before frontmatter can reference them. Built-in sources need no declaration.
155
169
 
156
170
  Validate a file without Notion credentials, a Notion client, or network access:
@@ -185,7 +199,7 @@ project-root/
185
199
  └── <database-page-id>.md # relative symlink
186
200
  ```
187
201
 
188
- Only database posts receive public views. Child pages live exclusively in the canonical store, and child links use `./<child-page-id>.md` so they resolve as siblings after Vite/Astro resolves symlinks to real paths. Child pages therefore stay out of `src/pages/posts/*.md` listings. Use `--store PATH` to change the canonical store independently of the public path.
202
+ Only database posts receive public views. Child pages live exclusively in the canonical store, and child links are written as `<marker> [title](./<child-page-id>.md)`. The marker defaults to `⸎` (U+2E0E EDITORIAL CORONIS) and is configurable with `output.childPageMarker`; set it to match the marker your theme recognizes. Set `output.childPageWrapper` to wrap the link in custom `head`/`tail` strings instead; the wrapper overrides the marker. Links resolve as siblings after Vite/Astro resolves symlinks to real paths, so child pages stay out of `src/pages/posts/*.md` listings. Use `--store PATH` to change the canonical store independently of the public path.
189
203
 
190
204
  **Routing migration:** Noblog no longer creates `/nob_children/` routes. Configure the theme to load and route child pages from `.noblog/pages/` (or the custom store). Existing nested view files are not deleted; remove or redirect them separately after updating the theme. Existing `SUB_PATH` arguments are ignored with a warning.
191
205
 
@@ -193,6 +207,10 @@ Generation is repeatable. Existing regular database-post view files with matchin
193
207
 
194
208
  The store and views must remain together when building or deploying. If generated content is committed, include both; otherwise regenerate before building. Creating the views requires filesystem symlink support. On Windows, this may require Developer Mode or elevated privileges.
195
209
 
210
+ ## Heading support
211
+
212
+ Noblog renders Notion heading levels 1 through 4 as Markdown `#` through `####`. The installed `@notionhq/client` types do not declare `heading_4`, so the package exports `Heading4BlockObjectResponse` and `NoblogBlock`; custom readers and block transformers can use those types without assertions.
213
+
196
214
  ## Frontmatter
197
215
 
198
216
  Without custom Notion or frontmatter mappings, generated files retain the legacy Astro-compatible frontmatter:
@@ -269,7 +287,7 @@ const pages = await renderPages(posts, notion, { layoutPath });
269
287
  await writePages(pages, { savePath, storePath });
270
288
  ```
271
289
 
272
- `renderPages` recursively collects child pages by default and returns complete Markdown documents with frontmatter. Set `recursive: false` to render only the supplied database pages. `writePages` writes every canonical file but returns only the public database-post view paths. When rendering for a canonical store, use `resolveStoreLayoutPath` as above so layout imports remain valid after symlink resolution.
290
+ `renderPages` recursively collects child pages by default and returns complete Markdown documents with frontmatter. Set `recursive: false` to render only the supplied database pages. Set `childPageMarker` in `RenderOptions` to override the default `⸎` (U+2E0E EDITORIAL CORONIS) prefix used for child-page links. Set `childPageWrapper` to `{ head, tail }` to wrap the link instead; it overrides `childPageMarker`. `writePages` writes every canonical file but returns only the public database-post view paths. When rendering for a canonical store, use `resolveStoreLayoutPath` as above so layout imports remain valid after symlink resolution.
273
291
 
274
292
  Use `renderPage` for a single page, or supply any implementation of the `NotionReader` interface instead of the built-in adapter. Lower-level helpers such as `renderRichText` and `renderFrontmatter` are exported as well. To customize a block, pass a `transformers` map in `RenderOptions`; return a string to override the default renderer or `undefined` to fall back to it. Each key narrows its handler's block payload to the corresponding Notion block type, so a `paragraph` transformer can access `block.paragraph` directly. The programmatic API does not load `.env`; load environment variables yourself or pass explicit configuration.
275
293
 
@@ -1,5 +1,5 @@
1
- import { t as getErrorCode } from "./errors-DgJ15MsB.js";
2
- import { t as DEFAULT_LAYOUT_PATH } from "./defaults-D9BbNYsd.js";
1
+ import { t as getErrorCode } from "./errors-Bm4iVQVp.js";
2
+ import { n as DEFAULT_LAYOUT_PATH } from "./defaults-RGOB9tfi.js";
3
3
  import { stringify } from "yaml";
4
4
  import { markdownTable } from "markdown-table";
5
5
  import { randomUUID } from "node:crypto";
@@ -140,6 +140,7 @@ const equation = (text) => `$$\n${text}\n$$`;
140
140
  const heading1 = (text) => `# ${text}`;
141
141
  const heading2 = (text) => `## ${text}`;
142
142
  const heading3 = (text) => `### ${text}`;
143
+ const heading4 = (text) => `#### ${text}`;
143
144
  const quote = (text) => `> ${text.replace(/\n/g, " \n> ")}`;
144
145
  function callout(text, icon) {
145
146
  return `> ${icon?.type === "emoji" ? `${icon.emoji} ` : ""}${text.replace(/\n/g, " \n> ")}`;
@@ -296,7 +297,7 @@ function transformBlock(type, block, context) {
296
297
  }
297
298
  async function renderBlockNode(node, children, listNumber, context) {
298
299
  const { block } = node;
299
- const content = await transformBlock(block.type, block, context) ?? await renderDefaultBlock(node);
300
+ const content = await transformBlock(block.type, block, context) ?? await renderDefaultBlock(node, context.childPageMarker ?? "⸎", context.childPageWrapper);
300
301
  switch (block.type) {
301
302
  case "toggle": return toggle(content, children);
302
303
  case "callout": return callout(children ? `${content}\n${children}`.trim() : content, block.callout.icon);
@@ -311,7 +312,7 @@ function withIndentedChildren(content, children, markerWidth) {
311
312
  const indentation = " ".repeat(markerWidth);
312
313
  return `${content}\n${nested.split("\n").map((line) => line ? `${indentation}${line}` : "").join("\n")}`;
313
314
  }
314
- async function renderDefaultBlock(node) {
315
+ async function renderDefaultBlock(node, childPageMarker, childPageWrapper) {
315
316
  const { block } = node;
316
317
  switch (block.type) {
317
318
  case "image": {
@@ -333,13 +334,14 @@ async function renderDefaultBlock(node) {
333
334
  const id = target.type === "page_id" ? target.page_id : target.database_id;
334
335
  return link(block.type, `https://www.notion.so/${id}`);
335
336
  }
336
- case "child_page": return renderChildPageLink(block);
337
+ case "child_page": return renderChildPageLink(block, childPageMarker, childPageWrapper);
337
338
  case "child_database": return heading2(block.child_database.title || "child_database");
338
339
  case "table": return renderTable(node.children, block.table.has_column_header);
339
340
  case "code": return codeBlock(renderRichText(block.code.rich_text), block.code.language);
340
341
  case "heading_1": return heading1(renderRichText(block.heading_1.rich_text));
341
342
  case "heading_2": return heading2(renderRichText(block.heading_2.rich_text));
342
343
  case "heading_3": return heading3(renderRichText(block.heading_3.rich_text));
344
+ case "heading_4": return heading4(renderRichText(block.heading_4.rich_text));
343
345
  case "quote": return quote(renderRichText(block.quote.rich_text));
344
346
  case "to_do": return todo(renderRichText(block.to_do.rich_text), block.to_do.checked);
345
347
  case "paragraph": return renderRichText(block.paragraph.rich_text);
@@ -363,8 +365,9 @@ function renderFileLink(content, fallbackTitle) {
363
365
  const title = renderPlainText(content.caption).trim() || getFileName(url) || fallbackTitle;
364
366
  return link(title, url);
365
367
  }
366
- function renderChildPageLink(block) {
367
- return `(」゚ロ゚)」 ${link(block.child_page.title, `./${block.id}.md`)}`;
368
+ function renderChildPageLink(block, marker, wrapper) {
369
+ const link$1 = link(block.child_page.title, `./${block.id}.md`);
370
+ return wrapper === void 0 ? `${marker} ${link$1}` : `${wrapper.head}${link$1}${wrapper.tail}`;
368
371
  }
369
372
  function renderTable(nodes, hasColumnHeader) {
370
373
  return table(nodes.flatMap(({ block }) => {
@@ -390,6 +393,8 @@ function createContext(databasePageIds, reader, options) {
390
393
  propertyMap: options.propertyMap,
391
394
  frontmatter: options.frontmatter,
392
395
  recursive: options.recursive ?? true,
396
+ childPageMarker: options.childPageMarker ?? "⸎",
397
+ childPageWrapper: options.childPageWrapper,
393
398
  transformers: options.transformers,
394
399
  renderedPages: /* @__PURE__ */ new Map()
395
400
  };
@@ -407,6 +412,8 @@ async function renderSinglePage(pageId, context, post) {
407
412
  const [page, blockTree] = await Promise.all([post ? Promise.resolve(post) : context.reader.getPage(pageId), loadBlockTree(pageId, context.reader)]);
408
413
  const rendered = await renderBlockTree(blockTree, {
409
414
  pageId,
415
+ childPageMarker: context.childPageMarker,
416
+ ...context.childPageWrapper !== void 0 ? { childPageWrapper: context.childPageWrapper } : {},
410
417
  ...context.transformers !== void 0 ? { transformers: context.transformers } : {}
411
418
  });
412
419
  return {
@@ -544,6 +551,6 @@ function delay(milliseconds) {
544
551
  return new Promise((resolve) => setTimeout(resolve, milliseconds));
545
552
  }
546
553
  //#endregion
547
- export { strikethrough as C, underline as D, toggle as E, renderFrontmatter as O, quote as S, todo as T, image as _, renderPages as a, italic as b, bold as c, codeBlock as d, divider as f, heading3 as g, heading2 as h, renderPage as i, extractPageMetadata as k, bullet as l, heading1 as m, resolveStoreLayoutPath as n, renderPlainText as o, equation as p, writePages as r, renderRichText as s, createNotionSource as t, callout as u, inlineCode as v, table as w, link as x, inlineEquation as y };
554
+ export { extractPageMetadata as A, quote as C, toggle as D, todo as E, underline as O, link as S, table as T, heading4 as _, renderPages as a, inlineEquation as b, bold as c, codeBlock as d, divider as f, heading3 as g, heading2 as h, renderPage as i, renderFrontmatter as k, bullet as l, heading1 as m, resolveStoreLayoutPath as n, renderPlainText as o, equation as p, writePages as r, renderRichText as s, createNotionSource as t, callout as u, image as v, strikethrough as w, italic as x, inlineCode as y };
548
555
 
549
- //# sourceMappingURL=client-CkOIgSvW.js.map
556
+ //# sourceMappingURL=client-BIBJQzBg.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client-BIBJQzBg.js","names":["md.toggle","md.callout","md.bullet","md.image","md.divider","md.equation","md.link","md.heading2","md.codeBlock","md.heading1","md.heading3","md.heading4","md.quote","md.todo","link","md.table"],"sources":["../src/lib/noblog/metadata.ts","../src/lib/noblog/frontmatter.ts","../src/lib/noblog/markdown.ts","../src/lib/noblog/rich-text.ts","../src/lib/noblog/blocks.ts","../src/lib/noblog/pages.ts","../src/lib/noblog/writer.ts","../src/lib/notion/client.ts"],"sourcesContent":["import type { PageObjectResponse } from \"@notionhq/client\";\nimport type { NotionPropertyMap } from \"./config.js\";\n\ntype NotionProperty = PageObjectResponse[\"properties\"][string];\n\nexport type MetadataValue =\n | string\n | number\n | boolean\n | readonly string[]\n | undefined;\n\nexport interface PageMetadata {\n readonly title: string;\n readonly description?: string | undefined;\n readonly tags: readonly string[];\n readonly publishedAt?: string | undefined;\n readonly archived?: boolean | undefined;\n readonly values: Readonly<Record<string, MetadataValue>>;\n}\n\nconst DEFAULT_PROPERTIES = {\n description: \"description\",\n tags: \"tags\",\n publishedAt: \"date\",\n archived: \"archived\",\n} as const;\n\nexport function extractPageMetadata(\n page: PageObjectResponse,\n properties: NotionPropertyMap = {},\n): PageMetadata {\n const propertyNames = new Map<string, string>(Object.entries(properties));\n for (const [key, name] of Object.entries(DEFAULT_PROPERTIES)) {\n if (!propertyNames.has(key)) propertyNames.set(key, name);\n }\n\n const values: Record<string, MetadataValue> = {};\n if (!propertyNames.has(\"title\")) {\n const titleProperty = Object.values(page.properties).findLast(\n (property) => property.type === \"title\",\n );\n values[\"title\"] = normalizeProperty(titleProperty);\n }\n for (const [key, name] of propertyNames) {\n values[key] = normalizeProperty(page.properties[name]);\n }\n\n return {\n title: asString(values[\"title\"]) ?? \"\",\n description: asString(values[\"description\"]),\n tags: asStringArray(values[\"tags\"]) ?? [],\n publishedAt: asString(values[\"publishedAt\"]),\n archived: asBoolean(values[\"archived\"]),\n values,\n };\n}\n\nfunction normalizeProperty(property?: NotionProperty): MetadataValue {\n if (!property) return undefined;\n\n switch (property.type) {\n case \"title\":\n return plainText(property.title);\n case \"rich_text\":\n return plainText(property.rich_text);\n case \"multi_select\":\n return property.multi_select.map(({ name }) => name);\n case \"select\":\n return property.select?.name;\n case \"status\":\n return property.status?.name;\n case \"checkbox\":\n return property.checkbox;\n case \"number\":\n return property.number ?? undefined;\n case \"date\":\n return property.date?.start;\n case \"url\":\n return property.url ?? undefined;\n case \"email\":\n return property.email ?? undefined;\n case \"phone_number\":\n return property.phone_number ?? undefined;\n case \"people\":\n return property.people.map(entityName);\n case \"relation\":\n return property.relation.map(({ id }) => id);\n case \"files\":\n return property.files.map((file) =>\n \"file\" in file ? file.file.url : file.external.url,\n );\n case \"formula\":\n return normalizeFormula(property.formula);\n case \"created_time\":\n return property.created_time;\n case \"last_edited_time\":\n return property.last_edited_time;\n case \"created_by\":\n return entityName(property.created_by);\n case \"last_edited_by\":\n return entityName(property.last_edited_by);\n case \"unique_id\":\n return property.unique_id.prefix\n ? `${property.unique_id.prefix}-${property.unique_id.number}`\n : String(property.unique_id.number);\n default:\n return undefined;\n }\n}\n\nfunction normalizeFormula(\n formula: Extract<NotionProperty, { type: \"formula\" }>[\"formula\"],\n): MetadataValue {\n switch (formula.type) {\n case \"string\":\n return formula.string ?? undefined;\n case \"number\":\n return formula.number ?? undefined;\n case \"boolean\":\n return formula.boolean ?? undefined;\n case \"date\":\n return formula.date?.start;\n }\n}\n\nfunction plainText(\n richText: Extract<NotionProperty, { type: \"title\" }>[\"title\"],\n): string {\n return richText.map((item) => item.plain_text).join(\"\");\n}\n\nfunction entityName(entity: {\n readonly id: string;\n readonly name?: string | null;\n}): string {\n return entity.name ?? entity.id;\n}\n\nfunction asString(value: MetadataValue): string | undefined {\n return typeof value === \"string\" ? value : undefined;\n}\n\nfunction asBoolean(value: MetadataValue): boolean | undefined {\n return typeof value === \"boolean\" ? value : undefined;\n}\n\nfunction asStringArray(value: MetadataValue): readonly string[] | undefined {\n if (typeof value === \"string\") return [value];\n return Array.isArray(value) ? value : undefined;\n}\n","import type { PageObjectResponse } from \"@notionhq/client\";\nimport { stringify } from \"yaml\";\nimport type { NotionPropertyMap } from \"./config.js\";\nimport { DEFAULT_LAYOUT_PATH } from \"./defaults.js\";\nimport { extractPageMetadata } from \"./metadata.js\";\nimport type { FrontmatterAdapter, FrontmatterRecord } from \"./types.js\";\n\nexport interface FrontmatterOptions {\n readonly layoutPath?: string;\n readonly propertyMap?: NotionPropertyMap;\n readonly frontmatter?: FrontmatterAdapter;\n readonly isDatabasePage?: boolean;\n}\n\nexport function renderFrontmatter(\n page: PageObjectResponse,\n options: FrontmatterOptions = {},\n): string {\n const { layoutPath = DEFAULT_LAYOUT_PATH } = options;\n if (options.frontmatter) {\n const metadata = extractPageMetadata(page, options.propertyMap);\n const values = options.frontmatter({\n page,\n metadata,\n layoutPath,\n isDatabasePage: options.isDatabasePage ?? true,\n });\n return serializeFrontmatter(values);\n }\n\n let title = \"\";\n let description = \"\";\n let tags: string[] = [];\n let pubDate = \"\";\n let archived = \"\";\n\n for (const [name, property] of Object.entries(page.properties)) {\n switch (property.type) {\n case \"title\":\n title = property.title.map((item) => item.plain_text).join(\"\");\n break;\n case \"multi_select\":\n if (name === \"tags\") {\n tags = property.multi_select.map((tag) => tag.name);\n }\n break;\n case \"date\":\n if (name === \"date\") {\n pubDate =\n property.date?.start ?? new Date().toISOString().slice(0, 10);\n }\n break;\n }\n\n if (name === \"archived\" && property.type === \"checkbox\") {\n archived = property.checkbox ? \"true\" : \"false\";\n }\n if (name === \"description\" && property.type === \"rich_text\") {\n description = property.rich_text\n .map((item) => item.plain_text)\n .join(\"\");\n }\n }\n\n return (\n \"---\\n\" +\n `layout: ${layoutPath}\\n` +\n `title: ${JSON.stringify(title)}\\n` +\n `description: ${JSON.stringify(description)}\\n` +\n `tags: ${JSON.stringify(tags)}\\n` +\n `pubDate: ${JSON.stringify(pubDate)}\\n` +\n `archived: ${JSON.stringify(archived)}\\n` +\n \"---\\n\"\n );\n}\n\nfunction serializeFrontmatter(values: FrontmatterRecord): string {\n if (\n values === null ||\n typeof values !== \"object\" ||\n Array.isArray(values) ||\n (Object.getPrototypeOf(values) !== Object.prototype &&\n Object.getPrototypeOf(values) !== null)\n ) {\n throw new Error(\"Frontmatter adapter must return a plain object\");\n }\n\n const entries = Object.entries(values).filter(\n ([, value]) => value !== undefined,\n );\n if (entries.length === 0) return \"---\\n---\\n\";\n\n const yaml = stringify(Object.fromEntries(entries), {\n aliasDuplicateObjects: false,\n schema: \"yaml-1.1\",\n });\n return `---\\n${yaml}---\\n`;\n}\n","import { markdownTable } from \"markdown-table\";\nimport type { CalloutIcon } from \"./types.js\";\n\nexport const inlineCode = (text: string) => `\\`${text}\\``;\nexport const inlineEquation = (text: string) => `$${text}$`;\nexport const bold = (text: string) => `**${text}**`;\nexport const italic = (text: string) => `_${text}_`;\nexport const strikethrough = (text: string) => `~~${text}~~`;\nexport const underline = (text: string) => `<u>${text}</u>`;\nexport const link = (text: string, href: string) => `[${text}](${href})`;\n\nexport function codeBlock(text: string, language = \"\"): string {\n const normalizedLanguage = language === \"plain text\" ? \"text\" : language;\n return `\\`\\`\\`${normalizedLanguage}\\n${text}\\n\\`\\`\\``;\n}\n\nexport const equation = (text: string) => `$$\\n${text}\\n$$`;\nexport const heading1 = (text: string) => `# ${text}`;\nexport const heading2 = (text: string) => `## ${text}`;\nexport const heading3 = (text: string) => `### ${text}`;\nexport const heading4 = (text: string) => `#### ${text}`;\nexport const quote = (text: string) => `> ${text.replace(/\\n/g, \" \\n> \")}`;\n\nexport function callout(text: string, icon?: CalloutIcon): string {\n const emoji = icon?.type === \"emoji\" ? `${icon.emoji} ` : \"\";\n return `> ${emoji}${text.replace(/\\n/g, \" \\n> \")}`;\n}\n\nexport function bullet(text: string, count?: number): string {\n const rendered = text.trim();\n return count === undefined ? `- ${rendered}` : `${count}. ${rendered}`;\n}\n\nexport const todo = (text: string, checked: boolean) =>\n checked ? `- [x] ${text}` : `- [ ] ${text}`;\n\nexport async function image(\n alt: string,\n href: string,\n convertToBase64 = false,\n): Promise<string> {\n if (!convertToBase64 || href.startsWith(\"data:\")) {\n if (href.startsWith(\"data:\")) {\n const base64 = href.split(\",\").pop();\n return `![${alt}](data:image/png;base64,${base64})`;\n }\n return `![${alt}](${href})`;\n }\n\n const response = await fetch(href);\n if (!response.ok) {\n throw new Error(\n `Failed to download image: ${response.status} ${response.statusText}`,\n );\n }\n const buffer = await response.arrayBuffer();\n return `![${alt}](data:image/png;base64,${Buffer.from(buffer).toString(\"base64\")})`;\n}\n\nexport const divider = () => \"---\";\n\nexport function toggle(summary?: string, children?: string): string {\n if (!summary) return children ?? \"\";\n return `<details>\\n<summary>${summary}</summary>\\n${children ?? \"\"}\\n</details>\\n\\n`;\n}\n\nexport function table(cells: string[][], hasColumnHeader = true): string {\n if (hasColumnHeader || cells.length === 0) return markdownTable(cells);\n\n const width = Math.max(...cells.map((row) => row.length));\n if (width === 0) return \"\";\n const emptyHeader = Array.from({ length: width }, () => \"\");\n return markdownTable([emptyHeader, ...cells]);\n}\n","import type { RichTextItemResponse } from \"@notionhq/client\";\nimport { inlineEquation, link } from \"./markdown.js\";\n\ntype AnnotationName = Exclude<\n keyof RichTextItemResponse[\"annotations\"],\n \"color\"\n>;\n\ninterface AnnotationFormat {\n readonly name: AnnotationName;\n readonly open: string;\n readonly close: string;\n}\n\n// Ordered from the outermost marker to the innermost marker. Stable sorting\n// keeps this order when annotations end on the same rich-text segment.\nconst ANNOTATION_FORMATS: readonly AnnotationFormat[] = [\n { name: \"strikethrough\", open: \"~~\", close: \"~~\" },\n { name: \"underline\", open: \"<u>\", close: \"</u>\" },\n { name: \"italic\", open: \"_\", close: \"_\" },\n { name: \"bold\", open: \"**\", close: \"**\" },\n { name: \"code\", open: \"`\", close: \"`\" },\n];\n\nexport function renderPlainText(\n richText: readonly RichTextItemResponse[],\n): string {\n return richText.map((item) => item.plain_text).join(\"\");\n}\n\nexport function renderRichText(\n richText: readonly RichTextItemResponse[],\n): string {\n let rendered = \"\";\n let active: AnnotationFormat[] = [];\n\n for (const [index, item] of richText.entries()) {\n const desired = getAnnotationFormats(richText, index, item);\n let shared = 0;\n while (\n shared < active.length &&\n shared < desired.length &&\n active[shared]?.name === desired[shared]?.name\n ) {\n shared++;\n }\n\n for (const format of active.slice(shared).toReversed()) {\n rendered += format.close;\n }\n for (const format of desired.slice(shared)) {\n rendered += format.open;\n }\n\n rendered += renderContent(item);\n active = desired;\n }\n\n for (const format of active.toReversed()) {\n rendered += format.close;\n }\n return rendered;\n}\n\nfunction getAnnotationFormats(\n richText: readonly RichTextItemResponse[],\n index: number,\n item: RichTextItemResponse,\n): AnnotationFormat[] {\n if (item.type === \"equation\") return [];\n\n return ANNOTATION_FORMATS.filter((format) => item.annotations[format.name])\n .map((format) => ({\n format,\n runEnd: getAnnotationRunEnd(richText, index, format.name),\n }))\n .sort((left, right) => right.runEnd - left.runEnd)\n .map(({ format }) => format);\n}\n\nfunction getAnnotationRunEnd(\n richText: readonly RichTextItemResponse[],\n start: number,\n name: AnnotationName,\n): number {\n let end = start;\n for (const [offset, next] of richText.slice(start + 1).entries()) {\n if (next.type === \"equation\" || !next.annotations[name]) break;\n end = start + offset + 1;\n }\n return end;\n}\n\nfunction renderContent(item: RichTextItemResponse): string {\n if (item.type === \"equation\") {\n return inlineEquation(item.equation.expression);\n }\n return item.href ? link(item.plain_text, item.href) : item.plain_text;\n}\n","import type { BlockObjectResponse } from \"@notionhq/client\";\nimport { DEFAULT_CHILD_PAGE_MARKER } from \"./defaults.js\";\nimport * as md from \"./markdown.js\";\nimport { renderPlainText, renderRichText } from \"./rich-text.js\";\nimport type {\n BlockTransformerContext,\n BlockTransformers,\n ChildPageWrapper,\n NoblogBlock,\n NotionReader,\n} from \"./types.js\";\n\nexport interface BlockNode {\n readonly block: NoblogBlock;\n readonly children: BlockNode[];\n}\n\nexport interface BlockRenderContext extends BlockTransformerContext {\n readonly childPageMarker?: string;\n readonly childPageWrapper?: ChildPageWrapper;\n readonly transformers?: BlockTransformers;\n}\n\nexport interface RenderedBlocks {\n content: string;\n childPageIds: string[];\n}\n\nexport async function loadBlockTree(\n blockId: string,\n reader: NotionReader,\n ancestors: ReadonlySet<string> = new Set(),\n): Promise<BlockNode[]> {\n if (ancestors.has(blockId)) return [];\n\n const blocks = await reader.getBlockChildren(blockId);\n const childAncestors = new Set(ancestors).add(blockId);\n const nodes: BlockNode[] = [];\n\n for (const block of blocks) {\n if (block.type === \"unsupported\") continue;\n\n const childSourceId = getChildSourceId(block);\n const children =\n childSourceId && !childAncestors.has(childSourceId)\n ? await loadBlockTree(childSourceId, reader, childAncestors)\n : [];\n nodes.push({ block, children });\n }\n\n return nodes;\n}\n\nfunction getChildSourceId(block: NoblogBlock): string | undefined {\n if (block.type === \"child_page\" || block.type === \"child_database\") {\n return undefined;\n }\n if (block.type === \"synced_block\") {\n return (\n block.synced_block.synced_from?.block_id ??\n (block.has_children ? block.id : undefined)\n );\n }\n return block.has_children ? block.id : undefined;\n}\n\nexport async function renderBlockTree(\n nodes: readonly BlockNode[],\n context: BlockRenderContext,\n): Promise<RenderedBlocks> {\n const result: RenderedBlocks = { content: \"\", childPageIds: [] };\n let numberedListIndex = 0;\n\n for (const node of nodes) {\n const { block } = node;\n numberedListIndex =\n block.type === \"numbered_list_item\" ? numberedListIndex + 1 : 0;\n\n const children =\n block.type === \"table\"\n ? { content: \"\", childPageIds: [] }\n : await renderBlockTree(node.children, context);\n const content = await renderBlockNode(\n node,\n children.content,\n numberedListIndex || undefined,\n context,\n );\n\n if (block.type === \"toggle\") {\n result.content += content;\n } else {\n const lineWrap = isListBlock(block) ? \"\" : \"\\n\";\n result.content += `${lineWrap}${content}\\n${lineWrap}`;\n if (!containsOwnChildren(block)) {\n result.content += children.content;\n }\n }\n\n if (block.type === \"child_page\") {\n result.childPageIds.push(block.id);\n } else {\n result.childPageIds.push(...children.childPageIds);\n }\n }\n\n return result;\n}\n\nfunction isListBlock(block: NoblogBlock): boolean {\n return (\n block.type === \"to_do\" ||\n block.type === \"bulleted_list_item\" ||\n block.type === \"numbered_list_item\"\n );\n}\n\nfunction containsOwnChildren(block: NoblogBlock): boolean {\n return (\n block.type === \"child_page\" ||\n block.type === \"callout\" ||\n block.type === \"bulleted_list_item\" ||\n block.type === \"numbered_list_item\" ||\n block.type === \"table\"\n );\n}\n\nfunction transformBlock<K extends NoblogBlock[\"type\"]>(\n type: K,\n block: Extract<NoblogBlock, { type: NoInfer<K> }>,\n context: BlockRenderContext,\n): string | undefined | Promise<string | undefined> {\n const transformer = context.transformers?.[type];\n return transformer?.(block, context);\n}\n\nasync function renderBlockNode(\n node: BlockNode,\n children: string,\n listNumber: number | undefined,\n context: BlockRenderContext,\n): Promise<string> {\n const { block } = node;\n const transformed = await transformBlock(block.type, block, context);\n const content =\n transformed ??\n (await renderDefaultBlock(\n node,\n context.childPageMarker ?? DEFAULT_CHILD_PAGE_MARKER,\n context.childPageWrapper,\n ));\n\n switch (block.type) {\n case \"toggle\":\n return md.toggle(content, children);\n case \"callout\":\n return md.callout(\n children ? `${content}\\n${children}`.trim() : content,\n block.callout.icon,\n );\n case \"bulleted_list_item\":\n return md.bullet(withIndentedChildren(content, children, 2));\n case \"numbered_list_item\":\n return md.bullet(\n withIndentedChildren(\n content,\n children,\n `${listNumber ?? 1}. `.length,\n ),\n listNumber,\n );\n default:\n return content;\n }\n}\n\nfunction withIndentedChildren(\n content: string,\n children: string,\n markerWidth: number,\n): string {\n const nested = children.trim();\n if (!nested) return content;\n\n const indentation = \" \".repeat(markerWidth);\n const indented = nested\n .split(\"\\n\")\n .map((line) => (line ? `${indentation}${line}` : \"\"))\n .join(\"\\n\");\n return `${content}\\n${indented}`;\n}\n\nasync function renderDefaultBlock(\n node: BlockNode,\n childPageMarker: string,\n childPageWrapper: ChildPageWrapper | undefined,\n): Promise<string> {\n const { block } = node;\n\n switch (block.type) {\n case \"image\": {\n const url = getFileUrl(block.image);\n const caption = renderPlainText(block.image.caption);\n const alt = caption.trim() || getFileName(url) || \"image\";\n return md.image(alt, url);\n }\n case \"divider\":\n return md.divider();\n case \"equation\":\n return md.equation(block.equation.expression);\n case \"pdf\":\n return renderFileLink(block.pdf, block.type);\n case \"file\":\n return renderFileLink(block.file, block.type);\n case \"video\":\n return renderFileLink(block.video, block.type);\n case \"audio\":\n return renderFileLink(block.audio, block.type);\n case \"embed\":\n return md.link(block.type, block.embed.url);\n case \"bookmark\":\n return md.link(block.type, block.bookmark.url);\n case \"link_preview\":\n return md.link(block.type, block.link_preview.url);\n case \"link_to_page\": {\n const target = block.link_to_page;\n if (target.type !== \"page_id\" && target.type !== \"database_id\") {\n return \"\";\n }\n const id = target.type === \"page_id\" ? target.page_id : target.database_id;\n return md.link(block.type, `https://www.notion.so/${id}`);\n }\n case \"child_page\":\n return renderChildPageLink(block, childPageMarker, childPageWrapper);\n case \"child_database\":\n return md.heading2(block.child_database.title || \"child_database\");\n case \"table\":\n return renderTable(node.children, block.table.has_column_header);\n case \"code\":\n return md.codeBlock(\n renderRichText(block.code.rich_text),\n block.code.language,\n );\n case \"heading_1\":\n return md.heading1(renderRichText(block.heading_1.rich_text));\n case \"heading_2\":\n return md.heading2(renderRichText(block.heading_2.rich_text));\n case \"heading_3\":\n return md.heading3(renderRichText(block.heading_3.rich_text));\n case \"heading_4\":\n return md.heading4(renderRichText(block.heading_4.rich_text));\n case \"quote\":\n return md.quote(renderRichText(block.quote.rich_text));\n case \"to_do\":\n return md.todo(\n renderRichText(block.to_do.rich_text),\n block.to_do.checked,\n );\n case \"paragraph\":\n return renderRichText(block.paragraph.rich_text);\n case \"toggle\":\n return renderRichText(block.toggle.rich_text);\n case \"callout\":\n return renderRichText(block.callout.rich_text);\n case \"bulleted_list_item\":\n return renderRichText(block.bulleted_list_item.rich_text);\n case \"numbered_list_item\":\n return renderRichText(block.numbered_list_item.rich_text);\n case \"template\":\n return renderRichText(block.template.rich_text);\n default:\n return \"\";\n }\n}\n\ntype FileContent =\n | Extract<BlockObjectResponse, { type: \"image\" }>[\"image\"]\n | Extract<BlockObjectResponse, { type: \"pdf\" }>[\"pdf\"]\n | Extract<BlockObjectResponse, { type: \"file\" }>[\"file\"]\n | Extract<BlockObjectResponse, { type: \"video\" }>[\"video\"]\n | Extract<BlockObjectResponse, { type: \"audio\" }>[\"audio\"];\n\nfunction getFileUrl(content: FileContent): string {\n if (content.type === \"external\") return content.external.url;\n return content.file.url;\n}\n\nfunction getFileName(url: string): string | undefined {\n return url.match(/[^\\/\\\\&?]+\\.\\w{3,4}(?=([?&].*$|$))/)?.[0];\n}\n\nfunction renderFileLink(content: FileContent, fallbackTitle: string): string {\n const url = getFileUrl(content);\n const caption = renderPlainText(content.caption);\n const title = caption.trim() || getFileName(url) || fallbackTitle;\n return md.link(title, url);\n}\n\nfunction renderChildPageLink(\n block: Extract<BlockObjectResponse, { type: \"child_page\" }>,\n marker: string,\n wrapper: ChildPageWrapper | undefined,\n): string {\n // The theme consumes the marker or wrapper to render subpage links.\n const link = md.link(block.child_page.title, `./${block.id}.md`);\n return wrapper === undefined\n ? `${marker} ${link}`\n : `${wrapper.head}${link}${wrapper.tail}`;\n}\n\nfunction renderTable(\n nodes: readonly BlockNode[],\n hasColumnHeader: boolean,\n): string {\n const rows = nodes.flatMap(({ block }) => {\n if (block.type !== \"table_row\") return [];\n return [\n block.table_row.cells.map((cell) => renderRichText(cell)),\n ];\n });\n return md.table(rows, hasColumnHeader);\n}\n","import type { PageObjectResponse } from \"@notionhq/client\";\nimport { loadBlockTree, renderBlockTree } from \"./blocks.js\";\nimport {\n DEFAULT_CHILD_PAGE_MARKER,\n DEFAULT_LAYOUT_PATH,\n} from \"./defaults.js\";\nimport { renderFrontmatter } from \"./frontmatter.js\";\nimport type { NotionPropertyMap } from \"./config.js\";\nimport type {\n BlockTransformers,\n ChildPageWrapper,\n FrontmatterAdapter,\n NotionReader,\n RenderedPage,\n RenderOptions,\n} from \"./types.js\";\n\ninterface PageRenderContext {\n readonly reader: NotionReader;\n readonly databasePageIds: ReadonlySet<string>;\n readonly layoutPath: string;\n readonly propertyMap: NotionPropertyMap | undefined;\n readonly frontmatter: FrontmatterAdapter | undefined;\n readonly recursive: boolean;\n readonly childPageMarker: string;\n readonly childPageWrapper: ChildPageWrapper | undefined;\n readonly transformers: BlockTransformers | undefined;\n readonly renderedPages: Map<string, Promise<RenderedPage>>;\n}\n\nexport async function renderPage(\n pageId: string,\n reader: NotionReader,\n options: RenderOptions = {},\n): Promise<RenderedPage> {\n const context = createContext([pageId], reader, options);\n return collectPage(pageId, context);\n}\n\nexport async function renderPages(\n posts: readonly PageObjectResponse[],\n reader: NotionReader,\n options: RenderOptions = {},\n): Promise<RenderedPage[]> {\n const context = createContext(\n posts.map((post) => post.id),\n reader,\n options,\n );\n\n for (const post of posts) {\n await collectPage(post.id, context, post);\n }\n\n return Promise.all([...context.renderedPages.values()]);\n}\n\nfunction createContext(\n databasePageIds: readonly string[],\n reader: NotionReader,\n options: RenderOptions,\n): PageRenderContext {\n return {\n reader,\n databasePageIds: new Set(databasePageIds),\n layoutPath: options.layoutPath ?? DEFAULT_LAYOUT_PATH,\n propertyMap: options.propertyMap,\n frontmatter: options.frontmatter,\n recursive: options.recursive ?? true,\n childPageMarker: options.childPageMarker ?? DEFAULT_CHILD_PAGE_MARKER,\n childPageWrapper: options.childPageWrapper,\n transformers: options.transformers,\n renderedPages: new Map(),\n };\n}\n\nasync function collectPage(\n pageId: string,\n context: PageRenderContext,\n post?: PageObjectResponse,\n): Promise<RenderedPage> {\n const cached = context.renderedPages.get(pageId);\n if (cached) return cached;\n\n const rendering = renderSinglePage(pageId, context, post);\n context.renderedPages.set(pageId, rendering);\n const rendered = await rendering;\n\n if (context.recursive) {\n for (const childPageId of rendered.childPageIds) {\n await collectPage(childPageId, context);\n }\n }\n\n return rendered;\n}\n\nasync function renderSinglePage(\n pageId: string,\n context: PageRenderContext,\n post?: PageObjectResponse,\n): Promise<RenderedPage> {\n const [page, blockTree] = await Promise.all([\n post ? Promise.resolve(post) : context.reader.getPage(pageId),\n loadBlockTree(pageId, context.reader),\n ]);\n const rendered = await renderBlockTree(blockTree, {\n pageId,\n childPageMarker: context.childPageMarker,\n ...(context.childPageWrapper !== undefined\n ? { childPageWrapper: context.childPageWrapper }\n : {}),\n ...(context.transformers !== undefined\n ? { transformers: context.transformers }\n : {}),\n });\n const frontmatter = renderFrontmatter(page, {\n layoutPath: context.layoutPath,\n ...(context.propertyMap !== undefined\n ? { propertyMap: context.propertyMap }\n : {}),\n ...(context.frontmatter !== undefined\n ? { frontmatter: context.frontmatter }\n : {}),\n isDatabasePage: context.databasePageIds.has(pageId),\n });\n\n return {\n id: pageId,\n content: frontmatter + rendered.content,\n childPageIds: [...new Set(rendered.childPageIds)],\n isDatabasePage: context.databasePageIds.has(pageId),\n };\n}\n","import { randomUUID } from \"node:crypto\";\nimport {\n lstat,\n mkdir,\n readlink,\n rename,\n rm,\n symlink,\n writeFile,\n} from \"node:fs/promises\";\nimport { dirname, join, relative, resolve, sep } from \"node:path\";\nimport { getErrorCode } from \"../errors.js\";\nimport {\n DEFAULT_LAYOUT_PATH,\n DEFAULT_SAVE_PATH,\n DEFAULT_STORE_PATH,\n} from \"./defaults.js\";\nimport type {\n RenderedPage,\n StoreLayoutOptions,\n WritePagesOptions,\n} from \"./types.js\";\n\nexport async function writePages(\n pages: readonly RenderedPage[],\n options: WritePagesOptions = {},\n): Promise<string[]> {\n const savePath = options.savePath ?? DEFAULT_SAVE_PATH;\n const storePath = options.storePath ?? DEFAULT_STORE_PATH;\n const writtenPaths: string[] = [];\n await mkdir(storePath, { recursive: true });\n\n for (const page of pages) {\n const storedPath = join(storePath, `${page.id}.md`);\n const storedStats = await getStats(storedPath);\n if (storedStats && !storedStats.isFile()) {\n throw new Error(`Page store path is not a regular file: ${storedPath}`);\n }\n await writeFile(storedPath, page.content, \"utf8\");\n\n if (!page.isDatabasePage) continue;\n\n const viewPath = join(savePath, `${page.id}.md`);\n await mkdir(savePath, { recursive: true });\n await linkStoredPage(storedPath, viewPath);\n writtenPaths.push(viewPath);\n }\n\n return writtenPaths;\n}\n\nexport function resolveStoreLayoutPath(\n options: StoreLayoutOptions = {},\n): string {\n const savePath = options.savePath ?? DEFAULT_SAVE_PATH;\n const storePath = options.storePath ?? DEFAULT_STORE_PATH;\n const layoutPath = options.layoutPath ?? DEFAULT_LAYOUT_PATH;\n const layoutTarget = resolve(savePath, layoutPath);\n return relative(resolve(storePath), layoutTarget).split(sep).join(\"/\");\n}\n\nasync function linkStoredPage(\n storedPath: string,\n viewPath: string,\n): Promise<void> {\n if (resolve(storedPath) === resolve(viewPath)) return;\n\n const viewStats = await getStats(viewPath);\n if (viewStats && !viewStats.isFile() && !viewStats.isSymbolicLink()) {\n throw new Error(`Refusing to replace non-file output: ${viewPath}`);\n }\n\n const target = relative(dirname(viewPath), storedPath);\n if (viewStats?.isSymbolicLink() && (await readlink(viewPath)) === target) {\n return;\n }\n\n const temporaryPath = `${viewPath}.noblog-${randomUUID()}.tmp`;\n try {\n await symlink(target, temporaryPath, \"file\");\n await rename(temporaryPath, viewPath);\n } catch (error) {\n await rm(temporaryPath, { force: true }).catch(() => {});\n throw error;\n }\n}\n\nasync function getStats(path: string) {\n try {\n return await lstat(path);\n } catch (error: unknown) {\n if (getErrorCode(error) === \"ENOENT\") return undefined;\n throw error;\n }\n}\n","import {\n Client,\n isFullBlock,\n isFullDatabase,\n isFullPage,\n isNotionClientError,\n} from \"@notionhq/client\";\nimport type {\n BlockObjectResponse,\n PageObjectResponse,\n} from \"@notionhq/client\";\nimport type { NotionReader } from \"../noblog/types.js\";\n\nexport interface NotionSourceConfig {\n readonly notionApiSecret: string;\n readonly databaseId: string;\n}\n\nexport interface NotionSource extends NotionReader {\n listPages(): Promise<PageObjectResponse[]>;\n}\n\nexport function createNotionSource(\n config: NotionSourceConfig,\n): NotionSource {\n const client = new Client({ auth: config.notionApiSecret });\n return {\n listPages: () => listPages(client, config.databaseId),\n getPage: (pageId) => getPage(client, pageId),\n getBlockChildren: (blockId) => getBlockChildren(client, blockId),\n };\n}\n\nasync function listPages(\n client: Client,\n databaseId: string,\n): Promise<PageObjectResponse[]> {\n const database = await withRetry(() =>\n client.databases.retrieve({ database_id: databaseId }),\n );\n if (!isFullDatabase(database)) {\n throw new Error(`Notion database ${databaseId} is not a full database`);\n }\n\n const pages: PageObjectResponse[] = [];\n for (const source of database.data_sources) {\n let startCursor: string | undefined;\n do {\n const response = await withRetry(() =>\n client.dataSources.query({\n data_source_id: source.id,\n filter: {\n property: \"publish\",\n checkbox: { equals: true },\n },\n sorts: [{ property: \"date\", direction: \"descending\" }],\n page_size: 100,\n ...(startCursor !== undefined\n ? { start_cursor: startCursor }\n : {}),\n }),\n );\n pages.push(...response.results.filter(isFullPage));\n startCursor = response.next_cursor ?? undefined;\n if (!response.has_more) break;\n } while (startCursor);\n }\n\n return pages;\n}\n\nasync function getPage(\n client: Client,\n pageId: string,\n): Promise<PageObjectResponse> {\n const page = await withRetry(() => client.pages.retrieve({ page_id: pageId }));\n if (!isFullPage(page)) {\n throw new Error(`Notion page ${pageId} is not a full page`);\n }\n return page;\n}\n\nasync function getBlockChildren(\n client: Client,\n blockId: string,\n): Promise<BlockObjectResponse[]> {\n const blocks: BlockObjectResponse[] = [];\n let startCursor: string | undefined;\n\n do {\n const response = await withRetry(() =>\n client.blocks.children.list({\n block_id: blockId,\n page_size: 100,\n ...(startCursor !== undefined ? { start_cursor: startCursor } : {}),\n }),\n );\n blocks.push(...response.results.filter(isFullBlock));\n startCursor = response.next_cursor ?? undefined;\n } while (startCursor);\n\n return blocks;\n}\n\nconst MAX_ATTEMPTS = 3;\nconst INITIAL_RETRY_DELAY_MS = 500;\n\nasync function withRetry<T>(operation: () => Promise<T>): Promise<T> {\n for (let attempt = 0; ; attempt++) {\n try {\n return await operation();\n } catch (error: unknown) {\n if (attempt === MAX_ATTEMPTS - 1 || !isRetryable(error)) throw error;\n await delay(INITIAL_RETRY_DELAY_MS * 2 ** attempt);\n }\n }\n}\n\nfunction isRetryable(error: unknown): boolean {\n if (!isNotionClientError(error) || !(\"status\" in error)) return true;\n return error.status === 408 || error.status === 429 || error.status >= 500;\n}\n\nfunction delay(milliseconds: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, milliseconds));\n}\n"],"mappings":";;;;;;;;;AAqBA,MAAM,qBAAqB;CACzB,aAAa;CACb,MAAM;CACN,aAAa;CACb,UAAU;AACZ;AAEA,SAAgB,oBACd,MACA,aAAgC,CAAC,GACnB;CACd,MAAM,gBAAgB,IAAI,IAAoB,OAAO,QAAQ,UAAU,CAAC;CACxE,KAAK,MAAM,CAAC,KAAK,SAAS,OAAO,QAAQ,kBAAkB,GACzD,IAAI,CAAC,cAAc,IAAI,GAAG,GAAG,cAAc,IAAI,KAAK,IAAI;CAG1D,MAAM,SAAwC,CAAC;CAC/C,IAAI,CAAC,cAAc,IAAI,OAAO,GAI5B,OAAO,WAAW,kBAHI,OAAO,OAAO,KAAK,UAAU,CAAC,CAAC,UAClD,aAAa,SAAS,SAAS,OAEc,CAAC;CAEnD,KAAK,MAAM,CAAC,KAAK,SAAS,eACxB,OAAO,OAAO,kBAAkB,KAAK,WAAW,KAAK;CAGvD,OAAO;EACL,OAAO,SAAS,OAAO,QAAQ,KAAK;EACpC,aAAa,SAAS,OAAO,cAAc;EAC3C,MAAM,cAAc,OAAO,OAAO,KAAK,CAAC;EACxC,aAAa,SAAS,OAAO,cAAc;EAC3C,UAAU,UAAU,OAAO,WAAW;EACtC;CACF;AACF;AAEA,SAAS,kBAAkB,UAA0C;CACnE,IAAI,CAAC,UAAU,OAAO,KAAA;CAEtB,QAAQ,SAAS,MAAjB;EACE,KAAK,SACH,OAAO,UAAU,SAAS,KAAK;EACjC,KAAK,aACH,OAAO,UAAU,SAAS,SAAS;EACrC,KAAK,gBACH,OAAO,SAAS,aAAa,KAAK,EAAE,WAAW,IAAI;EACrD,KAAK,UACH,OAAO,SAAS,QAAQ;EAC1B,KAAK,UACH,OAAO,SAAS,QAAQ;EAC1B,KAAK,YACH,OAAO,SAAS;EAClB,KAAK,UACH,OAAO,SAAS,UAAU,KAAA;EAC5B,KAAK,QACH,OAAO,SAAS,MAAM;EACxB,KAAK,OACH,OAAO,SAAS,OAAO,KAAA;EACzB,KAAK,SACH,OAAO,SAAS,SAAS,KAAA;EAC3B,KAAK,gBACH,OAAO,SAAS,gBAAgB,KAAA;EAClC,KAAK,UACH,OAAO,SAAS,OAAO,IAAI,UAAU;EACvC,KAAK,YACH,OAAO,SAAS,SAAS,KAAK,EAAE,SAAS,EAAE;EAC7C,KAAK,SACH,OAAO,SAAS,MAAM,KAAK,SACzB,UAAU,OAAO,KAAK,KAAK,MAAM,KAAK,SAAS,GACjD;EACF,KAAK,WACH,OAAO,iBAAiB,SAAS,OAAO;EAC1C,KAAK,gBACH,OAAO,SAAS;EAClB,KAAK,oBACH,OAAO,SAAS;EAClB,KAAK,cACH,OAAO,WAAW,SAAS,UAAU;EACvC,KAAK,kBACH,OAAO,WAAW,SAAS,cAAc;EAC3C,KAAK,aACH,OAAO,SAAS,UAAU,SACtB,GAAG,SAAS,UAAU,OAAO,GAAG,SAAS,UAAU,WACnD,OAAO,SAAS,UAAU,MAAM;EACtC,SACE;CACJ;AACF;AAEA,SAAS,iBACP,SACe;CACf,QAAQ,QAAQ,MAAhB;EACE,KAAK,UACH,OAAO,QAAQ,UAAU,KAAA;EAC3B,KAAK,UACH,OAAO,QAAQ,UAAU,KAAA;EAC3B,KAAK,WACH,OAAO,QAAQ,WAAW,KAAA;EAC5B,KAAK,QACH,OAAO,QAAQ,MAAM;CACzB;AACF;AAEA,SAAS,UACP,UACQ;CACR,OAAO,SAAS,KAAK,SAAS,KAAK,UAAU,CAAC,CAAC,KAAK,EAAE;AACxD;AAEA,SAAS,WAAW,QAGT;CACT,OAAO,OAAO,QAAQ,OAAO;AAC/B;AAEA,SAAS,SAAS,OAA0C;CAC1D,OAAO,OAAO,UAAU,WAAW,QAAQ,KAAA;AAC7C;AAEA,SAAS,UAAU,OAA2C;CAC5D,OAAO,OAAO,UAAU,YAAY,QAAQ,KAAA;AAC9C;AAEA,SAAS,cAAc,OAAqD;CAC1E,IAAI,OAAO,UAAU,UAAU,OAAO,CAAC,KAAK;CAC5C,OAAO,MAAM,QAAQ,KAAK,IAAI,QAAQ,KAAA;AACxC;;;ACxIA,SAAgB,kBACd,MACA,UAA8B,CAAC,GACvB;CACR,MAAM,EAAE,aAAa,wBAAwB;CAC7C,IAAI,QAAQ,aAAa;EACvB,MAAM,WAAW,oBAAoB,MAAM,QAAQ,WAAW;EAO9D,OAAO,qBANQ,QAAQ,YAAY;GACjC;GACA;GACA;GACA,gBAAgB,QAAQ,kBAAkB;EAC5C,CACiC,CAAC;CACpC;CAEA,IAAI,QAAQ;CACZ,IAAI,cAAc;CAClB,IAAI,OAAiB,CAAC;CACtB,IAAI,UAAU;CACd,IAAI,WAAW;CAEf,KAAK,MAAM,CAAC,MAAM,aAAa,OAAO,QAAQ,KAAK,UAAU,GAAG;EAC9D,QAAQ,SAAS,MAAjB;GACE,KAAK;IACH,QAAQ,SAAS,MAAM,KAAK,SAAS,KAAK,UAAU,CAAC,CAAC,KAAK,EAAE;IAC7D;GACF,KAAK;IACH,IAAI,SAAS,QACX,OAAO,SAAS,aAAa,KAAK,QAAQ,IAAI,IAAI;IAEpD;GACF,KAAK;IACH,IAAI,SAAS,QACX,UACE,SAAS,MAAM,0BAAS,IAAI,KAAK,EAAA,CAAE,YAAY,CAAC,CAAC,MAAM,GAAG,EAAE;IAEhE;EACJ;EAEA,IAAI,SAAS,cAAc,SAAS,SAAS,YAC3C,WAAW,SAAS,WAAW,SAAS;EAE1C,IAAI,SAAS,iBAAiB,SAAS,SAAS,aAC9C,cAAc,SAAS,UACpB,KAAK,SAAS,KAAK,UAAU,CAAC,CAC9B,KAAK,EAAE;CAEd;CAEA,OACE;UACW,WAAW,WACZ,KAAK,UAAU,KAAK,EAAE,iBAChB,KAAK,UAAU,WAAW,EAAE,UACnC,KAAK,UAAU,IAAI,EAAE,aAClB,KAAK,UAAU,OAAO,EAAE,cACvB,KAAK,UAAU,QAAQ,EAAE;;AAG1C;AAEA,SAAS,qBAAqB,QAAmC;CAC/D,IACE,WAAW,QACX,OAAO,WAAW,YAClB,MAAM,QAAQ,MAAM,KACnB,OAAO,eAAe,MAAM,MAAM,OAAO,aACxC,OAAO,eAAe,MAAM,MAAM,MAEpC,MAAM,IAAI,MAAM,gDAAgD;CAGlE,MAAM,UAAU,OAAO,QAAQ,MAAM,CAAC,CAAC,QACpC,GAAG,WAAW,UAAU,KAAA,CAC3B;CACA,IAAI,QAAQ,WAAW,GAAG,OAAO;CAMjC,OAAO,QAJM,UAAU,OAAO,YAAY,OAAO,GAAG;EAClD,uBAAuB;EACvB,QAAQ;CACV,CACkB,EAAE;AACtB;;;AC9FA,MAAa,cAAc,SAAiB,KAAK,KAAK;AACtD,MAAa,kBAAkB,SAAiB,IAAI,KAAK;AACzD,MAAa,QAAQ,SAAiB,KAAK,KAAK;AAChD,MAAa,UAAU,SAAiB,IAAI,KAAK;AACjD,MAAa,iBAAiB,SAAiB,KAAK,KAAK;AACzD,MAAa,aAAa,SAAiB,MAAM,KAAK;AACtD,MAAa,QAAQ,MAAc,SAAiB,IAAI,KAAK,IAAI,KAAK;AAEtE,SAAgB,UAAU,MAAc,WAAW,IAAY;CAE7D,OAAO,SADoB,aAAa,eAAe,SAAS,SAC7B,IAAI,KAAK;AAC9C;AAEA,MAAa,YAAY,SAAiB,OAAO,KAAK;AACtD,MAAa,YAAY,SAAiB,KAAK;AAC/C,MAAa,YAAY,SAAiB,MAAM;AAChD,MAAa,YAAY,SAAiB,OAAO;AACjD,MAAa,YAAY,SAAiB,QAAQ;AAClD,MAAa,SAAS,SAAiB,KAAK,KAAK,QAAQ,OAAO,QAAQ;AAExE,SAAgB,QAAQ,MAAc,MAA4B;CAEhE,OAAO,KADO,MAAM,SAAS,UAAU,GAAG,KAAK,MAAM,KAAK,KACtC,KAAK,QAAQ,OAAO,QAAQ;AAClD;AAEA,SAAgB,OAAO,MAAc,OAAwB;CAC3D,MAAM,WAAW,KAAK,KAAK;CAC3B,OAAO,UAAU,KAAA,IAAY,KAAK,aAAa,GAAG,MAAM,IAAI;AAC9D;AAEA,MAAa,QAAQ,MAAc,YACjC,UAAU,SAAS,SAAS,SAAS;AAEvC,eAAsB,MACpB,KACA,MACA,kBAAkB,OACD;CACjB,IAAI,CAAC,mBAAmB,KAAK,WAAW,OAAO,GAAG;EAChD,IAAI,KAAK,WAAW,OAAO,GAEzB,OAAO,KAAK,IAAI,0BADD,KAAK,MAAM,GAAG,CAAC,CAAC,IACgB,EAAE;EAEnD,OAAO,KAAK,IAAI,IAAI,KAAK;CAC3B;CAEA,MAAM,WAAW,MAAM,MAAM,IAAI;CACjC,IAAI,CAAC,SAAS,IACZ,MAAM,IAAI,MACR,6BAA6B,SAAS,OAAO,GAAG,SAAS,YAC3D;CAEF,MAAM,SAAS,MAAM,SAAS,YAAY;CAC1C,OAAO,KAAK,IAAI,0BAA0B,OAAO,KAAK,MAAM,CAAC,CAAC,SAAS,QAAQ,EAAE;AACnF;AAEA,MAAa,gBAAgB;AAE7B,SAAgB,OAAO,SAAkB,UAA2B;CAClE,IAAI,CAAC,SAAS,OAAO,YAAY;CACjC,OAAO,uBAAuB,QAAQ,cAAc,YAAY,GAAG;AACrE;AAEA,SAAgB,MAAM,OAAmB,kBAAkB,MAAc;CACvE,IAAI,mBAAmB,MAAM,WAAW,GAAG,OAAO,cAAc,KAAK;CAErE,MAAM,QAAQ,KAAK,IAAI,GAAG,MAAM,KAAK,QAAQ,IAAI,MAAM,CAAC;CACxD,IAAI,UAAU,GAAG,OAAO;CAExB,OAAO,cAAc,CADD,MAAM,KAAK,EAAE,QAAQ,MAAM,SAAS,EACxB,GAAG,GAAG,KAAK,CAAC;AAC9C;;;ACzDA,MAAM,qBAAkD;CACtD;EAAE,MAAM;EAAiB,MAAM;EAAM,OAAO;CAAK;CACjD;EAAE,MAAM;EAAa,MAAM;EAAO,OAAO;CAAO;CAChD;EAAE,MAAM;EAAU,MAAM;EAAK,OAAO;CAAI;CACxC;EAAE,MAAM;EAAQ,MAAM;EAAM,OAAO;CAAK;CACxC;EAAE,MAAM;EAAQ,MAAM;EAAK,OAAO;CAAI;AACxC;AAEA,SAAgB,gBACd,UACQ;CACR,OAAO,SAAS,KAAK,SAAS,KAAK,UAAU,CAAC,CAAC,KAAK,EAAE;AACxD;AAEA,SAAgB,eACd,UACQ;CACR,IAAI,WAAW;CACf,IAAI,SAA6B,CAAC;CAElC,KAAK,MAAM,CAAC,OAAO,SAAS,SAAS,QAAQ,GAAG;EAC9C,MAAM,UAAU,qBAAqB,UAAU,OAAO,IAAI;EAC1D,IAAI,SAAS;EACb,OACE,SAAS,OAAO,UAChB,SAAS,QAAQ,UACjB,OAAO,OAAO,EAAE,SAAS,QAAQ,OAAO,EAAE,MAE1C;EAGF,KAAK,MAAM,UAAU,OAAO,MAAM,MAAM,CAAC,CAAC,WAAW,GACnD,YAAY,OAAO;EAErB,KAAK,MAAM,UAAU,QAAQ,MAAM,MAAM,GACvC,YAAY,OAAO;EAGrB,YAAY,cAAc,IAAI;EAC9B,SAAS;CACX;CAEA,KAAK,MAAM,UAAU,OAAO,WAAW,GACrC,YAAY,OAAO;CAErB,OAAO;AACT;AAEA,SAAS,qBACP,UACA,OACA,MACoB;CACpB,IAAI,KAAK,SAAS,YAAY,OAAO,CAAC;CAEtC,OAAO,mBAAmB,QAAQ,WAAW,KAAK,YAAY,OAAO,KAAK,CAAC,CACxE,KAAK,YAAY;EAChB;EACA,QAAQ,oBAAoB,UAAU,OAAO,OAAO,IAAI;CAC1D,EAAE,CAAC,CACF,MAAM,MAAM,UAAU,MAAM,SAAS,KAAK,MAAM,CAAC,CACjD,KAAK,EAAE,aAAa,MAAM;AAC/B;AAEA,SAAS,oBACP,UACA,OACA,MACQ;CACR,IAAI,MAAM;CACV,KAAK,MAAM,CAAC,QAAQ,SAAS,SAAS,MAAM,QAAQ,CAAC,CAAC,CAAC,QAAQ,GAAG;EAChE,IAAI,KAAK,SAAS,cAAc,CAAC,KAAK,YAAY,OAAO;EACzD,MAAM,QAAQ,SAAS;CACzB;CACA,OAAO;AACT;AAEA,SAAS,cAAc,MAAoC;CACzD,IAAI,KAAK,SAAS,YAChB,OAAO,eAAe,KAAK,SAAS,UAAU;CAEhD,OAAO,KAAK,OAAO,KAAK,KAAK,YAAY,KAAK,IAAI,IAAI,KAAK;AAC7D;;;ACtEA,eAAsB,cACpB,SACA,QACA,4BAAiC,IAAI,IAAI,GACnB;CACtB,IAAI,UAAU,IAAI,OAAO,GAAG,OAAO,CAAC;CAEpC,MAAM,SAAS,MAAM,OAAO,iBAAiB,OAAO;CACpD,MAAM,iBAAiB,IAAI,IAAI,SAAS,CAAC,CAAC,IAAI,OAAO;CACrD,MAAM,QAAqB,CAAC;CAE5B,KAAK,MAAM,SAAS,QAAQ;EAC1B,IAAI,MAAM,SAAS,eAAe;EAElC,MAAM,gBAAgB,iBAAiB,KAAK;EAC5C,MAAM,WACJ,iBAAiB,CAAC,eAAe,IAAI,aAAa,IAC9C,MAAM,cAAc,eAAe,QAAQ,cAAc,IACzD,CAAC;EACP,MAAM,KAAK;GAAE;GAAO;EAAS,CAAC;CAChC;CAEA,OAAO;AACT;AAEA,SAAS,iBAAiB,OAAwC;CAChE,IAAI,MAAM,SAAS,gBAAgB,MAAM,SAAS,kBAChD;CAEF,IAAI,MAAM,SAAS,gBACjB,OACE,MAAM,aAAa,aAAa,aAC/B,MAAM,eAAe,MAAM,KAAK,KAAA;CAGrC,OAAO,MAAM,eAAe,MAAM,KAAK,KAAA;AACzC;AAEA,eAAsB,gBACpB,OACA,SACyB;CACzB,MAAM,SAAyB;EAAE,SAAS;EAAI,cAAc,CAAC;CAAE;CAC/D,IAAI,oBAAoB;CAExB,KAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,EAAE,UAAU;EAClB,oBACE,MAAM,SAAS,uBAAuB,oBAAoB,IAAI;EAEhE,MAAM,WACJ,MAAM,SAAS,UACX;GAAE,SAAS;GAAI,cAAc,CAAC;EAAE,IAChC,MAAM,gBAAgB,KAAK,UAAU,OAAO;EAClD,MAAM,UAAU,MAAM,gBACpB,MACA,SAAS,SACT,qBAAqB,KAAA,GACrB,OACF;EAEA,IAAI,MAAM,SAAS,UACjB,OAAO,WAAW;OACb;GACL,MAAM,WAAW,YAAY,KAAK,IAAI,KAAK;GAC3C,OAAO,WAAW,GAAG,WAAW,QAAQ,IAAI;GAC5C,IAAI,CAAC,oBAAoB,KAAK,GAC5B,OAAO,WAAW,SAAS;EAE/B;EAEA,IAAI,MAAM,SAAS,cACjB,OAAO,aAAa,KAAK,MAAM,EAAE;OAEjC,OAAO,aAAa,KAAK,GAAG,SAAS,YAAY;CAErD;CAEA,OAAO;AACT;AAEA,SAAS,YAAY,OAA6B;CAChD,OACE,MAAM,SAAS,WACf,MAAM,SAAS,wBACf,MAAM,SAAS;AAEnB;AAEA,SAAS,oBAAoB,OAA6B;CACxD,OACE,MAAM,SAAS,gBACf,MAAM,SAAS,aACf,MAAM,SAAS,wBACf,MAAM,SAAS,wBACf,MAAM,SAAS;AAEnB;AAEA,SAAS,eACP,MACA,OACA,SACkD;CAClD,MAAM,cAAc,QAAQ,eAAe;CAC3C,OAAO,cAAc,OAAO,OAAO;AACrC;AAEA,eAAe,gBACb,MACA,UACA,YACA,SACiB;CACjB,MAAM,EAAE,UAAU;CAElB,MAAM,UACJ,MAFwB,eAAe,MAAM,MAAM,OAAO,OAAO,KAGhE,MAAM,mBACL,MACA,QAAQ,mBAAA,KACR,QAAQ,gBACV;CAEF,QAAQ,MAAM,MAAd;EACE,KAAK,UACH,OAAOA,OAAU,SAAS,QAAQ;EACpC,KAAK,WACH,OAAOC,QACL,WAAW,GAAG,QAAQ,IAAI,WAAW,KAAK,IAAI,SAC9C,MAAM,QAAQ,IAChB;EACF,KAAK,sBACH,OAAOC,OAAU,qBAAqB,SAAS,UAAU,CAAC,CAAC;EAC7D,KAAK,sBACH,OAAOA,OACL,qBACE,SACA,UACA,GAAG,cAAc,EAAE,IAAI,MACzB,GACA,UACF;EACF,SACE,OAAO;CACX;AACF;AAEA,SAAS,qBACP,SACA,UACA,aACQ;CACR,MAAM,SAAS,SAAS,KAAK;CAC7B,IAAI,CAAC,QAAQ,OAAO;CAEpB,MAAM,cAAc,IAAI,OAAO,WAAW;CAK1C,OAAO,GAAG,QAAQ,IAJD,OACd,MAAM,IAAI,CAAC,CACX,KAAK,SAAU,OAAO,GAAG,cAAc,SAAS,EAAG,CAAC,CACpD,KAAK,IACqB;AAC/B;AAEA,eAAe,mBACb,MACA,iBACA,kBACiB;CACjB,MAAM,EAAE,UAAU;CAElB,QAAQ,MAAM,MAAd;EACE,KAAK,SAAS;GACZ,MAAM,MAAM,WAAW,MAAM,KAAK;GAGlC,OAAOC,MAFS,gBAAgB,MAAM,MAAM,OAC1B,CAAC,CAAC,KAAK,KAAK,YAAY,GAAG,KAAK,SAC7B,GAAG;EAC1B;EACA,KAAK,WACH,OAAOC,QAAW;EACpB,KAAK,YACH,OAAOC,SAAY,MAAM,SAAS,UAAU;EAC9C,KAAK,OACH,OAAO,eAAe,MAAM,KAAK,MAAM,IAAI;EAC7C,KAAK,QACH,OAAO,eAAe,MAAM,MAAM,MAAM,IAAI;EAC9C,KAAK,SACH,OAAO,eAAe,MAAM,OAAO,MAAM,IAAI;EAC/C,KAAK,SACH,OAAO,eAAe,MAAM,OAAO,MAAM,IAAI;EAC/C,KAAK,SACH,OAAOC,KAAQ,MAAM,MAAM,MAAM,MAAM,GAAG;EAC5C,KAAK,YACH,OAAOA,KAAQ,MAAM,MAAM,MAAM,SAAS,GAAG;EAC/C,KAAK,gBACH,OAAOA,KAAQ,MAAM,MAAM,MAAM,aAAa,GAAG;EACnD,KAAK,gBAAgB;GACnB,MAAM,SAAS,MAAM;GACrB,IAAI,OAAO,SAAS,aAAa,OAAO,SAAS,eAC/C,OAAO;GAET,MAAM,KAAK,OAAO,SAAS,YAAY,OAAO,UAAU,OAAO;GAC/D,OAAOA,KAAQ,MAAM,MAAM,yBAAyB,IAAI;EAC1D;EACA,KAAK,cACH,OAAO,oBAAoB,OAAO,iBAAiB,gBAAgB;EACrE,KAAK,kBACH,OAAOC,SAAY,MAAM,eAAe,SAAS,gBAAgB;EACnE,KAAK,SACH,OAAO,YAAY,KAAK,UAAU,MAAM,MAAM,iBAAiB;EACjE,KAAK,QACH,OAAOC,UACL,eAAe,MAAM,KAAK,SAAS,GACnC,MAAM,KAAK,QACb;EACF,KAAK,aACH,OAAOC,SAAY,eAAe,MAAM,UAAU,SAAS,CAAC;EAC9D,KAAK,aACH,OAAOF,SAAY,eAAe,MAAM,UAAU,SAAS,CAAC;EAC9D,KAAK,aACH,OAAOG,SAAY,eAAe,MAAM,UAAU,SAAS,CAAC;EAC9D,KAAK,aACH,OAAOC,SAAY,eAAe,MAAM,UAAU,SAAS,CAAC;EAC9D,KAAK,SACH,OAAOC,MAAS,eAAe,MAAM,MAAM,SAAS,CAAC;EACvD,KAAK,SACH,OAAOC,KACL,eAAe,MAAM,MAAM,SAAS,GACpC,MAAM,MAAM,OACd;EACF,KAAK,aACH,OAAO,eAAe,MAAM,UAAU,SAAS;EACjD,KAAK,UACH,OAAO,eAAe,MAAM,OAAO,SAAS;EAC9C,KAAK,WACH,OAAO,eAAe,MAAM,QAAQ,SAAS;EAC/C,KAAK,sBACH,OAAO,eAAe,MAAM,mBAAmB,SAAS;EAC1D,KAAK,sBACH,OAAO,eAAe,MAAM,mBAAmB,SAAS;EAC1D,KAAK,YACH,OAAO,eAAe,MAAM,SAAS,SAAS;EAChD,SACE,OAAO;CACX;AACF;AASA,SAAS,WAAW,SAA8B;CAChD,IAAI,QAAQ,SAAS,YAAY,OAAO,QAAQ,SAAS;CACzD,OAAO,QAAQ,KAAK;AACtB;AAEA,SAAS,YAAY,KAAiC;CACpD,OAAO,IAAI,MAAM,oCAAoC,CAAC,GAAG;AAC3D;AAEA,SAAS,eAAe,SAAsB,eAA+B;CAC3E,MAAM,MAAM,WAAW,OAAO;CAE9B,MAAM,QADU,gBAAgB,QAAQ,OACpB,CAAC,CAAC,KAAK,KAAK,YAAY,GAAG,KAAK;CACpD,OAAOP,KAAQ,OAAO,GAAG;AAC3B;AAEA,SAAS,oBACP,OACA,QACA,SACQ;CAER,MAAMQ,SAAOR,KAAQ,MAAM,WAAW,OAAO,KAAK,MAAM,GAAG,IAAI;CAC/D,OAAO,YAAY,KAAA,IACf,GAAG,OAAO,GAAGQ,WACb,GAAG,QAAQ,OAAOA,SAAO,QAAQ;AACvC;AAEA,SAAS,YACP,OACA,iBACQ;CAOR,OAAOC,MANM,MAAM,SAAS,EAAE,YAAY;EACxC,IAAI,MAAM,SAAS,aAAa,OAAO,CAAC;EACxC,OAAO,CACL,MAAM,UAAU,MAAM,KAAK,SAAS,eAAe,IAAI,CAAC,CAC1D;CACF,CACgB,GAAM,eAAe;AACvC;;;ACnSA,eAAsB,WACpB,QACA,QACA,UAAyB,CAAC,GACH;CAEvB,OAAO,YAAY,QADH,cAAc,CAAC,MAAM,GAAG,QAAQ,OACf,CAAC;AACpC;AAEA,eAAsB,YACpB,OACA,QACA,UAAyB,CAAC,GACD;CACzB,MAAM,UAAU,cACd,MAAM,KAAK,SAAS,KAAK,EAAE,GAC3B,QACA,OACF;CAEA,KAAK,MAAM,QAAQ,OACjB,MAAM,YAAY,KAAK,IAAI,SAAS,IAAI;CAG1C,OAAO,QAAQ,IAAI,CAAC,GAAG,QAAQ,cAAc,OAAO,CAAC,CAAC;AACxD;AAEA,SAAS,cACP,iBACA,QACA,SACmB;CACnB,OAAO;EACL;EACA,iBAAiB,IAAI,IAAI,eAAe;EACxC,YAAY,QAAQ,cAAA;EACpB,aAAa,QAAQ;EACrB,aAAa,QAAQ;EACrB,WAAW,QAAQ,aAAa;EAChC,iBAAiB,QAAQ,mBAAA;EACzB,kBAAkB,QAAQ;EAC1B,cAAc,QAAQ;EACtB,+BAAe,IAAI,IAAI;CACzB;AACF;AAEA,eAAe,YACb,QACA,SACA,MACuB;CACvB,MAAM,SAAS,QAAQ,cAAc,IAAI,MAAM;CAC/C,IAAI,QAAQ,OAAO;CAEnB,MAAM,YAAY,iBAAiB,QAAQ,SAAS,IAAI;CACxD,QAAQ,cAAc,IAAI,QAAQ,SAAS;CAC3C,MAAM,WAAW,MAAM;CAEvB,IAAI,QAAQ,WACV,KAAK,MAAM,eAAe,SAAS,cACjC,MAAM,YAAY,aAAa,OAAO;CAI1C,OAAO;AACT;AAEA,eAAe,iBACb,QACA,SACA,MACuB;CACvB,MAAM,CAAC,MAAM,aAAa,MAAM,QAAQ,IAAI,CAC1C,OAAO,QAAQ,QAAQ,IAAI,IAAI,QAAQ,OAAO,QAAQ,MAAM,GAC5D,cAAc,QAAQ,QAAQ,MAAM,CACtC,CAAC;CACD,MAAM,WAAW,MAAM,gBAAgB,WAAW;EAChD;EACA,iBAAiB,QAAQ;EACzB,GAAI,QAAQ,qBAAqB,KAAA,IAC7B,EAAE,kBAAkB,QAAQ,iBAAiB,IAC7C,CAAC;EACL,GAAI,QAAQ,iBAAiB,KAAA,IACzB,EAAE,cAAc,QAAQ,aAAa,IACrC,CAAC;CACP,CAAC;CAYD,OAAO;EACL,IAAI;EACJ,SAbkB,kBAAkB,MAAM;GAC1C,YAAY,QAAQ;GACpB,GAAI,QAAQ,gBAAgB,KAAA,IACxB,EAAE,aAAa,QAAQ,YAAY,IACnC,CAAC;GACL,GAAI,QAAQ,gBAAgB,KAAA,IACxB,EAAE,aAAa,QAAQ,YAAY,IACnC,CAAC;GACL,gBAAgB,QAAQ,gBAAgB,IAAI,MAAM;EACpD,CAIqB,IAAI,SAAS;EAChC,cAAc,CAAC,GAAG,IAAI,IAAI,SAAS,YAAY,CAAC;EAChD,gBAAgB,QAAQ,gBAAgB,IAAI,MAAM;CACpD;AACF;;;AC9GA,eAAsB,WACpB,OACA,UAA6B,CAAC,GACX;CACnB,MAAM,WAAW,QAAQ,YAAA;CACzB,MAAM,YAAY,QAAQ,aAAA;CAC1B,MAAM,eAAyB,CAAC;CAChC,MAAM,MAAM,WAAW,EAAE,WAAW,KAAK,CAAC;CAE1C,KAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,aAAa,KAAK,WAAW,GAAG,KAAK,GAAG,IAAI;EAClD,MAAM,cAAc,MAAM,SAAS,UAAU;EAC7C,IAAI,eAAe,CAAC,YAAY,OAAO,GACrC,MAAM,IAAI,MAAM,0CAA0C,YAAY;EAExE,MAAM,UAAU,YAAY,KAAK,SAAS,MAAM;EAEhD,IAAI,CAAC,KAAK,gBAAgB;EAE1B,MAAM,WAAW,KAAK,UAAU,GAAG,KAAK,GAAG,IAAI;EAC/C,MAAM,MAAM,UAAU,EAAE,WAAW,KAAK,CAAC;EACzC,MAAM,eAAe,YAAY,QAAQ;EACzC,aAAa,KAAK,QAAQ;CAC5B;CAEA,OAAO;AACT;AAEA,SAAgB,uBACd,UAA8B,CAAC,GACvB;CACR,MAAM,WAAW,QAAQ,YAAA;CACzB,MAAM,YAAY,QAAQ,aAAA;CAE1B,MAAM,eAAe,QAAQ,UADV,QAAQ,cAAA,wCACsB;CACjD,OAAO,SAAS,QAAQ,SAAS,GAAG,YAAY,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,KAAK,GAAG;AACvE;AAEA,eAAe,eACb,YACA,UACe;CACf,IAAI,QAAQ,UAAU,MAAM,QAAQ,QAAQ,GAAG;CAE/C,MAAM,YAAY,MAAM,SAAS,QAAQ;CACzC,IAAI,aAAa,CAAC,UAAU,OAAO,KAAK,CAAC,UAAU,eAAe,GAChE,MAAM,IAAI,MAAM,wCAAwC,UAAU;CAGpE,MAAM,SAAS,SAAS,QAAQ,QAAQ,GAAG,UAAU;CACrD,IAAI,WAAW,eAAe,KAAM,MAAM,SAAS,QAAQ,MAAO,QAChE;CAGF,MAAM,gBAAgB,GAAG,SAAS,UAAU,WAAW,EAAE;CACzD,IAAI;EACF,MAAM,QAAQ,QAAQ,eAAe,MAAM;EAC3C,MAAM,OAAO,eAAe,QAAQ;CACtC,SAAS,OAAO;EACd,MAAM,GAAG,eAAe,EAAE,OAAO,KAAK,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC;EACvD,MAAM;CACR;AACF;AAEA,eAAe,SAAS,MAAc;CACpC,IAAI;EACF,OAAO,MAAM,MAAM,IAAI;CACzB,SAAS,OAAgB;EACvB,IAAI,aAAa,KAAK,MAAM,UAAU,OAAO,KAAA;EAC7C,MAAM;CACR;AACF;;;ACxEA,SAAgB,mBACd,QACc;CACd,MAAM,SAAS,IAAI,OAAO,EAAE,MAAM,OAAO,gBAAgB,CAAC;CAC1D,OAAO;EACL,iBAAiB,UAAU,QAAQ,OAAO,UAAU;EACpD,UAAU,WAAW,QAAQ,QAAQ,MAAM;EAC3C,mBAAmB,YAAY,iBAAiB,QAAQ,OAAO;CACjE;AACF;AAEA,eAAe,UACb,QACA,YAC+B;CAC/B,MAAM,WAAW,MAAM,gBACrB,OAAO,UAAU,SAAS,EAAE,aAAa,WAAW,CAAC,CACvD;CACA,IAAI,CAAC,eAAe,QAAQ,GAC1B,MAAM,IAAI,MAAM,mBAAmB,WAAW,wBAAwB;CAGxE,MAAM,QAA8B,CAAC;CACrC,KAAK,MAAM,UAAU,SAAS,cAAc;EAC1C,IAAI;EACJ,GAAG;GACD,MAAM,WAAW,MAAM,gBACrB,OAAO,YAAY,MAAM;IACvB,gBAAgB,OAAO;IACvB,QAAQ;KACN,UAAU;KACV,UAAU,EAAE,QAAQ,KAAK;IAC3B;IACA,OAAO,CAAC;KAAE,UAAU;KAAQ,WAAW;IAAa,CAAC;IACrD,WAAW;IACX,GAAI,gBAAgB,KAAA,IAChB,EAAE,cAAc,YAAY,IAC5B,CAAC;GACP,CAAC,CACH;GACA,MAAM,KAAK,GAAG,SAAS,QAAQ,OAAO,UAAU,CAAC;GACjD,cAAc,SAAS,eAAe,KAAA;GACtC,IAAI,CAAC,SAAS,UAAU;EAC1B,SAAS;CACX;CAEA,OAAO;AACT;AAEA,eAAe,QACb,QACA,QAC6B;CAC7B,MAAM,OAAO,MAAM,gBAAgB,OAAO,MAAM,SAAS,EAAE,SAAS,OAAO,CAAC,CAAC;CAC7E,IAAI,CAAC,WAAW,IAAI,GAClB,MAAM,IAAI,MAAM,eAAe,OAAO,oBAAoB;CAE5D,OAAO;AACT;AAEA,eAAe,iBACb,QACA,SACgC;CAChC,MAAM,SAAgC,CAAC;CACvC,IAAI;CAEJ,GAAG;EACD,MAAM,WAAW,MAAM,gBACrB,OAAO,OAAO,SAAS,KAAK;GAC1B,UAAU;GACV,WAAW;GACX,GAAI,gBAAgB,KAAA,IAAY,EAAE,cAAc,YAAY,IAAI,CAAC;EACnE,CAAC,CACH;EACA,OAAO,KAAK,GAAG,SAAS,QAAQ,OAAO,WAAW,CAAC;EACnD,cAAc,SAAS,eAAe,KAAA;CACxC,SAAS;CAET,OAAO;AACT;AAEA,MAAM,eAAe;AACrB,MAAM,yBAAyB;AAE/B,eAAe,UAAa,WAAyC;CACnE,KAAK,IAAI,UAAU,IAAK,WACtB,IAAI;EACF,OAAO,MAAM,UAAU;CACzB,SAAS,OAAgB;EACvB,IAAI,YAAY,eAAe,KAAK,CAAC,YAAY,KAAK,GAAG,MAAM;EAC/D,MAAM,MAAM,yBAAyB,KAAK,OAAO;CACnD;AAEJ;AAEA,SAAS,YAAY,OAAyB;CAC5C,IAAI,CAAC,oBAAoB,KAAK,KAAK,EAAE,YAAY,QAAQ,OAAO;CAChE,OAAO,MAAM,WAAW,OAAO,MAAM,WAAW,OAAO,MAAM,UAAU;AACzE;AAEA,SAAS,MAAM,cAAqC;CAClD,OAAO,IAAI,SAAS,YAAY,WAAW,SAAS,YAAY,CAAC;AACnE"}
@@ -1,4 +1,4 @@
1
- import { t as loadNoblogConfig } from "./config-file-BF0G2iJq.js";
1
+ import { t as loadNoblogConfig } from "./config-file-CZX2WJnz.js";
2
2
  //#region src/commands/config-check.impl.ts
3
3
  async function checkConfig(flags) {
4
4
  try {
@@ -11,4 +11,4 @@ async function checkConfig(flags) {
11
11
  //#endregion
12
12
  export { checkConfig as default };
13
13
 
14
- //# sourceMappingURL=config-check.impl-BQvIrgNe.js.map
14
+ //# sourceMappingURL=config-check.impl-NO--hbh0.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"config-check.impl-BQvIrgNe.js","names":[],"sources":["../src/commands/config-check.impl.ts"],"sourcesContent":["import { loadNoblogConfig } from \"../config-file.js\";\nimport type { CliContext } from \"../context.js\";\n\ninterface ConfigCheckFlags {\n config: string;\n}\n\nexport default async function checkConfig(\n this: CliContext,\n flags: ConfigCheckFlags,\n): Promise<Error | void> {\n try {\n await loadNoblogConfig(flags.config);\n this.process.stdout.write(`Configuration valid: ${flags.config}\\n`);\n } catch (error) {\n return error instanceof Error\n ? error\n : new Error(\"Configuration check failed: an unknown error occurred\");\n }\n}\n"],"mappings":";;AAOA,eAA8B,YAE5B,OACuB;CACvB,IAAI;EACF,MAAM,iBAAiB,MAAM,MAAM;EACnC,KAAK,QAAQ,OAAO,MAAM,wBAAwB,MAAM,OAAO,GAAG;CACpE,SAAS,OAAO;EACd,OAAO,iBAAiB,QACpB,wBACA,IAAI,MAAM,uDAAuD;CACvE;AACF"}
1
+ {"version":3,"file":"config-check.impl-NO--hbh0.js","names":[],"sources":["../src/commands/config-check.impl.ts"],"sourcesContent":["import { loadNoblogConfig } from \"../config-file.js\";\nimport type { CliContext } from \"../context.js\";\n\ninterface ConfigCheckFlags {\n config: string;\n}\n\nexport default async function checkConfig(\n this: CliContext,\n flags: ConfigCheckFlags,\n): Promise<Error | void> {\n try {\n await loadNoblogConfig(flags.config);\n this.process.stdout.write(`Configuration valid: ${flags.config}\\n`);\n } catch (error) {\n return error instanceof Error\n ? error\n : new Error(\"Configuration check failed: an unknown error occurred\");\n }\n}\n"],"mappings":";;AAOA,eAA8B,YAE5B,OACuB;CACvB,IAAI;EACF,MAAM,iBAAiB,MAAM,MAAM;EACnC,KAAK,QAAQ,OAAO,MAAM,wBAAwB,MAAM,OAAO,GAAG;CACpE,SAAS,OAAO;EACd,OAAO,iBAAiB,QACpB,wBACA,IAAI,MAAM,uDAAuD;CACvE;AACF"}
@@ -1,5 +1,5 @@
1
1
  import { t as ConfigurationError } from "./config-csbquMhF.js";
2
- import { a as noblogConfigSchema, t as getErrorCode } from "./errors-DgJ15MsB.js";
2
+ import { a as noblogConfigSchema, t as getErrorCode } from "./errors-Bm4iVQVp.js";
3
3
  import { z } from "zod";
4
4
  import { parse } from "yaml";
5
5
  import { readFile } from "node:fs/promises";
@@ -24,4 +24,4 @@ async function loadNoblogConfig(path) {
24
24
  //#endregion
25
25
  export { loadNoblogConfig as t };
26
26
 
27
- //# sourceMappingURL=config-file-BF0G2iJq.js.map
27
+ //# sourceMappingURL=config-file-CZX2WJnz.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"config-file-BF0G2iJq.js","names":[],"sources":["../src/config-file.ts"],"sourcesContent":["import { readFile } from \"node:fs/promises\";\nimport { parse } from \"yaml\";\nimport { z } from \"zod\";\nimport { ConfigurationError } from \"./config.js\";\nimport { getErrorCode } from \"./lib/errors.js\";\nimport {\n noblogConfigSchema,\n type NoblogFileConfig,\n} from \"./lib/noblog/config.js\";\n\nexport async function loadNoblogConfig(\n path: string,\n): Promise<NoblogFileConfig> {\n let source: string;\n try {\n source = await readFile(path, \"utf8\");\n } catch (error) {\n const detail =\n getErrorCode(error) === \"ENOENT\"\n ? \"file not found\"\n : error instanceof Error\n ? error.message\n : \"unknown read error\";\n throw new ConfigurationError(\n `Cannot read Noblog config \"${path}\": ${detail}`,\n );\n }\n\n let input: unknown;\n try {\n input = parse(source, { prettyErrors: false });\n } catch (error) {\n throw new ConfigurationError(\n `Invalid YAML in Noblog config \"${path}\": ${\n error instanceof Error ? error.message : \"unknown parse error\"\n }`,\n );\n }\n\n const result = noblogConfigSchema.safeParse(input);\n if (!result.success) {\n throw new ConfigurationError(\n `Invalid Noblog config \"${path}\":\\n${z.prettifyError(result.error)}`,\n );\n }\n return result.data;\n}\n"],"mappings":";;;;;;AAUA,eAAsB,iBACpB,MAC2B;CAC3B,IAAI;CACJ,IAAI;EACF,SAAS,MAAM,SAAS,MAAM,MAAM;CACtC,SAAS,OAAO;EAOd,MAAM,IAAI,mBACR,8BAA8B,KAAK,KANnC,aAAa,KAAK,MAAM,WACpB,mBACA,iBAAiB,QACf,MAAM,UACN,sBAGR;CACF;CAEA,IAAI;CACJ,IAAI;EACF,QAAQ,MAAM,QAAQ,EAAE,cAAc,MAAM,CAAC;CAC/C,SAAS,OAAO;EACd,MAAM,IAAI,mBACR,kCAAkC,KAAK,KACrC,iBAAiB,QAAQ,MAAM,UAAU,uBAE7C;CACF;CAEA,MAAM,SAAS,mBAAmB,UAAU,KAAK;CACjD,IAAI,CAAC,OAAO,SACV,MAAM,IAAI,mBACR,0BAA0B,KAAK,MAAM,EAAE,cAAc,OAAO,KAAK,GACnE;CAEF,OAAO,OAAO;AAChB"}
1
+ {"version":3,"file":"config-file-CZX2WJnz.js","names":[],"sources":["../src/config-file.ts"],"sourcesContent":["import { readFile } from \"node:fs/promises\";\nimport { parse } from \"yaml\";\nimport { z } from \"zod\";\nimport { ConfigurationError } from \"./config.js\";\nimport { getErrorCode } from \"./lib/errors.js\";\nimport {\n noblogConfigSchema,\n type NoblogFileConfig,\n} from \"./lib/noblog/config.js\";\n\nexport async function loadNoblogConfig(\n path: string,\n): Promise<NoblogFileConfig> {\n let source: string;\n try {\n source = await readFile(path, \"utf8\");\n } catch (error) {\n const detail =\n getErrorCode(error) === \"ENOENT\"\n ? \"file not found\"\n : error instanceof Error\n ? error.message\n : \"unknown read error\";\n throw new ConfigurationError(\n `Cannot read Noblog config \"${path}\": ${detail}`,\n );\n }\n\n let input: unknown;\n try {\n input = parse(source, { prettyErrors: false });\n } catch (error) {\n throw new ConfigurationError(\n `Invalid YAML in Noblog config \"${path}\": ${\n error instanceof Error ? error.message : \"unknown parse error\"\n }`,\n );\n }\n\n const result = noblogConfigSchema.safeParse(input);\n if (!result.success) {\n throw new ConfigurationError(\n `Invalid Noblog config \"${path}\":\\n${z.prettifyError(result.error)}`,\n );\n }\n return result.data;\n}\n"],"mappings":";;;;;;AAUA,eAAsB,iBACpB,MAC2B;CAC3B,IAAI;CACJ,IAAI;EACF,SAAS,MAAM,SAAS,MAAM,MAAM;CACtC,SAAS,OAAO;EAOd,MAAM,IAAI,mBACR,8BAA8B,KAAK,KANnC,aAAa,KAAK,MAAM,WACpB,mBACA,iBAAiB,QACf,MAAM,UACN,sBAGR;CACF;CAEA,IAAI;CACJ,IAAI;EACF,QAAQ,MAAM,QAAQ,EAAE,cAAc,MAAM,CAAC;CAC/C,SAAS,OAAO;EACd,MAAM,IAAI,mBACR,kCAAkC,KAAK,KACrC,iBAAiB,QAAQ,MAAM,UAAU,uBAE7C;CACF;CAEA,MAAM,SAAS,mBAAmB,UAAU,KAAK;CACjD,IAAI,CAAC,OAAO,SACV,MAAM,IAAI,mBACR,0BAA0B,KAAK,MAAM,EAAE,cAAc,OAAO,KAAK,GACnE;CAEF,OAAO,OAAO;AAChB"}
@@ -0,0 +1,10 @@
1
+ //#region src/lib/noblog/defaults.ts
2
+ const DEFAULT_SAVE_PATH = "./src/pages/posts/";
3
+ const DEFAULT_STORE_PATH = ".noblog/pages/";
4
+ const DEFAULT_LAYOUT_PATH = "../../layouts/MarkdownPostLayout.astro";
5
+ /** U+2E0E EDITORIAL CORONIS, a rare marker for theme child-page links. */
6
+ const DEFAULT_CHILD_PAGE_MARKER = "⸎";
7
+ //#endregion
8
+ export { DEFAULT_STORE_PATH as i, DEFAULT_LAYOUT_PATH as n, DEFAULT_SAVE_PATH as r, DEFAULT_CHILD_PAGE_MARKER as t };
9
+
10
+ //# sourceMappingURL=defaults-RGOB9tfi.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"defaults-RGOB9tfi.js","names":[],"sources":["../src/lib/noblog/defaults.ts"],"sourcesContent":["export const DEFAULT_SAVE_PATH = \"./src/pages/posts/\";\nexport const DEFAULT_STORE_PATH = \".noblog/pages/\";\nexport const DEFAULT_LAYOUT_PATH = \"../../layouts/MarkdownPostLayout.astro\";\n\n/** U+2E0E EDITORIAL CORONIS, a rare marker for theme child-page links. */\nexport const DEFAULT_CHILD_PAGE_MARKER = \"⸎\";\n"],"mappings":";AAAA,MAAa,oBAAoB;AACjC,MAAa,qBAAqB;AAClC,MAAa,sBAAsB;;AAGnC,MAAa,4BAA4B"}
@@ -23,6 +23,10 @@ const frontmatterFieldSchema = z.union([z.strictObject({
23
23
  })]);
24
24
  const frontmatterFieldsSchema = z.record(z.string().min(1), frontmatterFieldSchema);
25
25
  const notionPropertiesSchema = z.record(z.string().min(1), z.string().min(1));
26
+ const childPageWrapperSchema = z.strictObject({
27
+ head: z.string().min(1).regex(/^[^\r\n]+$/, "must be a single line").describe("Opening wrapper emitted before the child-page Markdown link."),
28
+ tail: z.string().min(1).regex(/^[^\r\n]+$/, "must be a single line").describe("Closing wrapper emitted after the child-page Markdown link.")
29
+ });
26
30
  const BUILT_IN_FRONTMATTER_SOURCES = [
27
31
  "title",
28
32
  "description",
@@ -41,7 +45,9 @@ const noblogConfigSchema = z.strictObject({
41
45
  savePath: z.string().min(1).optional().describe("Public directory for database-post symlinks."),
42
46
  storePath: z.string().min(1).optional().describe("Flat directory containing every canonical Markdown file."),
43
47
  layoutPath: z.string().min(1).optional().describe("Layout import path interpreted relative to savePath."),
44
- recursive: z.boolean().optional().describe("Render child pages recursively (default: true).")
48
+ recursive: z.boolean().optional().describe("Render child pages recursively (default: true)."),
49
+ childPageMarker: z.string().min(1).optional().describe("Marker prefixed to child-page links (default: ⸎, U+2E0E)."),
50
+ childPageWrapper: childPageWrapperSchema.optional().describe("Optional wrapper around child-page Markdown links; overrides childPageMarker.")
45
51
  }).optional(),
46
52
  frontmatter: z.strictObject({ fields: frontmatterFieldsSchema.optional() }).optional()
47
53
  }).superRefine((config, context) => {
@@ -69,4 +75,4 @@ function getErrorCode(error) {
69
75
  //#endregion
70
76
  export { noblogConfigSchema as a, frontmatterFieldsSchema as i, BUILT_IN_FRONTMATTER_SOURCES as n, notionPropertiesSchema as o, frontmatterFieldSchema as r, getErrorCode as t };
71
77
 
72
- //# sourceMappingURL=errors-DgJ15MsB.js.map
78
+ //# sourceMappingURL=errors-Bm4iVQVp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors-Bm4iVQVp.js","names":[],"sources":["../src/lib/noblog/config.ts","../src/lib/errors.ts"],"sourcesContent":["import { z } from \"zod\";\n\nexport type ConfigValue =\n | string\n | number\n | boolean\n | null\n | ConfigValue[]\n | { [key: string]: ConfigValue };\n\nconst configValueSchema: z.ZodType<ConfigValue> = z.lazy(() =>\n z.union([\n z.string(),\n z.number().finite(),\n z.boolean(),\n z.null(),\n z.array(configValueSchema),\n z.record(z.string(), configValueSchema),\n ]),\n);\n\nconst fieldModifiers = {\n as: z\n .enum([\"string\", \"date\"])\n .optional()\n .describe(\n \"Convert to a string (quoting date-like strings) or a YAML date.\",\n ),\n default: configValueSchema\n .optional()\n .describe(\"Value used when the source is empty.\"),\n omitEmpty: z\n .boolean()\n .optional()\n .describe(\"Omit undefined, null, empty strings, and empty arrays.\"),\n required: z\n .boolean()\n .optional()\n .describe(\"Fail generation if the value is still empty after defaults.\"),\n};\n\nexport const frontmatterFieldSchema = z.union([\n z.strictObject({\n ...fieldModifiers,\n from: z\n .string()\n .min(1)\n .describe(\"Built-in source or a key declared under notion.properties.\"),\n }),\n z.strictObject({\n ...fieldModifiers,\n value: configValueSchema,\n }),\n]);\n\nexport const frontmatterFieldsSchema = z.record(\n z.string().min(1),\n frontmatterFieldSchema,\n);\n\nexport const notionPropertiesSchema = z.record(\n z.string().min(1),\n z.string().min(1),\n);\n\nconst childPageWrapperSchema = z.strictObject({\n head: z\n .string()\n .min(1)\n .regex(/^[^\\r\\n]+$/, \"must be a single line\")\n .describe(\"Opening wrapper emitted before the child-page Markdown link.\"),\n tail: z\n .string()\n .min(1)\n .regex(/^[^\\r\\n]+$/, \"must be a single line\")\n .describe(\"Closing wrapper emitted after the child-page Markdown link.\"),\n});\n\nexport const BUILT_IN_FRONTMATTER_SOURCES = [\n \"title\",\n \"description\",\n \"tags\",\n \"publishedAt\",\n \"archived\",\n \"layoutPath\",\n \"pageId\",\n \"isDatabasePage\",\n] as const;\n\nconst builtInSources: ReadonlySet<string> = new Set(\n BUILT_IN_FRONTMATTER_SOURCES,\n);\n\nexport const noblogConfigSchema = z\n .strictObject({\n version: z.literal(1),\n notion: z\n .strictObject({\n properties: notionPropertiesSchema.optional(),\n })\n .optional(),\n output: z\n .strictObject({\n savePath: z\n .string()\n .min(1)\n .optional()\n .describe(\"Public directory for database-post symlinks.\"),\n storePath: z\n .string()\n .min(1)\n .optional()\n .describe(\"Flat directory containing every canonical Markdown file.\"),\n layoutPath: z\n .string()\n .min(1)\n .optional()\n .describe(\"Layout import path interpreted relative to savePath.\"),\n recursive: z\n .boolean()\n .optional()\n .describe(\"Render child pages recursively (default: true).\"),\n childPageMarker: z\n .string()\n .min(1)\n .optional()\n .describe(\n \"Marker prefixed to child-page links (default: ⸎, U+2E0E).\",\n ),\n childPageWrapper: childPageWrapperSchema\n .optional()\n .describe(\n \"Optional wrapper around child-page Markdown links; overrides childPageMarker.\",\n ),\n })\n .optional(),\n frontmatter: z\n .strictObject({\n fields: frontmatterFieldsSchema.optional(),\n })\n .optional(),\n })\n .superRefine((config, context) => {\n const properties = config.notion?.properties ?? {};\n for (const [name, field] of Object.entries(\n config.frontmatter?.fields ?? {},\n )) {\n if (!(\"from\" in field)) continue;\n if (\n !builtInSources.has(field.from) &&\n !Object.hasOwn(properties, field.from)\n ) {\n context.addIssue({\n code: \"custom\",\n message: `Unknown frontmatter source \"${field.from}\"; declare it under notion.properties`,\n path: [\"frontmatter\", \"fields\", name, \"from\"],\n });\n }\n }\n })\n .describe(\n \"Noblog YAML configuration. Notion credentials remain in environment variables or .env files.\",\n );\n\nexport type FrontmatterField = z.infer<typeof frontmatterFieldSchema>;\nexport type FrontmatterFields = z.infer<typeof frontmatterFieldsSchema>;\nexport type NotionPropertyMap = z.infer<typeof notionPropertiesSchema>;\nexport type NoblogFileConfig = z.infer<typeof noblogConfigSchema>;\n","export function getErrorCode(error: unknown): string | undefined {\n if (\n typeof error !== \"object\" ||\n error === null ||\n !(\"code\" in error) ||\n typeof error.code !== \"string\"\n ) {\n return undefined;\n }\n return error.code;\n}\n"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import { n as loadConfig, t as ConfigurationError } from "./config-csbquMhF.js";
2
- import "./defaults-D9BbNYsd.js";
2
+ import "./defaults-RGOB9tfi.js";
3
3
  import { n as createFrontmatterAdapter, t as DEFAULT_FRONTMATTER_FIELDS } from "./frontmatter-fields-BkXq38CO.js";
4
- import { t as loadNoblogConfig } from "./config-file-BF0G2iJq.js";
4
+ import { t as loadNoblogConfig } from "./config-file-CZX2WJnz.js";
5
5
  //#region src/commands/generate.impl.ts
6
6
  async function generate(flags, savePath, legacySubPath) {
7
7
  try {
@@ -10,12 +10,15 @@ async function generate(flags, savePath, legacySubPath) {
10
10
  if (legacySubPath !== void 0) this.process.stderr.write("Warning: SUB_PATH is deprecated and ignored; child pages now live only in the canonical store.\n");
11
11
  const propertyMap = fileConfig?.notion?.properties;
12
12
  const fields = fileConfig?.frontmatter?.fields;
13
+ const childPageWrapper = fileConfig?.output?.childPageWrapper;
13
14
  const frontmatter = fields || propertyMap ? createFrontmatterAdapter(fields ?? DEFAULT_FRONTMATTER_FIELDS) : void 0;
14
15
  await this.generateBlog(config, {
15
16
  savePath: savePath ?? fileConfig?.output?.savePath ?? "./src/pages/posts/",
16
17
  storePath: flags.store ?? fileConfig?.output?.storePath ?? ".noblog/pages/",
17
18
  layoutPath: flags.layout ?? fileConfig?.output?.layoutPath ?? "../../layouts/MarkdownPostLayout.astro",
18
19
  recursive: fileConfig?.output?.recursive ?? true,
20
+ childPageMarker: fileConfig?.output?.childPageMarker ?? "⸎",
21
+ ...childPageWrapper !== void 0 ? { childPageWrapper } : {},
19
22
  ...propertyMap !== void 0 ? { propertyMap } : {},
20
23
  ...frontmatter !== void 0 ? { frontmatter } : {},
21
24
  verbose: flags.verbose
@@ -30,4 +33,4 @@ async function generate(flags, savePath, legacySubPath) {
30
33
  //#endregion
31
34
  export { generate as default };
32
35
 
33
- //# sourceMappingURL=generate.impl-BKu-Ix4a.js.map
36
+ //# sourceMappingURL=generate.impl-zbCKiANA.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate.impl-zbCKiANA.js","names":[],"sources":["../src/commands/generate.impl.ts"],"sourcesContent":["import { ConfigurationError, loadConfig } from \"../config.js\";\nimport { loadNoblogConfig } from \"../config-file.js\";\nimport type { CliContext } from \"../context.js\";\nimport {\n DEFAULT_CHILD_PAGE_MARKER,\n DEFAULT_LAYOUT_PATH,\n DEFAULT_SAVE_PATH,\n DEFAULT_STORE_PATH,\n} from \"../lib/noblog/defaults.js\";\nimport {\n createFrontmatterAdapter,\n DEFAULT_FRONTMATTER_FIELDS,\n} from \"../lib/noblog/frontmatter-fields.js\";\n\ninterface GenerateFlags {\n verbose: boolean;\n config?: string;\n layout?: string;\n store?: string;\n}\n\nexport default async function generate(\n this: CliContext,\n flags: GenerateFlags,\n savePath?: string,\n legacySubPath?: string,\n): Promise<Error | void> {\n try {\n const fileConfig = flags.config\n ? await loadNoblogConfig(flags.config)\n : undefined;\n const config = loadConfig(this.process.env);\n if (legacySubPath !== undefined) {\n this.process.stderr.write(\n \"Warning: SUB_PATH is deprecated and ignored; child pages now live only in the canonical store.\\n\",\n );\n }\n\n const propertyMap = fileConfig?.notion?.properties;\n const fields = fileConfig?.frontmatter?.fields;\n const childPageWrapper = fileConfig?.output?.childPageWrapper;\n const frontmatter =\n fields || propertyMap\n ? createFrontmatterAdapter(fields ?? DEFAULT_FRONTMATTER_FIELDS)\n : undefined;\n\n await this.generateBlog(\n config,\n {\n savePath:\n savePath ?? fileConfig?.output?.savePath ?? DEFAULT_SAVE_PATH,\n storePath:\n flags.store ?? fileConfig?.output?.storePath ?? DEFAULT_STORE_PATH,\n layoutPath:\n flags.layout ?? fileConfig?.output?.layoutPath ?? DEFAULT_LAYOUT_PATH,\n recursive: fileConfig?.output?.recursive ?? true,\n childPageMarker:\n fileConfig?.output?.childPageMarker ?? DEFAULT_CHILD_PAGE_MARKER,\n ...(childPageWrapper !== undefined ? { childPageWrapper } : {}),\n ...(propertyMap !== undefined ? { propertyMap } : {}),\n ...(frontmatter !== undefined ? { frontmatter } : {}),\n verbose: flags.verbose,\n },\n this.process,\n );\n } catch (error) {\n if (error instanceof ConfigurationError) {\n return error;\n }\n if (!(error instanceof Error)) {\n return new Error(\"Generation failed: an unknown error occurred\");\n }\n\n const message = flags.verbose\n ? (error.stack ?? error.message)\n : error.message;\n return new Error(`Generation failed: ${message}`, { cause: error });\n }\n}\n"],"mappings":";;;;;AAqBA,eAA8B,SAE5B,OACA,UACA,eACuB;CACvB,IAAI;EACF,MAAM,aAAa,MAAM,SACrB,MAAM,iBAAiB,MAAM,MAAM,IACnC,KAAA;EACJ,MAAM,SAAS,WAAW,KAAK,QAAQ,GAAG;EAC1C,IAAI,kBAAkB,KAAA,GACpB,KAAK,QAAQ,OAAO,MAClB,kGACF;EAGF,MAAM,cAAc,YAAY,QAAQ;EACxC,MAAM,SAAS,YAAY,aAAa;EACxC,MAAM,mBAAmB,YAAY,QAAQ;EAC7C,MAAM,cACJ,UAAU,cACN,yBAAyB,UAAU,0BAA0B,IAC7D,KAAA;EAEN,MAAM,KAAK,aACT,QACA;GACE,UACE,YAAY,YAAY,QAAQ,YAAA;GAClC,WACE,MAAM,SAAS,YAAY,QAAQ,aAAA;GACrC,YACE,MAAM,UAAU,YAAY,QAAQ,cAAA;GACtC,WAAW,YAAY,QAAQ,aAAa;GAC5C,iBACE,YAAY,QAAQ,mBAAA;GACtB,GAAI,qBAAqB,KAAA,IAAY,EAAE,iBAAiB,IAAI,CAAC;GAC7D,GAAI,gBAAgB,KAAA,IAAY,EAAE,YAAY,IAAI,CAAC;GACnD,GAAI,gBAAgB,KAAA,IAAY,EAAE,YAAY,IAAI,CAAC;GACnD,SAAS,MAAM;EACjB,GACA,KAAK,OACP;CACF,SAAS,OAAO;EACd,IAAI,iBAAiB,oBACnB,OAAO;EAET,IAAI,EAAE,iBAAiB,QACrB,uBAAO,IAAI,MAAM,8CAA8C;EAGjE,MAAM,UAAU,MAAM,UACjB,MAAM,SAAS,MAAM,UACtB,MAAM;EACV,OAAO,IAAI,MAAM,sBAAsB,WAAW,EAAE,OAAO,MAAM,CAAC;CACpE;AACF"}
package/dist/generate.js CHANGED
@@ -1,13 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
  import { t as ConfigurationError } from "./config-csbquMhF.js";
3
- import { a as renderPages, n as resolveStoreLayoutPath, r as writePages, t as createNotionSource } from "./client-CkOIgSvW.js";
3
+ import { a as renderPages, n as resolveStoreLayoutPath, r as writePages, t as createNotionSource } from "./client-BIBJQzBg.js";
4
4
  import { config } from "dotenv";
5
5
  import { ExitCode, buildApplication, buildCommand, buildRouteMap, help, run, version } from "@stricli/core";
6
6
  const app = buildApplication(buildRouteMap({
7
7
  routes: {
8
8
  config: buildRouteMap({
9
9
  routes: { check: buildCommand({
10
- loader: async () => import("./config-check.impl-BQvIrgNe.js"),
10
+ loader: async () => import("./config-check.impl-NO--hbh0.js"),
11
11
  parameters: {
12
12
  flags: { config: {
13
13
  kind: "parsed",
@@ -25,7 +25,7 @@ const app = buildApplication(buildRouteMap({
25
25
  docs: { brief: "Inspect Noblog configuration" }
26
26
  }),
27
27
  generate: buildCommand({
28
- loader: async () => import("./generate.impl-BKu-Ix4a.js"),
28
+ loader: async () => import("./generate.impl-zbCKiANA.js"),
29
29
  parameters: {
30
30
  flags: {
31
31
  verbose: {
@@ -98,7 +98,7 @@ const app = buildApplication(buildRouteMap({
98
98
  }),
99
99
  version: version({
100
100
  brief: "Show version information",
101
- info: { currentVersion: `noblog v0.3.0-beta.1` },
101
+ info: { currentVersion: `noblog v0.3.0-beta.3` },
102
102
  alias: false
103
103
  })
104
104
  });
@@ -161,6 +161,8 @@ async function generateBlog(config, options, output) {
161
161
  await writePages(await renderPages(posts, notion, {
162
162
  layoutPath: storeLayoutPath,
163
163
  recursive: options.recursive,
164
+ childPageMarker: options.childPageMarker,
165
+ ...options.childPageWrapper !== void 0 ? { childPageWrapper: options.childPageWrapper } : {},
164
166
  ...options.propertyMap !== void 0 ? { propertyMap: options.propertyMap } : {},
165
167
  ...options.frontmatter !== void 0 ? { frontmatter: options.frontmatter } : {}
166
168
  }), {
@@ -1 +1 @@
1
- {"version":3,"file":"generate.js","names":["packageJson.version"],"sources":["../package.json","../src/commands/config-check.ts","../src/commands/config.ts","../src/commands/generate.ts","../src/app.ts","../src/workflows/generate.ts","../src/main.ts","../src/cli.ts"],"sourcesContent":["","import { buildCommand } from \"@stricli/core\";\n\nexport const configCheckCommand = buildCommand({\n loader: async () => import(\"./config-check.impl.js\"),\n parameters: {\n flags: {\n config: {\n kind: \"parsed\",\n brief: \"YAML configuration file\",\n placeholder: \"PATH\",\n parse: (input) => input,\n },\n },\n positional: {\n kind: \"tuple\",\n parameters: [],\n },\n },\n docs: {\n brief: \"Validate a YAML configuration without network access\",\n },\n});\n","import { buildRouteMap } from \"@stricli/core\";\nimport { configCheckCommand } from \"./config-check.js\";\n\nexport const configCommand = buildRouteMap({\n routes: {\n check: configCheckCommand,\n },\n docs: {\n brief: \"Inspect Noblog configuration\",\n },\n});\n","import { buildCommand } from \"@stricli/core\";\n\nexport const generateCommand = buildCommand({\n loader: async () => import(\"./generate.impl.js\"),\n parameters: {\n flags: {\n verbose: {\n kind: \"boolean\",\n brief: \"Print diagnostic information\",\n default: false,\n withNegated: false,\n },\n config: {\n kind: \"parsed\",\n brief: \"YAML configuration file\",\n placeholder: \"PATH\",\n parse: (input) => input,\n optional: true,\n },\n layout: {\n kind: \"parsed\",\n brief: \"Layout path relative to SAVE_PATH\",\n placeholder: \"PATH\",\n parse: (input) => input,\n optional: true,\n },\n store: {\n kind: \"parsed\",\n brief: \"Directory for canonical Markdown files\",\n placeholder: \"PATH\",\n parse: (input) => input,\n optional: true,\n },\n },\n aliases: {\n v: \"verbose\",\n },\n positional: {\n kind: \"tuple\",\n parameters: [\n {\n brief: \"Public view directory for database posts\",\n placeholder: \"SAVE_PATH\",\n parse: (input) => input,\n optional: true,\n },\n {\n brief: \"Deprecated compatibility argument; ignored\",\n placeholder: \"SUB_PATH\",\n parse: (input) => input,\n optional: true,\n },\n ],\n },\n },\n docs: {\n brief: \"Generate blog content from Notion\",\n fullDescription:\n \"Fetch published pages from Notion and write Astro-compatible Markdown files. \" +\n \"Only database posts receive public symlinks; child pages live in the canonical store. \" +\n \"The legacy SUB_PATH argument is accepted but ignored.\",\n },\n});\n","import {\n buildApplication,\n buildRouteMap,\n ExitCode,\n help,\n run,\n version,\n} from \"@stricli/core\";\nimport packageJson from \"../package.json\" with { type: \"json\" };\nimport { configCommand } from \"./commands/config.js\";\nimport { generateCommand } from \"./commands/generate.js\";\nimport { ConfigurationError } from \"./config.js\";\nimport type { CliContext } from \"./context.js\";\n\nconst root = buildRouteMap({\n routes: {\n config: configCommand,\n generate: generateCommand,\n },\n docs: {\n brief: \"Generate static blog content from Notion\",\n fullDescription:\n \"Running noblog without a subcommand is equivalent to 'noblog generate'.\\n\" +\n \"The legacy 'help' and 'version' commands are also supported.\\n\\n\" +\n \"Generation requires environment variables:\\n\" +\n \" NOTION_API_SECRET Your Notion API secret\\n\" +\n \" NOBLOG_DATABASE_ID Your Notion database ID\",\n },\n});\n\nconst helpFormatting = {\n caseStyle: \"original\",\n useAliasInUsageLine: false,\n onlyRequiredInUsageLine: false,\n} as const;\n\nexport const app = buildApplication(\n root,\n {\n name: \"noblog\",\n scanner: {\n allowArgumentEscapeSequence: true,\n },\n determineExitCode: (error) =>\n error instanceof ConfigurationError ? 2 : ExitCode.CommandRunError,\n },\n {\n help: help({\n brief: \"Show help information\",\n defaultForRouteMap: true,\n formatting: helpFormatting,\n }),\n version: version({\n brief: \"Show version information\",\n info: { currentVersion: `noblog v${packageJson.version}` },\n alias: false,\n }),\n },\n);\n\nconst PATH_FLAGS = new Set([\"--config\", \"--layout\", \"--store\"]);\n\nexport async function runCli(\n inputs: readonly string[],\n context: CliContext,\n): Promise<void> {\n const escapeIndex = inputs.indexOf(\"--\");\n const commandInputs =\n escapeIndex === -1 ? inputs : inputs.slice(0, escapeIndex);\n const escapedInputs = escapeIndex === -1 ? [] : inputs.slice(escapeIndex);\n\n let normalizedInputs: readonly string[];\n if (hasLegacyControl(commandInputs, \"help\")) {\n normalizedInputs = [\"--help\"];\n } else if (\n hasLegacyControl(commandInputs, \"version\") ||\n hasLegacyControl(commandInputs, \"--version\")\n ) {\n normalizedInputs = [\"--version\"];\n } else {\n // Legacy control words were global. Keep the first explicit command and\n // skip later ones; escaped words and flag values remain positional data.\n const remainingInputs: string[] = [];\n for (let index = 0; index < commandInputs.length; index++) {\n const input = commandInputs[index];\n if (input === undefined) continue;\n if (input === \"generate\" && index !== 0) continue;\n\n remainingInputs.push(input);\n const flagValue = getFlagValue(commandInputs, index);\n if (PATH_FLAGS.has(input) && flagValue !== undefined) {\n remainingInputs.push(flagValue);\n index++;\n }\n }\n normalizedInputs = [...remainingInputs, ...escapedInputs];\n\n if (\n normalizedInputs[0] !== \"generate\" &&\n normalizedInputs[0] !== \"config\" &&\n normalizedInputs[0] !== \"-h\" &&\n normalizedInputs[0] !== \"--help\" &&\n normalizedInputs[0] !== \"--version\"\n ) {\n normalizedInputs = [\"generate\", ...normalizedInputs];\n }\n }\n\n await run(app, normalizedInputs, context);\n\n const exitCode = context.process.exitCode;\n if (typeof exitCode === \"number\" && exitCode < 0) {\n context.process.exitCode =\n exitCode === ExitCode.InvalidArgument ||\n exitCode === ExitCode.UnknownCommand\n ? 2\n : 1;\n }\n}\n\nfunction hasLegacyControl(\n inputs: readonly string[],\n control: \"help\" | \"version\" | \"--version\",\n): boolean {\n for (let index = 0; index < inputs.length; index++) {\n const input = inputs[index];\n if (input === undefined) continue;\n if (\n PATH_FLAGS.has(input) &&\n getFlagValue(inputs, index) !== undefined\n ) {\n index++;\n continue;\n }\n if (input === control) return true;\n }\n return false;\n}\n\nfunction getFlagValue(\n inputs: readonly string[],\n flagIndex: number,\n): string | undefined {\n const value = inputs[flagIndex + 1];\n return value !== undefined && !value.startsWith(\"-\") ? value : undefined;\n}\n","import type { NoblogConfig } from \"../config.js\";\nimport type { NotionPropertyMap } from \"../lib/noblog/config.js\";\nimport {\n renderPages,\n resolveStoreLayoutPath,\n writePages,\n} from \"../lib/noblog/index.js\";\nimport type { FrontmatterAdapter } from \"../lib/noblog/types.js\";\nimport { createNotionSource } from \"../lib/notion/client.js\";\n\nexport interface GenerateOptions {\n savePath: string;\n storePath: string;\n layoutPath: string;\n recursive: boolean;\n propertyMap?: NotionPropertyMap;\n frontmatter?: FrontmatterAdapter;\n verbose: boolean;\n}\n\nexport interface GenerateOutput {\n stdout: { write(chunk: string): void };\n stderr: { write(chunk: string): void };\n}\n\nexport async function generateBlog(\n config: NoblogConfig,\n options: GenerateOptions,\n output: GenerateOutput,\n): Promise<void> {\n const notion = createNotionSource(config);\n const storeLayoutPath = resolveStoreLayoutPath(options);\n\n if (options.verbose) {\n output.stderr.write(\"Fetching posts from Notion...\\n\");\n }\n\n const posts = await notion.listPages();\n\n if (options.verbose) {\n output.stderr.write(\n `Found ${posts.length} pages\\n` +\n `Save path: ${options.savePath}\\n` +\n `Store path: ${options.storePath}\\n` +\n `Layout path: ${options.layoutPath}\\n`,\n );\n }\n\n const pages = await renderPages(posts, notion, {\n layoutPath: storeLayoutPath,\n recursive: options.recursive,\n ...(options.propertyMap !== undefined\n ? { propertyMap: options.propertyMap }\n : {}),\n ...(options.frontmatter !== undefined\n ? { frontmatter: options.frontmatter }\n : {}),\n });\n await writePages(pages, {\n savePath: options.savePath,\n storePath: options.storePath,\n });\n\n output.stdout.write(\"✅ Successfully generated blog content\\n\");\n}\n\nexport type GenerateBlog = typeof generateBlog;\n","import { config as loadDotenv } from \"dotenv\";\nimport type { DotenvPopulateInput } from \"dotenv\";\nimport { runCli } from \"./app.js\";\nimport type { CliContext } from \"./context.js\";\nimport { generateBlog } from \"./workflows/generate.js\";\n\nconst nodeProcess: CliContext[\"process\"] = {\n env: process.env,\n stdout: process.stdout,\n stderr: process.stderr,\n get exitCode() {\n const exitCode = process.exitCode;\n return typeof exitCode === \"number\" ? exitCode : null;\n },\n set exitCode(value: number | null) {\n process.exitCode = value ?? undefined;\n },\n};\n\nexport async function main(\n inputs: readonly string[] = process.argv.slice(2),\n context: CliContext = { process: nodeProcess, generateBlog },\n): Promise<void> {\n const environment = context.process.env ?? {};\n const processEnv: DotenvPopulateInput = Object.fromEntries(\n Object.entries(environment).filter(\n (entry): entry is [string, string] => entry[1] !== undefined,\n ),\n );\n loadDotenv({ processEnv, quiet: true });\n Object.assign(environment, processEnv);\n await runCli(inputs, context);\n}\n","#!/usr/bin/env node\n\nimport { main } from \"./main.js\";\n\nawait main();\n"],"mappings":"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"}
1
+ {"version":3,"file":"generate.js","names":["packageJson.version"],"sources":["../package.json","../src/commands/config-check.ts","../src/commands/config.ts","../src/commands/generate.ts","../src/app.ts","../src/workflows/generate.ts","../src/main.ts","../src/cli.ts"],"sourcesContent":["","import { buildCommand } from \"@stricli/core\";\n\nexport const configCheckCommand = buildCommand({\n loader: async () => import(\"./config-check.impl.js\"),\n parameters: {\n flags: {\n config: {\n kind: \"parsed\",\n brief: \"YAML configuration file\",\n placeholder: \"PATH\",\n parse: (input) => input,\n },\n },\n positional: {\n kind: \"tuple\",\n parameters: [],\n },\n },\n docs: {\n brief: \"Validate a YAML configuration without network access\",\n },\n});\n","import { buildRouteMap } from \"@stricli/core\";\nimport { configCheckCommand } from \"./config-check.js\";\n\nexport const configCommand = buildRouteMap({\n routes: {\n check: configCheckCommand,\n },\n docs: {\n brief: \"Inspect Noblog configuration\",\n },\n});\n","import { buildCommand } from \"@stricli/core\";\n\nexport const generateCommand = buildCommand({\n loader: async () => import(\"./generate.impl.js\"),\n parameters: {\n flags: {\n verbose: {\n kind: \"boolean\",\n brief: \"Print diagnostic information\",\n default: false,\n withNegated: false,\n },\n config: {\n kind: \"parsed\",\n brief: \"YAML configuration file\",\n placeholder: \"PATH\",\n parse: (input) => input,\n optional: true,\n },\n layout: {\n kind: \"parsed\",\n brief: \"Layout path relative to SAVE_PATH\",\n placeholder: \"PATH\",\n parse: (input) => input,\n optional: true,\n },\n store: {\n kind: \"parsed\",\n brief: \"Directory for canonical Markdown files\",\n placeholder: \"PATH\",\n parse: (input) => input,\n optional: true,\n },\n },\n aliases: {\n v: \"verbose\",\n },\n positional: {\n kind: \"tuple\",\n parameters: [\n {\n brief: \"Public view directory for database posts\",\n placeholder: \"SAVE_PATH\",\n parse: (input) => input,\n optional: true,\n },\n {\n brief: \"Deprecated compatibility argument; ignored\",\n placeholder: \"SUB_PATH\",\n parse: (input) => input,\n optional: true,\n },\n ],\n },\n },\n docs: {\n brief: \"Generate blog content from Notion\",\n fullDescription:\n \"Fetch published pages from Notion and write Astro-compatible Markdown files. \" +\n \"Only database posts receive public symlinks; child pages live in the canonical store. \" +\n \"The legacy SUB_PATH argument is accepted but ignored.\",\n },\n});\n","import {\n buildApplication,\n buildRouteMap,\n ExitCode,\n help,\n run,\n version,\n} from \"@stricli/core\";\nimport packageJson from \"../package.json\" with { type: \"json\" };\nimport { configCommand } from \"./commands/config.js\";\nimport { generateCommand } from \"./commands/generate.js\";\nimport { ConfigurationError } from \"./config.js\";\nimport type { CliContext } from \"./context.js\";\n\nconst root = buildRouteMap({\n routes: {\n config: configCommand,\n generate: generateCommand,\n },\n docs: {\n brief: \"Generate static blog content from Notion\",\n fullDescription:\n \"Running noblog without a subcommand is equivalent to 'noblog generate'.\\n\" +\n \"The legacy 'help' and 'version' commands are also supported.\\n\\n\" +\n \"Generation requires environment variables:\\n\" +\n \" NOTION_API_SECRET Your Notion API secret\\n\" +\n \" NOBLOG_DATABASE_ID Your Notion database ID\",\n },\n});\n\nconst helpFormatting = {\n caseStyle: \"original\",\n useAliasInUsageLine: false,\n onlyRequiredInUsageLine: false,\n} as const;\n\nexport const app = buildApplication(\n root,\n {\n name: \"noblog\",\n scanner: {\n allowArgumentEscapeSequence: true,\n },\n determineExitCode: (error) =>\n error instanceof ConfigurationError ? 2 : ExitCode.CommandRunError,\n },\n {\n help: help({\n brief: \"Show help information\",\n defaultForRouteMap: true,\n formatting: helpFormatting,\n }),\n version: version({\n brief: \"Show version information\",\n info: { currentVersion: `noblog v${packageJson.version}` },\n alias: false,\n }),\n },\n);\n\nconst PATH_FLAGS = new Set([\"--config\", \"--layout\", \"--store\"]);\n\nexport async function runCli(\n inputs: readonly string[],\n context: CliContext,\n): Promise<void> {\n const escapeIndex = inputs.indexOf(\"--\");\n const commandInputs =\n escapeIndex === -1 ? inputs : inputs.slice(0, escapeIndex);\n const escapedInputs = escapeIndex === -1 ? [] : inputs.slice(escapeIndex);\n\n let normalizedInputs: readonly string[];\n if (hasLegacyControl(commandInputs, \"help\")) {\n normalizedInputs = [\"--help\"];\n } else if (\n hasLegacyControl(commandInputs, \"version\") ||\n hasLegacyControl(commandInputs, \"--version\")\n ) {\n normalizedInputs = [\"--version\"];\n } else {\n // Legacy control words were global. Keep the first explicit command and\n // skip later ones; escaped words and flag values remain positional data.\n const remainingInputs: string[] = [];\n for (let index = 0; index < commandInputs.length; index++) {\n const input = commandInputs[index];\n if (input === undefined) continue;\n if (input === \"generate\" && index !== 0) continue;\n\n remainingInputs.push(input);\n const flagValue = getFlagValue(commandInputs, index);\n if (PATH_FLAGS.has(input) && flagValue !== undefined) {\n remainingInputs.push(flagValue);\n index++;\n }\n }\n normalizedInputs = [...remainingInputs, ...escapedInputs];\n\n if (\n normalizedInputs[0] !== \"generate\" &&\n normalizedInputs[0] !== \"config\" &&\n normalizedInputs[0] !== \"-h\" &&\n normalizedInputs[0] !== \"--help\" &&\n normalizedInputs[0] !== \"--version\"\n ) {\n normalizedInputs = [\"generate\", ...normalizedInputs];\n }\n }\n\n await run(app, normalizedInputs, context);\n\n const exitCode = context.process.exitCode;\n if (typeof exitCode === \"number\" && exitCode < 0) {\n context.process.exitCode =\n exitCode === ExitCode.InvalidArgument ||\n exitCode === ExitCode.UnknownCommand\n ? 2\n : 1;\n }\n}\n\nfunction hasLegacyControl(\n inputs: readonly string[],\n control: \"help\" | \"version\" | \"--version\",\n): boolean {\n for (let index = 0; index < inputs.length; index++) {\n const input = inputs[index];\n if (input === undefined) continue;\n if (\n PATH_FLAGS.has(input) &&\n getFlagValue(inputs, index) !== undefined\n ) {\n index++;\n continue;\n }\n if (input === control) return true;\n }\n return false;\n}\n\nfunction getFlagValue(\n inputs: readonly string[],\n flagIndex: number,\n): string | undefined {\n const value = inputs[flagIndex + 1];\n return value !== undefined && !value.startsWith(\"-\") ? value : undefined;\n}\n","import type { NoblogConfig } from \"../config.js\";\nimport type { NotionPropertyMap } from \"../lib/noblog/config.js\";\nimport {\n renderPages,\n resolveStoreLayoutPath,\n writePages,\n} from \"../lib/noblog/index.js\";\nimport type {\n ChildPageWrapper,\n FrontmatterAdapter,\n} from \"../lib/noblog/types.js\";\nimport { createNotionSource } from \"../lib/notion/client.js\";\n\nexport interface GenerateOptions {\n savePath: string;\n storePath: string;\n layoutPath: string;\n recursive: boolean;\n childPageMarker: string;\n childPageWrapper?: ChildPageWrapper;\n propertyMap?: NotionPropertyMap;\n frontmatter?: FrontmatterAdapter;\n verbose: boolean;\n}\n\nexport interface GenerateOutput {\n stdout: { write(chunk: string): void };\n stderr: { write(chunk: string): void };\n}\n\nexport async function generateBlog(\n config: NoblogConfig,\n options: GenerateOptions,\n output: GenerateOutput,\n): Promise<void> {\n const notion = createNotionSource(config);\n const storeLayoutPath = resolveStoreLayoutPath(options);\n\n if (options.verbose) {\n output.stderr.write(\"Fetching posts from Notion...\\n\");\n }\n\n const posts = await notion.listPages();\n\n if (options.verbose) {\n output.stderr.write(\n `Found ${posts.length} pages\\n` +\n `Save path: ${options.savePath}\\n` +\n `Store path: ${options.storePath}\\n` +\n `Layout path: ${options.layoutPath}\\n`,\n );\n }\n\n const pages = await renderPages(posts, notion, {\n layoutPath: storeLayoutPath,\n recursive: options.recursive,\n childPageMarker: options.childPageMarker,\n ...(options.childPageWrapper !== undefined\n ? { childPageWrapper: options.childPageWrapper }\n : {}),\n ...(options.propertyMap !== undefined\n ? { propertyMap: options.propertyMap }\n : {}),\n ...(options.frontmatter !== undefined\n ? { frontmatter: options.frontmatter }\n : {}),\n });\n await writePages(pages, {\n savePath: options.savePath,\n storePath: options.storePath,\n });\n\n output.stdout.write(\"✅ Successfully generated blog content\\n\");\n}\n\nexport type GenerateBlog = typeof generateBlog;\n","import { config as loadDotenv } from \"dotenv\";\nimport type { DotenvPopulateInput } from \"dotenv\";\nimport { runCli } from \"./app.js\";\nimport type { CliContext } from \"./context.js\";\nimport { generateBlog } from \"./workflows/generate.js\";\n\nconst nodeProcess: CliContext[\"process\"] = {\n env: process.env,\n stdout: process.stdout,\n stderr: process.stderr,\n get exitCode() {\n const exitCode = process.exitCode;\n return typeof exitCode === \"number\" ? exitCode : null;\n },\n set exitCode(value: number | null) {\n process.exitCode = value ?? undefined;\n },\n};\n\nexport async function main(\n inputs: readonly string[] = process.argv.slice(2),\n context: CliContext = { process: nodeProcess, generateBlog },\n): Promise<void> {\n const environment = context.process.env ?? {};\n const processEnv: DotenvPopulateInput = Object.fromEntries(\n Object.entries(environment).filter(\n (entry): entry is [string, string] => entry[1] !== undefined,\n ),\n );\n loadDotenv({ processEnv, quiet: true });\n Object.assign(environment, processEnv);\n await runCli(inputs, context);\n}\n","#!/usr/bin/env node\n\nimport { main } from \"./main.js\";\n\nawait main();\n"],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -55,6 +55,11 @@ declare const noblogConfigSchema: z.ZodObject<{
55
55
  storePath: z.ZodOptional<z.ZodString>;
56
56
  layoutPath: z.ZodOptional<z.ZodString>;
57
57
  recursive: z.ZodOptional<z.ZodBoolean>;
58
+ childPageMarker: z.ZodOptional<z.ZodString>;
59
+ childPageWrapper: z.ZodOptional<z.ZodObject<{
60
+ head: z.ZodString;
61
+ tail: z.ZodString;
62
+ }, z.core.$strict>>;
58
63
  }, z.core.$strict>>;
59
64
  frontmatter: z.ZodOptional<z.ZodObject<{
60
65
  fields: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodObject<{
@@ -87,6 +92,8 @@ type NoblogFileConfig = z.infer<typeof noblogConfigSchema>;
87
92
  declare const DEFAULT_SAVE_PATH = "./src/pages/posts/";
88
93
  declare const DEFAULT_STORE_PATH = ".noblog/pages/";
89
94
  declare const DEFAULT_LAYOUT_PATH = "../../layouts/MarkdownPostLayout.astro";
95
+ /** U+2E0E EDITORIAL CORONIS, a rare marker for theme child-page links. */
96
+ declare const DEFAULT_CHILD_PAGE_MARKER = "\u2E0E";
90
97
  //#endregion
91
98
  //#region src/lib/noblog/metadata.d.ts
92
99
  type MetadataValue = string | number | boolean | readonly string[] | undefined;
@@ -104,9 +111,17 @@ declare function extractPageMetadata(page: PageObjectResponse, properties?: Noti
104
111
  type CalloutIcon = Extract<BlockObjectResponse, {
105
112
  type: "callout";
106
113
  }>["callout"]["icon"];
114
+ type Heading1Block = Extract<BlockObjectResponse, {
115
+ type: "heading_1";
116
+ }>;
117
+ type Heading4BlockObjectResponse = Omit<Heading1Block, "type" | "heading_1"> & {
118
+ readonly type: "heading_4";
119
+ readonly heading_4: Heading1Block["heading_1"];
120
+ };
121
+ type NoblogBlock = BlockObjectResponse | Heading4BlockObjectResponse;
107
122
  interface NotionReader {
108
123
  getPage(pageId: string): Promise<PageObjectResponse>;
109
- getBlockChildren(blockId: string): Promise<readonly BlockObjectResponse[]>;
124
+ getBlockChildren(blockId: string): Promise<readonly NoblogBlock[]>;
110
125
  }
111
126
  interface FrontmatterContext {
112
127
  readonly page: PageObjectResponse;
@@ -122,13 +137,19 @@ type FrontmatterAdapter = (context: FrontmatterContext) => FrontmatterRecord;
122
137
  interface BlockTransformerContext {
123
138
  readonly pageId: string;
124
139
  }
125
- type BlockTransformer<K extends BlockObjectResponse["type"] = BlockObjectResponse["type"]> = (block: Extract<BlockObjectResponse, {
140
+ type BlockTransformer<K extends NoblogBlock["type"] = NoblogBlock["type"]> = (block: Extract<NoblogBlock, {
126
141
  type: K;
127
142
  }>, context: BlockTransformerContext) => string | undefined | Promise<string | undefined>;
128
- type BlockTransformers = { readonly [K in BlockObjectResponse["type"]]?: BlockTransformer<K> | undefined };
143
+ type BlockTransformers = { readonly [K in NoblogBlock["type"]]?: BlockTransformer<K> | undefined };
144
+ interface ChildPageWrapper {
145
+ readonly head: string;
146
+ readonly tail: string;
147
+ }
129
148
  interface RenderOptions {
130
149
  readonly layoutPath?: string;
131
150
  readonly recursive?: boolean;
151
+ readonly childPageMarker?: string;
152
+ readonly childPageWrapper?: ChildPageWrapper;
132
153
  readonly propertyMap?: NotionPropertyMap;
133
154
  readonly frontmatter?: FrontmatterAdapter;
134
155
  readonly transformers?: BlockTransformers;
@@ -175,6 +196,7 @@ declare const equation: (text: string) => string;
175
196
  declare const heading1: (text: string) => string;
176
197
  declare const heading2: (text: string) => string;
177
198
  declare const heading3: (text: string) => string;
199
+ declare const heading4: (text: string) => string;
178
200
  declare const quote: (text: string) => string;
179
201
  declare function callout(text: string, icon?: CalloutIcon): string;
180
202
  declare function bullet(text: string, count?: number): string;
@@ -206,5 +228,5 @@ interface NotionSource extends NotionReader {
206
228
  }
207
229
  declare function createNotionSource(config: NotionSourceConfig): NotionSource;
208
230
  //#endregion
209
- export { BUILT_IN_FRONTMATTER_SOURCES, BlockTransformer, BlockTransformerContext, BlockTransformers, CalloutIcon, ConfigValue, DEFAULT_FRONTMATTER_FIELDS, DEFAULT_LAYOUT_PATH, DEFAULT_SAVE_PATH, DEFAULT_STORE_PATH, FrontmatterAdapter, FrontmatterContext, FrontmatterField, FrontmatterFields, FrontmatterOptions, FrontmatterRecord, FrontmatterValue, MetadataValue, NoblogFileConfig, NotionPropertyMap, NotionReader, NotionSource, NotionSourceConfig, PageMetadata, RenderOptions, RenderedPage, StoreLayoutOptions, WritePagesOptions, bold, bullet, callout, codeBlock, createFrontmatterAdapter, createNotionSource, divider, equation, extractPageMetadata, frontmatterFieldSchema, frontmatterFieldsSchema, heading1, heading2, heading3, image, inlineCode, inlineEquation, italic, link, noblogConfigSchema, notionPropertiesSchema, quote, renderFrontmatter, renderPage, renderPages, renderPlainText, renderRichText, resolveStoreLayoutPath, strikethrough, table, todo, toggle, underline, writePages };
231
+ export { BUILT_IN_FRONTMATTER_SOURCES, BlockTransformer, BlockTransformerContext, BlockTransformers, CalloutIcon, ChildPageWrapper, ConfigValue, DEFAULT_CHILD_PAGE_MARKER, DEFAULT_FRONTMATTER_FIELDS, DEFAULT_LAYOUT_PATH, DEFAULT_SAVE_PATH, DEFAULT_STORE_PATH, FrontmatterAdapter, FrontmatterContext, FrontmatterField, FrontmatterFields, FrontmatterOptions, FrontmatterRecord, FrontmatterValue, Heading4BlockObjectResponse, MetadataValue, NoblogBlock, NoblogFileConfig, NotionPropertyMap, NotionReader, NotionSource, NotionSourceConfig, PageMetadata, RenderOptions, RenderedPage, StoreLayoutOptions, WritePagesOptions, bold, bullet, callout, codeBlock, createFrontmatterAdapter, createNotionSource, divider, equation, extractPageMetadata, frontmatterFieldSchema, frontmatterFieldsSchema, heading1, heading2, heading3, heading4, image, inlineCode, inlineEquation, italic, link, noblogConfigSchema, notionPropertiesSchema, quote, renderFrontmatter, renderPage, renderPages, renderPlainText, renderRichText, resolveStoreLayoutPath, strikethrough, table, todo, toggle, underline, writePages };
210
232
  //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import { a as noblogConfigSchema, i as frontmatterFieldsSchema, n as BUILT_IN_FRONTMATTER_SOURCES, o as notionPropertiesSchema, r as frontmatterFieldSchema } from "./errors-DgJ15MsB.js";
2
- import { n as DEFAULT_SAVE_PATH, r as DEFAULT_STORE_PATH, t as DEFAULT_LAYOUT_PATH } from "./defaults-D9BbNYsd.js";
3
- import { C as strikethrough, D as underline, E as toggle, O as renderFrontmatter, S as quote, T as todo, _ as image, a as renderPages, b as italic, c as bold, d as codeBlock, f as divider, g as heading3, h as heading2, i as renderPage, k as extractPageMetadata, l as bullet, m as heading1, n as resolveStoreLayoutPath, o as renderPlainText, p as equation, r as writePages, s as renderRichText, t as createNotionSource, u as callout, v as inlineCode, w as table, x as link, y as inlineEquation } from "./client-CkOIgSvW.js";
1
+ import { a as noblogConfigSchema, i as frontmatterFieldsSchema, n as BUILT_IN_FRONTMATTER_SOURCES, o as notionPropertiesSchema, r as frontmatterFieldSchema } from "./errors-Bm4iVQVp.js";
2
+ import { i as DEFAULT_STORE_PATH, n as DEFAULT_LAYOUT_PATH, r as DEFAULT_SAVE_PATH, t as DEFAULT_CHILD_PAGE_MARKER } from "./defaults-RGOB9tfi.js";
3
+ import { A as extractPageMetadata, C as quote, D as toggle, E as todo, O as underline, S as link, T as table, _ as heading4, a as renderPages, b as inlineEquation, c as bold, d as codeBlock, f as divider, g as heading3, h as heading2, i as renderPage, k as renderFrontmatter, l as bullet, m as heading1, n as resolveStoreLayoutPath, o as renderPlainText, p as equation, r as writePages, s as renderRichText, t as createNotionSource, u as callout, v as image, w as strikethrough, x as italic, y as inlineCode } from "./client-BIBJQzBg.js";
4
4
  import { n as createFrontmatterAdapter, t as DEFAULT_FRONTMATTER_FIELDS } from "./frontmatter-fields-BkXq38CO.js";
5
- export { BUILT_IN_FRONTMATTER_SOURCES, DEFAULT_FRONTMATTER_FIELDS, DEFAULT_LAYOUT_PATH, DEFAULT_SAVE_PATH, DEFAULT_STORE_PATH, bold, bullet, callout, codeBlock, createFrontmatterAdapter, createNotionSource, divider, equation, extractPageMetadata, frontmatterFieldSchema, frontmatterFieldsSchema, heading1, heading2, heading3, image, inlineCode, inlineEquation, italic, link, noblogConfigSchema, notionPropertiesSchema, quote, renderFrontmatter, renderPage, renderPages, renderPlainText, renderRichText, resolveStoreLayoutPath, strikethrough, table, todo, toggle, underline, writePages };
5
+ export { BUILT_IN_FRONTMATTER_SOURCES, DEFAULT_CHILD_PAGE_MARKER, DEFAULT_FRONTMATTER_FIELDS, DEFAULT_LAYOUT_PATH, DEFAULT_SAVE_PATH, DEFAULT_STORE_PATH, bold, bullet, callout, codeBlock, createFrontmatterAdapter, createNotionSource, divider, equation, extractPageMetadata, frontmatterFieldSchema, frontmatterFieldsSchema, heading1, heading2, heading3, heading4, image, inlineCode, inlineEquation, italic, link, noblogConfigSchema, notionPropertiesSchema, quote, renderFrontmatter, renderPage, renderPages, renderPlainText, renderRichText, resolveStoreLayoutPath, strikethrough, table, todo, toggle, underline, writePages };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "noblog",
3
- "version": "0.3.0-beta.1",
3
+ "version": "0.3.0-beta.3",
4
4
  "description": "Generate static blog content from a Notion.so table.",
5
5
  "author": "humbornjo",
6
6
  "license": "MIT",
@@ -44,6 +44,34 @@
44
44
  "recursive": {
45
45
  "description": "Render child pages recursively (default: true).",
46
46
  "type": "boolean"
47
+ },
48
+ "childPageMarker": {
49
+ "description": "Marker prefixed to child-page links (default: ⸎, U+2E0E).",
50
+ "type": "string",
51
+ "minLength": 1
52
+ },
53
+ "childPageWrapper": {
54
+ "description": "Optional wrapper around child-page Markdown links; overrides childPageMarker.",
55
+ "type": "object",
56
+ "properties": {
57
+ "head": {
58
+ "type": "string",
59
+ "minLength": 1,
60
+ "pattern": "^[^\\r\\n]+$",
61
+ "description": "Opening wrapper emitted before the child-page Markdown link."
62
+ },
63
+ "tail": {
64
+ "type": "string",
65
+ "minLength": 1,
66
+ "pattern": "^[^\\r\\n]+$",
67
+ "description": "Closing wrapper emitted after the child-page Markdown link."
68
+ }
69
+ },
70
+ "required": [
71
+ "head",
72
+ "tail"
73
+ ],
74
+ "additionalProperties": false
47
75
  }
48
76
  },
49
77
  "additionalProperties": false
@@ -13,7 +13,7 @@ Create a `version: 1` Noblog YAML file that matches the installed theme, not a g
13
13
  2. **Inspect the real content contract.** Read collection schemas, Zod/Valibot/TypeScript frontmatter definitions, Markdown layouts, and the code that lists or routes content. For Astro, check both `src/content.config.ts` and `src/content/config.ts`, plus `Astro.glob` or dynamic collection routes. A layout alone is not proof of validation rules.
14
14
  3. **Record the contract before writing YAML.** Note required and optional fields, native types, defaults, date handling, collection glob patterns, and which directories produce routes. Distinguish file-based Markdown routes from content collections.
15
15
  4. **Confirm Notion property names.** Use names supplied by the user or visible in existing configuration/code. Do not invent custom properties or request credentials. Map logical sources to exact Notion names under `notion.properties`.
16
- 5. **Choose output paths with routing in mind.** `savePath` receives database-post symlinks; `storePath` receives every real Markdown file, including children. Child links target `./<page-id>.md` inside the store. Ensure the theme actually loads/routes the store if child pages must be public. Noblog does not create `nob_children` routes or remove old nested views.
16
+ 5. **Choose output paths with routing in mind.** `savePath` receives database-post symlinks; `storePath` receives every real Markdown file, including children. Child links target `./<page-id>.md` inside the store and are prefixed by `output.childPageMarker` (default `⸎`, U+2E0E EDITORIAL CORONIS); set it if the theme expects a specific marker. If the theme expects a wrapper, use `output.childPageWrapper` with non-empty single-line `head`/`tail` values; it overrides the marker. Ensure the theme actually loads/routes the store if child pages must be public. Noblog does not create `nob_children` routes or remove old nested views.
17
17
  6. **Write only the fields the theme consumes.** Custom `frontmatter.fields` replaces the legacy field set. Use exactly one of `from` or `value` per field. Keep booleans, numbers, arrays, and objects native unless the theme requires strings.
18
18
  7. **Validate without generating.** Run:
19
19
 
@@ -21,6 +21,7 @@ output:
21
21
  storePath: .noblog/pages/
22
22
  layoutPath: ../../layouts/MarkdownPostLayout.astro
23
23
  recursive: true
24
+ childPageMarker: "⸎"
24
25
  ```
25
26
 
26
27
  | Key | Default | Meaning |
@@ -29,8 +30,19 @@ output:
29
30
  | `storePath` | `.noblog/pages/` | Receives every real Markdown file in a flat page-ID layout. Override with `--store`. |
30
31
  | `layoutPath` | `../../layouts/MarkdownPostLayout.astro` | Interpreted relative to `savePath`; Noblog writes the equivalent store-relative import. Override with `--layout`. |
31
32
  | `recursive` | `true` | Collect and render child pages recursively. |
33
+ | `childPageMarker` | `⸎` (U+2E0E EDITORIAL CORONIS) | Non-empty prefix added to child-page links. Set it to the marker the theme recognizes. |
34
+ | `childPageWrapper` | — | Optional `{ head, tail }` wrapper around child-page links. Overrides `childPageMarker`. Both values must be non-empty single-line strings. |
32
35
 
33
- All paths are resolved from the generation working directory. Child pages have no public symlinks. The theme must load and route the canonical store itself when child pages should have routes.
36
+ Optional wrapper (overrides `childPageMarker`):
37
+
38
+ ```yaml
39
+ output:
40
+ childPageWrapper:
41
+ head: "<noblog-subpage>"
42
+ tail: "</noblog-subpage>"
43
+ ```
44
+
45
+ All paths are resolved from the generation working directory. Child pages have no public symlinks. Child-page links are written as `<marker> [title](./<page-id>.md)` or, when configured, `<head>[title](./<page-id>.md)<tail>`. The theme must load and route the canonical store itself when child pages should have routes.
34
46
 
35
47
  ## Notion property mapping
36
48
 
@@ -2,6 +2,8 @@
2
2
 
3
3
  These examples are snapshots, not substitutes for inspecting the installed theme. The assets contain complete YAML starting points.
4
4
 
5
+ Child-page links are prefixed by `output.childPageMarker`, which defaults to `⸎` (U+2E0E EDITORIAL CORONIS). Set it to the marker the theme recognizes. If the theme expects a wrapper element, configure `output.childPageWrapper` with `head` and `tail`; it overrides the marker.
6
+
5
7
  ## Nobloger
6
8
 
7
9
  Evidence inspected: `src/layouts/MarkdownPostLayout.astro` and `src/pages/blog/index.astro` in [humbornjo/nobloger](https://github.com/humbornjo/nobloger).
@@ -1 +0,0 @@
1
- {"version":3,"file":"client-CkOIgSvW.js","names":["md.toggle","md.callout","md.bullet","md.image","md.divider","md.equation","md.link","md.heading2","md.codeBlock","md.heading1","md.heading3","md.quote","md.todo","md.table"],"sources":["../src/lib/noblog/metadata.ts","../src/lib/noblog/frontmatter.ts","../src/lib/noblog/markdown.ts","../src/lib/noblog/rich-text.ts","../src/lib/noblog/blocks.ts","../src/lib/noblog/pages.ts","../src/lib/noblog/writer.ts","../src/lib/notion/client.ts"],"sourcesContent":["import type { PageObjectResponse } from \"@notionhq/client\";\nimport type { NotionPropertyMap } from \"./config.js\";\n\ntype NotionProperty = PageObjectResponse[\"properties\"][string];\n\nexport type MetadataValue =\n | string\n | number\n | boolean\n | readonly string[]\n | undefined;\n\nexport interface PageMetadata {\n readonly title: string;\n readonly description?: string | undefined;\n readonly tags: readonly string[];\n readonly publishedAt?: string | undefined;\n readonly archived?: boolean | undefined;\n readonly values: Readonly<Record<string, MetadataValue>>;\n}\n\nconst DEFAULT_PROPERTIES = {\n description: \"description\",\n tags: \"tags\",\n publishedAt: \"date\",\n archived: \"archived\",\n} as const;\n\nexport function extractPageMetadata(\n page: PageObjectResponse,\n properties: NotionPropertyMap = {},\n): PageMetadata {\n const propertyNames = new Map<string, string>(Object.entries(properties));\n for (const [key, name] of Object.entries(DEFAULT_PROPERTIES)) {\n if (!propertyNames.has(key)) propertyNames.set(key, name);\n }\n\n const values: Record<string, MetadataValue> = {};\n if (!propertyNames.has(\"title\")) {\n const titleProperty = Object.values(page.properties).findLast(\n (property) => property.type === \"title\",\n );\n values[\"title\"] = normalizeProperty(titleProperty);\n }\n for (const [key, name] of propertyNames) {\n values[key] = normalizeProperty(page.properties[name]);\n }\n\n return {\n title: asString(values[\"title\"]) ?? \"\",\n description: asString(values[\"description\"]),\n tags: asStringArray(values[\"tags\"]) ?? [],\n publishedAt: asString(values[\"publishedAt\"]),\n archived: asBoolean(values[\"archived\"]),\n values,\n };\n}\n\nfunction normalizeProperty(property?: NotionProperty): MetadataValue {\n if (!property) return undefined;\n\n switch (property.type) {\n case \"title\":\n return plainText(property.title);\n case \"rich_text\":\n return plainText(property.rich_text);\n case \"multi_select\":\n return property.multi_select.map(({ name }) => name);\n case \"select\":\n return property.select?.name;\n case \"status\":\n return property.status?.name;\n case \"checkbox\":\n return property.checkbox;\n case \"number\":\n return property.number ?? undefined;\n case \"date\":\n return property.date?.start;\n case \"url\":\n return property.url ?? undefined;\n case \"email\":\n return property.email ?? undefined;\n case \"phone_number\":\n return property.phone_number ?? undefined;\n case \"people\":\n return property.people.map(entityName);\n case \"relation\":\n return property.relation.map(({ id }) => id);\n case \"files\":\n return property.files.map((file) =>\n \"file\" in file ? file.file.url : file.external.url,\n );\n case \"formula\":\n return normalizeFormula(property.formula);\n case \"created_time\":\n return property.created_time;\n case \"last_edited_time\":\n return property.last_edited_time;\n case \"created_by\":\n return entityName(property.created_by);\n case \"last_edited_by\":\n return entityName(property.last_edited_by);\n case \"unique_id\":\n return property.unique_id.prefix\n ? `${property.unique_id.prefix}-${property.unique_id.number}`\n : String(property.unique_id.number);\n default:\n return undefined;\n }\n}\n\nfunction normalizeFormula(\n formula: Extract<NotionProperty, { type: \"formula\" }>[\"formula\"],\n): MetadataValue {\n switch (formula.type) {\n case \"string\":\n return formula.string ?? undefined;\n case \"number\":\n return formula.number ?? undefined;\n case \"boolean\":\n return formula.boolean ?? undefined;\n case \"date\":\n return formula.date?.start;\n }\n}\n\nfunction plainText(\n richText: Extract<NotionProperty, { type: \"title\" }>[\"title\"],\n): string {\n return richText.map((item) => item.plain_text).join(\"\");\n}\n\nfunction entityName(entity: {\n readonly id: string;\n readonly name?: string | null;\n}): string {\n return entity.name ?? entity.id;\n}\n\nfunction asString(value: MetadataValue): string | undefined {\n return typeof value === \"string\" ? value : undefined;\n}\n\nfunction asBoolean(value: MetadataValue): boolean | undefined {\n return typeof value === \"boolean\" ? value : undefined;\n}\n\nfunction asStringArray(value: MetadataValue): readonly string[] | undefined {\n if (typeof value === \"string\") return [value];\n return Array.isArray(value) ? value : undefined;\n}\n","import type { PageObjectResponse } from \"@notionhq/client\";\nimport { stringify } from \"yaml\";\nimport type { NotionPropertyMap } from \"./config.js\";\nimport { DEFAULT_LAYOUT_PATH } from \"./defaults.js\";\nimport { extractPageMetadata } from \"./metadata.js\";\nimport type { FrontmatterAdapter, FrontmatterRecord } from \"./types.js\";\n\nexport interface FrontmatterOptions {\n readonly layoutPath?: string;\n readonly propertyMap?: NotionPropertyMap;\n readonly frontmatter?: FrontmatterAdapter;\n readonly isDatabasePage?: boolean;\n}\n\nexport function renderFrontmatter(\n page: PageObjectResponse,\n options: FrontmatterOptions = {},\n): string {\n const { layoutPath = DEFAULT_LAYOUT_PATH } = options;\n if (options.frontmatter) {\n const metadata = extractPageMetadata(page, options.propertyMap);\n const values = options.frontmatter({\n page,\n metadata,\n layoutPath,\n isDatabasePage: options.isDatabasePage ?? true,\n });\n return serializeFrontmatter(values);\n }\n\n let title = \"\";\n let description = \"\";\n let tags: string[] = [];\n let pubDate = \"\";\n let archived = \"\";\n\n for (const [name, property] of Object.entries(page.properties)) {\n switch (property.type) {\n case \"title\":\n title = property.title.map((item) => item.plain_text).join(\"\");\n break;\n case \"multi_select\":\n if (name === \"tags\") {\n tags = property.multi_select.map((tag) => tag.name);\n }\n break;\n case \"date\":\n if (name === \"date\") {\n pubDate =\n property.date?.start ?? new Date().toISOString().slice(0, 10);\n }\n break;\n }\n\n if (name === \"archived\" && property.type === \"checkbox\") {\n archived = property.checkbox ? \"true\" : \"false\";\n }\n if (name === \"description\" && property.type === \"rich_text\") {\n description = property.rich_text\n .map((item) => item.plain_text)\n .join(\"\");\n }\n }\n\n return (\n \"---\\n\" +\n `layout: ${layoutPath}\\n` +\n `title: ${JSON.stringify(title)}\\n` +\n `description: ${JSON.stringify(description)}\\n` +\n `tags: ${JSON.stringify(tags)}\\n` +\n `pubDate: ${JSON.stringify(pubDate)}\\n` +\n `archived: ${JSON.stringify(archived)}\\n` +\n \"---\\n\"\n );\n}\n\nfunction serializeFrontmatter(values: FrontmatterRecord): string {\n if (\n values === null ||\n typeof values !== \"object\" ||\n Array.isArray(values) ||\n (Object.getPrototypeOf(values) !== Object.prototype &&\n Object.getPrototypeOf(values) !== null)\n ) {\n throw new Error(\"Frontmatter adapter must return a plain object\");\n }\n\n const entries = Object.entries(values).filter(\n ([, value]) => value !== undefined,\n );\n if (entries.length === 0) return \"---\\n---\\n\";\n\n const yaml = stringify(Object.fromEntries(entries), {\n aliasDuplicateObjects: false,\n schema: \"yaml-1.1\",\n });\n return `---\\n${yaml}---\\n`;\n}\n","import { markdownTable } from \"markdown-table\";\nimport type { CalloutIcon } from \"./types.js\";\n\nexport const inlineCode = (text: string) => `\\`${text}\\``;\nexport const inlineEquation = (text: string) => `$${text}$`;\nexport const bold = (text: string) => `**${text}**`;\nexport const italic = (text: string) => `_${text}_`;\nexport const strikethrough = (text: string) => `~~${text}~~`;\nexport const underline = (text: string) => `<u>${text}</u>`;\nexport const link = (text: string, href: string) => `[${text}](${href})`;\n\nexport function codeBlock(text: string, language = \"\"): string {\n const normalizedLanguage = language === \"plain text\" ? \"text\" : language;\n return `\\`\\`\\`${normalizedLanguage}\\n${text}\\n\\`\\`\\``;\n}\n\nexport const equation = (text: string) => `$$\\n${text}\\n$$`;\nexport const heading1 = (text: string) => `# ${text}`;\nexport const heading2 = (text: string) => `## ${text}`;\nexport const heading3 = (text: string) => `### ${text}`;\nexport const quote = (text: string) => `> ${text.replace(/\\n/g, \" \\n> \")}`;\n\nexport function callout(text: string, icon?: CalloutIcon): string {\n const emoji = icon?.type === \"emoji\" ? `${icon.emoji} ` : \"\";\n return `> ${emoji}${text.replace(/\\n/g, \" \\n> \")}`;\n}\n\nexport function bullet(text: string, count?: number): string {\n const rendered = text.trim();\n return count === undefined ? `- ${rendered}` : `${count}. ${rendered}`;\n}\n\nexport const todo = (text: string, checked: boolean) =>\n checked ? `- [x] ${text}` : `- [ ] ${text}`;\n\nexport async function image(\n alt: string,\n href: string,\n convertToBase64 = false,\n): Promise<string> {\n if (!convertToBase64 || href.startsWith(\"data:\")) {\n if (href.startsWith(\"data:\")) {\n const base64 = href.split(\",\").pop();\n return `![${alt}](data:image/png;base64,${base64})`;\n }\n return `![${alt}](${href})`;\n }\n\n const response = await fetch(href);\n if (!response.ok) {\n throw new Error(\n `Failed to download image: ${response.status} ${response.statusText}`,\n );\n }\n const buffer = await response.arrayBuffer();\n return `![${alt}](data:image/png;base64,${Buffer.from(buffer).toString(\"base64\")})`;\n}\n\nexport const divider = () => \"---\";\n\nexport function toggle(summary?: string, children?: string): string {\n if (!summary) return children ?? \"\";\n return `<details>\\n<summary>${summary}</summary>\\n${children ?? \"\"}\\n</details>\\n\\n`;\n}\n\nexport function table(cells: string[][], hasColumnHeader = true): string {\n if (hasColumnHeader || cells.length === 0) return markdownTable(cells);\n\n const width = Math.max(...cells.map((row) => row.length));\n if (width === 0) return \"\";\n const emptyHeader = Array.from({ length: width }, () => \"\");\n return markdownTable([emptyHeader, ...cells]);\n}\n","import type { RichTextItemResponse } from \"@notionhq/client\";\nimport { inlineEquation, link } from \"./markdown.js\";\n\ntype AnnotationName = Exclude<\n keyof RichTextItemResponse[\"annotations\"],\n \"color\"\n>;\n\ninterface AnnotationFormat {\n readonly name: AnnotationName;\n readonly open: string;\n readonly close: string;\n}\n\n// Ordered from the outermost marker to the innermost marker. Stable sorting\n// keeps this order when annotations end on the same rich-text segment.\nconst ANNOTATION_FORMATS: readonly AnnotationFormat[] = [\n { name: \"strikethrough\", open: \"~~\", close: \"~~\" },\n { name: \"underline\", open: \"<u>\", close: \"</u>\" },\n { name: \"italic\", open: \"_\", close: \"_\" },\n { name: \"bold\", open: \"**\", close: \"**\" },\n { name: \"code\", open: \"`\", close: \"`\" },\n];\n\nexport function renderPlainText(\n richText: readonly RichTextItemResponse[],\n): string {\n return richText.map((item) => item.plain_text).join(\"\");\n}\n\nexport function renderRichText(\n richText: readonly RichTextItemResponse[],\n): string {\n let rendered = \"\";\n let active: AnnotationFormat[] = [];\n\n for (const [index, item] of richText.entries()) {\n const desired = getAnnotationFormats(richText, index, item);\n let shared = 0;\n while (\n shared < active.length &&\n shared < desired.length &&\n active[shared]?.name === desired[shared]?.name\n ) {\n shared++;\n }\n\n for (const format of active.slice(shared).toReversed()) {\n rendered += format.close;\n }\n for (const format of desired.slice(shared)) {\n rendered += format.open;\n }\n\n rendered += renderContent(item);\n active = desired;\n }\n\n for (const format of active.toReversed()) {\n rendered += format.close;\n }\n return rendered;\n}\n\nfunction getAnnotationFormats(\n richText: readonly RichTextItemResponse[],\n index: number,\n item: RichTextItemResponse,\n): AnnotationFormat[] {\n if (item.type === \"equation\") return [];\n\n return ANNOTATION_FORMATS.filter((format) => item.annotations[format.name])\n .map((format) => ({\n format,\n runEnd: getAnnotationRunEnd(richText, index, format.name),\n }))\n .sort((left, right) => right.runEnd - left.runEnd)\n .map(({ format }) => format);\n}\n\nfunction getAnnotationRunEnd(\n richText: readonly RichTextItemResponse[],\n start: number,\n name: AnnotationName,\n): number {\n let end = start;\n for (const [offset, next] of richText.slice(start + 1).entries()) {\n if (next.type === \"equation\" || !next.annotations[name]) break;\n end = start + offset + 1;\n }\n return end;\n}\n\nfunction renderContent(item: RichTextItemResponse): string {\n if (item.type === \"equation\") {\n return inlineEquation(item.equation.expression);\n }\n return item.href ? link(item.plain_text, item.href) : item.plain_text;\n}\n","import type { BlockObjectResponse } from \"@notionhq/client\";\nimport * as md from \"./markdown.js\";\nimport { renderPlainText, renderRichText } from \"./rich-text.js\";\nimport type {\n BlockTransformerContext,\n BlockTransformers,\n NotionReader,\n} from \"./types.js\";\n\nexport interface BlockNode {\n readonly block: BlockObjectResponse;\n readonly children: BlockNode[];\n}\n\nexport interface BlockRenderContext extends BlockTransformerContext {\n readonly transformers?: BlockTransformers;\n}\n\nexport interface RenderedBlocks {\n content: string;\n childPageIds: string[];\n}\n\nexport async function loadBlockTree(\n blockId: string,\n reader: NotionReader,\n ancestors: ReadonlySet<string> = new Set(),\n): Promise<BlockNode[]> {\n if (ancestors.has(blockId)) return [];\n\n const blocks = await reader.getBlockChildren(blockId);\n const childAncestors = new Set(ancestors).add(blockId);\n const nodes: BlockNode[] = [];\n\n for (const block of blocks) {\n if (block.type === \"unsupported\") continue;\n\n const childSourceId = getChildSourceId(block);\n const children =\n childSourceId && !childAncestors.has(childSourceId)\n ? await loadBlockTree(childSourceId, reader, childAncestors)\n : [];\n nodes.push({ block, children });\n }\n\n return nodes;\n}\n\nfunction getChildSourceId(block: BlockObjectResponse): string | undefined {\n if (block.type === \"child_page\" || block.type === \"child_database\") {\n return undefined;\n }\n if (block.type === \"synced_block\") {\n return (\n block.synced_block.synced_from?.block_id ??\n (block.has_children ? block.id : undefined)\n );\n }\n return block.has_children ? block.id : undefined;\n}\n\nexport async function renderBlockTree(\n nodes: readonly BlockNode[],\n context: BlockRenderContext,\n): Promise<RenderedBlocks> {\n const result: RenderedBlocks = { content: \"\", childPageIds: [] };\n let numberedListIndex = 0;\n\n for (const node of nodes) {\n const { block } = node;\n numberedListIndex =\n block.type === \"numbered_list_item\" ? numberedListIndex + 1 : 0;\n\n const children =\n block.type === \"table\"\n ? { content: \"\", childPageIds: [] }\n : await renderBlockTree(node.children, context);\n const content = await renderBlockNode(\n node,\n children.content,\n numberedListIndex || undefined,\n context,\n );\n\n if (block.type === \"toggle\") {\n result.content += content;\n } else {\n const lineWrap = isListBlock(block) ? \"\" : \"\\n\";\n result.content += `${lineWrap}${content}\\n${lineWrap}`;\n if (!containsOwnChildren(block)) {\n result.content += children.content;\n }\n }\n\n if (block.type === \"child_page\") {\n result.childPageIds.push(block.id);\n } else {\n result.childPageIds.push(...children.childPageIds);\n }\n }\n\n return result;\n}\n\nfunction isListBlock(block: BlockObjectResponse): boolean {\n return (\n block.type === \"to_do\" ||\n block.type === \"bulleted_list_item\" ||\n block.type === \"numbered_list_item\"\n );\n}\n\nfunction containsOwnChildren(block: BlockObjectResponse): boolean {\n return (\n block.type === \"child_page\" ||\n block.type === \"callout\" ||\n block.type === \"bulleted_list_item\" ||\n block.type === \"numbered_list_item\" ||\n block.type === \"table\"\n );\n}\n\nfunction transformBlock<K extends BlockObjectResponse[\"type\"]>(\n type: K,\n block: Extract<BlockObjectResponse, { type: NoInfer<K> }>,\n context: BlockRenderContext,\n): string | undefined | Promise<string | undefined> {\n const transformer = context.transformers?.[type];\n return transformer?.(block, context);\n}\n\nasync function renderBlockNode(\n node: BlockNode,\n children: string,\n listNumber: number | undefined,\n context: BlockRenderContext,\n): Promise<string> {\n const { block } = node;\n const transformed = await transformBlock(block.type, block, context);\n const content = transformed ?? (await renderDefaultBlock(node));\n\n switch (block.type) {\n case \"toggle\":\n return md.toggle(content, children);\n case \"callout\":\n return md.callout(\n children ? `${content}\\n${children}`.trim() : content,\n block.callout.icon,\n );\n case \"bulleted_list_item\":\n return md.bullet(withIndentedChildren(content, children, 2));\n case \"numbered_list_item\":\n return md.bullet(\n withIndentedChildren(\n content,\n children,\n `${listNumber ?? 1}. `.length,\n ),\n listNumber,\n );\n default:\n return content;\n }\n}\n\nfunction withIndentedChildren(\n content: string,\n children: string,\n markerWidth: number,\n): string {\n const nested = children.trim();\n if (!nested) return content;\n\n const indentation = \" \".repeat(markerWidth);\n const indented = nested\n .split(\"\\n\")\n .map((line) => (line ? `${indentation}${line}` : \"\"))\n .join(\"\\n\");\n return `${content}\\n${indented}`;\n}\n\nasync function renderDefaultBlock(node: BlockNode): Promise<string> {\n const { block } = node;\n\n switch (block.type) {\n case \"image\": {\n const url = getFileUrl(block.image);\n const caption = renderPlainText(block.image.caption);\n const alt = caption.trim() || getFileName(url) || \"image\";\n return md.image(alt, url);\n }\n case \"divider\":\n return md.divider();\n case \"equation\":\n return md.equation(block.equation.expression);\n case \"pdf\":\n return renderFileLink(block.pdf, block.type);\n case \"file\":\n return renderFileLink(block.file, block.type);\n case \"video\":\n return renderFileLink(block.video, block.type);\n case \"audio\":\n return renderFileLink(block.audio, block.type);\n case \"embed\":\n return md.link(block.type, block.embed.url);\n case \"bookmark\":\n return md.link(block.type, block.bookmark.url);\n case \"link_preview\":\n return md.link(block.type, block.link_preview.url);\n case \"link_to_page\": {\n const target = block.link_to_page;\n if (target.type !== \"page_id\" && target.type !== \"database_id\") {\n return \"\";\n }\n const id = target.type === \"page_id\" ? target.page_id : target.database_id;\n return md.link(block.type, `https://www.notion.so/${id}`);\n }\n case \"child_page\":\n return renderChildPageLink(block);\n case \"child_database\":\n return md.heading2(block.child_database.title || \"child_database\");\n case \"table\":\n return renderTable(node.children, block.table.has_column_header);\n case \"code\":\n return md.codeBlock(\n renderRichText(block.code.rich_text),\n block.code.language,\n );\n case \"heading_1\":\n return md.heading1(renderRichText(block.heading_1.rich_text));\n case \"heading_2\":\n return md.heading2(renderRichText(block.heading_2.rich_text));\n case \"heading_3\":\n return md.heading3(renderRichText(block.heading_3.rich_text));\n case \"quote\":\n return md.quote(renderRichText(block.quote.rich_text));\n case \"to_do\":\n return md.todo(\n renderRichText(block.to_do.rich_text),\n block.to_do.checked,\n );\n case \"paragraph\":\n return renderRichText(block.paragraph.rich_text);\n case \"toggle\":\n return renderRichText(block.toggle.rich_text);\n case \"callout\":\n return renderRichText(block.callout.rich_text);\n case \"bulleted_list_item\":\n return renderRichText(block.bulleted_list_item.rich_text);\n case \"numbered_list_item\":\n return renderRichText(block.numbered_list_item.rich_text);\n case \"template\":\n return renderRichText(block.template.rich_text);\n default:\n return \"\";\n }\n}\n\ntype FileContent =\n | Extract<BlockObjectResponse, { type: \"image\" }>[\"image\"]\n | Extract<BlockObjectResponse, { type: \"pdf\" }>[\"pdf\"]\n | Extract<BlockObjectResponse, { type: \"file\" }>[\"file\"]\n | Extract<BlockObjectResponse, { type: \"video\" }>[\"video\"]\n | Extract<BlockObjectResponse, { type: \"audio\" }>[\"audio\"];\n\nfunction getFileUrl(content: FileContent): string {\n if (content.type === \"external\") return content.external.url;\n return content.file.url;\n}\n\nfunction getFileName(url: string): string | undefined {\n return url.match(/[^\\/\\\\&?]+\\.\\w{3,4}(?=([?&].*$|$))/)?.[0];\n}\n\nfunction renderFileLink(content: FileContent, fallbackTitle: string): string {\n const url = getFileUrl(content);\n const caption = renderPlainText(content.caption);\n const title = caption.trim() || getFileName(url) || fallbackTitle;\n return md.link(title, url);\n}\n\nfunction renderChildPageLink(\n block: Extract<BlockObjectResponse, { type: \"child_page\" }>,\n): string {\n return `(」゚ロ゚)」 ${md.link(block.child_page.title, `./${block.id}.md`)}`;\n}\n\nfunction renderTable(\n nodes: readonly BlockNode[],\n hasColumnHeader: boolean,\n): string {\n const rows = nodes.flatMap(({ block }) => {\n if (block.type !== \"table_row\") return [];\n return [\n block.table_row.cells.map((cell) => renderRichText(cell)),\n ];\n });\n return md.table(rows, hasColumnHeader);\n}\n","import type { PageObjectResponse } from \"@notionhq/client\";\nimport { loadBlockTree, renderBlockTree } from \"./blocks.js\";\nimport { DEFAULT_LAYOUT_PATH } from \"./defaults.js\";\nimport { renderFrontmatter } from \"./frontmatter.js\";\nimport type { NotionPropertyMap } from \"./config.js\";\nimport type {\n BlockTransformers,\n FrontmatterAdapter,\n NotionReader,\n RenderedPage,\n RenderOptions,\n} from \"./types.js\";\n\ninterface PageRenderContext {\n readonly reader: NotionReader;\n readonly databasePageIds: ReadonlySet<string>;\n readonly layoutPath: string;\n readonly propertyMap: NotionPropertyMap | undefined;\n readonly frontmatter: FrontmatterAdapter | undefined;\n readonly recursive: boolean;\n readonly transformers: BlockTransformers | undefined;\n readonly renderedPages: Map<string, Promise<RenderedPage>>;\n}\n\nexport async function renderPage(\n pageId: string,\n reader: NotionReader,\n options: RenderOptions = {},\n): Promise<RenderedPage> {\n const context = createContext([pageId], reader, options);\n return collectPage(pageId, context);\n}\n\nexport async function renderPages(\n posts: readonly PageObjectResponse[],\n reader: NotionReader,\n options: RenderOptions = {},\n): Promise<RenderedPage[]> {\n const context = createContext(\n posts.map((post) => post.id),\n reader,\n options,\n );\n\n for (const post of posts) {\n await collectPage(post.id, context, post);\n }\n\n return Promise.all([...context.renderedPages.values()]);\n}\n\nfunction createContext(\n databasePageIds: readonly string[],\n reader: NotionReader,\n options: RenderOptions,\n): PageRenderContext {\n return {\n reader,\n databasePageIds: new Set(databasePageIds),\n layoutPath: options.layoutPath ?? DEFAULT_LAYOUT_PATH,\n propertyMap: options.propertyMap,\n frontmatter: options.frontmatter,\n recursive: options.recursive ?? true,\n transformers: options.transformers,\n renderedPages: new Map(),\n };\n}\n\nasync function collectPage(\n pageId: string,\n context: PageRenderContext,\n post?: PageObjectResponse,\n): Promise<RenderedPage> {\n const cached = context.renderedPages.get(pageId);\n if (cached) return cached;\n\n const rendering = renderSinglePage(pageId, context, post);\n context.renderedPages.set(pageId, rendering);\n const rendered = await rendering;\n\n if (context.recursive) {\n for (const childPageId of rendered.childPageIds) {\n await collectPage(childPageId, context);\n }\n }\n\n return rendered;\n}\n\nasync function renderSinglePage(\n pageId: string,\n context: PageRenderContext,\n post?: PageObjectResponse,\n): Promise<RenderedPage> {\n const [page, blockTree] = await Promise.all([\n post ? Promise.resolve(post) : context.reader.getPage(pageId),\n loadBlockTree(pageId, context.reader),\n ]);\n const rendered = await renderBlockTree(blockTree, {\n pageId,\n ...(context.transformers !== undefined\n ? { transformers: context.transformers }\n : {}),\n });\n const frontmatter = renderFrontmatter(page, {\n layoutPath: context.layoutPath,\n ...(context.propertyMap !== undefined\n ? { propertyMap: context.propertyMap }\n : {}),\n ...(context.frontmatter !== undefined\n ? { frontmatter: context.frontmatter }\n : {}),\n isDatabasePage: context.databasePageIds.has(pageId),\n });\n\n return {\n id: pageId,\n content: frontmatter + rendered.content,\n childPageIds: [...new Set(rendered.childPageIds)],\n isDatabasePage: context.databasePageIds.has(pageId),\n };\n}\n","import { randomUUID } from \"node:crypto\";\nimport {\n lstat,\n mkdir,\n readlink,\n rename,\n rm,\n symlink,\n writeFile,\n} from \"node:fs/promises\";\nimport { dirname, join, relative, resolve, sep } from \"node:path\";\nimport { getErrorCode } from \"../errors.js\";\nimport {\n DEFAULT_LAYOUT_PATH,\n DEFAULT_SAVE_PATH,\n DEFAULT_STORE_PATH,\n} from \"./defaults.js\";\nimport type {\n RenderedPage,\n StoreLayoutOptions,\n WritePagesOptions,\n} from \"./types.js\";\n\nexport async function writePages(\n pages: readonly RenderedPage[],\n options: WritePagesOptions = {},\n): Promise<string[]> {\n const savePath = options.savePath ?? DEFAULT_SAVE_PATH;\n const storePath = options.storePath ?? DEFAULT_STORE_PATH;\n const writtenPaths: string[] = [];\n await mkdir(storePath, { recursive: true });\n\n for (const page of pages) {\n const storedPath = join(storePath, `${page.id}.md`);\n const storedStats = await getStats(storedPath);\n if (storedStats && !storedStats.isFile()) {\n throw new Error(`Page store path is not a regular file: ${storedPath}`);\n }\n await writeFile(storedPath, page.content, \"utf8\");\n\n if (!page.isDatabasePage) continue;\n\n const viewPath = join(savePath, `${page.id}.md`);\n await mkdir(savePath, { recursive: true });\n await linkStoredPage(storedPath, viewPath);\n writtenPaths.push(viewPath);\n }\n\n return writtenPaths;\n}\n\nexport function resolveStoreLayoutPath(\n options: StoreLayoutOptions = {},\n): string {\n const savePath = options.savePath ?? DEFAULT_SAVE_PATH;\n const storePath = options.storePath ?? DEFAULT_STORE_PATH;\n const layoutPath = options.layoutPath ?? DEFAULT_LAYOUT_PATH;\n const layoutTarget = resolve(savePath, layoutPath);\n return relative(resolve(storePath), layoutTarget).split(sep).join(\"/\");\n}\n\nasync function linkStoredPage(\n storedPath: string,\n viewPath: string,\n): Promise<void> {\n if (resolve(storedPath) === resolve(viewPath)) return;\n\n const viewStats = await getStats(viewPath);\n if (viewStats && !viewStats.isFile() && !viewStats.isSymbolicLink()) {\n throw new Error(`Refusing to replace non-file output: ${viewPath}`);\n }\n\n const target = relative(dirname(viewPath), storedPath);\n if (viewStats?.isSymbolicLink() && (await readlink(viewPath)) === target) {\n return;\n }\n\n const temporaryPath = `${viewPath}.noblog-${randomUUID()}.tmp`;\n try {\n await symlink(target, temporaryPath, \"file\");\n await rename(temporaryPath, viewPath);\n } catch (error) {\n await rm(temporaryPath, { force: true }).catch(() => {});\n throw error;\n }\n}\n\nasync function getStats(path: string) {\n try {\n return await lstat(path);\n } catch (error: unknown) {\n if (getErrorCode(error) === \"ENOENT\") return undefined;\n throw error;\n }\n}\n","import {\n Client,\n isFullBlock,\n isFullDatabase,\n isFullPage,\n isNotionClientError,\n} from \"@notionhq/client\";\nimport type {\n BlockObjectResponse,\n PageObjectResponse,\n} from \"@notionhq/client\";\nimport type { NotionReader } from \"../noblog/types.js\";\n\nexport interface NotionSourceConfig {\n readonly notionApiSecret: string;\n readonly databaseId: string;\n}\n\nexport interface NotionSource extends NotionReader {\n listPages(): Promise<PageObjectResponse[]>;\n}\n\nexport function createNotionSource(\n config: NotionSourceConfig,\n): NotionSource {\n const client = new Client({ auth: config.notionApiSecret });\n return {\n listPages: () => listPages(client, config.databaseId),\n getPage: (pageId) => getPage(client, pageId),\n getBlockChildren: (blockId) => getBlockChildren(client, blockId),\n };\n}\n\nasync function listPages(\n client: Client,\n databaseId: string,\n): Promise<PageObjectResponse[]> {\n const database = await withRetry(() =>\n client.databases.retrieve({ database_id: databaseId }),\n );\n if (!isFullDatabase(database)) {\n throw new Error(`Notion database ${databaseId} is not a full database`);\n }\n\n const pages: PageObjectResponse[] = [];\n for (const source of database.data_sources) {\n let startCursor: string | undefined;\n do {\n const response = await withRetry(() =>\n client.dataSources.query({\n data_source_id: source.id,\n filter: {\n property: \"publish\",\n checkbox: { equals: true },\n },\n sorts: [{ property: \"date\", direction: \"descending\" }],\n page_size: 100,\n ...(startCursor !== undefined\n ? { start_cursor: startCursor }\n : {}),\n }),\n );\n pages.push(...response.results.filter(isFullPage));\n startCursor = response.next_cursor ?? undefined;\n if (!response.has_more) break;\n } while (startCursor);\n }\n\n return pages;\n}\n\nasync function getPage(\n client: Client,\n pageId: string,\n): Promise<PageObjectResponse> {\n const page = await withRetry(() => client.pages.retrieve({ page_id: pageId }));\n if (!isFullPage(page)) {\n throw new Error(`Notion page ${pageId} is not a full page`);\n }\n return page;\n}\n\nasync function getBlockChildren(\n client: Client,\n blockId: string,\n): Promise<BlockObjectResponse[]> {\n const blocks: BlockObjectResponse[] = [];\n let startCursor: string | undefined;\n\n do {\n const response = await withRetry(() =>\n client.blocks.children.list({\n block_id: blockId,\n page_size: 100,\n ...(startCursor !== undefined ? { start_cursor: startCursor } : {}),\n }),\n );\n blocks.push(...response.results.filter(isFullBlock));\n startCursor = response.next_cursor ?? undefined;\n } while (startCursor);\n\n return blocks;\n}\n\nconst MAX_ATTEMPTS = 3;\nconst INITIAL_RETRY_DELAY_MS = 500;\n\nasync function withRetry<T>(operation: () => Promise<T>): Promise<T> {\n for (let attempt = 0; ; attempt++) {\n try {\n return await operation();\n } catch (error: unknown) {\n if (attempt === MAX_ATTEMPTS - 1 || !isRetryable(error)) throw error;\n await delay(INITIAL_RETRY_DELAY_MS * 2 ** attempt);\n }\n }\n}\n\nfunction isRetryable(error: unknown): boolean {\n if (!isNotionClientError(error) || !(\"status\" in error)) return true;\n return error.status === 408 || error.status === 429 || error.status >= 500;\n}\n\nfunction delay(milliseconds: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, milliseconds));\n}\n"],"mappings":"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"}
@@ -1,8 +0,0 @@
1
- //#region src/lib/noblog/defaults.ts
2
- const DEFAULT_SAVE_PATH = "./src/pages/posts/";
3
- const DEFAULT_STORE_PATH = ".noblog/pages/";
4
- const DEFAULT_LAYOUT_PATH = "../../layouts/MarkdownPostLayout.astro";
5
- //#endregion
6
- export { DEFAULT_SAVE_PATH as n, DEFAULT_STORE_PATH as r, DEFAULT_LAYOUT_PATH as t };
7
-
8
- //# sourceMappingURL=defaults-D9BbNYsd.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"defaults-D9BbNYsd.js","names":[],"sources":["../src/lib/noblog/defaults.ts"],"sourcesContent":["export const DEFAULT_SAVE_PATH = \"./src/pages/posts/\";\nexport const DEFAULT_STORE_PATH = \".noblog/pages/\";\nexport const DEFAULT_LAYOUT_PATH = \"../../layouts/MarkdownPostLayout.astro\";\n"],"mappings":";AAAA,MAAa,oBAAoB;AACjC,MAAa,qBAAqB;AAClC,MAAa,sBAAsB"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"errors-DgJ15MsB.js","names":[],"sources":["../src/lib/noblog/config.ts","../src/lib/errors.ts"],"sourcesContent":["import { z } from \"zod\";\n\nexport type ConfigValue =\n | string\n | number\n | boolean\n | null\n | ConfigValue[]\n | { [key: string]: ConfigValue };\n\nconst configValueSchema: z.ZodType<ConfigValue> = z.lazy(() =>\n z.union([\n z.string(),\n z.number().finite(),\n z.boolean(),\n z.null(),\n z.array(configValueSchema),\n z.record(z.string(), configValueSchema),\n ]),\n);\n\nconst fieldModifiers = {\n as: z\n .enum([\"string\", \"date\"])\n .optional()\n .describe(\n \"Convert to a string (quoting date-like strings) or a YAML date.\",\n ),\n default: configValueSchema\n .optional()\n .describe(\"Value used when the source is empty.\"),\n omitEmpty: z\n .boolean()\n .optional()\n .describe(\"Omit undefined, null, empty strings, and empty arrays.\"),\n required: z\n .boolean()\n .optional()\n .describe(\"Fail generation if the value is still empty after defaults.\"),\n};\n\nexport const frontmatterFieldSchema = z.union([\n z.strictObject({\n ...fieldModifiers,\n from: z\n .string()\n .min(1)\n .describe(\"Built-in source or a key declared under notion.properties.\"),\n }),\n z.strictObject({\n ...fieldModifiers,\n value: configValueSchema,\n }),\n]);\n\nexport const frontmatterFieldsSchema = z.record(\n z.string().min(1),\n frontmatterFieldSchema,\n);\n\nexport const notionPropertiesSchema = z.record(\n z.string().min(1),\n z.string().min(1),\n);\n\nexport const BUILT_IN_FRONTMATTER_SOURCES = [\n \"title\",\n \"description\",\n \"tags\",\n \"publishedAt\",\n \"archived\",\n \"layoutPath\",\n \"pageId\",\n \"isDatabasePage\",\n] as const;\n\nconst builtInSources: ReadonlySet<string> = new Set(\n BUILT_IN_FRONTMATTER_SOURCES,\n);\n\nexport const noblogConfigSchema = z\n .strictObject({\n version: z.literal(1),\n notion: z\n .strictObject({\n properties: notionPropertiesSchema.optional(),\n })\n .optional(),\n output: z\n .strictObject({\n savePath: z\n .string()\n .min(1)\n .optional()\n .describe(\"Public directory for database-post symlinks.\"),\n storePath: z\n .string()\n .min(1)\n .optional()\n .describe(\"Flat directory containing every canonical Markdown file.\"),\n layoutPath: z\n .string()\n .min(1)\n .optional()\n .describe(\"Layout import path interpreted relative to savePath.\"),\n recursive: z\n .boolean()\n .optional()\n .describe(\"Render child pages recursively (default: true).\"),\n })\n .optional(),\n frontmatter: z\n .strictObject({\n fields: frontmatterFieldsSchema.optional(),\n })\n .optional(),\n })\n .superRefine((config, context) => {\n const properties = config.notion?.properties ?? {};\n for (const [name, field] of Object.entries(\n config.frontmatter?.fields ?? {},\n )) {\n if (!(\"from\" in field)) continue;\n if (\n !builtInSources.has(field.from) &&\n !Object.hasOwn(properties, field.from)\n ) {\n context.addIssue({\n code: \"custom\",\n message: `Unknown frontmatter source \"${field.from}\"; declare it under notion.properties`,\n path: [\"frontmatter\", \"fields\", name, \"from\"],\n });\n }\n }\n })\n .describe(\n \"Noblog YAML configuration. Notion credentials remain in environment variables or .env files.\",\n );\n\nexport type FrontmatterField = z.infer<typeof frontmatterFieldSchema>;\nexport type FrontmatterFields = z.infer<typeof frontmatterFieldsSchema>;\nexport type NotionPropertyMap = z.infer<typeof notionPropertiesSchema>;\nexport type NoblogFileConfig = z.infer<typeof noblogConfigSchema>;\n","export function getErrorCode(error: unknown): string | undefined {\n if (\n typeof error !== \"object\" ||\n error === null ||\n !(\"code\" in error) ||\n typeof error.code !== \"string\"\n ) {\n return undefined;\n }\n return error.code;\n}\n"],"mappings":";;AAUA,MAAM,oBAA4C,EAAE,WAClD,EAAE,MAAM;CACN,EAAE,OAAO;CACT,EAAE,OAAO,CAAC,CAAC,OAAO;CAClB,EAAE,QAAQ;CACV,EAAE,KAAK;CACP,EAAE,MAAM,iBAAiB;CACzB,EAAE,OAAO,EAAE,OAAO,GAAG,iBAAiB;AACxC,CAAC,CACH;AAEA,MAAM,iBAAiB;CACrB,IAAI,EACD,KAAK,CAAC,UAAU,MAAM,CAAC,CAAC,CACxB,SAAS,CAAC,CACV,SACC,iEACF;CACF,SAAS,kBACN,SAAS,CAAC,CACV,SAAS,sCAAsC;CAClD,WAAW,EACR,QAAQ,CAAC,CACT,SAAS,CAAC,CACV,SAAS,wDAAwD;CACpE,UAAU,EACP,QAAQ,CAAC,CACT,SAAS,CAAC,CACV,SAAS,6DAA6D;AAC3E;AAEA,MAAa,yBAAyB,EAAE,MAAM,CAC5C,EAAE,aAAa;CACb,GAAG;CACH,MAAM,EACH,OAAO,CAAC,CACR,IAAI,CAAC,CAAC,CACN,SAAS,4DAA4D;AAC1E,CAAC,GACD,EAAE,aAAa;CACb,GAAG;CACH,OAAO;AACT,CAAC,CACH,CAAC;AAED,MAAa,0BAA0B,EAAE,OACvC,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,GAChB,sBACF;AAEA,MAAa,yBAAyB,EAAE,OACtC,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,GAChB,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAClB;AAEA,MAAa,+BAA+B;CAC1C;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;AAEA,MAAM,iBAAsC,IAAI,IAC9C,4BACF;AAEA,MAAa,qBAAqB,EAC/B,aAAa;CACZ,SAAS,EAAE,QAAQ,CAAC;CACpB,QAAQ,EACL,aAAa,EACZ,YAAY,uBAAuB,SAAS,EAC9C,CAAC,CAAC,CACD,SAAS;CACZ,QAAQ,EACL,aAAa;EACZ,UAAU,EACP,OAAO,CAAC,CACR,IAAI,CAAC,CAAC,CACN,SAAS,CAAC,CACV,SAAS,8CAA8C;EAC1D,WAAW,EACR,OAAO,CAAC,CACR,IAAI,CAAC,CAAC,CACN,SAAS,CAAC,CACV,SAAS,0DAA0D;EACtE,YAAY,EACT,OAAO,CAAC,CACR,IAAI,CAAC,CAAC,CACN,SAAS,CAAC,CACV,SAAS,sDAAsD;EAClE,WAAW,EACR,QAAQ,CAAC,CACT,SAAS,CAAC,CACV,SAAS,iDAAiD;CAC/D,CAAC,CAAC,CACD,SAAS;CACZ,aAAa,EACV,aAAa,EACZ,QAAQ,wBAAwB,SAAS,EAC3C,CAAC,CAAC,CACD,SAAS;AACd,CAAC,CAAC,CACD,aAAa,QAAQ,YAAY;CAChC,MAAM,aAAa,OAAO,QAAQ,cAAc,CAAC;CACjD,KAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QACjC,OAAO,aAAa,UAAU,CAAC,CACjC,GAAG;EACD,IAAI,EAAE,UAAU,QAAQ;EACxB,IACE,CAAC,eAAe,IAAI,MAAM,IAAI,KAC9B,CAAC,OAAO,OAAO,YAAY,MAAM,IAAI,GAErC,QAAQ,SAAS;GACf,MAAM;GACN,SAAS,+BAA+B,MAAM,KAAK;GACnD,MAAM;IAAC;IAAe;IAAU;IAAM;GAAM;EAC9C,CAAC;CAEL;AACF,CAAC,CAAC,CACD,SACC,8FACF;;;ACzIF,SAAgB,aAAa,OAAoC;CAC/D,IACE,OAAO,UAAU,YACjB,UAAU,QACV,EAAE,UAAU,UACZ,OAAO,MAAM,SAAS,UAEtB;CAEF,OAAO,MAAM;AACf"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"generate.impl-BKu-Ix4a.js","names":[],"sources":["../src/commands/generate.impl.ts"],"sourcesContent":["import { ConfigurationError, loadConfig } from \"../config.js\";\nimport { loadNoblogConfig } from \"../config-file.js\";\nimport type { CliContext } from \"../context.js\";\nimport {\n DEFAULT_LAYOUT_PATH,\n DEFAULT_SAVE_PATH,\n DEFAULT_STORE_PATH,\n} from \"../lib/noblog/defaults.js\";\nimport {\n createFrontmatterAdapter,\n DEFAULT_FRONTMATTER_FIELDS,\n} from \"../lib/noblog/frontmatter-fields.js\";\n\ninterface GenerateFlags {\n verbose: boolean;\n config?: string;\n layout?: string;\n store?: string;\n}\n\nexport default async function generate(\n this: CliContext,\n flags: GenerateFlags,\n savePath?: string,\n legacySubPath?: string,\n): Promise<Error | void> {\n try {\n const fileConfig = flags.config\n ? await loadNoblogConfig(flags.config)\n : undefined;\n const config = loadConfig(this.process.env);\n if (legacySubPath !== undefined) {\n this.process.stderr.write(\n \"Warning: SUB_PATH is deprecated and ignored; child pages now live only in the canonical store.\\n\",\n );\n }\n\n const propertyMap = fileConfig?.notion?.properties;\n const fields = fileConfig?.frontmatter?.fields;\n const frontmatter =\n fields || propertyMap\n ? createFrontmatterAdapter(fields ?? DEFAULT_FRONTMATTER_FIELDS)\n : undefined;\n\n await this.generateBlog(\n config,\n {\n savePath:\n savePath ?? fileConfig?.output?.savePath ?? DEFAULT_SAVE_PATH,\n storePath:\n flags.store ?? fileConfig?.output?.storePath ?? DEFAULT_STORE_PATH,\n layoutPath:\n flags.layout ?? fileConfig?.output?.layoutPath ?? DEFAULT_LAYOUT_PATH,\n recursive: fileConfig?.output?.recursive ?? true,\n ...(propertyMap !== undefined ? { propertyMap } : {}),\n ...(frontmatter !== undefined ? { frontmatter } : {}),\n verbose: flags.verbose,\n },\n this.process,\n );\n } catch (error) {\n if (error instanceof ConfigurationError) {\n return error;\n }\n if (!(error instanceof Error)) {\n return new Error(\"Generation failed: an unknown error occurred\");\n }\n\n const message = flags.verbose\n ? (error.stack ?? error.message)\n : error.message;\n return new Error(`Generation failed: ${message}`, { cause: error });\n }\n}\n"],"mappings":";;;;;AAoBA,eAA8B,SAE5B,OACA,UACA,eACuB;CACvB,IAAI;EACF,MAAM,aAAa,MAAM,SACrB,MAAM,iBAAiB,MAAM,MAAM,IACnC,KAAA;EACJ,MAAM,SAAS,WAAW,KAAK,QAAQ,GAAG;EAC1C,IAAI,kBAAkB,KAAA,GACpB,KAAK,QAAQ,OAAO,MAClB,kGACF;EAGF,MAAM,cAAc,YAAY,QAAQ;EACxC,MAAM,SAAS,YAAY,aAAa;EACxC,MAAM,cACJ,UAAU,cACN,yBAAyB,UAAU,0BAA0B,IAC7D,KAAA;EAEN,MAAM,KAAK,aACT,QACA;GACE,UACE,YAAY,YAAY,QAAQ,YAAA;GAClC,WACE,MAAM,SAAS,YAAY,QAAQ,aAAA;GACrC,YACE,MAAM,UAAU,YAAY,QAAQ,cAAA;GACtC,WAAW,YAAY,QAAQ,aAAa;GAC5C,GAAI,gBAAgB,KAAA,IAAY,EAAE,YAAY,IAAI,CAAC;GACnD,GAAI,gBAAgB,KAAA,IAAY,EAAE,YAAY,IAAI,CAAC;GACnD,SAAS,MAAM;EACjB,GACA,KAAK,OACP;CACF,SAAS,OAAO;EACd,IAAI,iBAAiB,oBACnB,OAAO;EAET,IAAI,EAAE,iBAAiB,QACrB,uBAAO,IAAI,MAAM,8CAA8C;EAGjE,MAAM,UAAU,MAAM,UACjB,MAAM,SAAS,MAAM,UACtB,MAAM;EACV,OAAO,IAAI,MAAM,sBAAsB,WAAW,EAAE,OAAO,MAAM,CAAC;CACpE;AACF"}