@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.
- package/dist/bin/wod.cjs +274 -58
- package/dist/bin/wod.cjs.map +1 -1
- package/dist/bin/wod.mjs +3 -2
- package/dist/bin/wod.mjs.map +1 -1
- package/dist/{chunk-EUQWEX6Z.mjs → chunk-CYKNTEGY.mjs} +3 -3
- package/dist/chunk-CYKNTEGY.mjs.map +1 -0
- package/dist/{compiler-KT6P3AC6.mjs → chunk-FZKXOYMJ.mjs} +4 -5
- package/dist/chunk-FZKXOYMJ.mjs.map +1 -0
- package/dist/{chunk-DZESCGCM.mjs → chunk-MA6BBTWC.mjs} +54 -12
- package/dist/chunk-MA6BBTWC.mjs.map +1 -0
- package/dist/{chunk-OZI2FYPF.mjs → chunk-OTJAPVT2.mjs} +217 -52
- package/dist/chunk-OTJAPVT2.mjs.map +1 -0
- package/dist/{chunk-K3GZZFAC.mjs → chunk-V23POHG2.mjs} +10 -9
- package/dist/chunk-V23POHG2.mjs.map +1 -0
- package/dist/compiler-CTPLIZJK.mjs +9 -0
- package/dist/compiler-CTPLIZJK.mjs.map +1 -0
- package/dist/index.cjs +274 -58
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.mjs +5 -4
- package/dist/server/app.cjs +7 -6
- package/dist/server/app.cjs.map +1 -1
- package/dist/server/app.mjs +1 -1
- package/dist/server-JXECWS4A.mjs +11 -0
- package/dist/{server-Z2T7IGZX.mjs.map → server-JXECWS4A.mjs.map} +1 -1
- package/dist/web/playground/assets/main-Bod1taHc.js +188 -0
- package/dist/web/playground/assets/{statementBuilderFlow-gvzmxfiE.js → statementBuilderFlow-CCaLco1R.js} +2 -2
- package/dist/web/playground/assets/{vendor-codemirror-BtJgoBjf.js → vendor-codemirror-C41SFn-r.js} +2 -2
- package/dist/web/playground/assets/vendor-g-NJOjvC.js +198 -0
- package/dist/web/playground/index.html +3 -3
- package/dist/web/playground/seed/core/chunks/canvas.2d3e2e0e.json +1 -0
- package/dist/web/playground/seed/core/manifest.json +7 -7
- package/dist/web/playground/seed/core/report.html +11 -11
- package/dist/web/server/assets/main-DtiBHHer.js +187 -0
- package/dist/web/server/assets/{statementBuilderFlow-DT1tyXgc.js → statementBuilderFlow-MCfILbWE.js} +2 -2
- package/dist/web/server/assets/{vendor-codemirror-BtJgoBjf.js → vendor-codemirror-C41SFn-r.js} +2 -2
- package/dist/web/server/assets/vendor-g-NJOjvC.js +198 -0
- package/dist/web/server/index.html +3 -3
- package/dist/web/server/seed/core/chunks/canvas.2d3e2e0e.json +1 -0
- package/dist/web/server/seed/core/manifest.json +7 -7
- package/dist/web/server/seed/core/report.html +10 -10
- package/package.json +6 -8
- package/dist/chunk-DZESCGCM.mjs.map +0 -1
- package/dist/chunk-EUQWEX6Z.mjs.map +0 -1
- package/dist/chunk-K3GZZFAC.mjs.map +0 -1
- package/dist/chunk-OZI2FYPF.mjs.map +0 -1
- package/dist/compiler-KT6P3AC6.mjs.map +0 -1
- package/dist/server-Z2T7IGZX.mjs +0 -11
- package/dist/web/playground/assets/main-Bh_mthqc.js +0 -212
- package/dist/web/playground/assets/vendor-BlJqgFME.js +0 -201
- package/dist/web/playground/seed/core/chunks/canvas.d2a5e6e7.json +0 -1
- package/dist/web/server/assets/main-CPRFCrjV.js +0 -211
- package/dist/web/server/assets/vendor-BlJqgFME.js +0 -201
- package/dist/web/server/seed/core/chunks/canvas.d2a5e6e7.json +0 -1
package/dist/bin/wod.cjs
CHANGED
|
@@ -9,11 +9,17 @@ var path = require('path');
|
|
|
9
9
|
var wodWikiWql = require('@bitcobblers/wod-wiki-wql');
|
|
10
10
|
var crypto = require('crypto');
|
|
11
11
|
var promises = require('fs/promises');
|
|
12
|
+
var stream = require('stream');
|
|
12
13
|
var sql = require('@bitcobblers/wod-wiki-storage/sql');
|
|
13
14
|
var wodWikiStorage = require('@bitcobblers/wod-wiki-storage');
|
|
15
|
+
var http = require('http');
|
|
14
16
|
var wodWikiEngine = require('@bitcobblers/wod-wiki-engine');
|
|
15
17
|
|
|
16
18
|
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
|
|
19
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
20
|
+
|
|
21
|
+
var http__default = /*#__PURE__*/_interopDefault(http);
|
|
22
|
+
|
|
17
23
|
var __defProp = Object.defineProperty;
|
|
18
24
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
19
25
|
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
@@ -1066,10 +1072,10 @@ var init_errors = __esm({
|
|
|
1066
1072
|
};
|
|
1067
1073
|
}
|
|
1068
1074
|
});
|
|
1069
|
-
function
|
|
1070
|
-
const
|
|
1071
|
-
if (
|
|
1072
|
-
return
|
|
1075
|
+
function streamFile(path) {
|
|
1076
|
+
const bun = globalThis.Bun;
|
|
1077
|
+
if (bun?.file) return bun.file(path);
|
|
1078
|
+
return stream.Readable.toWeb(fs.createReadStream(path));
|
|
1073
1079
|
}
|
|
1074
1080
|
function jsonParam(url, name) {
|
|
1075
1081
|
const raw = qget(url, name);
|
|
@@ -1158,14 +1164,14 @@ async function realpathSafe(path) {
|
|
|
1158
1164
|
async function serveFile(real, rel) {
|
|
1159
1165
|
const cache = rel.includes("seed/chunks/") ? "public, max-age=31536000, immutable" : "no-cache";
|
|
1160
1166
|
const ext = rel.slice(rel.lastIndexOf(".") + 1).toLowerCase();
|
|
1161
|
-
return new Response(
|
|
1167
|
+
return new Response(streamFile(real), {
|
|
1162
1168
|
headers: { "content-type": MIME[ext] ?? "application/octet-stream", "cache-control": cache }
|
|
1163
1169
|
});
|
|
1164
1170
|
}
|
|
1165
1171
|
async function serveSpaShell(distRoot) {
|
|
1166
1172
|
const index = await realpathSafe(path.join(distRoot, "index.html"));
|
|
1167
1173
|
if (index && (index === distRoot || index.startsWith(distRoot + path.sep)) && await promises.stat(index).then((s) => s.isFile(), () => false)) {
|
|
1168
|
-
return new Response(
|
|
1174
|
+
return new Response(streamFile(index), {
|
|
1169
1175
|
headers: { "content-type": MIME.html, "cache-control": "no-cache" }
|
|
1170
1176
|
});
|
|
1171
1177
|
}
|
|
@@ -1352,12 +1358,55 @@ var server_exports = {};
|
|
|
1352
1358
|
__export(server_exports, {
|
|
1353
1359
|
runCliServer: () => runCliServer
|
|
1354
1360
|
});
|
|
1355
|
-
function
|
|
1361
|
+
function withResolvers() {
|
|
1362
|
+
let resolve2;
|
|
1363
|
+
let reject;
|
|
1364
|
+
const promise = new Promise((res, rej) => {
|
|
1365
|
+
resolve2 = res;
|
|
1366
|
+
reject = rej;
|
|
1367
|
+
});
|
|
1368
|
+
return { promise, resolve: resolve2, reject };
|
|
1369
|
+
}
|
|
1370
|
+
function startHttpServer(handler, host, port) {
|
|
1356
1371
|
const b = globalThis.Bun;
|
|
1357
|
-
if (
|
|
1358
|
-
|
|
1372
|
+
if (b?.serve) {
|
|
1373
|
+
const s = b.serve({ port, hostname: host, fetch: handler });
|
|
1374
|
+
return Promise.resolve({ stop: () => s.stop() });
|
|
1359
1375
|
}
|
|
1360
|
-
|
|
1376
|
+
const { promise, resolve: resolve2, reject } = withResolvers();
|
|
1377
|
+
const server = http__default.default.createServer(async (req, res) => {
|
|
1378
|
+
try {
|
|
1379
|
+
const url = new URL(req.url || "/", `http://${req.headers.host || `${host}:${port}`}`);
|
|
1380
|
+
const hasBody = req.method !== "GET" && req.method !== "HEAD";
|
|
1381
|
+
const webReq = new Request(url, {
|
|
1382
|
+
method: req.method,
|
|
1383
|
+
headers: req.headers,
|
|
1384
|
+
body: hasBody ? stream.Readable.toWeb(req) : null,
|
|
1385
|
+
...hasBody ? { duplex: "half" } : {}
|
|
1386
|
+
});
|
|
1387
|
+
const webRes = await handler(webReq);
|
|
1388
|
+
res.writeHead(webRes.status, Object.fromEntries(webRes.headers.entries()));
|
|
1389
|
+
if (webRes.body) {
|
|
1390
|
+
stream.Readable.fromWeb(webRes.body).pipe(res);
|
|
1391
|
+
} else {
|
|
1392
|
+
res.end();
|
|
1393
|
+
}
|
|
1394
|
+
} catch {
|
|
1395
|
+
res.statusCode = 500;
|
|
1396
|
+
res.end("internal server error");
|
|
1397
|
+
}
|
|
1398
|
+
});
|
|
1399
|
+
server.once("error", reject);
|
|
1400
|
+
server.listen(port, host, () => {
|
|
1401
|
+
resolve2({
|
|
1402
|
+
stop: () => {
|
|
1403
|
+
const close = withResolvers();
|
|
1404
|
+
server.close(() => close.resolve());
|
|
1405
|
+
return close.promise;
|
|
1406
|
+
}
|
|
1407
|
+
});
|
|
1408
|
+
});
|
|
1409
|
+
return promise;
|
|
1361
1410
|
}
|
|
1362
1411
|
function moduleDir() {
|
|
1363
1412
|
const p = nodePath();
|
|
@@ -1411,7 +1460,6 @@ function resolveBind(mode, options) {
|
|
|
1411
1460
|
return { host, port };
|
|
1412
1461
|
}
|
|
1413
1462
|
async function runCliServer(mode, options, writeOut) {
|
|
1414
|
-
const runtime = bun();
|
|
1415
1463
|
const { host, port } = resolveBind(mode, options);
|
|
1416
1464
|
let db;
|
|
1417
1465
|
let dbLabel = "no database";
|
|
@@ -1432,7 +1480,7 @@ async function runCliServer(mode, options, writeOut) {
|
|
|
1432
1480
|
try {
|
|
1433
1481
|
const dist = resolveDist(mode, options);
|
|
1434
1482
|
const handler = createHandler(db?.db ?? null, { playgroundDist: dist });
|
|
1435
|
-
server =
|
|
1483
|
+
server = await startHttpServer(handler, host, port);
|
|
1436
1484
|
if (mode === "server") {
|
|
1437
1485
|
writeOut(`wod server: http://${host}:${port} \u2014 ${dbLabel} \xB7 dist ${dist}`);
|
|
1438
1486
|
} else {
|
|
@@ -2130,18 +2178,24 @@ async function saveSource(db, options, startTime) {
|
|
|
2130
2178
|
throw new Error(`Stale Note source revision for ${noteId}; expected ${expected}`);
|
|
2131
2179
|
}
|
|
2132
2180
|
let sourceRevision = existing?.rawContent === options.source ? existing.sourceRevision : (existing?.sourceRevision ?? 0) + 1;
|
|
2181
|
+
const history = await tx.readonly("block_revisions").getAllFromIndex("by-note", noteId);
|
|
2182
|
+
const latest = /* @__PURE__ */ new Map();
|
|
2183
|
+
for (const row of history) {
|
|
2184
|
+
if ((latest.get(row.blockId)?.version ?? 0) < row.version) latest.set(row.blockId, row);
|
|
2185
|
+
}
|
|
2186
|
+
let ordinal = 0;
|
|
2133
2187
|
const blocks = wodWikiCore.reconcileNoteBlocks(options.source, {
|
|
2134
2188
|
sourceRevision,
|
|
2135
2189
|
blocks: options.blocks ?? (existing?.rawContent === options.source ? existing.blocks : void 0),
|
|
2136
2190
|
registeredWidgets,
|
|
2137
|
-
createId:
|
|
2191
|
+
createId: () => {
|
|
2192
|
+
while (true) {
|
|
2193
|
+
const candidate = uuid5(CLI_UUID_NAMESPACE, `${noteId}:block:${ordinal++}`);
|
|
2194
|
+
if (!latest.has(candidate)) return candidate;
|
|
2195
|
+
}
|
|
2196
|
+
}
|
|
2138
2197
|
});
|
|
2139
2198
|
wodWikiCore.validateNoteBlocks(options.source, blocks, sourceRevision, { registeredWidgets });
|
|
2140
|
-
const history = await tx.readonly("block_revisions").getAllFromIndex("by-note", noteId);
|
|
2141
|
-
const latest = /* @__PURE__ */ new Map();
|
|
2142
|
-
for (const row of history) {
|
|
2143
|
-
if ((latest.get(row.blockId)?.version ?? 0) < row.version) latest.set(row.blockId, row);
|
|
2144
|
-
}
|
|
2145
2199
|
const revisions = [];
|
|
2146
2200
|
for (const block of Object.values(blocks)) {
|
|
2147
2201
|
const saved = latest.get(block.id);
|
|
@@ -2163,6 +2217,8 @@ async function saveSource(db, options, startTime) {
|
|
|
2163
2217
|
sourceRevision++;
|
|
2164
2218
|
for (const block of Object.values(blocks)) block.sourceRevision = sourceRevision;
|
|
2165
2219
|
}
|
|
2220
|
+
const fm = wodWikiEngine.readFrontmatterMeta(options.source);
|
|
2221
|
+
const catalog = typeof fm["catalog"] === "string" && fm["catalog"].trim() || typeof fm["collection"] === "string" && fm["collection"].trim() || existing?.catalog;
|
|
2166
2222
|
const note = {
|
|
2167
2223
|
...existing,
|
|
2168
2224
|
id: noteId,
|
|
@@ -2172,7 +2228,8 @@ async function saveSource(db, options, startTime) {
|
|
|
2172
2228
|
createdAt: existing?.createdAt ?? now,
|
|
2173
2229
|
rawContent: options.source,
|
|
2174
2230
|
sourceRevision,
|
|
2175
|
-
blocks
|
|
2231
|
+
blocks,
|
|
2232
|
+
...catalog ? { catalog } : {}
|
|
2176
2233
|
};
|
|
2177
2234
|
const priorRows = await tx.readonly("block_index").getAllFromIndex("by-note", noteId);
|
|
2178
2235
|
const priorEfforts = await tx.readonly("block_efforts").getAllFromIndex("by-note", noteId);
|
|
@@ -2200,6 +2257,7 @@ async function saveSource(db, options, startTime) {
|
|
|
2200
2257
|
noteTitle: note.title,
|
|
2201
2258
|
createdAt: prior?.createdAt ?? now,
|
|
2202
2259
|
blockContentId: fingerprint,
|
|
2260
|
+
...catalog ? { catalog } : {},
|
|
2203
2261
|
...note.sourceId ? { sourceId: note.sourceId } : {}
|
|
2204
2262
|
});
|
|
2205
2263
|
if (block.kind === "wod") {
|
|
@@ -2417,6 +2475,136 @@ function defaultSeedDirs(cwd) {
|
|
|
2417
2475
|
return ["core", "community"].map((id) => nodePath().resolve(base, id)).filter((dir) => nodeFs().existsSync(nodePath().resolve(dir, "manifest.json")));
|
|
2418
2476
|
}
|
|
2419
2477
|
|
|
2478
|
+
// src/cli/publish.ts
|
|
2479
|
+
init_db();
|
|
2480
|
+
init_contract();
|
|
2481
|
+
init_compiler();
|
|
2482
|
+
init_report();
|
|
2483
|
+
async function publishCatalogSeed(db, options) {
|
|
2484
|
+
const t0 = Date.now();
|
|
2485
|
+
const fs = nodeFs();
|
|
2486
|
+
const path = nodePath();
|
|
2487
|
+
const allNotes = await db.storage.readonly("notes").getAll();
|
|
2488
|
+
if (allNotes.length === 0) {
|
|
2489
|
+
throw new Error(`[wod publish] No notes found in database ${db.path}`);
|
|
2490
|
+
}
|
|
2491
|
+
const notesByCatalog = /* @__PURE__ */ new Map();
|
|
2492
|
+
for (const note of allNotes) {
|
|
2493
|
+
const fm = wodWikiEngine.readFrontmatterMeta(note.rawContent);
|
|
2494
|
+
const cat = typeof fm["catalog"] === "string" && fm["catalog"].trim() || typeof fm["collection"] === "string" && fm["collection"].trim() || note.catalog || wodWikiWql.catalogOfItem(note);
|
|
2495
|
+
const key = cat || "__uncataloged__";
|
|
2496
|
+
const list = notesByCatalog.get(key) ?? [];
|
|
2497
|
+
list.push(note);
|
|
2498
|
+
notesByCatalog.set(key, list);
|
|
2499
|
+
}
|
|
2500
|
+
const targetCatalogs = /* @__PURE__ */ new Map();
|
|
2501
|
+
if (options.catalog) {
|
|
2502
|
+
const matching = notesByCatalog.get(options.catalog);
|
|
2503
|
+
if (matching) {
|
|
2504
|
+
targetCatalogs.set(options.catalog, matching);
|
|
2505
|
+
} else if (notesByCatalog.has("__uncataloged__") && notesByCatalog.size === 1) {
|
|
2506
|
+
targetCatalogs.set(options.catalog, notesByCatalog.get("__uncataloged__"));
|
|
2507
|
+
} else {
|
|
2508
|
+
const available = [...notesByCatalog.keys()].filter((k) => k !== "__uncataloged__");
|
|
2509
|
+
throw new Error(
|
|
2510
|
+
`[wod publish] Catalog "${options.catalog}" not found in ${db.path}.` + (available.length > 0 ? ` Available catalogs: ${available.join(", ")}` : "")
|
|
2511
|
+
);
|
|
2512
|
+
}
|
|
2513
|
+
} else {
|
|
2514
|
+
for (const [cat, notes] of notesByCatalog.entries()) {
|
|
2515
|
+
targetCatalogs.set(cat === "__uncataloged__" ? "default" : cat, notes);
|
|
2516
|
+
}
|
|
2517
|
+
}
|
|
2518
|
+
const allIndexRows = await db.storage.readonly("block_index").getAll();
|
|
2519
|
+
const allEffortRows = await db.storage.readonly("block_efforts").getAll();
|
|
2520
|
+
const indexByNote = /* @__PURE__ */ new Map();
|
|
2521
|
+
for (const row of allIndexRows) {
|
|
2522
|
+
const list = indexByNote.get(row.noteId) ?? [];
|
|
2523
|
+
list.push(row);
|
|
2524
|
+
indexByNote.set(row.noteId, list);
|
|
2525
|
+
}
|
|
2526
|
+
const effortsByNote = /* @__PURE__ */ new Map();
|
|
2527
|
+
for (const row of allEffortRows) {
|
|
2528
|
+
const list = effortsByNote.get(row.noteId) ?? [];
|
|
2529
|
+
list.push(row);
|
|
2530
|
+
effortsByNote.set(row.noteId, list);
|
|
2531
|
+
}
|
|
2532
|
+
const outDir = path.resolve(process.cwd(), options.outDir);
|
|
2533
|
+
const chunksDir = path.join(outDir, "chunks");
|
|
2534
|
+
fs.mkdirSync(chunksDir, { recursive: true });
|
|
2535
|
+
const manifestChunks = [];
|
|
2536
|
+
let totalNotes = 0;
|
|
2537
|
+
const publishedCatalogs = [];
|
|
2538
|
+
for (const [catalogName, notes] of targetCatalogs.entries()) {
|
|
2539
|
+
publishedCatalogs.push(catalogName);
|
|
2540
|
+
const chunkRows = [];
|
|
2541
|
+
for (const note of notes) {
|
|
2542
|
+
const stem = (note.title || note.id).toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "") || "note";
|
|
2543
|
+
const notePath = note.sourcePath ?? `markdown/collections/${catalogName}/${stem}.md`;
|
|
2544
|
+
const noteBlockIndex = indexByNote.get(note.id) ?? [];
|
|
2545
|
+
const noteBlockEfforts = effortsByNote.get(note.id) ?? [];
|
|
2546
|
+
const blockIndex = noteBlockIndex.length > 0 ? noteBlockIndex.map((b) => ({
|
|
2547
|
+
...b,
|
|
2548
|
+
isStatic: true,
|
|
2549
|
+
sourceId: b.sourceId ?? `collection:${catalogName}/${stem}`,
|
|
2550
|
+
sourcePath: b.sourcePath ?? notePath,
|
|
2551
|
+
catalog: catalogName
|
|
2552
|
+
})) : compileSeedRow(notePath, note.rawContent, {
|
|
2553
|
+
routeId: `${catalogName}/${stem}`,
|
|
2554
|
+
sourceId: `collection:${catalogName}/${stem}`,
|
|
2555
|
+
createdAt: note.createdAt,
|
|
2556
|
+
noteTitle: note.title
|
|
2557
|
+
}).blockIndex;
|
|
2558
|
+
const blockEfforts = noteBlockEfforts.map((e) => ({
|
|
2559
|
+
...e,
|
|
2560
|
+
isStatic: true,
|
|
2561
|
+
sourcePath: e.sourcePath ?? notePath
|
|
2562
|
+
}));
|
|
2563
|
+
chunkRows.push({
|
|
2564
|
+
path: notePath,
|
|
2565
|
+
content: note.rawContent,
|
|
2566
|
+
blocks: note.blocks,
|
|
2567
|
+
blockIndex,
|
|
2568
|
+
blockEfforts
|
|
2569
|
+
});
|
|
2570
|
+
}
|
|
2571
|
+
const chunkId = `collection.${catalogName}`;
|
|
2572
|
+
const json2 = JSON.stringify(chunkRows);
|
|
2573
|
+
const digest = nodeCrypto().createHash("sha256").update(json2).digest();
|
|
2574
|
+
const sha = [...digest].map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
2575
|
+
const chunkFile = `chunks/${chunkId}.${sha.slice(0, 8)}.json`;
|
|
2576
|
+
fs.writeFileSync(path.join(outDir, chunkFile), json2, "utf-8");
|
|
2577
|
+
manifestChunks.push({
|
|
2578
|
+
id: chunkId,
|
|
2579
|
+
path: chunkFile,
|
|
2580
|
+
sha256: sha,
|
|
2581
|
+
bytes: Buffer.byteLength(json2, "utf8"),
|
|
2582
|
+
count: chunkRows.length,
|
|
2583
|
+
kind: "notes"
|
|
2584
|
+
});
|
|
2585
|
+
totalNotes += chunkRows.length;
|
|
2586
|
+
}
|
|
2587
|
+
const manifest = {
|
|
2588
|
+
schema: SEED_SCHEMA2,
|
|
2589
|
+
version: options.version ?? 1,
|
|
2590
|
+
versionLabel: `v${options.version ?? 1}`,
|
|
2591
|
+
builtAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2592
|
+
chunks: manifestChunks
|
|
2593
|
+
};
|
|
2594
|
+
fs.writeFileSync(path.join(outDir, "manifest.json"), JSON.stringify(manifest, null, 2) + "\n", "utf-8");
|
|
2595
|
+
writeBuildReport(outDir, {
|
|
2596
|
+
manifest,
|
|
2597
|
+
totalMs: Date.now() - t0,
|
|
2598
|
+
passMs: { scan: 0, blockIndex: 0, parseGate: 0, blockEfforts: 0, write: Date.now() - t0 },
|
|
2599
|
+
fileCount: totalNotes});
|
|
2600
|
+
return {
|
|
2601
|
+
manifest,
|
|
2602
|
+
outDir,
|
|
2603
|
+
catalogs: publishedCatalogs,
|
|
2604
|
+
totalNotes
|
|
2605
|
+
};
|
|
2606
|
+
}
|
|
2607
|
+
|
|
2420
2608
|
// src/cli/formatters.ts
|
|
2421
2609
|
function escapeCsvField(field) {
|
|
2422
2610
|
if (field === void 0 || field === null) return "";
|
|
@@ -2707,7 +2895,7 @@ ${escapeCsvField(data.parsed.metric)},${data.scalar},${escapeCsvField(data.unit
|
|
|
2707
2895
|
}
|
|
2708
2896
|
|
|
2709
2897
|
// src/version.ts
|
|
2710
|
-
var VERSION = "0.36.
|
|
2898
|
+
var VERSION = "0.36.2135";
|
|
2711
2899
|
|
|
2712
2900
|
// src/cli/runner.ts
|
|
2713
2901
|
function getFs2() {
|
|
@@ -2852,9 +3040,16 @@ function parseCliArgs(args) {
|
|
|
2852
3040
|
i++;
|
|
2853
3041
|
} else if (arg.startsWith("--dir=")) {
|
|
2854
3042
|
result.dir = arg.slice("--dir=".length);
|
|
3043
|
+
} else if (arg === "--seed-version") {
|
|
3044
|
+
const v = Number(args[++i]);
|
|
3045
|
+
if (Number.isSafeInteger(v) && v >= 0) result.seedVersion = v;
|
|
3046
|
+
i++;
|
|
3047
|
+
} else if (arg.startsWith("--seed-version=")) {
|
|
3048
|
+
const v = Number(arg.slice("--seed-version=".length));
|
|
3049
|
+
if (Number.isSafeInteger(v) && v >= 0) result.seedVersion = v;
|
|
2855
3050
|
i++;
|
|
2856
3051
|
} else if (!result.subcommand) {
|
|
2857
|
-
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") {
|
|
3052
|
+
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") {
|
|
2858
3053
|
result.subcommand = arg;
|
|
2859
3054
|
} else {
|
|
2860
3055
|
result.target = arg;
|
|
@@ -2880,9 +3075,9 @@ function parseCliArgs(args) {
|
|
|
2880
3075
|
function readBunStdinText() {
|
|
2881
3076
|
const g = globalThis;
|
|
2882
3077
|
if (g && typeof g === "object" && "Bun" in g) {
|
|
2883
|
-
const
|
|
2884
|
-
if (
|
|
2885
|
-
const stdin =
|
|
3078
|
+
const bun = g.Bun;
|
|
3079
|
+
if (bun && typeof bun === "object" && "stdin" in bun) {
|
|
3080
|
+
const stdin = bun.stdin;
|
|
2886
3081
|
if (stdin && typeof stdin === "object" && "text" in stdin) {
|
|
2887
3082
|
const text = stdin.text;
|
|
2888
3083
|
if (typeof text === "function") return text;
|
|
@@ -2915,8 +3110,10 @@ USAGE:
|
|
|
2915
3110
|
wod parse <file | -> [options]
|
|
2916
3111
|
wod run <file | -> [options]
|
|
2917
3112
|
wod query '<wql-string>' [options]
|
|
2918
|
-
wod
|
|
2919
|
-
wod
|
|
3113
|
+
wod export <note|playground|session|effort|equipment> <id> [options]
|
|
3114
|
+
wod download <note|playground|session|effort|equipment> <id> [options]
|
|
3115
|
+
wod import <note|playground|session|effort|equipment> <file> [options]
|
|
3116
|
+
wod publish [catalog] [options]
|
|
2920
3117
|
wod build [dir] [options]
|
|
2921
3118
|
wod seed [options]
|
|
2922
3119
|
wod reset [options]
|
|
@@ -2936,23 +3133,23 @@ SUBCOMMANDS:
|
|
|
2936
3133
|
execution-log IR
|
|
2937
3134
|
query '<wql>' Evaluate WQL against the database (or an explicit
|
|
2938
3135
|
dataset) and emit QueryResult / RowsQueryResult IR
|
|
2939
|
-
|
|
3136
|
+
export note <id> Export the note's authored markdown to <id>.md.
|
|
3137
|
+
(alias: export playground, download note).
|
|
2940
3138
|
The frontmatter identity stamp allows re-import.
|
|
2941
3139
|
Frontmatter tags (general + typed equipment/quality/\u2026)
|
|
2942
3140
|
are materialized into the tag graph on parse/import.
|
|
2943
|
-
|
|
3141
|
+
export session <id>
|
|
2944
3142
|
Write the session's generated metrics (event + summary
|
|
2945
3143
|
rows, one CSV row per metric) to <id>.csv. Import back
|
|
2946
|
-
with 'wod import session'.
|
|
2947
|
-
|
|
3144
|
+
with 'wod import session'. (alias: download session).
|
|
3145
|
+
export effort <slug>
|
|
2948
3146
|
Write the effort registry row as corpus-style markdown
|
|
2949
|
-
(frontmatter id/slug/label/aliases/baseAttributes + body)
|
|
2950
3147
|
to <slug>.md. Import back with 'wod import effort'.
|
|
2951
|
-
|
|
2952
|
-
Write every equipment tag as a one-column CSV to
|
|
3148
|
+
export equipment Write every equipment tag as a one-column CSV to
|
|
2953
3149
|
equipment.csv. Import back with 'wod import equipment'.
|
|
2954
|
-
|
|
2955
|
-
|
|
3150
|
+
download \u2026 Accepted alias for 'wod export'.
|
|
3151
|
+
import note <file> Re-import exported note markdown (alias: import playground;
|
|
3152
|
+
same as parse with the stamped identity).
|
|
2956
3153
|
import session <file>
|
|
2957
3154
|
Import a session + its events from a downloaded CSV.
|
|
2958
3155
|
The exact saved block revision must already exist.
|
|
@@ -2960,6 +3157,10 @@ SUBCOMMANDS:
|
|
|
2960
3157
|
Upsert an effort registry row from corpus-style markdown.
|
|
2961
3158
|
import equipment <file>
|
|
2962
3159
|
Create/upgrade equipment tags (CSV, one label per row).
|
|
3160
|
+
publish [catalog] Publish catalog notes from the database into a configured
|
|
3161
|
+
seed directory (manifest.json, chunks/, report.html)
|
|
3162
|
+
compatible with 'wod seed --source <dir>'. Override target
|
|
3163
|
+
with -o/--out/--output (defaults to ./dist/seed/<catalog>).
|
|
2963
3164
|
build [dir] Compile a seed corpus: validate every .md under
|
|
2964
3165
|
<dir>/markdown (parity gate + dashboard format gate) and
|
|
2965
3166
|
write manifest.json + content-hashed chunks/ +
|
|
@@ -3008,9 +3209,9 @@ DATABASE (init / parse / run / query / download / import / seed / reset / server
|
|
|
3008
3209
|
an error. Without it: ./.env when present, otherwise
|
|
3009
3210
|
the workspace-root .env. Credentials are never logged.
|
|
3010
3211
|
|
|
3011
|
-
HOSTING (for 'server' / 'playground';
|
|
3212
|
+
HOSTING (for 'server' / 'playground'; supported on Node and Bun):
|
|
3012
3213
|
server HTTP API over the configured database plus the frontend
|
|
3013
|
-
packaged inside the
|
|
3214
|
+
packaged inside the tools module. Requires an
|
|
3014
3215
|
initialized database. ('serve' is an accepted alias.)
|
|
3015
3216
|
playground Serves the playground app backed by browser IndexedDB;
|
|
3016
3217
|
the configured database is not touched.
|
|
@@ -3270,21 +3471,22 @@ async function cliMain(argv = process.argv.slice(2), io = {}) {
|
|
|
3270
3471
|
writeDebugFile("query", ir);
|
|
3271
3472
|
return 0;
|
|
3272
3473
|
}
|
|
3273
|
-
if (parsed.subcommand === "download") {
|
|
3474
|
+
if (parsed.subcommand === "download" || parsed.subcommand === "export") {
|
|
3475
|
+
const verb = parsed.subcommand;
|
|
3274
3476
|
const kind = parsed.target;
|
|
3275
3477
|
const id = parsed.target2;
|
|
3276
|
-
const DOWNLOAD_KINDS = ["note", "session", "effort", "equipment"];
|
|
3478
|
+
const DOWNLOAD_KINDS = ["note", "playground", "session", "effort", "equipment"];
|
|
3277
3479
|
if (!id && kind !== "equipment" || !DOWNLOAD_KINDS.includes(kind ?? "")) {
|
|
3278
|
-
writeErr(
|
|
3480
|
+
writeErr(`[wod ${verb}] Usage: wod ${verb} <note|playground|session|effort|equipment> <id> [-o <path>]`);
|
|
3279
3481
|
return 1;
|
|
3280
3482
|
}
|
|
3281
3483
|
const targetId = id;
|
|
3282
3484
|
const db = await openCliDb(cliDb());
|
|
3283
3485
|
try {
|
|
3284
|
-
if (kind === "note") {
|
|
3486
|
+
if (kind === "note" || kind === "playground") {
|
|
3285
3487
|
const bundle = await getNoteBundle(db.db, targetId);
|
|
3286
3488
|
if (!bundle.note) {
|
|
3287
|
-
writeErr(`[wod
|
|
3489
|
+
writeErr(`[wod ${verb}] Note not found in ${db.path}: ${id}`);
|
|
3288
3490
|
return 1;
|
|
3289
3491
|
}
|
|
3290
3492
|
const markdown = wodWikiEngine.composeNoteMarkdown(bundle.note);
|
|
@@ -3294,11 +3496,11 @@ async function cliMain(argv = process.argv.slice(2), io = {}) {
|
|
|
3294
3496
|
} else {
|
|
3295
3497
|
nodeFs().writeFileSync(target, markdown, "utf-8");
|
|
3296
3498
|
}
|
|
3297
|
-
if (parsed.output !== "-") writeOut(`[wod
|
|
3499
|
+
if (parsed.output !== "-") writeOut(`[wod ${verb}] ${kind} ${id} \u2192 ${target}`);
|
|
3298
3500
|
} else if (kind === "session") {
|
|
3299
3501
|
const bundle = await getSessionBundle(db.db, targetId);
|
|
3300
3502
|
if (!bundle) {
|
|
3301
|
-
writeErr(`[wod
|
|
3503
|
+
writeErr(`[wod ${verb}] Session not found in ${db.path}: ${id}`);
|
|
3302
3504
|
return 1;
|
|
3303
3505
|
}
|
|
3304
3506
|
const csv = wodWikiEngine.sessionToCsv(bundle.session, bundle.events);
|
|
@@ -3308,11 +3510,11 @@ async function cliMain(argv = process.argv.slice(2), io = {}) {
|
|
|
3308
3510
|
} else {
|
|
3309
3511
|
nodeFs().writeFileSync(target, csv, "utf-8");
|
|
3310
3512
|
}
|
|
3311
|
-
if (parsed.output !== "-") writeOut(`[wod
|
|
3513
|
+
if (parsed.output !== "-") writeOut(`[wod ${verb}] session ${id} \u2192 ${target} (${bundle.events.length} events)`);
|
|
3312
3514
|
} else if (kind === "effort") {
|
|
3313
3515
|
const effort = await getEffortBySlug(db.db, targetId);
|
|
3314
3516
|
if (!effort) {
|
|
3315
|
-
writeErr(`[wod
|
|
3517
|
+
writeErr(`[wod ${verb}] Effort not found in ${db.path}: ${id}`);
|
|
3316
3518
|
return 1;
|
|
3317
3519
|
}
|
|
3318
3520
|
const markdown = wodWikiEngine.effortToMarkdown(effort);
|
|
@@ -3322,7 +3524,7 @@ async function cliMain(argv = process.argv.slice(2), io = {}) {
|
|
|
3322
3524
|
} else {
|
|
3323
3525
|
nodeFs().writeFileSync(target, markdown, "utf-8");
|
|
3324
3526
|
}
|
|
3325
|
-
writeOut(`[wod
|
|
3527
|
+
writeOut(`[wod ${verb}] effort ${id} \u2192 ${target}`);
|
|
3326
3528
|
} else {
|
|
3327
3529
|
const labels = (await listTagsByType(db.db, "equipment")).map((t) => t.label).sort();
|
|
3328
3530
|
const csv = wodWikiEngine.equipmentToCsv(labels);
|
|
@@ -3332,7 +3534,7 @@ async function cliMain(argv = process.argv.slice(2), io = {}) {
|
|
|
3332
3534
|
} else {
|
|
3333
3535
|
nodeFs().writeFileSync(target, csv, "utf-8");
|
|
3334
3536
|
}
|
|
3335
|
-
writeOut(`[wod
|
|
3537
|
+
writeOut(`[wod ${verb}] equipment \u2192 ${target} (${labels.length} labels)`);
|
|
3336
3538
|
}
|
|
3337
3539
|
} finally {
|
|
3338
3540
|
await db.close();
|
|
@@ -3342,9 +3544,9 @@ async function cliMain(argv = process.argv.slice(2), io = {}) {
|
|
|
3342
3544
|
if (parsed.subcommand === "import") {
|
|
3343
3545
|
const kind = parsed.target;
|
|
3344
3546
|
const file = parsed.target2;
|
|
3345
|
-
const IMPORT_KINDS = ["note", "session", "effort", "equipment"];
|
|
3547
|
+
const IMPORT_KINDS = ["note", "playground", "session", "effort", "equipment"];
|
|
3346
3548
|
if (!file || !IMPORT_KINDS.includes(kind ?? "")) {
|
|
3347
|
-
writeErr("[wod import] Usage: wod import <note|session|effort|equipment> <file>");
|
|
3549
|
+
writeErr("[wod import] Usage: wod import <note|playground|session|effort|equipment> <file>");
|
|
3348
3550
|
return 1;
|
|
3349
3551
|
}
|
|
3350
3552
|
const fs = nodeFs();
|
|
@@ -3352,7 +3554,7 @@ async function cliMain(argv = process.argv.slice(2), io = {}) {
|
|
|
3352
3554
|
writeErr(`[wod import] File not found: ${file}`);
|
|
3353
3555
|
return 1;
|
|
3354
3556
|
}
|
|
3355
|
-
if (kind === "note") {
|
|
3557
|
+
if (kind === "note" || kind === "playground") {
|
|
3356
3558
|
const source = fs.readFileSync(file, "utf-8");
|
|
3357
3559
|
const identity = wodWikiEngine.extractNoteIdentity(source);
|
|
3358
3560
|
const tags = wodWikiEngine.extractTypedTags(source);
|
|
@@ -3366,7 +3568,7 @@ async function cliMain(argv = process.argv.slice(2), io = {}) {
|
|
|
3366
3568
|
tags
|
|
3367
3569
|
});
|
|
3368
3570
|
writeOut(
|
|
3369
|
-
`[wod import]
|
|
3571
|
+
`[wod import] ${kind} ${persisted.noteId}${persisted.created ? "" : " (updated)"} \u2014 ${persisted.blocks} blocks${persisted.tags > 0 ? `, ${persisted.tags} tags` : ""} \u2192 ${db2.path}`
|
|
3370
3572
|
);
|
|
3371
3573
|
} finally {
|
|
3372
3574
|
await db2.close();
|
|
@@ -3415,6 +3617,26 @@ async function cliMain(argv = process.argv.slice(2), io = {}) {
|
|
|
3415
3617
|
}
|
|
3416
3618
|
return 0;
|
|
3417
3619
|
}
|
|
3620
|
+
if (parsed.subcommand === "publish") {
|
|
3621
|
+
const isDir = (s) => Boolean(s && (s.startsWith(".") || s.startsWith("/") || s.includes("/") || s.includes("\\")));
|
|
3622
|
+
const catalog = isDir(parsed.target) ? void 0 : parsed.target;
|
|
3623
|
+
const targetDir = isDir(parsed.target) ? parsed.target : parsed.target2;
|
|
3624
|
+
const outDir = parsed.output ?? targetDir ?? (catalog ? `./dist/seed/${catalog}` : "./dist/seed");
|
|
3625
|
+
const db = await openCliDb(cliDb());
|
|
3626
|
+
try {
|
|
3627
|
+
const result = await publishCatalogSeed(db, {
|
|
3628
|
+
catalog,
|
|
3629
|
+
outDir,
|
|
3630
|
+
version: parsed.seedVersion
|
|
3631
|
+
});
|
|
3632
|
+
writeOut(
|
|
3633
|
+
`[wod publish] published ${result.catalogs.join(", ")} (${result.totalNotes} notes) \u2192 ${result.outDir} (manifest schema v${result.manifest.schema}, version ${result.manifest.version})`
|
|
3634
|
+
);
|
|
3635
|
+
} finally {
|
|
3636
|
+
await db.close();
|
|
3637
|
+
}
|
|
3638
|
+
return 0;
|
|
3639
|
+
}
|
|
3418
3640
|
if (parsed.subcommand === "build") {
|
|
3419
3641
|
const cwd = process.cwd();
|
|
3420
3642
|
const root = nodePath().resolve(cwd, parsed.dir ?? parsed.target ?? ".");
|
|
@@ -3491,12 +3713,6 @@ async function cliMain(argv = process.argv.slice(2), io = {}) {
|
|
|
3491
3713
|
return 0;
|
|
3492
3714
|
}
|
|
3493
3715
|
if (parsed.subcommand === "server" || parsed.subcommand === "serve" || parsed.subcommand === "playground") {
|
|
3494
|
-
if (!("Bun" in globalThis)) {
|
|
3495
|
-
writeErr(
|
|
3496
|
-
`[wod ${parsed.subcommand}] Error: hosting requires the Bun runtime (https://bun.sh) \u2014 run \`bun wod server\` / \`bun wod playground\`.`
|
|
3497
|
-
);
|
|
3498
|
-
return 1;
|
|
3499
|
-
}
|
|
3500
3716
|
const mode = parsed.subcommand === "playground" ? "playground" : "server";
|
|
3501
3717
|
const { runCliServer: runCliServer2 } = await Promise.resolve().then(() => (init_server(), server_exports));
|
|
3502
3718
|
const options = { env: cliEnv() };
|