@bitcobblers/wod-wiki-engine 0.35.2117 → 0.35.2120

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.
@@ -1,6 +1,6 @@
1
1
  import { createRequire } from 'module';
2
2
  import { parseFrontmatter, SEED_REGISTERED_WIDGETS, canvasRouteSlug, seedNoteSourceId, parseEffortFile, TEMPLATE_CHUNK_ID, SEED_SCHEMA, DASHBOARDS_CHUNK_ID } from './chunk-Y6WO6MXS.mjs';
3
- import { parseScript } from '@bitcobblers/wod-wiki-lang';
3
+ import { whiteboardParser, parseScript } from '@bitcobblers/wod-wiki-lang';
4
4
  import { reconcileNoteBlocks, parseDocumentSections, blockContentId } from '@bitcobblers/wod-wiki-core';
5
5
  import { parseDashboardNote, buildDashboardDocument, isDashboardWidgetType, resolveWidgetType, parseQuery } from '@bitcobblers/wod-wiki-wql';
6
6
  import { execSync } from 'child_process';
@@ -130,6 +130,7 @@ function writeBuildReport(outDir, data) {
130
130
  <dt>total</dt><dd>${totalMs} ms</dd>
131
131
  <dt>corpus scan + partition</dt><dd>${passMs.scan} ms (${fileCount} files)</dd>
132
132
  <dt>block-index derivation</dt><dd>${passMs.blockIndex} ms</dd>
133
+ <dt>block syntax gate</dt><dd>${passMs.parseGate} ms</dd>
133
134
  <dt>block-efforts derivation</dt><dd>${passMs.blockEfforts} ms</dd>
134
135
  <dt>chunk writes</dt><dd>${passMs.write} ms</dd>
135
136
  </dl>
@@ -273,6 +274,26 @@ function buildBlockIndexRows(markdownDir, corpusRoot) {
273
274
  }
274
275
  return rows;
275
276
  }
