@typicalday/firegraph 0.13.0 → 0.14.1

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 (35) hide show
  1. package/README.md +57 -1
  2. package/dist/backend.cjs +2 -3
  3. package/dist/backend.cjs.map +1 -1
  4. package/dist/backend.js +1 -1
  5. package/dist/{chunk-WRTFC5NG.js → chunk-3AHHXMWX.js} +2 -2
  6. package/dist/{chunk-PAD7WFFU.js → chunk-DJI3VXXA.js} +36 -10
  7. package/dist/chunk-DJI3VXXA.js.map +1 -0
  8. package/dist/{chunk-4MMQ5W74.js → chunk-NNBSUOOF.js} +7 -6
  9. package/dist/chunk-NNBSUOOF.js.map +1 -0
  10. package/dist/{chunk-TK64DNVK.js → chunk-SIHE4UY4.js} +3 -4
  11. package/dist/chunk-SIHE4UY4.js.map +1 -0
  12. package/dist/cloudflare/index.cjs +7 -7
  13. package/dist/cloudflare/index.cjs.map +1 -1
  14. package/dist/cloudflare/index.js +3 -3
  15. package/dist/firestore-enterprise/index.cjs +57 -48
  16. package/dist/firestore-enterprise/index.cjs.map +1 -1
  17. package/dist/firestore-enterprise/index.d.cts +41 -11
  18. package/dist/firestore-enterprise/index.d.ts +41 -11
  19. package/dist/firestore-enterprise/index.js +31 -42
  20. package/dist/firestore-enterprise/index.js.map +1 -1
  21. package/dist/firestore-standard/index.cjs +34 -37
  22. package/dist/firestore-standard/index.cjs.map +1 -1
  23. package/dist/firestore-standard/index.js +9 -34
  24. package/dist/firestore-standard/index.js.map +1 -1
  25. package/dist/index.cjs +2 -3
  26. package/dist/index.cjs.map +1 -1
  27. package/dist/index.js +2 -2
  28. package/dist/sqlite/index.cjs +7 -7
  29. package/dist/sqlite/index.cjs.map +1 -1
  30. package/dist/sqlite/index.js +3 -3
  31. package/package.json +1 -1
  32. package/dist/chunk-4MMQ5W74.js.map +0 -1
  33. package/dist/chunk-PAD7WFFU.js.map +0 -1
  34. package/dist/chunk-TK64DNVK.js.map +0 -1
  35. /package/dist/{chunk-WRTFC5NG.js.map → chunk-3AHHXMWX.js.map} +0 -0
@@ -6,7 +6,7 @@ import {
6
6
  dedupeIndexSpecs,
7
7
  isFirestoreSpecialType,
8
8
  validateJsonPathKey
9
- } from "../chunk-4MMQ5W74.js";
9
+ } from "../chunk-NNBSUOOF.js";
10
10
  import {
11
11
  DEFAULT_CORE_INDEXES
12
12
  } from "../chunk-2DHMNTV6.js";
@@ -25,14 +25,14 @@ import {
25
25
  createMergedRegistry,
26
26
  createRegistry,
27
27
  generateId
28
- } from "../chunk-WRTFC5NG.js";
28
+ } from "../chunk-3AHHXMWX.js";
29
29
  import {
30
30
  CapabilityNotSupportedError,
31
31
  FiregraphError,
32
32
  assertUpdatePayloadExclusive,
33
33
  deleteField,
34
34
  flattenPatch
35
- } from "../chunk-TK64DNVK.js";
35
+ } from "../chunk-SIHE4UY4.js";
36
36
  import "../chunk-EQJUUVFG.js";
37
37
 
38
38
  // src/cloudflare/schema.ts
@@ -235,7 +235,6 @@ function isTerminalValue(value) {
235
235
  if (ctor && typeof ctor.name === "string" && FIRESTORE_TERMINAL_CTOR.has(ctor.name)) return true;
236
236
  return true;
237
237
  }
