@typicalday/firegraph 0.14.1 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/README.md +23 -3
  2. package/dist/{backend-DuvHGgK1.d.cts → backend-BpYLdwCW.d.cts} +1 -1
  3. package/dist/{backend-DuvHGgK1.d.ts → backend-BpYLdwCW.d.ts} +1 -1
  4. package/dist/backend-CvImIwTY.d.cts +137 -0
  5. package/dist/backend-YH5HtawN.d.ts +137 -0
  6. package/dist/backend.d.cts +2 -2
  7. package/dist/backend.d.ts +2 -2
  8. package/dist/{chunk-3AHHXMWX.js → chunk-5HIRYV2S.js} +12 -35
  9. package/dist/chunk-5HIRYV2S.js.map +1 -0
  10. package/dist/{chunk-DJI3VXXA.js → chunk-7IEZ6IYY.js} +2 -2
  11. package/dist/chunk-7IEZ6IYY.js.map +1 -0
  12. package/dist/chunk-FODIMIWY.js +721 -0
  13. package/dist/chunk-FODIMIWY.js.map +1 -0
  14. package/dist/chunk-NGAJCALM.js +34 -0
  15. package/dist/chunk-NGAJCALM.js.map +1 -0
  16. package/dist/chunk-ULRDQ6HZ.js +862 -0
  17. package/dist/chunk-ULRDQ6HZ.js.map +1 -0
  18. package/dist/{client-BKi3vk0Q.d.ts → client-B5o39X79.d.ts} +1 -1
  19. package/dist/{client-BrsaXtDV.d.cts → client-BGHwxwPg.d.cts} +1 -1
  20. package/dist/{client-Bk2Cm6xv.d.cts → client-DoyEdJ5w.d.cts} +1 -1
  21. package/dist/{client-Bk2Cm6xv.d.ts → client-DoyEdJ5w.d.ts} +1 -1
  22. package/dist/cloudflare/index.cjs +148 -158
  23. package/dist/cloudflare/index.cjs.map +1 -1
  24. package/dist/cloudflare/index.d.cts +73 -70
  25. package/dist/cloudflare/index.d.ts +73 -70
  26. package/dist/cloudflare/index.js +53 -588
  27. package/dist/cloudflare/index.js.map +1 -1
  28. package/dist/codegen/index.d.cts +1 -1
  29. package/dist/codegen/index.d.ts +1 -1
  30. package/dist/firestore-enterprise/index.cjs.map +1 -1
  31. package/dist/firestore-enterprise/index.d.cts +3 -3
  32. package/dist/firestore-enterprise/index.d.ts +3 -3
  33. package/dist/firestore-enterprise/index.js +5 -3
  34. package/dist/firestore-enterprise/index.js.map +1 -1
  35. package/dist/firestore-standard/index.cjs.map +1 -1
  36. package/dist/firestore-standard/index.d.cts +3 -3
  37. package/dist/firestore-standard/index.d.ts +3 -3
  38. package/dist/firestore-standard/index.js +3 -2
  39. package/dist/firestore-standard/index.js.map +1 -1
  40. package/dist/index.d.cts +5 -5
  41. package/dist/index.d.ts +5 -5
  42. package/dist/index.js +6 -4
  43. package/dist/index.js.map +1 -1
  44. package/dist/query-client/index.d.cts +2 -2
  45. package/dist/query-client/index.d.ts +2 -2
  46. package/dist/{registry-Bc7h6WTM.d.cts → registry-BGh7Jqpb.d.cts} +2 -2
  47. package/dist/{registry-C2KUPVZj.d.ts → registry-tKTb5Kx1.d.ts} +2 -2
  48. package/dist/sqlite/index.cjs +578 -371
  49. package/dist/sqlite/index.cjs.map +1 -1
  50. package/dist/sqlite/index.d.cts +4 -110
  51. package/dist/sqlite/index.d.ts +4 -110
  52. package/dist/sqlite/index.js +7 -1144
  53. package/dist/sqlite/index.js.map +1 -1
  54. package/dist/sqlite/local.cjs +1835 -0
  55. package/dist/sqlite/local.cjs.map +1 -0
  56. package/dist/sqlite/local.d.cts +83 -0
  57. package/dist/sqlite/local.d.ts +83 -0
  58. package/dist/sqlite/local.js +121 -0
  59. package/dist/sqlite/local.js.map +1 -0
  60. package/package.json +15 -1
  61. package/dist/chunk-3AHHXMWX.js.map +0 -1
  62. package/dist/chunk-DJI3VXXA.js.map +0 -1
  63. package/dist/chunk-NNBSUOOF.js +0 -289
  64. package/dist/chunk-NNBSUOOF.js.map +0 -1
