@typicalday/firegraph 0.11.2 → 0.12.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 (44) hide show
  1. package/README.md +38 -5
  2. package/dist/backend-BsR0lnFL.d.ts +200 -0
  3. package/dist/backend-Ct-fLlkG.d.cts +200 -0
  4. package/dist/backend.cjs +143 -2
  5. package/dist/backend.cjs.map +1 -1
  6. package/dist/backend.d.cts +3 -3
  7. package/dist/backend.d.ts +3 -3
  8. package/dist/backend.js +13 -4
  9. package/dist/backend.js.map +1 -1
  10. package/dist/chunk-AWW4MUJ5.js +245 -0
  11. package/dist/chunk-AWW4MUJ5.js.map +1 -0
  12. package/dist/{chunk-5753Y42M.js → chunk-C2QMD7RY.js} +6 -10
  13. package/dist/chunk-C2QMD7RY.js.map +1 -0
  14. package/dist/chunk-EQJUUVFG.js +14 -0
  15. package/dist/chunk-EQJUUVFG.js.map +1 -0
  16. package/dist/{chunk-NJSOD64C.js → chunk-HONQY4HF.js} +80 -17
  17. package/dist/chunk-HONQY4HF.js.map +1 -0
  18. package/dist/cloudflare/index.cjs +458 -73
  19. package/dist/cloudflare/index.cjs.map +1 -1
  20. package/dist/cloudflare/index.d.cts +8 -5
  21. package/dist/cloudflare/index.d.ts +8 -5
  22. package/dist/cloudflare/index.js +234 -56
  23. package/dist/cloudflare/index.js.map +1 -1
  24. package/dist/codegen/index.d.cts +1 -1
  25. package/dist/codegen/index.d.ts +1 -1
  26. package/dist/index.cjs +271 -36
  27. package/dist/index.cjs.map +1 -1
  28. package/dist/index.d.cts +21 -69
  29. package/dist/index.d.ts +21 -69
  30. package/dist/index.js +58 -28
  31. package/dist/index.js.map +1 -1
  32. package/dist/registry-B1qsVL0E.d.cts +64 -0
  33. package/dist/registry-Fi074zVa.d.ts +64 -0
  34. package/dist/{serialization-ZZ7RSDRX.js → serialization-OE2PFZMY.js} +6 -4
  35. package/dist/{types-BGWxcpI_.d.cts → types-DxYLy8Ol.d.cts} +36 -2
  36. package/dist/{types-BGWxcpI_.d.ts → types-DxYLy8Ol.d.ts} +36 -2
  37. package/package.json +1 -1
  38. package/dist/backend-U-MLShlg.d.ts +0 -97
  39. package/dist/backend-np4gEVhB.d.cts +0 -97
  40. package/dist/chunk-5753Y42M.js.map +0 -1
  41. package/dist/chunk-NJSOD64C.js.map +0 -1
  42. package/dist/chunk-R7CRGYY4.js +0 -94
  43. package/dist/chunk-R7CRGYY4.js.map +0 -1
  44. /package/dist/{serialization-ZZ7RSDRX.js.map → serialization-OE2PFZMY.js.map} +0 -0
@@ -1,6 +1,8 @@
1
- import { W as WritableRecord, U as UpdatePayload, S as StorageBackend, T as TransactionBackend, B as BatchBackend } from '../backend-np4gEVhB.cjs';
2
- import { c as GraphRegistry, I as IndexSpec, i as QueryFilter, z as QueryOptions, C as CascadeResult, F as FindEdgesParams, m as BulkOptions, o as BulkResult, a as GraphClient, D as DynamicGraphClient, S as StoredGraphRecord, d as GraphReader, G as GraphClientOptions, e as DynamicRegistryConfig } from '../types-BGWxcpI_.cjs';
1
+ import { W as WritableRecord, b as WriteMode, U as UpdatePayload, S as StorageBackend, T as TransactionBackend, B as BatchBackend } from '../backend-Ct-fLlkG.cjs';
2
+ export { d as deleteField } from '../backend-Ct-fLlkG.cjs';
3
+ import { e as GraphRegistry, I as IndexSpec, g as QueryFilter, z as QueryOptions, C as CascadeResult, F as FindEdgesParams, m as BulkOptions, o as BulkResult, a as GraphClient, D as DynamicGraphClient, S as StoredGraphRecord, l as GraphReader, G as GraphClientOptions, c as DynamicRegistryConfig } from '../types-DxYLy8Ol.cjs';
3
4
  import { DurableObject } from 'cloudflare:workers';
