@prometheus-ags/entity-graph-core 3.0.4 → 3.1.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/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
+ import { graphStore, useGraphStore, subscribeDevtoolsEvent, getGraphDevtoolsCompatibilityDelegate, subscribeGraphDevtoolsCompatibility, matchesFilter, matchesSearch, compareEntities, emitLegacyDevtoolsEvent, __require } from './chunk-BE3BMRKF.mjs';
2
+ export { EMPTY_ENTITY_STATE, EMPTY_IDS, EMPTY_LIST_STATE, EMPTY_SYNC_METADATA, applyView, attachGlobalListeners, cascadeInvalidation, checkCompleteness, compareEntities, configureEngine, createGraphStore, dedupe, fetchEntity, fetchList, findInsertionIndex, getActiveSubscriberCount, getEngineOptions, getMergeStrategy, getSchema, graphStore, hasMergeStrategy, lwwStrategy, matchesFilter, matchesSearch, readRelations, registerMergeStrategy, registerSchema, registerSubscriber, serializeKey, setDefaultMergeStrategy, startGarbageCollector, stopGarbageCollector, subscribeDevtoolsEvent, subscribeSubscriberStats, unregisterSubscriber, useGraphStore } from './chunk-BE3BMRKF.mjs';
1
3
  import { createStore } from 'zustand/vanilla';
2
- import { subscribeWithSelector } from 'zustand/middleware';
3
- import { immer } from 'zustand/middleware/immer';
4
4
 
5
5
  // src/errors.ts
