@typicalday/firegraph 0.9.0 → 0.11.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 (60) hide show
  1. package/README.md +93 -90
  2. package/bin/firegraph.mjs +21 -7
  3. package/dist/backend-U-MLShlg.d.ts +97 -0
  4. package/dist/backend-np4gEVhB.d.cts +97 -0
  5. package/dist/backend.cjs.map +1 -1
  6. package/dist/backend.d.cts +7 -6
  7. package/dist/backend.d.ts +7 -6
  8. package/dist/backend.js +1 -1
  9. package/dist/backend.js.map +1 -1
  10. package/dist/{chunk-EVUM6ORB.js → chunk-6SB34IPQ.js} +76 -8
  11. package/dist/chunk-6SB34IPQ.js.map +1 -0
  12. package/dist/{chunk-SU4FNLC3.js → chunk-EEKWRX5E.js} +1 -1
  13. package/dist/{chunk-SU4FNLC3.js.map → chunk-EEKWRX5E.js.map} +1 -1
  14. package/dist/{chunk-YLGXLEUE.js → chunk-GJVVRTQT.js} +5 -14
  15. package/dist/chunk-GJVVRTQT.js.map +1 -0
  16. package/dist/{chunk-GLOVWKQH.js → chunk-R7CRGYY4.js} +1 -1
  17. package/dist/{chunk-GLOVWKQH.js.map → chunk-R7CRGYY4.js.map} +1 -1
  18. package/dist/{do-sqlite.cjs → cloudflare/index.cjs} +1659 -1422
  19. package/dist/cloudflare/index.cjs.map +1 -0
  20. package/dist/cloudflare/index.d.cts +529 -0
  21. package/dist/cloudflare/index.d.ts +529 -0
  22. package/dist/cloudflare/index.js +934 -0
  23. package/dist/cloudflare/index.js.map +1 -0
  24. package/dist/codegen/index.cjs +4 -13
  25. package/dist/codegen/index.cjs.map +1 -1
  26. package/dist/codegen/index.d.cts +1 -1
  27. package/dist/codegen/index.d.ts +1 -1
  28. package/dist/codegen/index.js +1 -1
  29. package/dist/index.cjs +144 -132
  30. package/dist/index.cjs.map +1 -1
  31. package/dist/index.d.cts +116 -27
  32. package/dist/index.d.ts +116 -27
  33. package/dist/index.js +77 -123
  34. package/dist/index.js.map +1 -1
  35. package/dist/query-client/index.cjs.map +1 -1
  36. package/dist/query-client/index.js +1 -1
  37. package/dist/{scope-path-BtajqNK5.d.ts → scope-path-B1G3YiA7.d.cts} +5 -100
  38. package/dist/{scope-path-D2mNENJ-.d.cts → scope-path-B1G3YiA7.d.ts} +5 -100
  39. package/dist/{types-DfWVTsMn.d.ts → types-BGWxcpI_.d.cts} +92 -1
  40. package/dist/{types-DfWVTsMn.d.cts → types-BGWxcpI_.d.ts} +92 -1
  41. package/package.json +13 -17
  42. package/dist/chunk-EVUM6ORB.js.map +0 -1
  43. package/dist/chunk-SZ6W4VAS.js +0 -701
  44. package/dist/chunk-SZ6W4VAS.js.map +0 -1
  45. package/dist/chunk-YLGXLEUE.js.map +0 -1
  46. package/dist/d1.cjs +0 -2421
  47. package/dist/d1.cjs.map +0 -1
  48. package/dist/d1.d.cts +0 -54
  49. package/dist/d1.d.ts +0 -54
  50. package/dist/d1.js +0 -76
  51. package/dist/d1.js.map +0 -1
  52. package/dist/do-sqlite.cjs.map +0 -1
  53. package/dist/do-sqlite.d.cts +0 -41
  54. package/dist/do-sqlite.d.ts +0 -41
  55. package/dist/do-sqlite.js +0 -79
  56. package/dist/do-sqlite.js.map +0 -1
  57. package/dist/editor/client/assets/index-Bq2bfzeY.js +0 -411
  58. package/dist/editor/client/assets/index-CJ4m_EOL.css +0 -1
  59. package/dist/editor/client/index.html +0 -16
  60. package/dist/editor/server/index.mjs +0 -51511
