@bitcobblers/wod-wiki-tools 0.36.2133 → 0.36.2135

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/dist/bin/wod.cjs +274 -58
  2. package/dist/bin/wod.cjs.map +1 -1
  3. package/dist/bin/wod.mjs +3 -2
  4. package/dist/bin/wod.mjs.map +1 -1
  5. package/dist/{chunk-EUQWEX6Z.mjs → chunk-CYKNTEGY.mjs} +3 -3
  6. package/dist/chunk-CYKNTEGY.mjs.map +1 -0
  7. package/dist/{compiler-KT6P3AC6.mjs → chunk-FZKXOYMJ.mjs} +4 -5
  8. package/dist/chunk-FZKXOYMJ.mjs.map +1 -0
  9. package/dist/{chunk-DZESCGCM.mjs → chunk-MA6BBTWC.mjs} +54 -12
  10. package/dist/chunk-MA6BBTWC.mjs.map +1 -0
  11. package/dist/{chunk-OZI2FYPF.mjs → chunk-OTJAPVT2.mjs} +217 -52
  12. package/dist/chunk-OTJAPVT2.mjs.map +1 -0
  13. package/dist/{chunk-K3GZZFAC.mjs → chunk-V23POHG2.mjs} +10 -9
  14. package/dist/chunk-V23POHG2.mjs.map +1 -0
  15. package/dist/compiler-CTPLIZJK.mjs +9 -0
  16. package/dist/compiler-CTPLIZJK.mjs.map +1 -0
  17. package/dist/index.cjs +274 -58
  18. package/dist/index.cjs.map +1 -1
  19. package/dist/index.d.cts +3 -1
  20. package/dist/index.d.ts +3 -1
  21. package/dist/index.mjs +5 -4
  22. package/dist/server/app.cjs +7 -6
  23. package/dist/server/app.cjs.map +1 -1
  24. package/dist/server/app.mjs +1 -1
  25. package/dist/server-JXECWS4A.mjs +11 -0
  26. package/dist/{server-Z2T7IGZX.mjs.map → server-JXECWS4A.mjs.map} +1 -1
  27. package/dist/web/playground/assets/main-Bod1taHc.js +188 -0
  28. package/dist/web/playground/assets/{statementBuilderFlow-gvzmxfiE.js → statementBuilderFlow-CCaLco1R.js} +2 -2
  29. package/dist/web/playground/assets/{vendor-codemirror-BtJgoBjf.js → vendor-codemirror-C41SFn-r.js} +2 -2
  30. package/dist/web/playground/assets/vendor-g-NJOjvC.js +198 -0
  31. package/dist/web/playground/index.html +3 -3
  32. package/dist/web/playground/seed/core/chunks/canvas.2d3e2e0e.json +1 -0
  33. package/dist/web/playground/seed/core/manifest.json +7 -7
  34. package/dist/web/playground/seed/core/report.html +11 -11
  35. package/dist/web/server/assets/main-DtiBHHer.js +187 -0
  36. package/dist/web/server/assets/{statementBuilderFlow-DT1tyXgc.js → statementBuilderFlow-MCfILbWE.js} +2 -2
  37. package/dist/web/server/assets/{vendor-codemirror-BtJgoBjf.js → vendor-codemirror-C41SFn-r.js} +2 -2
  38. package/dist/web/server/assets/vendor-g-NJOjvC.js +198 -0
  39. package/dist/web/server/index.html +3 -3
  40. package/dist/web/server/seed/core/chunks/canvas.2d3e2e0e.json +1 -0
  41. package/dist/web/server/seed/core/manifest.json +7 -7
  42. package/dist/web/server/seed/core/report.html +10 -10
  43. package/package.json +6 -8
  44. package/dist/chunk-DZESCGCM.mjs.map +0 -1
  45. package/dist/chunk-EUQWEX6Z.mjs.map +0 -1
  46. package/dist/chunk-K3GZZFAC.mjs.map +0 -1
  47. package/dist/chunk-OZI2FYPF.mjs.map +0 -1
  48. package/dist/compiler-KT6P3AC6.mjs.map +0 -1
  49. package/dist/server-Z2T7IGZX.mjs +0 -11
  50. package/dist/web/playground/assets/main-Bh_mthqc.js +0 -212
  51. package/dist/web/playground/assets/vendor-BlJqgFME.js +0 -201
  52. package/dist/web/playground/seed/core/chunks/canvas.d2a5e6e7.json +0 -1
  53. package/dist/web/server/assets/main-CPRFCrjV.js +0 -211
  54. package/dist/web/server/assets/vendor-BlJqgFME.js +0 -201
  55. package/dist/web/server/seed/core/chunks/canvas.d2a5e6e7.json +0 -1
