@typicalday/firegraph 0.11.1 → 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.
- package/README.md +40 -15
- package/dist/backend-BsR0lnFL.d.ts +200 -0
- package/dist/backend-Ct-fLlkG.d.cts +200 -0
- package/dist/backend.cjs +143 -2
- package/dist/backend.cjs.map +1 -1
- package/dist/backend.d.cts +3 -3
- package/dist/backend.d.ts +3 -3
- package/dist/backend.js +13 -4
- package/dist/backend.js.map +1 -1
- package/dist/chunk-AWW4MUJ5.js +245 -0
- package/dist/chunk-AWW4MUJ5.js.map +1 -0
- package/dist/{chunk-5753Y42M.js → chunk-C2QMD7RY.js} +6 -10
- package/dist/chunk-C2QMD7RY.js.map +1 -0
- package/dist/chunk-EQJUUVFG.js +14 -0
- package/dist/chunk-EQJUUVFG.js.map +1 -0
- package/dist/{chunk-6SB34IPQ.js → chunk-HONQY4HF.js} +100 -28
- package/dist/chunk-HONQY4HF.js.map +1 -0
- package/dist/cloudflare/index.cjs +509 -102
- package/dist/cloudflare/index.cjs.map +1 -1
- package/dist/cloudflare/index.d.cts +45 -17
- package/dist/cloudflare/index.d.ts +45 -17
- package/dist/cloudflare/index.js +265 -74
- package/dist/cloudflare/index.js.map +1 -1
- package/dist/codegen/index.d.cts +1 -1
- package/dist/codegen/index.d.ts +1 -1
- package/dist/index.cjs +291 -47
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +59 -77
- package/dist/index.d.ts +59 -77
- package/dist/index.js +58 -28
- package/dist/index.js.map +1 -1
- package/dist/registry-B1qsVL0E.d.cts +64 -0
- package/dist/registry-Fi074zVa.d.ts +64 -0
- package/dist/{serialization-ZZ7RSDRX.js → serialization-OE2PFZMY.js} +6 -4
- package/dist/{types-BGWxcpI_.d.cts → types-DxYLy8Ol.d.cts} +36 -2
- package/dist/{types-BGWxcpI_.d.ts → types-DxYLy8Ol.d.ts} +36 -2
- package/package.json +8 -3
- package/dist/backend-U-MLShlg.d.ts +0 -97
- package/dist/backend-np4gEVhB.d.cts +0 -97
- package/dist/chunk-5753Y42M.js.map +0 -1
- package/dist/chunk-6SB34IPQ.js.map +0 -1
- package/dist/chunk-R7CRGYY4.js +0 -94
- package/dist/chunk-R7CRGYY4.js.map +0 -1
- /package/dist/{serialization-ZZ7RSDRX.js.map → serialization-OE2PFZMY.js.map} +0 -0
package/dist/index.cjs
CHANGED
|
@@ -30,6 +30,21 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
30
30
|
));
|
|
31
31
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
32
32
|
|
|
33
|
+
// src/internal/serialization-tag.ts
|
|
34
|
+
function isTaggedValue(value) {
|
|
35
|
+
if (value === null || typeof value !== "object") return false;
|
|
36
|
+
const tag = value[SERIALIZATION_TAG];
|
|
37
|
+
return typeof tag === "string" && KNOWN_TYPES.has(tag);
|
|
38
|
+
}
|
|
39
|
+
var SERIALIZATION_TAG, KNOWN_TYPES;
|
|
40
|
+
var init_serialization_tag = __esm({
|
|
41
|
+
"src/internal/serialization-tag.ts"() {
|
|
42
|
+
"use strict";
|
|
43
|
+
SERIALIZATION_TAG = "__firegraph_ser__";
|
|
44
|
+
KNOWN_TYPES = /* @__PURE__ */ new Set(["Timestamp", "GeoPoint", "VectorValue", "DocumentReference"]);
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
|
|
33
48
|
// src/serialization.ts
|
|
34
49
|
var serialization_exports = {};
|
|
35
50
|
__export(serialization_exports, {
|
|
@@ -38,11 +53,6 @@ __export(serialization_exports, {
|
|
|
38
53
|
isTaggedValue: () => isTaggedValue,
|
|
39
54
|
serializeFirestoreTypes: () => serializeFirestoreTypes
|
|
40
55
|
});
|
|
41
|
-
function isTaggedValue(value) {
|
|
42
|
-
if (value === null || typeof value !== "object") return false;
|
|
43
|
-
const tag = value[SERIALIZATION_TAG];
|
|
44
|
-
return typeof tag === "string" && KNOWN_TYPES.has(tag);
|
|
45
|
-
}
|
|
46
56
|
function isTimestamp(value) {
|
|
47
57
|
return value instanceof import_firestore.Timestamp;
|
|
48
58
|
}
|
|
@@ -143,13 +153,13 @@ function deserializeValue(value, db) {
|
|
|
143
153
|
}
|
|
144
154
|
return result;
|
|
145
155
|
}
|
|
146
|
-
var import_firestore,
|
|
156
|
+
var import_firestore, _docRefWarned;
|
|
147
157
|
var init_serialization = __esm({
|
|
148
158
|
"src/serialization.ts"() {
|
|
149
159
|
"use strict";
|
|
150
160
|
import_firestore = require("@google-cloud/firestore");
|
|
151
|
-
|
|
152
|
-
|
|
161
|
+
init_serialization_tag();
|
|
162
|
+
init_serialization_tag();
|
|
153
163
|
_docRefWarned = false;
|
|
154
164
|
}
|
|
155
165
|
});
|
|
@@ -202,6 +212,7 @@ __export(index_exports, {
|
|
|
202
212
|
defaultExecutor: () => defaultExecutor,
|
|
203
213
|
defineConfig: () => defineConfig,
|
|
204
214
|
defineViews: () => defineViews,
|
|
215
|
+
deleteField: () => deleteField,
|
|
205
216
|
deserializeFirestoreTypes: () => deserializeFirestoreTypes,
|
|
206
217
|
destroySandboxWorker: () => destroySandboxWorker,
|
|
207
218
|
discoverEntities: () => discoverEntities,
|
|
@@ -255,6 +266,146 @@ function computeEdgeDocId(aUid, axbType, bUid) {
|
|
|
255
266
|
return `${shard}${SHARD_SEPARATOR}${aUid}${SHARD_SEPARATOR}${axbType}${SHARD_SEPARATOR}${bUid}`;
|
|
256
267
|
}
|
|
257
268
|
|
|
269
|
+
// src/internal/write-plan.ts
|
|
270
|
+
init_serialization_tag();
|
|
271
|
+
var DELETE_FIELD = /* @__PURE__ */ Symbol.for("firegraph.deleteField");
|
|
272
|
+
function deleteField() {
|
|
273
|
+
return DELETE_FIELD;
|
|
274
|
+
}
|
|
275
|
+
function isDeleteSentinel(value) {
|
|
276
|
+
return value === DELETE_FIELD;
|
|
277
|
+
}
|
|
278
|
+
var FIRESTORE_TERMINAL_CTOR = /* @__PURE__ */ new Set([
|
|
279
|
+
"Timestamp",
|
|
280
|
+
"GeoPoint",
|
|
281
|
+
"VectorValue",
|
|
282
|
+
"DocumentReference",
|
|
283
|
+
"FieldValue",
|
|
284
|
+
"NumericIncrementTransform",
|
|
285
|
+
"ArrayUnionTransform",
|
|
286
|
+
"ArrayRemoveTransform",
|
|
287
|
+
"ServerTimestampTransform",
|
|
288
|
+
"DeleteTransform"
|
|
289
|
+
]);
|
|
290
|
+
function isTerminalValue(value) {
|
|
291
|
+
if (value === null) return true;
|
|
292
|
+
const t = typeof value;
|
|
293
|
+
if (t !== "object") return true;
|
|
294
|
+
if (Array.isArray(value)) return true;
|
|
295
|
+
if (isTaggedValue(value)) return true;
|
|
296
|
+
const proto = Object.getPrototypeOf(value);
|
|
297
|
+
if (proto === null || proto === Object.prototype) return false;
|
|
298
|
+
const ctor = value.constructor;
|
|
299
|
+
if (ctor && typeof ctor.name === "string" && FIRESTORE_TERMINAL_CTOR.has(ctor.name)) return true;
|
|
300
|
+
return true;
|
|
301
|
+
}
|
|
302
|
+
var SAFE_KEY_RE = /^[A-Za-z_][A-Za-z0-9_-]*$/;
|
|
303
|
+
function assertUpdatePayloadExclusive(update) {
|
|
304
|
+
if (update.replaceData !== void 0 && update.dataOps !== void 0) {
|
|
305
|
+
throw new Error(
|
|
306
|
+
"firegraph: UpdatePayload cannot specify both `replaceData` and `dataOps`. Use one or the other \u2014 `replaceData` is the migration-write-back form, `dataOps` is the standard partial-update form."
|
|
307
|
+
);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
function assertNoDeleteSentinels(data, callerLabel) {
|
|
311
|
+
walkForDeleteSentinels(data, [], { kind: "root" }, ({ path }) => {
|
|
312
|
+
const where = path.length === 0 ? "<root>" : path.map((p) => JSON.stringify(p)).join(" > ");
|
|
313
|
+
throw new Error(
|
|
314
|
+
`firegraph: ${callerLabel} payload contains a deleteField() sentinel at ${where}. deleteField() is only valid inside updateNode/updateEdge \u2014 full-data writes (put*, replace*) cannot delete individual fields. Use updateNode with a deleteField() value, or omit the field from the replace payload.`
|
|
315
|
+
);
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
function walkForDeleteSentinels(node, path, parent, visit) {
|
|
319
|
+
if (node === null || node === void 0) return;
|
|
320
|
+
if (isDeleteSentinel(node)) {
|
|
321
|
+
visit({ path, parent });
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
if (typeof node !== "object") return;
|
|
325
|
+
if (isTaggedValue(node)) return;
|
|
326
|
+
if (Array.isArray(node)) {
|
|
327
|
+
for (let i = 0; i < node.length; i++) {
|
|
328
|
+
walkForDeleteSentinels(node[i], [...path, String(i)], { kind: "array", index: i }, visit);
|
|
329
|
+
}
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
const proto = Object.getPrototypeOf(node);
|
|
333
|
+
if (proto !== null && proto !== Object.prototype) return;
|
|
334
|
+
const obj = node;
|
|
335
|
+
for (const key of Object.keys(obj)) {
|
|
336
|
+
walkForDeleteSentinels(obj[key], [...path, key], { kind: "object" }, visit);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
function assertSafePath(path) {
|
|
340
|
+
for (const seg of path) {
|
|
341
|
+
if (!SAFE_KEY_RE.test(seg)) {
|
|
342
|
+
throw new Error(
|
|
343
|
+
`firegraph: unsafe object key ${JSON.stringify(seg)} at path ${path.map((p) => JSON.stringify(p)).join(" > ")}. Keys used inside update payloads must match /^[A-Za-z_][A-Za-z0-9_-]*$/ so they can be embedded safely in SQLite JSON paths.`
|
|
344
|
+
);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
function flattenPatch(data) {
|
|
349
|
+
const ops = [];
|
|
350
|
+
walk(data, [], ops);
|
|
351
|
+
return ops;
|
|
352
|
+
}
|
|
353
|
+
function assertNoDeleteSentinelsInArrayValue(arr, arrayPath) {
|
|
354
|
+
walkForDeleteSentinels(arr, arrayPath, { kind: "root" }, ({ parent }) => {
|
|
355
|
+
const arrayPathStr = arrayPath.length === 0 ? "<root>" : arrayPath.map((p) => JSON.stringify(p)).join(" > ");
|
|
356
|
+
if (parent.kind === "array") {
|
|
357
|
+
throw new Error(
|
|
358
|
+
`firegraph: deleteField() sentinel at index ${parent.index} inside an array at path ${arrayPathStr}. Arrays are terminal in update payloads (replaced as a unit), so the sentinel would be silently dropped by JSON serialization. To remove the field entirely, pass deleteField() in place of the whole array.`
|
|
359
|
+
);
|
|
360
|
+
}
|
|
361
|
+
throw new Error(
|
|
362
|
+
`firegraph: deleteField() sentinel inside an array element at path ${arrayPathStr}. Arrays are terminal in update payloads \u2014 the sentinel would be silently dropped by JSON serialization.`
|
|
363
|
+
);
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
function walk(node, path, out) {
|
|
367
|
+
if (node === void 0) return;
|
|
368
|
+
if (isDeleteSentinel(node)) {
|
|
369
|
+
if (path.length === 0) {
|
|
370
|
+
throw new Error("firegraph: deleteField() cannot be the entire update payload.");
|
|
371
|
+
}
|
|
372
|
+
assertSafePath(path);
|
|
373
|
+
out.push({ path: [...path], value: void 0, delete: true });
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
if (isTerminalValue(node)) {
|
|
377
|
+
if (path.length === 0) {
|
|
378
|
+
throw new Error(
|
|
379
|
+
"firegraph: update payload must be a plain object. Got " + (node === null ? "null" : Array.isArray(node) ? "array" : typeof node) + "."
|
|
380
|
+
);
|
|
381
|
+
}
|
|
382
|
+
if (Array.isArray(node)) {
|
|
383
|
+
assertNoDeleteSentinelsInArrayValue(node, path);
|
|
384
|
+
}
|
|
385
|
+
assertSafePath(path);
|
|
386
|
+
out.push({ path: [...path], value: node, delete: false });
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
const obj = node;
|
|
390
|
+
const keys = Object.keys(obj);
|
|
391
|
+
if (keys.length === 0) {
|
|
392
|
+
if (path.length > 0) {
|
|
393
|
+
assertSafePath(path);
|
|
394
|
+
out.push({ path: [...path], value: {}, delete: false });
|
|
395
|
+
}
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
for (const key of keys) {
|
|
399
|
+
if (key === SERIALIZATION_TAG) {
|
|
400
|
+
const where = path.length === 0 ? "<root>" : path.map((p) => JSON.stringify(p)).join(" > ");
|
|
401
|
+
throw new Error(
|
|
402
|
+
`firegraph: update payload contains a literal \`${SERIALIZATION_TAG}\` key at ${where}. That key is reserved for firegraph's serialization envelope and cannot appear on a plain object in user data. Use a different field name, or pass a recognized tagged value through replaceNode/replaceEdge instead.`
|
|
403
|
+
);
|
|
404
|
+
}
|
|
405
|
+
walk(obj[key], [...path, key], out);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
258
409
|
// src/batch.ts
|
|
259
410
|
function buildWritableNodeRecord(aType, uid, data) {
|
|
260
411
|
return { aType, aUid: uid, axbType: NODE_RELATION, bType: aType, bUid: uid, data };
|
|
@@ -269,6 +420,19 @@ var GraphBatchImpl = class {
|
|
|
269
420
|
this.scopePath = scopePath;
|
|
270
421
|
}
|
|
271
422
|
async putNode(aType, uid, data) {
|
|
423
|
+
this.writeNode(aType, uid, data, "merge");
|
|
424
|
+
}
|
|
425
|
+
async putEdge(aType, aUid, axbType, bType, bUid, data) {
|
|
426
|
+
this.writeEdge(aType, aUid, axbType, bType, bUid, data, "merge");
|
|
427
|
+
}
|
|
428
|
+
async replaceNode(aType, uid, data) {
|
|
429
|
+
this.writeNode(aType, uid, data, "replace");
|
|
430
|
+
}
|
|
431
|
+
async replaceEdge(aType, aUid, axbType, bType, bUid, data) {
|
|
432
|
+
this.writeEdge(aType, aUid, axbType, bType, bUid, data, "replace");
|
|
433
|
+
}
|
|
434
|
+
writeNode(aType, uid, data, mode) {
|
|
435
|
+
assertNoDeleteSentinels(data, mode === "replace" ? "replaceNode" : "putNode");
|
|
272
436
|
if (this.registry) {
|
|
273
437
|
this.registry.validate(aType, NODE_RELATION, aType, data, this.scopePath);
|
|
274
438
|
}
|
|
@@ -280,9 +444,10 @@ var GraphBatchImpl = class {
|
|
|
280
444
|
record.v = entry.schemaVersion;
|
|
281
445
|
}
|
|
282
446
|
}
|
|
283
|
-
this.backend.setDoc(docId, record);
|
|
447
|
+
this.backend.setDoc(docId, record, mode);
|
|
284
448
|
}
|
|
285
|
-
|
|
449
|
+
writeEdge(aType, aUid, axbType, bType, bUid, data, mode) {
|
|
450
|
+
assertNoDeleteSentinels(data, mode === "replace" ? "replaceEdge" : "putEdge");
|
|
286
451
|
if (this.registry) {
|
|
287
452
|
this.registry.validate(aType, axbType, bType, data, this.scopePath);
|
|
288
453
|
}
|
|
@@ -294,11 +459,15 @@ var GraphBatchImpl = class {
|
|
|
294
459
|
record.v = entry.schemaVersion;
|
|
295
460
|
}
|
|
296
461
|
}
|
|
297
|
-
this.backend.setDoc(docId, record);
|
|
462
|
+
this.backend.setDoc(docId, record, mode);
|
|
298
463
|
}
|
|
299
464
|
async updateNode(uid, data) {
|
|
300
465
|
const docId = computeNodeDocId(uid);
|
|
301
|
-
this.backend.updateDoc(docId, {
|
|
466
|
+
this.backend.updateDoc(docId, { dataOps: flattenPatch(data) });
|
|
467
|
+
}
|
|
468
|
+
async updateEdge(aUid, axbType, bUid, data) {
|
|
469
|
+
const docId = computeEdgeDocId(aUid, axbType, bUid);
|
|
470
|
+
this.backend.updateDoc(docId, { dataOps: flattenPatch(data) });
|
|
302
471
|
}
|
|
303
472
|
async removeNode(uid) {
|
|
304
473
|
const docId = computeNodeDocId(uid);
|
|
@@ -396,23 +565,29 @@ var CrossBackendTransactionError = class extends FiregraphError {
|
|
|
396
565
|
};
|
|
397
566
|
|
|
398
567
|
// src/json-schema.ts
|
|
399
|
-
var
|
|
400
|
-
var
|
|
401
|
-
var ajv = new import_ajv.default({ allErrors: true, strict: false });
|
|
402
|
-
(0, import_ajv_formats.default)(ajv);
|
|
568
|
+
var import_json_schema = require("@cfworker/json-schema");
|
|
569
|
+
var MAX_RENDERED_ERRORS = 20;
|
|
403
570
|
function compileSchema(schema, label) {
|
|
404
|
-
const
|
|
571
|
+
const validator = new import_json_schema.Validator(schema, "2020-12", false);
|
|
405
572
|
return (data) => {
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
const
|
|
573
|
+
const result = validator.validate(data);
|
|
574
|
+
if (!result.valid) {
|
|
575
|
+
const total = result.errors.length;
|
|
576
|
+
const head = result.errors.slice(0, MAX_RENDERED_ERRORS).map(formatError).join("; ");
|
|
577
|
+
const overflow = total > MAX_RENDERED_ERRORS ? ` (+${total - MAX_RENDERED_ERRORS} more)` : "";
|
|
409
578
|
throw new ValidationError(
|
|
410
|
-
`Data validation failed${label ? " for " + label : ""}: ${
|
|
411
|
-
errors
|
|
579
|
+
`Data validation failed${label ? " for " + label : ""}: ${head}${overflow}`,
|
|
580
|
+
result.errors
|
|
412
581
|
);
|
|
413
582
|
}
|
|
414
583
|
};
|
|
415
584
|
}
|
|
585
|
+
function formatError(err) {
|
|
586
|
+
const path = err.instanceLocation.replace(/^#/, "") || "/";
|
|
587
|
+
const keyword = err.keyword ? `[${err.keyword}] ` : "";
|
|
588
|
+
const detail = err.error ? `: ${keyword}${err.error}` : "";
|
|
589
|
+
return `${path}${detail}`;
|
|
590
|
+
}
|
|
416
591
|
function jsonSchemaToFieldMeta(schema) {
|
|
417
592
|
if (!schema || schema.type !== "object" || !schema.properties) return [];
|
|
418
593
|
const requiredSet = new Set(Array.isArray(schema.required) ? schema.required : []);
|
|
@@ -1145,8 +1320,11 @@ var BOOTSTRAP_ENTRIES = [
|
|
|
1145
1320
|
description: "Meta-type: defines an edge type"
|
|
1146
1321
|
}
|
|
1147
1322
|
];
|
|
1323
|
+
var _bootstrapRegistry = null;
|
|
1148
1324
|
function createBootstrapRegistry() {
|
|
1149
|
-
|
|
1325
|
+
if (_bootstrapRegistry) return _bootstrapRegistry;
|
|
1326
|
+
_bootstrapRegistry = createRegistry([...BOOTSTRAP_ENTRIES]);
|
|
1327
|
+
return _bootstrapRegistry;
|
|
1150
1328
|
}
|
|
1151
1329
|
function generateDeterministicUid(metaType, name) {
|
|
1152
1330
|
const hash = (0, import_node_crypto3.createHash)("sha256").update(`${metaType}:${name}`).digest("base64url");
|
|
@@ -1400,6 +1578,19 @@ var GraphTransactionImpl = class {
|
|
|
1400
1578
|
return results.map((r) => r.record);
|
|
1401
1579
|
}
|
|
1402
1580
|
async putNode(aType, uid, data) {
|
|
1581
|
+
await this.writeNode(aType, uid, data, "merge");
|
|
1582
|
+
}
|
|
1583
|
+
async putEdge(aType, aUid, axbType, bType, bUid, data) {
|
|
1584
|
+
await this.writeEdge(aType, aUid, axbType, bType, bUid, data, "merge");
|
|
1585
|
+
}
|
|
1586
|
+
async replaceNode(aType, uid, data) {
|
|
1587
|
+
await this.writeNode(aType, uid, data, "replace");
|
|
1588
|
+
}
|
|
1589
|
+
async replaceEdge(aType, aUid, axbType, bType, bUid, data) {
|
|
1590
|
+
await this.writeEdge(aType, aUid, axbType, bType, bUid, data, "replace");
|
|
1591
|
+
}
|
|
1592
|
+
async writeNode(aType, uid, data, mode) {
|
|
1593
|
+
assertNoDeleteSentinels(data, mode === "replace" ? "replaceNode" : "putNode");
|
|
1403
1594
|
if (this.registry) {
|
|
1404
1595
|
this.registry.validate(aType, NODE_RELATION, aType, data, this.scopePath);
|
|
1405
1596
|
}
|
|
@@ -1411,9 +1602,10 @@ var GraphTransactionImpl = class {
|
|
|
1411
1602
|
record.v = entry.schemaVersion;
|
|
1412
1603
|
}
|
|
1413
1604
|
}
|
|
1414
|
-
await this.backend.setDoc(docId, record);
|
|
1605
|
+
await this.backend.setDoc(docId, record, mode);
|
|
1415
1606
|
}
|
|
1416
|
-
async
|
|
1607
|
+
async writeEdge(aType, aUid, axbType, bType, bUid, data, mode) {
|
|
1608
|
+
assertNoDeleteSentinels(data, mode === "replace" ? "replaceEdge" : "putEdge");
|
|
1417
1609
|
if (this.registry) {
|
|
1418
1610
|
this.registry.validate(aType, axbType, bType, data, this.scopePath);
|
|
1419
1611
|
}
|
|
@@ -1425,11 +1617,15 @@ var GraphTransactionImpl = class {
|
|
|
1425
1617
|
record.v = entry.schemaVersion;
|
|
1426
1618
|
}
|
|
1427
1619
|
}
|
|
1428
|
-
await this.backend.setDoc(docId, record);
|
|
1620
|
+
await this.backend.setDoc(docId, record, mode);
|
|
1429
1621
|
}
|
|
1430
1622
|
async updateNode(uid, data) {
|
|
1431
1623
|
const docId = computeNodeDocId(uid);
|
|
1432
|
-
await this.backend.updateDoc(docId, {
|
|
1624
|
+
await this.backend.updateDoc(docId, { dataOps: flattenPatch(data) });
|
|
1625
|
+
}
|
|
1626
|
+
async updateEdge(aUid, axbType, bUid, data) {
|
|
1627
|
+
const docId = computeEdgeDocId(aUid, axbType, bUid);
|
|
1628
|
+
await this.backend.updateDoc(docId, { dataOps: flattenPatch(data) });
|
|
1433
1629
|
}
|
|
1434
1630
|
async removeNode(uid) {
|
|
1435
1631
|
const docId = computeNodeDocId(uid);
|
|
@@ -1628,6 +1824,19 @@ var GraphClientImpl = class _GraphClientImpl {
|
|
|
1628
1824
|
// GraphWriter
|
|
1629
1825
|
// ---------------------------------------------------------------------------
|
|
1630
1826
|
async putNode(aType, uid, data) {
|
|
1827
|
+
await this.writeNode(aType, uid, data, "merge");
|
|
1828
|
+
}
|
|
1829
|
+
async putEdge(aType, aUid, axbType, bType, bUid, data) {
|
|
1830
|
+
await this.writeEdge(aType, aUid, axbType, bType, bUid, data, "merge");
|
|
1831
|
+
}
|
|
1832
|
+
async replaceNode(aType, uid, data) {
|
|
1833
|
+
await this.writeNode(aType, uid, data, "replace");
|
|
1834
|
+
}
|
|
1835
|
+
async replaceEdge(aType, aUid, axbType, bType, bUid, data) {
|
|
1836
|
+
await this.writeEdge(aType, aUid, axbType, bType, bUid, data, "replace");
|
|
1837
|
+
}
|
|
1838
|
+
async writeNode(aType, uid, data, mode) {
|
|
1839
|
+
assertNoDeleteSentinels(data, mode === "replace" ? "replaceNode" : "putNode");
|
|
1631
1840
|
const registry = this.getRegistryForType(aType);
|
|
1632
1841
|
if (registry) {
|
|
1633
1842
|
registry.validate(aType, NODE_RELATION, aType, data, this.backend.scopePath);
|
|
@@ -1641,9 +1850,10 @@ var GraphClientImpl = class _GraphClientImpl {
|
|
|
1641
1850
|
record.v = entry.schemaVersion;
|
|
1642
1851
|
}
|
|
1643
1852
|
}
|
|
1644
|
-
await backend.setDoc(docId, record);
|
|
1853
|
+
await backend.setDoc(docId, record, mode);
|
|
1645
1854
|
}
|
|
1646
|
-
async
|
|
1855
|
+
async writeEdge(aType, aUid, axbType, bType, bUid, data, mode) {
|
|
1856
|
+
assertNoDeleteSentinels(data, mode === "replace" ? "replaceEdge" : "putEdge");
|
|
1647
1857
|
const registry = this.getRegistryForType(aType);
|
|
1648
1858
|
if (registry) {
|
|
1649
1859
|
registry.validate(aType, axbType, bType, data, this.backend.scopePath);
|
|
@@ -1657,11 +1867,15 @@ var GraphClientImpl = class _GraphClientImpl {
|
|
|
1657
1867
|
record.v = entry.schemaVersion;
|
|
1658
1868
|
}
|
|
1659
1869
|
}
|
|
1660
|
-
await backend.setDoc(docId, record);
|
|
1870
|
+
await backend.setDoc(docId, record, mode);
|
|
1661
1871
|
}
|
|
1662
1872
|
async updateNode(uid, data) {
|
|
1663
1873
|
const docId = computeNodeDocId(uid);
|
|
1664
|
-
await this.backend.updateDoc(docId, {
|
|
1874
|
+
await this.backend.updateDoc(docId, { dataOps: flattenPatch(data) });
|
|
1875
|
+
}
|
|
1876
|
+
async updateEdge(aUid, axbType, bUid, data) {
|
|
1877
|
+
const docId = computeEdgeDocId(aUid, axbType, bUid);
|
|
1878
|
+
await this.backend.updateDoc(docId, { dataOps: flattenPatch(data) });
|
|
1665
1879
|
}
|
|
1666
1880
|
async removeNode(uid) {
|
|
1667
1881
|
const docId = computeNodeDocId(uid);
|
|
@@ -2335,8 +2549,12 @@ function createFirestoreAdapter(db, collectionPath) {
|
|
|
2335
2549
|
if (!snap.exists) return null;
|
|
2336
2550
|
return snap.data();
|
|
2337
2551
|
},
|
|
2338
|
-
async setDoc(docId, data) {
|
|
2339
|
-
|
|
2552
|
+
async setDoc(docId, data, options) {
|
|
2553
|
+
if (options?.merge) {
|
|
2554
|
+
await collectionRef.doc(docId).set(data, { merge: true });
|
|
2555
|
+
} else {
|
|
2556
|
+
await collectionRef.doc(docId).set(data);
|
|
2557
|
+
}
|
|
2340
2558
|
},
|
|
2341
2559
|
async updateDoc(docId, data) {
|
|
2342
2560
|
await collectionRef.doc(docId).update(data);
|
|
@@ -2368,8 +2586,12 @@ function createTransactionAdapter(db, collectionPath, tx) {
|
|
|
2368
2586
|
if (!snap.exists) return null;
|
|
2369
2587
|
return snap.data();
|
|
2370
2588
|
},
|
|
2371
|
-
setDoc(docId, data) {
|
|
2372
|
-
|
|
2589
|
+
setDoc(docId, data, options) {
|
|
2590
|
+
if (options?.merge) {
|
|
2591
|
+
tx.set(collectionRef.doc(docId), data, { merge: true });
|
|
2592
|
+
} else {
|
|
2593
|
+
tx.set(collectionRef.doc(docId), data);
|
|
2594
|
+
}
|
|
2373
2595
|
},
|
|
2374
2596
|
updateDoc(docId, data) {
|
|
2375
2597
|
tx.update(collectionRef.doc(docId), data);
|
|
@@ -2397,8 +2619,12 @@ function createBatchAdapter(db, collectionPath) {
|
|
|
2397
2619
|
const collectionRef = db.collection(collectionPath);
|
|
2398
2620
|
const batch = db.batch();
|
|
2399
2621
|
return {
|
|
2400
|
-
setDoc(docId, data) {
|
|
2401
|
-
|
|
2622
|
+
setDoc(docId, data, options) {
|
|
2623
|
+
if (options?.merge) {
|
|
2624
|
+
batch.set(collectionRef.doc(docId), data, { merge: true });
|
|
2625
|
+
} else {
|
|
2626
|
+
batch.set(collectionRef.doc(docId), data);
|
|
2627
|
+
}
|
|
2402
2628
|
},
|
|
2403
2629
|
updateDoc(docId, data) {
|
|
2404
2630
|
batch.update(collectionRef.doc(docId), data);
|
|
@@ -2474,16 +2700,21 @@ function createPipelineQueryAdapter(db, collectionPath) {
|
|
|
2474
2700
|
}
|
|
2475
2701
|
|
|
2476
2702
|
// src/internal/firestore-backend.ts
|
|
2703
|
+
function dottedDataPath(op) {
|
|
2704
|
+
assertSafePath(op.path);
|
|
2705
|
+
return `data.${op.path.join(".")}`;
|
|
2706
|
+
}
|
|
2477
2707
|
function buildFirestoreUpdate(update, db) {
|
|
2708
|
+
assertUpdatePayloadExclusive(update);
|
|
2478
2709
|
const out = {
|
|
2479
2710
|
updatedAt: import_firestore2.FieldValue.serverTimestamp()
|
|
2480
2711
|
};
|
|
2481
2712
|
if (update.replaceData) {
|
|
2482
2713
|
out.data = deserializeFirestoreTypes(update.replaceData, db);
|
|
2483
|
-
}
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
out[
|
|
2714
|
+
} else if (update.dataOps) {
|
|
2715
|
+
for (const op of update.dataOps) {
|
|
2716
|
+
const key = dottedDataPath(op);
|
|
2717
|
+
out[key] = op.delete ? import_firestore2.FieldValue.delete() : op.value;
|
|
2487
2718
|
}
|
|
2488
2719
|
}
|
|
2489
2720
|
if (update.v !== void 0) {
|
|
@@ -2517,8 +2748,12 @@ var FirestoreTransactionBackend = class {
|
|
|
2517
2748
|
query(filters, options) {
|
|
2518
2749
|
return this.adapter.query(filters, options);
|
|
2519
2750
|
}
|
|
2520
|
-
async setDoc(docId, record) {
|
|
2521
|
-
this.adapter.setDoc(
|
|
2751
|
+
async setDoc(docId, record, mode) {
|
|
2752
|
+
this.adapter.setDoc(
|
|
2753
|
+
docId,
|
|
2754
|
+
stampWritableRecord(record),
|
|
2755
|
+
mode === "merge" ? { merge: true } : void 0
|
|
2756
|
+
);
|
|
2522
2757
|
}
|
|
2523
2758
|
async updateDoc(docId, update) {
|
|
2524
2759
|
this.adapter.updateDoc(docId, buildFirestoreUpdate(update, this.db));
|
|
@@ -2532,8 +2767,12 @@ var FirestoreBatchBackend = class {
|
|
|
2532
2767
|
this.adapter = adapter;
|
|
2533
2768
|
this.db = db;
|
|
2534
2769
|
}
|
|
2535
|
-
setDoc(docId, record) {
|
|
2536
|
-
this.adapter.setDoc(
|
|
2770
|
+
setDoc(docId, record, mode) {
|
|
2771
|
+
this.adapter.setDoc(
|
|
2772
|
+
docId,
|
|
2773
|
+
stampWritableRecord(record),
|
|
2774
|
+
mode === "merge" ? { merge: true } : void 0
|
|
2775
|
+
);
|
|
2537
2776
|
}
|
|
2538
2777
|
updateDoc(docId, update) {
|
|
2539
2778
|
this.adapter.updateDoc(docId, buildFirestoreUpdate(update, this.db));
|
|
@@ -2571,8 +2810,12 @@ var FirestoreBackendImpl = class _FirestoreBackendImpl {
|
|
|
2571
2810
|
return this.adapter.query(filters, options);
|
|
2572
2811
|
}
|
|
2573
2812
|
// --- Writes ---
|
|
2574
|
-
setDoc(docId, record) {
|
|
2575
|
-
return this.adapter.setDoc(
|
|
2813
|
+
setDoc(docId, record, mode) {
|
|
2814
|
+
return this.adapter.setDoc(
|
|
2815
|
+
docId,
|
|
2816
|
+
stampWritableRecord(record),
|
|
2817
|
+
mode === "merge" ? { merge: true } : void 0
|
|
2818
|
+
);
|
|
2576
2819
|
}
|
|
2577
2820
|
updateDoc(docId, update) {
|
|
2578
2821
|
return this.adapter.updateDoc(docId, buildFirestoreUpdate(update, this.db));
|
|
@@ -3432,6 +3675,7 @@ function defineViews(input) {
|
|
|
3432
3675
|
defaultExecutor,
|
|
3433
3676
|
defineConfig,
|
|
3434
3677
|
defineViews,
|
|
3678
|
+
deleteField,
|
|
3435
3679
|
deserializeFirestoreTypes,
|
|
3436
3680
|
destroySandboxWorker,
|
|
3437
3681
|
discoverEntities,
|