package/dist/index.cjs CHANGED
@@ -159,6 +159,7 @@ var index_exports = {};
159
159
  __export(index_exports, {
160
160
  BOOTSTRAP_ENTRIES: () => BOOTSTRAP_ENTRIES,
161
161
  CrossBackendTransactionError: () => CrossBackendTransactionError,
162
+ DEFAULT_CORE_INDEXES: () => DEFAULT_CORE_INDEXES,
162
163
  DEFAULT_QUERY_LIMIT: () => DEFAULT_QUERY_LIMIT,
163
164
  DiscoveryError: () => DiscoveryError,
164
165
  DynamicRegistryError: () => DynamicRegistryError,
@@ -573,9 +574,7 @@ async function migrateRecord(record, registry, globalWriteBack = "off") {
573
574
  };
574
575
  }
575
576
  async function migrateRecords(records, registry, globalWriteBack = "off") {
576
- return Promise.all(
577
- records.map((r) => migrateRecord(r, registry, globalWriteBack))
578
- );
577
+ return Promise.all(records.map((r) => migrateRecord(r, registry, globalWriteBack)));
579
578
  }
580
579
 
581
580
  // src/scope.ts
@@ -657,6 +656,28 @@ function createRegistry(input) {
657
656
  for (const [key, arr] of axbBuild) {
658
657
  axbIndex.set(key, Object.freeze(arr));
659
658
  }
659
+ const topologyIndex = /* @__PURE__ */ new Map();
660
+ const topologyBuild = /* @__PURE__ */ new Map();
661
+ const topologySeen = /* @__PURE__ */ new Map();
662
+ for (const entry of entries) {
663
+ if (!entry.targetGraph) continue;
664
+ let seen = topologySeen.get(entry.aType);
665
+ if (!seen) {
666
+ seen = /* @__PURE__ */ new Set();
667
+ topologySeen.set(entry.aType, seen);
668
+ }
669
+ if (seen.has(entry.targetGraph)) continue;
670
+ seen.add(entry.targetGraph);
671
+ const existing = topologyBuild.get(entry.aType);
672
+ if (existing) {
673
+ existing.push(entry);
674
+ } else {
675
+ topologyBuild.set(entry.aType, [entry]);
676
+ }
677
+ }
678
+ for (const [key, arr] of topologyBuild) {
679
+ topologyIndex.set(key, Object.freeze(arr));
680
+ }
660
681
  return {
661
682
  lookup(aType, axbType, bType) {
662
683
  return map.get(tripleKey(aType, axbType, bType))?.entry;
@@ -664,6 +685,9 @@ function createRegistry(input) {
664
685
  lookupByAxbType(axbType) {
665
686
  return axbIndex.get(axbType) ?? [];
666
687
  },
688
+ getSubgraphTopology(aType) {
689
+ return topologyIndex.get(aType) ?? [];
690
+ },
667
691
  validate(aType, axbType, bType, data, scopePath) {
668
692
  const rec = map.get(tripleKey(aType, axbType, bType));
669
693
  if (!rec) {
@@ -711,6 +735,21 @@ function createMergedRegistry(base, extension) {
711
735
  }
712
736
  return Object.freeze(merged);
713
737
  },
738
+ getSubgraphTopology(aType) {
739
+ const baseResults = base.getSubgraphTopology(aType);
740
+ const extResults = extension.getSubgraphTopology(aType);
741
+ if (extResults.length === 0) return baseResults;
742
+ if (baseResults.length === 0) return extResults;
743
+ const seen = new Set(baseResults.map((e) => e.targetGraph));
744
+ const merged = [...baseResults];
745
+ for (const entry of extResults) {
746
+ if (!seen.has(entry.targetGraph)) {
747
+ seen.add(entry.targetGraph);
748
+ merged.push(entry);
749
+ }
750
+ }
751
+ return Object.freeze(merged);
752
+ },
714
753
  validate(aType, axbType, bType, data, scopePath) {
715
754
  if (baseKeys.has(tripleKey(aType, axbType, bType))) {
716
755
  return base.validate(aType, axbType, bType, data, scopePath);
@@ -743,7 +782,8 @@ function discoveryToEntries(discovery) {
743
782
  subtitleField: entity.subtitleField,
744
783
  allowedIn: entity.allowedIn,
745
784
  migrations: entity.migrations,
746
- migrationWriteBack: entity.migrationWriteBack
785
+ migrationWriteBack: entity.migrationWriteBack,
786
+ indexes: entity.indexes
747
787
  });
748
788
  }
749
789
  for (const [axbType, entity] of discovery.edges) {
@@ -771,7 +811,8 @@ function discoveryToEntries(discovery) {
771
811
  allowedIn: entity.allowedIn,
772
812
  targetGraph: resolvedTargetGraph,
773
813
  migrations: entity.migrations,
774
- migrationWriteBack: entity.migrationWriteBack
814
+ migrationWriteBack: entity.migrationWriteBack,
815
+ indexes: entity.indexes
775
816
  });
776
817
  }
777
818
  }
@@ -1443,6 +1484,21 @@ var GraphClientImpl = class _GraphClientImpl {
1443
1484
  getBackend() {
1444
1485
  return this.backend;
1445
1486
  }
1487
+ /**
1488
+ * Snapshot of the currently-effective registry. Returns the merged view
1489
+ * used for domain-type validation and migration — in dynamic mode this is
1490
+ * `dynamicRegistry ?? staticRegistry ?? bootstrapRegistry`, so callers see
1491
+ * updates after `reloadRegistry()` without having to re-resolve anything.
1492
+ *
1493
+ * Exposed for backends that need topology access during bulk operations
1494
+ * (e.g. the Cloudflare DO backend's cross-DO cascade). Not part of the
1495
+ * public `GraphClient` surface.
1496
+ *
1497
+ * @internal
1498
+ */
1499
+ getRegistrySnapshot() {
1500
+ return this.getCombinedRegistry();
1501
+ }
1446
1502
  // ---------------------------------------------------------------------------
1447
1503
  // Registry routing
1448
1504
  // ---------------------------------------------------------------------------
@@ -1841,26 +1897,17 @@ function createGraphClientFromBackend(backend, options, metaBackend) {
1841
1897
 
1842
1898
  // src/codegen/index.ts
1843
1899
  function pascalCase(s) {
1844
- return s.replace(
1845
- /(^|[^a-zA-Z0-9])([a-zA-Z])/g,
1846
- (_, _sep, ch) => ch.toUpperCase()
1847
- );
1900
+ return s.replace(/(^|[^a-zA-Z0-9])([a-zA-Z])/g, (_, _sep, ch) => ch.toUpperCase());
1848
1901
  }
1849
1902
  async function generateTypes(discovery, options = {}) {
1850
1903
  const { compile } = await import("json-schema-to-typescript");
1851
1904
  const { banner = true } = options;
1852
1905
  const chunks = [];
1853
1906
  if (banner) {
1854
- chunks.push(
1855
- "// Auto-generated by firegraph codegen \u2014 do not edit manually\n"
1856
- );
1907
+ chunks.push("// Auto-generated by firegraph codegen \u2014 do not edit manually\n");
1857
1908
  }
1858
- const sortedNodes = [...discovery.nodes.entries()].sort(
1859
- ([a], [b]) => a.localeCompare(b)
1860
- );
1861
- const sortedEdges = [...discovery.edges.entries()].sort(
1862
- ([a], [b]) => a.localeCompare(b)
1863
- );
1909
+ const sortedNodes = [...discovery.nodes.entries()].sort(([a], [b]) => a.localeCompare(b));
1910
+ const sortedEdges = [...discovery.edges.entries()].sort(([a], [b]) => a.localeCompare(b));
1864
1911
  for (const [name, entity] of sortedNodes) {
1865
1912
  const typeName = `${pascalCase(name)}Data`;
1866
1913
  const ts = await compile(entity.schema, typeName, {
@@ -1915,6 +1962,18 @@ function isAncestorUid(collectionPath, uid) {
1915
1962
  return resolveAncestorCollection(collectionPath, uid) !== null;
1916
1963
  }
1917
1964
 
1965
+ // src/default-indexes.ts
1966
+ var DEFAULT_CORE_INDEXES = Object.freeze([
1967
+ { fields: ["aUid"] },
1968
+ { fields: ["bUid"] },
1969
+ { fields: ["aType"] },
1970
+ { fields: ["bType"] },
1971
+ { fields: ["aUid", "axbType"] },
1972
+ { fields: ["axbType", "bUid"] },
1973
+ { fields: ["aType", "axbType"] },
1974
+ { fields: ["axbType", "bType"] }
1975
+ ]);
1976
+
1918
1977
  // src/discover.ts
1919
1978
  var import_node_fs = require("fs");
1920
1979
  var import_node_module = require("module");
@@ -2036,7 +2095,8 @@ function loadNodeEntity(dir, name) {
2036
2095
  sampleData,
2037
2096
  allowedIn: meta?.allowedIn,
2038
2097
  migrations,
2039
- migrationWriteBack: meta?.migrationWriteBack
2098
+ migrationWriteBack: meta?.migrationWriteBack,
2099
+ indexes: meta?.indexes
2040
2100
  };
2041
2101
  }
2042
2102
  function loadEdgeEntity(dir, name) {
@@ -2073,7 +2133,8 @@ function loadEdgeEntity(dir, name) {
2073
2133
  allowedIn: meta?.allowedIn,
2074
2134
  targetGraph: topology.targetGraph ?? meta?.targetGraph,
2075
2135
  migrations,
2076
- migrationWriteBack: meta?.migrationWriteBack
2136
+ migrationWriteBack: meta?.migrationWriteBack,
2137
+ indexes: meta?.indexes
2077
2138
  };
2078
2139
  }
2079
2140
  function getSubdirectories(dir) {
@@ -2603,124 +2664,74 @@ function generateId() {
2603
2664
  }
2604
2665
 
2605
2666
  // src/indexes.ts
2606
- function baseIndexes(collection) {
2607
- return [
2608
- {
2609
- collectionGroup: collection,
2610
- queryScope: "COLLECTION",
2611
- fields: [
2612
- { fieldPath: "aUid", order: "ASCENDING" },
2613
- { fieldPath: "axbType", order: "ASCENDING" }
2614
- ]
2615
- },
2616
- {
2617
- collectionGroup: collection,
2618
- queryScope: "COLLECTION",
2619
- fields: [
2620
- { fieldPath: "axbType", order: "ASCENDING" },
2621
- { fieldPath: "bUid", order: "ASCENDING" }
2622
- ]
2623
- },
2624
- {
2625
- collectionGroup: collection,
2626
- queryScope: "COLLECTION",
2627
- fields: [
2628
- { fieldPath: "aType", order: "ASCENDING" },
2629
- { fieldPath: "axbType", order: "ASCENDING" }
2630
- ]
2631
- },
2632
- {
2633
- collectionGroup: collection,
2634
- queryScope: "COLLECTION",
2635
- fields: [
2636
- { fieldPath: "axbType", order: "ASCENDING" },
2637
- { fieldPath: "bType", order: "ASCENDING" }
2638
- ]
2639
- }
2640
- ];
2667
+ function normalizeField(f) {
2668
+ return typeof f === "string" ? { path: f, desc: false } : { path: f.path, desc: !!f.desc };
2641
2669
  }
2642
- function extractSchemaFields(schema) {
2643
- const s = schema;
2644
- if (s.type !== "object" || !s.properties) return [];
2645
- return Object.keys(s.properties);
2646
- }
2647
- function collectionGroupIndexes(collectionName) {
2648
- return [
2649
- {
2650
- collectionGroup: collectionName,
2651
- queryScope: "COLLECTION_GROUP",
2652
- fields: [
2653
- { fieldPath: "aUid", order: "ASCENDING" },
2654
- { fieldPath: "axbType", order: "ASCENDING" }
2655
- ]
2656
- },
2657
- {
2658
- collectionGroup: collectionName,
2659
- queryScope: "COLLECTION_GROUP",
2660
- fields: [
2661
- { fieldPath: "axbType", order: "ASCENDING" },
2662
- { fieldPath: "bUid", order: "ASCENDING" }
2663
- ]
2664
- },
2665
- {
2666
- collectionGroup: collectionName,
2667
- queryScope: "COLLECTION_GROUP",
2668
- fields: [
2669
- { fieldPath: "aType", order: "ASCENDING" },
2670
- { fieldPath: "axbType", order: "ASCENDING" }
2671
- ]
2672
- },
2673
- {
2674
- collectionGroup: collectionName,
2675
- queryScope: "COLLECTION_GROUP",
2676
- fields: [
2677
- { fieldPath: "axbType", order: "ASCENDING" },
2678
- { fieldPath: "bType", order: "ASCENDING" }
2679
- ]
2680
- }
2681
- ];
2670
+ function specFingerprint(spec, scope) {
2671
+ const normalized = spec.fields.map(normalizeField);
2672
+ return `${scope}::${JSON.stringify(normalized)}`;
2682
2673
  }
2683
- function generateIndexConfig(collection, entities, registryEntries) {
2684
- const indexes = baseIndexes(collection);
2685
- if (entities) {
2686
- for (const [, entity] of entities.nodes) {
2687
- const fields = extractSchemaFields(entity.schema);
2688
- for (const field of fields) {
2689
- indexes.push({
2690
- collectionGroup: collection,
2691
- queryScope: "COLLECTION",
2692
- fields: [
2693
- { fieldPath: "aType", order: "ASCENDING" },
2694
- { fieldPath: "axbType", order: "ASCENDING" },
2695
- { fieldPath: `data.${field}`, order: "ASCENDING" }
2696
- ]
2697
- });
2698
- }
2699
- }
2700
- for (const [, entity] of entities.edges) {
2701
- const fields = extractSchemaFields(entity.schema);
2702
- for (const field of fields) {
2703
- indexes.push({
2704
- collectionGroup: collection,
2705
- queryScope: "COLLECTION",
2706
- fields: [
2707
- { fieldPath: "aUid", order: "ASCENDING" },
2708
- { fieldPath: "axbType", order: "ASCENDING" },
2709
- { fieldPath: `data.${field}`, order: "ASCENDING" }
2710
- ]
2711
- });
2712
- }
2674
+ function toFirestoreFields(spec) {
2675
+ return spec.fields.map((f) => {
2676
+ const n = normalizeField(f);
2677
+ return {
2678
+ fieldPath: n.path,
2679
+ order: n.desc ? "DESCENDING" : "ASCENDING"
2680
+ };
2681
+ });
2682
+ }
2683
+ var warnedOnPartialIndex = false;
2684
+ function generateIndexConfig(collection, options = {}) {
2685
+ const core = options.coreIndexes ?? [...DEFAULT_CORE_INDEXES];
2686
+ const fromEntries = (options.registryEntries ?? []).flatMap((e) => {
2687
+ if (!e.indexes) return [];
2688
+ return e.indexes;
2689
+ });
2690
+ const targetGraphNames = /* @__PURE__ */ new Set();
2691
+ for (const entry of options.registryEntries ?? []) {
2692
+ if (entry.targetGraph) targetGraphNames.add(entry.targetGraph);
2693
+ }
2694
+ if (options.entities) {
2695
+ for (const [, entity] of options.entities.edges) {
2696
+ const tg = entity.targetGraph ?? entity.topology?.targetGraph;
2697
+ if (tg) targetGraphNames.add(tg);
2713
2698
  }
2714
2699
  }
2715
- if (registryEntries) {
2716
- const targetGraphNames = /* @__PURE__ */ new Set();
2717
- for (const entry of registryEntries) {
2718
- if (entry.targetGraph) {
2719
- targetGraphNames.add(entry.targetGraph);
2700
+ const allSpecs = [...core, ...fromEntries];
2701
+ const seen = /* @__PURE__ */ new Set();
2702
+ const indexes = [];
2703
+ for (const spec of allSpecs) {
2704
+ if (!spec.fields || spec.fields.length < 2) {
2705
+ continue;
2706
+ }
2707
+ if (spec.where) {
2708
+ if (!warnedOnPartialIndex) {
2709
+ warnedOnPartialIndex = true;
2710
+ console.warn(
2711
+ "firegraph: IndexSpec.where is ignored by the Firestore generator \u2014 Firestore composite indexes do not support predicates. The SQLite backends will still honor `where`."
2712
+ );
2720
2713
  }
2714
+ continue;
2715
+ }
2716
+ const fields = toFirestoreFields(spec);
2717
+ const colKey = specFingerprint(spec, `col:${collection}`);
2718
+ if (!seen.has(colKey)) {
2719
+ seen.add(colKey);
2720
+ indexes.push({
2721
+ collectionGroup: collection,
2722
+ queryScope: "COLLECTION",
2723
+ fields
2724
+ });
2721
2725
  }
2722
- for (const name of targetGraphNames) {
2723
- indexes.push(...collectionGroupIndexes(name));
2726
+ for (const tg of targetGraphNames) {
2727
+ const cgKey = specFingerprint(spec, `cg:${tg}`);
2728
+ if (seen.has(cgKey)) continue;
2729
+ seen.add(cgKey);
2730
+ indexes.push({
2731
+ collectionGroup: tg,
2732
+ queryScope: "COLLECTION_GROUP",
2733
+ fields
2734
+ });
2724
2735
  }
2725
2736
  }
2726
2737
  return { indexes, fieldOverrides: [] };
@@ -3378,6 +3389,7 @@ function defineViews(input) {
3378
3389
  0 && (module.exports = {
3379
3390
  BOOTSTRAP_ENTRIES,
3380
3391
  CrossBackendTransactionError,
3392
+ DEFAULT_CORE_INDEXES,
3381
3393
  DEFAULT_QUERY_LIMIT,
3382
3394
  DiscoveryError,
3383
3395
  DynamicRegistryError,