277
+ function validateWodBlockSyntax(blocks) {
278
+ const problems = [];
279
+ for (const block of blocks) {
280
+ if (block.dataType !== "wod" || !block.rawContent?.trim()) continue;
281
+ const section = parseDocumentSections(block.rawContent, { registeredWidgets: SEED_REGISTERED_WIDGETS }).find((s) => s.scriptBlock);
282
+ const source = section?.scriptBlock?.content ?? block.rawContent;
283
+ if (!source.trim()) continue;
284
+ const doc = source.endsWith("\n") ? source : source + "\n";
285
+ const tree = whiteboardParser.parse(doc);
286
+ tree.iterate({
287
+ enter: (node) => {
288
+ if (!node.type.isError) return;
289
+ const line = doc.slice(0, node.from).split("\n").length;
290
+ const snippet = source.slice(node.from, Math.min(node.to, node.from + 60)).split("\n")[0];
291
+ problems.push(`${block.sourcePath ?? block.noteId}:${line} \`${snippet}\``);
292
+ }
293
+ });
294
+ }
295
+ return problems;
296
+ }
276
297
  var NON_EFFORT_SLUGS = {
277
298
  "amrap": true,
278
299
  "emom": true,
@@ -441,7 +462,7 @@ function sha256Hex(data) {
441
462
  }
442
463
  function generateSeed(options = {}) {
443
464
  const t0 = Date.now();
444
- const passMs = { scan: 0, blockIndex: 0, blockEfforts: 0, write: 0 };
465
+ const passMs = { scan: 0, blockIndex: 0, parseGate: 0, blockEfforts: 0, write: 0 };
445
466
  const corpusRoot = options.corpusRoot ?? process.cwd();
446
467
  const markdownDir = options.markdownDir ?? join(corpusRoot, "markdown");
447
468
  const outDir = options.outDir ?? join(corpusRoot, "dist");
@@ -461,13 +482,23 @@ function generateSeed(options = {}) {
461
482
  groups.push({ id: TEMPLATE_CHUNK_ID, files: [templateRowPath] });
462
483
  }
463
484
  passMs.scan = Date.now() - tScan;
485
+ const tBlockIndex = Date.now();
486
+ const blockRows = buildBlockIndexRows(markdownDir, corpusRoot);
487
+ passMs.blockIndex = Date.now() - tBlockIndex;
488
+ const tParseGate = Date.now();
489
+ const syntaxProblems = validateWodBlockSyntax(blockRows);
490
+ passMs.parseGate = Date.now() - tParseGate;
491
+ if (syntaxProblems.length > 0) {
492
+ const shown = syntaxProblems.slice(0, 20).map((p) => ` ${p}`).join("\n");
493
+ const more = syntaxProblems.length > 20 ? `
494
+ \u2026 and ${syntaxProblems.length - 20} more` : "";
495
+ throw new Error(`[wod build] block syntax gate failed \u2014 ${syntaxProblems.length} block(s) do not parse:
496
+ ${shown}${more}`);
497
+ }
464
498
  const chunksDir = join(outDir, "chunks");
465
499
  rmSync(outDir, { recursive: true, force: true });
466
500
  mkdirSync(chunksDir, { recursive: true });
467
501
  const manifestChunks = [];
468
- const tBlockIndex = Date.now();
469
- const blockRows = buildBlockIndexRows(markdownDir, corpusRoot);
470
- passMs.blockIndex = Date.now() - tBlockIndex;
471
502
  const tBlockEfforts = Date.now();
472
503
  const effortRows = buildBlockEffortRows(markdownDir, corpusRoot, blockRows);
473
504
  passMs.blockEfforts = Date.now() - tBlockEfforts;
@@ -520,6 +551,6 @@ function generateSeed(options = {}) {
520
551
  return { manifest, outDir, totalFiles, reportPath, passMs };
521
552
  }
522
553
 
523
- export { buildBlockEffortRows, buildBlockIndexRows, chunkIdFor, collectCorpusMarkdown, compileSeedRow, discoverUncatalogedEfforts, feedDateToCreatedAt, generateSeed, loadExistingEffortMap, partitionIntoChunks, scaffoldDiscoveredEfforts, validateDashboardNote };
524
- //# sourceMappingURL=compiler-MQRZVZQW.mjs.map
525
- //# sourceMappingURL=compiler-MQRZVZQW.mjs.map
554
+ export { buildBlockEffortRows, buildBlockIndexRows, chunkIdFor, collectCorpusMarkdown, compileSeedRow, discoverUncatalogedEfforts, feedDateToCreatedAt, generateSeed, loadExistingEffortMap, partitionIntoChunks, scaffoldDiscoveredEfforts, validateDashboardNote, validateWodBlockSyntax };
555
+ //# sourceMappingURL=compiler-CAOKDLIY.mjs.map
556
+ //# sourceMappingURL=compiler-CAOKDLIY.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/seedbuild/effort-slugs.ts","../src/cli/seedbuild/filename.ts","../src/cli/seedbuild/git-version.ts","../src/cli/seedbuild/report.ts","../src/cli/seedbuild/compiler.ts"],"names":["join","execSync","section","writeFileSync"],"mappings":"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","file":"compiler-CAOKDLIY.mjs","sourcesContent":["/**\n * extractEffortSlugs — port of wod-wiki's\n * apps/playground/src/services/storage/StorageService.ts (the pure function\n * only). Derives canonical effort slugs referenced by script statements for\n * the block-efforts containment index.\n */\nimport type { ICodeStatement } from '@bitcobblers/wod-wiki-core';\n\nexport function extractEffortSlugs(statements: ICodeStatement[]): string[] {\n const slugs = new Set<string>();\n for (const statement of statements) {\n if (statement.exerciseId) {\n slugs.add(statement.exerciseId);\n } else {\n const effortMetric = statement.metrics?.getMetric?.('effort');\n const effort = effortMetric && typeof effortMetric.value === 'string' ? effortMetric.value : undefined;\n if (effort) {\n const slug = effort.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');\n if (slug) slugs.add(slug);\n }\n }\n }\n return Array.from(slugs);\n}\n","/**\n * fileToDisplayName — port of wod-wiki's\n * apps/playground/src/repositories/groupings.ts (display-name derivation\n * only). Block-index rows carry the same note titles the app renders, so\n * titles must derive identically on both sides.\n */\n\n/** Lowercased mid-title words — \"and\", \"of\", \"70.3\"-style fragments read wrong capped. */\nconst FILE_TITLE_STOP_WORDS: Record<string, true> = {\n and: true, or: true, of: true, for: true, to: true, in: true, on: true,\n at: true, by: true, with: true, the: true, a: true, an: true,\n};\n\n/**\n * Parse a filename into a display name.\n * \"simple-and-sinister.md\" -> \"Simple and Sinister\"\n */\nexport function fileToDisplayName(filename: string): string {\n const base = filename.replace(/\\.md$/, '');\n if (base.toUpperCase() === 'README') return 'Overview';\n // Strip leading \"day-01-\" prefixes if present, then humanise\n const cleaned = base.replace(/^day-\\d+-/, '');\n const tokens = cleaned.split(/[-_]/);\n const titled: string[] = [];\n for (let i = 0; i < tokens.length; i++) {\n const token = tokens[i]!;\n const next = tokens[i + 1];\n const after = tokens[i + 2];\n const prev = tokens[i - 1];\n // Race distances keep their dot: a lone 1-2-digit pair (\"70-3\" -> \"70.3\").\n // Longer numeric runs are dates (2009-12-01) or rep ladders (5-3-1) —\n // only an isolated pair between words is a distance.\n if (\n /^\\d{1,2}$/.test(token) && /^\\d{1,2}$/.test(next ?? '')\n && !/^\\d+$/.test(prev ?? '') && !/^\\d+$/.test(after ?? '')\n ) {\n titled.push(`${token}.${next}`);\n i++;\n continue;\n }\n if (titled.length > 0 && FILE_TITLE_STOP_WORDS[token.toLowerCase()]) {\n titled.push(token.toLowerCase());\n continue;\n }\n titled.push(token.charAt(0).toUpperCase() + token.slice(1));\n }\n return titled.join(' ');\n}\n","/**\n * Git-derived versioning — the seed artifact's version IS this repo's git\n * history. Every merged MR advances `version` (commit count), and\n * `versionLabel`/`commit` make an artifact traceable to its exact source\n * tree, so the app (and humans diffing manifests) can answer \"what changed\n * and what needs syncing\" from the manifest alone.\n */\nimport { execSync } from 'node:child_process';\n\nexport interface GitVersion {\n /** Monotonic commit count — the import-order version stamped on the manifest. */\n version: number;\n /** `git describe --tags --long --always --dirty`, e.g. `v1.2.0-4-gdeadbee`. */\n versionLabel: string;\n /** Full HEAD sha. */\n commit: string;\n /** True when built with uncommitted working-tree changes. */\n dirty: boolean;\n}\n\n/** Unversioned fallback for source trees without git history (e.g. exported tarballs). */\nexport const UNKNOWN_GIT_VERSION: GitVersion = {\n version: 0,\n versionLabel: 'unknown',\n commit: '',\n dirty: false,\n};\n\nexport function resolveGitVersion(repoRoot: string): GitVersion {\n try {\n const run = (args: string): string =>\n execSync(`git ${args}`, { cwd: repoRoot, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim();\n const count = Number(run('rev-list --count HEAD'));\n return {\n version: Number.isFinite(count) && count > 0 ? count : 0,\n versionLabel: run('describe --tags --long --always --dirty'),\n commit: run('rev-parse HEAD'),\n dirty: run('status --porcelain') !== '',\n };\n } catch {\n return UNKNOWN_GIT_VERSION;\n }\n}\n","/**\n * Build report — a static HTML benchmark page for each seed build\n * (`dist/report.html`). Per-chunk sizes, row counts, pass timings, and the\n * git version stamp, so CI and contributors see build health at a glance.\n * Deliberately dependency-free (one string template + a file write).\n */\nimport { writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport type { SeedManifest } from './contract';\n\nexport interface BuildReportData {\n manifest: SeedManifest;\n /** Wall-clock ms for the full build. */\n totalMs: number;\n /** Per-pass timings (ms): corpus scan, block-index derivation, effort-row derivation, chunk writes. */\n passMs: { scan: number; blockIndex: number; parseGate: number; blockEfforts: number; write: number };\n /** Number of corpus markdown files. */\n fileCount: number;\n outDir: string;\n}\n\nfunction escapeHtml(s: string): string {\n return s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\n}\n\nfunction rowHtml(c: SeedManifest['chunks'][number]): string {\n const kind = c.kind ?? 'notes';\n return `<tr><td>${escapeHtml(c.id)}</td><td>${kind}</td><td>${c.count}</td><td>${(c.bytes / 1024).toFixed(1)} KB</td><td><code>${c.sha256.slice(0, 8)}</code></td></tr>`;\n}\n\nexport function writeBuildReport(outDir: string, data: BuildReportData): string {\n const { manifest, totalMs, passMs, fileCount } = data;\n const totalBytes = manifest.chunks.reduce((s, c) => s + c.bytes, 0);\n const dirtyBadge = manifest.dirty\n ? ' <strong style=\"color:#b45309\">(dirty working tree — not reproducible from <code>' + escapeHtml(manifest.commit ?? '') + '</code>)</strong>'\n : '';\n const html = `<!doctype html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\" />\n<title>Seed build report</title>\n<style>\n body { font-family: system-ui, sans-serif; max-width: 960px; margin: 2rem auto; padding: 0 1rem; color: #1f2937; }\n h1 { font-size: 1.4rem; } h2 { font-size: 1.1rem; margin-top: 2rem; }\n table { border-collapse: collapse; width: 100%; font-size: 0.9rem; }\n th, td { text-align: left; padding: 0.3rem 0.6rem; border-bottom: 1px solid #e5e7eb; }\n code { font-size: 0.85em; }\n dl { display: grid; grid-template-columns: max-content 1fr; gap: 0.2rem 1rem; font-size: 0.95rem; }\n dt { font-weight: 600; }\n</style>\n</head>\n<body>\n<h1>Seed build report</h1>\n<h2>Version</h2>\n<dl>\n <dt>version</dt><dd>${manifest.version} <code>${escapeHtml(manifest.versionLabel ?? '')}</code>${dirtyBadge}</dd>\n <dt>commit</dt><dd><code>${escapeHtml(manifest.commit ?? 'unknown')}</code></dd>\n <dt>built at</dt><dd>${escapeHtml(manifest.builtAt)}</dd>\n <dt>schema</dt><dd>v${manifest.schema}</dd>\n</dl>\n<h2>Benchmarks</h2>\n<dl>\n <dt>total</dt><dd>${totalMs} ms</dd>\n <dt>corpus scan + partition</dt><dd>${passMs.scan} ms (${fileCount} files)</dd>\n <dt>block-index derivation</dt><dd>${passMs.blockIndex} ms</dd>\n <dt>block syntax gate</dt><dd>${passMs.parseGate} ms</dd>\n <dt>block-efforts derivation</dt><dd>${passMs.blockEfforts} ms</dd>\n <dt>chunk writes</dt><dd>${passMs.write} ms</dd>\n</dl>\n<h2>Chunks (${manifest.chunks.length}, ${(totalBytes / 1024).toFixed(0)} KB total)</h2>\n<table>\n<thead><tr><th>id</th><th>kind</th><th>rows</th><th>size</th><th>sha256</th></tr></thead>\n<tbody>\n${manifest.chunks.map(rowHtml).join('\\n')}\n</tbody>\n</table>\n</body>\n</html>\n`;\n const file = join(outDir, 'report.html');\n writeFileSync(file, html, 'utf8');\n return file;\n}\n","/** Compiles exact source, occurrence maps and sparse projections into atomic notes chunks. */\nimport { parseScript, whiteboardParser } from '@bitcobblers/wod-wiki-lang';\nimport { blockContentId, parseDocumentSections, reconcileNoteBlocks } from '@bitcobblers/wod-wiki-core';\nimport type { BlockEffort, BlockIndexRow } from '@bitcobblers/wod-wiki-storage';\nimport {\n buildDashboardDocument,\n isDashboardWidgetType,\n parseDashboardNote,\n parseQuery,\n resolveWidgetType,\n} from '@bitcobblers/wod-wiki-wql';\nimport {\n DASHBOARDS_CHUNK_ID,\n SEED_SCHEMA,\n SEED_REGISTERED_WIDGETS,\n TEMPLATE_CHUNK_ID,\n canvasRouteSlug,\n seedNoteSourceId,\n type ManifestChunk,\n type SeedManifest,\n type SeedRow,\n} from './contract';\nimport { extractEffortSlugs } from './effort-slugs';\nimport { fileToDisplayName } from './filename';\nimport { parseEffortFile } from './effort-file';\nimport { resolveGitVersion } from './git-version';\nimport { writeBuildReport } from './report';\nimport { parseFrontmatter } from './frontmatter';\nimport { createHash } from 'node:crypto';\nimport { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync } from 'node:fs';\nimport { execSync } from 'node:child_process';\nimport { join, relative } from 'node:path';\n\n// BlockEffort/BlockIndexRow row types come from @bitcobblers/wod-wiki-storage\n// (type-only import — chunk rows are plain JSON applied structurally by the\n// app, mirroring cli/seed.ts).\n\n/** Scan `dir` recursively for `*.md` files; returns POSIX paths relative to `dir`, sorted. */\nfunction listMarkdownFiles(dir: string, prefix = ''): string[] {\n if (!existsSync(dir)) return [];\n const out: string[] = [];\n for (const entry of readdirSync(dir, { withFileTypes: true })) {\n const rel = prefix ? `${prefix}/${entry.name}` : entry.name;\n if (entry.isDirectory()) out.push(...listMarkdownFiles(join(dir, entry.name), rel));\n else if (entry.isFile() && entry.name.endsWith('.md')) out.push(rel);\n }\n return out.sort();\n}\n\n// ── Types ─────────────────────────────────────────────────────────────────\n\nexport interface ChunkGroup {\n id: string;\n files: string[];\n}\n\nexport interface GenerateSeedOptions {\n /** The corpus directory (`markdown/`). Defaults to `<corpusRoot>/markdown`. */\n markdownDir?: string;\n /** Optional playground template file — the host passes its own; standalone builds omit the `template` chunk. */\n templateFile?: string;\n /**\n * Row path recorded for the template chunk (the runtime derives the\n * template note's id from it, so it must stay stable across builds). The\n * host pins its historical in-repo path; defaults to the path relative to\n * `corpusRoot`.\n */\n templateRowPath?: string;\n /** Output directory. Defaults to `<corpusRoot>/dist`. */\n outDir?: string;\n /**\n * Explicit manifest version (tests). Defaults to the corpus repo's git\n * commit count — see git-version.ts.\n */\n version?: number;\n /** Repo root the corpus paths resolve against and versioning derives from. Defaults to cwd. */\n corpusRoot?: string;\n /** Automatically scaffold markdown files for newly discovered efforts. */\n scaffoldDiscoveredEfforts?: boolean;\n}\n\nexport interface GenerateSeedResult {\n manifest: SeedManifest;\n outDir: string;\n totalFiles: number;\n reportPath: string;\n /** ms timings per pass (mirrored into report.html). */\n passMs: { scan: number; blockIndex: number; parseGate: number; blockEfforts: number; write: number };\n}\n\n// ── Corpus partition ──────────────────────────────────────────────────────\n\n/**\n * Map one `markdown/…` path to its chunk id. Returns null for files outside\n * the known corpora — callers MUST treat that as a parity failure so new\n * content directories cannot bypass the seed.\n */\nexport function chunkIdFor(relPath: string): string | null {\n const parts = relPath.split('/');\n if (parts[0] !== 'markdown' || parts.length < 2) return null;\n const corpus = parts[1];\n // markdown/<corpus>/<rest…>; a file directly under markdown/ has no corpus.\n if (parts.length === 2) return null;\n const slug = parts[2]?.replace(/\\.md$/, '');\n switch (corpus) {\n case 'canvas':\n case 'dashboards':\n case 'efforts':\n return corpus;\n case 'collections':\n return parts.length === 3 ? `collection._root` : `collection.${slug}`;\n case 'feeds':\n return parts.length === 3 ? `feed._root` : `feed.${slug}`;\n default:\n return null;\n }\n}\n\n/** Scan the markdown corpus; returns repo-relative POSIX paths of all .md files. */\nexport function collectCorpusMarkdown(markdownDir: string): string[] {\n return listMarkdownFiles(markdownDir).map((p) => `markdown/${p}`);\n}\n\n/** Group markdown corpus paths into chunk groups; throws on paths outside the known corpora. */\nexport function partitionIntoChunks(paths: string[]): ChunkGroup[] {\n const byId = new Map<string, string[]>();\n for (const p of paths) {\n const id = chunkIdFor(p);\n if (id === null) {\n throw new Error(\n `[generate-seed] Parity failure: '${p}' is not part of any known corpus ` +\n `(canvas | dashboards | efforts | collections | feeds). ` +\n `Extend chunkIdFor() if this is a new corpus.`,\n );\n }\n const list = byId.get(id);\n if (list) list.push(p);\n else byId.set(id, [p]);\n }\n return [...byId.entries()]\n .sort(([a], [b]) => (a < b ? -1 : 1))\n .map(([id, files]) => ({ id, files }));\n}\n\n// ── Dashboard gate (#906) ─────────────────────────────────────────────────\n\n/**\n * Validate one dashboard note in the locked #899 format: dashboard\n * frontmatter, a title, known widget types, WQL bodies that parse (and are\n * aggregate queries, not find queries), and every `$token` reference\n * declared. Returns the problems found (empty = valid).\n */\nexport function validateDashboardNote(raw: string): string[] {\n const problems: string[] = [];\n const { meta } = parseFrontmatter(raw);\n const { sections } = parseDashboardNote(raw);\n const doc = buildDashboardDocument(sections, meta);\n if (!doc.isDashboard) return ['file is not a dashboard note (missing dashboard frontmatter)'];\n if (doc.title === undefined) problems.push('dashboard has no title');\n if (doc.widgets.length < 3) problems.push(`dashboard has only ${doc.widgets.length} widgets (the locked format has ≥ 3)`);\n const declared = new Set(doc.tokens.map((t) => t.name));\n for (const widget of doc.widgets) {\n const where = `widget '${widget.title ?? widget.body.slice(0, 40)}'`;\n if (widget.title === undefined) problems.push(`${where}: missing title`);\n if (!isDashboardWidgetType(resolveWidgetType(widget.type))) {\n problems.push(`${where}: unknown widget type '${widget.type}'`);\n }\n const parsed = parseQuery(widget.body);\n if (parsed.error !== undefined) problems.push(`${where}: WQL parse error: ${parsed.error}`);\n for (const ref of [\n ...widget.body.matchAll(/\\$([A-Za-z][\\w-]*)/g),\n ...Object.values(widget.attributes).flatMap((v) => [...String(v).matchAll(/\\$([A-Za-z][\\w-]*)/g)]),\n ]) {\n if (!declared.has(ref[1])) problems.push(`${where}: undeclared token $${ref[1]}`);\n }\n }\n return problems;\n}\n\n/** Validate every dashboard file in a chunk group; throws on the first invalid file. */\nfunction gateDashboards(groups: ChunkGroup[], corpusRoot: string): void {\n for (const group of groups) {\n if (group.id !== DASHBOARDS_CHUNK_ID) continue;\n for (const p of group.files) {\n const problems = validateDashboardNote(readFileSync(join(corpusRoot, p), 'utf8'));\n if (problems.length > 0) {\n throw new Error(`[generate-seed] Dashboard validation failed for '${p}':\\n - ${problems.join('\\n - ')}`);\n }\n }\n }\n}\n\n// ── Block index derivation ────────────────────────────────────────────────\n\n\n/** Created-at from the corpus repo's git history (collections/guides), 0 fallback when unknown. */\nfunction getFileCreatedAt(file: string, repoRoot: string): number {\n try {\n const out = execSync(`git log -1 --format=%ct -- '${file}'`, { cwd: repoRoot, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim();\n const ts = Number(out) * 1000;\n return Number.isFinite(ts) && ts > 0 ? ts : 0;\n } catch {\n return 0;\n }\n}\n\n/** Created-at timestamp for a feed entry's date key (`yyyy-mm-dd` from the path). */\nexport function feedDateToCreatedAt(dateKey: string): number {\n if (!/^\\d{4}-\\d{2}-\\d{2}$/.test(dateKey)) return 0;\n const ts = Date.parse(`${dateKey}T00:00:00Z`);\n return Number.isNaN(ts) ? 0 : ts;\n}\n\n// canvasRouteSlug lives in contract.ts — one derivation shared by the\n// block plane (here) and the app's importer (note plane).\n\n\nexport function compileSeedRow(path: string, content: string, metadata: { routeId?: string; noteTitle?: string; sourceId?: string; createdAt?: number } = {}): SeedRow {\n let ordinal = 0;\n const blocks = reconcileNoteBlocks(content, { sourceRevision: 1, registeredWidgets: SEED_REGISTERED_WIDGETS, createId: () => `seed:${path}:${ordinal++}` });\n const noteId = metadata.routeId ?? path;\n const sections = parseDocumentSections(content);\n const blockIndex: BlockIndexRow[] = Object.values(blocks).map((block, position) => {\n const section = sections.find((section) => section.from === block.from && section.to === block.to);\n return { id: `${noteId}:${block.id}`, noteId, blockId: block.id, blockVersion: 1, sourceRevision: 1, from: block.from, to: block.to, position, dataType: block.kind, ...(block.kind === 'wod' ? { blockContentId: blockContentId(section?.scriptBlock?.content ?? content.slice(block.from, block.to)) } : {}), rawContent: content.slice(block.from, block.to), noteTitle: metadata.noteTitle ?? fileToDisplayName(path.split('/').at(-1) ?? path), createdAt: metadata.createdAt ?? 0, isStatic: true, sourceId: metadata.sourceId, sourcePath: path, routeId: metadata.routeId };\n });\n return { path, content, blocks, blockIndex, blockEfforts: [] };\n}\n\nexport function buildBlockIndexRows(markdownDir: string, corpusRoot: string): BlockIndexRow[] {\n const rows: BlockIndexRow[] = [];\n for (const path of collectCorpusMarkdown(markdownDir)) {\n const chunkId = chunkIdFor(path);\n if (!chunkId) continue;\n const content = readFileSync(join(corpusRoot, path), 'utf8');\n const parts = path.split('/');\n const stem = (parts.at(-1) ?? '').replace(/\\.md$/, '');\n const catalog = parts[2];\n const routeId = chunkId === 'canvas' ? canvasRouteSlug(content) ?? undefined : chunkId.startsWith('collection.') ? (stem.toLowerCase() === 'readme' ? catalog : `${catalog}/${stem}`) : chunkId.startsWith('feed.') ? parts.slice(1).join('/').replace(/\\.md$/, '') : undefined;\n const sourceId = seedNoteSourceId(path, content, chunkId);\n const createdAt = chunkId.startsWith('feed.') ? feedDateToCreatedAt(parts.at(-2) ?? '') : getFileCreatedAt(path, corpusRoot);\n rows.push(...compileSeedRow(path, content, { routeId, sourceId, createdAt }).blockIndex);\n }\n return rows;\n}\n\n/**\n * Syntax gate — every `wod` block's script must parse with zero Lezer error\n * nodes. `WhiteboardScript.errors` only carries mapper exceptions (the Lezer\n * parser recovers silently), so the tree is scanned for `isError` nodes\n * directly; a single bad block fails the build with `path:line` diagnostics.\n */\nexport function validateWodBlockSyntax(blocks: BlockIndexRow[]): string[] {\n const problems: string[] = [];\n for (const block of blocks) {\n if (block.dataType !== 'wod' || !block.rawContent?.trim()) continue;\n const section = parseDocumentSections(block.rawContent, { registeredWidgets: SEED_REGISTERED_WIDGETS })\n .find((s) => s.scriptBlock);\n const source = section?.scriptBlock?.content ?? block.rawContent;\n if (!source.trim()) continue;\n\n const doc = source.endsWith('\\n') ? source : source + '\\n';\n const tree = whiteboardParser.parse(doc);\n tree.iterate({\n enter: (node) => {\n if (!node.type.isError) return;\n const line = doc.slice(0, node.from).split('\\n').length;\n const snippet = source.slice(node.from, Math.min(node.to, node.from + 60)).split('\\n')[0];\n problems.push(`${block.sourcePath ?? block.noteId}:${line} \\`${snippet}\\``);\n },\n });\n }\n return problems;\n}\n\n/**\n * Uncataloged effort discovered during workout block analysis.\n */\nexport interface DiscoveredEffort {\n slug: string;\n label: string;\n rawLabels: string[];\n}\n\n/** Static lookup — syntax/structure keywords and known non-effort labels that must never scaffold. */\nconst NON_EFFORT_SLUGS: Record<string, true> = {\n 'amrap': true, 'emom': true, 'tabata': true, 'for-time': true, 'rounds': true, 'sets': true,\n 'repetitions': true, 'count-down': true, 'total-reps': true,\n 'warmup': true, 'cooldown': true, 'cool-down': true, 'transition': true, 'rest': true, 'break': true, 'work': true,\n 'i-suck-at-this': true, 'isuckatthis': true, 'movement': true, 'movement-you': true, 'selected-exercise': true,\n 'training-setup': true, 'grade-system': true, 'hangs': true, 'warm-light-wind': true, 'basin': true, 'rock-canyon': true,\n 'b-onsight': true, 'a-redpoint': true, 'd-redpoint': true, 'v0-flash': true, 'v2-flash': true, 'v7-redpoint': true,\n 'avgrpe-mean': true, 'calculate': true, 'totalload-sum': true, 'query': true, 'by': true,\n '': true, '-': true, '...': true, 'x': true, 's': true, 'bw': true,\n};\n\nfunction formatDiscoveredLabel(raw: string): string {\n const clean = raw.replace(/[-_]+/g, ' ').trim();\n return clean\n .split(/\\s+/)\n .map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase())\n .join(' ');\n}\n\nexport function loadExistingEffortMap(markdownDir: string): {\n slugs: Set<string>;\n aliasToCanonical: Map<string, string>;\n} {\n const slugs = new Set<string>();\n const aliasToCanonical = new Map<string, string>();\n const effortsDir = join(markdownDir, 'efforts');\n for (const file of listMarkdownFiles(effortsDir)) {\n const raw = readFileSync(join(effortsDir, file), 'utf8');\n const parsed = parseEffortFile(raw);\n if (!parsed) continue;\n\n slugs.add(parsed.slug);\n aliasToCanonical.set(parsed.slug, parsed.slug);\n aliasToCanonical.set(parsed.label.toLowerCase(), parsed.slug);\n aliasToCanonical.set(parsed.slug.replace(/-/g, ' '), parsed.slug);\n\n for (const alias of parsed.aliases) {\n aliasToCanonical.set(alias.toLowerCase(), parsed.slug);\n aliasToCanonical.set(alias.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/(^-|-$)/g, ''), parsed.slug);\n }\n }\n\n return { slugs, aliasToCanonical };\n}\n\nexport function discoverUncatalogedEfforts(markdownDir: string, corpusRoot: string): DiscoveredEffort[] {\n const { slugs, aliasToCanonical } = loadExistingEffortMap(markdownDir);\n const blocks = buildBlockIndexRows(markdownDir, corpusRoot);\n const discovered = new Map<string, { label: string; rawLabels: Set<string> }>();\n\n for (const block of blocks) {\n if (block.dataType !== 'wod' || !block.rawContent) continue;\n const section = parseDocumentSections(block.rawContent, { registeredWidgets: SEED_REGISTERED_WIDGETS }).find((section) => section.scriptBlock);\n const script = parseScript(section?.scriptBlock?.content ?? block.rawContent);\n for (const statement of script.statements) {\n const effortMetric = statement.metrics?.getMetric?.('effort');\n const effortLabel = (effortMetric && typeof effortMetric.value === 'string' ? effortMetric.value : undefined) ?? statement.exerciseId;\n if (!effortLabel) continue;\n\n const slugified = effortLabel.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/(^-|-$)/g, '');\n if (!slugified || NON_EFFORT_SLUGS[slugified]) continue;\n\n const canonical = aliasToCanonical.get(slugified) ?? aliasToCanonical.get(effortLabel.toLowerCase());\n if (canonical && slugs.has(canonical)) continue;\n\n const existing = discovered.get(slugified);\n if (existing) {\n existing.rawLabels.add(effortLabel);\n } else {\n discovered.set(slugified, {\n label: formatDiscoveredLabel(effortLabel),\n rawLabels: new Set([effortLabel]),\n });\n }\n }\n }\n\n return Array.from(discovered.entries()).map(([slug, data]) => ({\n slug,\n label: data.label,\n rawLabels: Array.from(data.rawLabels),\n }));\n}\n\nexport function scaffoldDiscoveredEfforts(\n discovered: DiscoveredEffort[],\n markdownDir: string,\n): string[] {\n const targetDir = join(markdownDir, 'efforts', 'discovered');\n if (!existsSync(targetDir)) {\n mkdirSync(targetDir, { recursive: true });\n }\n\n const createdPaths: string[] = [];\n for (const effort of discovered) {\n const filePath = join(targetDir, `${effort.slug}.md`);\n if (existsSync(filePath)) continue;\n\n const aliases = effort.rawLabels\n .filter((l) => l.toLowerCase() !== effort.label.toLowerCase())\n .map((l) => ` - \"${l.replace(/\"/g, '\\\\\"')}\"`);\n\n const content = [\n '---',\n `id: effort-bundled-${effort.slug}`,\n `slug: ${effort.slug}`,\n `label: \"${effort.label.replace(/\"/g, '\\\\\"')}\"`,\n aliases.length > 0 ? `aliases:\\n${aliases.join('\\n')}` : 'aliases: []',\n 'baseAttributes:',\n ' met: 5.0',\n ' discipline: general',\n ' intensityTier: moderate',\n 'registrySource: bundled',\n '---',\n '',\n ].join('\\n');\n\n writeFileSync(filePath, content, 'utf8');\n createdPaths.push(filePath);\n }\n\n return createdPaths;\n}\n\n/**\n * Derive BlockEffort[] containment rows from the block-index row set.\n * Requires a second pass over the source markdown to parse the script AST,\n * which is safe because the corpus is static and the pass is compile-time.\n */\nexport function buildBlockEffortRows(markdownDir: string, corpusRoot: string, blocks: BlockIndexRow[] = buildBlockIndexRows(markdownDir, corpusRoot)): BlockEffort[] {\n const efforts: BlockEffort[] = [];\n const { aliasToCanonical } = loadExistingEffortMap(markdownDir);\n for (const block of blocks) {\n if (block.dataType !== 'wod' || !block.rawContent) continue;\n const section = parseDocumentSections(block.rawContent, { registeredWidgets: SEED_REGISTERED_WIDGETS }).find((section) => section.scriptBlock);\n const script = parseScript(section?.scriptBlock?.content ?? block.rawContent);\n const effortSlugs = extractEffortSlugs(script.statements);\n const linked = new Set<string>();\n for (const rawSlug of effortSlugs) {\n const norm = rawSlug.toLowerCase().trim();\n const canonicalSlug =\n aliasToCanonical.get(norm) ??\n aliasToCanonical.get(norm.replace(/[^a-z0-9]+/g, '-').replace(/(^-|-$)/g, '')) ??\n norm.replace(/[^a-z0-9]+/g, '-').replace(/(^-|-$)/g, '');\n if (!canonicalSlug || NON_EFFORT_SLUGS[canonicalSlug] || NON_EFFORT_SLUGS[norm]) {\n continue;\n }\n if (linked.has(canonicalSlug)) continue;\n linked.add(canonicalSlug);\n efforts.push({\n id: `${block.id}:${canonicalSlug}`,\n noteId: block.noteId,\n blockId: block.blockId,\n blockContentId: block.blockContentId,\n effortSlug: canonicalSlug,\n isStatic: block.isStatic,\n sourcePath: block.sourcePath,\n createdAt: block.createdAt,\n });\n }\n }\n return efforts;\n}\n\n\n// ── Generation ────────────────────────────────────────────────────────────\n\nfunction sha256Hex(data: string | Buffer): string {\n return createHash('sha256').update(data).digest('hex');\n}\n\nexport function generateSeed(options: GenerateSeedOptions = {}): GenerateSeedResult {\n const t0 = Date.now();\n const passMs = { scan: 0, blockIndex: 0, parseGate: 0, blockEfforts: 0, write: 0 };\n const corpusRoot = options.corpusRoot ?? process.cwd();\n const markdownDir = options.markdownDir ?? join(corpusRoot, 'markdown');\n const outDir = options.outDir ?? join(corpusRoot, 'dist');\n const gitVersion = options.version !== undefined\n ? { ...resolveGitVersion(corpusRoot), version: options.version }\n : resolveGitVersion(corpusRoot);\n if (options.scaffoldDiscoveredEfforts) {\n const discovered = discoverUncatalogedEfforts(markdownDir, corpusRoot);\n if (discovered.length > 0) {\n scaffoldDiscoveredEfforts(discovered, markdownDir);\n }\n }\n\n const tScan = Date.now();\n const paths = collectCorpusMarkdown(markdownDir);\n const groups = partitionIntoChunks(paths);\n gateDashboards(groups, corpusRoot);\n if (options.templateFile && existsSync(options.templateFile)) {\n const templateRowPath = options.templateRowPath\n ?? relative(corpusRoot, options.templateFile).split('\\\\').join('/');\n groups.push({ id: TEMPLATE_CHUNK_ID, files: [templateRowPath] });\n }\n passMs.scan = Date.now() - tScan;\n\n const tBlockIndex = Date.now();\n const blockRows = buildBlockIndexRows(markdownDir, corpusRoot);\n passMs.blockIndex = Date.now() - tBlockIndex;\n const tParseGate = Date.now();\n const syntaxProblems = validateWodBlockSyntax(blockRows);\n passMs.parseGate = Date.now() - tParseGate;\n if (syntaxProblems.length > 0) {\n const shown = syntaxProblems.slice(0, 20).map((p) => ` ${p}`).join('\\n');\n const more = syntaxProblems.length > 20 ? `\\n … and ${syntaxProblems.length - 20} more` : '';\n throw new Error(`[wod build] block syntax gate failed — ${syntaxProblems.length} block(s) do not parse:\\n${shown}${more}`);\n }\n\n const chunksDir = join(outDir, 'chunks');\n rmSync(outDir, { recursive: true, force: true });\n mkdirSync(chunksDir, { recursive: true });\n\n const manifestChunks: ManifestChunk[] = [];\n const tBlockEfforts = Date.now();\n const effortRows = buildBlockEffortRows(markdownDir, corpusRoot, blockRows);\n passMs.blockEfforts = Date.now() - tBlockEfforts;\n const blocksByPath = new Map<string, BlockIndexRow[]>();\n const effortsByPath = new Map<string, BlockEffort[]>();\n for (const row of blockRows) {\n const rows = blocksByPath.get(row.sourcePath!) ?? [];\n rows.push(row); blocksByPath.set(row.sourcePath!, rows);\n }\n for (const row of effortRows) {\n const rows = effortsByPath.get(row.sourcePath!) ?? [];\n rows.push(row); effortsByPath.set(row.sourcePath!, rows);\n }\n let totalFiles = 0;\n\n const tWrite = Date.now();\n for (const { id, files } of groups) {\n // The template chunk's row path is the host-pinned historical identity,\n // not a path inside this repo — read it from the template file itself.\n const templateSource = id === TEMPLATE_CHUNK_ID ? options.templateFile : undefined;\n const rows: SeedRow[] = files.map((p) => {\n const content = readFileSync(templateSource ?? join(corpusRoot, p), 'utf8');\n const compiled = compileSeedRow(p, content);\n compiled.blockIndex = blocksByPath.get(p) ?? compiled.blockIndex;\n compiled.blockEfforts = effortsByPath.get(p) ?? [];\n return compiled;\n });\n const bytes = Buffer.from(JSON.stringify(rows), 'utf8');\n const sha = sha256Hex(bytes);\n const file = `chunks/${id}.${sha.slice(0, 8)}.json`;\n writeFileSync(join(outDir, file), bytes);\n manifestChunks.push({ id, path: file, sha256: sha, bytes: bytes.length, count: rows.length });\n totalFiles += rows.length;\n }\n\n passMs.write = Date.now() - tWrite;\n\n const manifest: SeedManifest = {\n schema: SEED_SCHEMA,\n version: gitVersion.version,\n versionLabel: gitVersion.versionLabel,\n commit: gitVersion.commit,\n dirty: gitVersion.dirty,\n builtAt: new Date().toISOString(),\n chunks: manifestChunks,\n };\n writeFileSync(join(outDir, 'manifest.json'), JSON.stringify(manifest, null, 2) + '\\n');\n\n const reportPath = writeBuildReport(outDir, {\n manifest,\n totalMs: Date.now() - t0,\n passMs,\n fileCount: paths.length,\n outDir,\n });\n\n return { manifest, outDir, totalFiles, reportPath, passMs };\n}\n"]}
package/dist/index.cjs CHANGED
@@ -343,6 +343,7 @@ function writeBuildReport(outDir, data) {
343
343
  <dt>total</dt><dd>${totalMs} ms</dd>
344
344
  <dt>corpus scan + partition</dt><dd>${passMs.scan} ms (${fileCount} files)</dd>
345
345
  <dt>block-index derivation</dt><dd>${passMs.blockIndex} ms</dd>
346
+ <dt>block syntax gate</dt><dd>${passMs.parseGate} ms</dd>
346
347
  <dt>block-efforts derivation</dt><dd>${passMs.blockEfforts} ms</dd>
347
348
  <dt>chunk writes</dt><dd>${passMs.write} ms</dd>
348
349
  </dl>
@@ -379,7 +380,8 @@ __export(compiler_exports, {
379
380
  loadExistingEffortMap: () => loadExistingEffortMap,
380
381
  partitionIntoChunks: () => partitionIntoChunks,
381
382
  scaffoldDiscoveredEfforts: () => scaffoldDiscoveredEfforts,
382
- validateDashboardNote: () => validateDashboardNote
383
+ validateDashboardNote: () => validateDashboardNote,
384
+ validateWodBlockSyntax: () => validateWodBlockSyntax
383
385
  });
384
386
  function listMarkdownFiles(dir, prefix = "") {
385
387
  if (!fs.existsSync(dir)) return [];
@@ -507,6 +509,26 @@ function buildBlockIndexRows(markdownDir, corpusRoot) {
507
509
  }
508
510
  return rows;
509
511
  }
512
+ function validateWodBlockSyntax(blocks) {
513
+ const problems = [];
514
+ for (const block of blocks) {
515
+ if (block.dataType !== "wod" || !block.rawContent?.trim()) continue;
516
+ const section = wodWikiCore.parseDocumentSections(block.rawContent, { registeredWidgets: exports.SEED_REGISTERED_WIDGETS }).find((s) => s.scriptBlock);
517
+ const source = section?.scriptBlock?.content ?? block.rawContent;
518
+ if (!source.trim()) continue;
519
+ const doc = source.endsWith("\n") ? source : source + "\n";
520
+ const tree = wodWikiLang.whiteboardParser.parse(doc);
521
+ tree.iterate({
522
+ enter: (node) => {
523
+ if (!node.type.isError) return;
524
+ const line = doc.slice(0, node.from).split("\n").length;
525
+ const snippet = source.slice(node.from, Math.min(node.to, node.from + 60)).split("\n")[0];
526
+ problems.push(`${block.sourcePath ?? block.noteId}:${line} \`${snippet}\``);
527
+ }
528
+ });
529
+ }
530
+ return problems;
531
+ }
510
532
  function formatDiscoveredLabel(raw) {
511
533
  const clean = raw.replace(/[-_]+/g, " ").trim();
512
534
  return clean.split(/\s+/).map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()).join(" ");
@@ -629,7 +651,7 @@ function sha256Hex(data) {
629
651
  }
630
652
  function generateSeed(options = {}) {
631
653
  const t0 = Date.now();
632
- const passMs = { scan: 0, blockIndex: 0, blockEfforts: 0, write: 0 };
654
+ const passMs = { scan: 0, blockIndex: 0, parseGate: 0, blockEfforts: 0, write: 0 };
633
655
  const corpusRoot = options.corpusRoot ?? process.cwd();
634
656
  const markdownDir = options.markdownDir ?? path.join(corpusRoot, "markdown");
635
657
  const outDir = options.outDir ?? path.join(corpusRoot, "dist");
@@ -649,13 +671,23 @@ function generateSeed(options = {}) {
649
671
  groups.push({ id: exports.TEMPLATE_CHUNK_ID, files: [templateRowPath] });
650
672
  }
651
673
  passMs.scan = Date.now() - tScan;
674
+ const tBlockIndex = Date.now();
675
+ const blockRows = buildBlockIndexRows(markdownDir, corpusRoot);
676
+ passMs.blockIndex = Date.now() - tBlockIndex;
677
+ const tParseGate = Date.now();
678
+ const syntaxProblems = validateWodBlockSyntax(blockRows);
679
+ passMs.parseGate = Date.now() - tParseGate;
680
+ if (syntaxProblems.length > 0) {
681
+ const shown = syntaxProblems.slice(0, 20).map((p) => ` ${p}`).join("\n");
682
+ const more = syntaxProblems.length > 20 ? `
683
+ \u2026 and ${syntaxProblems.length - 20} more` : "";
684
+ throw new Error(`[wod build] block syntax gate failed \u2014 ${syntaxProblems.length} block(s) do not parse:
685
+ ${shown}${more}`);
686
+ }
652
687
  const chunksDir = path.join(outDir, "chunks");
653
688
  fs.rmSync(outDir, { recursive: true, force: true });
654
689
  fs.mkdirSync(chunksDir, { recursive: true });
655
690
  const manifestChunks = [];
656
- const tBlockIndex = Date.now();
657
- const blockRows = buildBlockIndexRows(markdownDir, corpusRoot);
658
- passMs.blockIndex = Date.now() - tBlockIndex;
659
691
  const tBlockEfforts = Date.now();
660
692
  const effortRows = buildBlockEffortRows(markdownDir, corpusRoot, blockRows);
661
693
  passMs.blockEfforts = Date.now() - tBlockEfforts;
@@ -2350,7 +2382,7 @@ async function putChanged(store, values, keys) {
2350
2382
  async function applySeedChunk(storage, write) {
2351
2383
  assertSeedManifest(write.manifest);
2352
2384
  assertSeedRows(write.rows);
2353
- const result = { totalNotes: 0, totalEfforts: 0, totalBlocks: 0, skippedUserOwned: 0, deleted: 0 };
2385
+ const result = { totalNotes: 0, totalEfforts: 0, totalBlocks: 0, skippedUserOwned: 0, deleted: 0, addedNotes: 0, updatedNotes: 0 };
2354
2386
  const ids = await Promise.all(write.rows.map((row) => seedNoteId(row.path)));
2355
2387
  const pagesBySlug = /* @__PURE__ */ new Map();
2356
2388
  const linkIds = /* @__PURE__ */ new Map();
@@ -2405,6 +2437,8 @@ async function applySeedChunk(storage, write) {
2405
2437
  result.skippedUserOwned++;
2406
2438
  continue;
2407
2439
  }
2440
+ if (!existing) result.addedNotes++;
2441
+ else if (existing.rawContent !== row.content) result.updatedNotes++;
2408
2442
  wodWikiCore.validateNoteBlocks(row.content, row.blocks, 1, { registeredWidgets: exports.SEED_REGISTERED_WIDGETS });
2409
2443
  const sourceRevision = existing ? existing.sourceRevision + (existing.rawContent === row.content ? 0 : 1) : 1;
2410
2444
  let ordinal = 0;
@@ -3541,13 +3575,13 @@ async function cliMain(argv = process.argv.slice(2), io = {}) {
3541
3575
  }
3542
3576
 
3543
3577
  // src/version.ts
3544
- var VERSION2 = "0.35.2117";
3545
- var GIT_SHA = "03d1dfc";
3546
- var BUILD_TIME = "2026-10-12T20:08:12.374Z";
3578
+ var VERSION2 = "0.35.2120";
3579
+ var GIT_SHA = "62def0d";
3580
+ var BUILD_TIME = "2026-10-13T06:46:25.596Z";
3547
3581
  var SEMVER = {
3548
- version: "0.35.2117",
3549
- gitSha: "03d1dfc",
3550
- buildTime: "2026-10-12T20:08:12.374Z"
3582
+ version: "0.35.2120",
3583
+ gitSha: "62def0d",
3584
+ buildTime: "2026-10-13T06:46:25.596Z"
3551
3585
  };
3552
3586
 
3553
3587
  // src/index.ts