@@ -1,9 +1,10 @@
1
1
  import { createRequire } from 'module';
2
- import { openCliDb, nodeFs, sqliteIndexStore, sqliteEffortStore, sqliteBlockStore, sqliteNoteStore, sqliteEventStore, getNoteBundle, getSessionBundle, getEffortBySlug, listTagsByType, nodePath, loadSql, uuid5, CLI_UUID_NAMESPACE, stableNoteId, randomId } from './chunk-EUQWEX6Z.mjs';
2
+ import { compileSeedRow, SEED_SCHEMA, writeBuildReport } from './chunk-FZKXOYMJ.mjs';
3
+ import { openCliDb, nodeFs, sqliteIndexStore, sqliteEffortStore, sqliteBlockStore, sqliteNoteStore, sqliteEventStore, getNoteBundle, getSessionBundle, getEffortBySlug, listTagsByType, nodePath, loadSql, uuid5, CLI_UUID_NAMESPACE, nodeCrypto, stableNoteId, randomId } from './chunk-CYKNTEGY.mjs';
3
4
  import { __require } from './chunk-6L5ZDZJ7.mjs';
4
5
  import { createParser, frozenNow, createCompiler, createMockClock, RuntimeStack, EventBus, ScriptRuntime, createAnalyticsEngineForBlock, StartSessionAction, NextEvent, toStoredOutputStatement, bundledEfforts, parseScript } from '@bitcobblers/wod-wiki-lang';
5
- import { buildStatementTree, createIRFile, registerLanguagePack, extractNoteIdentity, extractTypedTags, composeNoteMarkdown, sessionToCsv, effortToMarkdown, equipmentToCsv, markdownToEffort, csvToEquipmentLabels, csvToSessionRows, isIRFile, inMemoryEventStore, assertSeedManifest, seedMetaKey, SEED_SCHEMA, emptySeedMeta, CANVAS_CHUNK_ID, assertSeedRows, seedNoteId, applySeedChunk } from '@bitcobblers/wod-wiki-engine';
6
- import { toEventRows, toSummaryEventRows, parseQuery, QueryService, isPipelineQuery, isFindQuery } from '@bitcobblers/wod-wiki-wql';
6
+ import { buildStatementTree, createIRFile, registerLanguagePack, extractNoteIdentity, extractTypedTags, composeNoteMarkdown, sessionToCsv, effortToMarkdown, equipmentToCsv, markdownToEffort, csvToEquipmentLabels, csvToSessionRows, isIRFile, inMemoryEventStore, readFrontmatterMeta, assertSeedManifest, seedMetaKey, SEED_SCHEMA as SEED_SCHEMA$1, emptySeedMeta, CANVAS_CHUNK_ID, assertSeedRows, seedNoteId, applySeedChunk } from '@bitcobblers/wod-wiki-engine';
7
+ import { toEventRows, toSummaryEventRows, parseQuery, QueryService, isPipelineQuery, isFindQuery, catalogOfItem } from '@bitcobblers/wod-wiki-wql';
7
8
  import { validateNoteBlocks, parseDocumentSections, blockContentId, reconcileNoteBlocks, KNOWN_SECTION_WIDGETS } from '@bitcobblers/wod-wiki-core';
8
9
  import { rowsEqual } from '@bitcobblers/wod-wiki-storage';
9
10
 
@@ -955,18 +956,24 @@ async function saveSource(db, options, startTime) {
955
956
  throw new Error(`Stale Note source revision for ${noteId}; expected ${expected}`);
956
957
  }
957
958
  let sourceRevision = existing?.rawContent === options.source ? existing.sourceRevision : (existing?.sourceRevision ?? 0) + 1;
959
+ const history = await tx.readonly("block_revisions").getAllFromIndex("by-note", noteId);
960
+ const latest = /* @__PURE__ */ new Map();
961
+ for (const row of history) {
962
+ if ((latest.get(row.blockId)?.version ?? 0) < row.version) latest.set(row.blockId, row);
963
+ }
964
+ let ordinal = 0;
958
965
  const blocks = reconcileNoteBlocks(options.source, {
959
966
  sourceRevision,
960
967
  blocks: options.blocks ?? (existing?.rawContent === options.source ? existing.blocks : void 0),
961
968
  registeredWidgets,
962
- createId: randomId
969
+ createId: () => {
970
+ while (true) {
971
+ const candidate = uuid5(CLI_UUID_NAMESPACE, `${noteId}:block:${ordinal++}`);
972
+ if (!latest.has(candidate)) return candidate;
973
+ }
974
+ }
963
975
  });
964
976
  validateNoteBlocks(options.source, blocks, sourceRevision, { registeredWidgets });