@@ -1,15 +1,23 @@
1
1
  import {
2
2
  GraphTimestampImpl,
3
- assertJsonSafePayload,
4
- buildIndexDDL,
5
- compileDataOpsExpr,
6
- dedupeIndexSpecs,
7
- isFirestoreSpecialType,
8
- validateJsonPathKey
9
- } from "../chunk-NNBSUOOF.js";
10
- import {
11
- DEFAULT_CORE_INDEXES
12
- } from "../chunk-2DHMNTV6.js";
3
+ buildSchemaStatements,
4
+ compileAggregate,
5
+ compileBulkDelete,
6
+ compileBulkUpdate,
7
+ compileDelete,
8
+ compileDeleteAll,
9
+ compileExpand,
10
+ compileExpandHydrate,
11
+ compileFindEdgesProjected,
12
+ compileSelect,
13
+ compileSelectByDocId,
14
+ compileSet,
15
+ compileUpdate,
16
+ decodeProjectedRow,
17
+ rowTimestampToMillis,
18
+ validateTableName
19
+ } from "../chunk-ULRDQ6HZ.js";
20
+ import "../chunk-2DHMNTV6.js";
13
21
  import {
14
22
  createCapabilities,
15
23
  intersectCapabilities
@@ -17,561 +25,30 @@ import {
17
25
  import {
18
26
  META_EDGE_TYPE,
19
27
  META_NODE_TYPE,
20
- NODE_RELATION,
21
28
  buildEdgeQueryPlan,
22
- computeEdgeDocId,
23
- computeNodeDocId,
24
29
  createGraphClientFromBackend,
25
30
  createMergedRegistry,
26
31
  createRegistry,
27
32
  generateId
28
- } from "../chunk-3AHHXMWX.js";
33
+ } from "../chunk-5HIRYV2S.js";
34
+ import {
35
+ NODE_RELATION,
36
+ computeEdgeDocId,
37
+ computeNodeDocId
38
+ } from "../chunk-NGAJCALM.js";
29
39
  import {
30
40
  CapabilityNotSupportedError,
31
41
  FiregraphError,
32
- assertUpdatePayloadExclusive,
33
- deleteField,
34
- flattenPatch
42
+ deleteField
35
43
  } from "../chunk-SIHE4UY4.js";
36
44
  import "../chunk-EQJUUVFG.js";
37
45
 
38
- // src/cloudflare/schema.ts
39
- var DO_FIELD_TO_COLUMN = {
40
- aType: "a_type",
41
- aUid: "a_uid",
42
- axbType: "axb_type",
43
- bType: "b_type",
44
- bUid: "b_uid",
45
- v: "v",
46
- createdAt: "created_at",
47
- updatedAt: "updated_at"
48
- };
49
- var IDENT_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
50
- function validateDOTableName(name) {
51
- if (!IDENT_RE.test(name)) {
52
- throw new Error(`Invalid SQL identifier: ${name}. Must match /^[A-Za-z_][A-Za-z0-9_]*$/.`);
53
- }
54
- }
55
- function quoteDOIdent(name) {
56
- validateDOTableName(name);
57
- return `"${name}"`;
58
- }
59
- function quoteDOColumnAlias(label) {
60
- return `"${label.replace(/"/g, '""')}"`;
61
- }
62
- function buildDOSchemaStatements(table, options = {}) {
63
- const t = quoteDOIdent(table);
64
- const statements = [
65
- `CREATE TABLE IF NOT EXISTS ${t} (
66
- doc_id TEXT NOT NULL PRIMARY KEY,
67
- a_type TEXT NOT NULL,
68
- a_uid TEXT NOT NULL,
69
- axb_type TEXT NOT NULL,
70
- b_type TEXT NOT NULL,
71
- b_uid TEXT NOT NULL,
72
- data TEXT NOT NULL,
73
- v INTEGER,
74
- created_at INTEGER NOT NULL,
75
- updated_at INTEGER NOT NULL
76
- )`
77
- ];
78
- const core = options.coreIndexes ?? [...DEFAULT_CORE_INDEXES];
79
- const fromRegistry = options.registry?.entries().flatMap((e) => e.indexes ?? []) ?? [];
80
- const deduped = dedupeIndexSpecs([...core, ...fromRegistry]);
81
- for (const spec of deduped) {
82
- statements.push(buildIndexDDL(spec, { table, fieldToColumn: DO_FIELD_TO_COLUMN }));
83
- }
84
- return statements;
85
- }
86
-
87
46
  // src/cloudflare/sql.ts
88
- var DO_BACKEND_LABEL = "DO SQLite";
89
- var DO_BACKEND_ERR_LABEL = "DO SQLite backend";
90
- function compileFieldRef(field) {
91
- const column = DO_FIELD_TO_COLUMN[field];
92
- if (column) {
93
- return { expr: quoteDOIdent(column) };
94
- }
95
- if (field.startsWith("data.")) {
96
- const suffix = field.slice(5);
97
- for (const part of suffix.split(".")) {
98
- validateJsonPathKey(part, DO_BACKEND_ERR_LABEL);
99
- }
100
- return { expr: `json_extract("data", '$.${suffix}')` };
101
- }
102
- if (field === "data") {
103
- return { expr: `json_extract("data", '$')` };
104
- }
105
- throw new FiregraphError(
106
- `DO SQLite backend cannot resolve filter field: ${field}`,
107
- "INVALID_QUERY"
108
- );
109
- }
110
- function bindValue(value) {
111
- if (value === null || value === void 0) return null;
112
- if (typeof value === "string" || typeof value === "number" || typeof value === "bigint" || typeof value === "boolean") {
113
- return value;
114
- }
115
- if (value instanceof Date) return value.getTime();
116
- if (typeof value === "object") {
117
- const firestoreType = isFirestoreSpecialType(value);
118
- if (firestoreType) {
119
- throw new FiregraphError(
120
- `DO SQLite backend cannot bind a Firestore ${firestoreType} value \u2014 JSON serialization would silently drop fields and the resulting bind would never match a stored row. Convert to a primitive (e.g. \`ts.toMillis()\` for Timestamp) before filtering or updating.`,
121
- "INVALID_QUERY"
122
- );
123
- }
124
- return JSON.stringify(value);
125
- }
126
- return String(value);
127
- }
128
- function compileFilter(filter, params) {
129
- const { expr } = compileFieldRef(filter.field);
130
- switch (filter.op) {
131
- case "==":
132
- params.push(bindValue(filter.value));
133
- return `${expr} = ?`;
134
- case "!=":
135
- params.push(bindValue(filter.value));
136
- return `${expr} != ?`;
137
- case "<":
138
- params.push(bindValue(filter.value));
139
- return `${expr} < ?`;
140
- case "<=":
141
- params.push(bindValue(filter.value));
142
- return `${expr} <= ?`;
143
- case ">":
144
- params.push(bindValue(filter.value));
145
- return `${expr} > ?`;
146
- case ">=":
147
- params.push(bindValue(filter.value));
148
- return `${expr} >= ?`;
149
- case "in": {
150
- const values = asArray(filter.value, "in");
151
- const placeholders = values.map(() => "?").join(", ");
152
- for (const v of values) params.push(bindValue(v));
153
- return `${expr} IN (${placeholders})`;
154
- }
155
- case "not-in": {
156
- const values = asArray(filter.value, "not-in");
157
- const placeholders = values.map(() => "?").join(", ");
158
- for (const v of values) params.push(bindValue(v));
159
- return `${expr} NOT IN (${placeholders})`;
160
- }
161
- case "array-contains": {
162
- params.push(bindValue(filter.value));
163
- return `EXISTS (SELECT 1 FROM json_each(${expr}) WHERE value = ?)`;
164
- }
165
- case "array-contains-any": {
166
- const values = asArray(filter.value, "array-contains-any");
167
- const placeholders = values.map(() => "?").join(", ");
168
- for (const v of values) params.push(bindValue(v));
169
- return `EXISTS (SELECT 1 FROM json_each(${expr}) WHERE value IN (${placeholders}))`;
170
- }
171
- default:
172
- throw new FiregraphError(
173
- `DO SQLite backend does not support filter operator: ${String(filter.op)}`,
174
- "INVALID_QUERY"
175
- );
176
- }
177
- }
178
- function asArray(value, op) {
179
- if (!Array.isArray(value) || value.length === 0) {
180
- throw new FiregraphError(`Operator "${op}" requires a non-empty array value`, "INVALID_QUERY");
181
- }
182
- return value;
183
- }
184
- function compileOrderBy(options, _params) {
185
- if (!options?.orderBy) return "";
186
- const { field, direction } = options.orderBy;
187
- const { expr } = compileFieldRef(field);
188
- const dir = direction === "desc" ? "DESC" : "ASC";
189
- return ` ORDER BY ${expr} ${dir}`;
190
- }
191
- function compileLimit(options, params) {
192
- if (options?.limit === void 0) return "";
193
- params.push(options.limit);
194
- return ` LIMIT ?`;
195
- }
196
- function compileDOSelect(table, filters, options) {
197
- const params = [];
198
- const conditions = [];
199
- for (const f of filters) {
200
- conditions.push(compileFilter(f, params));
201
- }
202
- const where = conditions.length > 0 ? ` WHERE ${conditions.join(" AND ")}` : "";
203
- let sql = `SELECT * FROM ${quoteDOIdent(table)}${where}`;
204
- sql += compileOrderBy(options, params);
205
- sql += compileLimit(options, params);
206
- return { sql, params };
207
- }
208
- function compileDOExpand(table, params) {
209
- if (params.sources.length === 0) {
210
- throw new FiregraphError(
211
- "compileDOExpand requires a non-empty sources list \u2014 empty IN () is invalid SQL.",
212
- "INVALID_QUERY"
213
- );
214
- }
215
- const direction = params.direction ?? "forward";
216
- const aUidCol = compileFieldRef("aUid").expr;
217
- const bUidCol = compileFieldRef("bUid").expr;
218
- const aTypeCol = compileFieldRef("aType").expr;
219
- const bTypeCol = compileFieldRef("bType").expr;
220
- const axbTypeCol = compileFieldRef("axbType").expr;
221
- const sourceColumn = direction === "forward" ? aUidCol : bUidCol;
222
- const sqlParams = [params.axbType];
223
- const conditions = [`${axbTypeCol} = ?`];
224
- const placeholders = params.sources.map(() => "?").join(", ");
225
- conditions.push(`${sourceColumn} IN (${placeholders})`);
226
- for (const uid of params.sources) sqlParams.push(uid);
227
- if (params.aType !== void 0) {
228
- conditions.push(`${aTypeCol} = ?`);
229
- sqlParams.push(params.aType);
230
- }
231
- if (params.bType !== void 0) {
232
- conditions.push(`${bTypeCol} = ?`);
233
- sqlParams.push(params.bType);
234
- }
235
- if (params.axbType === NODE_RELATION) {
236
- conditions.push(`${aUidCol} != ${bUidCol}`);
237
- }
238
- let sql = `SELECT * FROM ${quoteDOIdent(table)} WHERE ${conditions.join(" AND ")}`;
239
- if (params.orderBy) {
240
- sql += compileOrderBy({ orderBy: params.orderBy }, sqlParams);
241
- }
242
- if (params.limitPerSource !== void 0) {
243
- const totalLimit = params.sources.length * params.limitPerSource;
244
- sql += ` LIMIT ?`;
245
- sqlParams.push(totalLimit);
246
- }
247
- return { sql, params: sqlParams };
248
- }
249
- function compileDOExpandHydrate(table, targetUids) {
250
- if (targetUids.length === 0) {
251
- throw new FiregraphError(
252
- "compileDOExpandHydrate requires a non-empty target list \u2014 empty IN () is invalid SQL.",
253
- "INVALID_QUERY"
254
- );
255
- }
256
- const placeholders = targetUids.map(() => "?").join(", ");
257
- const sqlParams = [NODE_RELATION];
258
- for (const uid of targetUids) sqlParams.push(uid);
259
- const aUidCol = compileFieldRef("aUid").expr;
260
- const bUidCol = compileFieldRef("bUid").expr;
261
- const axbTypeCol = compileFieldRef("axbType").expr;
262
- return {
263
- sql: `SELECT * FROM ${quoteDOIdent(table)} WHERE ${axbTypeCol} = ? AND ${aUidCol} = ${bUidCol} AND ${bUidCol} IN (${placeholders})`,
264
- params: sqlParams
265
- };
266
- }
267
- function compileDOSelectByDocId(table, docId) {
268
- return {
269
- sql: `SELECT * FROM ${quoteDOIdent(table)} WHERE "doc_id" = ? LIMIT 1`,
270
- params: [docId]
271
- };
272
- }
273
- function normalizeDOProjectionField(field) {
274
- if (field in DO_FIELD_TO_COLUMN) return field;
275
- if (field === "data" || field.startsWith("data.")) return field;
276
- return `data.${field}`;
277
- }
278
- function compileDOFindEdgesProjected(table, select, filters, options) {
279
- if (select.length === 0) {
280
- throw new FiregraphError(
281
- "compileDOFindEdgesProjected requires a non-empty select list \u2014 an empty projection has no SQL representation distinct from `findEdges`.",
282
- "INVALID_QUERY"
283
- );
284
- }
285
- const seen = /* @__PURE__ */ new Set();
286
- const uniqueFields = [];
287
- for (const f of select) {
288
- if (!seen.has(f)) {
289
- seen.add(f);
290
- uniqueFields.push(f);
291
- }
292
- }
293
- const projections = [];
294
- const columns = [];
295
- for (let idx = 0; idx < uniqueFields.length; idx++) {
296
- const field = uniqueFields[idx];
297
- const canonical = normalizeDOProjectionField(field);
298
- const { expr } = compileFieldRef(canonical);
299
- const alias = quoteDOColumnAlias(field);
300
- projections.push(`${expr} AS ${alias}`);
301
- let kind;
302
- let typeAliasName;
303
- if (canonical === "data") {
304
- kind = "data";
305
- } else if (canonical.startsWith("data.")) {
306
- kind = "json";
307
- typeAliasName = `__fg_t_${idx}`;
308
- const typeAlias = quoteDOColumnAlias(typeAliasName);
309
- projections.push(`json_type("data", '$.${canonical.slice(5)}') AS ${typeAlias}`);
310
- } else {
311
- if (canonical === "v") kind = "builtin-int";
312
- else if (canonical === "createdAt" || canonical === "updatedAt") kind = "builtin-timestamp";
313
- else kind = "builtin-text";
314
- }
315
- columns.push({ field, kind, typeAlias: typeAliasName });
316
- }
317
- const params = [];
318
- const conditions = [];
319
- for (const f of filters) {
320
- conditions.push(compileFilter(f, params));
321
- }
322
- const where = conditions.length > 0 ? ` WHERE ${conditions.join(" AND ")}` : "";
323
- let sql = `SELECT ${projections.join(", ")} FROM ${quoteDOIdent(table)}${where}`;
324
- sql += compileOrderBy(options, params);
325
- sql += compileLimit(options, params);
326
- return { stmt: { sql, params }, columns };
327
- }
328
- function decodeDOProjectedRow(row, columns) {
329
- const out = {};
330
- for (const c of columns) {
331
- const raw = row[c.field];
332
- switch (c.kind) {
333
- case "builtin-text":
334
- out[c.field] = raw === null || raw === void 0 ? null : String(raw);
335
- break;
336
- case "builtin-int":
337
- if (raw === null || raw === void 0) {
338
- out[c.field] = null;
339
- } else if (typeof raw === "bigint") {
340
- out[c.field] = Number(raw);
341
- } else if (typeof raw === "number") {
342
- out[c.field] = raw;
343
- } else {
344
- out[c.field] = Number(raw);
345
- }
346
- break;
347
- case "builtin-timestamp": {
348
- const ms = toMillis(raw);
349
- out[c.field] = GraphTimestampImpl.fromMillis(ms);
350
- break;
351
- }
352
- case "data":
353
- if (raw === null || raw === void 0 || raw === "") {
354
- out[c.field] = {};
355
- } else {
356
- out[c.field] = JSON.parse(raw);
357
- }
358
- break;
359
- case "json": {
360
- const t = row[c.typeAlias];
361
- if (raw === null || raw === void 0) {
362
- out[c.field] = null;
363
- } else if (t === "object" || t === "array") {
364
- out[c.field] = typeof raw === "string" ? JSON.parse(raw) : raw;
365
- } else if (t === "integer" && typeof raw === "bigint") {
366
- out[c.field] = Number(raw);
367
- } else {
368
- out[c.field] = raw;
369
- }
370
- break;
371
- }
372
- }
373
- }
374
- return out;
375
- }
376
- function compileDOAggregate(table, spec, filters) {
377
- const aliases = Object.keys(spec);
378
- if (aliases.length === 0) {
379
- throw new FiregraphError(
380
- "aggregate() requires at least one aggregation in the `aggregates` map.",
381
- "INVALID_QUERY"
382
- );
383
- }
384
- const projections = [];
385
- for (const alias of aliases) {
386
- const { op, field } = spec[alias];
387
- validateJsonPathKey(alias, DO_BACKEND_ERR_LABEL);
388
- if (op === "count") {
389
- if (field !== void 0) {
390
- throw new FiregraphError(
391
- `Aggregate '${alias}' op 'count' must not specify a field \u2014 count operates on rows, not a column expression.`,
392
- "INVALID_QUERY"
393
- );
394
- }
395
- projections.push(`COUNT(*) AS ${quoteDOIdent(alias)}`);
396
- continue;
397
- }
398
- if (!field) {
399
- throw new FiregraphError(
400
- `Aggregate '${alias}' op '${op}' requires a field.`,
401
- "INVALID_QUERY"
402
- );
403
- }
404
- const { expr } = compileFieldRef(field);
405
- const numeric = `CAST(${expr} AS REAL)`;
406
- if (op === "sum") projections.push(`SUM(${numeric}) AS ${quoteDOIdent(alias)}`);
407
- else if (op === "avg") projections.push(`AVG(${numeric}) AS ${quoteDOIdent(alias)}`);
408
- else if (op === "min") projections.push(`MIN(${numeric}) AS ${quoteDOIdent(alias)}`);
409
- else if (op === "max") projections.push(`MAX(${numeric}) AS ${quoteDOIdent(alias)}`);
410
- else
411
- throw new FiregraphError(
412
- `DO SQLite backend does not support aggregate op: ${String(op)}`,
413
- "INVALID_QUERY"
414
- );
415
- }
416
- const params = [];
417
- const conditions = [];
418
- for (const f of filters) {
419
- conditions.push(compileFilter(f, params));
420
- }
421
- const where = conditions.length > 0 ? ` WHERE ${conditions.join(" AND ")}` : "";
422
- const sql = `SELECT ${projections.join(", ")} FROM ${quoteDOIdent(table)}${where}`;
423
- return { stmt: { sql, params }, aliases };
424
- }
425
- function compileDOSet(table, docId, record, nowMillis, mode) {
426
- assertJsonSafePayload(record.data, DO_BACKEND_LABEL);
427
- if (mode === "replace") {
428
- const sql2 = `INSERT OR REPLACE INTO ${quoteDOIdent(table)} (
429
- doc_id, a_type, a_uid, axb_type, b_type, b_uid, data, v, created_at, updated_at
430
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`;
431
- const params = [
432
- docId,
433
- record.aType,
434
- record.aUid,
435
- record.axbType,
436
- record.bType,
437
- record.bUid,
438
- JSON.stringify(record.data ?? {}),
439
- record.v ?? null,
440
- nowMillis,
441
- nowMillis
442
- ];
443
- return { sql: sql2, params };
444
- }
445
- const insertParams = [
446
- docId,
447
- record.aType,
448
- record.aUid,
449
- record.axbType,
450
- record.bType,
451
- record.bUid,
452
- JSON.stringify(record.data ?? {}),
453
- record.v ?? null,
454
- nowMillis,
455
- nowMillis
456
- ];
457
- const ops = flattenPatch(record.data ?? {});
458
- const updateParams = [];
459
- const dataExpr = compileDataOpsExpr(ops, `COALESCE("data", '{}')`, updateParams, DO_BACKEND_ERR_LABEL) ?? `COALESCE("data", '{}')`;
460
- const sql = `INSERT INTO ${quoteDOIdent(table)} (
461
- doc_id, a_type, a_uid, axb_type, b_type, b_uid, data, v, created_at, updated_at
462
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
463
- ON CONFLICT(doc_id) DO UPDATE SET
464
- "a_type" = excluded."a_type",
465
- "a_uid" = excluded."a_uid",
466
- "axb_type" = excluded."axb_type",
467
- "b_type" = excluded."b_type",
468
- "b_uid" = excluded."b_uid",
469
- "data" = ${dataExpr},
470
- "v" = COALESCE(excluded."v", "v"),
471
- "created_at" = excluded."created_at",
472
- "updated_at" = excluded."updated_at"`;
473
- return { sql, params: [...insertParams, ...updateParams] };
474
- }
475
- function compileDOUpdate(table, docId, update, nowMillis) {
476
- assertUpdatePayloadExclusive(update);
477
- const setClauses = [];
478
- const params = [];
479
- if (update.replaceData) {
480
- assertJsonSafePayload(update.replaceData, DO_BACKEND_LABEL);
481
- setClauses.push(`"data" = ?`);
482
- params.push(JSON.stringify(update.replaceData));
483
- } else if (update.dataOps && update.dataOps.length > 0) {
484
- for (const op of update.dataOps) {
485
- if (!op.delete) assertJsonSafePayload(op.value, DO_BACKEND_LABEL);
486
- }
487
- const expr = compileDataOpsExpr(
488
- update.dataOps,
489
- `COALESCE("data", '{}')`,
490
- params,
491
- DO_BACKEND_ERR_LABEL
492
- );
493
- if (expr !== null) {
494
- setClauses.push(`"data" = ${expr}`);
495
- }
496
- }
497
- if (update.v !== void 0) {
498
- setClauses.push(`"v" = ?`);
499
- params.push(update.v);
500
- }
501
- setClauses.push(`"updated_at" = ?`);
502
- params.push(nowMillis);
503
- params.push(docId);
504
- return {
505
- sql: `UPDATE ${quoteDOIdent(table)} SET ${setClauses.join(", ")} WHERE "doc_id" = ?`,
506
- params
507
- };
508
- }
509
- function compileDODelete(table, docId) {
510
- return {
511
- sql: `DELETE FROM ${quoteDOIdent(table)} WHERE "doc_id" = ?`,
512
- params: [docId]
513
- };
514
- }
515
- function compileDOBulkDelete(table, filters) {
516
- const params = [];
517
- const conditions = [];
518
- for (const f of filters) {
519
- conditions.push(compileFilter(f, params));
520
- }
521
- const where = conditions.length > 0 ? ` WHERE ${conditions.join(" AND ")}` : "";
522
- return {
523
- sql: `DELETE FROM ${quoteDOIdent(table)}${where}`,
524
- params
525
- };
526
- }
527
- function compileDOBulkUpdate(table, filters, patchData, nowMillis) {
528
- const dataOps = flattenPatch(patchData);
529
- if (dataOps.length === 0) {
530
- throw new FiregraphError(
531
- "bulkUpdate() patch.data must contain at least one leaf \u2014 an empty patch would only rewrite `updated_at`, which is almost certainly a bug. Use `setDoc` with merge mode if you want to stamp without editing data.",
532
- "INVALID_QUERY"
533
- );
534
- }
535
- for (const op of dataOps) {
536
- if (!op.delete) assertJsonSafePayload(op.value, DO_BACKEND_LABEL);
537
- }
538
- const setParams = [];
539
- const expr = compileDataOpsExpr(
540
- dataOps,
541
- `COALESCE("data", '{}')`,
542
- setParams,
543
- DO_BACKEND_ERR_LABEL
544
- );
545
- if (expr === null) {
546
- throw new FiregraphError(
547
- "bulkUpdate() patch produced no SQL operations \u2014 internal invariant violated.",
548
- "INVALID_ARGUMENT"
549
- );
550
- }
551
- const setClauses = [`"data" = ${expr}`, `"updated_at" = ?`];
552
- setParams.push(nowMillis);
553
- const whereParams = [];
554
- const conditions = [];
555
- for (const f of filters) {
556
- conditions.push(compileFilter(f, whereParams));
557
- }
558
- const where = conditions.length > 0 ? ` WHERE ${conditions.join(" AND ")}` : "";
559
- return {
560
- sql: `UPDATE ${quoteDOIdent(table)} SET ${setClauses.join(", ")}${where}`,
561
- params: [...setParams, ...whereParams]
562
- };
563
- }
564
- function compileDODeleteAll(table) {
565
- return {
566
- sql: `DELETE FROM ${quoteDOIdent(table)}`,
567
- params: []
568
- };
569
- }
570
47
  function rowToDORecord(row) {
571
48
  const dataString = row.data;
572
49
  const data = dataString ? JSON.parse(dataString) : {};
573
- const createdAtMs = toMillis(row.created_at);
574
- const updatedAtMs = toMillis(row.updated_at);
50
+ const createdAtMs = rowTimestampToMillis(row.created_at);
51
+ const updatedAtMs = rowTimestampToMillis(row.updated_at);
575
52
  const record = {
576
53
  aType: row.a_type,
577
54
  aUid: row.a_uid,
@@ -603,18 +80,6 @@ function hydrateDORecord(wire) {
603
80
  }
604
81
  return record;
605
82
  }
606
- function toMillis(value) {
607
- if (typeof value === "number") return value;
608
- if (typeof value === "bigint") return Number(value);
609
- if (typeof value === "string") {
610
- const n = Number(value);
611
- if (Number.isFinite(n)) return n;
612
- }
613
- throw new FiregraphError(
614
- `DO SQLite row has non-numeric timestamp column: ${typeof value} (${String(value)})`,
615
- "INVALID_QUERY"
616
- );
617
- }
618
83
 
619
84
  // src/cloudflare/backend.ts
620
85
  function validateSegment(value, label) {
@@ -878,7 +343,7 @@ var DORPCBackend = class _DORPCBackend {
878
343
  );
879
344
  }
880
345
  const { rows, columns } = await stub._fgFindEdgesProjected(select, filters, options);
881
- return rows.map((row) => decodeDOProjectedRow(row, columns));
346
+ return rows.map((row) => decodeProjectedRow(row, columns));
882
347
  }
883
348
  // --- Cross-scope queries ---
884
349
  //
@@ -1045,7 +510,7 @@ var FiregraphDO = class extends DurableObject {
1045
510
  super(ctx, env);
1046
511
  this.state = ctx;
1047
512
  const table = options.table ?? DEFAULT_OPTIONS.table;
1048
- validateDOTableName(table);
513
+ validateTableName(table);
1049
514
  this.table = table;
1050
515
  this.registry = options.registry;
1051
516
  this.coreIndexes = options.coreIndexes;
@@ -1064,12 +529,12 @@ var FiregraphDO = class extends DurableObject {
1064
529
  // `src/cloudflare/backend.ts` calls these directly on the DO stub.
1065
530
  // ---------------------------------------------------------------------------
1066
531
  async _fgGetDoc(docId) {
1067
- const stmt = compileDOSelectByDocId(this.table, docId);
532
+ const stmt = compileSelectByDocId(this.table, docId);
1068
533
  const rows = this.execAll(stmt);
1069
534
  return rows.length === 0 ? null : rowToDORecord(rows[0]);
1070
535
  }
1071
536
  async _fgQuery(filters, options) {
1072
- const stmt = compileDOSelect(this.table, filters, options);
537
+ const stmt = compileSelect(this.table, filters, options);
1073
538
  const rows = this.execAll(stmt);
1074
539
  return rows.map(rowToDORecord);
1075
540
  }
@@ -1081,7 +546,7 @@ var FiregraphDO = class extends DurableObject {
1081
546
  * the wire payload stays a plain row of (alias → number | null).
1082
547
  */
1083
548
  async _fgAggregate(spec, filters) {
1084
- const { stmt, aliases } = compileDOAggregate(this.table, spec, filters);
549
+ const { stmt, aliases } = compileAggregate(this.table, spec, filters);
1085
550
  const rows = this.execAll(stmt);
1086
551
  const row = rows[0] ?? {};
1087
552
  const out = {};
@@ -1098,11 +563,11 @@ var FiregraphDO = class extends DurableObject {
1098
563
  // RPC: writes
1099
564
  // ---------------------------------------------------------------------------
1100
565
  async _fgSetDoc(docId, record, mode) {
1101
- const stmt = compileDOSet(this.table, docId, record, Date.now(), mode);
566
+ const stmt = compileSet(this.table, docId, record, Date.now(), mode);
1102
567
  this.execRun(stmt);
1103
568
  }
1104
569
  async _fgUpdateDoc(docId, update) {
1105
- const stmt = compileDOUpdate(this.table, docId, update, Date.now());
570
+ const stmt = compileUpdate(this.table, docId, update, Date.now());
1106
571
  const sqlWithReturning = `${stmt.sql} RETURNING "doc_id"`;
1107
572
  const rows = this.state.storage.sql.exec(sqlWithReturning, ...stmt.params).toArray();
1108
573
  if (rows.length === 0) {
@@ -1110,7 +575,7 @@ var FiregraphDO = class extends DurableObject {
1110
575
  }
1111
576
  }
1112
577
  async _fgDeleteDoc(docId) {
1113
- const stmt = compileDODelete(this.table, docId);
578
+ const stmt = compileDelete(this.table, docId);
1114
579
  this.execRun(stmt);
1115
580
  }
1116
581
  // ---------------------------------------------------------------------------
@@ -1128,11 +593,11 @@ var FiregraphDO = class extends DurableObject {
1128
593
  const statements = ops.map((op) => {
1129
594
  switch (op.kind) {
1130
595
  case "set":
1131
- return compileDOSet(this.table, op.docId, op.record, now, op.mode);
596
+ return compileSet(this.table, op.docId, op.record, now, op.mode);
1132
597
  case "update":
1133
- return compileDOUpdate(this.table, op.docId, op.update, now);
598
+ return compileUpdate(this.table, op.docId, op.update, now);
1134
599
  case "delete":
1135
- return compileDODelete(this.table, op.docId);
600
+ return compileDelete(this.table, op.docId);
1136
601
  }
1137
602
  });
1138
603
  this.state.storage.transactionSync(() => {
@@ -1151,8 +616,8 @@ var FiregraphDO = class extends DurableObject {
1151
616
  // Without that topology the DO has no way to enumerate its children.
1152
617
  // ---------------------------------------------------------------------------
1153
618
  async _fgRemoveNodeCascade(uid) {
1154
- const outgoingStmt = compileDOSelect(this.table, [{ field: "aUid", op: "==", value: uid }]);
1155
- const incomingStmt = compileDOSelect(this.table, [{ field: "bUid", op: "==", value: uid }]);
619
+ const outgoingStmt = compileSelect(this.table, [{ field: "aUid", op: "==", value: uid }]);
620
+ const incomingStmt = compileSelect(this.table, [{ field: "bUid", op: "==", value: uid }]);
1156
621
  const outgoingRows = this.execAll(outgoingStmt);
1157
622
  const incomingRows = this.execAll(incomingStmt);
1158
623
  const seen = /* @__PURE__ */ new Set();
@@ -1172,9 +637,9 @@ var FiregraphDO = class extends DurableObject {
1172
637
  edgeDocIds.push(docId);
1173
638
  }
1174
639
  }
1175
- const statements = edgeDocIds.map((id) => compileDODelete(this.table, id));
640
+ const statements = edgeDocIds.map((id) => compileDelete(this.table, id));
1176
641
  if (nodeExists) {
1177
- statements.push(compileDODelete(this.table, computeNodeDocId(uid)));
642
+ statements.push(compileDelete(this.table, computeNodeDocId(uid)));
1178
643
  }
1179
644
  if (statements.length === 0) {
1180
645
  return {
@@ -1213,11 +678,11 @@ var FiregraphDO = class extends DurableObject {
1213
678
  const plan = buildEdgeQueryPlan(params);
1214
679
  let docIds;
1215
680
  if (plan.strategy === "get") {
1216
- const existsStmt = compileDOSelectByDocId(this.table, plan.docId);
681
+ const existsStmt = compileSelectByDocId(this.table, plan.docId);
1217
682
  const rows = this.execAll(existsStmt);
1218
683
  docIds = rows.length > 0 ? [plan.docId] : [];
1219
684
  } else {
1220
- const selectStmt = compileDOSelect(this.table, plan.filters, plan.options);
685
+ const selectStmt = compileSelect(this.table, plan.filters, plan.options);
1221
686
  const rows = this.execAll(selectStmt);
1222
687
  docIds = rows.map(
1223
688
  (row) => computeEdgeDocId(row.a_uid, row.axb_type, row.b_uid)
@@ -1226,7 +691,7 @@ var FiregraphDO = class extends DurableObject {
1226
691
  if (docIds.length === 0) {
1227
692
  return { deleted: 0, batches: 0, errors: [] };
1228
693
  }
1229
- const deleteStmts = docIds.map((id) => compileDODelete(this.table, id));
694
+ const deleteStmts = docIds.map((id) => compileDelete(this.table, id));
1230
695
  try {
1231
696
  this.state.storage.transactionSync(() => {
1232
697
  for (const stmt of deleteStmts) {
@@ -1271,12 +736,12 @@ var FiregraphDO = class extends DurableObject {
1271
736
  "INVALID_ARGUMENT"
1272
737
  );
1273
738
  }
1274
- const stmt = compileDOBulkDelete(this.table, filters);
739
+ const stmt = compileBulkDelete(this.table, filters);
1275
740
  return this.execDmlWithReturning(stmt);
1276
741
  }
1277
742
  async _fgBulkUpdate(filters, patch, _options) {
1278
743
  void _options;
1279
- const stmt = compileDOBulkUpdate(this.table, filters, patch.data, Date.now());
744
+ const stmt = compileBulkUpdate(this.table, filters, patch.data, Date.now());
1280
745
  return this.execDmlWithReturning(stmt);
1281
746
  }
1282
747
  // ---------------------------------------------------------------------------
@@ -1293,7 +758,7 @@ var FiregraphDO = class extends DurableObject {
1293
758
  if (params.sources.length === 0) {
1294
759
  return params.hydrate ? { edges: [], targets: [] } : { edges: [] };
1295
760
  }
1296
- const stmt = compileDOExpand(this.table, params);
761
+ const stmt = compileExpand(this.table, params);
1297
762
  const rows = this.state.storage.sql.exec(stmt.sql, ...stmt.params).toArray();
1298
763
  const edges = rows.map((row) => rowToDORecord(row));
1299
764
  if (!params.hydrate) {
@@ -1305,7 +770,7 @@ var FiregraphDO = class extends DurableObject {
1305
770
  if (uniqueTargets.length === 0) {
1306
771
  return { edges, targets: [] };
1307
772
  }
1308
- const hydrateStmt = compileDOExpandHydrate(this.table, uniqueTargets);
773
+ const hydrateStmt = compileExpandHydrate(this.table, uniqueTargets);
1309
774
  const hydrateRows = this.state.storage.sql.exec(hydrateStmt.sql, ...hydrateStmt.params).toArray();
1310
775
  const byUid = /* @__PURE__ */ new Map();
1311
776
  for (const row of hydrateRows) {
@@ -1327,7 +792,7 @@ var FiregraphDO = class extends DurableObject {
1327
792
  // a typical projection); structured clone copes happily.
1328
793
  // ---------------------------------------------------------------------------
1329
794
  async _fgFindEdgesProjected(select, filters, options) {
1330
- const { stmt, columns } = compileDOFindEdgesProjected(this.table, select, filters, options);
795
+ const { stmt, columns } = compileFindEdgesProjected(this.table, select, filters, options);
1331
796
  const rows = this.state.storage.sql.exec(stmt.sql, ...stmt.params).toArray();
1332
797
  return { rows, columns };
1333
798
  }
@@ -1365,14 +830,14 @@ var FiregraphDO = class extends DurableObject {
1365
830
  * forever), but its storage can be emptied.
1366
831
  */
1367
832
  async _fgDestroy() {
1368
- const stmt = compileDODeleteAll(this.table);
833
+ const stmt = compileDeleteAll(this.table);
1369
834
  this.execRun(stmt);
1370
835
  }
1371
836
  // ---------------------------------------------------------------------------
1372
837
  // Internals
1373
838
  // ---------------------------------------------------------------------------
1374
839
  runSchema() {
1375
- const statements = buildDOSchemaStatements(this.table, {
840
+ const statements = buildSchemaStatements(this.table, {
1376
841
  coreIndexes: this.coreIndexes,
1377
842
  registry: this.registry
1378
843
  });
@@ -1393,7 +858,7 @@ export {
1393
858
  FiregraphDO,
1394
859
  META_EDGE_TYPE,
1395
860
  META_NODE_TYPE,
1396
- buildDOSchemaStatements,
861
+ buildSchemaStatements as buildDOSchemaStatements,
1397
862
  createCapabilities,
1398
863
  createDOClient,
1399
864
  createMergedRegistry,