@bitcobblers/wod-wiki-tools 0.0.0-stage → 0.36.2134
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 +3543 -0
- package/dist/bin/wod.cjs.map +1 -0
- package/dist/bin/wod.d.cts +1 -0
- package/dist/bin/wod.d.ts +1 -0
- package/dist/bin/wod.mjs +27 -0
- package/dist/bin/wod.mjs.map +1 -0
- package/dist/chunk-32FASQP5.mjs +123 -0
- package/dist/chunk-32FASQP5.mjs.map +1 -0
- package/dist/chunk-6L5ZDZJ7.mjs +15 -0
- package/dist/chunk-6L5ZDZJ7.mjs.map +1 -0
- package/dist/chunk-B3QJYJVI.mjs +2061 -0
- package/dist/chunk-B3QJYJVI.mjs.map +1 -0
- package/dist/chunk-EUQWEX6Z.mjs +253 -0
- package/dist/chunk-EUQWEX6Z.mjs.map +1 -0
- package/dist/chunk-K3GZZFAC.mjs +300 -0
- package/dist/chunk-K3GZZFAC.mjs.map +1 -0
- package/dist/compiler-KT6P3AC6.mjs +730 -0
- package/dist/compiler-KT6P3AC6.mjs.map +1 -0
- package/dist/index.cjs +3548 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +356 -0
- package/dist/index.d.ts +356 -0
- package/dist/index.mjs +12 -0
- package/dist/index.mjs.map +1 -0
- 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 +9 -0
- package/dist/server/app.mjs.map +1 -0
- package/dist/server-3TJZDKQC.mjs +11 -0
- package/dist/server-3TJZDKQC.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-Bj_mxNMk.js +212 -0
- package/dist/web/playground/assets/rolldown-runtime-C0FnF6B9.js +1 -0
- package/dist/web/playground/assets/statementBuilderFlow-DXlHS86r.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.054cd7db.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-CfAVkURA.js +211 -0
- package/dist/web/server/assets/rolldown-runtime-C0FnF6B9.js +1 -0
- package/dist/web/server/assets/statementBuilderFlow-CXugzTXB.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.054cd7db.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 +46 -4
- package/README.md +0 -3
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
import { createRequire } from 'module';
|
|
2
|
+
import { __require } from './chunk-6L5ZDZJ7.mjs';
|
|
3
|
+
import { bundledEfforts } from '@bitcobblers/wod-wiki-lang';
|
|
4
|
+
|
|
5
|
+
if (typeof globalThis.require === 'undefined') {
|
|
6
|
+
try { globalThis.require = createRequire(import.meta.url); } catch {}
|
|
7
|
+
}
|
|
8
|
+
var nodePath = () => nodeRequire()("node:path");
|
|
9
|
+
var nodeFs = () => nodeRequire()("node:fs");
|
|
10
|
+
var nodeCrypto = () => nodeRequire()("node:crypto");
|
|
11
|
+
function nodeRequire() {
|
|
12
|
+
const g = globalThis;
|
|
13
|
+
if (typeof g.process?.versions?.node === "undefined" && typeof g.Bun === "undefined") {
|
|
14
|
+
throw new Error("SQLite operations are only supported in Node / Bun environments");
|
|
15
|
+
}
|
|
16
|
+
const req = g.require ?? (typeof __require === "function" ? __require : void 0);
|
|
17
|
+
if (!req) throw new Error("SQLite operations require a CJS require shim (missing tsup banner?)");
|
|
18
|
+
return req;
|
|
19
|
+
}
|
|
20
|
+
var sqlModulePromise;
|
|
21
|
+
function loadSql() {
|
|
22
|
+
sqlModulePromise ??= import(
|
|
23
|
+
/* @vite-ignore */
|
|
24
|
+
'@bitcobblers/wod-wiki-storage/sql'
|
|
25
|
+
);
|
|
26
|
+
return sqlModulePromise;
|
|
27
|
+
}
|
|
28
|
+
function resolveDbPath(dbPath) {
|
|
29
|
+
const chosen = dbPath ?? process.env.WOD_WIKI_DB ?? "./wodwiki.db";
|
|
30
|
+
return nodePath().resolve(process.cwd(), chosen);
|
|
31
|
+
}
|
|
32
|
+
async function openCliDb(target, opts) {
|
|
33
|
+
if (target === void 0 || typeof target === "string") {
|
|
34
|
+
return openSqliteDb(resolveDbPath(target), opts);
|
|
35
|
+
}
|
|
36
|
+
switch (target.kind) {
|
|
37
|
+
case "sqlite":
|
|
38
|
+
return openSqliteDb(target.path, opts);
|
|
39
|
+
case "turso":
|
|
40
|
+
return openRemoteDb(
|
|
41
|
+
{ DB_DRIVER: "turso", DATABASE_URL: target.url, LIBSQL_AUTH_TOKEN: target.authToken },
|
|
42
|
+
target.label,
|
|
43
|
+
opts
|
|
44
|
+
);
|
|
45
|
+
case "postgres":
|
|
46
|
+
return openRemoteDb({ DB_DRIVER: "postgres", DATABASE_URL: target.url }, target.label, opts);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
async function openSqliteDb(resolved, opts) {
|
|
50
|
+
const [{ createClient }, sql] = await Promise.all([
|
|
51
|
+
import(
|
|
52
|
+
/* @vite-ignore */
|
|
53
|
+
'@libsql/client'
|
|
54
|
+
),
|
|
55
|
+
loadSql()
|
|
56
|
+
]);
|
|
57
|
+
const initialize = opts?.initialize ?? true;
|
|
58
|
+
if (!initialize && !nodeFs().existsSync(resolved)) {
|
|
59
|
+
throw new Error(`[wod] no database at ${resolved} \u2014 run \`wod init\` to create and initialize it`);
|
|
60
|
+
}
|
|
61
|
+
if (initialize) nodeFs().mkdirSync(nodePath().dirname(resolved), { recursive: true });
|
|
62
|
+
const db = sql.libsqlDatabase(createClient({ url: `file:${resolved}` }));
|
|
63
|
+
try {
|
|
64
|
+
if (initialize) {
|
|
65
|
+
await sql.initializeStore(db);
|
|
66
|
+
await sql.initializeDomain(db);
|
|
67
|
+
} else {
|
|
68
|
+
await sql.verifyReadiness(db).catch((error) => {
|
|
69
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
70
|
+
throw new Error(`${message} \u2014 run \`wod init\` to initialize it`, { cause: error });
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
return { db, storage: new sql.SqlStorage(db), path: resolved, close: () => db.close() };
|
|
74
|
+
} catch (error) {
|
|
75
|
+
await db.close();
|
|
76
|
+
throw error;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
async function openRemoteDb(env, label, opts) {
|
|
80
|
+
const [{ openDatabase }, sql] = await Promise.all([
|
|
81
|
+
import(
|
|
82
|
+
/* @vite-ignore */
|
|
83
|
+
'@bitcobblers/wod-wiki-storage/sql/node'
|
|
84
|
+
),
|
|
85
|
+
loadSql()
|
|
86
|
+
]);
|
|
87
|
+
let db;
|
|
88
|
+
try {
|
|
89
|
+
db = await openDatabase(env, { create: opts?.create });
|
|
90
|
+
if (opts?.initialize === false) {
|
|
91
|
+
await sql.verifyReadiness(db);
|
|
92
|
+
} else {
|
|
93
|
+
await sql.initializeStore(db);
|
|
94
|
+
await sql.initializeDomain(db);
|
|
95
|
+
}
|
|
96
|
+
} catch (error) {
|
|
97
|
+
await db?.close().catch(() => {
|
|
98
|
+
});
|
|
99
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
100
|
+
throw new Error(
|
|
101
|
+
`[wod] ${label}: ${message}${opts?.initialize === false ? " \u2014 run `wod init` to initialize it" : ""}`,
|
|
102
|
+
{ cause: error }
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
return { db, storage: new sql.SqlStorage(db), path: label, close: () => db.close() };
|
|
106
|
+
}
|
|
107
|
+
function formatV5Digest(digest) {
|
|
108
|
+
const bytes = digest.slice(0, 16);
|
|
109
|
+
bytes[6] = bytes[6] & 15 | 80;
|
|
110
|
+
bytes[8] = bytes[8] & 63 | 128;
|
|
111
|
+
const hex = [...bytes].map((b) => b.toString(16).padStart(2, "0"));
|
|
112
|
+
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("")}`;
|
|
113
|
+
}
|
|
114
|
+
function uuid5(namespace, name) {
|
|
115
|
+
return formatV5Digest(nodeCrypto().createHash("sha1").update(namespace).update(name).digest());
|
|
116
|
+
}
|
|
117
|
+
var CLI_UUID_NAMESPACE = "c0ffee00-8b1a-4e2c-9d3f-5a6b7c8d9e0f";
|
|
118
|
+
function stableNoteId(sourceLabel, content) {
|
|
119
|
+
if (sourceLabel && sourceLabel !== "-") {
|
|
120
|
+
return uuid5(CLI_UUID_NAMESPACE, `wod-cli:${nodePath().resolve(process.cwd(), sourceLabel)}`);
|
|
121
|
+
}
|
|
122
|
+
return uuid5(CLI_UUID_NAMESPACE, `wod-cli:stdin:${content.length}:${content.slice(0, 256)}`);
|
|
123
|
+
}
|
|
124
|
+
function randomId() {
|
|
125
|
+
return nodeCrypto().randomUUID();
|
|
126
|
+
}
|
|
127
|
+
async function allRows(sql, db, store) {
|
|
128
|
+
return sql.listRows(db, sql.storeDefinition(store));
|
|
129
|
+
}
|
|
130
|
+
async function rowsByIndex(sql, db, store, index, only) {
|
|
131
|
+
return sql.listRows(db, sql.storeDefinition(store), { only }, void 0, index);
|
|
132
|
+
}
|
|
133
|
+
function sqliteEventStore(db) {
|
|
134
|
+
const all = async () => await allRows(await loadSql(), db, "events");
|
|
135
|
+
return {
|
|
136
|
+
getEventsByTimeRange: async (start, end) => (await all()).filter((r) => r.timestamp >= start && r.timestamp <= end),
|
|
137
|
+
getEventsByResult: async (resultId) => (await all()).filter((r) => r.resultId === resultId),
|
|
138
|
+
getEventsForNote: async (noteId) => (await all()).filter((r) => r.noteId === noteId),
|
|
139
|
+
getEventsByContent: async (blockContentId) => (await all()).filter((r) => r.blockContentId === blockContentId),
|
|
140
|
+
scanAll: all,
|
|
141
|
+
appendEvents: async (appended) => {
|
|
142
|
+
const sql = await loadSql();
|
|
143
|
+
await db.transaction(async (tx) => {
|
|
144
|
+
const eventsDef = sql.storeDefinition("events");
|
|
145
|
+
for (const row of appended) await sql.putRow(tx, eventsDef, row);
|
|
146
|
+
});
|
|
147
|
+
},
|
|
148
|
+
finalizeSummaries: async (resultId, finals) => {
|
|
149
|
+
const sql = await loadSql();
|
|
150
|
+
await db.transaction(async (tx) => {
|
|
151
|
+
const eventsDef = sql.storeDefinition("events");
|
|
152
|
+
const existing = (await sql.listRows(tx, eventsDef)).filter(
|
|
153
|
+
(r) => r.resultId === resultId && r.grain === "summary" && r.origin !== "user"
|
|
154
|
+
);
|
|
155
|
+
for (const row of existing) await sql.deleteRows(tx, eventsDef, row.id);
|
|
156
|
+
for (const row of finals) await sql.putRow(tx, eventsDef, row);
|
|
157
|
+
});
|
|
158
|
+
},
|
|
159
|
+
deleteEvents: async (ids) => {
|
|
160
|
+
const sql = await loadSql();
|
|
161
|
+
await db.transaction(async (tx) => {
|
|
162
|
+
const eventsDef = sql.storeDefinition("events");
|
|
163
|
+
for (const id of ids) await sql.deleteRows(tx, eventsDef, id);
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
function sqliteNoteStore(db) {
|
|
169
|
+
return {
|
|
170
|
+
getAllNotes: async () => await allRows(await loadSql(), db, "notes"),
|
|
171
|
+
getNotesByIds: async (noteIds) => {
|
|
172
|
+
const sql = await loadSql();
|
|
173
|
+
const rows = await sql.getRows(db, sql.storeDefinition("notes"), [...noteIds]);
|
|
174
|
+
return rows.filter((row) => row !== void 0);
|
|
175
|
+
},
|
|
176
|
+
getNoteIdsForTag: async (label) => {
|
|
177
|
+
const sql = await loadSql();
|
|
178
|
+
const tags = (await allRows(sql, db, "tags")).filter((t) => t.label === label);
|
|
179
|
+
const ids = {};
|
|
180
|
+
for (const tag of tags) {
|
|
181
|
+
for (const row of await rowsByIndex(sql, db, "note_tags", "by-tag", tag.id)) {
|
|
182
|
+
ids[row.noteId] = true;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return new Set(Object.keys(ids));
|
|
186
|
+
},
|
|
187
|
+
getNoteTagLabels: async (noteId) => {
|
|
188
|
+
const sql = await loadSql();
|
|
189
|
+
const tagRows = await allRows(sql, db, "tags");
|
|
190
|
+
const labelById = {};
|
|
191
|
+
for (const t of tagRows) labelById[t.id] = t.label;
|
|
192
|
+
const links = await rowsByIndex(sql, db, "note_tags", "by-note", noteId);
|
|
193
|
+
return links.map((l) => labelById[l.tagId]).filter((l) => typeof l === "string");
|
|
194
|
+
},
|
|
195
|
+
queryDomain: async (request) => {
|
|
196
|
+
const result = await (await loadSql()).handleDomainQuery(db, request);
|
|
197
|
+
return result;
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
function sqliteBlockStore(db) {
|
|
202
|
+
return {
|
|
203
|
+
getAllBlocks: async () => await allRows(await loadSql(), db, "block_index"),
|
|
204
|
+
queryDomain: async (request) => {
|
|
205
|
+
const result = await (await loadSql()).handleDomainQuery(db, { ...request, plan: "blocks" });
|
|
206
|
+
return result;
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
function sqliteEffortStore(db) {
|
|
211
|
+
return {
|
|
212
|
+
getAllEfforts: async () => {
|
|
213
|
+
const stored = await allRows(await loadSql(), db, "efforts");
|
|
214
|
+
const bySlug = {};
|
|
215
|
+
for (const effort of stored) bySlug[effort.slug] = effort;
|
|
216
|
+
for (const effort of bundledEfforts) {
|
|
217
|
+
bySlug[effort.slug] ??= effort;
|
|
218
|
+
}
|
|
219
|
+
return Object.values(bySlug);
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
function sqliteIndexStore(db, store) {
|
|
224
|
+
return {
|
|
225
|
+
getAllFromIndex: async (index, key) => rowsByIndex(await loadSql(), db, store, index, key)
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
async function getNoteBundle(db, noteId) {
|
|
229
|
+
const sql = await loadSql();
|
|
230
|
+
const note = await sql.getRow(db, sql.storeDefinition("notes"), noteId);
|
|
231
|
+
return { note };
|
|
232
|
+
}
|
|
233
|
+
async function getSessionBundle(db, resultId) {
|
|
234
|
+
const sql = await loadSql();
|
|
235
|
+
const session = await sql.getRow(db, sql.storeDefinition("sessions"), resultId);
|
|
236
|
+
if (!session) return void 0;
|
|
237
|
+
const events = await sql.listRows(db, sql.storeDefinition("events"), { only: resultId }, void 0, "by-result-grain");
|
|
238
|
+
return { session, events };
|
|
239
|
+
}
|
|
240
|
+
async function getEffortBySlug(db, slug) {
|
|
241
|
+
const sql = await loadSql();
|
|
242
|
+
const efforts = await allRows(sql, db, "efforts");
|
|
243
|
+
return efforts.find((e) => e.slug === slug);
|
|
244
|
+
}
|
|
245
|
+
async function listTagsByType(db, type) {
|
|
246
|
+
const sql = await loadSql();
|
|
247
|
+
const tags = await allRows(sql, db, "tags");
|
|
248
|
+
return tags.filter((t) => t.type === type);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
export { CLI_UUID_NAMESPACE, getEffortBySlug, getNoteBundle, getSessionBundle, listTagsByType, loadSql, nodeFs, nodePath, openCliDb, randomId, sqliteBlockStore, sqliteEffortStore, sqliteEventStore, sqliteIndexStore, sqliteNoteStore, stableNoteId, uuid5 };
|
|
252
|
+
//# sourceMappingURL=chunk-EUQWEX6Z.mjs.map
|
|
253
|
+
//# sourceMappingURL=chunk-EUQWEX6Z.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli/db.ts"],"names":[],"mappings":"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","file":"chunk-EUQWEX6Z.mjs","sourcesContent":["/**\n * Local SQLite seam for the `wod` CLI.\n *\n * Opens a libsql `file:` database, initializes the shared storage schema\n * (KV store tables + domain projections), and adapts the rows into the WQL\n * store interfaces — the same shape the playground builds over IndexedDB.\n *\n * Everything heavy is loaded lazily (`@libsql/client`,\n * `@bitcobblers/wod-wiki-storage/sql`, node `fs`/`crypto`) so browser\n * bundles that import the engine index never pull native drivers; the\n * dynamic specifiers stay external in the CLI bundle.\n */\n\nimport type { EventRecord } from '@bitcobblers/wod-wiki-core';\nimport type { IStorage, KeyDTO, Note, Session } from '@bitcobblers/wod-wiki-storage';\nimport type { RangeDTO, StoreDef } from '@bitcobblers/wod-wiki-storage';\nimport type {\n Database,\n LibsqlClient,\n SqlConnection,\n} from '@bitcobblers/wod-wiki-storage/sql';\nimport type { CliDatabaseConfig } from './config';\nimport type {\n BlockQueryStore,\n EventStore,\n EffortQueryStore,\n NoteQueryStore,\n WqlDomainReadResult,\n} from '@bitcobblers/wod-wiki-wql';\nimport { bundledEfforts } from '@bitcobblers/wod-wiki-lang';\n\n/** Structural CJS require — avoids the deprecated NodeJS namespace type. */\ntype CjsRequire = (id: string) => unknown;\n\n/** Minimal node builtin surfaces the CLI uses (kept out of browser bundles). */\nexport interface PathShim { resolve(...parts: string[]): string; join(...parts: string[]): string; dirname(p: string): string; basename(p: string): string }\nexport interface FsShim {\n mkdirSync(p: string, opts: { recursive: boolean }): void;\n existsSync(p: string): boolean;\n unlinkSync(p: string): void;\n readFileSync(p: string, encoding: string): string;\n writeFileSync(p: string, data: string, encoding: string): void;\n}\nexport interface HashShim { update(data: string): HashShim; digest(): Uint8Array }\nexport interface CryptoShim {\n createHash(algorithm: string): HashShim;\n randomUUID(): string;\n}\nexport const nodePath = (): PathShim => nodeRequire()('node:path') as PathShim;\nexport const nodeFs = (): FsShim => nodeRequire()('node:fs') as FsShim;\nexport const nodeCrypto = (): CryptoShim => nodeRequire()('node:crypto') as CryptoShim;\n\n/** Lazy CJS require — keeps node builtins out of browser bundles. */\nfunction nodeRequire(): CjsRequire {\n const g = globalThis as { process?: { versions?: Record<string, string> }; Bun?: unknown; require?: CjsRequire };\n if (typeof g.process?.versions?.node === 'undefined' && typeof g.Bun === 'undefined') {\n throw new Error('SQLite operations are only supported in Node / Bun environments');\n }\n const req = g.require ?? (typeof require === 'function' ? require : undefined);\n if (!req) throw new Error('SQLite operations require a CJS require shim (missing tsup banner?)');\n return req;\n}\n\n/** The storage/sql surface the CLI uses — structural mirror so the lazy import types check. */\nexport interface SqlApi {\n libsqlDatabase(client: LibsqlClient): Database;\n SqlStorage: new (db: Database) => IStorage;\n initializeStore(db: Database): Promise<void>;\n initializeDomain(db: Database): Promise<void>;\n verifyReadiness(db: Database): Promise<void>;\n storeDefinition(name: string): StoreDef;\n listRows(conn: SqlConnection, def: StoreDef, range?: RangeDTO, count?: number, indexName?: string): Promise<unknown[]>;\n getRow(conn: SqlConnection, def: StoreDef, key: KeyDTO): Promise<unknown | undefined>;\n getRows(conn: SqlConnection, def: StoreDef, keys: KeyDTO[]): Promise<(unknown | undefined)[]>;\n putRow(conn: SqlConnection, def: StoreDef, value: unknown): Promise<unknown>;\n deleteRows(conn: SqlConnection, def: StoreDef, keyOrRange: unknown): Promise<void>;\n refreshDomain(conn: SqlConnection, ops: unknown[]): Promise<unknown>;\n handleDomainQuery(db: Database, body: unknown): Promise<unknown>;\n}\n\nlet sqlModulePromise: Promise<SqlApi> | undefined;\n\n/** Lazily import the storage SQL subpath (kept external in the CLI bundle). */\nexport function loadSql(): Promise<SqlApi> {\n sqlModulePromise ??= import(/* @vite-ignore */ '@bitcobblers/wod-wiki-storage/sql') as Promise<SqlApi>;\n return sqlModulePromise;\n}\n\nexport interface CliDb {\n db: Database;\n storage: IStorage;\n /** Absolute path of the underlying .sqlite file — or the sanitized display\n * label (URL without credentials) for remote backends. */\n path: string;\n close(): Promise<void>;\n}\n\n/**\n * Resolve the effective database path: explicit flag wins, then\n * WOD_WIKI_DB, then ./wodwiki.db in the current working directory.\n */\nexport function resolveDbPath(dbPath?: string): string {\n const chosen = dbPath ?? process.env.WOD_WIKI_DB ?? './wodwiki.db';\n return nodePath().resolve(process.cwd(), chosen);\n}\n\n/**\n * Open the CLI database: a legacy local sqlite path (internal callers,\n * created/initialized as before) or a resolved backend config from ./config.\n *\n * opts.initialize (default true): run the schema + projection\n * initialization. false → verify the schema/projection guards only: a\n * missing local sqlite file is NOT created and yields an actionable\n * `wod init` error; remote targets are only probed, never created.\n *\n * opts.create (explicit init requests only, postgres): when the configured\n * PostgreSQL database is missing, openDatabase creates it through the admin\n * \"postgres\" database before initializing. Turso has no management API —\n * its hosted target must already exist.\n */\nexport async function openCliDb(\n target?: string | CliDatabaseConfig,\n opts?: { initialize?: boolean; create?: boolean },\n): Promise<CliDb> {\n if (target === undefined || typeof target === 'string') {\n return openSqliteDb(resolveDbPath(target), opts);\n }\n switch (target.kind) {\n case 'sqlite':\n return openSqliteDb(target.path, opts);\n case 'turso':\n return openRemoteDb(\n { DB_DRIVER: 'turso', DATABASE_URL: target.url, LIBSQL_AUTH_TOKEN: target.authToken },\n target.label,\n opts,\n );\n case 'postgres':\n return openRemoteDb({ DB_DRIVER: 'postgres', DATABASE_URL: target.url }, target.label, opts);\n }\n}\n\nasync function openSqliteDb(resolved: string, opts?: { initialize?: boolean; create?: boolean }): Promise<CliDb> {\n const [{ createClient }, sql] = await Promise.all([\n import(/* @vite-ignore */ '@libsql/client') as Promise<{ createClient: (config: { url: string }) => LibsqlClient }>,\n loadSql(),\n ]);\n const initialize = opts?.initialize ?? true;\n if (!initialize && !nodeFs().existsSync(resolved)) {\n throw new Error(`[wod] no database at ${resolved} — run \\`wod init\\` to create and initialize it`);\n }\n if (initialize) nodeFs().mkdirSync(nodePath().dirname(resolved), { recursive: true });\n const db = sql.libsqlDatabase(createClient({ url: `file:${resolved}` }));\n try {\n if (initialize) {\n await sql.initializeStore(db);\n await sql.initializeDomain(db);\n } else {\n // Guards only — no schema creation on an existing file either.\n await sql.verifyReadiness(db).catch((error: unknown) => {\n const message = error instanceof Error ? error.message : String(error);\n throw new Error(`${message} — run \\`wod init\\` to initialize it`, { cause: error });\n });\n }\n return { db, storage: new sql.SqlStorage(db), path: resolved, close: () => db.close() };\n } catch (error) {\n await db.close();\n throw error;\n }\n}\n\n/** Open a postgres/turso/remote-sqlite backend through the extracted server\n * adapter (dynamic on purpose: node-only subpath kept out of browser\n * bundles and external in the CLI bundle). Never falls back locally. */\nasync function openRemoteDb(\n env: Record<string, string | undefined>,\n label: string,\n opts?: { initialize?: boolean; create?: boolean },\n): Promise<CliDb> {\n const [{ openDatabase }, sql] = await Promise.all([\n import(/* @vite-ignore */ '@bitcobblers/wod-wiki-storage/sql/node') as Promise<{\n openDatabase: (env?: Record<string, string | undefined>, opts?: { create?: boolean }) => Promise<Database>;\n }>,\n loadSql(),\n ]);\n let db: Database | undefined;\n try {\n db = await openDatabase(env, { create: opts?.create });\n if (opts?.initialize === false) {\n await sql.verifyReadiness(db);\n } else {\n await sql.initializeStore(db);\n await sql.initializeDomain(db);\n }\n } catch (error) {\n await db?.close().catch(() => {});\n const message = error instanceof Error ? error.message : String(error);\n throw new Error(\n `[wod] ${label}: ${message}${opts?.initialize === false ? ' — run `wod init` to initialize it' : ''}`,\n { cause: error },\n );\n }\n return { db, storage: new sql.SqlStorage(db), path: label, close: () => db.close() };\n}\n\n// ---------------------------------------------------------------------------\n// Identity helpers\n// ---------------------------------------------------------------------------\n\n/** RFC-4122 byte munging + formatting over a 16-byte digest slice. */\nexport function formatV5Digest(digest: Uint8Array): string {\n const bytes = digest.slice(0, 16);\n bytes[6] = (bytes[6] & 0x0f) | 0x50; // version 5\n bytes[8] = (bytes[8] & 0x3f) | 0x80; // RFC-4122 variant\n const hex = [...bytes].map((b) => b.toString(16).padStart(2, '0'));\n 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('')}`;\n}\n\n/** RFC-4122 v5 (SHA-1) name-based UUID — deterministic seed/note identity. */\nexport function uuid5(namespace: string, name: string): string {\n return formatV5Digest(nodeCrypto().createHash('sha1').update(namespace).update(name).digest());\n}\n\n/** Local namespace for wod-CLI derived identities (not shared with seed ids). */\nexport const CLI_UUID_NAMESPACE = 'c0ffee00-8b1a-4e2c-9d3f-5a6b7c8d9e0f';\n\n/**\n * Stable note identity for a parsed/run source: the same file always maps\n * to the same note, so re-parsing updates instead of duplicating. Stdin\n * sources content-address (no stable path exists).\n */\nexport function stableNoteId(sourceLabel: string, content: string): string {\n if (sourceLabel && sourceLabel !== '-') {\n return uuid5(CLI_UUID_NAMESPACE, `wod-cli:${nodePath().resolve(process.cwd(), sourceLabel)}`);\n }\n return uuid5(CLI_UUID_NAMESPACE, `wod-cli:stdin:${content.length}:${content.slice(0, 256)}`);\n}\n\nexport function randomId(): string {\n return nodeCrypto().randomUUID();\n}\n\n// ---------------------------------------------------------------------------\n// WQL store adapters over the sqlite rows\n// ---------------------------------------------------------------------------\n\nasync function allRows(sql: SqlApi, db: Database, store: string): Promise<unknown[]> {\n return sql.listRows(db, sql.storeDefinition(store));\n}\n\nasync function rowsByIndex(sql: SqlApi, db: Database, store: string, index: string, only: string): Promise<unknown[]> {\n return sql.listRows(db, sql.storeDefinition(store), { only }, undefined, index);\n}\n\n/** EventStore over the `events` table — whole-table reads, keyed writes. */\nexport function sqliteEventStore(db: Database): EventStore {\n const all = async (): Promise<EventRecord[]> => (await allRows(await loadSql(), db, 'events')) as EventRecord[];\n return {\n getEventsByTimeRange: async (start, end) => (await all()).filter((r) => r.timestamp >= start && r.timestamp <= end),\n getEventsByResult: async (resultId) => (await all()).filter((r) => r.resultId === resultId),\n getEventsForNote: async (noteId) => (await all()).filter((r) => r.noteId === noteId),\n getEventsByContent: async (blockContentId) => (await all()).filter((r) => r.blockContentId === blockContentId),\n scanAll: all,\n appendEvents: async (appended) => {\n const sql = await loadSql();\n await db.transaction(async (tx) => {\n const eventsDef = sql.storeDefinition('events');\n for (const row of appended) await sql.putRow(tx, eventsDef, row);\n });\n },\n finalizeSummaries: async (resultId, finals) => {\n const sql = await loadSql();\n await db.transaction(async (tx) => {\n const eventsDef = sql.storeDefinition('events');\n const existing = ((await sql.listRows(tx, eventsDef)) as EventRecord[]).filter(\n (r) => r.resultId === resultId && r.grain === 'summary' && r.origin !== 'user',\n );\n for (const row of existing) await sql.deleteRows(tx, eventsDef, row.id);\n for (const row of finals) await sql.putRow(tx, eventsDef, row);\n });\n },\n deleteEvents: async (ids) => {\n const sql = await loadSql();\n await db.transaction(async (tx) => {\n const eventsDef = sql.storeDefinition('events');\n for (const id of ids) await sql.deleteRows(tx, eventsDef, id);\n });\n },\n };\n}\n\ninterface StoredTag { id: string; label: string; type?: string }\ninterface StoredNoteTag { id: string; noteId: string; tagId: string }\n\n/** NoteQueryStore over the notes/tags/note_tags rows + domain projection reads. */\nexport function sqliteNoteStore(db: Database): NoteQueryStore {\n return {\n getAllNotes: async () => (await allRows(await loadSql(), db, 'notes')) as never,\n getNotesByIds: async (noteIds) => {\n const sql = await loadSql();\n const rows = await sql.getRows(db, sql.storeDefinition('notes'), [...noteIds]) as Array<Note | undefined>;\n return rows.filter((row): row is Note => row !== undefined);\n },\n getNoteIdsForTag: async (label) => {\n const sql = await loadSql();\n const tags = ((await allRows(sql, db, 'tags')) as StoredTag[]).filter((t) => t.label === label);\n const ids: Record<string, true> = {};\n for (const tag of tags) {\n for (const row of await rowsByIndex(sql, db, 'note_tags', 'by-tag', tag.id)) {\n ids[(row as StoredNoteTag).noteId] = true;\n }\n }\n return new Set(Object.keys(ids));\n },\n getNoteTagLabels: async (noteId) => {\n const sql = await loadSql();\n const tagRows = (await allRows(sql, db, 'tags')) as StoredTag[];\n const labelById: Record<string, string> = {};\n for (const t of tagRows) labelById[t.id] = t.label;\n const links = (await rowsByIndex(sql, db, 'note_tags', 'by-note', noteId)) as StoredNoteTag[];\n return links.map((l) => labelById[l.tagId]).filter((l): l is string => typeof l === 'string');\n },\n queryDomain: async (request) => {\n // WQL's domain wire is a structural mirror of the storage DomainQuery.\n const result = await (await loadSql()).handleDomainQuery(db, request as never);\n return result as WqlDomainReadResult;\n },\n };\n}\n\n/** BlockQueryStore over the block_index rows + domain projection reads. */\nexport function sqliteBlockStore(db: Database): BlockQueryStore {\n return {\n getAllBlocks: async () => (await allRows(await loadSql(), db, 'block_index')) as never,\n queryDomain: async (request) => {\n const result = await (await loadSql()).handleDomainQuery(db, { ...request, plan: 'blocks' } as never);\n return result as WqlDomainReadResult;\n },\n };\n}\n\ninterface Slugged { slug: string }\n\n/**\n * EffortQueryStore: rows the seed wrote into the `efforts` registry win by\n * slug; the lang-bundled registry fills the rest (same fallback the\n * playground's RegistryEffortStore performs).\n */\nexport function sqliteEffortStore(db: Database): EffortQueryStore {\n return {\n getAllEfforts: async () => {\n const stored = (await allRows(await loadSql(), db, 'efforts')) as Slugged[];\n const bySlug: Record<string, unknown> = {};\n for (const effort of stored) bySlug[effort.slug] = effort;\n for (const effort of bundledEfforts as unknown as Slugged[]) {\n bySlug[effort.slug] ??= effort;\n }\n return Object.values(bySlug) as never;\n },\n };\n}\n\n/** Generic by-index reader over a store table — the QueryService seam shape. */\nexport interface IndexStore { getAllFromIndex(index: string, key: string): Promise<unknown[]> }\n\n/** Indexed reads over `block_efforts` / `tags` / `note_tags` rows. */\nexport function sqliteIndexStore(db: Database, store: 'block_efforts' | 'tags' | 'note_tags'): IndexStore {\n return {\n getAllFromIndex: async (index, key) => rowsByIndex(await loadSql(), db, store, index, key),\n };\n}\n\n// ---------------------------------------------------------------------------\n// Download bundles (exchange feedstock)\n// ---------------------------------------------------------------------------\n\nexport interface NoteBundle {\n note: Note | undefined;\n}\n\nexport interface SessionBundle {\n session: Session;\n events: EventRecord[];\n}\n\n/** Fetch the whole authored Note. */\nexport async function getNoteBundle(db: Database, noteId: string): Promise<NoteBundle> {\n const sql = await loadSql();\n const note = await sql.getRow(db, sql.storeDefinition('notes'), noteId) as Note | undefined;\n return { note };\n}\n\n/** Fetch a session + its event/summary rows. */\nexport async function getSessionBundle(db: Database, resultId: string): Promise<SessionBundle | undefined> {\n const sql = await loadSql();\n const session = await sql.getRow(db, sql.storeDefinition('sessions'), resultId) as Session | undefined;\n if (!session) return undefined;\n const events = await sql.listRows(db, sql.storeDefinition('events'), { only: resultId }, undefined, 'by-result-grain') as EventRecord[];\n return { session, events };\n}\n\n/** Fetch one effort registry row by slug. */\nexport async function getEffortBySlug(db: Database, slug: string): Promise<Record<string, unknown> | undefined> {\n const sql = await loadSql();\n const efforts = (await allRows(sql, db, 'efforts')) as Array<{ slug: string } & Record<string, unknown>>;\n return efforts.find((e) => e.slug === slug);\n}\n\n/** Fetch tag rows of one type (e.g. 'equipment'). */\nexport async function listTagsByType(db: Database, type: string): Promise<Array<{ id: string; label: string; type?: string }>> {\n const sql = await loadSql();\n const tags = (await allRows(sql, db, 'tags')) as Array<{ id: string; label: string; type?: string }>;\n return tags.filter((t) => t.type === type);\n}\n"]}
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
import { createRequire } from 'module';
|
|
2
|
+
import { resolve, sep, join } from 'path';
|
|
3
|
+
import { stat, realpath } from 'fs/promises';
|
|
4
|
+
import { realpathSync } from 'fs';
|
|
5
|
+
import { StorageError, wipeRows, wipeDomain, refreshDomain, handleDomainQuery, storeDefinition, MAX_BODY, getRow, listRows, countRows } from '@bitcobblers/wod-wiki-storage/sql';
|
|
6
|
+
import { keyFromValue } from '@bitcobblers/wod-wiki-storage';
|
|
7
|
+
|
|
8
|
+
if (typeof globalThis.require === 'undefined') {
|
|
9
|
+
try { globalThis.require = createRequire(import.meta.url); } catch {}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// src/server/errors.ts
|
|
13
|
+
var HttpError = class extends Error {
|
|
14
|
+
constructor(status, message, code) {
|
|
15
|
+
super(message);
|
|
16
|
+
this.status = status;
|
|
17
|
+
this.code = code;
|
|
18
|
+
this.name = "HttpError";
|
|
19
|
+
}
|
|
20
|
+
status;
|
|
21
|
+
code;
|
|
22
|
+
};
|
|
23
|
+
var json = (value, status) => new Response(JSON.stringify(value), { status, headers: { "content-type": "application/json" } });
|
|
24
|
+
function bunFile(path) {
|
|
25
|
+
const bun = globalThis;
|
|
26
|
+
if (!bun.Bun) throw new Error("the wod-wiki server handler requires the Bun runtime (https://bun.sh)");
|
|
27
|
+
return bun.Bun.file(path);
|
|
28
|
+
}
|
|
29
|
+
var plain = (body, status) => new Response(body, { status, headers: { "content-type": "text/plain; charset=utf-8" } });
|
|
30
|
+
var notFound = () => json({ error: "not found" }, 404);
|
|
31
|
+
var methodNotAllowed = () => json({ error: "method not allowed" }, 405);
|
|
32
|
+
var noContent = () => new Response(null, { status: 204 });
|
|
33
|
+
var qget = (url, name) => url.searchParams.get(name) ?? void 0;
|
|
34
|
+
function jsonParam(url, name) {
|
|
35
|
+
const raw = qget(url, name);
|
|
36
|
+
if (raw === void 0) return void 0;
|
|
37
|
+
try {
|
|
38
|
+
return JSON.parse(raw);
|
|
39
|
+
} catch {
|
|
40
|
+
throw new HttpError(400, `invalid JSON in ?${name}`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
function rangeParam(url) {
|
|
44
|
+
const v = jsonParam(url, "range");
|
|
45
|
+
if (v === void 0) return void 0;
|
|
46
|
+
if (v !== null && typeof v === "object" && !Array.isArray(v)) return v;
|
|
47
|
+
return { only: v };
|
|
48
|
+
}
|
|
49
|
+
function countParam(url) {
|
|
50
|
+
const raw = qget(url, "count");
|
|
51
|
+
if (raw === void 0) return void 0;
|
|
52
|
+
const n = Number(raw);
|
|
53
|
+
if (!Number.isFinite(n) || n < 0) throw new HttpError(400, "invalid ?count");
|
|
54
|
+
return Math.trunc(n);
|
|
55
|
+
}
|
|
56
|
+
async function readJsonBody(req, maxBytes) {
|
|
57
|
+
const body = await req.text();
|
|
58
|
+
if (maxBytes !== void 0 && body.length > maxBytes) {
|
|
59
|
+
throw new HttpError(413, `domain query body exceeds ${maxBytes} bytes`);
|
|
60
|
+
}
|
|
61
|
+
try {
|
|
62
|
+
return JSON.parse(body);
|
|
63
|
+
} catch {
|
|
64
|
+
throw new HttpError(400, "body must be valid JSON");
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
function txOps(value) {
|
|
68
|
+
if (!Array.isArray(value)) throw new HttpError(400, "tx body must be {ops: []}");
|
|
69
|
+
return value.map((op) => {
|
|
70
|
+
if (op === null || typeof op !== "object" || Array.isArray(op) || !("store" in op) || typeof op.store !== "string" || !("op" in op)) {
|
|
71
|
+
throw new HttpError(400, "tx op must have a store and operation");
|
|
72
|
+
}
|
|
73
|
+
const def = storeDefinition(op.store);
|
|
74
|
+
const store = def.name;
|
|
75
|
+
switch (op.op) {
|
|
76
|
+
case "put":
|
|
77
|
+
if (!("value" in op)) throw new HttpError(400, "put requires value");
|
|
78
|
+
return { op: "put", store, value: op.value };
|
|
79
|
+
case "delete":
|
|
80
|
+
if (!("key" in op)) throw new HttpError(400, "delete requires key");
|
|
81
|
+
return { op: "delete", store, key: op.key };
|
|
82
|
+
case "clear":
|
|
83
|
+
return { op: "clear", store };
|
|
84
|
+
case "check": {
|
|
85
|
+
if (!("key" in op) || !("value" in op)) throw new HttpError(400, "check requires key and value");
|
|
86
|
+
const scalar = (key) => typeof key === "string" || typeof key === "number" && Number.isFinite(key);
|
|
87
|
+
if (!scalar(op.key) && !(Array.isArray(op.key) && op.key.length > 0 && op.key.every(scalar))) {
|
|
88
|
+
throw new HttpError(400, "check requires a scalar key or non-empty scalar tuple");
|
|
89
|
+
}
|
|
90
|
+
if (op.value !== null && (typeof op.value !== "object" || Array.isArray(op.value))) {
|
|
91
|
+
throw new HttpError(400, "check value must be a row or null");
|
|
92
|
+
}
|
|
93
|
+
const parts = Array.isArray(op.key) ? op.key : [op.key];
|
|
94
|
+
if (parts.length !== def.keyPath.length) throw new HttpError(400, "check key arity must match the store");
|
|
95
|
+
if (op.value !== null) {
|
|
96
|
+
let rowKey;
|
|
97
|
+
try {
|
|
98
|
+
rowKey = keyFromValue(def, op.value);
|
|
99
|
+
} catch {
|
|
100
|
+
throw new HttpError(400, "check row must have its primary key");
|
|
101
|
+
}
|
|
102
|
+
if (JSON.stringify(rowKey) !== JSON.stringify(op.key)) throw new HttpError(400, "check row primary key must match key");
|
|
103
|
+
}
|
|
104
|
+
return { op: "check", store, key: op.key, value: op.value };
|
|
105
|
+
}
|
|
106
|
+
default:
|
|
107
|
+
throw new HttpError(400, "tx op must be put | delete | clear | check");
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
var MIME = {
|
|
112
|
+
html: "text/html; charset=utf-8",
|
|
113
|
+
js: "text/javascript; charset=utf-8",
|
|
114
|
+
mjs: "text/javascript; charset=utf-8",
|
|
115
|
+
css: "text/css; charset=utf-8",
|
|
116
|
+
json: "application/json; charset=utf-8",
|
|
117
|
+
map: "application/json; charset=utf-8",
|
|
118
|
+
svg: "image/svg+xml",
|
|
119
|
+
png: "image/png",
|
|
120
|
+
jpg: "image/jpeg",
|
|
121
|
+
jpeg: "image/jpeg",
|
|
122
|
+
webp: "image/webp",
|
|
123
|
+
ico: "image/x-icon",
|
|
124
|
+
woff: "font/woff",
|
|
125
|
+
woff2: "font/woff2",
|
|
126
|
+
ttf: "font/ttf",
|
|
127
|
+
mp3: "audio/mpeg",
|
|
128
|
+
wav: "audio/wav",
|
|
129
|
+
webmanifest: "application/manifest+json",
|
|
130
|
+
xml: "application/xml",
|
|
131
|
+
txt: "text/plain; charset=utf-8"
|
|
132
|
+
};
|
|
133
|
+
async function realpathSafe(path) {
|
|
134
|
+
try {
|
|
135
|
+
return await realpath(path);
|
|
136
|
+
} catch {
|
|
137
|
+
return void 0;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
async function serveFile(real, rel) {
|
|
141
|
+
const cache = rel.includes("seed/chunks/") ? "public, max-age=31536000, immutable" : "no-cache";
|
|
142
|
+
const ext = rel.slice(rel.lastIndexOf(".") + 1).toLowerCase();
|
|
143
|
+
return new Response(bunFile(real), {
|
|
144
|
+
headers: { "content-type": MIME[ext] ?? "application/octet-stream", "cache-control": cache }
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
async function serveSpaShell(distRoot) {
|
|
148
|
+
const index = await realpathSafe(join(distRoot, "index.html"));
|
|
149
|
+
if (index && (index === distRoot || index.startsWith(distRoot + sep)) && await stat(index).then((s) => s.isFile(), () => false)) {
|
|
150
|
+
return new Response(bunFile(index), {
|
|
151
|
+
headers: { "content-type": MIME.html, "cache-control": "no-cache" }
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
return void 0;
|
|
155
|
+
}
|
|
156
|
+
async function staticResponse(distRoot, method, pathname) {
|
|
157
|
+
if (method !== "GET") return plain("method not allowed", 405);
|
|
158
|
+
if (!distRoot) {
|
|
159
|
+
return plain("wod-wiki-api: playground dist not found. Set PLAYGROUND_DIST or run the playground server build.", 503);
|
|
160
|
+
}
|
|
161
|
+
let rel;
|
|
162
|
+
try {
|
|
163
|
+
rel = decodeURIComponent(pathname).replace(/^\/+/, "");
|
|
164
|
+
} catch {
|
|
165
|
+
return plain("bad request", 400);
|
|
166
|
+
}
|
|
167
|
+
if (rel === "") rel = "index.html";
|
|
168
|
+
const parts = rel.split("/");
|
|
169
|
+
if (parts.some((p) => p === "..")) return plain("forbidden", 403);
|
|
170
|
+
const base = parts[parts.length - 1] ?? "";
|
|
171
|
+
const hasExtension = base.includes(".");
|
|
172
|
+
const abs = resolve(distRoot, ...parts);
|
|
173
|
+
const real = await realpathSafe(abs);
|
|
174
|
+
if (real && (real === distRoot || real.startsWith(distRoot + sep))) {
|
|
175
|
+
const info = await stat(real).then((s) => s.isFile(), () => false);
|
|
176
|
+
if (info) return serveFile(real, rel);
|
|
177
|
+
}
|
|
178
|
+
if (!hasExtension) {
|
|
179
|
+
const shell = await serveSpaShell(distRoot);
|
|
180
|
+
if (shell) return shell;
|
|
181
|
+
}
|
|
182
|
+
return plain("not found", 404);
|
|
183
|
+
}
|
|
184
|
+
async function storeRoutes(db, method, sub, url, req) {
|
|
185
|
+
let inner = sub.slice("/v1/".length);
|
|
186
|
+
if (inner.endsWith("/")) inner = inner.slice(0, -1);
|
|
187
|
+
if (inner === "") return notFound();
|
|
188
|
+
const segments = inner.split("/");
|
|
189
|
+
let name;
|
|
190
|
+
let action;
|
|
191
|
+
if (segments.length === 1) {
|
|
192
|
+
name = segments[0];
|
|
193
|
+
} else if (segments.length === 2) {
|
|
194
|
+
[name, action] = segments;
|
|
195
|
+
} else if (segments.length === 3 && segments[1] === "index" && segments[2] !== "") {
|
|
196
|
+
[name, , action] = segments;
|
|
197
|
+
action = `index/${action}`;
|
|
198
|
+
} else {
|
|
199
|
+
return notFound();
|
|
200
|
+
}
|
|
201
|
+
const def = storeDefinition(name);
|
|
202
|
+
const range = rangeParam(url);
|
|
203
|
+
const count = countParam(url);
|
|
204
|
+
if (action === "get" && method === "GET") {
|
|
205
|
+
const key = jsonParam(url, "key");
|
|
206
|
+
if (key === void 0) throw new HttpError(400, "missing ?key");
|
|
207
|
+
const row = await getRow(db, def, key);
|
|
208
|
+
return row === void 0 ? notFound() : json(row, 200);
|
|
209
|
+
}
|
|
210
|
+
if (action === "all" && method === "GET") {
|
|
211
|
+
return json(await listRows(db, def, range, count), 200);
|
|
212
|
+
}
|
|
213
|
+
if (action === "count" && method === "GET") {
|
|
214
|
+
const indexName = qget(url, "index");
|
|
215
|
+
const n = indexName ? await countRows(db, def, range, indexName) : await countRows(db, def, range);
|
|
216
|
+
return json({ count: Number(n) }, 200);
|
|
217
|
+
}
|
|
218
|
+
if (action === "clear" && method === "POST") {
|
|
219
|
+
await db.transaction((tx) => refreshDomain(tx, [{ op: "clear", store: def.name }]));
|
|
220
|
+
return noContent();
|
|
221
|
+
}
|
|
222
|
+
if (action?.startsWith("index/") && method === "GET") {
|
|
223
|
+
return json(await listRows(db, def, range, count, action.slice("index/".length)), 200);
|
|
224
|
+
}
|
|
225
|
+
if (action === void 0 && method === "PUT") {
|
|
226
|
+
const parsed = await readJsonBody(req);
|
|
227
|
+
const value = typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) && "value" in parsed ? parsed.value : void 0;
|
|
228
|
+
if (value === void 0) throw new HttpError(400, "body must be {value: ...}");
|
|
229
|
+
const [key] = await db.transaction((tx) => refreshDomain(tx, [{ op: "put", store: def.name, value }]));
|
|
230
|
+
return json({ key }, 200);
|
|
231
|
+
}
|
|
232
|
+
if (action === void 0 && method === "DELETE") {
|
|
233
|
+
const keyParam = jsonParam(url, "key");
|
|
234
|
+
const target = keyParam !== void 0 ? keyParam : range;
|
|
235
|
+
if (target === void 0 || target === null) throw new HttpError(400, "DELETE requires ?key or ?range");
|
|
236
|
+
await db.transaction((tx) => refreshDomain(tx, [{ op: "delete", store: def.name, key: target }]));
|
|
237
|
+
return noContent();
|
|
238
|
+
}
|
|
239
|
+
return methodNotAllowed();
|
|
240
|
+
}
|
|
241
|
+
async function apiRoutes(db, req, method, sub, url) {
|
|
242
|
+
if (sub === "/health") {
|
|
243
|
+
return json({ ok: true, driver: db.dialect }, 200);
|
|
244
|
+
}
|
|
245
|
+
if (sub === "/v1/wipe" && method === "POST") {
|
|
246
|
+
await db.transaction(async (tx) => {
|
|
247
|
+
await wipeRows(tx);
|
|
248
|
+
await wipeDomain(tx);
|
|
249
|
+
});
|
|
250
|
+
return noContent();
|
|
251
|
+
}
|
|
252
|
+
if (sub === "/v1/tx" && method === "POST") {
|
|
253
|
+
const parsed = await readJsonBody(req);
|
|
254
|
+
const ops = typeof parsed === "object" && parsed !== null && "ops" in parsed ? parsed.ops : void 0;
|
|
255
|
+
const parsedOps = txOps(ops);
|
|
256
|
+
const results = await db.transaction((tx) => refreshDomain(tx, parsedOps));
|
|
257
|
+
return json({ results }, 200);
|
|
258
|
+
}
|
|
259
|
+
if (sub === "/v1/query") {
|
|
260
|
+
if (method !== "POST") return methodNotAllowed();
|
|
261
|
+
const body = await readJsonBody(req, MAX_BODY);
|
|
262
|
+
return json(await handleDomainQuery(db, body), 200);
|
|
263
|
+
}
|
|
264
|
+
if (sub.startsWith("/v1/")) return storeRoutes(db, method, sub, url, req);
|
|
265
|
+
return notFound();
|
|
266
|
+
}
|
|
267
|
+
function distRootOf(playgroundDist) {
|
|
268
|
+
if (!playgroundDist) return void 0;
|
|
269
|
+
try {
|
|
270
|
+
return realpathSync(playgroundDist);
|
|
271
|
+
} catch {
|
|
272
|
+
return void 0;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
function createHandler(db, options) {
|
|
276
|
+
const distRoot = distRootOf(options.playgroundDist);
|
|
277
|
+
return async (request) => {
|
|
278
|
+
try {
|
|
279
|
+
const url = new URL(request.url);
|
|
280
|
+
const method = request.method === "HEAD" ? "GET" : request.method;
|
|
281
|
+
const path = url.pathname;
|
|
282
|
+
const response = path === "/api" || path.startsWith("/api/") ? db ? await apiRoutes(db, request, method, path.slice("/api".length) || "/", url) : notFound() : await staticResponse(distRoot, method, path);
|
|
283
|
+
return response;
|
|
284
|
+
} catch (e) {
|
|
285
|
+
if (e instanceof HttpError || e instanceof StorageError) {
|
|
286
|
+
const status = e.status;
|
|
287
|
+
if (status >= 500) console.error("[wod-wiki-api]", e.message);
|
|
288
|
+
const body = { error: status >= 500 ? "internal error" : e.message };
|
|
289
|
+
if (e.code) body.code = e.code;
|
|
290
|
+
return json(body, status);
|
|
291
|
+
}
|
|
292
|
+
console.error("[wod-wiki-api]", e);
|
|
293
|
+
return json({ error: "internal error" }, 500);
|
|
294
|
+
}
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
export { createHandler };
|
|
299
|
+
//# sourceMappingURL=chunk-K3GZZFAC.mjs.map
|
|
300
|
+
//# sourceMappingURL=chunk-K3GZZFAC.mjs.map
|