965
- const history = await tx.readonly("block_revisions").getAllFromIndex("by-note", noteId);
966
- const latest = /* @__PURE__ */ new Map();
967
- for (const row of history) {
968
- if ((latest.get(row.blockId)?.version ?? 0) < row.version) latest.set(row.blockId, row);
969
- }
970
977
  const revisions = [];
971
978
  for (const block of Object.values(blocks)) {
972
979
  const saved = latest.get(block.id);
@@ -988,6 +995,8 @@ async function saveSource(db, options, startTime) {
988
995
  sourceRevision++;
989
996
  for (const block of Object.values(blocks)) block.sourceRevision = sourceRevision;
990
997
  }
998
+ const fm = readFrontmatterMeta(options.source);
999
+ const catalog = typeof fm["catalog"] === "string" && fm["catalog"].trim() || typeof fm["collection"] === "string" && fm["collection"].trim() || existing?.catalog;
991
1000
  const note = {
992
1001
  ...existing,
993
1002
  id: noteId,
@@ -997,7 +1006,8 @@ async function saveSource(db, options, startTime) {
997
1006
  createdAt: existing?.createdAt ?? now,
998
1007
  rawContent: options.source,
999
1008
  sourceRevision,
1000
- blocks
1009
+ blocks,
1010
+ ...catalog ? { catalog } : {}
1001
1011
  };
1002
1012
  const priorRows = await tx.readonly("block_index").getAllFromIndex("by-note", noteId);
1003
1013
  const priorEfforts = await tx.readonly("block_efforts").getAllFromIndex("by-note", noteId);
@@ -1025,6 +1035,7 @@ async function saveSource(db, options, startTime) {
1025
1035
  noteTitle: note.title,
1026
1036
  createdAt: prior?.createdAt ?? now,
1027
1037
  blockContentId: fingerprint,
1038
+ ...catalog ? { catalog } : {},
1028
1039
  ...note.sourceId ? { sourceId: note.sourceId } : {}
1029
1040
  });
1030
1041
  if (block.kind === "wod") {
@@ -1196,7 +1207,7 @@ async function importSeedDir(db, dir, sourceId) {
1196
1207
  const manifest = assertSeedManifest(JSON.parse(fs.readFileSync(path.resolve(dir, "manifest.json"), "utf-8")));
1197
1208
  const key = seedMetaKey(source);
1198
1209
  let expected = await db.storage.readonly("meta").get(key);
1199
- if (expected && expected.schema !== SEED_SCHEMA) throw new Error("Incompatible seed checkpoint; recreate storage");
1210
+ if (expected && expected.schema !== SEED_SCHEMA$1) throw new Error("Incompatible seed checkpoint; recreate storage");
1200
1211
  let meta = expected ?? emptySeedMeta(key);
1201
1212
  if (manifest.version < meta.seedVersion) throw new Error("Seed manifest is older than stored checkpoint");
1202
1213
  const summary = { sourceId: source, version: manifest.version, notes: 0, efforts: 0, blocks: 0, blockEfforts: 0, deleted: 0, skippedUserOwned: 0 };
@@ -1229,7 +1240,7 @@ async function importSeedDir(db, dir, sourceId) {
1229
1240
  expected = next;
1230
1241
  }
1231
1242
  if (plan.length || metadataChanged || Object.keys(meta.chunks).length !== Object.keys(expected?.chunks ?? {}).length) {
1232
- const next = { ...meta, schema: SEED_SCHEMA, seedVersion: manifest.version, builtAt: manifest.builtAt, importedAt: Date.now() };
1243
+ const next = { ...meta, schema: SEED_SCHEMA$1, seedVersion: manifest.version, builtAt: manifest.builtAt, importedAt: Date.now() };
1233
1244
  await applySeedChunk(db.storage, { chunkId: null, rows: [], manifest, expectedMeta: expected, meta: next });
1234
1245
  }
1235
1246
  return summary;
@@ -1238,9 +1249,135 @@ function defaultSeedDirs(cwd) {
1238
1249
  const base = nodePath().resolve(cwd, "apps", "playground", "public", "seed");
1239
1250
  return ["core", "community"].map((id) => nodePath().resolve(base, id)).filter((dir) => nodeFs().existsSync(nodePath().resolve(dir, "manifest.json")));
1240
1251
  }
1252
+ async function publishCatalogSeed(db, options) {
1253
+ const t0 = Date.now();
1254
+ const fs = nodeFs();
1255
+ const path = nodePath();
1256
+ const allNotes = await db.storage.readonly("notes").getAll();
1257
+ if (allNotes.length === 0) {
1258
+ throw new Error(`[wod publish] No notes found in database ${db.path}`);
1259
+ }
1260
+ const notesByCatalog = /* @__PURE__ */ new Map();
1261
+ for (const note of allNotes) {
1262
+ const fm = readFrontmatterMeta(note.rawContent);
1263
+ const cat = typeof fm["catalog"] === "string" && fm["catalog"].trim() || typeof fm["collection"] === "string" && fm["collection"].trim() || note.catalog || catalogOfItem(note);
1264
+ const key = cat || "__uncataloged__";
1265
+ const list = notesByCatalog.get(key) ?? [];
1266
+ list.push(note);
1267
+ notesByCatalog.set(key, list);
1268
+ }
1269
+ const targetCatalogs = /* @__PURE__ */ new Map();
1270
+ if (options.catalog) {
1271
+ const matching = notesByCatalog.get(options.catalog);
1272
+ if (matching) {
1273
+ targetCatalogs.set(options.catalog, matching);
1274
+ } else if (notesByCatalog.has("__uncataloged__") && notesByCatalog.size === 1) {
1275
+ targetCatalogs.set(options.catalog, notesByCatalog.get("__uncataloged__"));
1276
+ } else {
1277
+ const available = [...notesByCatalog.keys()].filter((k) => k !== "__uncataloged__");
1278
+ throw new Error(
1279
+ `[wod publish] Catalog "${options.catalog}" not found in ${db.path}.` + (available.length > 0 ? ` Available catalogs: ${available.join(", ")}` : "")
1280
+ );
1281
+ }
1282
+ } else {
1283
+ for (const [cat, notes] of notesByCatalog.entries()) {
1284
+ targetCatalogs.set(cat === "__uncataloged__" ? "default" : cat, notes);
1285
+ }
1286
+ }
1287
+ const allIndexRows = await db.storage.readonly("block_index").getAll();
1288
+ const allEffortRows = await db.storage.readonly("block_efforts").getAll();
1289
+ const indexByNote = /* @__PURE__ */ new Map();
1290
+ for (const row of allIndexRows) {
1291
+ const list = indexByNote.get(row.noteId) ?? [];
1292
+ list.push(row);
1293
+ indexByNote.set(row.noteId, list);
1294
+ }
1295
+ const effortsByNote = /* @__PURE__ */ new Map();
1296
+ for (const row of allEffortRows) {
1297
+ const list = effortsByNote.get(row.noteId) ?? [];
1298
+ list.push(row);
1299
+ effortsByNote.set(row.noteId, list);
1300
+ }
1301
+ const outDir = path.resolve(process.cwd(), options.outDir);
1302
+ const chunksDir = path.join(outDir, "chunks");
1303
+ fs.mkdirSync(chunksDir, { recursive: true });
1304
+ const manifestChunks = [];
1305
+ let totalNotes = 0;
1306
+ const publishedCatalogs = [];
1307
+ for (const [catalogName, notes] of targetCatalogs.entries()) {
1308
+ publishedCatalogs.push(catalogName);
1309
+ const chunkRows = [];
1310
+ for (const note of notes) {
1311
+ const stem = (note.title || note.id).toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "") || "note";
1312
+ const notePath = note.sourcePath ?? `markdown/collections/${catalogName}/${stem}.md`;
1313
+ const noteBlockIndex = indexByNote.get(note.id) ?? [];
1314
+ const noteBlockEfforts = effortsByNote.get(note.id) ?? [];
1315
+ const blockIndex = noteBlockIndex.length > 0 ? noteBlockIndex.map((b) => ({
1316
+ ...b,
1317
+ isStatic: true,
1318
+ sourceId: b.sourceId ?? `collection:${catalogName}/${stem}`,
1319
+ sourcePath: b.sourcePath ?? notePath,
1320
+ catalog: catalogName
1321
+ })) : compileSeedRow(notePath, note.rawContent, {
1322
+ routeId: `${catalogName}/${stem}`,
1323
+ sourceId: `collection:${catalogName}/${stem}`,
1324
+ createdAt: note.createdAt,
1325
+ noteTitle: note.title
1326
+ }).blockIndex;
1327
+ const blockEfforts = noteBlockEfforts.map((e) => ({
1328
+ ...e,
1329
+ isStatic: true,
1330
+ sourcePath: e.sourcePath ?? notePath
1331
+ }));
1332
+ chunkRows.push({
1333
+ path: notePath,
1334
+ content: note.rawContent,
1335
+ blocks: note.blocks,
1336
+ blockIndex,
1337
+ blockEfforts
1338
+ });
1339
+ }
1340
+ const chunkId = `collection.${catalogName}`;
1341
+ const json = JSON.stringify(chunkRows);
1342
+ const digest = nodeCrypto().createHash("sha256").update(json).digest();
1343
+ const sha = [...digest].map((b) => b.toString(16).padStart(2, "0")).join("");
1344
+ const chunkFile = `chunks/${chunkId}.${sha.slice(0, 8)}.json`;
1345
+ fs.writeFileSync(path.join(outDir, chunkFile), json, "utf-8");
1346
+ manifestChunks.push({
1347
+ id: chunkId,
1348
+ path: chunkFile,
1349
+ sha256: sha,
1350
+ bytes: Buffer.byteLength(json, "utf8"),
1351
+ count: chunkRows.length,
1352
+ kind: "notes"
1353
+ });
1354
+ totalNotes += chunkRows.length;
1355
+ }
1356
+ const manifest = {
1357
+ schema: SEED_SCHEMA,
1358
+ version: options.version ?? 1,
1359
+ versionLabel: `v${options.version ?? 1}`,
1360
+ builtAt: (/* @__PURE__ */ new Date()).toISOString(),
1361
+ chunks: manifestChunks
1362
+ };
1363
+ fs.writeFileSync(path.join(outDir, "manifest.json"), JSON.stringify(manifest, null, 2) + "\n", "utf-8");
1364
+ writeBuildReport(outDir, {
1365
+ manifest,
1366
+ totalMs: Date.now() - t0,
1367
+ passMs: { scan: 0, blockIndex: 0, parseGate: 0, blockEfforts: 0, write: Date.now() - t0 },
1368
+ fileCount: totalNotes,
1369
+ outDir
1370
+ });
1371
+ return {
1372
+ manifest,
1373
+ outDir,
1374
+ catalogs: publishedCatalogs,
1375
+ totalNotes
1376
+ };
1377
+ }
1241
1378
 
1242
1379
  // src/version.ts
1243
- var VERSION = "0.36.2133";
1380
+ var VERSION = "0.36.2135";
1244
1381
 
1245
1382
  // src/cli/runner.ts
1246
1383
  function getFs2() {
@@ -1385,9 +1522,16 @@ function parseCliArgs(args) {
1385
1522
  i++;
1386
1523
  } else if (arg.startsWith("--dir=")) {
1387
1524
  result.dir = arg.slice("--dir=".length);
1525
+ } else if (arg === "--seed-version") {
1526
+ const v = Number(args[++i]);
1527
+ if (Number.isSafeInteger(v) && v >= 0) result.seedVersion = v;
1528
+ i++;
1529
+ } else if (arg.startsWith("--seed-version=")) {
1530
+ const v = Number(arg.slice("--seed-version=".length));
1531
+ if (Number.isSafeInteger(v) && v >= 0) result.seedVersion = v;
1388
1532
  i++;
1389
1533
  } else if (!result.subcommand) {
1390
- if (arg === "parse" || arg === "run" || arg === "query" || arg === "download" || arg === "import" || arg === "build" || arg === "seed" || arg === "reset" || arg === "init" || arg === "server" || arg === "serve" || arg === "playground" || arg === "help" || arg === "version") {
1534
+ if (arg === "parse" || arg === "run" || arg === "query" || arg === "download" || arg === "export" || arg === "import" || arg === "publish" || arg === "build" || arg === "seed" || arg === "reset" || arg === "init" || arg === "server" || arg === "serve" || arg === "playground" || arg === "help" || arg === "version") {
1391
1535
  result.subcommand = arg;
1392
1536
  } else {
1393
1537
  result.target = arg;
@@ -1448,8 +1592,10 @@ USAGE:
1448
1592
  wod parse <file | -> [options]
1449
1593
  wod run <file | -> [options]
1450
1594
  wod query '<wql-string>' [options]
1451
- wod download <note|session|effort|equipment> <id> [options]
1452
- wod import <note|session|effort|equipment> <file> [options]
1595
+ wod export <note|playground|session|effort|equipment> <id> [options]
1596
+ wod download <note|playground|session|effort|equipment> <id> [options]
1597
+ wod import <note|playground|session|effort|equipment> <file> [options]
1598
+ wod publish [catalog] [options]
1453
1599
  wod build [dir] [options]
1454
1600
  wod seed [options]
1455
1601
  wod reset [options]
@@ -1469,23 +1615,23 @@ SUBCOMMANDS:
1469
1615
  execution-log IR
1470
1616
  query '<wql>' Evaluate WQL against the database (or an explicit
1471
1617
  dataset) and emit QueryResult / RowsQueryResult IR
1472
- download note <id> Export the note's authored markdown to <id>.md.
1618
+ export note <id> Export the note's authored markdown to <id>.md.
1619
+ (alias: export playground, download note).
1473
1620
  The frontmatter identity stamp allows re-import.
1474
1621
  Frontmatter tags (general + typed equipment/quality/\u2026)
1475
1622
  are materialized into the tag graph on parse/import.
1476
- download session <id>
1623
+ export session <id>
1477
1624
  Write the session's generated metrics (event + summary
1478
1625
  rows, one CSV row per metric) to <id>.csv. Import back
1479
- with 'wod import session'.
1480
- download effort <slug>
1626
+ with 'wod import session'. (alias: download session).
1627
+ export effort <slug>
1481
1628
  Write the effort registry row as corpus-style markdown
1482
- (frontmatter id/slug/label/aliases/baseAttributes + body)
1483
1629
  to <slug>.md. Import back with 'wod import effort'.
1484
- download equipment
1485
- Write every equipment tag as a one-column CSV to
1630
+ export equipment Write every equipment tag as a one-column CSV to
1486
1631
  equipment.csv. Import back with 'wod import equipment'.
1487
- import note <file> Re-import exported note markdown (same as parse with
1488
- the stamped identity).
1632
+ download \u2026 Accepted alias for 'wod export'.
1633
+ import note <file> Re-import exported note markdown (alias: import playground;
1634
+ same as parse with the stamped identity).
1489
1635
  import session <file>
1490
1636
  Import a session + its events from a downloaded CSV.
1491
1637
  The exact saved block revision must already exist.
@@ -1493,6 +1639,10 @@ SUBCOMMANDS:
1493
1639
  Upsert an effort registry row from corpus-style markdown.
1494
1640
  import equipment <file>
1495
1641
  Create/upgrade equipment tags (CSV, one label per row).
1642
+ publish [catalog] Publish catalog notes from the database into a configured
1643
+ seed directory (manifest.json, chunks/, report.html)
1644
+ compatible with 'wod seed --source <dir>'. Override target
1645
+ with -o/--out/--output (defaults to ./dist/seed/<catalog>).
1496
1646
  build [dir] Compile a seed corpus: validate every .md under
1497
1647
  <dir>/markdown (parity gate + dashboard format gate) and
1498
1648
  write manifest.json + content-hashed chunks/ +
@@ -1541,9 +1691,9 @@ DATABASE (init / parse / run / query / download / import / seed / reset / server
1541
1691
  an error. Without it: ./.env when present, otherwise
1542
1692
  the workspace-root .env. Credentials are never logged.
1543
1693
 
1544
- HOSTING (for 'server' / 'playground'; requires the Bun runtime):
1694
+ HOSTING (for 'server' / 'playground'; supported on Node and Bun):
1545
1695
  server HTTP API over the configured database plus the frontend
1546
- packaged inside the engine module. Requires an
1696
+ packaged inside the tools module. Requires an
1547
1697
  initialized database. ('serve' is an accepted alias.)
1548
1698
  playground Serves the playground app backed by browser IndexedDB;
1549
1699
  the configured database is not touched.
@@ -1803,21 +1953,22 @@ async function cliMain(argv = process.argv.slice(2), io = {}) {
1803
1953
  writeDebugFile("query", ir);
1804
1954
  return 0;
1805
1955
  }
1806
- if (parsed.subcommand === "download") {
1956
+ if (parsed.subcommand === "download" || parsed.subcommand === "export") {
1957
+ const verb = parsed.subcommand;
1807
1958
  const kind = parsed.target;
1808
1959
  const id = parsed.target2;
1809
- const DOWNLOAD_KINDS = ["note", "session", "effort", "equipment"];
1960
+ const DOWNLOAD_KINDS = ["note", "playground", "session", "effort", "equipment"];
1810
1961
  if (!id && kind !== "equipment" || !DOWNLOAD_KINDS.includes(kind ?? "")) {
1811
- writeErr("[wod download] Usage: wod download <note|session|effort|equipment> <id> [-o <path>]");
1962
+ writeErr(`[wod ${verb}] Usage: wod ${verb} <note|playground|session|effort|equipment> <id> [-o <path>]`);
1812
1963
  return 1;
1813
1964
  }
1814
1965
  const targetId = id;
1815
1966
  const db = await openCliDb(cliDb());
1816
1967
  try {
1817
- if (kind === "note") {
1968
+ if (kind === "note" || kind === "playground") {
1818
1969
  const bundle = await getNoteBundle(db.db, targetId);
1819
1970
  if (!bundle.note) {
1820
- writeErr(`[wod download] Note not found in ${db.path}: ${id}`);
1971
+ writeErr(`[wod ${verb}] Note not found in ${db.path}: ${id}`);
1821
1972
  return 1;
1822
1973
  }
1823
1974
  const markdown = composeNoteMarkdown(bundle.note);
@@ -1827,11 +1978,11 @@ async function cliMain(argv = process.argv.slice(2), io = {}) {
1827
1978
  } else {
1828
1979
  nodeFs().writeFileSync(target, markdown, "utf-8");
1829
1980
  }
1830
- if (parsed.output !== "-") writeOut(`[wod download] note ${id} \u2192 ${target}`);
1981
+ if (parsed.output !== "-") writeOut(`[wod ${verb}] ${kind} ${id} \u2192 ${target}`);
1831
1982
  } else if (kind === "session") {
1832
1983
  const bundle = await getSessionBundle(db.db, targetId);
1833
1984
  if (!bundle) {
1834
- writeErr(`[wod download] Session not found in ${db.path}: ${id}`);
1985
+ writeErr(`[wod ${verb}] Session not found in ${db.path}: ${id}`);
1835
1986
  return 1;
1836
1987
  }
1837
1988
  const csv = sessionToCsv(bundle.session, bundle.events);
@@ -1841,11 +1992,11 @@ async function cliMain(argv = process.argv.slice(2), io = {}) {
1841
1992
  } else {
1842
1993
  nodeFs().writeFileSync(target, csv, "utf-8");
1843
1994
  }
1844
- if (parsed.output !== "-") writeOut(`[wod download] session ${id} \u2192 ${target} (${bundle.events.length} events)`);
1995
+ if (parsed.output !== "-") writeOut(`[wod ${verb}] session ${id} \u2192 ${target} (${bundle.events.length} events)`);
1845
1996
  } else if (kind === "effort") {
1846
1997
  const effort = await getEffortBySlug(db.db, targetId);
1847
1998
  if (!effort) {
1848
- writeErr(`[wod download] Effort not found in ${db.path}: ${id}`);
1999
+ writeErr(`[wod ${verb}] Effort not found in ${db.path}: ${id}`);
1849
2000
  return 1;
1850
2001
  }
1851
2002
  const markdown = effortToMarkdown(effort);
@@ -1855,7 +2006,7 @@ async function cliMain(argv = process.argv.slice(2), io = {}) {
1855
2006
  } else {
1856
2007
  nodeFs().writeFileSync(target, markdown, "utf-8");
1857
2008
  }
1858
- writeOut(`[wod download] effort ${id} \u2192 ${target}`);
2009
+ writeOut(`[wod ${verb}] effort ${id} \u2192 ${target}`);
1859
2010
  } else {
1860
2011
  const labels = (await listTagsByType(db.db, "equipment")).map((t) => t.label).sort();
1861
2012
  const csv = equipmentToCsv(labels);
@@ -1865,7 +2016,7 @@ async function cliMain(argv = process.argv.slice(2), io = {}) {
1865
2016
  } else {
1866
2017
  nodeFs().writeFileSync(target, csv, "utf-8");
1867
2018
  }
1868
- writeOut(`[wod download] equipment \u2192 ${target} (${labels.length} labels)`);
2019
+ writeOut(`[wod ${verb}] equipment \u2192 ${target} (${labels.length} labels)`);
1869
2020
  }
1870
2021
  } finally {
1871
2022
  await db.close();
@@ -1875,9 +2026,9 @@ async function cliMain(argv = process.argv.slice(2), io = {}) {
1875
2026
  if (parsed.subcommand === "import") {
1876
2027
  const kind = parsed.target;
1877
2028
  const file = parsed.target2;
1878
- const IMPORT_KINDS = ["note", "session", "effort", "equipment"];
2029
+ const IMPORT_KINDS = ["note", "playground", "session", "effort", "equipment"];
1879
2030
  if (!file || !IMPORT_KINDS.includes(kind ?? "")) {
1880
- writeErr("[wod import] Usage: wod import <note|session|effort|equipment> <file>");
2031
+ writeErr("[wod import] Usage: wod import <note|playground|session|effort|equipment> <file>");
1881
2032
  return 1;
1882
2033
  }
1883
2034
  const fs = nodeFs();
@@ -1885,7 +2036,7 @@ async function cliMain(argv = process.argv.slice(2), io = {}) {
1885
2036
  writeErr(`[wod import] File not found: ${file}`);
1886
2037
  return 1;
1887
2038
  }
1888
- if (kind === "note") {
2039
+ if (kind === "note" || kind === "playground") {
1889
2040
  const source = fs.readFileSync(file, "utf-8");
1890
2041
  const identity = extractNoteIdentity(source);
1891
2042
  const tags = extractTypedTags(source);
@@ -1899,7 +2050,7 @@ async function cliMain(argv = process.argv.slice(2), io = {}) {
1899
2050
  tags
1900
2051
  });
1901
2052
  writeOut(
1902
- `[wod import] note ${persisted.noteId}${persisted.created ? "" : " (updated)"} \u2014 ${persisted.blocks} blocks${persisted.tags > 0 ? `, ${persisted.tags} tags` : ""} \u2192 ${db2.path}`
2053
+ `[wod import] ${kind} ${persisted.noteId}${persisted.created ? "" : " (updated)"} \u2014 ${persisted.blocks} blocks${persisted.tags > 0 ? `, ${persisted.tags} tags` : ""} \u2192 ${db2.path}`
1903
2054
  );
1904
2055
  } finally {
1905
2056
  await db2.close();
@@ -1948,6 +2099,26 @@ async function cliMain(argv = process.argv.slice(2), io = {}) {
1948
2099
  }
1949
2100
  return 0;
1950
2101
  }
2102
+ if (parsed.subcommand === "publish") {
2103
+ const isDir = (s) => Boolean(s && (s.startsWith(".") || s.startsWith("/") || s.includes("/") || s.includes("\\")));
2104
+ const catalog = isDir(parsed.target) ? void 0 : parsed.target;
2105
+ const targetDir = isDir(parsed.target) ? parsed.target : parsed.target2;
2106
+ const outDir = parsed.output ?? targetDir ?? (catalog ? `./dist/seed/${catalog}` : "./dist/seed");
2107
+ const db = await openCliDb(cliDb());
2108
+ try {
2109
+ const result = await publishCatalogSeed(db, {
2110
+ catalog,
2111
+ outDir,
2112
+ version: parsed.seedVersion
2113
+ });
2114
+ writeOut(
2115
+ `[wod publish] published ${result.catalogs.join(", ")} (${result.totalNotes} notes) \u2192 ${result.outDir} (manifest schema v${result.manifest.schema}, version ${result.manifest.version})`
2116
+ );
2117
+ } finally {
2118
+ await db.close();
2119
+ }
2120
+ return 0;
2121
+ }
1951
2122
  if (parsed.subcommand === "build") {
1952
2123
  const cwd = process.cwd();
1953
2124
  const root = nodePath().resolve(cwd, parsed.dir ?? parsed.target ?? ".");
@@ -1955,7 +2126,7 @@ async function cliMain(argv = process.argv.slice(2), io = {}) {
1955
2126
  writeErr(`[wod build] No markdown/ corpus under ${root} \u2014 run from the content repo root (or pass its directory).`);
1956
2127
  return 1;
1957
2128
  }
1958
- const { generateSeed } = await import('./compiler-KT6P3AC6.mjs');
2129
+ const { generateSeed } = await import('./compiler-CTPLIZJK.mjs');
1959
2130
  try {
1960
2131
  const result = generateSeed({
1961
2132
  corpusRoot: root,
@@ -2024,14 +2195,8 @@ async function cliMain(argv = process.argv.slice(2), io = {}) {
2024
2195
  return 0;
2025
2196
  }
2026
2197
  if (parsed.subcommand === "server" || parsed.subcommand === "serve" || parsed.subcommand === "playground") {
2027
- if (!("Bun" in globalThis)) {
2028
- writeErr(
2029
- `[wod ${parsed.subcommand}] Error: hosting requires the Bun runtime (https://bun.sh) \u2014 run \`bun wod server\` / \`bun wod playground\`.`
2030
- );
2031
- return 1;
2032
- }
2033
2198
  const mode = parsed.subcommand === "playground" ? "playground" : "server";
2034
- const { runCliServer } = await import('./server-Z2T7IGZX.mjs');
2199
+ const { runCliServer } = await import('./server-JXECWS4A.mjs');
2035
2200
  const options = { env: cliEnv() };
2036
2201
  if (mode === "server") options.database = cliDb();
2037
2202
  if (parsed.host !== void 0) options.host = parsed.host;
@@ -2057,5 +2222,5 @@ async function cliMain(argv = process.argv.slice(2), io = {}) {
2057
2222
  }
2058
2223
 
2059
2224
  export { PackLoadError, ParseSyntaxError, WqlSyntaxError, cliMain, formatExecutionOutput, formatParseOutput, formatQueryOutput, loadCliEnvironment, loadLanguagePack, loadQueryData, parseCliArgs, readStdin, resolveDatabaseConfig, runExecution, runParse, runQueryCli };
2060
- //# sourceMappingURL=chunk-OZI2FYPF.mjs.map
2061
- //# sourceMappingURL=chunk-OZI2FYPF.mjs.map
2225
+ //# sourceMappingURL=chunk-OTJAPVT2.mjs.map
2226
+ //# sourceMappingURL=chunk-OTJAPVT2.mjs.map