5
+ export { M as META_EDGE_TYPE, a as META_NODE_TYPE, b as createMergedRegistry, d as createRegistry, f as generateId } from '../registry-B1qsVL0E.cjs';
4
6
  import '@google-cloud/firestore';
5
7
 
6
8
  /**
@@ -124,6 +126,7 @@ type BatchOp = {
124
126
  kind: 'set';
125
127
  docId: string;
126
128
  record: WritableRecord;
129
+ mode: WriteMode;
127
130
  } | {
128
131
  kind: 'update';
129
132
  docId: string;
@@ -185,7 +188,7 @@ declare class FiregraphDO extends DurableObject<unknown> {
185
188
  constructor(ctx: DurableObjectStateLike, env: unknown, options?: FiregraphDOOptions);
186
189
  _fgGetDoc(docId: string): Promise<DORecordWire | null>;
187
190
  _fgQuery(filters: QueryFilter[], options?: QueryOptions): Promise<DORecordWire[]>;
188
- _fgSetDoc(docId: string, record: WritableRecord): Promise<void>;
191
+ _fgSetDoc(docId: string, record: WritableRecord, mode: WriteMode): Promise<void>;
189
192
  _fgUpdateDoc(docId: string, update: UpdatePayload): Promise<void>;
190
193
  _fgDeleteDoc(docId: string): Promise<void>;
191
194
  /**
@@ -257,7 +260,7 @@ interface DurableObjectIdLike {
257
260
  interface FiregraphStub {
258
261
  _fgGetDoc(docId: string): Promise<DORecordWire | null>;
259
262
  _fgQuery(filters: QueryFilter[], options?: QueryOptions): Promise<DORecordWire[]>;
260
- _fgSetDoc(docId: string, record: WritableRecord): Promise<void>;
263
+ _fgSetDoc(docId: string, record: WritableRecord, mode: WriteMode): Promise<void>;
261
264
  _fgUpdateDoc(docId: string, update: UpdatePayload): Promise<void>;
262
265
  _fgDeleteDoc(docId: string): Promise<void>;
263
266
  _fgBatch(ops: BatchOp[]): Promise<void>;
@@ -320,7 +323,7 @@ declare class DORPCBackend implements StorageBackend {
320
323
  private get stub();
321
324
  getDoc(docId: string): Promise<StoredGraphRecord | null>;
322
325
  query(filters: QueryFilter[], options?: QueryOptions): Promise<StoredGraphRecord[]>;
323
- setDoc(docId: string, record: WritableRecord): Promise<void>;
326
+ setDoc(docId: string, record: WritableRecord, mode: WriteMode): Promise<void>;
324
327
  updateDoc(docId: string, update: UpdatePayload): Promise<void>;
325
328
  deleteDoc(docId: string): Promise<void>;
326
329
  runTransaction<T>(_fn: (tx: TransactionBackend) => Promise<T>): Promise<T>;
@@ -1,6 +1,8 @@
1
- import { W as WritableRecord, U as UpdatePayload, S as StorageBackend, T as TransactionBackend, B as BatchBackend } from '../backend-U-MLShlg.js';
2
- import { c as GraphRegistry, I as IndexSpec, i as QueryFilter, z as QueryOptions, C as CascadeResult, F as FindEdgesParams, m as BulkOptions, o as BulkResult, a as GraphClient, D as DynamicGraphClient, S as StoredGraphRecord, d as GraphReader, G as GraphClientOptions, e as DynamicRegistryConfig } from '../types-BGWxcpI_.js';
1
+ import { W as WritableRecord, b as WriteMode, U as UpdatePayload, S as StorageBackend, T as TransactionBackend, B as BatchBackend } from '../backend-BsR0lnFL.js';
2
+ export { d as deleteField } from '../backend-BsR0lnFL.js';
3
+ import { e as GraphRegistry, I as IndexSpec, g as QueryFilter, z as QueryOptions, C as CascadeResult, F as FindEdgesParams, m as BulkOptions, o as BulkResult, a as GraphClient, D as DynamicGraphClient, S as StoredGraphRecord, l as GraphReader, G as GraphClientOptions, c as DynamicRegistryConfig } from '../types-DxYLy8Ol.js';
3
4
  import { DurableObject } from 'cloudflare:workers';
5
+ export { M as META_EDGE_TYPE, a as META_NODE_TYPE, b as createMergedRegistry, d as createRegistry, f as generateId } from '../registry-Fi074zVa.js';
4
6
  import '@google-cloud/firestore';
5
7
 
6
8
  /**
@@ -124,6 +126,7 @@ type BatchOp = {
124
126
  kind: 'set';
125
127
  docId: string;
126
128
  record: WritableRecord;
129
+ mode: WriteMode;
127
130
  } | {
128
131
  kind: 'update';
129
132
  docId: string;
@@ -185,7 +188,7 @@ declare class FiregraphDO extends DurableObject<unknown> {
185
188
  constructor(ctx: DurableObjectStateLike, env: unknown, options?: FiregraphDOOptions);
186
189
  _fgGetDoc(docId: string): Promise<DORecordWire | null>;
187
190
  _fgQuery(filters: QueryFilter[], options?: QueryOptions): Promise<DORecordWire[]>;
188
- _fgSetDoc(docId: string, record: WritableRecord): Promise<void>;
191
+ _fgSetDoc(docId: string, record: WritableRecord, mode: WriteMode): Promise<void>;
189
192
  _fgUpdateDoc(docId: string, update: UpdatePayload): Promise<void>;
190
193
  _fgDeleteDoc(docId: string): Promise<void>;
191
194
  /**
@@ -257,7 +260,7 @@ interface DurableObjectIdLike {
257
260
  interface FiregraphStub {
258
261
  _fgGetDoc(docId: string): Promise<DORecordWire | null>;
259
262
  _fgQuery(filters: QueryFilter[], options?: QueryOptions): Promise<DORecordWire[]>;
260
- _fgSetDoc(docId: string, record: WritableRecord): Promise<void>;
263
+ _fgSetDoc(docId: string, record: WritableRecord, mode: WriteMode): Promise<void>;
261
264
  _fgUpdateDoc(docId: string, update: UpdatePayload): Promise<void>;
262
265
  _fgDeleteDoc(docId: string): Promise<void>;
263
266
  _fgBatch(ops: BatchOp[]): Promise<void>;
@@ -320,7 +323,7 @@ declare class DORPCBackend implements StorageBackend {
320
323
  private get stub();
321
324
  getDoc(docId: string): Promise<StoredGraphRecord | null>;
322
325
  query(filters: QueryFilter[], options?: QueryOptions): Promise<StoredGraphRecord[]>;
323
- setDoc(docId: string, record: WritableRecord): Promise<void>;
326
+ setDoc(docId: string, record: WritableRecord, mode: WriteMode): Promise<void>;
324
327
  updateDoc(docId: string, update: UpdatePayload): Promise<void>;
325
328
  deleteDoc(docId: string): Promise<void>;
326
329
  runTransaction<T>(_fn: (tx: TransactionBackend) => Promise<T>): Promise<T>;
@@ -1,14 +1,173 @@
1
1
  import {
2
2
  DEFAULT_CORE_INDEXES,
3
+ META_EDGE_TYPE,
4
+ META_NODE_TYPE,
3
5
  NODE_RELATION,
4
6
  buildEdgeQueryPlan,
5
7
  computeEdgeDocId,
6
8
  computeNodeDocId,
7
- createGraphClientFromBackend
8
- } from "../chunk-NJSOD64C.js";
9
+ createGraphClientFromBackend,
10
+ createMergedRegistry,
11
+ createRegistry,
12
+ generateId
13
+ } from "../chunk-HONQY4HF.js";
9
14
  import {
10
- FiregraphError
11
- } from "../chunk-R7CRGYY4.js";
15
+ FiregraphError,
16
+ assertUpdatePayloadExclusive,
17
+ deleteField,
18
+ flattenPatch,
19
+ isDeleteSentinel
20
+ } from "../chunk-AWW4MUJ5.js";
21
+ import {
22
+ SERIALIZATION_TAG
23
+ } from "../chunk-EQJUUVFG.js";
24
+
25
+ // src/internal/sqlite-data-ops.ts
26
+ var FIRESTORE_TYPE_NAMES = /* @__PURE__ */ new Set([
27
+ "Timestamp",
28
+ "GeoPoint",
29
+ "VectorValue",
30
+ "DocumentReference",
31
+ "FieldValue"
32
+ ]);
33
+ function isFirestoreSpecialType(value) {
34
+ const ctorName = value.constructor?.name;
35
+ if (ctorName && FIRESTORE_TYPE_NAMES.has(ctorName)) return ctorName;
36
+ return null;
37
+ }
38
+ var JSON_PATH_KEY_RE = /^[A-Za-z_][A-Za-z0-9_-]*$/;
39
+ function validateJsonPathKey(key, backendLabel) {
40
+ if (key.length === 0) {
41
+ throw new FiregraphError(
42
+ `${backendLabel}: empty JSON path component is not allowed`,
43
+ "INVALID_QUERY"
44
+ );
45
+ }
46
+ if (!JSON_PATH_KEY_RE.test(key)) {
47
+ throw new FiregraphError(
48
+ `${backendLabel}: data field path component "${key}" is not a safe JSON-path identifier. Allowed pattern: /^[A-Za-z_][A-Za-z0-9_-]*$/. Use replaceNode/replaceEdge (full-data overwrite) for keys with reserved characters (whitespace, dots, brackets, quotes, etc.).`,
49
+ "INVALID_QUERY"
50
+ );
51
+ }
52
+ }
53
+ function jsonBind(value, backendLabel) {
54
+ if (value === void 0) return "null";
55
+ if (value !== null && typeof value === "object") {
56
+ const firestoreType = isFirestoreSpecialType(value);
57
+ if (firestoreType) {
58
+ throw new FiregraphError(
59
+ `${backendLabel} cannot persist a Firestore ${firestoreType} value. Convert to a primitive before writing (e.g. \`ts.toMillis()\` for Timestamp).`,
60
+ "INVALID_ARGUMENT"
61
+ );
62
+ }
63
+ }
64
+ return JSON.stringify(value);
65
+ }
66
+ function compileDataOpsExpr(ops, base, params, backendLabel) {
67
+ if (ops.length === 0) return null;
68
+ const deletes = [];
69
+ const sets = [];
70
+ for (const op of ops) (op.delete ? deletes : sets).push(op);
71
+ let expr = base;
72
+ if (deletes.length > 0) {
73
+ const placeholders = deletes.map(() => "?").join(", ");
74
+ expr = `json_remove(${expr}, ${placeholders})`;
75
+ for (const op of deletes) {
76
+ for (const seg of op.path) validateJsonPathKey(seg, backendLabel);
77
+ params.push(`$.${op.path.join(".")}`);
78
+ }
79
+ }
80
+ if (sets.length > 0) {
81
+ const pieces = sets.map(() => "?, json(?)").join(", ");
82
+ expr = `json_set(${expr}, ${pieces})`;
83
+ for (const op of sets) {
84
+ for (const seg of op.path) validateJsonPathKey(seg, backendLabel);
85
+ params.push(`$.${op.path.join(".")}`);
86
+ params.push(jsonBind(op.value, backendLabel));
87
+ }
88
+ }
89
+ return expr;
90
+ }
91
+
92
+ // src/internal/sqlite-payload-guard.ts
93
+ var FIRESTORE_TYPE_NAMES2 = /* @__PURE__ */ new Set([
94
+ "Timestamp",
95
+ "GeoPoint",
96
+ "VectorValue",
97
+ "DocumentReference",
98
+ "FieldValue"
99
+ ]);
100
+ function assertJsonSafePayload(data, label) {
101
+ walk(data, [], label);
102
+ }
103
+ function walk(node, path, label) {
104
+ if (node === null || node === void 0) return;
105
+ if (isDeleteSentinel(node)) {
106
+ throw new FiregraphError(
107
+ `${label} backend cannot persist a deleteField() sentinel inside a full-data payload (replaceNode/replaceEdge or first-insert). The sentinel is only valid inside an updateNode/updateEdge dataOps patch. Path: ${formatPath(path)}.`,
108
+ "INVALID_ARGUMENT"
109
+ );
110
+ }
111
+ const t = typeof node;
112
+ if (t === "symbol" || t === "function") {
113
+ throw new FiregraphError(
114
+ `${label} backend cannot persist a value of type ${t}. JSON.stringify drops it silently. Path: ${formatPath(path)}.`,
115
+ "INVALID_ARGUMENT"
116
+ );
117
+ }
118
+ if (t === "bigint") {
119
+ throw new FiregraphError(
120
+ `${label} backend cannot persist a value of type bigint. JSON.stringify cannot serialize this type (throws TypeError). Path: ${formatPath(path)}.`,
121
+ "INVALID_ARGUMENT"
122
+ );
123
+ }
124
+ if (t !== "object") return;
125
+ if (Array.isArray(node)) {
126
+ for (let i = 0; i < node.length; i++) {
127
+ walk(node[i], [...path, String(i)], label);
128
+ }
129
+ return;
130
+ }
131
+ const obj = node;
132
+ if (Object.prototype.hasOwnProperty.call(obj, SERIALIZATION_TAG)) {
133
+ const tagValue = obj[SERIALIZATION_TAG];
134
+ throw new FiregraphError(
135
+ `${label} backend cannot persist an object with a \`${SERIALIZATION_TAG}\` key (value: ${formatTagValue(tagValue)}). Recognised tags are valid only on the Firestore backend (migration-sandbox output); a literal \`${SERIALIZATION_TAG}\` field in user data is reserved and not allowed. Path: ${formatPath(path)}.`,
136
+ "INVALID_ARGUMENT"
137
+ );
138
+ }
139
+ const proto = Object.getPrototypeOf(node);
140
+ if (proto !== null && proto !== Object.prototype) {
141
+ const ctor = node.constructor;
142
+ const ctorName = ctor && typeof ctor.name === "string" ? ctor.name : "<anonymous>";
143
+ if (FIRESTORE_TYPE_NAMES2.has(ctorName)) {
144
+ throw new FiregraphError(
145
+ `${label} backend cannot persist a Firestore ${ctorName} value. Convert to a primitive before writing (e.g. \`ts.toMillis()\` for Timestamp, \`{lat,lng}\` for GeoPoint). Path: ${formatPath(path)}.`,
146
+ "INVALID_ARGUMENT"
147
+ );
148
+ }
149
+ if (node instanceof Date) return;
150
+ throw new FiregraphError(
151
+ `${label} backend cannot persist a class instance of type ${ctorName}. Only plain objects, arrays, and primitives round-trip safely through JSON storage. Path: ${formatPath(path)}.`,
152
+ "INVALID_ARGUMENT"
153
+ );
154
+ }
155
+ for (const key of Object.keys(obj)) {
156
+ walk(obj[key], [...path, key], label);
157
+ }
158
+ }
159
+ function formatPath(path) {
160
+ return path.length === 0 ? "<root>" : path.map((p) => JSON.stringify(p)).join(" > ");
161
+ }
162
+ function formatTagValue(value) {
163
+ if (value === null) return "null";
164
+ if (value === void 0) return "undefined";
165
+ if (typeof value === "string") return JSON.stringify(value);
166
+ if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
167
+ return String(value);
168
+ }
169
+ return typeof value;
170
+ }
12
171
 
