@prometheus-ags/entity-graph-core 3.0.0-alpha.0 → 3.0.0-rc.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.
- package/CHANGELOG.md +39 -0
- package/LICENSE +21 -0
- package/README.md +124 -0
- package/dist/{index.js → index.cjs} +376 -291
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +458 -127
- package/dist/index.d.ts +458 -127
- package/dist/index.mjs +371 -289
- package/dist/index.mjs.map +1 -1
- package/package.json +33 -9
- package/dist/index.js.map +0 -1
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createStore } from 'zustand/vanilla';
|
|
2
2
|
import { subscribeWithSelector } from 'zustand/middleware';
|
|
3
3
|
import { immer } from 'zustand/middleware/immer';
|
|
4
4
|
|
|
@@ -483,183 +483,187 @@ function readCachedEntitySnapshot(type, id, base, patch, metadata) {
|
|
|
483
483
|
snapshotCache.set(key, { base, patch, metadata, snapshot });
|
|
484
484
|
return snapshot;
|
|
485
485
|
}
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
const strategy = getMergeStrategy(type);
|
|
498
|
-
const ctx = { type, id, origin: s.syncMetadata[key]?.origin ?? "server", updatedAt: Date.now() };
|
|
499
|
-
s.entities[type][id] = strategy(s.entities[type][id], data, ctx);
|
|
500
|
-
if (!s.syncMetadata[key]) s.syncMetadata[key] = defaultSyncMetadata();
|
|
501
|
-
}),
|
|
502
|
-
upsertEntities: (type, entries) => set((s) => {
|
|
503
|
-
if (!s.entities[type]) s.entities[type] = {};
|
|
504
|
-
const strategy = getMergeStrategy(type);
|
|
505
|
-
for (const { id, data } of entries) {
|
|
486
|
+
function createGraphStore() {
|
|
487
|
+
return createStore()(
|
|
488
|
+
subscribeWithSelector(
|
|
489
|
+
immer((set, get) => ({
|
|
490
|
+
entities: {},
|
|
491
|
+
patches: {},
|
|
492
|
+
entityStates: {},
|
|
493
|
+
syncMetadata: {},
|
|
494
|
+
lists: {},
|
|
495
|
+
upsertEntity: (type, id, data) => set((s) => {
|
|
496
|
+
if (!s.entities[type]) s.entities[type] = {};
|
|
506
497
|
const key = ek(type, id);
|
|
498
|
+
const strategy = getMergeStrategy(type);
|
|
507
499
|
const ctx = { type, id, origin: s.syncMetadata[key]?.origin ?? "server", updatedAt: Date.now() };
|
|
508
500
|
s.entities[type][id] = strategy(s.entities[type][id], data, ctx);
|
|
509
501
|
if (!s.syncMetadata[key]) s.syncMetadata[key] = defaultSyncMetadata();
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
delete s.entities[type]?.[id];
|
|
520
|
-
delete s.patches[type]?.[id];
|
|
521
|
-
delete s.entityStates[ek(type, id)];
|
|
522
|
-
delete s.syncMetadata[ek(type, id)];
|
|
523
|
-
}),
|
|
524
|
-
patchEntity: (type, id, patch) => set((s) => {
|
|
525
|
-
if (!s.patches[type]) s.patches[type] = {};
|
|
526
|
-
s.patches[type][id] = { ...s.patches[type][id] ?? {}, ...patch };
|
|
527
|
-
}),
|
|
528
|
-
unpatchEntity: (type, id, keys) => set((s) => {
|
|
529
|
-
const p = s.patches[type]?.[id];
|
|
530
|
-
if (!p) return;
|
|
531
|
-
for (const k of keys) delete p[k];
|
|
532
|
-
}),
|
|
533
|
-
clearPatch: (type, id) => set((s) => {
|
|
534
|
-
delete s.patches[type]?.[id];
|
|
535
|
-
}),
|
|
536
|
-
setEntityFetching: (type, id, fetching) => set((s) => {
|
|
537
|
-
const k = ek(type, id);
|
|
538
|
-
if (!s.entityStates[k]) s.entityStates[k] = defaultEntityState();
|
|
539
|
-
s.entityStates[k].isFetching = fetching;
|
|
540
|
-
}),
|
|
541
|
-
setEntityError: (type, id, error) => set((s) => {
|
|
542
|
-
const k = ek(type, id);
|
|
543
|
-
if (!s.entityStates[k]) s.entityStates[k] = defaultEntityState();
|
|
544
|
-
s.entityStates[k].error = error;
|
|
545
|
-
s.entityStates[k].isFetching = false;
|
|
546
|
-
}),
|
|
547
|
-
setEntityFetched: (type, id) => set((s) => {
|
|
548
|
-
const k = ek(type, id);
|
|
549
|
-
if (!s.entityStates[k]) s.entityStates[k] = defaultEntityState();
|
|
550
|
-
s.entityStates[k].lastFetched = Date.now();
|
|
551
|
-
s.entityStates[k].isFetching = false;
|
|
552
|
-
s.entityStates[k].error = null;
|
|
553
|
-
s.entityStates[k].stale = false;
|
|
554
|
-
s.syncMetadata[k] = { ...s.syncMetadata[k] ?? defaultSyncMetadata(), synced: true, origin: "server", updatedAt: Date.now() };
|
|
555
|
-
}),
|
|
556
|
-
setEntityStale: (type, id, stale) => set((s) => {
|
|
557
|
-
const k = ek(type, id);
|
|
558
|
-
if (!s.entityStates[k]) s.entityStates[k] = defaultEntityState();
|
|
559
|
-
s.entityStates[k].stale = stale;
|
|
560
|
-
}),
|
|
561
|
-
setEntitySyncMetadata: (type, id, metadata) => set((s) => {
|
|
562
|
-
const k = ek(type, id);
|
|
563
|
-
s.syncMetadata[k] = { ...s.syncMetadata[k] ?? defaultSyncMetadata(), ...metadata };
|
|
564
|
-
}),
|
|
565
|
-
clearEntitySyncMetadata: (type, id) => set((s) => {
|
|
566
|
-
delete s.syncMetadata[ek(type, id)];
|
|
567
|
-
}),
|
|
568
|
-
setListResult: (key, ids, meta) => set((s) => {
|
|
569
|
-
const ex = s.lists[key] ?? defaultListState();
|
|
570
|
-
s.lists[key] = { ...ex, ...meta, ids, isFetching: false, isFetchingMore: false, error: null, lastError: null, stale: false, lastFetched: Date.now() };
|
|
571
|
-
}),
|
|
572
|
-
appendListResult: (key, ids, meta) => set((s) => {
|
|
573
|
-
const ex = s.lists[key] ?? defaultListState();
|
|
574
|
-
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() };
|
|
575
|
-
}),
|
|
576
|
-
prependListResult: (key, ids, meta) => set((s) => {
|
|
577
|
-
const ex = s.lists[key] ?? defaultListState();
|
|
578
|
-
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() };
|
|
579
|
-
}),
|
|
580
|
-
removeIdFromAllLists: (_type, id) => set((s) => {
|
|
581
|
-
for (const key of Object.keys(s.lists)) {
|
|
582
|
-
const list = s.lists[key];
|
|
583
|
-
const idx = list.ids.indexOf(id);
|
|
584
|
-
if (idx !== -1) {
|
|
585
|
-
list.ids.splice(idx, 1);
|
|
586
|
-
if (list.total !== null) list.total -= 1;
|
|
502
|
+
}),
|
|
503
|
+
upsertEntities: (type, entries) => set((s) => {
|
|
504
|
+
if (!s.entities[type]) s.entities[type] = {};
|
|
505
|
+
const strategy = getMergeStrategy(type);
|
|
506
|
+
for (const { id, data } of entries) {
|
|
507
|
+
const key = ek(type, id);
|
|
508
|
+
const ctx = { type, id, origin: s.syncMetadata[key]?.origin ?? "server", updatedAt: Date.now() };
|
|
509
|
+
s.entities[type][id] = strategy(s.entities[type][id], data, ctx);
|
|
510
|
+
if (!s.syncMetadata[key]) s.syncMetadata[key] = defaultSyncMetadata();
|
|
587
511
|
}
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
s
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
s
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
s.lists[key].stale = false;
|
|
615
|
-
}),
|
|
616
|
-
setListStale: (key, stale) => set((s) => {
|
|
617
|
-
if (!s.lists[key]) s.lists[key] = defaultListState();
|
|
618
|
-
s.lists[key].stale = stale;
|
|
619
|
-
}),
|
|
620
|
-
invalidateEntity: (type, id) => set((s) => {
|
|
621
|
-
if (id) {
|
|
512
|
+
}),
|
|
513
|
+
replaceEntity: (type, id, data) => set((s) => {
|
|
514
|
+
if (!s.entities[type]) s.entities[type] = {};
|
|
515
|
+
s.entities[type][id] = data;
|
|
516
|
+
const key = ek(type, id);
|
|
517
|
+
if (!s.syncMetadata[key]) s.syncMetadata[key] = defaultSyncMetadata();
|
|
518
|
+
}),
|
|
519
|
+
removeEntity: (type, id) => set((s) => {
|
|
520
|
+
delete s.entities[type]?.[id];
|
|
521
|
+
delete s.patches[type]?.[id];
|
|
522
|
+
delete s.entityStates[ek(type, id)];
|
|
523
|
+
delete s.syncMetadata[ek(type, id)];
|
|
524
|
+
}),
|
|
525
|
+
patchEntity: (type, id, patch) => set((s) => {
|
|
526
|
+
if (!s.patches[type]) s.patches[type] = {};
|
|
527
|
+
s.patches[type][id] = { ...s.patches[type][id] ?? {}, ...patch };
|
|
528
|
+
}),
|
|
529
|
+
unpatchEntity: (type, id, keys) => set((s) => {
|
|
530
|
+
const p = s.patches[type]?.[id];
|
|
531
|
+
if (!p) return;
|
|
532
|
+
for (const k of keys) delete p[k];
|
|
533
|
+
}),
|
|
534
|
+
clearPatch: (type, id) => set((s) => {
|
|
535
|
+
delete s.patches[type]?.[id];
|
|
536
|
+
}),
|
|
537
|
+
setEntityFetching: (type, id, fetching) => set((s) => {
|
|
622
538
|
const k = ek(type, id);
|
|
623
|
-
if (s.entityStates[k]) s.entityStates[k]
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
539
|
+
if (!s.entityStates[k]) s.entityStates[k] = defaultEntityState();
|
|
540
|
+
s.entityStates[k].isFetching = fetching;
|
|
541
|
+
}),
|
|
542
|
+
setEntityError: (type, id, error) => set((s) => {
|
|
543
|
+
const k = ek(type, id);
|
|
544
|
+
if (!s.entityStates[k]) s.entityStates[k] = defaultEntityState();
|
|
545
|
+
s.entityStates[k].error = error;
|
|
546
|
+
s.entityStates[k].isFetching = false;
|
|
547
|
+
}),
|
|
548
|
+
setEntityFetched: (type, id) => set((s) => {
|
|
549
|
+
const k = ek(type, id);
|
|
550
|
+
if (!s.entityStates[k]) s.entityStates[k] = defaultEntityState();
|
|
551
|
+
s.entityStates[k].lastFetched = Date.now();
|
|
552
|
+
s.entityStates[k].isFetching = false;
|
|
553
|
+
s.entityStates[k].error = null;
|
|
554
|
+
s.entityStates[k].stale = false;
|
|
555
|
+
s.syncMetadata[k] = { ...s.syncMetadata[k] ?? defaultSyncMetadata(), synced: true, origin: "server", updatedAt: Date.now() };
|
|
556
|
+
}),
|
|
557
|
+
setEntityStale: (type, id, stale) => set((s) => {
|
|
558
|
+
const k = ek(type, id);
|
|
559
|
+
if (!s.entityStates[k]) s.entityStates[k] = defaultEntityState();
|
|
560
|
+
s.entityStates[k].stale = stale;
|
|
561
|
+
}),
|
|
562
|
+
setEntitySyncMetadata: (type, id, metadata) => set((s) => {
|
|
563
|
+
const k = ek(type, id);
|
|
564
|
+
s.syncMetadata[k] = { ...s.syncMetadata[k] ?? defaultSyncMetadata(), ...metadata };
|
|
565
|
+
}),
|
|
566
|
+
clearEntitySyncMetadata: (type, id) => set((s) => {
|
|
567
|
+
delete s.syncMetadata[ek(type, id)];
|
|
568
|
+
}),
|
|
569
|
+
setListResult: (key, ids, meta) => set((s) => {
|
|
570
|
+
const ex = s.lists[key] ?? defaultListState();
|
|
571
|
+
s.lists[key] = { ...ex, ...meta, ids, isFetching: false, isFetchingMore: false, error: null, lastError: null, stale: false, lastFetched: Date.now() };
|
|
572
|
+
}),
|
|
573
|
+
appendListResult: (key, ids, meta) => set((s) => {
|
|
574
|
+
const ex = s.lists[key] ?? defaultListState();
|
|
575
|
+
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() };
|
|
576
|
+
}),
|
|
577
|
+
prependListResult: (key, ids, meta) => set((s) => {
|
|
578
|
+
const ex = s.lists[key] ?? defaultListState();
|
|
579
|
+
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() };
|
|
580
|
+
}),
|
|
581
|
+
removeIdFromAllLists: (_type, id) => set((s) => {
|
|
582
|
+
for (const key of Object.keys(s.lists)) {
|
|
583
|
+
const list = s.lists[key];
|
|
584
|
+
const idx = list.ids.indexOf(id);
|
|
585
|
+
if (idx !== -1) {
|
|
586
|
+
list.ids.splice(idx, 1);
|
|
587
|
+
if (list.total !== null) list.total -= 1;
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
}),
|
|
591
|
+
insertIdInList: (key, id, position) => set((s) => {
|
|
592
|
+
if (!s.lists[key]) s.lists[key] = defaultListState();
|
|
593
|
+
const ids = s.lists[key].ids;
|
|
594
|
+
const ex = ids.indexOf(id);
|
|
595
|
+
if (ex !== -1) ids.splice(ex, 1);
|
|
596
|
+
if (position === "start") ids.unshift(id);
|
|
597
|
+
else if (position === "end") ids.push(id);
|
|
598
|
+
else ids.splice(position, 0, id);
|
|
599
|
+
}),
|
|
600
|
+
setListFetching: (key, fetching) => set((s) => {
|
|
601
|
+
if (!s.lists[key]) s.lists[key] = defaultListState();
|
|
602
|
+
s.lists[key].isFetching = fetching;
|
|
603
|
+
}),
|
|
604
|
+
setListFetchingMore: (key, fetchingMore) => set((s) => {
|
|
605
|
+
if (!s.lists[key]) s.lists[key] = defaultListState();
|
|
606
|
+
s.lists[key].isFetchingMore = fetchingMore;
|
|
607
|
+
}),
|
|
608
|
+
setListError: (key, error, typed) => set((s) => {
|
|
609
|
+
if (!s.lists[key]) s.lists[key] = defaultListState();
|
|
610
|
+
s.lists[key].error = error;
|
|
611
|
+
s.lists[key].lastError = typed ?? (error === null ? null : s.lists[key].lastError);
|
|
612
|
+
s.lists[key].isFetching = false;
|
|
613
|
+
s.lists[key].isFetchingMore = false;
|
|
614
|
+
s.lists[key].lastFetched = Date.now();
|
|
615
|
+
s.lists[key].stale = false;
|
|
616
|
+
}),
|
|
617
|
+
setListStale: (key, stale) => set((s) => {
|
|
618
|
+
if (!s.lists[key]) s.lists[key] = defaultListState();
|
|
619
|
+
s.lists[key].stale = stale;
|
|
620
|
+
}),
|
|
621
|
+
invalidateEntity: (type, id) => set((s) => {
|
|
622
|
+
if (id) {
|
|
623
|
+
const k = ek(type, id);
|
|
624
|
+
if (s.entityStates[k]) s.entityStates[k].stale = true;
|
|
625
|
+
} else {
|
|
626
|
+
for (const k of Object.keys(s.entityStates)) if (k.startsWith(`${type}:`)) s.entityStates[k].stale = true;
|
|
627
|
+
}
|
|
628
|
+
}),
|
|
629
|
+
invalidateLists: (matcher) => set((s) => {
|
|
630
|
+
const pred = typeof matcher === "string" ? (k) => k.startsWith(matcher) : matcher;
|
|
631
|
+
for (const key of Object.keys(s.lists)) if (pred(key)) s.lists[key].stale = true;
|
|
632
|
+
}),
|
|
633
|
+
invalidateType: (type) => {
|
|
634
|
+
get().invalidateEntity(type);
|
|
635
|
+
get().invalidateLists(type);
|
|
636
|
+
},
|
|
637
|
+
readEntity: (type, id) => {
|
|
638
|
+
const s = get();
|
|
639
|
+
const base = s.entities[type]?.[id];
|
|
640
|
+
if (!base) return null;
|
|
641
|
+
const patch = s.patches[type]?.[id];
|
|
642
|
+
return patch ? { ...base, ...patch } : base;
|
|
643
|
+
},
|
|
644
|
+
readEntitySnapshot: (type, id) => {
|
|
645
|
+
const s = get();
|
|
646
|
+
const base = s.entities[type]?.[id];
|
|
647
|
+
if (!base) {
|
|
648
|
+
snapshotCache.delete(ek(type, id));
|
|
649
|
+
return null;
|
|
650
|
+
}
|
|
651
|
+
const patch = s.patches[type]?.[id];
|
|
652
|
+
const metadata = s.syncMetadata[ek(type, id)] ?? EMPTY_SYNC_METADATA;
|
|
653
|
+
return readCachedEntitySnapshot(
|
|
654
|
+
type,
|
|
655
|
+
id,
|
|
656
|
+
base,
|
|
657
|
+
patch,
|
|
658
|
+
metadata
|
|
659
|
+
);
|
|
649
660
|
}
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
patch,
|
|
657
|
-
metadata
|
|
658
|
-
);
|
|
659
|
-
}
|
|
660
|
-
}))
|
|
661
|
-
)
|
|
662
|
-
);
|
|
661
|
+
}))
|
|
662
|
+
)
|
|
663
|
+
);
|
|
664
|
+
}
|
|
665
|
+
var graphStore = createGraphStore();
|
|
666
|
+
var useGraphStore = graphStore;
|
|
663
667
|
|
|
664
668
|
// src/object-path.ts
|
|
665
669
|
function isObject(value) {
|
|
@@ -708,13 +712,17 @@ var docs = /* @__PURE__ */ new Map();
|
|
|
708
712
|
function ek2(type, id) {
|
|
709
713
|
return `${type}:${id}`;
|
|
710
714
|
}
|
|
711
|
-
async function createLoroMergeStrategy() {
|
|
715
|
+
async function createLoroMergeStrategy(loadLoro) {
|
|
712
716
|
let mod;
|
|
713
717
|
try {
|
|
714
|
-
|
|
718
|
+
const loaded = loadLoro ? await loadLoro() : await import(
|
|
715
719
|
/* @vite-ignore */
|
|
716
720
|
'loro-crdt'
|
|
717
721
|
);
|
|
722
|
+
if (typeof loaded.LoroDoc !== "function") {
|
|
723
|
+
throw new TypeError("loro-crdt did not export a LoroDoc constructor");
|
|
724
|
+
}
|
|
725
|
+
mod = loaded;
|
|
718
726
|
} catch (cause) {
|
|
719
727
|
throw new Error(
|
|
720
728
|
"[merge/loro] createLoroMergeStrategy requires the optional peer dependency 'loro-crdt'. Install it with `pnpm add loro-crdt`.",
|
|
@@ -818,8 +826,17 @@ function serializeKey(key) {
|
|
|
818
826
|
function sleep(ms) {
|
|
819
827
|
return new Promise((r) => setTimeout(r, ms));
|
|
820
828
|
}
|
|
821
|
-
var
|
|
822
|
-
function
|
|
829
|
+
var inflightByStore = /* @__PURE__ */ new WeakMap();
|
|
830
|
+
function inflightFor(store) {
|
|
831
|
+
let inflight = inflightByStore.get(store);
|
|
832
|
+
if (!inflight) {
|
|
833
|
+
inflight = /* @__PURE__ */ new Map();
|
|
834
|
+
inflightByStore.set(store, inflight);
|
|
835
|
+
}
|
|
836
|
+
return inflight;
|
|
837
|
+
}
|
|
838
|
+
function dedupe(key, fn, store = graphStore) {
|
|
839
|
+
const inflight = inflightFor(store);
|
|
823
840
|
if (inflight.has(key)) return inflight.get(key);
|
|
824
841
|
const p = fn().finally(() => inflight.delete(key));
|
|
825
842
|
inflight.set(key, p);
|
|
@@ -829,23 +846,33 @@ var subscriberStatsListeners = /* @__PURE__ */ new Set();
|
|
|
829
846
|
function emitSubscriberStatsChange() {
|
|
830
847
|
for (const cb of subscriberStatsListeners) cb();
|
|
831
848
|
}
|
|
832
|
-
var
|
|
833
|
-
function
|
|
849
|
+
var subscribersByStore = /* @__PURE__ */ new WeakMap();
|
|
850
|
+
function subscribersFor(store) {
|
|
851
|
+
let subscribers = subscribersByStore.get(store);
|
|
852
|
+
if (!subscribers) {
|
|
853
|
+
subscribers = /* @__PURE__ */ new Map();
|
|
854
|
+
subscribersByStore.set(store, subscribers);
|
|
855
|
+
}
|
|
856
|
+
return subscribers;
|
|
857
|
+
}
|
|
858
|
+
function registerSubscriber(key, store = graphStore) {
|
|
859
|
+
const subscribers = subscribersFor(store);
|
|
834
860
|
const token = Symbol(key);
|
|
835
861
|
if (!subscribers.has(key)) subscribers.set(key, /* @__PURE__ */ new Set());
|
|
836
862
|
subscribers.get(key).add(token);
|
|
837
863
|
emitSubscriberStatsChange();
|
|
838
864
|
return token;
|
|
839
865
|
}
|
|
840
|
-
function unregisterSubscriber(key, token) {
|
|
866
|
+
function unregisterSubscriber(key, token, store = graphStore) {
|
|
867
|
+
const subscribers = subscribersFor(store);
|
|
841
868
|
const set = subscribers.get(key);
|
|
842
869
|
if (!set) return;
|
|
843
870
|
set.delete(token);
|
|
844
871
|
if (set.size === 0) subscribers.delete(key);
|
|
845
872
|
emitSubscriberStatsChange();
|
|
846
873
|
}
|
|
847
|
-
function hasSubscribers(key) {
|
|
848
|
-
return (
|
|
874
|
+
function hasSubscribers(key, store = graphStore) {
|
|
875
|
+
return (subscribersFor(store).get(key)?.size ?? 0) > 0;
|
|
849
876
|
}
|
|
850
877
|
var DEFAULT_OPTIONS = {
|
|
851
878
|
defaultStaleTime: 3e4,
|
|
@@ -861,9 +888,9 @@ function subscribeSubscriberStats(onChange) {
|
|
|
861
888
|
subscriberStatsListeners.add(onChange);
|
|
862
889
|
return () => subscriberStatsListeners.delete(onChange);
|
|
863
890
|
}
|
|
864
|
-
function getActiveSubscriberCount() {
|
|
891
|
+
function getActiveSubscriberCount(store = graphStore) {
|
|
865
892
|
let n = 0;
|
|
866
|
-
for (const set of
|
|
893
|
+
for (const set of subscribersFor(store).values()) n += set.size;
|
|
867
894
|
return n;
|
|
868
895
|
}
|
|
869
896
|
var devtoolsSubscribers = /* @__PURE__ */ new Set();
|
|
@@ -884,9 +911,9 @@ function notifyDevtools(event) {
|
|
|
884
911
|
}
|
|
885
912
|
}
|
|
886
913
|
}
|
|
887
|
-
var
|
|
888
|
-
function runGarbageCollection() {
|
|
889
|
-
const store =
|
|
914
|
+
var gcIntervalsByStore = /* @__PURE__ */ new Map();
|
|
915
|
+
function runGarbageCollection(storeApi = graphStore) {
|
|
916
|
+
const store = storeApi.getState();
|
|
890
917
|
const { defaultGcTime: gcTime } = getEngineOptions();
|
|
891
918
|
const now = Date.now();
|
|
892
919
|
const toRemove = [];
|
|
@@ -895,7 +922,7 @@ function runGarbageCollection() {
|
|
|
895
922
|
if (!bucket) continue;
|
|
896
923
|
for (const id of Object.keys(bucket)) {
|
|
897
924
|
const key = `${type}:${id}`;
|
|
898
|
-
if (hasSubscribers(key)) continue;
|
|
925
|
+
if (hasSubscribers(key, storeApi)) continue;
|
|
899
926
|
const patch = store.patches[type]?.[id];
|
|
900
927
|
if (patch !== void 0 && Object.keys(patch).length > 0) continue;
|
|
901
928
|
const entityState = store.entityStates[key];
|
|
@@ -911,41 +938,48 @@ function runGarbageCollection() {
|
|
|
911
938
|
store.removeIdFromAllLists(type, id);
|
|
912
939
|
}
|
|
913
940
|
}
|
|
914
|
-
function stopGarbageCollector() {
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
941
|
+
function stopGarbageCollector(storeApi = graphStore) {
|
|
942
|
+
const intervalId = gcIntervalsByStore.get(storeApi);
|
|
943
|
+
if (intervalId != null && typeof clearInterval !== "undefined") {
|
|
944
|
+
clearInterval(intervalId);
|
|
945
|
+
gcIntervalsByStore.delete(storeApi);
|
|
918
946
|
}
|
|
919
947
|
}
|
|
920
|
-
function startGarbageCollector() {
|
|
921
|
-
stopGarbageCollector();
|
|
948
|
+
function startGarbageCollector(storeApi = graphStore) {
|
|
949
|
+
stopGarbageCollector(storeApi);
|
|
922
950
|
if (typeof window === "undefined" || typeof setInterval === "undefined") return () => {
|
|
923
951
|
};
|
|
924
|
-
|
|
925
|
-
|
|
952
|
+
const intervalId = setInterval(
|
|
953
|
+
() => runGarbageCollection(storeApi),
|
|
954
|
+
getEngineOptions().gcInterval
|
|
955
|
+
);
|
|
956
|
+
gcIntervalsByStore.set(storeApi, intervalId);
|
|
957
|
+
return () => stopGarbageCollector(storeApi);
|
|
926
958
|
}
|
|
927
|
-
function restartGarbageCollector() {
|
|
928
|
-
startGarbageCollector();
|
|
959
|
+
function restartGarbageCollector(storeApi = graphStore) {
|
|
960
|
+
startGarbageCollector(storeApi);
|
|
929
961
|
}
|
|
930
962
|
function configureEngine(opts) {
|
|
931
963
|
engineOptions = { ...DEFAULT_OPTIONS, ...opts };
|
|
932
|
-
|
|
964
|
+
const activeStores = [...gcIntervalsByStore.keys()];
|
|
965
|
+
if (activeStores.length === 0) restartGarbageCollector();
|
|
966
|
+
else for (const storeApi of activeStores) restartGarbageCollector(storeApi);
|
|
933
967
|
}
|
|
934
968
|
function getEngineOptions() {
|
|
935
969
|
return engineOptions;
|
|
936
970
|
}
|
|
937
|
-
async function fetchEntity(opts, engineOpts) {
|
|
971
|
+
async function fetchEntity(opts, engineOpts, storeApi = graphStore) {
|
|
938
972
|
const { type, id, fetch, normalize, sideEffects, idField = "id" } = opts;
|
|
939
973
|
if (!id) return;
|
|
940
|
-
|
|
974
|
+
storeApi.getState().setEntityFetching(type, id, true);
|
|
941
975
|
const attempt = async (retries) => {
|
|
942
976
|
try {
|
|
943
977
|
const raw = await fetch(id);
|
|
944
978
|
const normalized = normalize(raw);
|
|
945
979
|
const resolvedId = normalized[idField] ?? id;
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
if (sideEffects) sideEffects(raw,
|
|
980
|
+
storeApi.getState().upsertEntity(type, resolvedId, normalized);
|
|
981
|
+
storeApi.getState().setEntityFetched(type, resolvedId);
|
|
982
|
+
if (sideEffects) sideEffects(raw, storeApi);
|
|
949
983
|
opts.onSuccess?.(normalized);
|
|
950
984
|
} catch (err) {
|
|
951
985
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
@@ -953,16 +987,16 @@ async function fetchEntity(opts, engineOpts) {
|
|
|
953
987
|
await sleep(engineOpts.retryBaseDelay * Math.pow(2, retries));
|
|
954
988
|
return attempt(retries + 1);
|
|
955
989
|
}
|
|
956
|
-
|
|
990
|
+
storeApi.getState().setEntityError(type, id, error.message);
|
|
957
991
|
opts.onError?.(error);
|
|
958
992
|
}
|
|
959
993
|
};
|
|
960
|
-
await dedupe(`${type}:${id}`, () => attempt(0));
|
|
994
|
+
await dedupe(`${type}:${id}`, () => attempt(0), storeApi);
|
|
961
995
|
}
|
|
962
|
-
async function fetchList(opts, params, engineOpts, isLoadMore = false) {
|
|
996
|
+
async function fetchList(opts, params, engineOpts, isLoadMore = false, storeApi = graphStore) {
|
|
963
997
|
const { type, queryKey, fetch, normalize, sideEffects, mode = "replace" } = opts;
|
|
964
998
|
const key = serializeKey(queryKey);
|
|
965
|
-
const store =
|
|
999
|
+
const store = storeApi.getState();
|
|
966
1000
|
if (!fetch || !normalize) return;
|
|
967
1001
|
if (isLoadMore) store.setListFetchingMore(key, true);
|
|
968
1002
|
else store.setListFetching(key, true);
|
|
@@ -970,13 +1004,13 @@ async function fetchList(opts, params, engineOpts, isLoadMore = false) {
|
|
|
970
1004
|
try {
|
|
971
1005
|
const response = await fetch(params);
|
|
972
1006
|
const normalized = response.items.map(normalize);
|
|
973
|
-
|
|
974
|
-
for (const { id } of normalized)
|
|
1007
|
+
storeApi.getState().upsertEntities(type, normalized.map(({ id, data }) => ({ id, data })));
|
|
1008
|
+
for (const { id } of normalized) storeApi.getState().setEntityFetched(type, id);
|
|
975
1009
|
const ids = normalized.map(({ id }) => id);
|
|
976
1010
|
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 };
|
|
977
|
-
if (mode === "append" && isLoadMore)
|
|
978
|
-
else
|
|
979
|
-
if (sideEffects) sideEffects(response.items,
|
|
1011
|
+
if (mode === "append" && isLoadMore) storeApi.getState().appendListResult(key, ids, meta);
|
|
1012
|
+
else storeApi.getState().setListResult(key, ids, meta);
|
|
1013
|
+
if (sideEffects) sideEffects(response.items, storeApi);
|
|
980
1014
|
opts.onSuccess?.(response);
|
|
981
1015
|
} catch (err) {
|
|
982
1016
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
@@ -984,21 +1018,39 @@ async function fetchList(opts, params, engineOpts, isLoadMore = false) {
|
|
|
984
1018
|
await sleep(engineOpts.retryBaseDelay * Math.pow(2, retries));
|
|
985
1019
|
return attempt(retries + 1);
|
|
986
1020
|
}
|
|
987
|
-
|
|
1021
|
+
storeApi.getState().setListError(key, error.message);
|
|
988
1022
|
opts.onError?.(error);
|
|
989
1023
|
}
|
|
990
1024
|
};
|
|
991
|
-
await dedupe(isLoadMore ? `${key}:more` : key, () => attempt(0));
|
|
1025
|
+
await dedupe(isLoadMore ? `${key}:more` : key, () => attempt(0), storeApi);
|
|
992
1026
|
}
|
|
993
|
-
var
|
|
994
|
-
function
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
1027
|
+
var globalListenerAttachments = /* @__PURE__ */ new WeakMap();
|
|
1028
|
+
function releaseGlobalListenerAttachment(storeApi, attachment) {
|
|
1029
|
+
let released = false;
|
|
1030
|
+
return () => {
|
|
1031
|
+
if (released) return;
|
|
1032
|
+
released = true;
|
|
1033
|
+
const current = globalListenerAttachments.get(storeApi);
|
|
1034
|
+
if (current !== attachment) return;
|
|
1035
|
+
current.references -= 1;
|
|
1036
|
+
if (current.references > 0) return;
|
|
1037
|
+
current.dispose();
|
|
1038
|
+
globalListenerAttachments.delete(storeApi);
|
|
1039
|
+
};
|
|
1040
|
+
}
|
|
1041
|
+
function attachGlobalListeners(storeApi = graphStore) {
|
|
1042
|
+
if (typeof window === "undefined") return () => {
|
|
1043
|
+
};
|
|
1044
|
+
const existing = globalListenerAttachments.get(storeApi);
|
|
1045
|
+
if (existing) {
|
|
1046
|
+
existing.references += 1;
|
|
1047
|
+
return releaseGlobalListenerAttachment(storeApi, existing);
|
|
1048
|
+
}
|
|
1049
|
+
const stopGc = startGarbageCollector(storeApi);
|
|
998
1050
|
const revalidateAll = () => {
|
|
999
|
-
const state =
|
|
1000
|
-
for (const key of
|
|
1001
|
-
if (!hasSubscribers(key)) continue;
|
|
1051
|
+
const state = storeApi.getState();
|
|
1052
|
+
for (const key of subscribersFor(storeApi).keys()) {
|
|
1053
|
+
if (!hasSubscribers(key, storeApi)) continue;
|
|
1002
1054
|
const colonIdx = key.indexOf(":");
|
|
1003
1055
|
if (colonIdx === -1) continue;
|
|
1004
1056
|
const type = key.slice(0, colonIdx);
|
|
@@ -1006,65 +1058,83 @@ function attachGlobalListeners() {
|
|
|
1006
1058
|
state.setEntityStale(type, id, true);
|
|
1007
1059
|
}
|
|
1008
1060
|
};
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1061
|
+
const revalidateWhenVisible = () => {
|
|
1062
|
+
if (document.visibilityState === "visible") revalidateAll();
|
|
1063
|
+
};
|
|
1064
|
+
const revalidateOnFocus = engineOptions.revalidateOnFocus;
|
|
1065
|
+
const revalidateOnReconnect = engineOptions.revalidateOnReconnect;
|
|
1066
|
+
if (revalidateOnFocus) {
|
|
1067
|
+
window.addEventListener("visibilitychange", revalidateWhenVisible);
|
|
1013
1068
|
window.addEventListener("focus", revalidateAll);
|
|
1014
1069
|
}
|
|
1015
|
-
if (
|
|
1070
|
+
if (revalidateOnReconnect) window.addEventListener("online", revalidateAll);
|
|
1071
|
+
const attachment = {
|
|
1072
|
+
references: 1,
|
|
1073
|
+
dispose: () => {
|
|
1074
|
+
if (revalidateOnFocus) {
|
|
1075
|
+
window.removeEventListener("visibilitychange", revalidateWhenVisible);
|
|
1076
|
+
window.removeEventListener("focus", revalidateAll);
|
|
1077
|
+
}
|
|
1078
|
+
if (revalidateOnReconnect) {
|
|
1079
|
+
window.removeEventListener("online", revalidateAll);
|
|
1080
|
+
}
|
|
1081
|
+
stopGc();
|
|
1082
|
+
}
|
|
1083
|
+
};
|
|
1084
|
+
globalListenerAttachments.set(storeApi, attachment);
|
|
1085
|
+
return releaseGlobalListenerAttachment(storeApi, attachment);
|
|
1016
1086
|
}
|
|
1017
1087
|
|
|
1018
1088
|
// src/graph-actions.ts
|
|
1019
1089
|
var graphActionListeners = /* @__PURE__ */ new Set();
|
|
1020
1090
|
var graphActionReplayers = /* @__PURE__ */ new Map();
|
|
1021
|
-
function createGraphTransaction() {
|
|
1022
|
-
const baseline = cloneGraphData();
|
|
1091
|
+
function createGraphTransaction(storeApi = graphStore) {
|
|
1092
|
+
const baseline = cloneGraphData(storeApi.getState());
|
|
1023
1093
|
let closed = false;
|
|
1024
1094
|
const tx = {
|
|
1025
1095
|
upsertEntity(type, id, data) {
|
|
1026
|
-
|
|
1096
|
+
storeApi.getState().upsertEntity(type, id, data);
|
|
1027
1097
|
if (typeof process === "undefined" || process.env.NODE_ENV !== "production") {
|
|
1028
1098
|
notifyDevtools({ kind: "upsert", type, id, data, at: (/* @__PURE__ */ new Date()).toISOString() });
|
|
1029
1099
|
}
|
|
1030
1100
|
return tx;
|
|
1031
1101
|
},
|
|
1032
1102
|
replaceEntity(type, id, data) {
|
|
1033
|
-
|
|
1103
|
+
storeApi.getState().replaceEntity(type, id, data);
|
|
1034
1104
|
return tx;
|
|
1035
1105
|
},
|
|
1036
1106
|
removeEntity(type, id) {
|
|
1037
|
-
|
|
1107
|
+
storeApi.getState().removeEntity(type, id);
|
|
1038
1108
|
return tx;
|
|
1039
1109
|
},
|
|
1040
1110
|
patchEntity(type, id, patch) {
|
|
1041
|
-
|
|
1111
|
+
storeApi.getState().patchEntity(type, id, patch);
|
|
1042
1112
|
if (typeof process === "undefined" || process.env.NODE_ENV !== "production") {
|
|
1043
1113
|
notifyDevtools({ kind: "patch", type, id, patch, at: (/* @__PURE__ */ new Date()).toISOString() });
|
|
1044
1114
|
}
|
|
1045
1115
|
return tx;
|
|
1046
1116
|
},
|
|
1047
1117
|
clearPatch(type, id) {
|
|
1048
|
-
|
|
1118
|
+
storeApi.getState().clearPatch(type, id);
|
|
1049
1119
|
if (typeof process === "undefined" || process.env.NODE_ENV !== "production") {
|
|
1050
1120
|
notifyDevtools({ kind: "clearPatch", type, id, at: (/* @__PURE__ */ new Date()).toISOString() });
|
|
1051
1121
|
}
|
|
1052
1122
|
return tx;
|
|
1053
1123
|
},
|
|
1054
1124
|
insertIdInList(key, id, position) {
|
|
1055
|
-
|
|
1125
|
+
storeApi.getState().insertIdInList(key, id, position);
|
|
1056
1126
|
return tx;
|
|
1057
1127
|
},
|
|
1058
1128
|
removeIdFromAllLists(type, id) {
|
|
1059
|
-
|
|
1129
|
+
storeApi.getState().removeIdFromAllLists(type, id);
|
|
1060
1130
|
return tx;
|
|
1061
1131
|
},
|
|
1062
1132
|
setEntitySyncMetadata(type, id, metadata) {
|
|
1063
|
-
|
|
1133
|
+
storeApi.getState().setEntitySyncMetadata(type, id, metadata);
|
|
1064
1134
|
return tx;
|
|
1065
1135
|
},
|
|
1066
1136
|
markEntityPending(type, id, origin = "optimistic") {
|
|
1067
|
-
|
|
1137
|
+
storeApi.getState().setEntitySyncMetadata(type, id, {
|
|
1068
1138
|
synced: false,
|
|
1069
1139
|
origin,
|
|
1070
1140
|
updatedAt: Date.now()
|
|
@@ -1072,7 +1142,7 @@ function createGraphTransaction() {
|
|
|
1072
1142
|
return tx;
|
|
1073
1143
|
},
|
|
1074
1144
|
markEntitySynced(type, id, origin = "server") {
|
|
1075
|
-
|
|
1145
|
+
storeApi.getState().setEntitySyncMetadata(type, id, {
|
|
1076
1146
|
synced: true,
|
|
1077
1147
|
origin,
|
|
1078
1148
|
updatedAt: Date.now()
|
|
@@ -1084,19 +1154,19 @@ function createGraphTransaction() {
|
|
|
1084
1154
|
},
|
|
1085
1155
|
rollback() {
|
|
1086
1156
|
if (closed) return;
|
|
1087
|
-
|
|
1157
|
+
storeApi.setState(cloneGraphData(baseline));
|
|
1088
1158
|
closed = true;
|
|
1089
1159
|
},
|
|
1090
1160
|
snapshot() {
|
|
1091
|
-
return cloneGraphData();
|
|
1161
|
+
return cloneGraphData(storeApi.getState());
|
|
1092
1162
|
}
|
|
1093
1163
|
};
|
|
1094
1164
|
return tx;
|
|
1095
1165
|
}
|
|
1096
1166
|
function createGraphAction(opts) {
|
|
1097
1167
|
if (opts.key) {
|
|
1098
|
-
graphActionReplayers.set(opts.key, async (record) => {
|
|
1099
|
-
const tx = createGraphTransaction();
|
|
1168
|
+
graphActionReplayers.set(opts.key, async (record, storeApi) => {
|
|
1169
|
+
const tx = createGraphTransaction(storeApi);
|
|
1100
1170
|
try {
|
|
1101
1171
|
const result = await opts.run(tx, record.input);
|
|
1102
1172
|
tx.commit();
|
|
@@ -1136,12 +1206,12 @@ function subscribeGraphActionEvents(listener) {
|
|
|
1136
1206
|
graphActionListeners.add(listener);
|
|
1137
1207
|
return () => graphActionListeners.delete(listener);
|
|
1138
1208
|
}
|
|
1139
|
-
async function replayRegisteredGraphAction(record) {
|
|
1209
|
+
async function replayRegisteredGraphAction(record, storeApi = graphStore) {
|
|
1140
1210
|
const replayer = graphActionReplayers.get(record.key);
|
|
1141
1211
|
if (!replayer) throw new Error(`No graph action registered for key "${record.key}"`);
|
|
1142
|
-
return replayer(record);
|
|
1212
|
+
return replayer(record, storeApi);
|
|
1143
1213
|
}
|
|
1144
|
-
function cloneGraphData(source =
|
|
1214
|
+
function cloneGraphData(source = graphStore.getState()) {
|
|
1145
1215
|
return {
|
|
1146
1216
|
entities: structuredClone(source.entities),
|
|
1147
1217
|
patches: structuredClone(source.patches),
|
|
@@ -1323,7 +1393,7 @@ function createSchemaGraphTool(handler) {
|
|
|
1323
1393
|
});
|
|
1324
1394
|
}
|
|
1325
1395
|
var DEFAULT_STORAGE_KEY = "prometheus:graph";
|
|
1326
|
-
var
|
|
1396
|
+
var graphSyncStatusStore = createStore()((set) => ({
|
|
1327
1397
|
status: {
|
|
1328
1398
|
phase: "idle",
|
|
1329
1399
|
isOnline: true,
|
|
@@ -1342,19 +1412,20 @@ var useGraphSyncStatusStore = create((set) => ({
|
|
|
1342
1412
|
}))
|
|
1343
1413
|
}));
|
|
1344
1414
|
var pendingActions = /* @__PURE__ */ new Map();
|
|
1345
|
-
function
|
|
1346
|
-
return
|
|
1415
|
+
function getGraphSyncStatus() {
|
|
1416
|
+
return graphSyncStatusStore.getState().status;
|
|
1347
1417
|
}
|
|
1348
1418
|
async function persistGraphToStorage(opts) {
|
|
1419
|
+
const storeApi = opts.store ?? graphStore;
|
|
1349
1420
|
const payload = {
|
|
1350
1421
|
version: 1,
|
|
1351
|
-
snapshot: cloneGraphSnapshot(),
|
|
1422
|
+
snapshot: cloneGraphSnapshot(storeApi),
|
|
1352
1423
|
pendingActions: opts.pendingActions ?? Array.from(pendingActions.values())
|
|
1353
1424
|
};
|
|
1354
1425
|
const json = JSON.stringify(payload);
|
|
1355
1426
|
await opts.storage.set(opts.key, json);
|
|
1356
1427
|
const persistedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1357
|
-
|
|
1428
|
+
graphSyncStatusStore.getState().setStatus({
|
|
1358
1429
|
lastPersistedAt: persistedAt,
|
|
1359
1430
|
storageKey: opts.key,
|
|
1360
1431
|
pendingActions: payload.pendingActions.length
|
|
@@ -1367,6 +1438,7 @@ async function persistGraphToStorage(opts) {
|
|
|
1367
1438
|
};
|
|
1368
1439
|
}
|
|
1369
1440
|
async function hydrateGraphFromStorage(opts) {
|
|
1441
|
+
const storeApi = opts.store ?? graphStore;
|
|
1370
1442
|
const raw = await opts.storage.get(opts.key);
|
|
1371
1443
|
if (!raw) {
|
|
1372
1444
|
return {
|
|
@@ -1379,11 +1451,11 @@ async function hydrateGraphFromStorage(opts) {
|
|
|
1379
1451
|
}
|
|
1380
1452
|
try {
|
|
1381
1453
|
const parsed = JSON.parse(raw);
|
|
1382
|
-
|
|
1454
|
+
storeApi.setState(parsed.snapshot);
|
|
1383
1455
|
pendingActions.clear();
|
|
1384
1456
|
for (const action of parsed.pendingActions ?? []) pendingActions.set(action.id, action);
|
|
1385
1457
|
const hydratedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1386
|
-
|
|
1458
|
+
graphSyncStatusStore.getState().setStatus({
|
|
1387
1459
|
lastHydratedAt: hydratedAt,
|
|
1388
1460
|
storageKey: opts.key,
|
|
1389
1461
|
pendingActions: pendingActions.size,
|
|
@@ -1400,7 +1472,7 @@ async function hydrateGraphFromStorage(opts) {
|
|
|
1400
1472
|
};
|
|
1401
1473
|
} catch (error) {
|
|
1402
1474
|
const message = error instanceof Error ? error.message : String(error);
|
|
1403
|
-
|
|
1475
|
+
graphSyncStatusStore.getState().setStatus({
|
|
1404
1476
|
phase: "error",
|
|
1405
1477
|
error: message,
|
|
1406
1478
|
storageKey: opts.key
|
|
@@ -1415,9 +1487,10 @@ async function hydrateGraphFromStorage(opts) {
|
|
|
1415
1487
|
}
|
|
1416
1488
|
}
|
|
1417
1489
|
function startLocalFirstGraph(opts) {
|
|
1490
|
+
const storeApi = opts.store ?? graphStore;
|
|
1418
1491
|
const key = opts.key ?? DEFAULT_STORAGE_KEY;
|
|
1419
1492
|
const persistDebounceMs = opts.persistDebounceMs ?? 50;
|
|
1420
|
-
const statusStore =
|
|
1493
|
+
const statusStore = graphSyncStatusStore.getState();
|
|
1421
1494
|
statusStore.setStatus({
|
|
1422
1495
|
phase: "hydrating",
|
|
1423
1496
|
storageKey: key,
|
|
@@ -1429,16 +1502,16 @@ function startLocalFirstGraph(opts) {
|
|
|
1429
1502
|
const schedulePersist = () => {
|
|
1430
1503
|
if (persistTimer) clearTimeout(persistTimer);
|
|
1431
1504
|
persistTimer = setTimeout(() => {
|
|
1432
|
-
void persistGraphToStorage({ storage: opts.storage, key });
|
|
1505
|
+
void persistGraphToStorage({ storage: opts.storage, key, store: storeApi });
|
|
1433
1506
|
}, persistDebounceMs);
|
|
1434
1507
|
};
|
|
1435
|
-
const graphUnsub =
|
|
1508
|
+
const graphUnsub = storeApi.subscribe(() => {
|
|
1436
1509
|
schedulePersist();
|
|
1437
1510
|
});
|
|
1438
1511
|
const actionUnsub = subscribeGraphActionEvents((event) => {
|
|
1439
1512
|
if (event.type === "enqueued") pendingActions.set(event.record.id, event.record);
|
|
1440
1513
|
if (event.type === "settled") pendingActions.delete(event.record.id);
|
|
1441
|
-
|
|
1514
|
+
graphSyncStatusStore.getState().setStatus({
|
|
1442
1515
|
pendingActions: pendingActions.size,
|
|
1443
1516
|
isSynced: pendingActions.size === 0
|
|
1444
1517
|
});
|
|
@@ -1446,27 +1519,27 @@ function startLocalFirstGraph(opts) {
|
|
|
1446
1519
|
});
|
|
1447
1520
|
const onlineSource = opts.onlineSource ?? getDefaultOnlineSource();
|
|
1448
1521
|
const onlineUnsub = onlineSource.subscribe((online) => {
|
|
1449
|
-
|
|
1522
|
+
graphSyncStatusStore.getState().setStatus({
|
|
1450
1523
|
isOnline: online,
|
|
1451
1524
|
phase: online ? "ready" : "offline"
|
|
1452
1525
|
});
|
|
1453
1526
|
});
|
|
1454
1527
|
const ready = (async () => {
|
|
1455
|
-
const hydrated = await hydrateGraphFromStorage({ storage: opts.storage, key });
|
|
1528
|
+
const hydrated = await hydrateGraphFromStorage({ storage: opts.storage, key, store: storeApi });
|
|
1456
1529
|
if (opts.replayPendingActions && hydrated.ok && pendingActions.size > 0) {
|
|
1457
|
-
|
|
1530
|
+
graphSyncStatusStore.getState().setStatus({
|
|
1458
1531
|
phase: "syncing",
|
|
1459
1532
|
isSynced: false
|
|
1460
1533
|
});
|
|
1461
1534
|
const policy = resolveRetryPolicy(opts.retryPolicy);
|
|
1462
1535
|
for (const action of Array.from(pendingActions.values())) {
|
|
1463
|
-
await replayActionWithRetry(action, policy);
|
|
1536
|
+
await replayActionWithRetry(action, policy, storeApi);
|
|
1464
1537
|
pendingActions.delete(action.id);
|
|
1465
1538
|
}
|
|
1466
|
-
await persistGraphToStorage({ storage: opts.storage, key });
|
|
1539
|
+
await persistGraphToStorage({ storage: opts.storage, key, store: storeApi });
|
|
1467
1540
|
}
|
|
1468
1541
|
const online = onlineSource.getIsOnline();
|
|
1469
|
-
|
|
1542
|
+
graphSyncStatusStore.getState().setStatus({
|
|
1470
1543
|
phase: online ? "ready" : "offline",
|
|
1471
1544
|
isOnline: online,
|
|
1472
1545
|
isSynced: pendingActions.size === 0,
|
|
@@ -1482,18 +1555,18 @@ function startLocalFirstGraph(opts) {
|
|
|
1482
1555
|
if (persistTimer) clearTimeout(persistTimer);
|
|
1483
1556
|
},
|
|
1484
1557
|
async persistNow() {
|
|
1485
|
-
await persistGraphToStorage({ storage: opts.storage, key });
|
|
1558
|
+
await persistGraphToStorage({ storage: opts.storage, key, store: storeApi });
|
|
1486
1559
|
},
|
|
1487
1560
|
hydrate() {
|
|
1488
|
-
return hydrateGraphFromStorage({ storage: opts.storage, key });
|
|
1561
|
+
return hydrateGraphFromStorage({ storage: opts.storage, key, store: storeApi });
|
|
1489
1562
|
},
|
|
1490
1563
|
getStatus() {
|
|
1491
|
-
return
|
|
1564
|
+
return graphSyncStatusStore.getState().status;
|
|
1492
1565
|
}
|
|
1493
1566
|
};
|
|
1494
1567
|
}
|
|
1495
|
-
function cloneGraphSnapshot() {
|
|
1496
|
-
const state =
|
|
1568
|
+
function cloneGraphSnapshot(storeApi) {
|
|
1569
|
+
const state = storeApi.getState();
|
|
1497
1570
|
return {
|
|
1498
1571
|
entities: structuredClone(state.entities),
|
|
1499
1572
|
patches: structuredClone(state.patches),
|
|
@@ -1531,11 +1604,11 @@ function sleep2(ms) {
|
|
|
1531
1604
|
if (ms <= 0) return Promise.resolve();
|
|
1532
1605
|
return new Promise((r) => setTimeout(r, ms));
|
|
1533
1606
|
}
|
|
1534
|
-
async function replayActionWithRetry(action, policy) {
|
|
1607
|
+
async function replayActionWithRetry(action, policy, storeApi = graphStore) {
|
|
1535
1608
|
let lastError = null;
|
|
1536
1609
|
for (let attempt = 1; attempt <= policy.maxAttempts; attempt++) {
|
|
1537
1610
|
try {
|
|
1538
|
-
await replayRegisteredGraphAction(action);
|
|
1611
|
+
await replayRegisteredGraphAction(action, storeApi);
|
|
1539
1612
|
return { ok: true };
|
|
1540
1613
|
} catch (error) {
|
|
1541
1614
|
lastError = error;
|
|
@@ -1595,9 +1668,9 @@ function createDevtoolsEventBus(opts) {
|
|
|
1595
1668
|
if (count < bufferSize) return buffer.slice(0, count);
|
|
1596
1669
|
return [...buffer.slice(head), ...buffer.slice(0, head)];
|
|
1597
1670
|
}
|
|
1598
|
-
const
|
|
1671
|
+
const subscribers = /* @__PURE__ */ new Set();
|
|
1599
1672
|
function dispatchToSubscribers(event) {
|
|
1600
|
-
const snap = [...
|
|
1673
|
+
const snap = [...subscribers];
|
|
1601
1674
|
for (const cb of snap) {
|
|
1602
1675
|
try {
|
|
1603
1676
|
cb(event);
|
|
@@ -1673,9 +1746,9 @@ function createDevtoolsEventBus(opts) {
|
|
|
1673
1746
|
console.warn("[devtools-event-bus] subscriber threw during replay:", err);
|
|
1674
1747
|
}
|
|
1675
1748
|
}
|
|
1676
|
-
|
|
1749
|
+
subscribers.add(cb);
|
|
1677
1750
|
return () => {
|
|
1678
|
-
|
|
1751
|
+
subscribers.delete(cb);
|
|
1679
1752
|
};
|
|
1680
1753
|
},
|
|
1681
1754
|
getBuffer,
|
|
@@ -1687,7 +1760,7 @@ function createDevtoolsEventBus(opts) {
|
|
|
1687
1760
|
if (destroyed) return;
|
|
1688
1761
|
destroyed = true;
|
|
1689
1762
|
unsubscribeEngine();
|
|
1690
|
-
|
|
1763
|
+
subscribers.clear();
|
|
1691
1764
|
pendingBurst = [];
|
|
1692
1765
|
dispatchedCount = 0;
|
|
1693
1766
|
flushScheduled = false;
|
|
@@ -1753,8 +1826,8 @@ function configureTimeTravel(opts) {
|
|
|
1753
1826
|
if (ring.length > capacity) ring = ring.slice(-capacity);
|
|
1754
1827
|
notify();
|
|
1755
1828
|
}
|
|
1756
|
-
function recordGraphSnapshot(label) {
|
|
1757
|
-
const s =
|
|
1829
|
+
function recordGraphSnapshot(label, storeApi = graphStore) {
|
|
1830
|
+
const s = storeApi.getState();
|
|
1758
1831
|
const snap = {
|
|
1759
1832
|
seq: seqCounter++,
|
|
1760
1833
|
at: Date.now(),
|
|
@@ -1767,23 +1840,23 @@ function recordGraphSnapshot(label) {
|
|
|
1767
1840
|
notify();
|
|
1768
1841
|
return snap.seq;
|
|
1769
1842
|
}
|
|
1770
|
-
function restoreGraphSnapshot(index) {
|
|
1843
|
+
function restoreGraphSnapshot(index, storeApi = graphStore) {
|
|
1771
1844
|
const snap = ring[index];
|
|
1772
1845
|
if (!snap) return false;
|
|
1773
1846
|
const clone2 = cloneSlice(snap.data);
|
|
1774
|
-
|
|
1847
|
+
storeApi.setState(clone2);
|
|
1775
1848
|
cursor = index;
|
|
1776
1849
|
notify();
|
|
1777
1850
|
return true;
|
|
1778
1851
|
}
|
|
1779
|
-
function restoreGraphSnapshotBySeq(seq) {
|
|
1852
|
+
function restoreGraphSnapshotBySeq(seq, storeApi = graphStore) {
|
|
1780
1853
|
const index = ring.findIndex((s) => s.seq === seq);
|
|
1781
|
-
return index === -1 ? false : restoreGraphSnapshot(index);
|
|
1854
|
+
return index === -1 ? false : restoreGraphSnapshot(index, storeApi);
|
|
1782
1855
|
}
|
|
1783
|
-
function stepTimeTravel(delta) {
|
|
1856
|
+
function stepTimeTravel(delta, storeApi = graphStore) {
|
|
1784
1857
|
const base = cursor ?? ring.length - 1;
|
|
1785
1858
|
const target = Math.max(0, Math.min(ring.length - 1, base + delta));
|
|
1786
|
-
return restoreGraphSnapshot(target);
|
|
1859
|
+
return restoreGraphSnapshot(target, storeApi);
|
|
1787
1860
|
}
|
|
1788
1861
|
function getTimeTravelState() {
|
|
1789
1862
|
return {
|
|
@@ -2026,10 +2099,10 @@ function registerSchema(schema) {
|
|
|
2026
2099
|
function getSchema(type) {
|
|
2027
2100
|
return schemaRegistry2.get(type) ?? null;
|
|
2028
2101
|
}
|
|
2029
|
-
function cascadeInvalidation(ctx) {
|
|
2102
|
+
function cascadeInvalidation(ctx, storeApi = graphStore) {
|
|
2030
2103
|
const schema = schemaRegistry2.get(ctx.type);
|
|
2031
2104
|
if (!schema) return;
|
|
2032
|
-
const store =
|
|
2105
|
+
const store = storeApi.getState();
|
|
2033
2106
|
if (schema.globalListKeys) for (const key of schema.globalListKeys) store.invalidateLists(key);
|
|
2034
2107
|
if (!schema.relations) return;
|
|
2035
2108
|
for (const [, relation] of Object.entries(schema.relations)) {
|
|
@@ -2062,10 +2135,10 @@ function cascadeInvalidation(ctx) {
|
|
|
2062
2135
|
}
|
|
2063
2136
|
}
|
|
2064
2137
|
}
|
|
2065
|
-
function readRelations(type, entity) {
|
|
2138
|
+
function readRelations(type, entity, storeApi = graphStore) {
|
|
2066
2139
|
const schema = schemaRegistry2.get(type);
|
|
2067
2140
|
if (!schema?.relations) return {};
|
|
2068
|
-
const store =
|
|
2141
|
+
const store = storeApi.getState();
|
|
2069
2142
|
const result = {};
|
|
2070
2143
|
for (const [name, relation] of Object.entries(schema.relations)) {
|
|
2071
2144
|
switch (relation.cardinality) {
|
|
@@ -2281,7 +2354,9 @@ var RealtimeManager = class {
|
|
|
2281
2354
|
pendingListKeys = /* @__PURE__ */ new Set();
|
|
2282
2355
|
flushTimer = null;
|
|
2283
2356
|
opts;
|
|
2357
|
+
store;
|
|
2284
2358
|
constructor(opts = {}) {
|
|
2359
|
+
this.store = opts.store ?? graphStore;
|
|
2285
2360
|
this.opts = {
|
|
2286
2361
|
flushInterval: opts.flushInterval ?? 16,
|
|
2287
2362
|
onStatusChange: opts.onStatusChange ?? (() => {
|
|
@@ -2341,7 +2416,7 @@ var RealtimeManager = class {
|
|
|
2341
2416
|
const changes = this.pendingChanges.splice(0);
|
|
2342
2417
|
const listKeys = new Set(this.pendingListKeys);
|
|
2343
2418
|
this.pendingListKeys.clear();
|
|
2344
|
-
const store =
|
|
2419
|
+
const store = this.store.getState();
|
|
2345
2420
|
for (const change of coalesceChanges(changes)) {
|
|
2346
2421
|
switch (change.op) {
|
|
2347
2422
|
case "insert":
|
|
@@ -2390,6 +2465,13 @@ function coalesceChanges(changes) {
|
|
|
2390
2465
|
continue;
|
|
2391
2466
|
}
|
|
2392
2467
|
if (existing.op === "delete") continue;
|
|
2468
|
+
if (existing.op === "update" && c.op === "update") {
|
|
2469
|
+
byKey.set(key, {
|
|
2470
|
+
...existing,
|
|
2471
|
+
patch: { ...existing.patch ?? {}, ...c.patch ?? {} }
|
|
2472
|
+
});
|
|
2473
|
+
continue;
|
|
2474
|
+
}
|
|
2393
2475
|
byKey.set(key, { ...existing, op: "upsert", data: c.data ?? existing.data, patch: c.patch ? { ...existing.patch ?? {}, ...c.patch } : existing.patch });
|
|
2394
2476
|
}
|
|
2395
2477
|
return Array.from(byKey.values());
|
|
@@ -3753,6 +3835,6 @@ function getFacetedRowModel(preFilteredRowModel, columnId, allFilteredRowModel)
|
|
|
3753
3835
|
return preFilteredRowModel;
|
|
3754
3836
|
}
|
|
3755
3837
|
|
|
3756
|
-
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, 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, getGroupedRowModel, getMergeStrategy, getPaginatedRowModel, getRealtimeManager, getRegisteredEntityTypes, getRegisteredStores, getSchema, getSelectedRowModel, getSortedRowModel, getTimeTravelState, getValueAtPath, 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
|
|
3838
|
+
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 };
|
|
3757
3839
|
//# sourceMappingURL=index.mjs.map
|
|
3758
3840
|
//# sourceMappingURL=index.mjs.map
|