238
- var SAFE_KEY_RE = /^[A-Za-z_][A-Za-z0-9_-]*$/;
239
238
  function assertUpdatePayloadExclusive(update) {
240
239
  if (update.replaceData !== void 0 && update.dataOps !== void 0) {
241
240
  throw new Error(
@@ -274,9 +273,9 @@ function walkForDeleteSentinels(node, path, parent, visit) {
274
273
  }
275
274
  function assertSafePath(path) {
276
275
  for (const seg of path) {
277
- if (!SAFE_KEY_RE.test(seg)) {
276
+ if (seg === "") {
278
277
  throw new Error(
279
- `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.`
278
+ `firegraph: empty object key at path ${path.map((p) => JSON.stringify(p)).join(" > ")}. Object keys in update payloads must be non-empty.`
280
279
  );
281
280
  }
282
281
  }
@@ -2302,7 +2301,7 @@ function generateId() {
2302
2301
  }
2303
2302
 
2304
2303
  // src/firestore-enterprise/backend.ts
2305
- var import_firestore4 = require("@google-cloud/firestore");
2304
+ var import_firestore5 = require("@google-cloud/firestore");
2306
2305
 
2307
2306
  // src/bulk.ts
2308
2307
  var MAX_BATCH_SIZE = 500;
@@ -2527,6 +2526,13 @@ async function getFirestoreSurface() {
2527
2526
  }
2528
2527
  return { P: _Pipelines, Ts: _Timestamp };
2529
2528
  }
2529
+ var UNESCAPED_FIELD_NAME_RE = /^[_A-Za-z][_A-Za-z0-9]*$/;
2530
+ function buildDataPathAlias(segments) {
2531
+ const encoded = segments.map(
2532
+ (seg) => UNESCAPED_FIELD_NAME_RE.test(seg) ? seg : "`" + seg.replace(/\\/g, "\\\\").replace(/`/g, "\\`") + "`"
2533
+ );
2534
+ return ["data", ...encoded].join(".");
2535
+ }
2530
2536
  function buildFilterExpression(P, filter) {
2531
2537
  const { field: fieldName, op, value } = filter;
2532
2538
  switch (op) {
@@ -2604,7 +2610,7 @@ async function runFirestorePipelineUpdate(db, collectionPath, filters, patch, _o
2604
2610
  }
2605
2611
  const { P, Ts } = await getFirestoreSurface();
2606
2612
  const transforms = ops.map((op) => {
2607
- const alias = `data.${op.path.join(".")}`;
2613
+ const alias = buildDataPathAlias(op.path);
2608
2614
  return P.constant(op.value).as(alias);
2609
2615
  });
2610
2616
  transforms.push(P.constant(Ts.now()).as("updatedAt"));
@@ -2635,8 +2641,8 @@ function createFirestoreAdapter(db, collectionPath) {
2635
2641
  await collectionRef.doc(docId).set(data);
2636
2642
  }
2637
2643
  },
2638
- async updateDoc(docId, data) {
2639
- await collectionRef.doc(docId).update(data);
2644
+ async updateDoc(docId, args) {
2645
+ await collectionRef.doc(docId).update(...args);
2640
2646
  },
2641
2647
  async deleteDoc(docId) {
2642
2648
  await collectionRef.doc(docId).delete();
@@ -2672,8 +2678,8 @@ function createTransactionAdapter(db, collectionPath, tx) {
2672
2678
  tx.set(collectionRef.doc(docId), data);
2673
2679
  }
2674
2680
  },
2675
- updateDoc(docId, data) {
2676
- tx.update(collectionRef.doc(docId), data);
2681
+ updateDoc(docId, args) {
2682
+ tx.update(collectionRef.doc(docId), ...args);
2677
2683
  },
2678
2684
  deleteDoc(docId) {
2679
2685
  tx.delete(collectionRef.doc(docId));
@@ -2705,8 +2711,8 @@ function createBatchAdapter(db, collectionPath) {
2705
2711
  batch.set(collectionRef.doc(docId), data);
2706
2712
  }
2707
2713
  },
2708
- updateDoc(docId, data) {
2709
- batch.update(collectionRef.doc(docId), data);
2714
+ updateDoc(docId, args) {
2715
+ batch.update(collectionRef.doc(docId), ...args);
2710
2716
  },
2711
2717
  deleteDoc(docId) {
2712
2718
  batch.delete(collectionRef.doc(docId));
@@ -3292,10 +3298,13 @@ function decodeTree(rootRows, hops) {
3292
3298
  }
3293
3299
  async function runFirestoreEngineTraversal(db, collectionPath, params) {
3294
3300
  if (process.env.FIRESTORE_EMULATOR_HOST) {
3295
- throw new FiregraphError(
3296
- "engine traversal requires Pipelines \u2014 not supported on the Firestore emulator",
3297
- "UNSUPPORTED_OPERATION"
3298
- );
3301
+ const emulatorEdition = (process.env.FIRESTORE_EMULATOR_EDITION ?? "").trim().toLowerCase();
3302
+ if (emulatorEdition !== "enterprise") {
3303
+ throw new FiregraphError(
3304
+ "engine traversal requires Pipelines \u2014 not supported on the default Firestore emulator. Run firebase-tools v15.14+ with `--database-edition enterprise` and set `FIRESTORE_EMULATOR_EDITION=enterprise` to opt in.",
3305
+ "UNSUPPORTED_OPERATION"
3306
+ );
3307
+ }
3299
3308
  }
3300
3309
  const compiled = compileEngineTraversal(params);
3301
3310
  if (!compiled.eligible) {
@@ -3322,6 +3331,27 @@ async function runFirestoreEngineTraversal(db, collectionPath, params) {
3322
3331
  return result;
3323
3332
  }
3324
3333
 
3334
+ // src/internal/firestore-update.ts
3335
+ var import_firestore4 = require("@google-cloud/firestore");
3336
+ init_serialization();
3337
+ function buildFirestoreUpdateArgs(update, db) {
3338
+ assertUpdatePayloadExclusive(update);
3339
+ const args = [];
3340
+ if (update.replaceData) {
3341
+ args.push("data", deserializeFirestoreTypes(update.replaceData, db));
3342
+ } else if (update.dataOps) {
3343
+ for (const op of update.dataOps) {
3344
+ assertSafePath(op.path);
3345
+ args.push(new import_firestore4.FieldPath("data", ...op.path), op.delete ? import_firestore4.FieldValue.delete() : op.value);
3346
+ }
3347
+ }
3348
+ args.push("updatedAt", import_firestore4.FieldValue.serverTimestamp());
3349
+ if (update.v !== void 0) {
3350
+ args.push("v", update.v);
3351
+ }
3352
+ return args;
3353
+ }
3354
+
3325
3355
  // src/internal/firestore-vector.ts
3326
3356
  var ENVELOPE_FIELDS3 = /* @__PURE__ */ new Set([
3327
3357
  "aType",
@@ -3390,9 +3420,6 @@ async function runFirestoreFindNearest(base, params) {
3390
3420
  return snap.docs.map((doc) => doc.data());
3391
3421
  }
3392
3422
 
3393
- // src/firestore-enterprise/backend.ts
3394
- init_serialization();
3395
-
3396
3423
  // src/firestore-enterprise/pipeline-adapter.ts
3397
3424
  var _Pipelines6 = null;
3398
3425
  async function getPipelines5() {
@@ -3473,30 +3500,8 @@ var ENTERPRISE_BASE_CAPS = /* @__PURE__ */ new Set([
3473
3500
  var _emulatorFallbackWarned = false;
3474
3501
  var _classicInProductionWarned = false;
3475
3502
  var _previewDmlWarned = false;
3476
- function dottedDataPath(op) {
3477
- assertSafePath(op.path);
3478
- return `data.${op.path.join(".")}`;
3479
- }
3480
- function buildFirestoreUpdate(update, db) {
3481
- assertUpdatePayloadExclusive(update);
3482
- const out = {
3483
- updatedAt: import_firestore4.FieldValue.serverTimestamp()
3484
- };
3485
- if (update.replaceData) {
3486
- out.data = deserializeFirestoreTypes(update.replaceData, db);
3487
- } else if (update.dataOps) {
3488
- for (const op of update.dataOps) {
3489
- const key = dottedDataPath(op);
3490
- out[key] = op.delete ? import_firestore4.FieldValue.delete() : op.value;
3491
- }
3492
- }
3493
- if (update.v !== void 0) {
3494
- out.v = update.v;
3495
- }
3496
- return out;
3497
- }
3498
3503
  function stampWritableRecord(record) {
3499
- const now = import_firestore4.FieldValue.serverTimestamp();
3504
+ const now = import_firestore5.FieldValue.serverTimestamp();
3500
3505
  const out = {
3501
3506
  aType: record.aType,
3502
3507
  aUid: record.aUid,
@@ -3529,7 +3534,7 @@ var FirestoreEnterpriseTransactionBackend = class {
3529
3534
  );
3530
3535
  }
3531
3536
  async updateDoc(docId, update) {
3532
- this.adapter.updateDoc(docId, buildFirestoreUpdate(update, this.db));
3537
+ this.adapter.updateDoc(docId, buildFirestoreUpdateArgs(update, this.db));
3533
3538
  }
3534
3539
  async deleteDoc(docId) {
3535
3540
  this.adapter.deleteDoc(docId);
@@ -3548,7 +3553,7 @@ var FirestoreEnterpriseBatchBackend = class {
3548
3553
  );
3549
3554
  }
3550
3555
  updateDoc(docId, update) {
3551
- this.adapter.updateDoc(docId, buildFirestoreUpdate(update, this.db));
3556
+ this.adapter.updateDoc(docId, buildFirestoreUpdateArgs(update, this.db));
3552
3557
  }
3553
3558
  deleteDoc(docId) {
3554
3559
  this.adapter.deleteDoc(docId);
@@ -3560,8 +3565,8 @@ var FirestoreEnterpriseBatchBackend = class {
3560
3565
  var FirestoreEnterpriseBackendImpl = class _FirestoreEnterpriseBackendImpl {
3561
3566
  constructor(db, collectionPath, queryMode, scopePath, previewDml) {
3562
3567
  this.db = db;
3563
- this.queryMode = queryMode;
3564
3568
  this.previewDml = previewDml;
3569
+ this.queryMode = queryMode;
3565
3570
  this.collectionPath = collectionPath;
3566
3571
  this.scopePath = scopePath;
3567
3572
  this.adapter = createFirestoreAdapter(db, collectionPath);
@@ -3574,6 +3579,7 @@ var FirestoreEnterpriseBackendImpl = class _FirestoreEnterpriseBackendImpl {
3574
3579
  capabilities;
3575
3580
  collectionPath;
3576
3581
  scopePath;
3582
+ queryMode;
3577
3583
  adapter;
3578
3584
  pipelineAdapter;
3579
3585
  // --- Reads ---
@@ -3595,7 +3601,7 @@ var FirestoreEnterpriseBackendImpl = class _FirestoreEnterpriseBackendImpl {
3595
3601
  );
3596
3602
  }
3597
3603
  updateDoc(docId, update) {
3598
- return this.adapter.updateDoc(docId, buildFirestoreUpdate(update, this.db));
3604
+ return this.adapter.updateDoc(docId, buildFirestoreUpdateArgs(update, this.db));
3599
3605
  }
3600
3606
  deleteDoc(docId) {
3601
3607
  return this.adapter.deleteDoc(docId);
@@ -3835,11 +3841,14 @@ var FirestoreEnterpriseBackendImpl = class _FirestoreEnterpriseBackendImpl {
3835
3841
  function createFirestoreEnterpriseBackend(db, collectionPath, options = {}) {
3836
3842
  const requestedMode = options.defaultQueryMode ?? "pipeline";
3837
3843
  const isEmulator = !!process.env.FIRESTORE_EMULATOR_HOST;
3838
- const effectiveMode = isEmulator && requestedMode === "pipeline" ? "classic" : requestedMode;
3839
- if (isEmulator && requestedMode === "pipeline" && effectiveMode === "classic" && !_emulatorFallbackWarned) {
3844
+ const emulatorEdition = (process.env.FIRESTORE_EMULATOR_EDITION ?? "").trim().toLowerCase();
3845
+ const emulatorIsEnterprise = emulatorEdition === "enterprise";
3846
+ const emulatorForcesClassic = isEmulator && !emulatorIsEnterprise;
3847
+ const effectiveMode = emulatorForcesClassic && requestedMode === "pipeline" ? "classic" : requestedMode;
3848
+ if (emulatorForcesClassic && requestedMode === "pipeline" && effectiveMode === "classic" && !_emulatorFallbackWarned) {
3840
3849
  _emulatorFallbackWarned = true;
3841
3850
  console.warn(
3842
- "[firegraph] Firestore Enterprise pipeline mode is unavailable in the emulator; falling back to classic Query API for this run. Set `defaultQueryMode: 'classic'` to silence this warning."
3851
+ "[firegraph] Firestore Enterprise pipeline mode is unavailable in the default emulator; falling back to classic Query API for this run. If you are running firebase-tools v15.14+ with the enterprise-edition emulator (`firebase emulators:start --database-edition enterprise`), set `FIRESTORE_EMULATOR_EDITION=enterprise` in your environment to honor pipeline mode. Otherwise set `defaultQueryMode: 'classic'` to silence this warning."
3843
3852
  );
3844
3853
  }
3845
3854
  if (!isEmulator && requestedMode === "classic" && !_classicInProductionWarned) {