6
6
  var TerminalError = class _TerminalError extends Error {
@@ -68,6 +68,88 @@ function getRegisteredEntityTypes() {
68
68
  return Array.from(transports.keys());
69
69
  }
70
70
 
71
+ // src/active-store.ts
72
+ var moduleActiveStore = null;
73
+ var asyncScope = null;
74
+ var asyncScopeAttempted = false;
75
+ function isNodeLike() {
76
+ return typeof process !== "undefined" && typeof process.versions?.node === "string";
77
+ }
78
+ function resolveAsyncScope() {
79
+ if (asyncScopeAttempted) return asyncScope;
80
+ asyncScopeAttempted = true;
81
+ if (!isNodeLike()) return null;
82
+ try {
83
+ const req = typeof module !== "undefined" && typeof module.require === "function" ? module.require : typeof __require === "function" ? __require : null;
84
+ if (!req) return null;
85
+ const { AsyncLocalStorage } = req("node:async_hooks");
86
+ asyncScope = new AsyncLocalStorage();
87
+ } catch {
88
+ asyncScope = null;
89
+ }
90
+ return asyncScope;
91
+ }
92
+ async function prepareGraphStoreScope() {
93
+ if (asyncScope) return true;
94
+ if (!isNodeLike()) {
95
+ asyncScopeAttempted = true;
96
+ return false;
97
+ }
98
+ if (resolveAsyncScope()) return true;
99
+ try {
100
+ const specifier = "node:async_hooks";
101
+ const mod = await import(
102
+ /* @vite-ignore */
103
+ specifier
104
+ );
105
+ asyncScope = new mod.AsyncLocalStorage();
106
+ asyncScopeAttempted = true;
107
+ return true;
108
+ } catch {
109
+ asyncScopeAttempted = true;
110
+ return false;
111
+ }
112
+ }
113
+ function setActiveGraphStore(store) {
114
+ const previous = moduleActiveStore;
115
+ moduleActiveStore = store;
116
+ return () => {
117
+ moduleActiveStore = previous;
118
+ };
119
+ }
120
+ var warnedUnscopedFallback = false;
121
+ function warnUnscopedRequestFallback() {
122
+ if (warnedUnscopedFallback || !isNodeLike()) return;
123
+ warnedUnscopedFallback = true;
124
+ console.warn(
125
+ "[entity-graph-core] runWithGraphStore() could not load node:async_hooks, so it fell back to a module-level store. Concurrent requests will NOT be isolated. Await prepareGraphStoreScope() once during server startup."
126
+ );
127
+ }
128
+ function runWithGraphStore(store, fn) {
129
+ const scope = resolveAsyncScope();
130
+ if (!scope) {
131
+ warnUnscopedRequestFallback();
132
+ const restore = setActiveGraphStore(store);
133
+ try {
134
+ return fn();
135
+ } finally {
136
+ restore();
137
+ }
138
+ }
139
+ return scope.run(store, fn);
140
+ }
141
+ function resolveActiveGraphStore(fallback) {
142
+ const scoped = resolveAsyncScope()?.getStore();
143
+ if (scoped) return scoped;
144
+ return moduleActiveStore ?? fallback;
145
+ }
146
+ function __resetActiveGraphStore() {
147
+ moduleActiveStore = null;
148
+ asyncScope = null;
149
+ asyncScopeAttempted = false;
150
+ warnedUnscopedFallback = false;
151
+ }
152
+
71
153
  // src/view/prisma-compile.ts
72
154
  function nestWhereField(parts, leaf) {
73
155
  if (parts.length === 0) return {};
@@ -405,506 +487,6 @@ function mapPostgrestError(error, status) {
405
487
  return toEntityError(error);
406
488
  }
407
489
 
408
- // src/merge/lww.ts
409
- var lwwStrategy = (prev, next) => {
410
- return prev ? { ...prev, ...next } : { ...next };
411
- };
412
-
413
- // src/merge/registry.ts
414
- var byType = /* @__PURE__ */ new Map();
415
- var globalDefault = null;
416
- function registerMergeStrategy(type, strategy) {
417
- byType.set(type, strategy);
418
- }
419
- function setDefaultMergeStrategy(strategy) {
420
- globalDefault = strategy;
421
- }
422
- function getMergeStrategy(type) {
423
- return byType.get(type) ?? globalDefault ?? lwwStrategy;
424
- }
425
- function hasMergeStrategy(type) {
426
- return byType.has(type) || globalDefault !== null;
427
- }
428
-
429
- // src/view/evaluator.ts
430
- function matchesFilter(entity, filter) {
431
- if (Array.isArray(filter)) return filter.every((clause) => matchesClause(entity, clause));
432
- return matchesGroup(entity, filter);
433
- }
434
- function matchesGroup(entity, group) {
435
- const { logic, clauses } = group;
436
- if (logic === "and") return clauses.every((c) => "logic" in c ? matchesGroup(entity, c) : matchesClause(entity, c));
437
- return clauses.some((c) => "logic" in c ? matchesGroup(entity, c) : matchesClause(entity, c));
438
- }
439
- function matchesClause(entity, clause) {
440
- const { field, op, value, predicate } = clause;
441
- const fv = getNestedValue(entity, field);
442
- switch (op) {
443
- case "eq":
444
- return fv === value;
445
- case "neq":
446
- return fv !== value;
447
- case "gt":
448
- return fv > value;
449
- case "gte":
450
- return fv >= value;
451
- case "lt":
452
- return fv < value;
453
- case "lte":
454
- return fv <= value;
455
- case "in":
456
- return Array.isArray(value) && value.includes(fv);
457
- case "nin":
458
- return Array.isArray(value) && !value.includes(fv);
459
- case "isNull":
460
- return fv == null;
461
- case "isNotNull":
462
- return fv != null;
463
- case "contains":
464
- return typeof fv === "string" && typeof value === "string" && fv.toLowerCase().includes(value.toLowerCase());
465
- case "startsWith":
466
- return typeof fv === "string" && typeof value === "string" && fv.toLowerCase().startsWith(value.toLowerCase());
467
- case "endsWith":
468
- return typeof fv === "string" && typeof value === "string" && fv.toLowerCase().endsWith(value.toLowerCase());
469
- case "between": {
470
- const [lo, hi] = value;
471
- return fv >= lo && fv <= hi;
472
- }
473
- case "arrayContains":
474
- return Array.isArray(fv) && fv.includes(value);
475
- case "arrayOverlaps":
476
- return Array.isArray(fv) && Array.isArray(value) && value.some((v) => fv.includes(v));
477
- case "matches":
478
- return typeof fv === "string" && new RegExp(value).test(fv);
479
- case "custom":
480
- return predicate ? predicate(fv, entity) : true;
481
- default:
482
- return true;
483
- }
484
- }
485
- function matchesSearch(entity, query, fields) {
486
- if (!query.trim()) return true;
487
- const lq = query.toLowerCase();
488
- return fields.some((field) => {
489
- const v = getNestedValue(entity, field);
490
- return typeof v === "string" && v.toLowerCase().includes(lq);
491
- });
492
- }
493
- function compareEntities(a, b, sort) {
494
- for (const clause of sort) {
495
- const r = compareByClause(a, b, clause);
496
- if (r !== 0) return r;
497
- }
498
- return 0;
499
- }
500
- function compareByClause(a, b, clause) {
501
- const { field, direction, nulls = "last", comparator } = clause;
502
- const av = getNestedValue(a, field);
503
- const bv = getNestedValue(b, field);
504
- const aNull = av == null;
505
- const bNull = bv == null;
506
- if (aNull && bNull) return 0;
507
- if (aNull) return nulls === "first" ? -1 : 1;
508
- if (bNull) return nulls === "first" ? 1 : -1;
509
- let cmp;
510
- if (comparator) cmp = comparator(av, bv);
511
- else if (typeof av === "string" && typeof bv === "string") cmp = av.localeCompare(bv, void 0, { sensitivity: "base", numeric: true });
512
- else if (typeof av === "number" && typeof bv === "number") cmp = av - bv;
513
- else cmp = String(av).localeCompare(String(bv));
514
- return direction === "desc" ? -cmp : cmp;
515
- }
516
- function findInsertionIndex(entity, sortedIds, getEntity, sort) {
517
- let lo = 0;
518
- let hi = sortedIds.length;
519
- while (lo < hi) {
520
- const mid = lo + hi >>> 1;
521
- const me = getEntity(sortedIds[mid]);
522
- if (!me) {
523
- lo = mid + 1;
524
- continue;
525
- }
526
- if (compareEntities(entity, me, sort) <= 0) hi = mid;
527
- else lo = mid + 1;
528
- }
529
- return lo;
530
- }
531
- function applyView(ids, getEntity, filter, sort, search) {
532
- let entries = [];
533
- for (const id of ids) {
534
- const entity = getEntity(id);
535
- if (!entity) continue;
536
- entries.push({ id, entity });
537
- }
538
- if (filter && entries.length > 0) entries = entries.filter(({ entity }) => matchesFilter(entity, filter));
539
- if (search?.query) entries = entries.filter(({ entity }) => matchesSearch(entity, search.query, search.fields));
540
- if (sort && sort.length > 0) entries.sort((a, b) => compareEntities(a.entity, b.entity, sort));
541
- return entries.map((e) => e.id);
542
- }
543
- function checkCompleteness(loadedCount, total, hasNextPage) {
544
- if (!hasNextPage && total !== null && loadedCount >= total) return { isComplete: true, reason: "all-loaded" };
545
- if (hasNextPage) return { isComplete: false, reason: "has-more-pages" };
546
- return { isComplete: true, reason: "no-more-pages" };
547
- }
548
- function getNestedValue(obj, path) {
549
- const parts = path.replace(/\[(\d+)\]/g, ".$1").split(".");
550
- let current = obj;
551
- for (const part of parts) {
552
- if (current == null || typeof current !== "object") return void 0;
553
- current = current[part];
554
- }
555
- return current;
556
- }
557
-
558
- // src/graph.ts
559
- var EMPTY_IDS = [];
560
- var EMPTY_ENTITY_STATE = {
561
- isFetching: false,
562
- lastFetched: null,
563
- error: null,
564
- stale: false
565
- };
566
- var EMPTY_SYNC_METADATA = {
567
- synced: true,
568
- origin: "server",
569
- updatedAt: null
570
- };
571
- var EMPTY_LIST_STATE = {
572
- ids: EMPTY_IDS,
573
- total: null,
574
- nextCursor: null,
575
- prevCursor: null,
576
- hasNextPage: false,
577
- hasPrevPage: false,
578
- isFetching: false,
579
- isFetchingMore: false,
580
- error: null,
581
- lastError: null,
582
- lastFetched: null,
583
- stale: false,
584
- currentPage: null,
585
- pageSize: null
586
- };
587
- function defaultEntityState() {
588
- return { ...EMPTY_ENTITY_STATE };
589
- }
590
- function defaultSyncMetadata() {
591
- return { ...EMPTY_SYNC_METADATA };
592
- }
593
- function defaultListState() {
594
- return { ...EMPTY_LIST_STATE, ids: [] };
595
- }
596
- function ek(type, id) {
597
- return `${type}:${id}`;
598
- }
599
- var snapshotCache = /* @__PURE__ */ new Map();
600
- function readCachedEntitySnapshot(type, id, base, patch, metadata) {
601
- const key = ek(type, id);
602
- const cached = snapshotCache.get(key);
603
- if (cached && cached.base === base && cached.patch === patch && cached.metadata === metadata) {
604
- return cached.snapshot;
605
- }
606
- const snapshot = {
607
- ...patch ? { ...base, ...patch } : base,
608
- $synced: metadata.synced,
609
- $origin: metadata.origin,
610
- $updatedAt: metadata.updatedAt
611
- };
612
- snapshotCache.set(key, { base, patch, metadata, snapshot });
613
- return snapshot;
614
- }
615
- function createGraphStore() {
616
- return createStore()(
617
- subscribeWithSelector(
618
- immer((set, get) => ({
619
- entities: {},
620
- patches: {},
621
- entityStates: {},
622
- syncMetadata: {},
623
- lists: {},
624
- upsertEntity: (type, id, data) => set((s) => {
625
- if (!s.entities[type]) s.entities[type] = {};
626
- const key = ek(type, id);
627
- const strategy = getMergeStrategy(type);
628
- const ctx = { type, id, origin: s.syncMetadata[key]?.origin ?? "server", updatedAt: Date.now() };
629
- s.entities[type][id] = strategy(s.entities[type][id], data, ctx);
630
- if (!s.syncMetadata[key]) s.syncMetadata[key] = defaultSyncMetadata();
631
- }),
632
- upsertEntities: (type, entries) => set((s) => {
633
- if (!s.entities[type]) s.entities[type] = {};
634
- const strategy = getMergeStrategy(type);
635
- for (const { id, data } of entries) {
636
- const key = ek(type, id);
637
- const ctx = { type, id, origin: s.syncMetadata[key]?.origin ?? "server", updatedAt: Date.now() };
638
- s.entities[type][id] = strategy(s.entities[type][id], data, ctx);
639
- if (!s.syncMetadata[key]) s.syncMetadata[key] = defaultSyncMetadata();
640
- }
641
- }),
642
- ingestFetchedList: (type, entries, options = {}) => set((s) => {
643
- const fetchedAt = Date.now();
644
- const ids = entries.map(({ id }) => id);
645
- const batches = [
646
- { type, entries },
647
- ...options.sideBatches ?? []
648
- ];
649
- const origins = /* @__PURE__ */ new Map();
650
- for (const batch of batches) {
651
- if (!s.entities[batch.type]) s.entities[batch.type] = {};
652
- for (const { id } of batch.entries) {
653
- const key = ek(batch.type, id);
654
- if (!origins.has(key)) {
655
- origins.set(key, s.syncMetadata[key]?.origin ?? "server");
656
- }
657
- }
658
- }
659
- for (const batch of batches) {
660
- const strategy = getMergeStrategy(batch.type);
661
- for (const { id, data } of batch.entries) {
662
- const key = ek(batch.type, id);
663
- const ctx = {
664
- type: batch.type,
665
- id,
666
- origin: origins.get(key) ?? "server",
667
- updatedAt: fetchedAt
668
- };
669
- s.entities[batch.type][id] = strategy(s.entities[batch.type][id], data, ctx);
670
- }
671
- }
672
- const completed = /* @__PURE__ */ new Set();
673
- for (const batch of batches) {
674
- for (const { id } of batch.entries) {
675
- const key = ek(batch.type, id);
676
- if (completed.has(key)) continue;
677
- completed.add(key);
678
- if (!s.entityStates[key]) s.entityStates[key] = defaultEntityState();
679
- s.entityStates[key].lastFetched = fetchedAt;
680
- s.entityStates[key].isFetching = false;
681
- s.entityStates[key].error = null;
682
- s.entityStates[key].stale = false;
683
- s.syncMetadata[key] = {
684
- ...s.syncMetadata[key] ?? defaultSyncMetadata(),
685
- synced: true,
686
- origin: "server",
687
- updatedAt: fetchedAt
688
- };
689
- }
690
- }
691
- const readDraftEntity = (id) => {
692
- const base = s.entities[type]?.[id];
693
- if (!base) return null;
694
- const patch = s.patches[type]?.[id];
695
- const metadata = s.syncMetadata[ek(type, id)] ?? defaultSyncMetadata();
696
- return {
697
- ...base,
698
- ...patch ?? {},
699
- $synced: metadata.synced,
700
- $origin: metadata.origin,
701
- $updatedAt: metadata.updatedAt
702
- };
703
- };
704
- for (const target of options.lists ?? []) {
705
- const existing = s.lists[target.key] ?? defaultListState();
706
- const targetIds = target.ids ?? ids;
707
- const nextIds = target.mode === "append" ? Array.from(/* @__PURE__ */ new Set([...existing.ids, ...targetIds])) : targetIds;
708
- s.lists[target.key] = {
709
- ...existing,
710
- ...target.meta ?? {},
711
- ids: nextIds,
712
- isFetching: false,
713
- isFetchingMore: false,
714
- error: null,
715
- lastError: null,
716
- stale: false,
717
- lastFetched: fetchedAt
718
- };
719
- }
720
- for (const projection of options.projections ?? []) {
721
- const existing = s.lists[projection.key] ?? defaultListState();
722
- const nextIds = [...existing.ids];
723
- const { filter, sort, search } = projection.view;
724
- for (const id of new Set(ids)) {
725
- if (nextIds.includes(id)) continue;
726
- const entity = readDraftEntity(id);
727
- if (!entity) continue;
728
- const matches = (!filter || matchesFilter(entity, filter)) && (!search?.query || matchesSearch(entity, search.query, search.fields));
729
- if (!matches) continue;
730
- if (sort && sort.length > 0) {
731
- const index = findInsertionIndex(entity, nextIds, readDraftEntity, sort);
732
- nextIds.splice(index, 0, id);
733
- } else {
734
- nextIds.unshift(id);
735
- }
736
- }
737
- s.lists[projection.key] = projection.completeFetch ? {
738
- ...existing,
739
- ids: nextIds,
740
- isFetching: false,
741
- isFetchingMore: false,
742
- error: null,
743
- lastError: null,
744
- stale: false,
745
- lastFetched: fetchedAt
746
- } : { ...existing, ids: nextIds };
747
- }
748
- for (const key of options.finishListFetches ?? []) {
749
- if (!s.lists[key]) s.lists[key] = defaultListState();
750
- s.lists[key].isFetching = false;
751
- }
752
- }),
753
- replaceEntity: (type, id, data) => set((s) => {
754
- if (!s.entities[type]) s.entities[type] = {};
755
- s.entities[type][id] = data;
756
- const key = ek(type, id);
757
- if (!s.syncMetadata[key]) s.syncMetadata[key] = defaultSyncMetadata();
758
- }),
759
- removeEntity: (type, id) => set((s) => {
760
- delete s.entities[type]?.[id];
761
- delete s.patches[type]?.[id];
762
- delete s.entityStates[ek(type, id)];
763
- delete s.syncMetadata[ek(type, id)];
764
- }),
765
- patchEntity: (type, id, patch) => set((s) => {
766
- if (!s.patches[type]) s.patches[type] = {};
767
- s.patches[type][id] = { ...s.patches[type][id] ?? {}, ...patch };
768
- }),
769
- unpatchEntity: (type, id, keys) => set((s) => {
770
- const p = s.patches[type]?.[id];
771
- if (!p) return;
772
- for (const k of keys) delete p[k];
773
- }),
774
- clearPatch: (type, id) => set((s) => {
775
- delete s.patches[type]?.[id];
776
- }),
777
- setEntityFetching: (type, id, fetching) => set((s) => {
778
- const k = ek(type, id);
779
- if (!s.entityStates[k]) s.entityStates[k] = defaultEntityState();
780
- s.entityStates[k].isFetching = fetching;
781
- }),
782
- setEntityError: (type, id, error) => set((s) => {
783
- const k = ek(type, id);
784
- if (!s.entityStates[k]) s.entityStates[k] = defaultEntityState();
785
- s.entityStates[k].error = error;
786
- s.entityStates[k].isFetching = false;
787
- }),
788
- setEntityFetched: (type, id) => set((s) => {
789
- const k = ek(type, id);
790
- if (!s.entityStates[k]) s.entityStates[k] = defaultEntityState();
791
- s.entityStates[k].lastFetched = Date.now();
792
- s.entityStates[k].isFetching = false;
793
- s.entityStates[k].error = null;
794
- s.entityStates[k].stale = false;
795
- s.syncMetadata[k] = { ...s.syncMetadata[k] ?? defaultSyncMetadata(), synced: true, origin: "server", updatedAt: Date.now() };
796
- }),
797
- setEntityStale: (type, id, stale) => set((s) => {
798
- const k = ek(type, id);
799
- if (!s.entityStates[k]) s.entityStates[k] = defaultEntityState();
800
- s.entityStates[k].stale = stale;
801
- }),
802
- setEntitySyncMetadata: (type, id, metadata) => set((s) => {
803
- const k = ek(type, id);
804
- s.syncMetadata[k] = { ...s.syncMetadata[k] ?? defaultSyncMetadata(), ...metadata };
805
- }),
806
- clearEntitySyncMetadata: (type, id) => set((s) => {
807
- delete s.syncMetadata[ek(type, id)];
808
- }),
809
- setListResult: (key, ids, meta) => set((s) => {
810
- const ex = s.lists[key] ?? defaultListState();
811
- s.lists[key] = { ...ex, ...meta, ids, isFetching: false, isFetchingMore: false, error: null, lastError: null, stale: false, lastFetched: Date.now() };
812
- }),
813
- appendListResult: (key, ids, meta) => set((s) => {
814
- const ex = s.lists[key] ?? defaultListState();
815
- s.lists[key] = { ...ex, ...meta, ids: Array.from(/* @__PURE__ */ new Set([...ex.ids, ...ids])), isFetching: false, isFetchingMore: false, error: null, lastError: null, stale: false, lastFetched: Date.now() };
816
- }),
817
- prependListResult: (key, ids, meta) => set((s) => {
818
- const ex = s.lists[key] ?? defaultListState();
819
- s.lists[key] = { ...ex, ...meta ?? {}, ids: Array.from(/* @__PURE__ */ new Set([...ids, ...ex.ids])), isFetching: false, isFetchingMore: false, error: null, lastError: null, stale: false, lastFetched: Date.now() };
820
- }),
821
- removeIdFromAllLists: (_type, id) => set((s) => {
822
- for (const key of Object.keys(s.lists)) {
823
- const list = s.lists[key];
824
- const idx = list.ids.indexOf(id);
825
- if (idx !== -1) {
826
- list.ids.splice(idx, 1);
827
- if (list.total !== null) list.total -= 1;
828
- }
829
- }
830
- }),
831
- insertIdInList: (key, id, position) => set((s) => {
832
- if (!s.lists[key]) s.lists[key] = defaultListState();
833
- const ids = s.lists[key].ids;
834
- const ex = ids.indexOf(id);
835
- if (ex !== -1) ids.splice(ex, 1);
836
- if (position === "start") ids.unshift(id);
837
- else if (position === "end") ids.push(id);
838
- else ids.splice(position, 0, id);
839
- }),
840
- setListFetching: (key, fetching) => set((s) => {
841
- if (!s.lists[key]) s.lists[key] = defaultListState();
842
- s.lists[key].isFetching = fetching;
843
- }),
844
- setListFetchingMore: (key, fetchingMore) => set((s) => {
845
- if (!s.lists[key]) s.lists[key] = defaultListState();
846
- s.lists[key].isFetchingMore = fetchingMore;
847
- }),
848
- setListError: (key, error, typed) => set((s) => {
849
- if (!s.lists[key]) s.lists[key] = defaultListState();
850
- s.lists[key].error = error;
851
- s.lists[key].lastError = typed ?? (error === null ? null : s.lists[key].lastError);
852
- s.lists[key].isFetching = false;
853
- s.lists[key].isFetchingMore = false;
854
- s.lists[key].lastFetched = Date.now();
855
- s.lists[key].stale = false;
856
- }),
857
- setListStale: (key, stale) => set((s) => {
858
- if (!s.lists[key]) s.lists[key] = defaultListState();
859
- s.lists[key].stale = stale;
860
- }),
861
- invalidateEntity: (type, id) => set((s) => {
862
- if (id) {
863
- const k = ek(type, id);
864
- if (s.entityStates[k]) s.entityStates[k].stale = true;
865
- } else {
866
- for (const k of Object.keys(s.entityStates)) if (k.startsWith(`${type}:`)) s.entityStates[k].stale = true;
867
- }
868
- }),
869
- invalidateLists: (matcher) => set((s) => {
870
- const pred = typeof matcher === "string" ? (k) => k.startsWith(matcher) : matcher;
871
- for (const key of Object.keys(s.lists)) if (pred(key)) s.lists[key].stale = true;
872
- }),
873
- invalidateType: (type) => {
874
- get().invalidateEntity(type);
875
- get().invalidateLists(type);
876
- },
877
- readEntity: (type, id) => {
878
- const s = get();
879
- const base = s.entities[type]?.[id];
880
- if (!base) return null;
881
- const patch = s.patches[type]?.[id];
882
- return patch ? { ...base, ...patch } : base;
883
- },
884
- readEntitySnapshot: (type, id) => {
885
- const s = get();
886
- const base = s.entities[type]?.[id];
887
- if (!base) {
888
- snapshotCache.delete(ek(type, id));
889
- return null;
890
- }
891
- const patch = s.patches[type]?.[id];
892
- const metadata = s.syncMetadata[ek(type, id)] ?? EMPTY_SYNC_METADATA;
893
- return readCachedEntitySnapshot(
894
- type,
895
- id,
896
- base,
897
- patch,
898
- metadata
899
- );
900
- }
901
- }))
902
- )
903
- );
904
- }
905
- var graphStore = createGraphStore();
906
- var useGraphStore = graphStore;
907
-
908
490
  // src/object-path.ts
909
491
  function isObject(value) {
910
492
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -948,8 +530,9 @@ function collectDirtyPaths(current, original, prefix = "", acc = /* @__PURE__ */
948
530
 
949
531
  // src/merge/loro.ts
950
532
  var ROOT_MAP = "entity";
533
+ var LORO_MODULE_SPECIFIER = "loro-crdt";
951
534
  var docs = /* @__PURE__ */ new Map();
952
- function ek2(type, id) {
535
+ function ek(type, id) {
953
536
  return `${type}:${id}`;
954
537
  }
955
538
  async function createLoroMergeStrategy(loadLoro) {
@@ -957,7 +540,7 @@ async function createLoroMergeStrategy(loadLoro) {
957
540
  try {
958
541
  const loaded = loadLoro ? await loadLoro() : await import(
959
542
  /* @vite-ignore */
960
- 'loro-crdt'
543
+ LORO_MODULE_SPECIFIER
961
544
  );
962
545
  if (typeof loaded.LoroDoc !== "function") {
963
546
  throw new TypeError("loro-crdt did not export a LoroDoc constructor");
@@ -971,7 +554,7 @@ async function createLoroMergeStrategy(loadLoro) {
971
554
  }
972
555
  const { LoroDoc } = mod;
973
556
  return (prev, next, ctx) => {
974
- const key = ek2(ctx.type, ctx.id);
557
+ const key = ek(ctx.type, ctx.id);
975
558
  let doc = docs.get(key);
976
559
  if (!doc) {
977
560
  doc = new LoroDoc();
@@ -1059,272 +642,6 @@ function applySelection(row, select) {
1059
642
  return picked;
1060
643
  }
1061
644
 
1062
- // src/engine.ts
1063
- function serializeKey(key) {
1064
- return JSON.stringify(key, (_, v) => v && typeof v === "object" && !Array.isArray(v) ? Object.fromEntries(Object.entries(v).sort()) : v);
1065
- }
1066
- function sleep(ms) {
1067
- return new Promise((r) => setTimeout(r, ms));
1068
- }
1069
- var inflightByStore = /* @__PURE__ */ new WeakMap();
1070
- function inflightFor(store) {
1071
- let inflight = inflightByStore.get(store);
1072
- if (!inflight) {
1073
- inflight = /* @__PURE__ */ new Map();
1074
- inflightByStore.set(store, inflight);
1075
- }
1076
- return inflight;
1077
- }
1078
- function dedupe(key, fn, store = graphStore) {
1079
- const inflight = inflightFor(store);
1080
- if (inflight.has(key)) return inflight.get(key);
1081
- const p = fn().finally(() => inflight.delete(key));
1082
- inflight.set(key, p);
1083
- return p;
1084
- }
1085
- var subscriberStatsListeners = /* @__PURE__ */ new Set();
1086
- function emitSubscriberStatsChange() {
1087
- for (const cb of subscriberStatsListeners) cb();
1088
- }
1089
- var subscribersByStore = /* @__PURE__ */ new WeakMap();
1090
- function subscribersFor(store) {
1091
- let subscribers = subscribersByStore.get(store);
1092
- if (!subscribers) {
1093
- subscribers = /* @__PURE__ */ new Map();
1094
- subscribersByStore.set(store, subscribers);
1095
- }
1096
- return subscribers;
1097
- }
1098
- function registerSubscriber(key, store = graphStore) {
1099
- const subscribers = subscribersFor(store);
1100
- const token = Symbol(key);
1101
- if (!subscribers.has(key)) subscribers.set(key, /* @__PURE__ */ new Set());
1102
- subscribers.get(key).add(token);
1103
- emitSubscriberStatsChange();
1104
- return token;
1105
- }
1106
- function unregisterSubscriber(key, token, store = graphStore) {
1107
- const subscribers = subscribersFor(store);
1108
- const set = subscribers.get(key);
1109
- if (!set) return;
1110
- set.delete(token);
1111
- if (set.size === 0) subscribers.delete(key);
1112
- emitSubscriberStatsChange();
1113
- }
1114
- function hasSubscribers(key, store = graphStore) {
1115
- return (subscribersFor(store).get(key)?.size ?? 0) > 0;
1116
- }
1117
- var DEFAULT_OPTIONS = {
1118
- defaultStaleTime: 3e4,
1119
- defaultGcTime: 3e5,
1120
- gcInterval: 6e4,
1121
- maxRetries: 3,
1122
- retryBaseDelay: 1e3,
1123
- revalidateOnFocus: true,
1124
- revalidateOnReconnect: true
1125
- };
1126
- var engineOptions = { ...DEFAULT_OPTIONS };
1127
- function subscribeSubscriberStats(onChange) {
1128
- subscriberStatsListeners.add(onChange);
1129
- return () => subscriberStatsListeners.delete(onChange);
1130
- }
1131
- function getActiveSubscriberCount(store = graphStore) {
1132
- let n = 0;
1133
- for (const set of subscribersFor(store).values()) n += set.size;
1134
- return n;
1135
- }
1136
- var devtoolsSubscribers = /* @__PURE__ */ new Set();
1137
- function subscribeDevtoolsEvent(cb) {
1138
- devtoolsSubscribers.add(cb);
1139
- return () => {
1140
- devtoolsSubscribers.delete(cb);
1141
- };
1142
- }
1143
- function notifyDevtools(event) {
1144
- if (devtoolsSubscribers.size === 0) return;
1145
- const listeners2 = [...devtoolsSubscribers];
1146
- for (const cb of listeners2) {
1147
- try {
1148
- cb(event);
1149
- } catch (err) {
1150
- console.warn("[engine] devtools subscriber threw:", err);
1151
- }
1152
- }
1153
- }
1154
- var gcIntervalsByStore = /* @__PURE__ */ new Map();
1155
- function runGarbageCollection(storeApi = graphStore) {
1156
- const store = storeApi.getState();
1157
- const { defaultGcTime: gcTime } = getEngineOptions();
1158
- const now = Date.now();
1159
- const toRemove = [];
1160
- for (const type of Object.keys(store.entities)) {
1161
- const bucket = store.entities[type];
1162
- if (!bucket) continue;
1163
- for (const id of Object.keys(bucket)) {
1164
- const key = `${type}:${id}`;
1165
- if (hasSubscribers(key, storeApi)) continue;
1166
- const patch = store.patches[type]?.[id];
1167
- if (patch !== void 0 && Object.keys(patch).length > 0) continue;
1168
- const entityState = store.entityStates[key];
1169
- if (entityState?.isFetching) continue;
1170
- const lastFetched = entityState?.lastFetched;
1171
- if (lastFetched == null) continue;
1172
- if (now - lastFetched <= gcTime) continue;
1173
- toRemove.push({ type, id });
1174
- }
1175
- }
1176
- for (const { type, id } of toRemove) {
1177
- store.removeEntity(type, id);
1178
- store.removeIdFromAllLists(type, id);
1179
- }
1180
- }
1181
- function stopGarbageCollector(storeApi = graphStore) {
1182
- const intervalId = gcIntervalsByStore.get(storeApi);
1183
- if (intervalId != null && typeof clearInterval !== "undefined") {
1184
- clearInterval(intervalId);
1185
- gcIntervalsByStore.delete(storeApi);
1186
- }
1187
- }
1188
- function startGarbageCollector(storeApi = graphStore) {
1189
- stopGarbageCollector(storeApi);
1190
- if (typeof window === "undefined" || typeof setInterval === "undefined") return () => {
1191
- };
1192
- const intervalId = setInterval(
1193
- () => runGarbageCollection(storeApi),
1194
- getEngineOptions().gcInterval
1195
- );
1196
- gcIntervalsByStore.set(storeApi, intervalId);
1197
- return () => stopGarbageCollector(storeApi);
1198
- }
1199
- function restartGarbageCollector(storeApi = graphStore) {
1200
- startGarbageCollector(storeApi);
1201
- }
1202
- function configureEngine(opts) {
1203
- engineOptions = { ...DEFAULT_OPTIONS, ...opts };
1204
- const activeStores = [...gcIntervalsByStore.keys()];
1205
- if (activeStores.length === 0) restartGarbageCollector();
1206
- else for (const storeApi of activeStores) restartGarbageCollector(storeApi);
1207
- }
1208
- function getEngineOptions() {
1209
- return engineOptions;
1210
- }
1211
- async function fetchEntity(opts, engineOpts, storeApi = graphStore) {
1212
- const { type, id, fetch, normalize, sideEffects, idField = "id" } = opts;
1213
- if (!id) return;
1214
- storeApi.getState().setEntityFetching(type, id, true);
1215
- const attempt = async (retries) => {
1216
- try {
1217
- const raw = await fetch(id);
1218
- const normalized = normalize(raw);
1219
- const resolvedId = normalized[idField] ?? id;
1220
- storeApi.getState().upsertEntity(type, resolvedId, normalized);
1221
- storeApi.getState().setEntityFetched(type, resolvedId);
1222
- if (sideEffects) sideEffects(raw, storeApi);
1223
- opts.onSuccess?.(normalized);
1224
- } catch (err) {
1225
- const error = err instanceof Error ? err : new Error(String(err));
1226
- if (retries < engineOpts.maxRetries) {
1227
- await sleep(engineOpts.retryBaseDelay * Math.pow(2, retries));
1228
- return attempt(retries + 1);
1229
- }
1230
- storeApi.getState().setEntityError(type, id, error.message);
1231
- opts.onError?.(error);
1232
- }
1233
- };
1234
- await dedupe(`${type}:${id}`, () => attempt(0), storeApi);
1235
- }
1236
- async function fetchList(opts, params, engineOpts, isLoadMore = false, storeApi = graphStore) {
1237
- const { type, queryKey, fetch, normalize, sideEffects, mode = "replace" } = opts;
1238
- const key = serializeKey(queryKey);
1239
- const store = storeApi.getState();
1240
- if (!fetch || !normalize) return;
1241
- if (isLoadMore) store.setListFetchingMore(key, true);
1242
- else store.setListFetching(key, true);
1243
- const attempt = async (retries) => {
1244
- try {
1245
- const response = await fetch(params);
1246
- const normalized = response.items.map(normalize);
1247
- const meta = { total: response.total ?? null, nextCursor: response.nextCursor ?? null, prevCursor: response.prevCursor ?? null, hasNextPage: response.hasNextPage ?? !!response.nextCursor, hasPrevPage: response.hasPrevPage ?? !!response.prevCursor, currentPage: response.page ?? null, pageSize: response.pageSize ?? null };
1248
- storeApi.getState().ingestFetchedList(
1249
- type,
1250
- normalized.map(({ id, data }) => ({ id, data })),
1251
- { lists: [{ key, mode: mode === "append" && isLoadMore ? "append" : "replace", meta }] }
1252
- );
1253
- if (sideEffects) sideEffects(response.items, storeApi);
1254
- opts.onSuccess?.(response);
1255
- } catch (err) {
1256
- const error = err instanceof Error ? err : new Error(String(err));
1257
- if (retries < engineOpts.maxRetries) {
1258
- await sleep(engineOpts.retryBaseDelay * Math.pow(2, retries));
1259
- return attempt(retries + 1);
1260
- }
1261
- storeApi.getState().setListError(key, error.message);
1262
- opts.onError?.(error);
1263
- }
1264
- };
1265
- await dedupe(isLoadMore ? `${key}:more` : key, () => attempt(0), storeApi);
1266
- }
1267
- var globalListenerAttachments = /* @__PURE__ */ new WeakMap();
1268
- function releaseGlobalListenerAttachment(storeApi, attachment) {
1269
- let released = false;
1270
- return () => {
1271
- if (released) return;
1272
- released = true;
1273
- const current = globalListenerAttachments.get(storeApi);
1274
- if (current !== attachment) return;
1275
- current.references -= 1;
1276
- if (current.references > 0) return;
1277
- current.dispose();
1278
- globalListenerAttachments.delete(storeApi);
1279
- };
1280
- }
1281
- function attachGlobalListeners(storeApi = graphStore) {
1282
- if (typeof window === "undefined") return () => {
1283
- };
1284
- const existing = globalListenerAttachments.get(storeApi);
1285
- if (existing) {
1286
- existing.references += 1;
1287
- return releaseGlobalListenerAttachment(storeApi, existing);
1288
- }
1289
- const stopGc = startGarbageCollector(storeApi);
1290
- const revalidateAll = () => {
1291
- const state = storeApi.getState();
1292
- for (const key of subscribersFor(storeApi).keys()) {
1293
- if (!hasSubscribers(key, storeApi)) continue;
1294
- const colonIdx = key.indexOf(":");
1295
- if (colonIdx === -1) continue;
1296
- const type = key.slice(0, colonIdx);
1297
- const id = key.slice(colonIdx + 1);
1298
- state.setEntityStale(type, id, true);
1299
- }
1300
- };
1301
- const revalidateWhenVisible = () => {
1302
- if (document.visibilityState === "visible") revalidateAll();
1303
- };
1304
- const revalidateOnFocus = engineOptions.revalidateOnFocus;
1305
- const revalidateOnReconnect = engineOptions.revalidateOnReconnect;
1306
- if (revalidateOnFocus) {
1307
- window.addEventListener("visibilitychange", revalidateWhenVisible);
1308
- window.addEventListener("focus", revalidateAll);
1309
- }
1310
- if (revalidateOnReconnect) window.addEventListener("online", revalidateAll);
1311
- const attachment = {
1312
- references: 1,
1313
- dispose: () => {
1314
- if (revalidateOnFocus) {
1315
- window.removeEventListener("visibilitychange", revalidateWhenVisible);
1316
- window.removeEventListener("focus", revalidateAll);
1317
- }
1318
- if (revalidateOnReconnect) {
1319
- window.removeEventListener("online", revalidateAll);
1320
- }
1321
- stopGc();
1322
- }
1323
- };
1324
- globalListenerAttachments.set(storeApi, attachment);
1325
- return releaseGlobalListenerAttachment(storeApi, attachment);
1326
- }
1327
-
1328
645
  // src/graph-actions.ts
1329
646
  var graphActionListeners = /* @__PURE__ */ new Set();
1330
647
  var graphActionReplayers = /* @__PURE__ */ new Map();
@@ -1335,7 +652,7 @@ function createGraphTransaction(storeApi = graphStore) {
1335
652
  upsertEntity(type, id, data) {
1336
653
  storeApi.getState().upsertEntity(type, id, data);
1337
654
  if (typeof process === "undefined" || process.env.NODE_ENV !== "production") {
1338
- notifyDevtools({ kind: "upsert", type, id, data, at: (/* @__PURE__ */ new Date()).toISOString() });
655
+ emitLegacyDevtoolsEvent({ kind: "upsert", type, id, data, at: (/* @__PURE__ */ new Date()).toISOString() });
1339
656
  }
1340
657
  return tx;
1341
658
  },
@@ -1350,14 +667,14 @@ function createGraphTransaction(storeApi = graphStore) {
1350
667
  patchEntity(type, id, patch) {
1351
668
  storeApi.getState().patchEntity(type, id, patch);
1352
669
  if (typeof process === "undefined" || process.env.NODE_ENV !== "production") {
1353
- notifyDevtools({ kind: "patch", type, id, patch, at: (/* @__PURE__ */ new Date()).toISOString() });
670
+ emitLegacyDevtoolsEvent({ kind: "patch", type, id, patch, at: (/* @__PURE__ */ new Date()).toISOString() });
1354
671
  }
1355
672
  return tx;
1356
673
  },
1357
674
  clearPatch(type, id) {
1358
675
  storeApi.getState().clearPatch(type, id);
1359
676
  if (typeof process === "undefined" || process.env.NODE_ENV !== "production") {
1360
- notifyDevtools({ kind: "clearPatch", type, id, at: (/* @__PURE__ */ new Date()).toISOString() });
677
+ emitLegacyDevtoolsEvent({ kind: "clearPatch", type, id, at: (/* @__PURE__ */ new Date()).toISOString() });
1361
678
  }
1362
679
  return tx;
1363
680
  },
@@ -1840,7 +1157,7 @@ function computeDelay(policy, attempt) {
1840
1157
  return base / 2 + Math.random() * (base / 2);
1841
1158
  }
1842
1159
  }
1843
- function sleep2(ms) {
1160
+ function sleep(ms) {
1844
1161
  if (ms <= 0) return Promise.resolve();
1845
1162
  return new Promise((r) => setTimeout(r, ms));
1846
1163
  }
@@ -1853,7 +1170,7 @@ async function replayActionWithRetry(action, policy, storeApi = graphStore) {
1853
1170
  } catch (error) {
1854
1171
  lastError = error;
1855
1172
  if (attempt >= policy.maxAttempts) break;
1856
- await sleep2(computeDelay(policy, attempt));
1173
+ await sleep(computeDelay(policy, attempt));
1857
1174
  }
1858
1175
  }
1859
1176
  try {
@@ -1887,9 +1204,9 @@ function getDefaultOnlineSource() {
1887
1204
 
1888
1205
  // src/devtools-event-bus.ts
1889
1206
  var _busInjectMap = /* @__PURE__ */ new WeakMap();
1890
- var registry = /* @__PURE__ */ new Map();
1207
+ var registries = /* @__PURE__ */ new Map();
1891
1208
  function __resetStoreRegistry() {
1892
- registry.clear();
1209
+ registries.clear();
1893
1210
  }
1894
1211
  function createDevtoolsEventBus(opts) {
1895
1212
  const bufferSize = opts?.bufferSize ?? 500;
@@ -2004,8 +1321,10 @@ function createDevtoolsEventBus(opts) {
2004
1321
  pendingBurst = [];
2005
1322
  dispatchedCount = 0;
2006
1323
  flushScheduled = false;
2007
- for (const entry of registry.values()) {
2008
- if (entry.bus === bus && entry.active) {
1324
+ const registry = registries.get(bus);
1325
+ if (registry) {
1326
+ for (const entry of registry.values()) {
1327
+ if (!entry.active) continue;
2009
1328
  entry.active = false;
2010
1329
  try {
2011
1330
  entry.unsubscribeSrc();
@@ -2013,12 +1332,19 @@ function createDevtoolsEventBus(opts) {
2013
1332
  }
2014
1333
  }
2015
1334
  }
1335
+ registries.delete(bus);
1336
+ _busInjectMap.delete(bus);
2016
1337
  }
2017
1338
  };
2018
1339
  _busInjectMap.set(bus, handleEngineEvent);
2019
1340
  return bus;
2020
1341
  }
2021
1342
  function registerStore(bus, source, name) {
1343
+ let registry = registries.get(bus);
1344
+ if (!registry) {
1345
+ registry = /* @__PURE__ */ new Map();
1346
+ registries.set(bus, registry);
1347
+ }
2022
1348
  if (registry.get(name)?.active === true) {
2023
1349
  throw new Error(
2024
1350
  `[devtools-store-registry] Store name "${name}" is already registered and active`
@@ -2039,75 +1365,38 @@ function registerStore(bus, source, name) {
2039
1365
  unsubscribeSrc();
2040
1366
  };
2041
1367
  }
2042
- function getRegisteredStores() {
2043
- return Array.from(registry.values()).map((e) => ({ name: e.name, active: e.active }));
1368
+ function getRegisteredStores(bus) {
1369
+ const entries = bus ? [...registries.get(bus)?.values() ?? []] : [...registries.values()].flatMap((registry) => [...registry.values()]);
1370
+ return entries.map((entry) => ({ name: entry.name, active: entry.active }));
2044
1371
  }
2045
1372
 
2046
1373
  // src/devtools-time-travel.ts
2047
- var capacity = 50;
2048
- var ring = [];
2049
- var seqCounter = 0;
2050
- var cursor = null;
2051
- var listeners = /* @__PURE__ */ new Set();
2052
- function notify() {
2053
- for (const l of listeners) l();
2054
- }
2055
- function cloneSlice(s) {
2056
- return {
2057
- entities: structuredClone(s.entities),
2058
- patches: structuredClone(s.patches),
2059
- entityStates: structuredClone(s.entityStates),
2060
- syncMetadata: structuredClone(s.syncMetadata),
2061
- lists: structuredClone(s.lists)
2062
- };
2063
- }
2064
- function configureTimeTravel(opts) {
2065
- if (opts.capacity && opts.capacity > 0) capacity = opts.capacity;
2066
- if (ring.length > capacity) ring = ring.slice(-capacity);
2067
- notify();
1374
+ function configureTimeTravel(opts, storeApi = graphStore) {
1375
+ if (opts.capacity && opts.capacity > 0) {
1376
+ getGraphDevtoolsCompatibilityDelegate(storeApi)?.configure(opts.capacity);
1377
+ }
2068
1378
  }
2069
1379
  function recordGraphSnapshot(label, storeApi = graphStore) {
2070
- const s = storeApi.getState();
2071
- const snap = {
2072
- seq: seqCounter++,
2073
- at: Date.now(),
2074
- ...label !== void 0 ? { label } : {},
2075
- data: cloneSlice(s)
2076
- };
2077
- ring.push(snap);
2078
- if (ring.length > capacity) ring = ring.slice(-capacity);
2079
- cursor = null;
2080
- notify();
2081
- return snap.seq;
1380
+ return getGraphDevtoolsCompatibilityDelegate(storeApi)?.capture(label) ?? -1;
2082
1381
  }
2083
1382
  function restoreGraphSnapshot(index, storeApi = graphStore) {
2084
- const snap = ring[index];
2085
- if (!snap) return false;
2086
- const clone2 = cloneSlice(snap.data);
2087
- storeApi.setState(clone2);
2088
- cursor = index;
2089
- notify();
2090
- return true;
1383
+ return getGraphDevtoolsCompatibilityDelegate(storeApi)?.restoreByIndex(index) ?? false;
2091
1384
  }
2092
1385
  function restoreGraphSnapshotBySeq(seq, storeApi = graphStore) {
2093
- const index = ring.findIndex((s) => s.seq === seq);
2094
- return index === -1 ? false : restoreGraphSnapshot(index, storeApi);
1386
+ return getGraphDevtoolsCompatibilityDelegate(storeApi)?.restoreByCursor(seq) ?? false;
2095
1387
  }
2096
1388
  function stepTimeTravel(delta, storeApi = graphStore) {
2097
- const base = cursor ?? ring.length - 1;
2098
- const target = Math.max(0, Math.min(ring.length - 1, base + delta));
2099
- return restoreGraphSnapshot(target, storeApi);
1389
+ return getGraphDevtoolsCompatibilityDelegate(storeApi)?.step(delta) ?? false;
2100
1390
  }
2101
- function getTimeTravelState() {
2102
- return {
2103
- snapshots: ring.map(({ data: _data, ...meta }) => meta),
2104
- cursor,
2105
- capacity
1391
+ function getTimeTravelState(storeApi = graphStore) {
1392
+ return getGraphDevtoolsCompatibilityDelegate(storeApi)?.getState() ?? {
1393
+ snapshots: [],
1394
+ cursor: null,
1395
+ capacity: 50
2106
1396
  };
2107
1397
  }
2108
- function subscribeTimeTravel(cb) {
2109
- listeners.add(cb);
2110
- return () => listeners.delete(cb);
1398
+ function subscribeTimeTravel(cb, storeApi = graphStore) {
1399
+ return subscribeGraphDevtoolsCompatibility(storeApi, cb);
2111
1400
  }
2112
1401
 
2113
1402
  // src/view/incremental.ts
@@ -2202,78 +1491,6 @@ var IncrementalView = class {
2202
1491
  }
2203
1492
  };
2204
1493
 
2205
- // src/crud/relations.ts
2206
- var schemaRegistry2 = /* @__PURE__ */ new Map();
2207
- function registerSchema(schema) {
2208
- schemaRegistry2.set(schema.type, schema);
2209
- }
2210
- function getSchema(type) {
2211
- return schemaRegistry2.get(type) ?? null;
2212
- }
2213
- function cascadeInvalidation(ctx, storeApi = graphStore) {
2214
- const schema = schemaRegistry2.get(ctx.type);
2215
- if (!schema) return;
2216
- const store = storeApi.getState();
2217
- if (schema.globalListKeys) for (const key of schema.globalListKeys) store.invalidateLists(key);
2218
- if (!schema.relations) return;
2219
- for (const [, relation] of Object.entries(schema.relations)) {
2220
- switch (relation.cardinality) {
2221
- case "belongsTo": {
2222
- const prevFk = ctx.previous?.[relation.foreignKey];
2223
- const nextFk = ctx.next?.[relation.foreignKey];
2224
- if (prevFk && prevFk !== nextFk && relation.invalidateTargetLists) for (const kp of relation.invalidateTargetLists) store.invalidateLists((k) => k.startsWith(kp) && k.includes(prevFk));
2225
- if (nextFk && relation.invalidateTargetLists) for (const kp of relation.invalidateTargetLists) store.invalidateLists((k) => k.startsWith(kp) && k.includes(nextFk));
2226
- if (prevFk) store.invalidateEntity(relation.targetType, prevFk);
2227
- if (nextFk) store.invalidateEntity(relation.targetType, nextFk);
2228
- break;
2229
- }
2230
- case "hasMany":
2231
- store.invalidateLists(serializeKey(relation.listKeyPrefix(ctx.id)));
2232
- break;
2233
- case "manyToMany": {
2234
- const prevIds = ctx.previous?.[relation.localArrayField ?? ""];
2235
- const nextIds = ctx.next?.[relation.localArrayField ?? ""];
2236
- for (const relatedId of /* @__PURE__ */ new Set([...prevIds ?? [], ...nextIds ?? []])) store.invalidateLists(serializeKey(relation.listKeyPrefix(relatedId)));
2237
- break;
2238
- }
2239
- }
2240
- }
2241
- for (const [, otherSchema] of schemaRegistry2) {
2242
- if (!otherSchema.relations) continue;
2243
- for (const [, rel] of Object.entries(otherSchema.relations)) {
2244
- if (rel.targetType !== ctx.type) continue;
2245
- if (rel.cardinality === "hasMany") store.invalidateLists(serializeKey(rel.listKeyPrefix(ctx.id)));
2246
- }
2247
- }
2248
- }
2249
- function readRelations(type, entity, storeApi = graphStore) {
2250
- const schema = schemaRegistry2.get(type);
2251
- if (!schema?.relations) return {};
2252
- const store = storeApi.getState();
2253
- const result = {};
2254
- for (const [name, relation] of Object.entries(schema.relations)) {
2255
- switch (relation.cardinality) {
2256
- case "belongsTo": {
2257
- const fkValue = entity[relation.foreignKey];
2258
- result[name] = fkValue ? store.readEntity(relation.targetType, fkValue) : null;
2259
- break;
2260
- }
2261
- case "hasMany": {
2262
- const listKey = serializeKey(relation.listKeyPrefix(entity.id));
2263
- const listState = store.lists[listKey];
2264
- result[name] = listState ? listState.ids.map((id) => store.readEntity(relation.targetType, id)).filter(Boolean) : [];
2265
- break;
2266
- }
2267
- case "manyToMany": {
2268
- const ids = entity[relation.localArrayField ?? ""];
2269
- result[name] = ids ? ids.map((id) => store.readEntity(relation.targetType, id)).filter(Boolean) : [];
2270
- break;
2271
- }
2272
- }
2273
- }
2274
- return result;
2275
- }
2276
-
2277
1494
  // src/agent/json-patch.ts
2278
1495
  function unescape(token) {
2279
1496
  return token.replace(/~1/g, "/").replace(/~0/g, "~");
@@ -2811,10 +2028,10 @@ function createGraphQLSubscriptionAdapter(opts) {
2811
2028
  subscribe(_cfg, handler) {
2812
2029
  const unsubs = [];
2813
2030
  for (const sub of subscriptions) {
2814
- const { type, document: document2, variables, getPayload } = sub;
2031
+ const { type, document, variables, getPayload } = sub;
2815
2032
  unsubs.push(
2816
2033
  client.subscribe(
2817
- { query: document2, variables },
2034
+ { query: document, variables },
2818
2035
  {
2819
2036
  next: ({ data }) => {
2820
2037
  const raw = getPayload(data);
@@ -3946,6 +3163,6 @@ function getFacetedRowModel(preFilteredRowModel, columnId, allFilteredRowModel)
3946
3163
  return preFilteredRowModel;
3947
3164
  }
3948
3165
 
3949
- export { EMPTY_ENTITY_STATE, EMPTY_IDS, EMPTY_LIST_STATE, EMPTY_SYNC_METADATA, IncrementalView, RealtimeManager, TerminalError, TransientError, __resetEntityTransports, __resetStoreRegistry, applyAgUiDelta, applyAgUiSnapshot, applyJsonPatch, applyView, attachGlobalListeners, buildTenantWhere, cascadeInvalidation, checkCompleteness, collectDirtyPaths, compareEntities, configureEngine, configureTimeTravel, createConvexAdapter, createDevtoolsEventBus, createElectricAdapter, createFlintAdapter, createGraphAction, createGraphEffect, createGraphQLSubscriptionAdapter, createGraphStore, createGraphTool, createGraphTransaction, createLoroMergeStrategy, createPGlitePersistenceAdapter, createRow, createSchemaGraphTool, createSupabaseRealtimeAdapter, createSurrealLiveAdapter, createTauriSqlPersistenceAdapter, createTenantScopedElectricAdapter, createWebSocketAdapter, dedupe, escapeHtml, exportGraphSnapshot, exportGraphSnapshotWithSchemas, fetchEntity, fetchList, findInsertionIndex, flattenClauses, getActiveSubscriberCount, getCoreRowModel, getRealtimeManager as getElectricRealtimeManager, getEngineOptions, getEntityJsonSchema, getEntityTransport, getExpandedRowModel, getFacetedMinMaxValues, getFacetedRowModel, getFacetedUniqueValues, getFilteredRowModel, getGraphSyncStatus, getGroupedRowModel, getMergeStrategy, getPaginatedRowModel, getRealtimeManager, getRegisteredEntityTypes, getRegisteredStores, getSchema, getSelectedRowModel, getSortedRowModel, getTimeTravelState, getValueAtPath, graphStore, graphSyncStatusStore, hasCustomPredicates, hasMergeStrategy, humanizeFieldName, hydrateGraphFromStorage, inferSchemaFieldType, lwwStrategy, makeRestTransport, matchesFilter, matchesSearch, parseCreateTable, persistGraphToStorage, publishFlintMutation, queryOnce, readRelations, recordGraphSnapshot, registerEntityFromSql, registerEntityJsonSchema, registerEntityTransport, registerMergeStrategy, registerRuntimeSchema, registerSchema, registerStore, registerSubscriber, registryKey, renderMarkdownToHtml, replayActionWithRetry, resetRealtimeManager, restoreGraphSnapshot, restoreGraphSnapshotBySeq, selectGraph, serializeKey, setDefaultMergeStrategy, setValueAtPath, sqlTypeToJsonSchema, startGarbageCollector, startLocalFirstGraph, stepTimeTravel, stopGarbageCollector, subscribeDevtoolsEvent, subscribeSubscriberStats, subscribeTimeTravel, toEntityError, toGraphQLVariables, toPrismaOrderBy, toPrismaWhere, toRestParams, toSQLClauses, unregisterSubscriber, useGraphStore };
3166
+ export { IncrementalView, RealtimeManager, TerminalError, TransientError, __resetActiveGraphStore, __resetEntityTransports, __resetStoreRegistry, applyAgUiDelta, applyAgUiSnapshot, applyJsonPatch, buildTenantWhere, collectDirtyPaths, configureTimeTravel, createConvexAdapter, createDevtoolsEventBus, createElectricAdapter, createFlintAdapter, createGraphAction, createGraphEffect, createGraphQLSubscriptionAdapter, createGraphTool, createGraphTransaction, createLoroMergeStrategy, createPGlitePersistenceAdapter, createRow, createSchemaGraphTool, createSupabaseRealtimeAdapter, createSurrealLiveAdapter, createTauriSqlPersistenceAdapter, createTenantScopedElectricAdapter, createWebSocketAdapter, escapeHtml, exportGraphSnapshot, exportGraphSnapshotWithSchemas, flattenClauses, getCoreRowModel, getRealtimeManager as getElectricRealtimeManager, getEntityJsonSchema, getEntityTransport, getExpandedRowModel, getFacetedMinMaxValues, getFacetedRowModel, getFacetedUniqueValues, getFilteredRowModel, getGraphSyncStatus, getGroupedRowModel, getPaginatedRowModel, getRealtimeManager, getRegisteredEntityTypes, getRegisteredStores, getSelectedRowModel, getSortedRowModel, getTimeTravelState, getValueAtPath, graphSyncStatusStore, hasCustomPredicates, humanizeFieldName, hydrateGraphFromStorage, inferSchemaFieldType, makeRestTransport, parseCreateTable, persistGraphToStorage, prepareGraphStoreScope, publishFlintMutation, queryOnce, recordGraphSnapshot, registerEntityFromSql, registerEntityJsonSchema, registerEntityTransport, registerRuntimeSchema, registerStore, registryKey, renderMarkdownToHtml, replayActionWithRetry, resetRealtimeManager, resolveActiveGraphStore, restoreGraphSnapshot, restoreGraphSnapshotBySeq, runWithGraphStore, selectGraph, setActiveGraphStore, setValueAtPath, sqlTypeToJsonSchema, startLocalFirstGraph, stepTimeTravel, subscribeTimeTravel, toEntityError, toGraphQLVariables, toPrismaOrderBy, toPrismaWhere, toRestParams, toSQLClauses };
3950
3167
  //# sourceMappingURL=index.mjs.map
3951
3168
  //# sourceMappingURL=index.mjs.map