@bitcobblers/wod-wiki-engine 0.35.2128 → 0.36.2130
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 +758 -194
- package/dist/bin/wod.cjs.map +1 -1
- package/dist/bin/wod.d.cts +1 -1
- package/dist/bin/wod.d.ts +1 -1
- package/dist/bin/wod.mjs +5 -3
- package/dist/bin/wod.mjs.map +1 -1
- package/dist/chunk-6L5ZDZJ7.mjs +15 -0
- package/dist/chunk-6L5ZDZJ7.mjs.map +1 -0
- package/dist/{chunk-Y6WO6MXS.mjs → chunk-BMHE7DDG.mjs} +3 -9
- package/dist/chunk-BMHE7DDG.mjs.map +1 -0
- package/dist/chunk-EUQWEX6Z.mjs +253 -0
- package/dist/chunk-EUQWEX6Z.mjs.map +1 -0
- package/dist/{chunk-N4KFWY6Z.mjs → chunk-UKBQ36RW.mjs} +377 -193
- package/dist/chunk-UKBQ36RW.mjs.map +1 -0
- package/dist/{compiler-SXUCF7AU.mjs → compiler-NODHKWJJ.mjs} +4 -3
- package/dist/compiler-NODHKWJJ.mjs.map +1 -0
- package/dist/index.cjs +763 -199
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +27 -8
- package/dist/index.d.ts +27 -8
- package/dist/index.mjs +10 -8
- package/dist/index.mjs.map +1 -1
- package/dist/server/app.cjs +299 -0
- package/dist/server/app.cjs.map +1 -0
- package/dist/server/app.d.cts +24 -0
- package/dist/server/app.d.ts +24 -0
- package/dist/server/app.mjs +301 -0
- package/dist/server/app.mjs.map +1 -0
- package/dist/server-4P6AJAGT.mjs +128 -0
- package/dist/server-4P6AJAGT.mjs.map +1 -0
- package/dist/web/playground/ai-skills/parse.md +656 -0
- package/dist/web/playground/assets/main-Bhn9NHgQ.css +2 -0
- package/dist/web/playground/assets/main-CGSJY1vM.js +212 -0
- package/dist/web/playground/assets/rolldown-runtime-C0FnF6B9.js +1 -0
- package/dist/web/playground/assets/statementBuilderFlow-PTgOOzz5.js +2 -0
- package/dist/web/playground/assets/vendor-BlJqgFME.js +201 -0
- package/dist/web/playground/assets/vendor-codemirror-BtJgoBjf.js +40 -0
- package/dist/web/playground/fonts/Inter-Regular.woff2 +0 -0
- package/dist/web/playground/fonts/Inter-SemiBold.woff2 +0 -0
- package/dist/web/playground/images/hero/editing.png +0 -0
- package/dist/web/playground/images/hero/journal.png +0 -0
- package/dist/web/playground/images/hero/results.png +0 -0
- package/dist/web/playground/images/hero/timer.png +0 -0
- package/dist/web/playground/images/wod-wiki-logo-dark.png +0 -0
- package/dist/web/playground/images/wod-wiki-logo-light.png +0 -0
- package/dist/web/playground/index.html +60 -0
- package/dist/web/playground/seed/core/chunks/canvas.e6c77ad0.json +1 -0
- package/dist/web/playground/seed/core/chunks/template.e5e0e82d.json +1 -0
- package/dist/web/playground/seed/core/manifest.json +24 -0
- package/dist/web/playground/seed/core/report.html +43 -0
- package/dist/web/server/ai-skills/parse.md +656 -0
- package/dist/web/server/assets/main-Bhn9NHgQ.css +2 -0
- package/dist/web/server/assets/main-DhN2J5Ax.js +211 -0
- package/dist/web/server/assets/rolldown-runtime-C0FnF6B9.js +1 -0
- package/dist/web/server/assets/statementBuilderFlow-Dj0o2Wpk.js +2 -0
- package/dist/web/server/assets/vendor-BlJqgFME.js +201 -0
- package/dist/web/server/assets/vendor-codemirror-BtJgoBjf.js +40 -0
- package/dist/web/server/fonts/Inter-Regular.woff2 +0 -0
- package/dist/web/server/fonts/Inter-SemiBold.woff2 +0 -0
- package/dist/web/server/images/hero/editing.png +0 -0
- package/dist/web/server/images/hero/journal.png +0 -0
- package/dist/web/server/images/hero/results.png +0 -0
- package/dist/web/server/images/hero/timer.png +0 -0
- package/dist/web/server/images/wod-wiki-logo-dark.png +0 -0
- package/dist/web/server/images/wod-wiki-logo-light.png +0 -0
- package/dist/web/server/index.html +60 -0
- package/dist/web/server/seed/core/chunks/canvas.e6c77ad0.json +1 -0
- package/dist/web/server/seed/core/chunks/template.e5e0e82d.json +1 -0
- package/dist/web/server/seed/core/manifest.json +24 -0
- package/dist/web/server/seed/core/report.html +43 -0
- package/package.json +11 -5
- package/dist/chunk-N4KFWY6Z.mjs.map +0 -1
- package/dist/chunk-Y6WO6MXS.mjs.map +0 -1
- package/dist/compiler-SXUCF7AU.mjs.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -10,6 +10,7 @@ var crypto$1 = require('crypto');
|
|
|
10
10
|
var react = require('@bitcobblers/wod-wiki-lang/react');
|
|
11
11
|
var wodWikiStorage = require('@bitcobblers/wod-wiki-storage');
|
|
12
12
|
|
|
13
|
+
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
|
|
13
14
|
var __defProp = Object.defineProperty;
|
|
14
15
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
15
16
|
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
@@ -25,6 +26,252 @@ var __export = (target, all) => {
|
|
|
25
26
|
for (var name in all)
|
|
26
27
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
27
28
|
};
|
|
29
|
+
function nodeRequire() {
|
|
30
|
+
const g = globalThis;
|
|
31
|
+
if (typeof g.process?.versions?.node === "undefined" && typeof g.Bun === "undefined") {
|
|
32
|
+
throw new Error("SQLite operations are only supported in Node / Bun environments");
|
|
33
|
+
}
|
|
34
|
+
const req = g.require ?? (typeof __require === "function" ? __require : void 0);
|
|
35
|
+
if (!req) throw new Error("SQLite operations require a CJS require shim (missing tsup banner?)");
|
|
36
|
+
return req;
|
|
37
|
+
}
|
|
38
|
+
function loadSql() {
|
|
39
|
+
sqlModulePromise ??= import(
|
|
40
|
+
/* @vite-ignore */
|
|
41
|
+
'@bitcobblers/wod-wiki-storage/sql'
|
|
42
|
+
);
|
|
43
|
+
return sqlModulePromise;
|
|
44
|
+
}
|
|
45
|
+
function resolveDbPath(dbPath) {
|
|
46
|
+
const chosen = dbPath ?? process.env.WOD_WIKI_DB ?? "./wodwiki.db";
|
|
47
|
+
return nodePath().resolve(process.cwd(), chosen);
|
|
48
|
+
}
|
|
49
|
+
async function openCliDb(target, opts) {
|
|
50
|
+
if (target === void 0 || typeof target === "string") {
|
|
51
|
+
return openSqliteDb(resolveDbPath(target), opts);
|
|
52
|
+
}
|
|
53
|
+
switch (target.kind) {
|
|
54
|
+
case "sqlite":
|
|
55
|
+
return openSqliteDb(target.path, opts);
|
|
56
|
+
case "turso":
|
|
57
|
+
return openRemoteDb(
|
|
58
|
+
{ DB_DRIVER: "turso", DATABASE_URL: target.url, LIBSQL_AUTH_TOKEN: target.authToken },
|
|
59
|
+
target.label,
|
|
60
|
+
opts
|
|
61
|
+
);
|
|
62
|
+
case "postgres":
|
|
63
|
+
return openRemoteDb({ DB_DRIVER: "postgres", DATABASE_URL: target.url }, target.label, opts);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
async function openSqliteDb(resolved, opts) {
|
|
67
|
+
const [{ createClient }, sql] = await Promise.all([
|
|
68
|
+
import(
|
|
69
|
+
/* @vite-ignore */
|
|
70
|
+
'@libsql/client'
|
|
71
|
+
),
|
|
72
|
+
loadSql()
|
|
73
|
+
]);
|
|
74
|
+
const initialize = opts?.initialize ?? true;
|
|
75
|
+
if (!initialize && !nodeFs().existsSync(resolved)) {
|
|
76
|
+
throw new Error(`[wod] no database at ${resolved} \u2014 run \`wod init\` to create and initialize it`);
|
|
77
|
+
}
|
|
78
|
+
if (initialize) nodeFs().mkdirSync(nodePath().dirname(resolved), { recursive: true });
|
|
79
|
+
const db = sql.libsqlDatabase(createClient({ url: `file:${resolved}` }));
|
|
80
|
+
try {
|
|
81
|
+
if (initialize) {
|
|
82
|
+
await sql.initializeStore(db);
|
|
83
|
+
await sql.initializeDomain(db);
|
|
84
|
+
} else {
|
|
85
|
+
await sql.verifyReadiness(db).catch((error) => {
|
|
86
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
87
|
+
throw new Error(`${message} \u2014 run \`wod init\` to initialize it`, { cause: error });
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
return { db, storage: new sql.SqlStorage(db), path: resolved, close: () => db.close() };
|
|
91
|
+
} catch (error) {
|
|
92
|
+
await db.close();
|
|
93
|
+
throw error;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
async function openRemoteDb(env, label, opts) {
|
|
97
|
+
const [{ openDatabase }, sql] = await Promise.all([
|
|
98
|
+
import(
|
|
99
|
+
/* @vite-ignore */
|
|
100
|
+
'@bitcobblers/wod-wiki-storage/sql/node'
|
|
101
|
+
),
|
|
102
|
+
loadSql()
|
|
103
|
+
]);
|
|
104
|
+
let db;
|
|
105
|
+
try {
|
|
106
|
+
db = await openDatabase(env, { create: opts?.create });
|
|
107
|
+
if (opts?.initialize === false) {
|
|
108
|
+
await sql.verifyReadiness(db);
|
|
109
|
+
} else {
|
|
110
|
+
await sql.initializeStore(db);
|
|
111
|
+
await sql.initializeDomain(db);
|
|
112
|
+
}
|
|
113
|
+
} catch (error) {
|
|
114
|
+
await db?.close().catch(() => {
|
|
115
|
+
});
|
|
116
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
117
|
+
throw new Error(
|
|
118
|
+
`[wod] ${label}: ${message}${opts?.initialize === false ? " \u2014 run `wod init` to initialize it" : ""}`,
|
|
119
|
+
{ cause: error }
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
return { db, storage: new sql.SqlStorage(db), path: label, close: () => db.close() };
|
|
123
|
+
}
|
|
124
|
+
function formatV5Digest(digest) {
|
|
125
|
+
const bytes = digest.slice(0, 16);
|
|
126
|
+
bytes[6] = bytes[6] & 15 | 80;
|
|
127
|
+
bytes[8] = bytes[8] & 63 | 128;
|
|
128
|
+
const hex = [...bytes].map((b) => b.toString(16).padStart(2, "0"));
|
|
129
|
+
return `${hex.slice(0, 4).join("")}-${hex.slice(4, 6).join("")}-${hex.slice(6, 8).join("")}-${hex.slice(8, 10).join("")}-${hex.slice(10, 16).join("")}`;
|
|
130
|
+
}
|
|
131
|
+
function uuid5(namespace, name) {
|
|
132
|
+
return formatV5Digest(nodeCrypto().createHash("sha1").update(namespace).update(name).digest());
|
|
133
|
+
}
|
|
134
|
+
function stableNoteId(sourceLabel, content) {
|
|
135
|
+
if (sourceLabel && sourceLabel !== "-") {
|
|
136
|
+
return uuid5(CLI_UUID_NAMESPACE, `wod-cli:${nodePath().resolve(process.cwd(), sourceLabel)}`);
|
|
137
|
+
}
|
|
138
|
+
return uuid5(CLI_UUID_NAMESPACE, `wod-cli:stdin:${content.length}:${content.slice(0, 256)}`);
|
|
139
|
+
}
|
|
140
|
+
function randomId() {
|
|
141
|
+
return nodeCrypto().randomUUID();
|
|
142
|
+
}
|
|
143
|
+
async function allRows(sql, db, store) {
|
|
144
|
+
return sql.listRows(db, sql.storeDefinition(store));
|
|
145
|
+
}
|
|
146
|
+
async function rowsByIndex(sql, db, store, index, only) {
|
|
147
|
+
return sql.listRows(db, sql.storeDefinition(store), { only }, void 0, index);
|
|
148
|
+
}
|
|
149
|
+
function sqliteEventStore(db) {
|
|
150
|
+
const all = async () => await allRows(await loadSql(), db, "events");
|
|
151
|
+
return {
|
|
152
|
+
getEventsByTimeRange: async (start, end) => (await all()).filter((r) => r.timestamp >= start && r.timestamp <= end),
|
|
153
|
+
getEventsByResult: async (resultId) => (await all()).filter((r) => r.resultId === resultId),
|
|
154
|
+
getEventsForNote: async (noteId) => (await all()).filter((r) => r.noteId === noteId),
|
|
155
|
+
getEventsByContent: async (blockContentId5) => (await all()).filter((r) => r.blockContentId === blockContentId5),
|
|
156
|
+
scanAll: all,
|
|
157
|
+
appendEvents: async (appended) => {
|
|
158
|
+
const sql = await loadSql();
|
|
159
|
+
await db.transaction(async (tx) => {
|
|
160
|
+
const eventsDef = sql.storeDefinition("events");
|
|
161
|
+
for (const row of appended) await sql.putRow(tx, eventsDef, row);
|
|
162
|
+
});
|
|
163
|
+
},
|
|
164
|
+
finalizeSummaries: async (resultId, finals) => {
|
|
165
|
+
const sql = await loadSql();
|
|
166
|
+
await db.transaction(async (tx) => {
|
|
167
|
+
const eventsDef = sql.storeDefinition("events");
|
|
168
|
+
const existing = (await sql.listRows(tx, eventsDef)).filter(
|
|
169
|
+
(r) => r.resultId === resultId && r.grain === "summary" && r.origin !== "user"
|
|
170
|
+
);
|
|
171
|
+
for (const row of existing) await sql.deleteRows(tx, eventsDef, row.id);
|
|
172
|
+
for (const row of finals) await sql.putRow(tx, eventsDef, row);
|
|
173
|
+
});
|
|
174
|
+
},
|
|
175
|
+
deleteEvents: async (ids) => {
|
|
176
|
+
const sql = await loadSql();
|
|
177
|
+
await db.transaction(async (tx) => {
|
|
178
|
+
const eventsDef = sql.storeDefinition("events");
|
|
179
|
+
for (const id of ids) await sql.deleteRows(tx, eventsDef, id);
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
function sqliteNoteStore(db) {
|
|
185
|
+
return {
|
|
186
|
+
getAllNotes: async () => await allRows(await loadSql(), db, "notes"),
|
|
187
|
+
getNotesByIds: async (noteIds) => {
|
|
188
|
+
const sql = await loadSql();
|
|
189
|
+
const rows = await sql.getRows(db, sql.storeDefinition("notes"), [...noteIds]);
|
|
190
|
+
return rows.filter((row) => row !== void 0);
|
|
191
|
+
},
|
|
192
|
+
getNoteIdsForTag: async (label) => {
|
|
193
|
+
const sql = await loadSql();
|
|
194
|
+
const tags = (await allRows(sql, db, "tags")).filter((t) => t.label === label);
|
|
195
|
+
const ids = {};
|
|
196
|
+
for (const tag of tags) {
|
|
197
|
+
for (const row of await rowsByIndex(sql, db, "note_tags", "by-tag", tag.id)) {
|
|
198
|
+
ids[row.noteId] = true;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return new Set(Object.keys(ids));
|
|
202
|
+
},
|
|
203
|
+
getNoteTagLabels: async (noteId) => {
|
|
204
|
+
const sql = await loadSql();
|
|
205
|
+
const tagRows = await allRows(sql, db, "tags");
|
|
206
|
+
const labelById = {};
|
|
207
|
+
for (const t of tagRows) labelById[t.id] = t.label;
|
|
208
|
+
const links = await rowsByIndex(sql, db, "note_tags", "by-note", noteId);
|
|
209
|
+
return links.map((l) => labelById[l.tagId]).filter((l) => typeof l === "string");
|
|
210
|
+
},
|
|
211
|
+
queryDomain: async (request) => {
|
|
212
|
+
const result = await (await loadSql()).handleDomainQuery(db, request);
|
|
213
|
+
return result;
|
|
214
|
+
}
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
function sqliteBlockStore(db) {
|
|
218
|
+
return {
|
|
219
|
+
getAllBlocks: async () => await allRows(await loadSql(), db, "block_index"),
|
|
220
|
+
queryDomain: async (request) => {
|
|
221
|
+
const result = await (await loadSql()).handleDomainQuery(db, { ...request, plan: "blocks" });
|
|
222
|
+
return result;
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
function sqliteEffortStore(db) {
|
|
227
|
+
return {
|
|
228
|
+
getAllEfforts: async () => {
|
|
229
|
+
const stored = await allRows(await loadSql(), db, "efforts");
|
|
230
|
+
const bySlug = {};
|
|
231
|
+
for (const effort of stored) bySlug[effort.slug] = effort;
|
|
232
|
+
for (const effort of wodWikiLang.bundledEfforts) {
|
|
233
|
+
bySlug[effort.slug] ??= effort;
|
|
234
|
+
}
|
|
235
|
+
return Object.values(bySlug);
|
|
236
|
+
}
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
function sqliteIndexStore(db, store) {
|
|
240
|
+
return {
|
|
241
|
+
getAllFromIndex: async (index, key) => rowsByIndex(await loadSql(), db, store, index, key)
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
async function getNoteBundle(db, noteId) {
|
|
245
|
+
const sql = await loadSql();
|
|
246
|
+
const note = await sql.getRow(db, sql.storeDefinition("notes"), noteId);
|
|
247
|
+
return { note };
|
|
248
|
+
}
|
|
249
|
+
async function getSessionBundle(db, resultId) {
|
|
250
|
+
const sql = await loadSql();
|
|
251
|
+
const session = await sql.getRow(db, sql.storeDefinition("sessions"), resultId);
|
|
252
|
+
if (!session) return void 0;
|
|
253
|
+
const events = await sql.listRows(db, sql.storeDefinition("events"), { only: resultId }, void 0, "by-result-grain");
|
|
254
|
+
return { session, events };
|
|
255
|
+
}
|
|
256
|
+
async function getEffortBySlug(db, slug) {
|
|
257
|
+
const sql = await loadSql();
|
|
258
|
+
const efforts = await allRows(sql, db, "efforts");
|
|
259
|
+
return efforts.find((e) => e.slug === slug);
|
|
260
|
+
}
|
|
261
|
+
async function listTagsByType(db, type) {
|
|
262
|
+
const sql = await loadSql();
|
|
263
|
+
const tags = await allRows(sql, db, "tags");
|
|
264
|
+
return tags.filter((t) => t.type === type);
|
|
265
|
+
}
|
|
266
|
+
var nodePath, nodeFs, nodeCrypto, sqlModulePromise, CLI_UUID_NAMESPACE;
|
|
267
|
+
var init_db = __esm({
|
|
268
|
+
"src/cli/db.ts"() {
|
|
269
|
+
nodePath = () => nodeRequire()("node:path");
|
|
270
|
+
nodeFs = () => nodeRequire()("node:fs");
|
|
271
|
+
nodeCrypto = () => nodeRequire()("node:crypto");
|
|
272
|
+
CLI_UUID_NAMESPACE = "c0ffee00-8b1a-4e2c-9d3f-5a6b7c8d9e0f";
|
|
273
|
+
}
|
|
274
|
+
});
|
|
28
275
|
|
|
29
276
|
// src/cli/seedbuild/frontmatter.ts
|
|
30
277
|
function unquote(value) {
|
|
@@ -806,6 +1053,132 @@ var init_compiler = __esm({
|
|
|
806
1053
|
};
|
|
807
1054
|
}
|
|
808
1055
|
});
|
|
1056
|
+
|
|
1057
|
+
// src/cli/server.ts
|
|
1058
|
+
var server_exports = {};
|
|
1059
|
+
__export(server_exports, {
|
|
1060
|
+
runCliServer: () => runCliServer
|
|
1061
|
+
});
|
|
1062
|
+
function bun() {
|
|
1063
|
+
const b = globalThis.Bun;
|
|
1064
|
+
if (!b) {
|
|
1065
|
+
throw new Error("wod server/playground requires the Bun runtime \u2014 start with: bun wod server");
|
|
1066
|
+
}
|
|
1067
|
+
return b;
|
|
1068
|
+
}
|
|
1069
|
+
function moduleDir() {
|
|
1070
|
+
const p = nodePath();
|
|
1071
|
+
const meta = ({ url: (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)) });
|
|
1072
|
+
if (typeof meta.dir === "string") return p.resolve(meta.dir);
|
|
1073
|
+
if (typeof meta.url === "string" && meta.url.startsWith("file:")) {
|
|
1074
|
+
return p.resolve(p.dirname(decodeURIComponent(new URL(".", meta.url).pathname)));
|
|
1075
|
+
}
|
|
1076
|
+
throw new Error("wod server/playground: cannot determine the installed engine module directory");
|
|
1077
|
+
}
|
|
1078
|
+
function engineRoot() {
|
|
1079
|
+
const p = nodePath();
|
|
1080
|
+
const fs = nodeFs();
|
|
1081
|
+
for (let dir = moduleDir(); ; dir = p.dirname(dir)) {
|
|
1082
|
+
try {
|
|
1083
|
+
if (JSON.parse(fs.readFileSync(p.resolve(dir, "package.json"), "utf8")).name === "@bitcobblers/wod-wiki-engine") return dir;
|
|
1084
|
+
} catch {
|
|
1085
|
+
}
|
|
1086
|
+
const parent = p.dirname(dir);
|
|
1087
|
+
if (parent === dir) break;
|
|
1088
|
+
}
|
|
1089
|
+
throw new Error("wod server/playground: cannot locate the installed @bitcobblers/wod-wiki-engine package");
|
|
1090
|
+
}
|
|
1091
|
+
function resolveDist(mode, options) {
|
|
1092
|
+
const p = nodePath();
|
|
1093
|
+
const explicit = options.dist?.trim() || options.env.PLAYGROUND_DIST?.trim() || void 0;
|
|
1094
|
+
if (explicit) {
|
|
1095
|
+
const dist2 = p.resolve(explicit);
|
|
1096
|
+
if (!nodeFs().existsSync(p.resolve(dist2, "index.html"))) {
|
|
1097
|
+
throw new Error(
|
|
1098
|
+
`wod ${mode}: no built frontend (index.html) at ${dist2} \u2014 --dist/PLAYGROUND_DIST must point at an already-built directory (the CLI never builds frontends at runtime)`
|
|
1099
|
+
);
|
|
1100
|
+
}
|
|
1101
|
+
return dist2;
|
|
1102
|
+
}
|
|
1103
|
+
const dist = p.resolve(engineRoot(), "dist", "web", mode === "playground" ? "playground" : "server");
|
|
1104
|
+
if (!nodeFs().existsSync(p.resolve(dist, "index.html"))) {
|
|
1105
|
+
throw new Error(
|
|
1106
|
+
`wod ${mode}: packaged frontend missing \u2014 expected ${dist}/index.html. Reinstall @bitcobblers/wod-wiki-engine (bun add @bitcobblers/wod-wiki-engine), or rebuild the release assets from a checkout (bun run build:cli) and reinstall the package.`
|
|
1107
|
+
);
|
|
1108
|
+
}
|
|
1109
|
+
return dist;
|
|
1110
|
+
}
|
|
1111
|
+
function resolveBind(mode, options) {
|
|
1112
|
+
const host = options.host?.trim() || options.env.HOST?.trim() || "127.0.0.1";
|
|
1113
|
+
const rawPort = options.port?.trim() || options.env.PORT?.trim() || "3000";
|
|
1114
|
+
const port = Number(rawPort);
|
|
1115
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535) {
|
|
1116
|
+
throw new Error(`wod ${mode}: invalid port ${JSON.stringify(rawPort)} (need integer 1..65535)`);
|
|
1117
|
+
}
|
|
1118
|
+
return { host, port };
|
|
1119
|
+
}
|
|
1120
|
+
async function runCliServer(mode, options, writeOut) {
|
|
1121
|
+
const runtime = bun();
|
|
1122
|
+
const { host, port } = resolveBind(mode, options);
|
|
1123
|
+
let db;
|
|
1124
|
+
let dbLabel = "no database";
|
|
1125
|
+
if (mode === "server") {
|
|
1126
|
+
const database = options.database;
|
|
1127
|
+
if (!database) {
|
|
1128
|
+
throw new Error("wod server: no database configuration resolved \u2014 this is a runner bug (expected resolveDatabaseConfig output)");
|
|
1129
|
+
}
|
|
1130
|
+
try {
|
|
1131
|
+
db = await openCliDb(database, { initialize: false });
|
|
1132
|
+
} catch (e) {
|
|
1133
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
1134
|
+
throw new Error(`wod server: database not ready \u2014 run \`wod init\` first. ${message}`, { cause: e });
|
|
1135
|
+
}
|
|
1136
|
+
dbLabel = db.path;
|
|
1137
|
+
}
|
|
1138
|
+
let server;
|
|
1139
|
+
try {
|
|
1140
|
+
const dist = resolveDist(mode, options);
|
|
1141
|
+
const handlerModule = "@bitcobblers/wod-wiki-engine/server";
|
|
1142
|
+
const { createHandler } = await import(
|
|
1143
|
+
/* @vite-ignore */
|
|
1144
|
+
handlerModule
|
|
1145
|
+
);
|
|
1146
|
+
const handler = createHandler(db?.db ?? null, { playgroundDist: dist });
|
|
1147
|
+
server = runtime.serve({ port, hostname: host, fetch: handler });
|
|
1148
|
+
if (mode === "server") {
|
|
1149
|
+
writeOut(`wod server: http://${host}:${port} \u2014 ${dbLabel} \xB7 dist ${dist}`);
|
|
1150
|
+
} else {
|
|
1151
|
+
writeOut(`wod playground: http://${host}:${port} \u2014 in-browser storage (no database) \xB7 dist ${dist}`);
|
|
1152
|
+
}
|
|
1153
|
+
} catch (e) {
|
|
1154
|
+
if (db) await db.close();
|
|
1155
|
+
throw e;
|
|
1156
|
+
}
|
|
1157
|
+
let closing = false;
|
|
1158
|
+
const shutdown = async () => {
|
|
1159
|
+
if (closing) process.exit(0);
|
|
1160
|
+
closing = true;
|
|
1161
|
+
try {
|
|
1162
|
+
await server.stop();
|
|
1163
|
+
} finally {
|
|
1164
|
+
if (db) await db.close();
|
|
1165
|
+
process.exit(0);
|
|
1166
|
+
}
|
|
1167
|
+
};
|
|
1168
|
+
process.on("SIGINT", () => {
|
|
1169
|
+
void shutdown();
|
|
1170
|
+
});
|
|
1171
|
+
process.on("SIGTERM", () => {
|
|
1172
|
+
void shutdown();
|
|
1173
|
+
});
|
|
1174
|
+
await new Promise(() => {
|
|
1175
|
+
});
|
|
1176
|
+
}
|
|
1177
|
+
var init_server = __esm({
|
|
1178
|
+
"src/cli/server.ts"() {
|
|
1179
|
+
init_db();
|
|
1180
|
+
}
|
|
1181
|
+
});
|
|
809
1182
|
var activePacks = /* @__PURE__ */ new Map();
|
|
810
1183
|
function defineLanguagePack(pack) {
|
|
811
1184
|
return pack;
|
|
@@ -1158,7 +1531,7 @@ function loadQueryData(options) {
|
|
|
1158
1531
|
} else if (obj.facts || obj.results || obj.notes || obj.blocks || obj.efforts || obj.tags || obj.logs) {
|
|
1159
1532
|
const corpus = payload;
|
|
1160
1533
|
if (corpus.facts) data.facts = corpus.facts;
|
|
1161
|
-
if (corpus.results) data.results = corpus.results;
|
|
1534
|
+
if (Array.isArray(corpus.results)) data.results = corpus.results;
|
|
1162
1535
|
if (corpus.notes) data.notes = corpus.notes;
|
|
1163
1536
|
if (corpus.blocks) data.blocks = corpus.blocks;
|
|
1164
1537
|
if (corpus.efforts) data.efforts = corpus.efforts;
|
|
@@ -1333,193 +1706,256 @@ async function loadLanguagePack(moduleSpec) {
|
|
|
1333
1706
|
throw new PackLoadError(moduleSpec, error);
|
|
1334
1707
|
}
|
|
1335
1708
|
}
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1709
|
+
|
|
1710
|
+
// src/cli/runner.ts
|
|
1711
|
+
init_db();
|
|
1712
|
+
|
|
1713
|
+
// src/cli/config.ts
|
|
1714
|
+
function nodeRequire2() {
|
|
1340
1715
|
const g = globalThis;
|
|
1341
1716
|
if (typeof g.process?.versions?.node === "undefined" && typeof g.Bun === "undefined") {
|
|
1342
|
-
throw new Error("
|
|
1717
|
+
throw new Error("database configuration is only supported in Node / Bun environments");
|
|
1343
1718
|
}
|
|
1344
1719
|
const req = g.require ?? (typeof __require === "function" ? __require : void 0);
|
|
1345
|
-
if (!req) throw new Error("
|
|
1720
|
+
if (!req) throw new Error("database configuration requires a CJS require shim (missing tsup banner?)");
|
|
1346
1721
|
return req;
|
|
1347
1722
|
}
|
|
1348
|
-
var
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
}
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1723
|
+
var nodePath2 = () => nodeRequire2()("node:path");
|
|
1724
|
+
var nodeFs2 = () => nodeRequire2()("node:fs");
|
|
1725
|
+
function parseEnvFile(content) {
|
|
1726
|
+
return nodeRequire2()("node:util").parseEnv(content);
|
|
1727
|
+
}
|
|
1728
|
+
function loadCliEnvironment(envFile) {
|
|
1729
|
+
const fs = nodeFs2();
|
|
1730
|
+
const path = nodePath2();
|
|
1731
|
+
let file;
|
|
1732
|
+
if (envFile !== void 0) {
|
|
1733
|
+
file = path.resolve(process.cwd(), envFile);
|
|
1734
|
+
if (!fs.existsSync(file)) throw new Error(`[wod] --env-file: file not found: ${envFile}`);
|
|
1735
|
+
} else {
|
|
1736
|
+
let dir = process.cwd();
|
|
1737
|
+
for (; ; ) {
|
|
1738
|
+
const candidate = path.join(dir, ".env");
|
|
1739
|
+
if (fs.existsSync(candidate)) {
|
|
1740
|
+
file = candidate;
|
|
1741
|
+
break;
|
|
1742
|
+
}
|
|
1743
|
+
const parent = path.dirname(dir);
|
|
1744
|
+
if (parent === dir) break;
|
|
1745
|
+
dir = parent;
|
|
1746
|
+
}
|
|
1747
|
+
}
|
|
1748
|
+
return file !== void 0 ? parseEnvFile(fs.readFileSync(file, "utf-8")) : {};
|
|
1749
|
+
}
|
|
1750
|
+
var SQLITE_URL_SCHEMES = /^(libsql|https?|wss?|file):\/\//;
|
|
1751
|
+
var PG_URL_SCHEMES = /^postgres(ql)?:\/\//;
|
|
1752
|
+
var TURSO_URL_SCHEMES = /^(libsql|https?|wss?):\/\//;
|
|
1753
|
+
var PG_FLAG_KEYS = [
|
|
1754
|
+
"postgresHost",
|
|
1755
|
+
"postgresPort",
|
|
1756
|
+
"postgresUser",
|
|
1757
|
+
"postgresPassword",
|
|
1758
|
+
"postgresDatabase",
|
|
1759
|
+
"postgresSsl"
|
|
1760
|
+
];
|
|
1761
|
+
var PG_ENV_KEYS = [
|
|
1762
|
+
"POSTGRES_HOST",
|
|
1763
|
+
"POSTGRES_PORT",
|
|
1764
|
+
"POSTGRES_USER",
|
|
1765
|
+
"POSTGRES_PASSWORD",
|
|
1766
|
+
"POSTGRES_DATABASE",
|
|
1767
|
+
"POSTGRES_SSL"
|
|
1768
|
+
];
|
|
1769
|
+
var isBackend = (v) => v === "sqlite" || v === "postgres" || v === "turso";
|
|
1770
|
+
function setEnv(v) {
|
|
1771
|
+
return v !== void 0 && v.trim() !== "" ? v : void 0;
|
|
1772
|
+
}
|
|
1773
|
+
function fail(message) {
|
|
1774
|
+
throw new Error(`[wod] ${message}`);
|
|
1775
|
+
}
|
|
1776
|
+
function parsePort(v, source) {
|
|
1777
|
+
if (!/^\d+$/.test(v)) fail(`${source} must be an integer 1..65535, got ${JSON.stringify(v)}`);
|
|
1778
|
+
const port = Number(v);
|
|
1779
|
+
if (port < 1 || port > 65535) fail(`${source} must be an integer 1..65535, got ${v}`);
|
|
1780
|
+
return port;
|
|
1781
|
+
}
|
|
1782
|
+
function sslMode(v, source) {
|
|
1783
|
+
if (v === void 0 || v === "") return "prefer";
|
|
1784
|
+
if (typeof v === "boolean") return v ? "require" : "disable";
|
|
1785
|
+
const s = v.trim().toLowerCase();
|
|
1786
|
+
if (["true", "1", "yes", "require"].includes(s)) return "require";
|
|
1787
|
+
if (["false", "0", "no", "disable", "disabled"].includes(s)) return "disable";
|
|
1788
|
+
if (s === "prefer" || s === "verify-ca" || s === "verify-full") return s;
|
|
1789
|
+
fail(`${source} must be disable|prefer|require|verify-ca|verify-full, got ${JSON.stringify(v)}`);
|
|
1790
|
+
}
|
|
1791
|
+
function safeUrlLabel(raw) {
|
|
1371
1792
|
try {
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
await db.close();
|
|
1377
|
-
throw error;
|
|
1793
|
+
const u = new URL(raw);
|
|
1794
|
+
return `${u.protocol}//${u.host}${u.pathname}`;
|
|
1795
|
+
} catch {
|
|
1796
|
+
return "<unparsable-url>";
|
|
1378
1797
|
}
|
|
1379
1798
|
}
|
|
1380
|
-
function
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
bytes[8] = bytes[8] & 63 | 128;
|
|
1384
|
-
const hex = [...bytes].map((b) => b.toString(16).padStart(2, "0"));
|
|
1385
|
-
return `${hex.slice(0, 4).join("")}-${hex.slice(4, 6).join("")}-${hex.slice(6, 8).join("")}-${hex.slice(8, 10).join("")}-${hex.slice(10, 16).join("")}`;
|
|
1386
|
-
}
|
|
1387
|
-
function uuid5(namespace, name) {
|
|
1388
|
-
return formatV5Digest(nodeCrypto().createHash("sha1").update(namespace).update(name).digest());
|
|
1389
|
-
}
|
|
1390
|
-
var CLI_UUID_NAMESPACE = "c0ffee00-8b1a-4e2c-9d3f-5a6b7c8d9e0f";
|
|
1391
|
-
function stableNoteId(sourceLabel, content) {
|
|
1392
|
-
if (sourceLabel && sourceLabel !== "-") {
|
|
1393
|
-
return uuid5(CLI_UUID_NAMESPACE, `wod-cli:${nodePath().resolve(process.cwd(), sourceLabel)}`);
|
|
1799
|
+
function postgresFromUrl(url, source) {
|
|
1800
|
+
if (!PG_URL_SCHEMES.test(url)) {
|
|
1801
|
+
fail(`${source} must start with postgres:// or postgresql://`);
|
|
1394
1802
|
}
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
}
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
}
|
|
1403
|
-
async function rowsByIndex(sql, db, store, index, only) {
|
|
1404
|
-
return sql.listRows(db, sql.storeDefinition(store), { only }, void 0, index);
|
|
1405
|
-
}
|
|
1406
|
-
function sqliteEventStore(db) {
|
|
1407
|
-
const all = async () => await allRows(await loadSql(), db, "events");
|
|
1408
|
-
return {
|
|
1409
|
-
getEventsByTimeRange: async (start, end) => (await all()).filter((r) => r.timestamp >= start && r.timestamp <= end),
|
|
1410
|
-
getEventsByResult: async (resultId) => (await all()).filter((r) => r.resultId === resultId),
|
|
1411
|
-
getEventsForNote: async (noteId) => (await all()).filter((r) => r.noteId === noteId),
|
|
1412
|
-
getEventsByContent: async (blockContentId5) => (await all()).filter((r) => r.blockContentId === blockContentId5),
|
|
1413
|
-
scanAll: all,
|
|
1414
|
-
appendEvents: async (appended) => {
|
|
1415
|
-
const sql = await loadSql();
|
|
1416
|
-
await db.transaction(async (tx) => {
|
|
1417
|
-
const eventsDef = sql.storeDefinition("events");
|
|
1418
|
-
for (const row of appended) await sql.putRow(tx, eventsDef, row);
|
|
1419
|
-
});
|
|
1420
|
-
},
|
|
1421
|
-
finalizeSummaries: async (resultId, finals) => {
|
|
1422
|
-
const sql = await loadSql();
|
|
1423
|
-
await db.transaction(async (tx) => {
|
|
1424
|
-
const eventsDef = sql.storeDefinition("events");
|
|
1425
|
-
const existing = (await sql.listRows(tx, eventsDef)).filter(
|
|
1426
|
-
(r) => r.resultId === resultId && r.grain === "summary" && r.origin !== "user"
|
|
1427
|
-
);
|
|
1428
|
-
for (const row of existing) await sql.deleteRows(tx, eventsDef, row.id);
|
|
1429
|
-
for (const row of finals) await sql.putRow(tx, eventsDef, row);
|
|
1430
|
-
});
|
|
1431
|
-
},
|
|
1432
|
-
deleteEvents: async (ids) => {
|
|
1433
|
-
const sql = await loadSql();
|
|
1434
|
-
await db.transaction(async (tx) => {
|
|
1435
|
-
const eventsDef = sql.storeDefinition("events");
|
|
1436
|
-
for (const id of ids) await sql.deleteRows(tx, eventsDef, id);
|
|
1437
|
-
});
|
|
1438
|
-
}
|
|
1439
|
-
};
|
|
1440
|
-
}
|
|
1441
|
-
function sqliteNoteStore(db) {
|
|
1803
|
+
let u;
|
|
1804
|
+
try {
|
|
1805
|
+
u = new URL(url);
|
|
1806
|
+
} catch {
|
|
1807
|
+
fail(`${source} is not a parseable URL`);
|
|
1808
|
+
}
|
|
1809
|
+
const database = decodeURIComponent(u.pathname.replace(/^\/+|\/+$/g, ""));
|
|
1810
|
+
if (database === "") fail(`${source} has no database name`);
|
|
1442
1811
|
return {
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
const ids = {};
|
|
1453
|
-
for (const tag of tags) {
|
|
1454
|
-
for (const row of await rowsByIndex(sql, db, "note_tags", "by-tag", tag.id)) {
|
|
1455
|
-
ids[row.noteId] = true;
|
|
1456
|
-
}
|
|
1457
|
-
}
|
|
1458
|
-
return new Set(Object.keys(ids));
|
|
1459
|
-
},
|
|
1460
|
-
getNoteTagLabels: async (noteId) => {
|
|
1461
|
-
const sql = await loadSql();
|
|
1462
|
-
const tagRows = await allRows(sql, db, "tags");
|
|
1463
|
-
const labelById = {};
|
|
1464
|
-
for (const t of tagRows) labelById[t.id] = t.label;
|
|
1465
|
-
const links = await rowsByIndex(sql, db, "note_tags", "by-note", noteId);
|
|
1466
|
-
return links.map((l) => labelById[l.tagId]).filter((l) => typeof l === "string");
|
|
1467
|
-
},
|
|
1468
|
-
queryDomain: async (request) => {
|
|
1469
|
-
const result = await (await loadSql()).handleDomainQuery(db, request);
|
|
1470
|
-
return result;
|
|
1471
|
-
}
|
|
1812
|
+
kind: "postgres",
|
|
1813
|
+
label: safeUrlLabel(url),
|
|
1814
|
+
url,
|
|
1815
|
+
host: u.hostname,
|
|
1816
|
+
port: u.port !== "" ? parsePort(u.port, `${source} port`) : 5432,
|
|
1817
|
+
database,
|
|
1818
|
+
user: u.username !== "" ? decodeURIComponent(u.username) : void 0,
|
|
1819
|
+
password: u.password !== "" ? decodeURIComponent(u.password) : void 0,
|
|
1820
|
+
ssl: sslMode(u.searchParams.get("sslmode") ?? u.searchParams.get("ssl") ?? void 0, `${source} ssl parameter`)
|
|
1472
1821
|
};
|
|
1473
1822
|
}
|
|
1474
|
-
function
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1823
|
+
function resolvePostgres(options, e, databaseUrl) {
|
|
1824
|
+
const flagProps = PG_FLAG_KEYS.some((k) => options[k] !== void 0);
|
|
1825
|
+
const envProps = PG_ENV_KEYS.some((k) => setEnv(e[k]) !== void 0);
|
|
1826
|
+
if (flagProps || envProps && databaseUrl === void 0) {
|
|
1827
|
+
const host = options.postgresHost ?? setEnv(e.POSTGRES_HOST) ?? "127.0.0.1";
|
|
1828
|
+
const port = parsePort(options.postgresPort ?? setEnv(e.POSTGRES_PORT) ?? "5432", "--postgres-port/POSTGRES_PORT");
|
|
1829
|
+
const database = options.postgresDatabase ?? setEnv(e.POSTGRES_DATABASE);
|
|
1830
|
+
if (database === void 0) {
|
|
1831
|
+
fail("--postgres-database (or POSTGRES_DATABASE) is required for per-property PostgreSQL configuration");
|
|
1480
1832
|
}
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1833
|
+
const user = options.postgresUser ?? setEnv(e.POSTGRES_USER);
|
|
1834
|
+
const password = options.postgresPassword ?? setEnv(e.POSTGRES_PASSWORD);
|
|
1835
|
+
const ssl = sslMode(options.postgresSsl ?? setEnv(e.POSTGRES_SSL), "--postgres-ssl/POSTGRES_SSL");
|
|
1836
|
+
try {
|
|
1837
|
+
const u = new URL(`postgresql://${host}`);
|
|
1838
|
+
u.port = String(port);
|
|
1839
|
+
u.pathname = `/${database}`;
|
|
1840
|
+
if (user !== void 0) u.username = user;
|
|
1841
|
+
if (password !== void 0) u.password = password;
|
|
1842
|
+
u.searchParams.set("sslmode", ssl);
|
|
1843
|
+
return {
|
|
1844
|
+
kind: "postgres",
|
|
1845
|
+
label: `postgresql://${u.host}${u.pathname}`,
|
|
1846
|
+
url: u.toString(),
|
|
1847
|
+
host,
|
|
1848
|
+
port,
|
|
1849
|
+
database,
|
|
1850
|
+
user,
|
|
1851
|
+
password,
|
|
1852
|
+
ssl
|
|
1853
|
+
};
|
|
1854
|
+
} catch {
|
|
1855
|
+
fail("invalid PostgreSQL host/database in per-property configuration");
|
|
1493
1856
|
}
|
|
1494
|
-
}
|
|
1857
|
+
}
|
|
1858
|
+
if (databaseUrl !== void 0) return postgresFromUrl(databaseUrl, "DB_DRIVER=postgres DATABASE_URL");
|
|
1859
|
+
fail("DB_DRIVER=postgres requires DATABASE_URL starting with postgres:// or postgresql:// \u2014 or per-property --postgres-host/--postgres-database flags");
|
|
1495
1860
|
}
|
|
1496
|
-
function
|
|
1861
|
+
function resolveTurso(options, e, databaseUrl) {
|
|
1862
|
+
const url = options.tursoUrl ?? setEnv(e.TURSO_DATABASE_URL) ?? databaseUrl;
|
|
1863
|
+
if (url === void 0) {
|
|
1864
|
+
fail("DB_DRIVER=turso requires --turso-url / TURSO_DATABASE_URL or DATABASE_URL (libsql:// or https://)");
|
|
1865
|
+
}
|
|
1866
|
+
if (PG_URL_SCHEMES.test(url)) fail("DB_DRIVER=turso cannot use a postgres:// URL");
|
|
1867
|
+
if (!TURSO_URL_SCHEMES.test(url)) fail("DB_DRIVER=turso requires a libsql://, https://, http:// or wss:// URL");
|
|
1497
1868
|
return {
|
|
1498
|
-
|
|
1869
|
+
kind: "turso",
|
|
1870
|
+
label: safeUrlLabel(url),
|
|
1871
|
+
url,
|
|
1872
|
+
authToken: options.tursoAuthToken ?? setEnv(e.TURSO_AUTH_TOKEN) ?? setEnv(e.LIBSQL_AUTH_TOKEN)
|
|
1499
1873
|
};
|
|
1500
1874
|
}
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
}
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1875
|
+
function resolveSqlite(options, e, databaseUrl) {
|
|
1876
|
+
if (options.db !== void 0) {
|
|
1877
|
+
const path2 = nodePath2().resolve(process.cwd(), options.db);
|
|
1878
|
+
return { kind: "sqlite", label: path2, path: path2 };
|
|
1879
|
+
}
|
|
1880
|
+
if (databaseUrl !== void 0) {
|
|
1881
|
+
if (PG_URL_SCHEMES.test(databaseUrl)) fail("DB_DRIVER=sqlite cannot use a postgres:// DATABASE_URL");
|
|
1882
|
+
if (databaseUrl.startsWith("file:")) {
|
|
1883
|
+
const path2 = nodePath2().resolve(process.cwd(), decodeURIComponent(databaseUrl.slice("file:".length).split("?")[0]));
|
|
1884
|
+
return { kind: "sqlite", label: path2, path: path2 };
|
|
1885
|
+
}
|
|
1886
|
+
if (!SQLITE_URL_SCHEMES.test(databaseUrl)) {
|
|
1887
|
+
fail("DB_DRIVER=sqlite requires a libsql://, https://, http://, wss:// or file:// DATABASE_URL");
|
|
1888
|
+
}
|
|
1889
|
+
return { kind: "turso", label: safeUrlLabel(databaseUrl), url: databaseUrl, authToken: setEnv(e.LIBSQL_AUTH_TOKEN) };
|
|
1890
|
+
}
|
|
1891
|
+
const path = nodePath2().resolve(process.cwd(), setEnv(e.WOD_WIKI_DB) ?? setEnv(e.SQLITE_PATH) ?? "./wodwiki.db");
|
|
1892
|
+
return { kind: "sqlite", label: path, path };
|
|
1893
|
+
}
|
|
1894
|
+
function envBackend(e) {
|
|
1895
|
+
const kinds = /* @__PURE__ */ new Set();
|
|
1896
|
+
if (setEnv(e.WOD_WIKI_DB) !== void 0 || setEnv(e.SQLITE_PATH) !== void 0) kinds.add("sqlite");
|
|
1897
|
+
const dbUrl = setEnv(e.DATABASE_URL);
|
|
1898
|
+
if (PG_ENV_KEYS.some((k) => setEnv(e[k]) !== void 0) || dbUrl !== void 0 && PG_URL_SCHEMES.test(dbUrl)) {
|
|
1899
|
+
kinds.add("postgres");
|
|
1900
|
+
} else if (dbUrl !== void 0 && SQLITE_URL_SCHEMES.test(dbUrl)) {
|
|
1901
|
+
kinds.add("sqlite");
|
|
1902
|
+
}
|
|
1903
|
+
if (setEnv(e.TURSO_DATABASE_URL) !== void 0) kinds.add("turso");
|
|
1904
|
+
if (kinds.size > 1) {
|
|
1905
|
+
fail(`ambiguous database configuration: ${[...kinds].join(" + ")} env vars are both set \u2014 set DB_DRIVER (or pass explicit flags) to choose`);
|
|
1906
|
+
}
|
|
1907
|
+
return kinds.values().next().value ?? "sqlite";
|
|
1517
1908
|
}
|
|
1518
|
-
|
|
1519
|
-
const
|
|
1520
|
-
const
|
|
1521
|
-
|
|
1909
|
+
function resolveDatabaseConfig(options = {}, env) {
|
|
1910
|
+
const e = env ?? { ...loadCliEnvironment(options.envFile), ...process.env };
|
|
1911
|
+
const driverFlag = options.dbDriver;
|
|
1912
|
+
if (driverFlag !== void 0 && !isBackend(driverFlag)) {
|
|
1913
|
+
fail(`--db-driver must be sqlite|postgres|turso, got ${JSON.stringify(driverFlag)}`);
|
|
1914
|
+
}
|
|
1915
|
+
const pinned = [];
|
|
1916
|
+
if (options.db !== void 0) pinned.push(["--db", "sqlite"]);
|
|
1917
|
+
if (driverFlag !== void 0) pinned.push([`--db-driver ${driverFlag}`, driverFlag]);
|
|
1918
|
+
if (options.tursoUrl !== void 0) pinned.push(["--turso-url", "turso"]);
|
|
1919
|
+
if (options.tursoAuthToken !== void 0) pinned.push(["--turso-auth-token", "turso"]);
|
|
1920
|
+
if (PG_FLAG_KEYS.some((k) => options[k] !== void 0)) pinned.push(["--postgres-*", "postgres"]);
|
|
1921
|
+
const kinds = new Set(pinned.map(([, kind]) => kind));
|
|
1922
|
+
if (kinds.size > 1) {
|
|
1923
|
+
fail(`conflicting database selectors: ${pinned.map(([flag, kind]) => `${flag} (${kind})`).join(", ")}`);
|
|
1924
|
+
}
|
|
1925
|
+
if (options.databaseUrl !== void 0) {
|
|
1926
|
+
if (options.db !== void 0) fail("conflicting database selectors: --db and --database-url are both set");
|
|
1927
|
+
if (options.tursoUrl !== void 0) fail("conflicting database selectors: --turso-url and --database-url are both set");
|
|
1928
|
+
if (PG_FLAG_KEYS.some((k) => options[k] !== void 0)) {
|
|
1929
|
+
fail("conflicting database selectors: --database-url and --postgres-* flags are both set");
|
|
1930
|
+
}
|
|
1931
|
+
}
|
|
1932
|
+
let backend;
|
|
1933
|
+
if (pinned.length > 0) {
|
|
1934
|
+
backend = pinned[0][1];
|
|
1935
|
+
} else {
|
|
1936
|
+
const envDriver = setEnv(e.DB_DRIVER);
|
|
1937
|
+
if (envDriver !== void 0) {
|
|
1938
|
+
if (!isBackend(envDriver)) {
|
|
1939
|
+
fail(`Unsupported DB_DRIVER: ${envDriver} (expected sqlite|turso|postgres)`);
|
|
1940
|
+
}
|
|
1941
|
+
backend = envDriver;
|
|
1942
|
+
} else {
|
|
1943
|
+
backend = envBackend(e);
|
|
1944
|
+
}
|
|
1945
|
+
}
|
|
1946
|
+
const databaseUrl = options.databaseUrl ?? setEnv(e.DATABASE_URL);
|
|
1947
|
+
switch (backend) {
|
|
1948
|
+
case "postgres":
|
|
1949
|
+
return resolvePostgres(options, e, databaseUrl);
|
|
1950
|
+
case "turso":
|
|
1951
|
+
return resolveTurso(options, e, databaseUrl);
|
|
1952
|
+
case "sqlite":
|
|
1953
|
+
return resolveSqlite(options, e, databaseUrl);
|
|
1954
|
+
}
|
|
1522
1955
|
}
|
|
1956
|
+
|
|
1957
|
+
// src/cli/persist.ts
|
|
1958
|
+
init_db();
|
|
1523
1959
|
var registeredWidgets = wodWikiCore.KNOWN_SECTION_WIDGETS;
|
|
1524
1960
|
var saveStores = ["notes", "block_revisions", "block_index", "block_efforts", "tags", "note_tags", "sessions"];
|
|
1525
1961
|
function extractEffortSlugs(statements) {
|
|
@@ -1789,6 +2225,9 @@ async function persistTypedTags(db, labels, type) {
|
|
|
1789
2225
|
return added;
|
|
1790
2226
|
});
|
|
1791
2227
|
}
|
|
2228
|
+
|
|
2229
|
+
// src/cli/seed.ts
|
|
2230
|
+
init_db();
|
|
1792
2231
|
var NOTE_ID_FRONTMATTER_KEY = "wod-note-id";
|
|
1793
2232
|
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
1794
2233
|
function extractNoteIdentity(markdown) {
|
|
@@ -2911,16 +3350,53 @@ ${source}
|
|
|
2911
3350
|
${fence}
|
|
2912
3351
|
`;
|
|
2913
3352
|
}
|
|
3353
|
+
var VALUE_FLAGS = [
|
|
3354
|
+
["--db", "db"],
|
|
3355
|
+
["--db-driver", "dbDriver"],
|
|
3356
|
+
["--database-url", "databaseUrl"],
|
|
3357
|
+
["--postgres-host", "postgresHost"],
|
|
3358
|
+
["--postgres-port", "postgresPort"],
|
|
3359
|
+
["--postgres-user", "postgresUser"],
|
|
3360
|
+
["--postgres-password", "postgresPassword"],
|
|
3361
|
+
["--postgres-database", "postgresDatabase"],
|
|
3362
|
+
["--postgres-ssl", "postgresSsl"],
|
|
3363
|
+
["--turso-url", "tursoUrl"],
|
|
3364
|
+
["--turso-auth-token", "tursoAuthToken"],
|
|
3365
|
+
["--env-file", "envFile"],
|
|
3366
|
+
["--host", "host"],
|
|
3367
|
+
["--port", "port"],
|
|
3368
|
+
["--dist", "dist"]
|
|
3369
|
+
];
|
|
3370
|
+
function readValueFlag(args, i, flag) {
|
|
3371
|
+
const inline = args[i].startsWith(`${flag}=`);
|
|
3372
|
+
if (inline) {
|
|
3373
|
+
const value2 = args[i].slice(flag.length + 1);
|
|
3374
|
+
if (!value2) throw new Error(`Missing value for ${flag}`);
|
|
3375
|
+
return { value: value2, next: i + 1 };
|
|
3376
|
+
}
|
|
3377
|
+
const value = args[i + 1];
|
|
3378
|
+
if (value === void 0 || value === "" || value.startsWith("--")) throw new Error(`Missing value for ${flag}`);
|
|
3379
|
+
return { value, next: i + 2 };
|
|
3380
|
+
}
|
|
2914
3381
|
function parseCliArgs(args) {
|
|
2915
3382
|
const result = {
|
|
2916
3383
|
format: "json",
|
|
2917
3384
|
packs: [],
|
|
2918
3385
|
sources: []
|
|
2919
3386
|
};
|
|
3387
|
+
const valueArgs = result;
|
|
2920
3388
|
let i = 0;
|
|
2921
3389
|
while (i < args.length) {
|
|
2922
3390
|
const arg = args[i];
|
|
2923
3391
|
if (arg === "--at" || arg.startsWith("--at=")) throw new Error("Unknown option --at; whole Note exports have no as-of version");
|
|
3392
|
+
const valueFlag = VALUE_FLAGS.find(([flag]) => arg === flag || arg.startsWith(`${flag}=`));
|
|
3393
|
+
if (valueFlag) {
|
|
3394
|
+
const [flag, key] = valueFlag;
|
|
3395
|
+
const { value, next } = readValueFlag(args, i, flag);
|
|
3396
|
+
valueArgs[key] = value;
|
|
3397
|
+
i = next;
|
|
3398
|
+
continue;
|
|
3399
|
+
}
|
|
2924
3400
|
if (arg === "--help" || arg === "-h") {
|
|
2925
3401
|
result.help = true;
|
|
2926
3402
|
i++;
|
|
@@ -2967,12 +3443,6 @@ function parseCliArgs(args) {
|
|
|
2967
3443
|
} else if (arg === "--unit") {
|
|
2968
3444
|
result.unit = args[++i];
|
|
2969
3445
|
i++;
|
|
2970
|
-
} else if (arg === "--db") {
|
|
2971
|
-
result.db = args[++i];
|
|
2972
|
-
i++;
|
|
2973
|
-
} else if (arg.startsWith("--db=")) {
|
|
2974
|
-
result.db = arg.slice("--db=".length);
|
|
2975
|
-
i++;
|
|
2976
3446
|
} else if (arg === "--debug") {
|
|
2977
3447
|
result.debug = "";
|
|
2978
3448
|
i++;
|
|
@@ -3008,7 +3478,7 @@ function parseCliArgs(args) {
|
|
|
3008
3478
|
result.dir = arg.slice("--dir=".length);
|
|
3009
3479
|
i++;
|
|
3010
3480
|
} else if (!result.subcommand) {
|
|
3011
|
-
if (arg === "parse" || arg === "run" || arg === "query" || arg === "download" || arg === "import" || arg === "build" || arg === "seed" || arg === "reset" || arg === "help" || arg === "version") {
|
|
3481
|
+
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") {
|
|
3012
3482
|
result.subcommand = arg;
|
|
3013
3483
|
} else {
|
|
3014
3484
|
result.target = arg;
|
|
@@ -3024,14 +3494,19 @@ function parseCliArgs(args) {
|
|
|
3024
3494
|
i++;
|
|
3025
3495
|
}
|
|
3026
3496
|
}
|
|
3497
|
+
for (const [flag, value] of [["--port", result.port], ["--postgres-port", result.postgresPort]]) {
|
|
3498
|
+
if (value !== void 0 && (!/^\d+$/.test(value) || Number(value) < 1 || Number(value) > 65535)) {
|
|
3499
|
+
throw new Error(`Invalid value for ${flag}: "${value}" is not a TCP port (1-65535)`);
|
|
3500
|
+
}
|
|
3501
|
+
}
|
|
3027
3502
|
return result;
|
|
3028
3503
|
}
|
|
3029
3504
|
function readBunStdinText() {
|
|
3030
3505
|
const g = globalThis;
|
|
3031
3506
|
if (g && typeof g === "object" && "Bun" in g) {
|
|
3032
|
-
const
|
|
3033
|
-
if (
|
|
3034
|
-
const stdin =
|
|
3507
|
+
const bun2 = g.Bun;
|
|
3508
|
+
if (bun2 && typeof bun2 === "object" && "stdin" in bun2) {
|
|
3509
|
+
const stdin = bun2.stdin;
|
|
3035
3510
|
if (stdin && typeof stdin === "object" && "text" in stdin) {
|
|
3036
3511
|
const text = stdin.text;
|
|
3037
3512
|
if (typeof text === "function") return text;
|
|
@@ -3069,6 +3544,11 @@ USAGE:
|
|
|
3069
3544
|
wod build [dir] [options]
|
|
3070
3545
|
wod seed [options]
|
|
3071
3546
|
wod reset [options]
|
|
3547
|
+
wod init Initialize the configured database (idempotent)
|
|
3548
|
+
wod server [options]
|
|
3549
|
+
Serve the app (HTTP API + packaged frontend; alias: serve)
|
|
3550
|
+
wod playground [options]
|
|
3551
|
+
Serve the local playground (browser IndexedDB build)
|
|
3072
3552
|
|
|
3073
3553
|
SUBCOMMANDS:
|
|
3074
3554
|
parse <file | -> Parse Whiteboard Script, create a note in the database
|
|
@@ -3122,9 +3602,47 @@ INPUT SOURCING (for 'query'; without these, the database is queried):
|
|
|
3122
3602
|
--stdin-log Read kind: 'execution-log' JSON payload from stdin
|
|
3123
3603
|
--stdin-facts Read raw kind: 'fact-set' JSON payload from stdin
|
|
3124
3604
|
|
|
3125
|
-
DATABASE (parse / run / query / download / import / seed / reset):
|
|
3126
|
-
|
|
3127
|
-
|
|
3605
|
+
DATABASE (init / parse / run / query / download / import / seed / reset / server):
|
|
3606
|
+
Pick one backend; explicit flags win over the process environment, the
|
|
3607
|
+
process environment wins over .env files. Run 'wod init' once first: it
|
|
3608
|
+
creates the SQLite file + schema, creates the PostgreSQL database when
|
|
3609
|
+
your role is authorized to (otherwise use an existing/provisioned one),
|
|
3610
|
+
or installs the schema in your provisioned Turso database \u2014 idempotently,
|
|
3611
|
+
preserving existing notes. 'wod server' refuses an uninitialized database.
|
|
3612
|
+
--db <path> Local SQLite database file (default: $WOD_WIKI_DB
|
|
3613
|
+
or ./wodwiki.db). Passing --db always selects SQLite,
|
|
3614
|
+
even when the environment names a remote backend.
|
|
3615
|
+
Created on demand by parse/run/seed.
|
|
3616
|
+
--db-driver <name> Backend driver: sqlite (default), postgres, or
|
|
3617
|
+
turso ($DB_DRIVER)
|
|
3618
|
+
--database-url <url> Database connection URL ($DATABASE_URL); accepts
|
|
3619
|
+
PostgreSQL, local SQLite, and remote libSQL URLs.
|
|
3620
|
+
--postgres-host <host> PostgreSQL host ($POSTGRES_HOST)
|
|
3621
|
+
--postgres-port <port> PostgreSQL port ($POSTGRES_PORT)
|
|
3622
|
+
--postgres-user <user> PostgreSQL user ($POSTGRES_USER)
|
|
3623
|
+
--postgres-password <pw> PostgreSQL password ($POSTGRES_PASSWORD)
|
|
3624
|
+
--postgres-database <db> PostgreSQL database name ($POSTGRES_DATABASE)
|
|
3625
|
+
--postgres-ssl <mode> PostgreSQL SSL mode, e.g. disable/prefer/require
|
|
3626
|
+
($POSTGRES_SSL)
|
|
3627
|
+
--turso-url <url> Turso/libSQL database URL (libsql://\u2026). The
|
|
3628
|
+
hosted database must already exist (provisioned on
|
|
3629
|
+
Turso); only its schema is installed.
|
|
3630
|
+
--turso-auth-token <token> Turso authentication token (never printed)
|
|
3631
|
+
--env-file <path> Load variables from this file; a missing file is
|
|
3632
|
+
an error. Without it: ./.env when present, otherwise
|
|
3633
|
+
the workspace-root .env. Credentials are never logged.
|
|
3634
|
+
|
|
3635
|
+
HOSTING (for 'server' / 'playground'; requires the Bun runtime):
|
|
3636
|
+
server HTTP API over the configured database plus the frontend
|
|
3637
|
+
packaged inside the engine module. Requires an
|
|
3638
|
+
initialized database. ('serve' is an accepted alias.)
|
|
3639
|
+
playground Serves the playground app backed by browser IndexedDB;
|
|
3640
|
+
the configured database is not touched.
|
|
3641
|
+
--host <host> Bind address (default 127.0.0.1, $HOST)
|
|
3642
|
+
--port <port> TCP port (default 3000, $PORT)
|
|
3643
|
+
--dist <dir> Serve this prebuilt frontend directory instead of the
|
|
3644
|
+
packaged one ($PLAYGROUND_DIST); must already contain
|
|
3645
|
+
index.html.
|
|
3128
3646
|
|
|
3129
3647
|
SEED SOURCING (for 'seed'):
|
|
3130
3648
|
--source <dir> Extra compiled seed directory (manifest.json + chunks);
|
|
@@ -3144,6 +3662,15 @@ OPTIONS:
|
|
|
3144
3662
|
-h, --help Show help message
|
|
3145
3663
|
-v, --version Show version number
|
|
3146
3664
|
|
|
3665
|
+
ENVIRONMENT (flags > process env > .env):
|
|
3666
|
+
WOD_WIKI_DB, SQLITE_PATH SQLite file path
|
|
3667
|
+
DB_DRIVER sqlite | postgres | turso
|
|
3668
|
+
DATABASE_URL PostgreSQL connection URL
|
|
3669
|
+
POSTGRES_HOST, POSTGRES_PORT, POSTGRES_USER,
|
|
3670
|
+
POSTGRES_PASSWORD, POSTGRES_DATABASE, POSTGRES_SSL
|
|
3671
|
+
TURSO_DATABASE_URL, TURSO_AUTH_TOKEN, LIBSQL_AUTH_TOKEN
|
|
3672
|
+
HOST, PORT, PLAYGROUND_DIST server / playground defaults
|
|
3673
|
+
|
|
3147
3674
|
EXIT CODES:
|
|
3148
3675
|
0 - OK / Success
|
|
3149
3676
|
1 - General / Runtime error
|
|
@@ -3163,6 +3690,10 @@ async function cliMain(argv = process.argv.slice(2), io = {}) {
|
|
|
3163
3690
|
const writeOut = io.stdout ?? ((t) => process.stdout.write(t + "\n"));
|
|
3164
3691
|
const writeErr = io.stderr ?? ((t) => process.stderr.write(t + "\n"));
|
|
3165
3692
|
const getStdin = io.readStdinFn ?? readStdin;
|
|
3693
|
+
let envLoaded;
|
|
3694
|
+
const cliEnv = () => envLoaded ??= { ...loadCliEnvironment(parsed.envFile), ...process.env };
|
|
3695
|
+
let dbResolved;
|
|
3696
|
+
const cliDb = () => dbResolved ??= resolveDatabaseConfig(parsed, cliEnv());
|
|
3166
3697
|
let parsed;
|
|
3167
3698
|
try {
|
|
3168
3699
|
parsed = parseCliArgs(argv);
|
|
@@ -3229,7 +3760,7 @@ async function cliMain(argv = process.argv.slice(2), io = {}) {
|
|
|
3229
3760
|
const ir = runParse(source, { sport: parsed.sport, sourceLabel: `cli:wod parse ${parsed.target}` });
|
|
3230
3761
|
const identity = extractNoteIdentity(source);
|
|
3231
3762
|
const tags = extractTypedTags(source);
|
|
3232
|
-
const db = await openCliDb(
|
|
3763
|
+
const db = await openCliDb(cliDb());
|
|
3233
3764
|
try {
|
|
3234
3765
|
const persisted = await persistNote(db, {
|
|
3235
3766
|
source: noteSource,
|
|
@@ -3268,7 +3799,7 @@ async function cliMain(argv = process.argv.slice(2), io = {}) {
|
|
|
3268
3799
|
source = fs.readFileSync(parsed.target, "utf-8");
|
|
3269
3800
|
}
|
|
3270
3801
|
const identity = extractNoteIdentity(source);
|
|
3271
|
-
const db = await openCliDb(
|
|
3802
|
+
const db = await openCliDb(cliDb());
|
|
3272
3803
|
let ir;
|
|
3273
3804
|
try {
|
|
3274
3805
|
const started = await startRun(db, {
|
|
@@ -3319,14 +3850,14 @@ async function cliMain(argv = process.argv.slice(2), io = {}) {
|
|
|
3319
3850
|
}
|
|
3320
3851
|
let ir;
|
|
3321
3852
|
if (useDb) {
|
|
3322
|
-
const
|
|
3323
|
-
if (!nodeFs().existsSync(
|
|
3853
|
+
const config = cliDb();
|
|
3854
|
+
if (config.kind === "sqlite" && !nodeFs().existsSync(config.path)) {
|
|
3324
3855
|
writeErr(
|
|
3325
|
-
`[wod query] No input dataset and no database at ${
|
|
3856
|
+
`[wod query] No input dataset and no database at ${config.path}. Specify --corpus <file>, --stdin-log, --stdin-facts, or run \`wod seed\` first.`
|
|
3326
3857
|
);
|
|
3327
3858
|
return 1;
|
|
3328
3859
|
}
|
|
3329
|
-
const db = await openCliDb(
|
|
3860
|
+
const db = await openCliDb(config, config.kind === "sqlite" ? void 0 : { initialize: false });
|
|
3330
3861
|
try {
|
|
3331
3862
|
ir = await runQueryCli(parsed.target, {
|
|
3332
3863
|
preferredUnit: parsed.unit,
|
|
@@ -3372,7 +3903,7 @@ async function cliMain(argv = process.argv.slice(2), io = {}) {
|
|
|
3372
3903
|
return 1;
|
|
3373
3904
|
}
|
|
3374
3905
|
const targetId = id;
|
|
3375
|
-
const db = await openCliDb(
|
|
3906
|
+
const db = await openCliDb(cliDb());
|
|
3376
3907
|
try {
|
|
3377
3908
|
if (kind === "note") {
|
|
3378
3909
|
const bundle = await getNoteBundle(db.db, targetId);
|
|
@@ -3449,7 +3980,7 @@ async function cliMain(argv = process.argv.slice(2), io = {}) {
|
|
|
3449
3980
|
const source = fs.readFileSync(file, "utf-8");
|
|
3450
3981
|
const identity = extractNoteIdentity(source);
|
|
3451
3982
|
const tags = extractTypedTags(source);
|
|
3452
|
-
const db2 = await openCliDb(
|
|
3983
|
+
const db2 = await openCliDb(cliDb());
|
|
3453
3984
|
try {
|
|
3454
3985
|
const persisted = await persistNote(db2, {
|
|
3455
3986
|
source,
|
|
@@ -3472,7 +4003,7 @@ async function cliMain(argv = process.argv.slice(2), io = {}) {
|
|
|
3472
4003
|
writeErr(`[wod import] Unparseable effort file (need frontmatter id/slug/label + numeric baseAttributes.met): ${file}`);
|
|
3473
4004
|
return 1;
|
|
3474
4005
|
}
|
|
3475
|
-
const db2 = await openCliDb(
|
|
4006
|
+
const db2 = await openCliDb(cliDb());
|
|
3476
4007
|
try {
|
|
3477
4008
|
await persistEffort(db2, { ...effort, registrySource: "user" });
|
|
3478
4009
|
writeOut(`[wod import] effort ${effort.slug} \u2014 ${effort.label} \u2192 ${db2.path}`);
|
|
@@ -3487,7 +4018,7 @@ async function cliMain(argv = process.argv.slice(2), io = {}) {
|
|
|
3487
4018
|
writeErr(`[wod import] No equipment labels found in ${file}`);
|
|
3488
4019
|
return 1;
|
|
3489
4020
|
}
|
|
3490
|
-
const db2 = await openCliDb(
|
|
4021
|
+
const db2 = await openCliDb(cliDb());
|
|
3491
4022
|
try {
|
|
3492
4023
|
const added = await persistTypedTags(db2, labels, "equipment");
|
|
3493
4024
|
writeOut(`[wod import] equipment \u2014 ${labels.length} labels (${added} new) \u2192 ${db2.path}`);
|
|
@@ -3497,7 +4028,7 @@ async function cliMain(argv = process.argv.slice(2), io = {}) {
|
|
|
3497
4028
|
return 0;
|
|
3498
4029
|
}
|
|
3499
4030
|
const { session, events } = csvToSessionRows(fs.readFileSync(file, "utf-8"));
|
|
3500
|
-
const db = await openCliDb(
|
|
4031
|
+
const db = await openCliDb(cliDb());
|
|
3501
4032
|
try {
|
|
3502
4033
|
const result = await persistSessionBundle(db, session, events);
|
|
3503
4034
|
writeOut(
|
|
@@ -3544,7 +4075,7 @@ async function cliMain(argv = process.argv.slice(2), io = {}) {
|
|
|
3544
4075
|
);
|
|
3545
4076
|
return 1;
|
|
3546
4077
|
}
|
|
3547
|
-
const db = await openCliDb(
|
|
4078
|
+
const db = await openCliDb(cliDb());
|
|
3548
4079
|
try {
|
|
3549
4080
|
for (const dir of dirs) {
|
|
3550
4081
|
const summary = await importSeedDir(db, dir, "");
|
|
@@ -3558,7 +4089,14 @@ async function cliMain(argv = process.argv.slice(2), io = {}) {
|
|
|
3558
4089
|
return 0;
|
|
3559
4090
|
}
|
|
3560
4091
|
if (parsed.subcommand === "reset") {
|
|
3561
|
-
const
|
|
4092
|
+
const config = cliDb();
|
|
4093
|
+
if (config.kind !== "sqlite") {
|
|
4094
|
+
writeErr(
|
|
4095
|
+
`[wod reset] refusing to reset the configured ${config.kind} database \u2014 reset only removes a local SQLite file. Clear remote databases through their provider.`
|
|
4096
|
+
);
|
|
4097
|
+
return 1;
|
|
4098
|
+
}
|
|
4099
|
+
const target = config.path;
|
|
3562
4100
|
if (nodeFs().existsSync(target)) {
|
|
3563
4101
|
nodeFs().unlinkSync(target);
|
|
3564
4102
|
writeOut(`[wod reset] removed ${target}`);
|
|
@@ -3567,6 +4105,32 @@ async function cliMain(argv = process.argv.slice(2), io = {}) {
|
|
|
3567
4105
|
}
|
|
3568
4106
|
return 0;
|
|
3569
4107
|
}
|
|
4108
|
+
if (parsed.subcommand === "init") {
|
|
4109
|
+
const db = await openCliDb(cliDb(), { create: true });
|
|
4110
|
+
try {
|
|
4111
|
+
writeOut(`[wod init] database ready at ${db.path}`);
|
|
4112
|
+
} finally {
|
|
4113
|
+
await db.close();
|
|
4114
|
+
}
|
|
4115
|
+
return 0;
|
|
4116
|
+
}
|
|
4117
|
+
if (parsed.subcommand === "server" || parsed.subcommand === "serve" || parsed.subcommand === "playground") {
|
|
4118
|
+
if (!("Bun" in globalThis)) {
|
|
4119
|
+
writeErr(
|
|
4120
|
+
`[wod ${parsed.subcommand}] Error: hosting requires the Bun runtime (https://bun.sh) \u2014 run \`bun wod server\` / \`bun wod playground\`.`
|
|
4121
|
+
);
|
|
4122
|
+
return 1;
|
|
4123
|
+
}
|
|
4124
|
+
const mode = parsed.subcommand === "playground" ? "playground" : "server";
|
|
4125
|
+
const { runCliServer: runCliServer2 } = await Promise.resolve().then(() => (init_server(), server_exports));
|
|
4126
|
+
const options = { env: cliEnv() };
|
|
4127
|
+
if (mode === "server") options.database = cliDb();
|
|
4128
|
+
if (parsed.host !== void 0) options.host = parsed.host;
|
|
4129
|
+
if (parsed.port !== void 0) options.port = parsed.port;
|
|
4130
|
+
if (parsed.dist !== void 0) options.dist = parsed.dist;
|
|
4131
|
+
await runCliServer2(mode, options, writeOut);
|
|
4132
|
+
return 0;
|
|
4133
|
+
}
|
|
3570
4134
|
writeErr(`[wod] Unknown subcommand "${parsed.subcommand}"`);
|
|
3571
4135
|
return 1;
|
|
3572
4136
|
} catch (error) {
|
|
@@ -3584,13 +4148,13 @@ async function cliMain(argv = process.argv.slice(2), io = {}) {
|
|
|
3584
4148
|
}
|
|
3585
4149
|
|
|
3586
4150
|
// src/version.ts
|
|
3587
|
-
var VERSION2 = "0.
|
|
3588
|
-
var GIT_SHA = "
|
|
3589
|
-
var BUILD_TIME = "2026-10-
|
|
4151
|
+
var VERSION2 = "0.36.2130";
|
|
4152
|
+
var GIT_SHA = "aabe90c";
|
|
4153
|
+
var BUILD_TIME = "2026-10-14T23:19:24.665Z";
|
|
3590
4154
|
var SEMVER = {
|
|
3591
|
-
version: "0.
|
|
3592
|
-
gitSha: "
|
|
3593
|
-
buildTime: "2026-10-
|
|
4155
|
+
version: "0.36.2130",
|
|
4156
|
+
gitSha: "aabe90c",
|
|
4157
|
+
buildTime: "2026-10-14T23:19:24.665Z"
|
|
3594
4158
|
};
|
|
3595
4159
|
|
|
3596
4160
|
// src/index.ts
|