13
172
  // src/timestamp.ts
14
173
  var GraphTimestampImpl = class _GraphTimestampImpl {
@@ -37,7 +196,7 @@ var GraphTimestampImpl = class _GraphTimestampImpl {
37
196
 
38
197
  // src/internal/sqlite-index-ddl.ts
39
198
  var IDENT_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
40
- var JSON_PATH_KEY_RE = /^[A-Za-z_][A-Za-z0-9_-]*$/;
199
+ var JSON_PATH_KEY_RE2 = /^[A-Za-z_][A-Za-z0-9_-]*$/;
41
200
  function quoteIdent(name) {
42
201
  if (!IDENT_RE.test(name)) {
43
202
  throw new FiregraphError(
@@ -85,7 +244,7 @@ function compileFieldExpr(path, fieldToColumn) {
85
244
  const suffix = path.slice(5);
86
245
  const parts = suffix.split(".");
87
246
  for (const part of parts) {
88
- if (!JSON_PATH_KEY_RE.test(part)) {
247
+ if (!JSON_PATH_KEY_RE2.test(part)) {
89
248
  throw new FiregraphError(
90
249
  `IndexSpec data path "${path}" has invalid component "${part}". Each component must match /^[A-Za-z_][A-Za-z0-9_-]*$/.`,
91
250
  "INVALID_INDEX"
@@ -180,6 +339,8 @@ function buildDOSchemaStatements(table, options = {}) {
180
339
  }
181
340
 
182
341
  // src/cloudflare/sql.ts
342
+ var DO_BACKEND_LABEL = "DO SQLite";
343
+ var DO_BACKEND_ERR_LABEL = "DO SQLite backend";
183
344
  function compileFieldRef(field) {
184
345
  const column = DO_FIELD_TO_COLUMN[field];
185
346
  if (column) {
@@ -188,7 +349,7 @@ function compileFieldRef(field) {
188
349
  if (field.startsWith("data.")) {
189
350
  const suffix = field.slice(5);
190
351
  for (const part of suffix.split(".")) {
191
- validateJsonPathKey(part);
352
+ validateJsonPathKey(part, DO_BACKEND_ERR_LABEL);
192
353
  }
193
354
  return { expr: `json_extract("data", '$.${suffix}')` };
194
355
  }
@@ -200,18 +361,6 @@ function compileFieldRef(field) {
200
361
  "INVALID_QUERY"
201
362
  );
202
363
  }
203
- var FIRESTORE_TYPE_NAMES = /* @__PURE__ */ new Set([
204
- "Timestamp",
205
- "GeoPoint",
206
- "VectorValue",
207
- "DocumentReference",
208
- "FieldValue"
209
- ]);
210
- function isFirestoreSpecialType(value) {
211
- const ctorName = value.constructor?.name;
212
- if (ctorName && FIRESTORE_TYPE_NAMES.has(ctorName)) return ctorName;
213
- return null;
214
- }
215
364
  function bindValue(value) {
216
365
  if (value === null || value === void 0) return null;
217
366
  if (typeof value === "string" || typeof value === "number" || typeof value === "bigint" || typeof value === "boolean") {
@@ -230,21 +379,6 @@ function bindValue(value) {
230
379
  }
231
380
  return String(value);
232
381
  }
233
- var JSON_PATH_KEY_RE2 = /^[A-Za-z_][A-Za-z0-9_-]*$/;
234
- function validateJsonPathKey(key) {
235
- if (key.length === 0) {
236
- throw new FiregraphError(
237
- "DO SQLite backend: empty JSON path component is not allowed",
238
- "INVALID_QUERY"
239
- );
240
- }
241
- if (!JSON_PATH_KEY_RE2.test(key)) {
242
- throw new FiregraphError(
243
- `DO SQLite backend: data field path component "${key}" is not a safe JSON-path identifier. Allowed pattern: /^[A-Za-z_][A-Za-z0-9_-]*$/. Use replaceData (full-data overwrite) for keys with reserved characters (whitespace, dots, brackets, quotes, etc.).`,
244
- "INVALID_QUERY"
245
- );
246
- }
247
- }
248
382
  function compileFilter(filter, params) {
249
383
  const { expr } = compileFieldRef(filter.field);
250
384
  switch (filter.op) {
@@ -331,11 +465,27 @@ function compileDOSelectByDocId(table, docId) {
331
465
  params: [docId]
332
466
  };
333
467
  }
334
- function compileDOSet(table, docId, record, nowMillis) {
335
- const sql = `INSERT OR REPLACE INTO ${quoteDOIdent(table)} (
336
- doc_id, a_type, a_uid, axb_type, b_type, b_uid, data, v, created_at, updated_at
337
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`;
338
- const params = [
468
+ function compileDOSet(table, docId, record, nowMillis, mode) {
469
+ assertJsonSafePayload(record.data, DO_BACKEND_LABEL);
470
+ if (mode === "replace") {
471
+ const sql2 = `INSERT OR REPLACE INTO ${quoteDOIdent(table)} (
472
+ doc_id, a_type, a_uid, axb_type, b_type, b_uid, data, v, created_at, updated_at
473
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`;
474
+ const params = [
475
+ docId,
476
+ record.aType,
477
+ record.aUid,
478
+ record.axbType,
479
+ record.bType,
480
+ record.bUid,
481
+ JSON.stringify(record.data ?? {}),
482
+ record.v ?? null,
483
+ nowMillis,
484
+ nowMillis
485
+ ];
486
+ return { sql: sql2, params };
487
+ }
488
+ const insertParams = [
339
489
  docId,
340
490
  record.aType,
341
491
  record.aUid,
@@ -347,22 +497,44 @@ function compileDOSet(table, docId, record, nowMillis) {
347
497
  nowMillis,
348
498
  nowMillis
349
499
  ];
350
- return { sql, params };
500
+ const ops = flattenPatch(record.data ?? {});
501
+ const updateParams = [];
502
+ const dataExpr = compileDataOpsExpr(ops, `COALESCE("data", '{}')`, updateParams, DO_BACKEND_ERR_LABEL) ?? `COALESCE("data", '{}')`;
503
+ const sql = `INSERT INTO ${quoteDOIdent(table)} (
504
+ doc_id, a_type, a_uid, axb_type, b_type, b_uid, data, v, created_at, updated_at
505
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
506
+ ON CONFLICT(doc_id) DO UPDATE SET
507
+ "a_type" = excluded."a_type",
508
+ "a_uid" = excluded."a_uid",
509
+ "axb_type" = excluded."axb_type",
510
+ "b_type" = excluded."b_type",
511
+ "b_uid" = excluded."b_uid",
512
+ "data" = ${dataExpr},
513
+ "v" = COALESCE(excluded."v", "v"),
514
+ "created_at" = excluded."created_at",
515
+ "updated_at" = excluded."updated_at"`;
516
+ return { sql, params: [...insertParams, ...updateParams] };
351
517
  }
352
518
  function compileDOUpdate(table, docId, update, nowMillis) {
519
+ assertUpdatePayloadExclusive(update);
353
520
  const setClauses = [];
354
521
  const params = [];
355
522
  if (update.replaceData) {
523
+ assertJsonSafePayload(update.replaceData, DO_BACKEND_LABEL);
356
524
  setClauses.push(`"data" = ?`);
357
525
  params.push(JSON.stringify(update.replaceData));
358
- } else if (update.dataFields && Object.keys(update.dataFields).length > 0) {
359
- const entries = Object.entries(update.dataFields);
360
- const pathArgs = entries.map(() => `?, ?`).join(", ");
361
- setClauses.push(`"data" = json_set(COALESCE("data", '{}'), ${pathArgs})`);
362
- for (const [k, v] of entries) {
363
- validateJsonPathKey(k);
364
- params.push(`$.${k}`);
365
- params.push(bindValue(v));
526
+ } else if (update.dataOps && update.dataOps.length > 0) {
527
+ for (const op of update.dataOps) {
528
+ if (!op.delete) assertJsonSafePayload(op.value, DO_BACKEND_LABEL);
529
+ }
530
+ const expr = compileDataOpsExpr(
531
+ update.dataOps,
532
+ `COALESCE("data", '{}')`,
533
+ params,
534
+ DO_BACKEND_ERR_LABEL
535
+ );
536
+ if (expr !== null) {
537
+ setClauses.push(`"data" = ${expr}`);
366
538
  }
367
539
  }
368
540
  if (update.v !== void 0) {
@@ -458,8 +630,8 @@ var DORPCBatchBackend = class {
458
630
  this.getStub = getStub;
459
631
  }
460
632
  ops = [];
461
- setDoc(docId, record) {
462
- this.ops.push({ kind: "set", docId, record });
633
+ setDoc(docId, record, mode) {
634
+ this.ops.push({ kind: "set", docId, record, mode });
463
635
  }
464
636
  updateDoc(docId, update) {
465
637
  this.ops.push({ kind: "update", docId, update });
@@ -509,8 +681,8 @@ var DORPCBackend = class _DORPCBackend {
509
681
  return wires.map(hydrateDORecord);
510
682
  }
511
683
  // --- Writes ---
512
- async setDoc(docId, record) {
513
- return this.stub._fgSetDoc(docId, record);
684
+ async setDoc(docId, record, mode) {
685
+ return this.stub._fgSetDoc(docId, record, mode);
514
686
  }
515
687
  async updateDoc(docId, update) {
516
688
  return this.stub._fgUpdateDoc(docId, update);
@@ -760,8 +932,8 @@ var FiregraphDO = class extends DurableObject {
760
932
  // ---------------------------------------------------------------------------
761
933
  // RPC: writes
762
934
  // ---------------------------------------------------------------------------
763
- async _fgSetDoc(docId, record) {
764
- const stmt = compileDOSet(this.table, docId, record, Date.now());
935
+ async _fgSetDoc(docId, record, mode) {
936
+ const stmt = compileDOSet(this.table, docId, record, Date.now(), mode);
765
937
  this.execRun(stmt);
766
938
  }
767
939
  async _fgUpdateDoc(docId, update) {
@@ -791,7 +963,7 @@ var FiregraphDO = class extends DurableObject {
791
963
  const statements = ops.map((op) => {
792
964
  switch (op.kind) {
793
965
  case "set":
794
- return compileDOSet(this.table, op.docId, op.record, now);
966
+ return compileDOSet(this.table, op.docId, op.record, now, op.mode);
795
967
  case "update":
796
968
  return compileDOUpdate(this.table, op.docId, op.update, now);
797
969
  case "delete":
@@ -940,8 +1112,14 @@ var FiregraphDO = class extends DurableObject {
940
1112
  export {
941
1113
  DORPCBackend,
942
1114
  FiregraphDO,
1115
+ META_EDGE_TYPE,
1116
+ META_NODE_TYPE,
943
1117
  buildDOSchemaStatements,
944
1118
  createDOClient,
945
- createSiblingClient
1119
+ createMergedRegistry,
1120
+ createRegistry,
1121
+ createSiblingClient,
1122
+ deleteField,
1123
+ generateId
946
1124
  };
947
1125
  //# sourceMappingURL=index.js.map