@prometheus-ags/entity-graph-core 3.0.5 → 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/CHANGELOG.md +2 -0
- package/README.md +248 -0
- package/dist/chunk-BE3BMRKF.mjs +938 -0
- package/dist/chunk-BE3BMRKF.mjs.map +1 -0
- package/dist/devtools.cjs +2561 -0
- package/dist/devtools.cjs.map +1 -0
- package/dist/devtools.d.cts +531 -0
- package/dist/devtools.d.ts +531 -0
- package/dist/devtools.mjs +2019 -0
- package/dist/devtools.mjs.map +1 -0
- package/dist/graph-ClW3HD8B.d.cts +990 -0
- package/dist/graph-ClW3HD8B.d.ts +990 -0
- package/dist/index.cjs +352 -322
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +60 -1059
- package/dist/index.d.ts +60 -1059
- package/dist/index.mjs +45 -917
- package/dist/index.mjs.map +1 -1
- package/fixtures/devtools/README.md +37 -0
- package/fixtures/devtools/entity-inspection-v1.json +304 -0
- package/fixtures/devtools/time-travel-v1.json +110 -0
- package/package.json +25 -1
package/dist/index.mjs
CHANGED
|
@@ -1,13 +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
|
-
|
|
5
|
-
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
6
|
-
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
7
|
-
}) : x)(function(x) {
|
|
8
|
-
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
9
|
-
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
10
|
-
});
|
|
11
4
|
|
|
12
5
|
// src/errors.ts
|
|
13
6
|
var TerminalError = class _TerminalError extends Error {
|
|
@@ -494,506 +487,6 @@ function mapPostgrestError(error, status) {
|
|
|
494
487
|
return toEntityError(error);
|
|
495
488
|
}
|
|
496
489
|
|
|
497
|
-
// src/merge/lww.ts
|
|
498
|
-
var lwwStrategy = (prev, next) => {
|
|
499
|
-
return prev ? { ...prev, ...next } : { ...next };
|
|
500
|
-
};
|
|
501
|
-
|
|
502
|
-
// src/merge/registry.ts
|
|
503
|
-
var byType = /* @__PURE__ */ new Map();
|
|
504
|
-
var globalDefault = null;
|
|
505
|
-
function registerMergeStrategy(type, strategy) {
|
|
506
|
-
byType.set(type, strategy);
|
|
507
|
-
}
|
|
508
|
-
function setDefaultMergeStrategy(strategy) {
|
|
509
|
-
globalDefault = strategy;
|
|
510
|
-
}
|
|
511
|
-
function getMergeStrategy(type) {
|
|
512
|
-
return byType.get(type) ?? globalDefault ?? lwwStrategy;
|
|
513
|
-
}
|
|
514
|
-
function hasMergeStrategy(type) {
|
|
515
|
-
return byType.has(type) || globalDefault !== null;
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
// src/view/evaluator.ts
|
|
519
|
-
function matchesFilter(entity, filter) {
|
|
520
|
-
if (Array.isArray(filter)) return filter.every((clause) => matchesClause(entity, clause));
|
|
521
|
-
return matchesGroup(entity, filter);
|
|
522
|
-
}
|
|
523
|
-
function matchesGroup(entity, group) {
|
|
524
|
-
const { logic, clauses } = group;
|
|
525
|
-
if (logic === "and") return clauses.every((c) => "logic" in c ? matchesGroup(entity, c) : matchesClause(entity, c));
|
|
526
|
-
return clauses.some((c) => "logic" in c ? matchesGroup(entity, c) : matchesClause(entity, c));
|
|
527
|
-
}
|
|
528
|
-
function matchesClause(entity, clause) {
|
|
529
|
-
const { field, op, value, predicate } = clause;
|
|
530
|
-
const fv = getNestedValue(entity, field);
|
|
531
|
-
switch (op) {
|
|
532
|
-
case "eq":
|
|
533
|
-
return fv === value;
|
|
534
|
-
case "neq":
|
|
535
|
-
return fv !== value;
|
|
536
|
-
case "gt":
|
|
537
|
-
return fv > value;
|
|
538
|
-
case "gte":
|
|
539
|
-
return fv >= value;
|
|
540
|
-
case "lt":
|
|
541
|
-
return fv < value;
|
|
542
|
-
case "lte":
|
|
543
|
-
return fv <= value;
|
|
544
|
-
case "in":
|
|
545
|
-
return Array.isArray(value) && value.includes(fv);
|
|
546
|
-
case "nin":
|
|
547
|
-
return Array.isArray(value) && !value.includes(fv);
|
|
548
|
-
case "isNull":
|
|
549
|
-
return fv == null;
|
|
550
|
-
case "isNotNull":
|
|
551
|
-
return fv != null;
|
|
552
|
-
case "contains":
|
|
553
|
-
return typeof fv === "string" && typeof value === "string" && fv.toLowerCase().includes(value.toLowerCase());
|
|
554
|
-
case "startsWith":
|
|
555
|
-
return typeof fv === "string" && typeof value === "string" && fv.toLowerCase().startsWith(value.toLowerCase());
|
|
556
|
-
case "endsWith":
|
|
557
|
-
return typeof fv === "string" && typeof value === "string" && fv.toLowerCase().endsWith(value.toLowerCase());
|
|
558
|
-
case "between": {
|
|
559
|
-
const [lo, hi] = value;
|
|
560
|
-
return fv >= lo && fv <= hi;
|
|
561
|
-
}
|
|
562
|
-
case "arrayContains":
|
|
563
|
-
return Array.isArray(fv) && fv.includes(value);
|
|
564
|
-
case "arrayOverlaps":
|
|
565
|
-
return Array.isArray(fv) && Array.isArray(value) && value.some((v) => fv.includes(v));
|
|
566
|
-
case "matches":
|
|
567
|
-
return typeof fv === "string" && new RegExp(value).test(fv);
|
|
568
|
-
case "custom":
|
|
569
|
-
return predicate ? predicate(fv, entity) : true;
|
|
570
|
-
default:
|
|
571
|
-
return true;
|
|
572
|
-
}
|
|
573
|
-
}
|
|
574
|
-
function matchesSearch(entity, query, fields) {
|
|
575
|
-
if (!query.trim()) return true;
|
|
576
|
-
const lq = query.toLowerCase();
|
|
577
|
-
return fields.some((field) => {
|
|
578
|
-
const v = getNestedValue(entity, field);
|
|
579
|
-
return typeof v === "string" && v.toLowerCase().includes(lq);
|
|
580
|
-
});
|
|
581
|
-
}
|
|
582
|
-
function compareEntities(a, b, sort) {
|
|
583
|
-
for (const clause of sort) {
|
|
584
|
-
const r = compareByClause(a, b, clause);
|
|
585
|
-
if (r !== 0) return r;
|
|
586
|
-
}
|
|
587
|
-
return 0;
|
|
588
|
-
}
|
|
589
|
-
function compareByClause(a, b, clause) {
|
|
590
|
-
const { field, direction, nulls = "last", comparator } = clause;
|
|
591
|
-
const av = getNestedValue(a, field);
|
|
592
|
-
const bv = getNestedValue(b, field);
|
|
593
|
-
const aNull = av == null;
|
|
594
|
-
const bNull = bv == null;
|
|
595
|
-
if (aNull && bNull) return 0;
|
|
596
|
-
if (aNull) return nulls === "first" ? -1 : 1;
|
|
597
|
-
if (bNull) return nulls === "first" ? 1 : -1;
|
|
598
|
-
let cmp;
|
|
599
|
-
if (comparator) cmp = comparator(av, bv);
|
|
600
|
-
else if (typeof av === "string" && typeof bv === "string") cmp = av.localeCompare(bv, void 0, { sensitivity: "base", numeric: true });
|
|
601
|
-
else if (typeof av === "number" && typeof bv === "number") cmp = av - bv;
|
|
602
|
-
else cmp = String(av).localeCompare(String(bv));
|
|
603
|
-
return direction === "desc" ? -cmp : cmp;
|
|
604
|
-
}
|
|
605
|
-
function findInsertionIndex(entity, sortedIds, getEntity, sort) {
|
|
606
|
-
let lo = 0;
|
|
607
|
-
let hi = sortedIds.length;
|
|
608
|
-
while (lo < hi) {
|
|
609
|
-
const mid = lo + hi >>> 1;
|
|
610
|
-
const me = getEntity(sortedIds[mid]);
|
|
611
|
-
if (!me) {
|
|
612
|
-
lo = mid + 1;
|
|
613
|
-
continue;
|
|
614
|
-
}
|
|
615
|
-
if (compareEntities(entity, me, sort) <= 0) hi = mid;
|
|
616
|
-
else lo = mid + 1;
|
|
617
|
-
}
|
|
618
|
-
return lo;
|
|
619
|
-
}
|
|
620
|
-
function applyView(ids, getEntity, filter, sort, search) {
|
|
621
|
-
let entries = [];
|
|
622
|
-
for (const id of ids) {
|
|
623
|
-
const entity = getEntity(id);
|
|
624
|
-
if (!entity) continue;
|
|
625
|
-
entries.push({ id, entity });
|
|
626
|
-
}
|
|
627
|
-
if (filter && entries.length > 0) entries = entries.filter(({ entity }) => matchesFilter(entity, filter));
|
|
628
|
-
if (search?.query) entries = entries.filter(({ entity }) => matchesSearch(entity, search.query, search.fields));
|
|
629
|
-
if (sort && sort.length > 0) entries.sort((a, b) => compareEntities(a.entity, b.entity, sort));
|
|
630
|
-
return entries.map((e) => e.id);
|
|
631
|
-
}
|
|
632
|
-
function checkCompleteness(loadedCount, total, hasNextPage) {
|
|
633
|
-
if (!hasNextPage && total !== null && loadedCount >= total) return { isComplete: true, reason: "all-loaded" };
|
|
634
|
-
if (hasNextPage) return { isComplete: false, reason: "has-more-pages" };
|
|
635
|
-
return { isComplete: true, reason: "no-more-pages" };
|
|
636
|
-
}
|
|
637
|
-
function getNestedValue(obj, path) {
|
|
638
|
-
const parts = path.replace(/\[(\d+)\]/g, ".$1").split(".");
|
|
639
|
-
let current = obj;
|
|
640
|
-
for (const part of parts) {
|
|
641
|
-
if (current == null || typeof current !== "object") return void 0;
|
|
642
|
-
current = current[part];
|
|
643
|
-
}
|
|
644
|
-
return current;
|
|
645
|
-
}
|
|
646
|
-
|
|
647
|
-
// src/graph.ts
|
|
648
|
-
var EMPTY_IDS = [];
|
|
649
|
-
var EMPTY_ENTITY_STATE = {
|
|
650
|
-
isFetching: false,
|
|
651
|
-
lastFetched: null,
|
|
652
|
-
error: null,
|
|
653
|
-
stale: false
|
|
654
|
-
};
|
|
655
|
-
var EMPTY_SYNC_METADATA = {
|
|
656
|
-
synced: true,
|
|
657
|
-
origin: "server",
|
|
658
|
-
updatedAt: null
|
|
659
|
-
};
|
|
660
|
-
var EMPTY_LIST_STATE = {
|
|
661
|
-
ids: EMPTY_IDS,
|
|
662
|
-
total: null,
|
|
663
|
-
nextCursor: null,
|
|
664
|
-
prevCursor: null,
|
|
665
|
-
hasNextPage: false,
|
|
666
|
-
hasPrevPage: false,
|
|
667
|
-
isFetching: false,
|
|
668
|
-
isFetchingMore: false,
|
|
669
|
-
error: null,
|
|
670
|
-
lastError: null,
|
|
671
|
-
lastFetched: null,
|
|
672
|
-
stale: false,
|
|
673
|
-
currentPage: null,
|
|
674
|
-
pageSize: null
|
|
675
|
-
};
|
|
676
|
-
function defaultEntityState() {
|
|
677
|
-
return { ...EMPTY_ENTITY_STATE };
|
|
678
|
-
}
|
|
679
|
-
function defaultSyncMetadata() {
|
|
680
|
-
return { ...EMPTY_SYNC_METADATA };
|
|
681
|
-
}
|
|
682
|
-
function defaultListState() {
|
|
683
|
-
return { ...EMPTY_LIST_STATE, ids: [] };
|
|
684
|
-
}
|
|
685
|
-
function ek(type, id) {
|
|
686
|
-
return `${type}:${id}`;
|
|
687
|
-
}
|
|
688
|
-
var snapshotCache = /* @__PURE__ */ new Map();
|
|
689
|
-
function readCachedEntitySnapshot(type, id, base, patch, metadata) {
|
|
690
|
-
const key = ek(type, id);
|
|
691
|
-
const cached = snapshotCache.get(key);
|
|
692
|
-
if (cached && cached.base === base && cached.patch === patch && cached.metadata === metadata) {
|
|
693
|
-
return cached.snapshot;
|
|
694
|
-
}
|
|
695
|
-
const snapshot = {
|
|
696
|
-
...patch ? { ...base, ...patch } : base,
|
|
697
|
-
$synced: metadata.synced,
|
|
698
|
-
$origin: metadata.origin,
|
|
699
|
-
$updatedAt: metadata.updatedAt
|
|
700
|
-
};
|
|
701
|
-
snapshotCache.set(key, { base, patch, metadata, snapshot });
|
|
702
|
-
return snapshot;
|
|
703
|
-
}
|
|
704
|
-
function createGraphStore() {
|
|
705
|
-
return createStore()(
|
|
706
|
-
subscribeWithSelector(
|
|
707
|
-
immer((set, get) => ({
|
|
708
|
-
entities: {},
|
|
709
|
-
patches: {},
|
|
710
|
-
entityStates: {},
|
|
711
|
-
syncMetadata: {},
|
|
712
|
-
lists: {},
|
|
713
|
-
upsertEntity: (type, id, data) => set((s) => {
|
|
714
|
-
if (!s.entities[type]) s.entities[type] = {};
|
|
715
|
-
const key = ek(type, id);
|
|
716
|
-
const strategy = getMergeStrategy(type);
|
|
717
|
-
const ctx = { type, id, origin: s.syncMetadata[key]?.origin ?? "server", updatedAt: Date.now() };
|
|
718
|
-
s.entities[type][id] = strategy(s.entities[type][id], data, ctx);
|
|
719
|
-
if (!s.syncMetadata[key]) s.syncMetadata[key] = defaultSyncMetadata();
|
|
720
|
-
}),
|
|
721
|
-
upsertEntities: (type, entries) => set((s) => {
|
|
722
|
-
if (!s.entities[type]) s.entities[type] = {};
|
|
723
|
-
const strategy = getMergeStrategy(type);
|
|
724
|
-
for (const { id, data } of entries) {
|
|
725
|
-
const key = ek(type, id);
|
|
726
|
-
const ctx = { type, id, origin: s.syncMetadata[key]?.origin ?? "server", updatedAt: Date.now() };
|
|
727
|
-
s.entities[type][id] = strategy(s.entities[type][id], data, ctx);
|
|
728
|
-
if (!s.syncMetadata[key]) s.syncMetadata[key] = defaultSyncMetadata();
|
|
729
|
-
}
|
|
730
|
-
}),
|
|
731
|
-
ingestFetchedList: (type, entries, options = {}) => set((s) => {
|
|
732
|
-
const fetchedAt = Date.now();
|
|
733
|
-
const ids = entries.map(({ id }) => id);
|
|
734
|
-
const batches = [
|
|
735
|
-
{ type, entries },
|
|
736
|
-
...options.sideBatches ?? []
|
|
737
|
-
];
|
|
738
|
-
const origins = /* @__PURE__ */ new Map();
|
|
739
|
-
for (const batch of batches) {
|
|
740
|
-
if (!s.entities[batch.type]) s.entities[batch.type] = {};
|
|
741
|
-
for (const { id } of batch.entries) {
|
|
742
|
-
const key = ek(batch.type, id);
|
|
743
|
-
if (!origins.has(key)) {
|
|
744
|
-
origins.set(key, s.syncMetadata[key]?.origin ?? "server");
|
|
745
|
-
}
|
|
746
|
-
}
|
|
747
|
-
}
|
|
748
|
-
for (const batch of batches) {
|
|
749
|
-
const strategy = getMergeStrategy(batch.type);
|
|
750
|
-
for (const { id, data } of batch.entries) {
|
|
751
|
-
const key = ek(batch.type, id);
|
|
752
|
-
const ctx = {
|
|
753
|
-
type: batch.type,
|
|
754
|
-
id,
|
|
755
|
-
origin: origins.get(key) ?? "server",
|
|
756
|
-
updatedAt: fetchedAt
|
|
757
|
-
};
|
|
758
|
-
s.entities[batch.type][id] = strategy(s.entities[batch.type][id], data, ctx);
|
|
759
|
-
}
|
|
760
|
-
}
|
|
761
|
-
const completed = /* @__PURE__ */ new Set();
|
|
762
|
-
for (const batch of batches) {
|
|
763
|
-
for (const { id } of batch.entries) {
|
|
764
|
-
const key = ek(batch.type, id);
|
|
765
|
-
if (completed.has(key)) continue;
|
|
766
|
-
completed.add(key);
|
|
767
|
-
if (!s.entityStates[key]) s.entityStates[key] = defaultEntityState();
|
|
768
|
-
s.entityStates[key].lastFetched = fetchedAt;
|
|
769
|
-
s.entityStates[key].isFetching = false;
|
|
770
|
-
s.entityStates[key].error = null;
|
|
771
|
-
s.entityStates[key].stale = false;
|
|
772
|
-
s.syncMetadata[key] = {
|
|
773
|
-
...s.syncMetadata[key] ?? defaultSyncMetadata(),
|
|
774
|
-
synced: true,
|
|
775
|
-
origin: "server",
|
|
776
|
-
updatedAt: fetchedAt
|
|
777
|
-
};
|
|
778
|
-
}
|
|
779
|
-
}
|
|
780
|
-
const readDraftEntity = (id) => {
|
|
781
|
-
const base = s.entities[type]?.[id];
|
|
782
|
-
if (!base) return null;
|
|
783
|
-
const patch = s.patches[type]?.[id];
|
|
784
|
-
const metadata = s.syncMetadata[ek(type, id)] ?? defaultSyncMetadata();
|
|
785
|
-
return {
|
|
786
|
-
...base,
|
|
787
|
-
...patch ?? {},
|
|
788
|
-
$synced: metadata.synced,
|
|
789
|
-
$origin: metadata.origin,
|
|
790
|
-
$updatedAt: metadata.updatedAt
|
|
791
|
-
};
|
|
792
|
-
};
|
|
793
|
-
for (const target of options.lists ?? []) {
|
|
794
|
-
const existing = s.lists[target.key] ?? defaultListState();
|
|
795
|
-
const targetIds = target.ids ?? ids;
|
|
796
|
-
const nextIds = target.mode === "append" ? Array.from(/* @__PURE__ */ new Set([...existing.ids, ...targetIds])) : targetIds;
|
|
797
|
-
s.lists[target.key] = {
|
|
798
|
-
...existing,
|
|
799
|
-
...target.meta ?? {},
|
|
800
|
-
ids: nextIds,
|
|
801
|
-
isFetching: false,
|
|
802
|
-
isFetchingMore: false,
|
|
803
|
-
error: null,
|
|
804
|
-
lastError: null,
|
|
805
|
-
stale: false,
|
|
806
|
-
lastFetched: fetchedAt
|
|
807
|
-
};
|
|
808
|
-
}
|
|
809
|
-
for (const projection of options.projections ?? []) {
|
|
810
|
-
const existing = s.lists[projection.key] ?? defaultListState();
|
|
811
|
-
const nextIds = [...existing.ids];
|
|
812
|
-
const { filter, sort, search } = projection.view;
|
|
813
|
-
for (const id of new Set(ids)) {
|
|
814
|
-
if (nextIds.includes(id)) continue;
|
|
815
|
-
const entity = readDraftEntity(id);
|
|
816
|
-
if (!entity) continue;
|
|
817
|
-
const matches = (!filter || matchesFilter(entity, filter)) && (!search?.query || matchesSearch(entity, search.query, search.fields));
|
|
818
|
-
if (!matches) continue;
|
|
819
|
-
if (sort && sort.length > 0) {
|
|
820
|
-
const index = findInsertionIndex(entity, nextIds, readDraftEntity, sort);
|
|
821
|
-
nextIds.splice(index, 0, id);
|
|
822
|
-
} else {
|
|
823
|
-
nextIds.unshift(id);
|
|
824
|
-
}
|
|
825
|
-
}
|
|
826
|
-
s.lists[projection.key] = projection.completeFetch ? {
|
|
827
|
-
...existing,
|
|
828
|
-
ids: nextIds,
|
|
829
|
-
isFetching: false,
|
|
830
|
-
isFetchingMore: false,
|
|
831
|
-
error: null,
|
|
832
|
-
lastError: null,
|
|
833
|
-
stale: false,
|
|
834
|
-
lastFetched: fetchedAt
|
|
835
|
-
} : { ...existing, ids: nextIds };
|
|
836
|
-
}
|
|
837
|
-
for (const key of options.finishListFetches ?? []) {
|
|
838
|
-
if (!s.lists[key]) s.lists[key] = defaultListState();
|
|
839
|
-
s.lists[key].isFetching = false;
|
|
840
|
-
}
|
|
841
|
-
}),
|
|
842
|
-
replaceEntity: (type, id, data) => set((s) => {
|
|
843
|
-
if (!s.entities[type]) s.entities[type] = {};
|
|
844
|
-
s.entities[type][id] = data;
|
|
845
|
-
const key = ek(type, id);
|
|
846
|
-
if (!s.syncMetadata[key]) s.syncMetadata[key] = defaultSyncMetadata();
|
|
847
|
-
}),
|
|
848
|
-
removeEntity: (type, id) => set((s) => {
|
|
849
|
-
delete s.entities[type]?.[id];
|
|
850
|
-
delete s.patches[type]?.[id];
|
|
851
|
-
delete s.entityStates[ek(type, id)];
|
|
852
|
-
delete s.syncMetadata[ek(type, id)];
|
|
853
|
-
}),
|
|
854
|
-
patchEntity: (type, id, patch) => set((s) => {
|
|
855
|
-
if (!s.patches[type]) s.patches[type] = {};
|
|
856
|
-
s.patches[type][id] = { ...s.patches[type][id] ?? {}, ...patch };
|
|
857
|
-
}),
|
|
858
|
-
unpatchEntity: (type, id, keys) => set((s) => {
|
|
859
|
-
const p = s.patches[type]?.[id];
|
|
860
|
-
if (!p) return;
|
|
861
|
-
for (const k of keys) delete p[k];
|
|
862
|
-
}),
|
|
863
|
-
clearPatch: (type, id) => set((s) => {
|
|
864
|
-
delete s.patches[type]?.[id];
|
|
865
|
-
}),
|
|
866
|
-
setEntityFetching: (type, id, fetching) => set((s) => {
|
|
867
|
-
const k = ek(type, id);
|
|
868
|
-
if (!s.entityStates[k]) s.entityStates[k] = defaultEntityState();
|
|
869
|
-
s.entityStates[k].isFetching = fetching;
|
|
870
|
-
}),
|
|
871
|
-
setEntityError: (type, id, error) => set((s) => {
|
|
872
|
-
const k = ek(type, id);
|
|
873
|
-
if (!s.entityStates[k]) s.entityStates[k] = defaultEntityState();
|
|
874
|
-
s.entityStates[k].error = error;
|
|
875
|
-
s.entityStates[k].isFetching = false;
|
|
876
|
-
}),
|
|
877
|
-
setEntityFetched: (type, id) => set((s) => {
|
|
878
|
-
const k = ek(type, id);
|
|
879
|
-
if (!s.entityStates[k]) s.entityStates[k] = defaultEntityState();
|
|
880
|
-
s.entityStates[k].lastFetched = Date.now();
|
|
881
|
-
s.entityStates[k].isFetching = false;
|
|
882
|
-
s.entityStates[k].error = null;
|
|
883
|
-
s.entityStates[k].stale = false;
|
|
884
|
-
s.syncMetadata[k] = { ...s.syncMetadata[k] ?? defaultSyncMetadata(), synced: true, origin: "server", updatedAt: Date.now() };
|
|
885
|
-
}),
|
|
886
|
-
setEntityStale: (type, id, stale) => set((s) => {
|
|
887
|
-
const k = ek(type, id);
|
|
888
|
-
if (!s.entityStates[k]) s.entityStates[k] = defaultEntityState();
|
|
889
|
-
s.entityStates[k].stale = stale;
|
|
890
|
-
}),
|
|
891
|
-
setEntitySyncMetadata: (type, id, metadata) => set((s) => {
|
|
892
|
-
const k = ek(type, id);
|
|
893
|
-
s.syncMetadata[k] = { ...s.syncMetadata[k] ?? defaultSyncMetadata(), ...metadata };
|
|
894
|
-
}),
|
|
895
|
-
clearEntitySyncMetadata: (type, id) => set((s) => {
|
|
896
|
-
delete s.syncMetadata[ek(type, id)];
|
|
897
|
-
}),
|
|
898
|
-
setListResult: (key, ids, meta) => set((s) => {
|
|
899
|
-
const ex = s.lists[key] ?? defaultListState();
|
|
900
|
-
s.lists[key] = { ...ex, ...meta, ids, isFetching: false, isFetchingMore: false, error: null, lastError: null, stale: false, lastFetched: Date.now() };
|
|
901
|
-
}),
|
|
902
|
-
appendListResult: (key, ids, meta) => set((s) => {
|
|
903
|
-
const ex = s.lists[key] ?? defaultListState();
|
|
904
|
-
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() };
|
|
905
|
-
}),
|
|
906
|
-
prependListResult: (key, ids, meta) => set((s) => {
|
|
907
|
-
const ex = s.lists[key] ?? defaultListState();
|
|
908
|
-
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() };
|
|
909
|
-
}),
|
|
910
|
-
removeIdFromAllLists: (_type, id) => set((s) => {
|
|
911
|
-
for (const key of Object.keys(s.lists)) {
|
|
912
|
-
const list = s.lists[key];
|
|
913
|
-
const idx = list.ids.indexOf(id);
|
|
914
|
-
if (idx !== -1) {
|
|
915
|
-
list.ids.splice(idx, 1);
|
|
916
|
-
if (list.total !== null) list.total -= 1;
|
|
917
|
-
}
|
|
918
|
-
}
|
|
919
|
-
}),
|
|
920
|
-
insertIdInList: (key, id, position) => set((s) => {
|
|
921
|
-
if (!s.lists[key]) s.lists[key] = defaultListState();
|
|
922
|
-
const ids = s.lists[key].ids;
|
|
923
|
-
const ex = ids.indexOf(id);
|
|
924
|
-
if (ex !== -1) ids.splice(ex, 1);
|
|
925
|
-
if (position === "start") ids.unshift(id);
|
|
926
|
-
else if (position === "end") ids.push(id);
|
|
927
|
-
else ids.splice(position, 0, id);
|
|
928
|
-
}),
|
|
929
|
-
setListFetching: (key, fetching) => set((s) => {
|
|
930
|
-
if (!s.lists[key]) s.lists[key] = defaultListState();
|
|
931
|
-
s.lists[key].isFetching = fetching;
|
|
932
|
-
}),
|
|
933
|
-
setListFetchingMore: (key, fetchingMore) => set((s) => {
|
|
934
|
-
if (!s.lists[key]) s.lists[key] = defaultListState();
|
|
935
|
-
s.lists[key].isFetchingMore = fetchingMore;
|
|
936
|
-
}),
|
|
937
|
-
setListError: (key, error, typed) => set((s) => {
|
|
938
|
-
if (!s.lists[key]) s.lists[key] = defaultListState();
|
|
939
|
-
s.lists[key].error = error;
|
|
940
|
-
s.lists[key].lastError = typed ?? (error === null ? null : s.lists[key].lastError);
|
|
941
|
-
s.lists[key].isFetching = false;
|
|
942
|
-
s.lists[key].isFetchingMore = false;
|
|
943
|
-
s.lists[key].lastFetched = Date.now();
|
|
944
|
-
s.lists[key].stale = false;
|
|
945
|
-
}),
|
|
946
|
-
setListStale: (key, stale) => set((s) => {
|
|
947
|
-
if (!s.lists[key]) s.lists[key] = defaultListState();
|
|
948
|
-
s.lists[key].stale = stale;
|
|
949
|
-
}),
|
|
950
|
-
invalidateEntity: (type, id) => set((s) => {
|
|
951
|
-
if (id) {
|
|
952
|
-
const k = ek(type, id);
|
|
953
|
-
if (s.entityStates[k]) s.entityStates[k].stale = true;
|
|
954
|
-
} else {
|
|
955
|
-
for (const k of Object.keys(s.entityStates)) if (k.startsWith(`${type}:`)) s.entityStates[k].stale = true;
|
|
956
|
-
}
|
|
957
|
-
}),
|
|
958
|
-
invalidateLists: (matcher) => set((s) => {
|
|
959
|
-
const pred = typeof matcher === "string" ? (k) => k.startsWith(matcher) : matcher;
|
|
960
|
-
for (const key of Object.keys(s.lists)) if (pred(key)) s.lists[key].stale = true;
|
|
961
|
-
}),
|
|
962
|
-
invalidateType: (type) => {
|
|
963
|
-
get().invalidateEntity(type);
|
|
964
|
-
get().invalidateLists(type);
|
|
965
|
-
},
|
|
966
|
-
readEntity: (type, id) => {
|
|
967
|
-
const s = get();
|
|
968
|
-
const base = s.entities[type]?.[id];
|
|
969
|
-
if (!base) return null;
|
|
970
|
-
const patch = s.patches[type]?.[id];
|
|
971
|
-
return patch ? { ...base, ...patch } : base;
|
|
972
|
-
},
|
|
973
|
-
readEntitySnapshot: (type, id) => {
|
|
974
|
-
const s = get();
|
|
975
|
-
const base = s.entities[type]?.[id];
|
|
976
|
-
if (!base) {
|
|
977
|
-
snapshotCache.delete(ek(type, id));
|
|
978
|
-
return null;
|
|
979
|
-
}
|
|
980
|
-
const patch = s.patches[type]?.[id];
|
|
981
|
-
const metadata = s.syncMetadata[ek(type, id)] ?? EMPTY_SYNC_METADATA;
|
|
982
|
-
return readCachedEntitySnapshot(
|
|
983
|
-
type,
|
|
984
|
-
id,
|
|
985
|
-
base,
|
|
986
|
-
patch,
|
|
987
|
-
metadata
|
|
988
|
-
);
|
|
989
|
-
}
|
|
990
|
-
}))
|
|
991
|
-
)
|
|
992
|
-
);
|
|
993
|
-
}
|
|
994
|
-
var graphStore = createGraphStore();
|
|
995
|
-
var useGraphStore = graphStore;
|
|
996
|
-
|
|
997
490
|
// src/object-path.ts
|
|
998
491
|
function isObject(value) {
|
|
999
492
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -1037,8 +530,9 @@ function collectDirtyPaths(current, original, prefix = "", acc = /* @__PURE__ */
|
|
|
1037
530
|
|
|
1038
531
|
// src/merge/loro.ts
|
|
1039
532
|
var ROOT_MAP = "entity";
|
|
533
|
+
var LORO_MODULE_SPECIFIER = "loro-crdt";
|
|
1040
534
|
var docs = /* @__PURE__ */ new Map();
|
|
1041
|
-
function
|
|
535
|
+
function ek(type, id) {
|
|
1042
536
|
return `${type}:${id}`;
|
|
1043
537
|
}
|
|
1044
538
|
async function createLoroMergeStrategy(loadLoro) {
|
|
@@ -1046,7 +540,7 @@ async function createLoroMergeStrategy(loadLoro) {
|
|
|
1046
540
|
try {
|
|
1047
541
|
const loaded = loadLoro ? await loadLoro() : await import(
|
|
1048
542
|
/* @vite-ignore */
|
|
1049
|
-
|
|
543
|
+
LORO_MODULE_SPECIFIER
|
|
1050
544
|
);
|
|
1051
545
|
if (typeof loaded.LoroDoc !== "function") {
|
|
1052
546
|
throw new TypeError("loro-crdt did not export a LoroDoc constructor");
|
|
@@ -1060,7 +554,7 @@ async function createLoroMergeStrategy(loadLoro) {
|
|
|
1060
554
|
}
|
|
1061
555
|
const { LoroDoc } = mod;
|
|
1062
556
|
return (prev, next, ctx) => {
|
|
1063
|
-
const key =
|
|
557
|
+
const key = ek(ctx.type, ctx.id);
|
|
1064
558
|
let doc = docs.get(key);
|
|
1065
559
|
if (!doc) {
|
|
1066
560
|
doc = new LoroDoc();
|
|
@@ -1148,272 +642,6 @@ function applySelection(row, select) {
|
|
|
1148
642
|
return picked;
|
|
1149
643
|
}
|
|
1150
644
|
|
|
1151
|
-
// src/engine.ts
|
|
1152
|
-
function serializeKey(key) {
|
|
1153
|
-
return JSON.stringify(key, (_, v) => v && typeof v === "object" && !Array.isArray(v) ? Object.fromEntries(Object.entries(v).sort()) : v);
|
|
1154
|
-
}
|
|
1155
|
-
function sleep(ms) {
|
|
1156
|
-
return new Promise((r) => setTimeout(r, ms));
|
|
1157
|
-
}
|
|
1158
|
-
var inflightByStore = /* @__PURE__ */ new WeakMap();
|
|
1159
|
-
function inflightFor(store) {
|
|
1160
|
-
let inflight = inflightByStore.get(store);
|
|
1161
|
-
if (!inflight) {
|
|
1162
|
-
inflight = /* @__PURE__ */ new Map();
|
|
1163
|
-
inflightByStore.set(store, inflight);
|
|
1164
|
-
}
|
|
1165
|
-
return inflight;
|
|
1166
|
-
}
|
|
1167
|
-
function dedupe(key, fn, store = graphStore) {
|
|
1168
|
-
const inflight = inflightFor(store);
|
|
1169
|
-
if (inflight.has(key)) return inflight.get(key);
|
|
1170
|
-
const p = fn().finally(() => inflight.delete(key));
|
|
1171
|
-
inflight.set(key, p);
|
|
1172
|
-
return p;
|
|
1173
|
-
}
|
|
1174
|
-
var subscriberStatsListeners = /* @__PURE__ */ new Set();
|
|
1175
|
-
function emitSubscriberStatsChange() {
|
|
1176
|
-
for (const cb of subscriberStatsListeners) cb();
|
|
1177
|
-
}
|
|
1178
|
-
var subscribersByStore = /* @__PURE__ */ new WeakMap();
|
|
1179
|
-
function subscribersFor(store) {
|
|
1180
|
-
let subscribers = subscribersByStore.get(store);
|
|
1181
|
-
if (!subscribers) {
|
|
1182
|
-
subscribers = /* @__PURE__ */ new Map();
|
|
1183
|
-
subscribersByStore.set(store, subscribers);
|
|
1184
|
-
}
|
|
1185
|
-
return subscribers;
|
|
1186
|
-
}
|
|
1187
|
-
function registerSubscriber(key, store = graphStore) {
|
|
1188
|
-
const subscribers = subscribersFor(store);
|
|
1189
|
-
const token = Symbol(key);
|
|
1190
|
-
if (!subscribers.has(key)) subscribers.set(key, /* @__PURE__ */ new Set());
|
|
1191
|
-
subscribers.get(key).add(token);
|
|
1192
|
-
emitSubscriberStatsChange();
|
|
1193
|
-
return token;
|
|
1194
|
-
}
|
|
1195
|
-
function unregisterSubscriber(key, token, store = graphStore) {
|
|
1196
|
-
const subscribers = subscribersFor(store);
|
|
1197
|
-
const set = subscribers.get(key);
|
|
1198
|
-
if (!set) return;
|
|
1199
|
-
set.delete(token);
|
|
1200
|
-
if (set.size === 0) subscribers.delete(key);
|
|
1201
|
-
emitSubscriberStatsChange();
|
|
1202
|
-
}
|
|
1203
|
-
function hasSubscribers(key, store = graphStore) {
|
|
1204
|
-
return (subscribersFor(store).get(key)?.size ?? 0) > 0;
|
|
1205
|
-
}
|
|
1206
|
-
var DEFAULT_OPTIONS = {
|
|
1207
|
-
defaultStaleTime: 3e4,
|
|
1208
|
-
defaultGcTime: 3e5,
|
|
1209
|
-
gcInterval: 6e4,
|
|
1210
|
-
maxRetries: 3,
|
|
1211
|
-
retryBaseDelay: 1e3,
|
|
1212
|
-
revalidateOnFocus: true,
|
|
1213
|
-
revalidateOnReconnect: true
|
|
1214
|
-
};
|
|
1215
|
-
var engineOptions = { ...DEFAULT_OPTIONS };
|
|
1216
|
-
function subscribeSubscriberStats(onChange) {
|
|
1217
|
-
subscriberStatsListeners.add(onChange);
|
|
1218
|
-
return () => subscriberStatsListeners.delete(onChange);
|
|
1219
|
-
}
|
|
1220
|
-
function getActiveSubscriberCount(store = graphStore) {
|
|
1221
|
-
let n = 0;
|
|
1222
|
-
for (const set of subscribersFor(store).values()) n += set.size;
|
|
1223
|
-
return n;
|
|
1224
|
-
}
|
|
1225
|
-
var devtoolsSubscribers = /* @__PURE__ */ new Set();
|
|
1226
|
-
function subscribeDevtoolsEvent(cb) {
|
|
1227
|
-
devtoolsSubscribers.add(cb);
|
|
1228
|
-
return () => {
|
|
1229
|
-
devtoolsSubscribers.delete(cb);
|
|
1230
|
-
};
|
|
1231
|
-
}
|
|
1232
|
-
function notifyDevtools(event) {
|
|
1233
|
-
if (devtoolsSubscribers.size === 0) return;
|
|
1234
|
-
const listeners2 = [...devtoolsSubscribers];
|
|
1235
|
-
for (const cb of listeners2) {
|
|
1236
|
-
try {
|
|
1237
|
-
cb(event);
|
|
1238
|
-
} catch (err) {
|
|
1239
|
-
console.warn("[engine] devtools subscriber threw:", err);
|
|
1240
|
-
}
|
|
1241
|
-
}
|
|
1242
|
-
}
|
|
1243
|
-
var gcIntervalsByStore = /* @__PURE__ */ new Map();
|
|
1244
|
-
function runGarbageCollection(storeApi = graphStore) {
|
|
1245
|
-
const store = storeApi.getState();
|
|
1246
|
-
const { defaultGcTime: gcTime } = getEngineOptions();
|
|
1247
|
-
const now = Date.now();
|
|
1248
|
-
const toRemove = [];
|
|
1249
|
-
for (const type of Object.keys(store.entities)) {
|
|
1250
|
-
const bucket = store.entities[type];
|
|
1251
|
-
if (!bucket) continue;
|
|
1252
|
-
for (const id of Object.keys(bucket)) {
|
|
1253
|
-
const key = `${type}:${id}`;
|
|
1254
|
-
if (hasSubscribers(key, storeApi)) continue;
|
|
1255
|
-
const patch = store.patches[type]?.[id];
|
|
1256
|
-
if (patch !== void 0 && Object.keys(patch).length > 0) continue;
|
|
1257
|
-
const entityState = store.entityStates[key];
|
|
1258
|
-
if (entityState?.isFetching) continue;
|
|
1259
|
-
const lastFetched = entityState?.lastFetched;
|
|
1260
|
-
if (lastFetched == null) continue;
|
|
1261
|
-
if (now - lastFetched <= gcTime) continue;
|
|
1262
|
-
toRemove.push({ type, id });
|
|
1263
|
-
}
|
|
1264
|
-
}
|
|
1265
|
-
for (const { type, id } of toRemove) {
|
|
1266
|
-
store.removeEntity(type, id);
|
|
1267
|
-
store.removeIdFromAllLists(type, id);
|
|
1268
|
-
}
|
|
1269
|
-
}
|
|
1270
|
-
function stopGarbageCollector(storeApi = graphStore) {
|
|
1271
|
-
const intervalId = gcIntervalsByStore.get(storeApi);
|
|
1272
|
-
if (intervalId != null && typeof clearInterval !== "undefined") {
|
|
1273
|
-
clearInterval(intervalId);
|
|
1274
|
-
gcIntervalsByStore.delete(storeApi);
|
|
1275
|
-
}
|
|
1276
|
-
}
|
|
1277
|
-
function startGarbageCollector(storeApi = graphStore) {
|
|
1278
|
-
stopGarbageCollector(storeApi);
|
|
1279
|
-
if (typeof window === "undefined" || typeof setInterval === "undefined") return () => {
|
|
1280
|
-
};
|
|
1281
|
-
const intervalId = setInterval(
|
|
1282
|
-
() => runGarbageCollection(storeApi),
|
|
1283
|
-
getEngineOptions().gcInterval
|
|
1284
|
-
);
|
|
1285
|
-
gcIntervalsByStore.set(storeApi, intervalId);
|
|
1286
|
-
return () => stopGarbageCollector(storeApi);
|
|
1287
|
-
}
|
|
1288
|
-
function restartGarbageCollector(storeApi = graphStore) {
|
|
1289
|
-
startGarbageCollector(storeApi);
|
|
1290
|
-
}
|
|
1291
|
-
function configureEngine(opts) {
|
|
1292
|
-
engineOptions = { ...DEFAULT_OPTIONS, ...opts };
|
|
1293
|
-
const activeStores = [...gcIntervalsByStore.keys()];
|
|
1294
|
-
if (activeStores.length === 0) restartGarbageCollector();
|
|
1295
|
-
else for (const storeApi of activeStores) restartGarbageCollector(storeApi);
|
|
1296
|
-
}
|
|
1297
|
-
function getEngineOptions() {
|
|
1298
|
-
return engineOptions;
|
|
1299
|
-
}
|
|
1300
|
-
async function fetchEntity(opts, engineOpts, storeApi = graphStore) {
|
|
1301
|
-
const { type, id, fetch, normalize, sideEffects, idField = "id" } = opts;
|
|
1302
|
-
if (!id) return;
|
|
1303
|
-
storeApi.getState().setEntityFetching(type, id, true);
|
|
1304
|
-
const attempt = async (retries) => {
|
|
1305
|
-
try {
|
|
1306
|
-
const raw = await fetch(id);
|
|
1307
|
-
const normalized = normalize(raw);
|
|
1308
|
-
const resolvedId = normalized[idField] ?? id;
|
|
1309
|
-
storeApi.getState().upsertEntity(type, resolvedId, normalized);
|
|
1310
|
-
storeApi.getState().setEntityFetched(type, resolvedId);
|
|
1311
|
-
if (sideEffects) sideEffects(raw, storeApi);
|
|
1312
|
-
opts.onSuccess?.(normalized);
|
|
1313
|
-
} catch (err) {
|
|
1314
|
-
const error = err instanceof Error ? err : new Error(String(err));
|
|
1315
|
-
if (retries < engineOpts.maxRetries) {
|
|
1316
|
-
await sleep(engineOpts.retryBaseDelay * Math.pow(2, retries));
|
|
1317
|
-
return attempt(retries + 1);
|
|
1318
|
-
}
|
|
1319
|
-
storeApi.getState().setEntityError(type, id, error.message);
|
|
1320
|
-
opts.onError?.(error);
|
|
1321
|
-
}
|
|
1322
|
-
};
|
|
1323
|
-
await dedupe(`${type}:${id}`, () => attempt(0), storeApi);
|
|
1324
|
-
}
|
|
1325
|
-
async function fetchList(opts, params, engineOpts, isLoadMore = false, storeApi = graphStore) {
|
|
1326
|
-
const { type, queryKey, fetch, normalize, sideEffects, mode = "replace" } = opts;
|
|
1327
|
-
const key = serializeKey(queryKey);
|
|
1328
|
-
const store = storeApi.getState();
|
|
1329
|
-
if (!fetch || !normalize) return;
|
|
1330
|
-
if (isLoadMore) store.setListFetchingMore(key, true);
|
|
1331
|
-
else store.setListFetching(key, true);
|
|
1332
|
-
const attempt = async (retries) => {
|
|
1333
|
-
try {
|
|
1334
|
-
const response = await fetch(params);
|
|
1335
|
-
const normalized = response.items.map(normalize);
|
|
1336
|
-
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 };
|
|
1337
|
-
storeApi.getState().ingestFetchedList(
|
|
1338
|
-
type,
|
|
1339
|
-
normalized.map(({ id, data }) => ({ id, data })),
|
|
1340
|
-
{ lists: [{ key, mode: mode === "append" && isLoadMore ? "append" : "replace", meta }] }
|
|
1341
|
-
);
|
|
1342
|
-
if (sideEffects) sideEffects(response.items, storeApi);
|
|
1343
|
-
opts.onSuccess?.(response);
|
|
1344
|
-
} catch (err) {
|
|
1345
|
-
const error = err instanceof Error ? err : new Error(String(err));
|
|
1346
|
-
if (retries < engineOpts.maxRetries) {
|
|
1347
|
-
await sleep(engineOpts.retryBaseDelay * Math.pow(2, retries));
|
|
1348
|
-
return attempt(retries + 1);
|
|
1349
|
-
}
|
|
1350
|
-
storeApi.getState().setListError(key, error.message);
|
|
1351
|
-
opts.onError?.(error);
|
|
1352
|
-
}
|
|
1353
|
-
};
|
|
1354
|
-
await dedupe(isLoadMore ? `${key}:more` : key, () => attempt(0), storeApi);
|
|
1355
|
-
}
|
|
1356
|
-
var globalListenerAttachments = /* @__PURE__ */ new WeakMap();
|
|
1357
|
-
function releaseGlobalListenerAttachment(storeApi, attachment) {
|
|
1358
|
-
let released = false;
|
|
1359
|
-
return () => {
|
|
1360
|
-
if (released) return;
|
|
1361
|
-
released = true;
|
|
1362
|
-
const current = globalListenerAttachments.get(storeApi);
|
|
1363
|
-
if (current !== attachment) return;
|
|
1364
|
-
current.references -= 1;
|
|
1365
|
-
if (current.references > 0) return;
|
|
1366
|
-
current.dispose();
|
|
1367
|
-
globalListenerAttachments.delete(storeApi);
|
|
1368
|
-
};
|
|
1369
|
-
}
|
|
1370
|
-
function attachGlobalListeners(storeApi = graphStore) {
|
|
1371
|
-
if (typeof window === "undefined") return () => {
|
|
1372
|
-
};
|
|
1373
|
-
const existing = globalListenerAttachments.get(storeApi);
|
|
1374
|
-
if (existing) {
|
|
1375
|
-
existing.references += 1;
|
|
1376
|
-
return releaseGlobalListenerAttachment(storeApi, existing);
|
|
1377
|
-
}
|
|
1378
|
-
const stopGc = startGarbageCollector(storeApi);
|
|
1379
|
-
const revalidateAll = () => {
|
|
1380
|
-
const state = storeApi.getState();
|
|
1381
|
-
for (const key of subscribersFor(storeApi).keys()) {
|
|
1382
|
-
if (!hasSubscribers(key, storeApi)) continue;
|
|
1383
|
-
const colonIdx = key.indexOf(":");
|
|
1384
|
-
if (colonIdx === -1) continue;
|
|
1385
|
-
const type = key.slice(0, colonIdx);
|
|
1386
|
-
const id = key.slice(colonIdx + 1);
|
|
1387
|
-
state.setEntityStale(type, id, true);
|
|
1388
|
-
}
|
|
1389
|
-
};
|
|
1390
|
-
const revalidateWhenVisible = () => {
|
|
1391
|
-
if (document.visibilityState === "visible") revalidateAll();
|
|
1392
|
-
};
|
|
1393
|
-
const revalidateOnFocus = engineOptions.revalidateOnFocus;
|
|
1394
|
-
const revalidateOnReconnect = engineOptions.revalidateOnReconnect;
|
|
1395
|
-
if (revalidateOnFocus) {
|
|
1396
|
-
window.addEventListener("visibilitychange", revalidateWhenVisible);
|
|
1397
|
-
window.addEventListener("focus", revalidateAll);
|
|
1398
|
-
}
|
|
1399
|
-
if (revalidateOnReconnect) window.addEventListener("online", revalidateAll);
|
|
1400
|
-
const attachment = {
|
|
1401
|
-
references: 1,
|
|
1402
|
-
dispose: () => {
|
|
1403
|
-
if (revalidateOnFocus) {
|
|
1404
|
-
window.removeEventListener("visibilitychange", revalidateWhenVisible);
|
|
1405
|
-
window.removeEventListener("focus", revalidateAll);
|
|
1406
|
-
}
|
|
1407
|
-
if (revalidateOnReconnect) {
|
|
1408
|
-
window.removeEventListener("online", revalidateAll);
|
|
1409
|
-
}
|
|
1410
|
-
stopGc();
|
|
1411
|
-
}
|
|
1412
|
-
};
|
|
1413
|
-
globalListenerAttachments.set(storeApi, attachment);
|
|
1414
|
-
return releaseGlobalListenerAttachment(storeApi, attachment);
|
|
1415
|
-
}
|
|
1416
|
-
|
|
1417
645
|
// src/graph-actions.ts
|
|
1418
646
|
var graphActionListeners = /* @__PURE__ */ new Set();
|
|
1419
647
|
var graphActionReplayers = /* @__PURE__ */ new Map();
|
|
@@ -1424,7 +652,7 @@ function createGraphTransaction(storeApi = graphStore) {
|
|
|
1424
652
|
upsertEntity(type, id, data) {
|
|
1425
653
|
storeApi.getState().upsertEntity(type, id, data);
|
|
1426
654
|
if (typeof process === "undefined" || process.env.NODE_ENV !== "production") {
|
|
1427
|
-
|
|
655
|
+
emitLegacyDevtoolsEvent({ kind: "upsert", type, id, data, at: (/* @__PURE__ */ new Date()).toISOString() });
|
|
1428
656
|
}
|
|
1429
657
|
return tx;
|
|
1430
658
|
},
|
|
@@ -1439,14 +667,14 @@ function createGraphTransaction(storeApi = graphStore) {
|
|
|
1439
667
|
patchEntity(type, id, patch) {
|
|
1440
668
|
storeApi.getState().patchEntity(type, id, patch);
|
|
1441
669
|
if (typeof process === "undefined" || process.env.NODE_ENV !== "production") {
|
|
1442
|
-
|
|
670
|
+
emitLegacyDevtoolsEvent({ kind: "patch", type, id, patch, at: (/* @__PURE__ */ new Date()).toISOString() });
|
|
1443
671
|
}
|
|
1444
672
|
return tx;
|
|
1445
673
|
},
|
|
1446
674
|
clearPatch(type, id) {
|
|
1447
675
|
storeApi.getState().clearPatch(type, id);
|
|
1448
676
|
if (typeof process === "undefined" || process.env.NODE_ENV !== "production") {
|
|
1449
|
-
|
|
677
|
+
emitLegacyDevtoolsEvent({ kind: "clearPatch", type, id, at: (/* @__PURE__ */ new Date()).toISOString() });
|
|
1450
678
|
}
|
|
1451
679
|
return tx;
|
|
1452
680
|
},
|
|
@@ -1929,7 +1157,7 @@ function computeDelay(policy, attempt) {
|
|
|
1929
1157
|
return base / 2 + Math.random() * (base / 2);
|
|
1930
1158
|
}
|
|
1931
1159
|
}
|
|
1932
|
-
function
|
|
1160
|
+
function sleep(ms) {
|
|
1933
1161
|
if (ms <= 0) return Promise.resolve();
|
|
1934
1162
|
return new Promise((r) => setTimeout(r, ms));
|
|
1935
1163
|
}
|
|
@@ -1942,7 +1170,7 @@ async function replayActionWithRetry(action, policy, storeApi = graphStore) {
|
|
|
1942
1170
|
} catch (error) {
|
|
1943
1171
|
lastError = error;
|
|
1944
1172
|
if (attempt >= policy.maxAttempts) break;
|
|
1945
|
-
await
|
|
1173
|
+
await sleep(computeDelay(policy, attempt));
|
|
1946
1174
|
}
|
|
1947
1175
|
}
|
|
1948
1176
|
try {
|
|
@@ -1976,9 +1204,9 @@ function getDefaultOnlineSource() {
|
|
|
1976
1204
|
|
|
1977
1205
|
// src/devtools-event-bus.ts
|
|
1978
1206
|
var _busInjectMap = /* @__PURE__ */ new WeakMap();
|
|
1979
|
-
var
|
|
1207
|
+
var registries = /* @__PURE__ */ new Map();
|
|
1980
1208
|
function __resetStoreRegistry() {
|
|
1981
|
-
|
|
1209
|
+
registries.clear();
|
|
1982
1210
|
}
|
|
1983
1211
|
function createDevtoolsEventBus(opts) {
|
|
1984
1212
|
const bufferSize = opts?.bufferSize ?? 500;
|
|
@@ -2093,8 +1321,10 @@ function createDevtoolsEventBus(opts) {
|
|
|
2093
1321
|
pendingBurst = [];
|
|
2094
1322
|
dispatchedCount = 0;
|
|
2095
1323
|
flushScheduled = false;
|
|
2096
|
-
|
|
2097
|
-
|
|
1324
|
+
const registry = registries.get(bus);
|
|
1325
|
+
if (registry) {
|
|
1326
|
+
for (const entry of registry.values()) {
|
|
1327
|
+
if (!entry.active) continue;
|
|
2098
1328
|
entry.active = false;
|
|
2099
1329
|
try {
|
|
2100
1330
|
entry.unsubscribeSrc();
|
|
@@ -2102,12 +1332,19 @@ function createDevtoolsEventBus(opts) {
|
|
|
2102
1332
|
}
|
|
2103
1333
|
}
|
|
2104
1334
|
}
|
|
1335
|
+
registries.delete(bus);
|
|
1336
|
+
_busInjectMap.delete(bus);
|
|
2105
1337
|
}
|
|
2106
1338
|
};
|
|
2107
1339
|
_busInjectMap.set(bus, handleEngineEvent);
|
|
2108
1340
|
return bus;
|
|
2109
1341
|
}
|
|
2110
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
|
+
}
|
|
2111
1348
|
if (registry.get(name)?.active === true) {
|
|
2112
1349
|
throw new Error(
|
|
2113
1350
|
`[devtools-store-registry] Store name "${name}" is already registered and active`
|
|
@@ -2128,75 +1365,38 @@ function registerStore(bus, source, name) {
|
|
|
2128
1365
|
unsubscribeSrc();
|
|
2129
1366
|
};
|
|
2130
1367
|
}
|
|
2131
|
-
function getRegisteredStores() {
|
|
2132
|
-
|
|
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 }));
|
|
2133
1371
|
}
|
|
2134
1372
|
|
|
2135
1373
|
// src/devtools-time-travel.ts
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
var listeners = /* @__PURE__ */ new Set();
|
|
2141
|
-
function notify() {
|
|
2142
|
-
for (const l of listeners) l();
|
|
2143
|
-
}
|
|
2144
|
-
function cloneSlice(s) {
|
|
2145
|
-
return {
|
|
2146
|
-
entities: structuredClone(s.entities),
|
|
2147
|
-
patches: structuredClone(s.patches),
|
|
2148
|
-
entityStates: structuredClone(s.entityStates),
|
|
2149
|
-
syncMetadata: structuredClone(s.syncMetadata),
|
|
2150
|
-
lists: structuredClone(s.lists)
|
|
2151
|
-
};
|
|
2152
|
-
}
|
|
2153
|
-
function configureTimeTravel(opts) {
|
|
2154
|
-
if (opts.capacity && opts.capacity > 0) capacity = opts.capacity;
|
|
2155
|
-
if (ring.length > capacity) ring = ring.slice(-capacity);
|
|
2156
|
-
notify();
|
|
1374
|
+
function configureTimeTravel(opts, storeApi = graphStore) {
|
|
1375
|
+
if (opts.capacity && opts.capacity > 0) {
|
|
1376
|
+
getGraphDevtoolsCompatibilityDelegate(storeApi)?.configure(opts.capacity);
|
|
1377
|
+
}
|
|
2157
1378
|
}
|
|
2158
1379
|
function recordGraphSnapshot(label, storeApi = graphStore) {
|
|
2159
|
-
|
|
2160
|
-
const snap = {
|
|
2161
|
-
seq: seqCounter++,
|
|
2162
|
-
at: Date.now(),
|
|
2163
|
-
...label !== void 0 ? { label } : {},
|
|
2164
|
-
data: cloneSlice(s)
|
|
2165
|
-
};
|
|
2166
|
-
ring.push(snap);
|
|
2167
|
-
if (ring.length > capacity) ring = ring.slice(-capacity);
|
|
2168
|
-
cursor = null;
|
|
2169
|
-
notify();
|
|
2170
|
-
return snap.seq;
|
|
1380
|
+
return getGraphDevtoolsCompatibilityDelegate(storeApi)?.capture(label) ?? -1;
|
|
2171
1381
|
}
|
|
2172
1382
|
function restoreGraphSnapshot(index, storeApi = graphStore) {
|
|
2173
|
-
|
|
2174
|
-
if (!snap) return false;
|
|
2175
|
-
const clone2 = cloneSlice(snap.data);
|
|
2176
|
-
storeApi.setState(clone2);
|
|
2177
|
-
cursor = index;
|
|
2178
|
-
notify();
|
|
2179
|
-
return true;
|
|
1383
|
+
return getGraphDevtoolsCompatibilityDelegate(storeApi)?.restoreByIndex(index) ?? false;
|
|
2180
1384
|
}
|
|
2181
1385
|
function restoreGraphSnapshotBySeq(seq, storeApi = graphStore) {
|
|
2182
|
-
|
|
2183
|
-
return index === -1 ? false : restoreGraphSnapshot(index, storeApi);
|
|
1386
|
+
return getGraphDevtoolsCompatibilityDelegate(storeApi)?.restoreByCursor(seq) ?? false;
|
|
2184
1387
|
}
|
|
2185
1388
|
function stepTimeTravel(delta, storeApi = graphStore) {
|
|
2186
|
-
|
|
2187
|
-
const target = Math.max(0, Math.min(ring.length - 1, base + delta));
|
|
2188
|
-
return restoreGraphSnapshot(target, storeApi);
|
|
1389
|
+
return getGraphDevtoolsCompatibilityDelegate(storeApi)?.step(delta) ?? false;
|
|
2189
1390
|
}
|
|
2190
|
-
function getTimeTravelState() {
|
|
2191
|
-
return {
|
|
2192
|
-
snapshots:
|
|
2193
|
-
cursor,
|
|
2194
|
-
capacity
|
|
1391
|
+
function getTimeTravelState(storeApi = graphStore) {
|
|
1392
|
+
return getGraphDevtoolsCompatibilityDelegate(storeApi)?.getState() ?? {
|
|
1393
|
+
snapshots: [],
|
|
1394
|
+
cursor: null,
|
|
1395
|
+
capacity: 50
|
|
2195
1396
|
};
|
|
2196
1397
|
}
|
|
2197
|
-
function subscribeTimeTravel(cb) {
|
|
2198
|
-
|
|
2199
|
-
return () => listeners.delete(cb);
|
|
1398
|
+
function subscribeTimeTravel(cb, storeApi = graphStore) {
|
|
1399
|
+
return subscribeGraphDevtoolsCompatibility(storeApi, cb);
|
|
2200
1400
|
}
|
|
2201
1401
|
|
|
2202
1402
|
// src/view/incremental.ts
|
|
@@ -2291,78 +1491,6 @@ var IncrementalView = class {
|
|
|
2291
1491
|
}
|
|
2292
1492
|
};
|
|
2293
1493
|
|
|
2294
|
-
// src/crud/relations.ts
|
|
2295
|
-
var schemaRegistry2 = /* @__PURE__ */ new Map();
|
|
2296
|
-
function registerSchema(schema) {
|
|
2297
|
-
schemaRegistry2.set(schema.type, schema);
|
|
2298
|
-
}
|
|
2299
|
-
function getSchema(type) {
|
|
2300
|
-
return schemaRegistry2.get(type) ?? null;
|
|
2301
|
-
}
|
|
2302
|
-
function cascadeInvalidation(ctx, storeApi = graphStore) {
|
|
2303
|
-
const schema = schemaRegistry2.get(ctx.type);
|
|
2304
|
-
if (!schema) return;
|
|
2305
|
-
const store = storeApi.getState();
|
|
2306
|
-
if (schema.globalListKeys) for (const key of schema.globalListKeys) store.invalidateLists(key);
|
|
2307
|
-
if (!schema.relations) return;
|
|
2308
|
-
for (const [, relation] of Object.entries(schema.relations)) {
|
|
2309
|
-
switch (relation.cardinality) {
|
|
2310
|
-
case "belongsTo": {
|
|
2311
|
-
const prevFk = ctx.previous?.[relation.foreignKey];
|
|
2312
|
-
const nextFk = ctx.next?.[relation.foreignKey];
|
|
2313
|
-
if (prevFk && prevFk !== nextFk && relation.invalidateTargetLists) for (const kp of relation.invalidateTargetLists) store.invalidateLists((k) => k.startsWith(kp) && k.includes(prevFk));
|
|
2314
|
-
if (nextFk && relation.invalidateTargetLists) for (const kp of relation.invalidateTargetLists) store.invalidateLists((k) => k.startsWith(kp) && k.includes(nextFk));
|
|
2315
|
-
if (prevFk) store.invalidateEntity(relation.targetType, prevFk);
|
|
2316
|
-
if (nextFk) store.invalidateEntity(relation.targetType, nextFk);
|
|
2317
|
-
break;
|
|
2318
|
-
}
|
|
2319
|
-
case "hasMany":
|
|
2320
|
-
store.invalidateLists(serializeKey(relation.listKeyPrefix(ctx.id)));
|
|
2321
|
-
break;
|
|
2322
|
-
case "manyToMany": {
|
|
2323
|
-
const prevIds = ctx.previous?.[relation.localArrayField ?? ""];
|
|
2324
|
-
const nextIds = ctx.next?.[relation.localArrayField ?? ""];
|
|
2325
|
-
for (const relatedId of /* @__PURE__ */ new Set([...prevIds ?? [], ...nextIds ?? []])) store.invalidateLists(serializeKey(relation.listKeyPrefix(relatedId)));
|
|
2326
|
-
break;
|
|
2327
|
-
}
|
|
2328
|
-
}
|
|
2329
|
-
}
|
|
2330
|
-
for (const [, otherSchema] of schemaRegistry2) {
|
|
2331
|
-
if (!otherSchema.relations) continue;
|
|
2332
|
-
for (const [, rel] of Object.entries(otherSchema.relations)) {
|
|
2333
|
-
if (rel.targetType !== ctx.type) continue;
|
|
2334
|
-
if (rel.cardinality === "hasMany") store.invalidateLists(serializeKey(rel.listKeyPrefix(ctx.id)));
|
|
2335
|
-
}
|
|
2336
|
-
}
|
|
2337
|
-
}
|
|
2338
|
-
function readRelations(type, entity, storeApi = graphStore) {
|
|
2339
|
-
const schema = schemaRegistry2.get(type);
|
|
2340
|
-
if (!schema?.relations) return {};
|
|
2341
|
-
const store = storeApi.getState();
|
|
2342
|
-
const result = {};
|
|
2343
|
-
for (const [name, relation] of Object.entries(schema.relations)) {
|
|
2344
|
-
switch (relation.cardinality) {
|
|
2345
|
-
case "belongsTo": {
|
|
2346
|
-
const fkValue = entity[relation.foreignKey];
|
|
2347
|
-
result[name] = fkValue ? store.readEntity(relation.targetType, fkValue) : null;
|
|
2348
|
-
break;
|
|
2349
|
-
}
|
|
2350
|
-
case "hasMany": {
|
|
2351
|
-
const listKey = serializeKey(relation.listKeyPrefix(entity.id));
|
|
2352
|
-
const listState = store.lists[listKey];
|
|
2353
|
-
result[name] = listState ? listState.ids.map((id) => store.readEntity(relation.targetType, id)).filter(Boolean) : [];
|
|
2354
|
-
break;
|
|
2355
|
-
}
|
|
2356
|
-
case "manyToMany": {
|
|
2357
|
-
const ids = entity[relation.localArrayField ?? ""];
|
|
2358
|
-
result[name] = ids ? ids.map((id) => store.readEntity(relation.targetType, id)).filter(Boolean) : [];
|
|
2359
|
-
break;
|
|
2360
|
-
}
|
|
2361
|
-
}
|
|
2362
|
-
}
|
|
2363
|
-
return result;
|
|
2364
|
-
}
|
|
2365
|
-
|
|
2366
1494
|
// src/agent/json-patch.ts
|
|
2367
1495
|
function unescape(token) {
|
|
2368
1496
|
return token.replace(/~1/g, "/").replace(/~0/g, "~");
|
|
@@ -2900,10 +2028,10 @@ function createGraphQLSubscriptionAdapter(opts) {
|
|
|
2900
2028
|
subscribe(_cfg, handler) {
|
|
2901
2029
|
const unsubs = [];
|
|
2902
2030
|
for (const sub of subscriptions) {
|
|
2903
|
-
const { type, document
|
|
2031
|
+
const { type, document, variables, getPayload } = sub;
|
|
2904
2032
|
unsubs.push(
|
|
2905
2033
|
client.subscribe(
|
|
2906
|
-
{ query:
|
|
2034
|
+
{ query: document, variables },
|
|
2907
2035
|
{
|
|
2908
2036
|
next: ({ data }) => {
|
|
2909
2037
|
const raw = getPayload(data);
|
|
@@ -4035,6 +3163,6 @@ function getFacetedRowModel(preFilteredRowModel, columnId, allFilteredRowModel)
|
|
|
4035
3163
|
return preFilteredRowModel;
|
|
4036
3164
|
}
|
|
4037
3165
|
|
|
4038
|
-
export {
|
|
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 };
|
|
4039
3167
|
//# sourceMappingURL=index.mjs.map
|
|
4040
3168
|
//# sourceMappingURL=index.mjs.map
|