@prometheus-ags/entity-graph-core 3.0.4 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -4,6 +4,13 @@ var vanilla = require('zustand/vanilla');
4
4
  var middleware = require('zustand/middleware');
5
5
  var immer = require('zustand/middleware/immer');
6
6
 
7
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
8
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
9
+ }) : x)(function(x) {
10
+ if (typeof require !== "undefined") return require.apply(this, arguments);
11
+ throw Error('Dynamic require of "' + x + '" is not supported');
12
+ });
13
+
7
14
  // src/errors.ts
8
15
  var TerminalError = class _TerminalError extends Error {
9
16
  kind = "terminal";
@@ -70,6 +77,88 @@ function getRegisteredEntityTypes() {
70
77
  return Array.from(transports.keys());
71
78
  }
72
79
 
80
+ // src/active-store.ts
81
+ var moduleActiveStore = null;
82
+ var asyncScope = null;
83
+ var asyncScopeAttempted = false;
84
+ function isNodeLike() {
85
+ return typeof process !== "undefined" && typeof process.versions?.node === "string";
86
+ }
87
+ function resolveAsyncScope() {
88
+ if (asyncScopeAttempted) return asyncScope;
89
+ asyncScopeAttempted = true;
90
+ if (!isNodeLike()) return null;
91
+ try {
92
+ const req = typeof module !== "undefined" && typeof module.require === "function" ? module.require : typeof __require === "function" ? __require : null;
93
+ if (!req) return null;
94
+ const { AsyncLocalStorage } = req("node:async_hooks");
95
+ asyncScope = new AsyncLocalStorage();
96
+ } catch {
97
+ asyncScope = null;
98
+ }
99
+ return asyncScope;
100
+ }
101
+ async function prepareGraphStoreScope() {
102
+ if (asyncScope) return true;
103
+ if (!isNodeLike()) {
104
+ asyncScopeAttempted = true;
105
+ return false;
106
+ }
107
+ if (resolveAsyncScope()) return true;
108
+ try {
109
+ const specifier = "node:async_hooks";
110
+ const mod = await import(
111
+ /* @vite-ignore */
112
+ specifier
113
+ );
114
+ asyncScope = new mod.AsyncLocalStorage();
115
+ asyncScopeAttempted = true;
116
+ return true;
117
+ } catch {
118
+ asyncScopeAttempted = true;
119
+ return false;
120
+ }
121
+ }
122
+ function setActiveGraphStore(store) {
123
+ const previous = moduleActiveStore;
124
+ moduleActiveStore = store;
125
+ return () => {
126
+ moduleActiveStore = previous;
127
+ };
128
+ }
129
+ var warnedUnscopedFallback = false;
130
+ function warnUnscopedRequestFallback() {
131
+ if (warnedUnscopedFallback || !isNodeLike()) return;
132
+ warnedUnscopedFallback = true;
133
+ console.warn(
134
+ "[entity-graph-core] runWithGraphStore() could not load node:async_hooks, so it fell back to a module-level store. Concurrent requests will NOT be isolated. Await prepareGraphStoreScope() once during server startup."
135
+ );
136
+ }
137
+ function runWithGraphStore(store, fn) {
138
+ const scope = resolveAsyncScope();
139
+ if (!scope) {
140
+ warnUnscopedRequestFallback();
141
+ const restore = setActiveGraphStore(store);
142
+ try {
143
+ return fn();
144
+ } finally {
145
+ restore();
146
+ }
147
+ }
148
+ return scope.run(store, fn);
149
+ }
150
+ function resolveActiveGraphStore(fallback) {
151
+ const scoped = resolveAsyncScope()?.getStore();
152
+ if (scoped) return scoped;
153
+ return moduleActiveStore ?? fallback;
154
+ }
155
+ function __resetActiveGraphStore() {
156
+ moduleActiveStore = null;
157
+ asyncScope = null;
158
+ asyncScopeAttempted = false;
159
+ warnedUnscopedFallback = false;
160
+ }
161
+
73
162
  // src/view/prisma-compile.ts
74
163
  function nestWhereField(parts, leaf) {
75
164
  if (parts.length === 0) return {};
@@ -950,6 +1039,7 @@ function collectDirtyPaths(current, original, prefix = "", acc = /* @__PURE__ */
950
1039
 
951
1040
  // src/merge/loro.ts
952
1041
  var ROOT_MAP = "entity";
1042
+ var LORO_MODULE_SPECIFIER = "loro-crdt";
953
1043
  var docs = /* @__PURE__ */ new Map();
954
1044
  function ek2(type, id) {
955
1045
  return `${type}:${id}`;
@@ -959,7 +1049,7 @@ async function createLoroMergeStrategy(loadLoro) {
959
1049
  try {
960
1050
  const loaded = loadLoro ? await loadLoro() : await import(
961
1051
  /* @vite-ignore */
962
- 'loro-crdt'
1052
+ LORO_MODULE_SPECIFIER
963
1053
  );
964
1054
  if (typeof loaded.LoroDoc !== "function") {
965
1055
  throw new TypeError("loro-crdt did not export a LoroDoc constructor");
@@ -1061,405 +1151,159 @@ function applySelection(row, select) {
1061
1151
  return picked;
1062
1152
  }
1063
1153
 
1064
- // src/engine.ts
1065
- function serializeKey(key) {
1066
- return JSON.stringify(key, (_, v) => v && typeof v === "object" && !Array.isArray(v) ? Object.fromEntries(Object.entries(v).sort()) : v);
1067
- }
1068
- function sleep(ms) {
1069
- return new Promise((r) => setTimeout(r, ms));
1070
- }
1071
- var inflightByStore = /* @__PURE__ */ new WeakMap();
1072
- function inflightFor(store) {
1073
- let inflight = inflightByStore.get(store);
1074
- if (!inflight) {
1075
- inflight = /* @__PURE__ */ new Map();
1076
- inflightByStore.set(store, inflight);
1077
- }
1078
- return inflight;
1079
- }
1080
- function dedupe(key, fn, store = graphStore) {
1081
- const inflight = inflightFor(store);
1082
- if (inflight.has(key)) return inflight.get(key);
1083
- const p = fn().finally(() => inflight.delete(key));
1084
- inflight.set(key, p);
1085
- return p;
1086
- }
1087
- var subscriberStatsListeners = /* @__PURE__ */ new Set();
1088
- function emitSubscriberStatsChange() {
1089
- for (const cb of subscriberStatsListeners) cb();
1090
- }
1091
- var subscribersByStore = /* @__PURE__ */ new WeakMap();
1092
- function subscribersFor(store) {
1093
- let subscribers = subscribersByStore.get(store);
1094
- if (!subscribers) {
1095
- subscribers = /* @__PURE__ */ new Map();
1096
- subscribersByStore.set(store, subscribers);
1154
+ // src/legacy-devtools.ts
1155
+ var listeners = /* @__PURE__ */ new Set();
1156
+ function publish(event) {
1157
+ if (listeners.size === 0) return;
1158
+ for (const listener of [...listeners]) {
1159
+ try {
1160
+ listener(event);
1161
+ } catch {
1162
+ console.warn("[engine] devtools subscriber threw");
1163
+ }
1097
1164
  }
1098
- return subscribers;
1099
- }
1100
- function registerSubscriber(key, store = graphStore) {
1101
- const subscribers = subscribersFor(store);
1102
- const token = Symbol(key);
1103
- if (!subscribers.has(key)) subscribers.set(key, /* @__PURE__ */ new Set());
1104
- subscribers.get(key).add(token);
1105
- emitSubscriberStatsChange();
1106
- return token;
1107
- }
1108
- function unregisterSubscriber(key, token, store = graphStore) {
1109
- const subscribers = subscribersFor(store);
1110
- const set = subscribers.get(key);
1111
- if (!set) return;
1112
- set.delete(token);
1113
- if (set.size === 0) subscribers.delete(key);
1114
- emitSubscriberStatsChange();
1115
- }
1116
- function hasSubscribers(key, store = graphStore) {
1117
- return (subscribersFor(store).get(key)?.size ?? 0) > 0;
1118
1165
  }
1119
- var DEFAULT_OPTIONS = {
1120
- defaultStaleTime: 3e4,
1121
- defaultGcTime: 3e5,
1122
- gcInterval: 6e4,
1123
- maxRetries: 3,
1124
- retryBaseDelay: 1e3,
1125
- revalidateOnFocus: true,
1126
- revalidateOnReconnect: true
1127
- };
1128
- var engineOptions = { ...DEFAULT_OPTIONS };
1129
- function subscribeSubscriberStats(onChange) {
1130
- subscriberStatsListeners.add(onChange);
1131
- return () => subscriberStatsListeners.delete(onChange);
1166
+ function subscribeLegacyDevtoolsEvent(listener) {
1167
+ listeners.add(listener);
1168
+ return () => listeners.delete(listener);
1132
1169
  }
1133
- function getActiveSubscriberCount(store = graphStore) {
1134
- let n = 0;
1135
- for (const set of subscribersFor(store).values()) n += set.size;
1136
- return n;
1170
+ function emitLegacyDevtoolsEvent(event) {
1171
+ publish(event);
1137
1172
  }
1138
- var devtoolsSubscribers = /* @__PURE__ */ new Set();
1139
- function subscribeDevtoolsEvent(cb) {
1140
- devtoolsSubscribers.add(cb);
1141
- return () => {
1142
- devtoolsSubscribers.delete(cb);
1173
+
1174
+ // src/graph-actions.ts
1175
+ var graphActionListeners = /* @__PURE__ */ new Set();
1176
+ var graphActionReplayers = /* @__PURE__ */ new Map();
1177
+ function createGraphTransaction(storeApi = graphStore) {
1178
+ const baseline = cloneGraphData(storeApi.getState());
1179
+ let closed = false;
1180
+ const tx = {
1181
+ upsertEntity(type, id, data) {
1182
+ storeApi.getState().upsertEntity(type, id, data);
1183
+ if (typeof process === "undefined" || process.env.NODE_ENV !== "production") {
1184
+ emitLegacyDevtoolsEvent({ kind: "upsert", type, id, data, at: (/* @__PURE__ */ new Date()).toISOString() });
1185
+ }
1186
+ return tx;
1187
+ },
1188
+ replaceEntity(type, id, data) {
1189
+ storeApi.getState().replaceEntity(type, id, data);
1190
+ return tx;
1191
+ },
1192
+ removeEntity(type, id) {
1193
+ storeApi.getState().removeEntity(type, id);
1194
+ return tx;
1195
+ },
1196
+ patchEntity(type, id, patch) {
1197
+ storeApi.getState().patchEntity(type, id, patch);
1198
+ if (typeof process === "undefined" || process.env.NODE_ENV !== "production") {
1199
+ emitLegacyDevtoolsEvent({ kind: "patch", type, id, patch, at: (/* @__PURE__ */ new Date()).toISOString() });
1200
+ }
1201
+ return tx;
1202
+ },
1203
+ clearPatch(type, id) {
1204
+ storeApi.getState().clearPatch(type, id);
1205
+ if (typeof process === "undefined" || process.env.NODE_ENV !== "production") {
1206
+ emitLegacyDevtoolsEvent({ kind: "clearPatch", type, id, at: (/* @__PURE__ */ new Date()).toISOString() });
1207
+ }
1208
+ return tx;
1209
+ },
1210
+ insertIdInList(key, id, position) {
1211
+ storeApi.getState().insertIdInList(key, id, position);
1212
+ return tx;
1213
+ },
1214
+ removeIdFromAllLists(type, id) {
1215
+ storeApi.getState().removeIdFromAllLists(type, id);
1216
+ return tx;
1217
+ },
1218
+ setEntitySyncMetadata(type, id, metadata) {
1219
+ storeApi.getState().setEntitySyncMetadata(type, id, metadata);
1220
+ return tx;
1221
+ },
1222
+ markEntityPending(type, id, origin = "optimistic") {
1223
+ storeApi.getState().setEntitySyncMetadata(type, id, {
1224
+ synced: false,
1225
+ origin,
1226
+ updatedAt: Date.now()
1227
+ });
1228
+ return tx;
1229
+ },
1230
+ markEntitySynced(type, id, origin = "server") {
1231
+ storeApi.getState().setEntitySyncMetadata(type, id, {
1232
+ synced: true,
1233
+ origin,
1234
+ updatedAt: Date.now()
1235
+ });
1236
+ return tx;
1237
+ },
1238
+ commit() {
1239
+ closed = true;
1240
+ },
1241
+ rollback() {
1242
+ if (closed) return;
1243
+ storeApi.setState(cloneGraphData(baseline));
1244
+ closed = true;
1245
+ },
1246
+ snapshot() {
1247
+ return cloneGraphData(storeApi.getState());
1248
+ }
1143
1249
  };
1250
+ return tx;
1144
1251
  }
1145
- function notifyDevtools(event) {
1146
- if (devtoolsSubscribers.size === 0) return;
1147
- const listeners2 = [...devtoolsSubscribers];
1148
- for (const cb of listeners2) {
1252
+ function createGraphAction(opts) {
1253
+ if (opts.key) {
1254
+ graphActionReplayers.set(opts.key, async (record, storeApi) => {
1255
+ const tx = createGraphTransaction(storeApi);
1256
+ try {
1257
+ const result = await opts.run(tx, record.input);
1258
+ tx.commit();
1259
+ return result;
1260
+ } catch (error) {
1261
+ tx.rollback();
1262
+ throw error;
1263
+ }
1264
+ });
1265
+ }
1266
+ return async (input) => {
1267
+ const tx = createGraphTransaction();
1268
+ const record = opts.key ? {
1269
+ id: `${opts.key}:${Date.now()}`,
1270
+ key: opts.key,
1271
+ input: structuredClone(input),
1272
+ enqueuedAt: (/* @__PURE__ */ new Date()).toISOString()
1273
+ } : null;
1149
1274
  try {
1150
- cb(event);
1151
- } catch (err) {
1152
- console.warn("[engine] devtools subscriber threw:", err);
1275
+ if (record) emitGraphActionEvent({ type: "enqueued", record });
1276
+ opts.optimistic?.(tx, input);
1277
+ const result = await opts.run(tx, input);
1278
+ opts.onSuccess?.(result, input, tx);
1279
+ tx.commit();
1280
+ if (record) emitGraphActionEvent({ type: "settled", record });
1281
+ return result;
1282
+ } catch (error) {
1283
+ tx.rollback();
1284
+ const normalized = error instanceof Error ? error : new Error(String(error));
1285
+ if (record) emitGraphActionEvent({ type: "settled", record });
1286
+ opts.onError?.(normalized, input);
1287
+ throw normalized;
1153
1288
  }
1154
- }
1289
+ };
1155
1290
  }
1156
- var gcIntervalsByStore = /* @__PURE__ */ new Map();
1157
- function runGarbageCollection(storeApi = graphStore) {
1158
- const store = storeApi.getState();
1159
- const { defaultGcTime: gcTime } = getEngineOptions();
1160
- const now = Date.now();
1161
- const toRemove = [];
1162
- for (const type of Object.keys(store.entities)) {
1163
- const bucket = store.entities[type];
1164
- if (!bucket) continue;
1165
- for (const id of Object.keys(bucket)) {
1166
- const key = `${type}:${id}`;
1167
- if (hasSubscribers(key, storeApi)) continue;
1168
- const patch = store.patches[type]?.[id];
1169
- if (patch !== void 0 && Object.keys(patch).length > 0) continue;
1170
- const entityState = store.entityStates[key];
1171
- if (entityState?.isFetching) continue;
1172
- const lastFetched = entityState?.lastFetched;
1173
- if (lastFetched == null) continue;
1174
- if (now - lastFetched <= gcTime) continue;
1175
- toRemove.push({ type, id });
1176
- }
1177
- }
1178
- for (const { type, id } of toRemove) {
1179
- store.removeEntity(type, id);
1180
- store.removeIdFromAllLists(type, id);
1181
- }
1291
+ function subscribeGraphActionEvents(listener) {
1292
+ graphActionListeners.add(listener);
1293
+ return () => graphActionListeners.delete(listener);
1182
1294
  }
1183
- function stopGarbageCollector(storeApi = graphStore) {
1184
- const intervalId = gcIntervalsByStore.get(storeApi);
1185
- if (intervalId != null && typeof clearInterval !== "undefined") {
1186
- clearInterval(intervalId);
1187
- gcIntervalsByStore.delete(storeApi);
1188
- }
1295
+ async function replayRegisteredGraphAction(record, storeApi = graphStore) {
1296
+ const replayer = graphActionReplayers.get(record.key);
1297
+ if (!replayer) throw new Error(`No graph action registered for key "${record.key}"`);
1298
+ return replayer(record, storeApi);
1189
1299
  }
1190
- function startGarbageCollector(storeApi = graphStore) {
1191
- stopGarbageCollector(storeApi);
1192
- if (typeof window === "undefined" || typeof setInterval === "undefined") return () => {
1193
- };
1194
- const intervalId = setInterval(
1195
- () => runGarbageCollection(storeApi),
1196
- getEngineOptions().gcInterval
1197
- );
1198
- gcIntervalsByStore.set(storeApi, intervalId);
1199
- return () => stopGarbageCollector(storeApi);
1200
- }
1201
- function restartGarbageCollector(storeApi = graphStore) {
1202
- startGarbageCollector(storeApi);
1203
- }
1204
- function configureEngine(opts) {
1205
- engineOptions = { ...DEFAULT_OPTIONS, ...opts };
1206
- const activeStores = [...gcIntervalsByStore.keys()];
1207
- if (activeStores.length === 0) restartGarbageCollector();
1208
- else for (const storeApi of activeStores) restartGarbageCollector(storeApi);
1209
- }
1210
- function getEngineOptions() {
1211
- return engineOptions;
1212
- }
1213
- async function fetchEntity(opts, engineOpts, storeApi = graphStore) {
1214
- const { type, id, fetch, normalize, sideEffects, idField = "id" } = opts;
1215
- if (!id) return;
1216
- storeApi.getState().setEntityFetching(type, id, true);
1217
- const attempt = async (retries) => {
1218
- try {
1219
- const raw = await fetch(id);
1220
- const normalized = normalize(raw);
1221
- const resolvedId = normalized[idField] ?? id;
1222
- storeApi.getState().upsertEntity(type, resolvedId, normalized);
1223
- storeApi.getState().setEntityFetched(type, resolvedId);
1224
- if (sideEffects) sideEffects(raw, storeApi);
1225
- opts.onSuccess?.(normalized);
1226
- } catch (err) {
1227
- const error = err instanceof Error ? err : new Error(String(err));
1228
- if (retries < engineOpts.maxRetries) {
1229
- await sleep(engineOpts.retryBaseDelay * Math.pow(2, retries));
1230
- return attempt(retries + 1);
1231
- }
1232
- storeApi.getState().setEntityError(type, id, error.message);
1233
- opts.onError?.(error);
1234
- }
1235
- };
1236
- await dedupe(`${type}:${id}`, () => attempt(0), storeApi);
1237
- }
1238
- async function fetchList(opts, params, engineOpts, isLoadMore = false, storeApi = graphStore) {
1239
- const { type, queryKey, fetch, normalize, sideEffects, mode = "replace" } = opts;
1240
- const key = serializeKey(queryKey);
1241
- const store = storeApi.getState();
1242
- if (!fetch || !normalize) return;
1243
- if (isLoadMore) store.setListFetchingMore(key, true);
1244
- else store.setListFetching(key, true);
1245
- const attempt = async (retries) => {
1246
- try {
1247
- const response = await fetch(params);
1248
- const normalized = response.items.map(normalize);
1249
- 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 };
1250
- storeApi.getState().ingestFetchedList(
1251
- type,
1252
- normalized.map(({ id, data }) => ({ id, data })),
1253
- { lists: [{ key, mode: mode === "append" && isLoadMore ? "append" : "replace", meta }] }
1254
- );
1255
- if (sideEffects) sideEffects(response.items, storeApi);
1256
- opts.onSuccess?.(response);
1257
- } catch (err) {
1258
- const error = err instanceof Error ? err : new Error(String(err));
1259
- if (retries < engineOpts.maxRetries) {
1260
- await sleep(engineOpts.retryBaseDelay * Math.pow(2, retries));
1261
- return attempt(retries + 1);
1262
- }
1263
- storeApi.getState().setListError(key, error.message);
1264
- opts.onError?.(error);
1265
- }
1266
- };
1267
- await dedupe(isLoadMore ? `${key}:more` : key, () => attempt(0), storeApi);
1268
- }
1269
- var globalListenerAttachments = /* @__PURE__ */ new WeakMap();
1270
- function releaseGlobalListenerAttachment(storeApi, attachment) {
1271
- let released = false;
1272
- return () => {
1273
- if (released) return;
1274
- released = true;
1275
- const current = globalListenerAttachments.get(storeApi);
1276
- if (current !== attachment) return;
1277
- current.references -= 1;
1278
- if (current.references > 0) return;
1279
- current.dispose();
1280
- globalListenerAttachments.delete(storeApi);
1281
- };
1282
- }
1283
- function attachGlobalListeners(storeApi = graphStore) {
1284
- if (typeof window === "undefined") return () => {
1285
- };
1286
- const existing = globalListenerAttachments.get(storeApi);
1287
- if (existing) {
1288
- existing.references += 1;
1289
- return releaseGlobalListenerAttachment(storeApi, existing);
1290
- }
1291
- const stopGc = startGarbageCollector(storeApi);
1292
- const revalidateAll = () => {
1293
- const state = storeApi.getState();
1294
- for (const key of subscribersFor(storeApi).keys()) {
1295
- if (!hasSubscribers(key, storeApi)) continue;
1296
- const colonIdx = key.indexOf(":");
1297
- if (colonIdx === -1) continue;
1298
- const type = key.slice(0, colonIdx);
1299
- const id = key.slice(colonIdx + 1);
1300
- state.setEntityStale(type, id, true);
1301
- }
1302
- };
1303
- const revalidateWhenVisible = () => {
1304
- if (document.visibilityState === "visible") revalidateAll();
1305
- };
1306
- const revalidateOnFocus = engineOptions.revalidateOnFocus;
1307
- const revalidateOnReconnect = engineOptions.revalidateOnReconnect;
1308
- if (revalidateOnFocus) {
1309
- window.addEventListener("visibilitychange", revalidateWhenVisible);
1310
- window.addEventListener("focus", revalidateAll);
1311
- }
1312
- if (revalidateOnReconnect) window.addEventListener("online", revalidateAll);
1313
- const attachment = {
1314
- references: 1,
1315
- dispose: () => {
1316
- if (revalidateOnFocus) {
1317
- window.removeEventListener("visibilitychange", revalidateWhenVisible);
1318
- window.removeEventListener("focus", revalidateAll);
1319
- }
1320
- if (revalidateOnReconnect) {
1321
- window.removeEventListener("online", revalidateAll);
1322
- }
1323
- stopGc();
1324
- }
1325
- };
1326
- globalListenerAttachments.set(storeApi, attachment);
1327
- return releaseGlobalListenerAttachment(storeApi, attachment);
1328
- }
1329
-
1330
- // src/graph-actions.ts
1331
- var graphActionListeners = /* @__PURE__ */ new Set();
1332
- var graphActionReplayers = /* @__PURE__ */ new Map();
1333
- function createGraphTransaction(storeApi = graphStore) {
1334
- const baseline = cloneGraphData(storeApi.getState());
1335
- let closed = false;
1336
- const tx = {
1337
- upsertEntity(type, id, data) {
1338
- storeApi.getState().upsertEntity(type, id, data);
1339
- if (typeof process === "undefined" || process.env.NODE_ENV !== "production") {
1340
- notifyDevtools({ kind: "upsert", type, id, data, at: (/* @__PURE__ */ new Date()).toISOString() });
1341
- }
1342
- return tx;
1343
- },
1344
- replaceEntity(type, id, data) {
1345
- storeApi.getState().replaceEntity(type, id, data);
1346
- return tx;
1347
- },
1348
- removeEntity(type, id) {
1349
- storeApi.getState().removeEntity(type, id);
1350
- return tx;
1351
- },
1352
- patchEntity(type, id, patch) {
1353
- storeApi.getState().patchEntity(type, id, patch);
1354
- if (typeof process === "undefined" || process.env.NODE_ENV !== "production") {
1355
- notifyDevtools({ kind: "patch", type, id, patch, at: (/* @__PURE__ */ new Date()).toISOString() });
1356
- }
1357
- return tx;
1358
- },
1359
- clearPatch(type, id) {
1360
- storeApi.getState().clearPatch(type, id);
1361
- if (typeof process === "undefined" || process.env.NODE_ENV !== "production") {
1362
- notifyDevtools({ kind: "clearPatch", type, id, at: (/* @__PURE__ */ new Date()).toISOString() });
1363
- }
1364
- return tx;
1365
- },
1366
- insertIdInList(key, id, position) {
1367
- storeApi.getState().insertIdInList(key, id, position);
1368
- return tx;
1369
- },
1370
- removeIdFromAllLists(type, id) {
1371
- storeApi.getState().removeIdFromAllLists(type, id);
1372
- return tx;
1373
- },
1374
- setEntitySyncMetadata(type, id, metadata) {
1375
- storeApi.getState().setEntitySyncMetadata(type, id, metadata);
1376
- return tx;
1377
- },
1378
- markEntityPending(type, id, origin = "optimistic") {
1379
- storeApi.getState().setEntitySyncMetadata(type, id, {
1380
- synced: false,
1381
- origin,
1382
- updatedAt: Date.now()
1383
- });
1384
- return tx;
1385
- },
1386
- markEntitySynced(type, id, origin = "server") {
1387
- storeApi.getState().setEntitySyncMetadata(type, id, {
1388
- synced: true,
1389
- origin,
1390
- updatedAt: Date.now()
1391
- });
1392
- return tx;
1393
- },
1394
- commit() {
1395
- closed = true;
1396
- },
1397
- rollback() {
1398
- if (closed) return;
1399
- storeApi.setState(cloneGraphData(baseline));
1400
- closed = true;
1401
- },
1402
- snapshot() {
1403
- return cloneGraphData(storeApi.getState());
1404
- }
1405
- };
1406
- return tx;
1407
- }
1408
- function createGraphAction(opts) {
1409
- if (opts.key) {
1410
- graphActionReplayers.set(opts.key, async (record, storeApi) => {
1411
- const tx = createGraphTransaction(storeApi);
1412
- try {
1413
- const result = await opts.run(tx, record.input);
1414
- tx.commit();
1415
- return result;
1416
- } catch (error) {
1417
- tx.rollback();
1418
- throw error;
1419
- }
1420
- });
1421
- }
1422
- return async (input) => {
1423
- const tx = createGraphTransaction();
1424
- const record = opts.key ? {
1425
- id: `${opts.key}:${Date.now()}`,
1426
- key: opts.key,
1427
- input: structuredClone(input),
1428
- enqueuedAt: (/* @__PURE__ */ new Date()).toISOString()
1429
- } : null;
1430
- try {
1431
- if (record) emitGraphActionEvent({ type: "enqueued", record });
1432
- opts.optimistic?.(tx, input);
1433
- const result = await opts.run(tx, input);
1434
- opts.onSuccess?.(result, input, tx);
1435
- tx.commit();
1436
- if (record) emitGraphActionEvent({ type: "settled", record });
1437
- return result;
1438
- } catch (error) {
1439
- tx.rollback();
1440
- const normalized = error instanceof Error ? error : new Error(String(error));
1441
- if (record) emitGraphActionEvent({ type: "settled", record });
1442
- opts.onError?.(normalized, input);
1443
- throw normalized;
1444
- }
1445
- };
1446
- }
1447
- function subscribeGraphActionEvents(listener) {
1448
- graphActionListeners.add(listener);
1449
- return () => graphActionListeners.delete(listener);
1450
- }
1451
- async function replayRegisteredGraphAction(record, storeApi = graphStore) {
1452
- const replayer = graphActionReplayers.get(record.key);
1453
- if (!replayer) throw new Error(`No graph action registered for key "${record.key}"`);
1454
- return replayer(record, storeApi);
1455
- }
1456
- function cloneGraphData(source = graphStore.getState()) {
1457
- return {
1458
- entities: structuredClone(source.entities),
1459
- patches: structuredClone(source.patches),
1460
- entityStates: structuredClone(source.entityStates),
1461
- syncMetadata: structuredClone(source.syncMetadata),
1462
- lists: structuredClone(source.lists)
1300
+ function cloneGraphData(source = graphStore.getState()) {
1301
+ return {
1302
+ entities: structuredClone(source.entities),
1303
+ patches: structuredClone(source.patches),
1304
+ entityStates: structuredClone(source.entityStates),
1305
+ syncMetadata: structuredClone(source.syncMetadata),
1306
+ lists: structuredClone(source.lists)
1463
1307
  };
1464
1308
  }
1465
1309
  function emitGraphActionEvent(event) {
@@ -1842,7 +1686,7 @@ function computeDelay(policy, attempt) {
1842
1686
  return base / 2 + Math.random() * (base / 2);
1843
1687
  }
1844
1688
  }
1845
- function sleep2(ms) {
1689
+ function sleep(ms) {
1846
1690
  if (ms <= 0) return Promise.resolve();
1847
1691
  return new Promise((r) => setTimeout(r, ms));
1848
1692
  }
@@ -1855,7 +1699,7 @@ async function replayActionWithRetry(action, policy, storeApi = graphStore) {
1855
1699
  } catch (error) {
1856
1700
  lastError = error;
1857
1701
  if (attempt >= policy.maxAttempts) break;
1858
- await sleep2(computeDelay(policy, attempt));
1702
+ await sleep(computeDelay(policy, attempt));
1859
1703
  }
1860
1704
  }
1861
1705
  try {
@@ -1887,11 +1731,275 @@ function getDefaultOnlineSource() {
1887
1731
  };
1888
1732
  }
1889
1733
 
1734
+ // src/engine.ts
1735
+ function serializeKey(key) {
1736
+ return JSON.stringify(key, (_, v) => v && typeof v === "object" && !Array.isArray(v) ? Object.fromEntries(Object.entries(v).sort()) : v);
1737
+ }
1738
+ function sleep2(ms) {
1739
+ return new Promise((r) => setTimeout(r, ms));
1740
+ }
1741
+ var inflightByStore = /* @__PURE__ */ new WeakMap();
1742
+ function inflightFor(store) {
1743
+ let inflight = inflightByStore.get(store);
1744
+ if (!inflight) {
1745
+ inflight = /* @__PURE__ */ new Map();
1746
+ inflightByStore.set(store, inflight);
1747
+ }
1748
+ return inflight;
1749
+ }
1750
+ function dedupe(key, fn, store = graphStore) {
1751
+ const inflight = inflightFor(store);
1752
+ if (inflight.has(key)) return inflight.get(key);
1753
+ const p = fn().finally(() => inflight.delete(key));
1754
+ inflight.set(key, p);
1755
+ return p;
1756
+ }
1757
+ var globalSubscriberStatsListeners = /* @__PURE__ */ new Set();
1758
+ var subscriberStatsListenersByStore = /* @__PURE__ */ new WeakMap();
1759
+ function subscriberStatsListenersFor(store) {
1760
+ let listeners2 = subscriberStatsListenersByStore.get(store);
1761
+ if (!listeners2) {
1762
+ listeners2 = /* @__PURE__ */ new Set();
1763
+ subscriberStatsListenersByStore.set(store, listeners2);
1764
+ }
1765
+ return listeners2;
1766
+ }
1767
+ function emitSubscriberStatsChange(store) {
1768
+ for (const cb of /* @__PURE__ */ new Set([
1769
+ ...globalSubscriberStatsListeners,
1770
+ ...subscriberStatsListenersFor(store)
1771
+ ])) cb();
1772
+ }
1773
+ var subscribersByStore = /* @__PURE__ */ new WeakMap();
1774
+ function subscribersFor(store) {
1775
+ let subscribers = subscribersByStore.get(store);
1776
+ if (!subscribers) {
1777
+ subscribers = /* @__PURE__ */ new Map();
1778
+ subscribersByStore.set(store, subscribers);
1779
+ }
1780
+ return subscribers;
1781
+ }
1782
+ function registerSubscriber(key, store = graphStore) {
1783
+ const subscribers = subscribersFor(store);
1784
+ const token = Symbol(key);
1785
+ if (!subscribers.has(key)) subscribers.set(key, /* @__PURE__ */ new Set());
1786
+ subscribers.get(key).add(token);
1787
+ emitSubscriberStatsChange(store);
1788
+ return token;
1789
+ }
1790
+ function unregisterSubscriber(key, token, store = graphStore) {
1791
+ const subscribers = subscribersFor(store);
1792
+ const set = subscribers.get(key);
1793
+ if (!set) return;
1794
+ set.delete(token);
1795
+ if (set.size === 0) subscribers.delete(key);
1796
+ emitSubscriberStatsChange(store);
1797
+ }
1798
+ function hasSubscribers(key, store = graphStore) {
1799
+ return (subscribersFor(store).get(key)?.size ?? 0) > 0;
1800
+ }
1801
+ var DEFAULT_OPTIONS = {
1802
+ defaultStaleTime: 3e4,
1803
+ defaultGcTime: 3e5,
1804
+ gcInterval: 6e4,
1805
+ maxRetries: 3,
1806
+ retryBaseDelay: 1e3,
1807
+ revalidateOnFocus: true,
1808
+ revalidateOnReconnect: true
1809
+ };
1810
+ var engineOptions = { ...DEFAULT_OPTIONS };
1811
+ function subscribeSubscriberStats(onChange, store) {
1812
+ const listeners2 = store ? subscriberStatsListenersFor(store) : globalSubscriberStatsListeners;
1813
+ listeners2.add(onChange);
1814
+ return () => listeners2.delete(onChange);
1815
+ }
1816
+ function getActiveSubscriberCount(store = graphStore) {
1817
+ let n = 0;
1818
+ for (const set of subscribersFor(store).values()) n += set.size;
1819
+ return n;
1820
+ }
1821
+ function subscribeDevtoolsEvent(cb) {
1822
+ return subscribeLegacyDevtoolsEvent(cb);
1823
+ }
1824
+ var gcIntervalsByStore = /* @__PURE__ */ new Map();
1825
+ function runGarbageCollection(storeApi = graphStore) {
1826
+ const store = storeApi.getState();
1827
+ const { defaultGcTime: gcTime } = getEngineOptions();
1828
+ const now = Date.now();
1829
+ const toRemove = [];
1830
+ for (const type of Object.keys(store.entities)) {
1831
+ const bucket = store.entities[type];
1832
+ if (!bucket) continue;
1833
+ for (const id of Object.keys(bucket)) {
1834
+ const key = `${type}:${id}`;
1835
+ if (hasSubscribers(key, storeApi)) continue;
1836
+ const patch = store.patches[type]?.[id];
1837
+ if (patch !== void 0 && Object.keys(patch).length > 0) continue;
1838
+ const entityState = store.entityStates[key];
1839
+ if (entityState?.isFetching) continue;
1840
+ const lastFetched = entityState?.lastFetched;
1841
+ if (lastFetched == null) continue;
1842
+ if (now - lastFetched <= gcTime) continue;
1843
+ toRemove.push({ type, id });
1844
+ }
1845
+ }
1846
+ for (const { type, id } of toRemove) {
1847
+ store.removeEntity(type, id);
1848
+ store.removeIdFromAllLists(type, id);
1849
+ }
1850
+ }
1851
+ function stopGarbageCollector(storeApi = graphStore) {
1852
+ const intervalId = gcIntervalsByStore.get(storeApi);
1853
+ if (intervalId != null && typeof clearInterval !== "undefined") {
1854
+ clearInterval(intervalId);
1855
+ gcIntervalsByStore.delete(storeApi);
1856
+ }
1857
+ }
1858
+ function startGarbageCollector(storeApi = graphStore) {
1859
+ stopGarbageCollector(storeApi);
1860
+ if (typeof window === "undefined" || typeof setInterval === "undefined") return () => {
1861
+ };
1862
+ const intervalId = setInterval(
1863
+ () => runGarbageCollection(storeApi),
1864
+ getEngineOptions().gcInterval
1865
+ );
1866
+ gcIntervalsByStore.set(storeApi, intervalId);
1867
+ return () => stopGarbageCollector(storeApi);
1868
+ }
1869
+ function restartGarbageCollector(storeApi = graphStore) {
1870
+ startGarbageCollector(storeApi);
1871
+ }
1872
+ function configureEngine(opts) {
1873
+ engineOptions = { ...DEFAULT_OPTIONS, ...opts };
1874
+ const activeStores = [...gcIntervalsByStore.keys()];
1875
+ if (activeStores.length === 0) restartGarbageCollector();
1876
+ else for (const storeApi of activeStores) restartGarbageCollector(storeApi);
1877
+ }
1878
+ function getEngineOptions() {
1879
+ return engineOptions;
1880
+ }
1881
+ async function fetchEntity(opts, engineOpts, storeApi = graphStore) {
1882
+ const { type, id, fetch, normalize, sideEffects, idField = "id" } = opts;
1883
+ if (!id) return;
1884
+ storeApi.getState().setEntityFetching(type, id, true);
1885
+ const attempt = async (retries) => {
1886
+ try {
1887
+ const raw = await fetch(id);
1888
+ const normalized = normalize(raw);
1889
+ const resolvedId = normalized[idField] ?? id;
1890
+ storeApi.getState().upsertEntity(type, resolvedId, normalized);
1891
+ storeApi.getState().setEntityFetched(type, resolvedId);
1892
+ if (sideEffects) sideEffects(raw, storeApi);
1893
+ opts.onSuccess?.(normalized);
1894
+ } catch (err) {
1895
+ const error = err instanceof Error ? err : new Error(String(err));
1896
+ if (retries < engineOpts.maxRetries) {
1897
+ await sleep2(engineOpts.retryBaseDelay * Math.pow(2, retries));
1898
+ return attempt(retries + 1);
1899
+ }
1900
+ storeApi.getState().setEntityError(type, id, error.message);
1901
+ opts.onError?.(error);
1902
+ }
1903
+ };
1904
+ await dedupe(`${type}:${id}`, () => attempt(0), storeApi);
1905
+ }
1906
+ async function fetchList(opts, params, engineOpts, isLoadMore = false, storeApi = graphStore) {
1907
+ const { type, queryKey, fetch, normalize, sideEffects, mode = "replace" } = opts;
1908
+ const key = serializeKey(queryKey);
1909
+ const store = storeApi.getState();
1910
+ if (!fetch || !normalize) return;
1911
+ if (isLoadMore) store.setListFetchingMore(key, true);
1912
+ else store.setListFetching(key, true);
1913
+ const attempt = async (retries) => {
1914
+ try {
1915
+ const response = await fetch(params);
1916
+ const normalized = response.items.map(normalize);
1917
+ 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 };
1918
+ storeApi.getState().ingestFetchedList(
1919
+ type,
1920
+ normalized.map(({ id, data }) => ({ id, data })),
1921
+ { lists: [{ key, mode: mode === "append" && isLoadMore ? "append" : "replace", meta }] }
1922
+ );
1923
+ if (sideEffects) sideEffects(response.items, storeApi);
1924
+ opts.onSuccess?.(response);
1925
+ } catch (err) {
1926
+ const error = err instanceof Error ? err : new Error(String(err));
1927
+ if (retries < engineOpts.maxRetries) {
1928
+ await sleep2(engineOpts.retryBaseDelay * Math.pow(2, retries));
1929
+ return attempt(retries + 1);
1930
+ }
1931
+ storeApi.getState().setListError(key, error.message);
1932
+ opts.onError?.(error);
1933
+ }
1934
+ };
1935
+ await dedupe(isLoadMore ? `${key}:more` : key, () => attempt(0), storeApi);
1936
+ }
1937
+ var globalListenerAttachments = /* @__PURE__ */ new WeakMap();
1938
+ function releaseGlobalListenerAttachment(storeApi, attachment) {
1939
+ let released = false;
1940
+ return () => {
1941
+ if (released) return;
1942
+ released = true;
1943
+ const current = globalListenerAttachments.get(storeApi);
1944
+ if (current !== attachment) return;
1945
+ current.references -= 1;
1946
+ if (current.references > 0) return;
1947
+ current.dispose();
1948
+ globalListenerAttachments.delete(storeApi);
1949
+ };
1950
+ }
1951
+ function attachGlobalListeners(storeApi = graphStore) {
1952
+ if (typeof window === "undefined") return () => {
1953
+ };
1954
+ const existing = globalListenerAttachments.get(storeApi);
1955
+ if (existing) {
1956
+ existing.references += 1;
1957
+ return releaseGlobalListenerAttachment(storeApi, existing);
1958
+ }
1959
+ const stopGc = startGarbageCollector(storeApi);
1960
+ const revalidateAll = () => {
1961
+ const state = storeApi.getState();
1962
+ for (const key of subscribersFor(storeApi).keys()) {
1963
+ if (!hasSubscribers(key, storeApi)) continue;
1964
+ const colonIdx = key.indexOf(":");
1965
+ if (colonIdx === -1) continue;
1966
+ const type = key.slice(0, colonIdx);
1967
+ const id = key.slice(colonIdx + 1);
1968
+ state.setEntityStale(type, id, true);
1969
+ }
1970
+ };
1971
+ const revalidateWhenVisible = () => {
1972
+ if (document.visibilityState === "visible") revalidateAll();
1973
+ };
1974
+ const revalidateOnFocus = engineOptions.revalidateOnFocus;
1975
+ const revalidateOnReconnect = engineOptions.revalidateOnReconnect;
1976
+ if (revalidateOnFocus) {
1977
+ window.addEventListener("visibilitychange", revalidateWhenVisible);
1978
+ window.addEventListener("focus", revalidateAll);
1979
+ }
1980
+ if (revalidateOnReconnect) window.addEventListener("online", revalidateAll);
1981
+ const attachment = {
1982
+ references: 1,
1983
+ dispose: () => {
1984
+ if (revalidateOnFocus) {
1985
+ window.removeEventListener("visibilitychange", revalidateWhenVisible);
1986
+ window.removeEventListener("focus", revalidateAll);
1987
+ }
1988
+ if (revalidateOnReconnect) {
1989
+ window.removeEventListener("online", revalidateAll);
1990
+ }
1991
+ stopGc();
1992
+ }
1993
+ };
1994
+ globalListenerAttachments.set(storeApi, attachment);
1995
+ return releaseGlobalListenerAttachment(storeApi, attachment);
1996
+ }
1997
+
1890
1998
  // src/devtools-event-bus.ts
1891
1999
  var _busInjectMap = /* @__PURE__ */ new WeakMap();
1892
- var registry = /* @__PURE__ */ new Map();
2000
+ var registries = /* @__PURE__ */ new Map();
1893
2001
  function __resetStoreRegistry() {
1894
- registry.clear();
2002
+ registries.clear();
1895
2003
  }
1896
2004
  function createDevtoolsEventBus(opts) {
1897
2005
  const bufferSize = opts?.bufferSize ?? 500;
@@ -2006,8 +2114,10 @@ function createDevtoolsEventBus(opts) {
2006
2114
  pendingBurst = [];
2007
2115
  dispatchedCount = 0;
2008
2116
  flushScheduled = false;
2009
- for (const entry of registry.values()) {
2010
- if (entry.bus === bus && entry.active) {
2117
+ const registry = registries.get(bus);
2118
+ if (registry) {
2119
+ for (const entry of registry.values()) {
2120
+ if (!entry.active) continue;
2011
2121
  entry.active = false;
2012
2122
  try {
2013
2123
  entry.unsubscribeSrc();
@@ -2015,12 +2125,19 @@ function createDevtoolsEventBus(opts) {
2015
2125
  }
2016
2126
  }
2017
2127
  }
2128
+ registries.delete(bus);
2129
+ _busInjectMap.delete(bus);
2018
2130
  }
2019
2131
  };
2020
2132
  _busInjectMap.set(bus, handleEngineEvent);
2021
2133
  return bus;
2022
2134
  }
2023
2135
  function registerStore(bus, source, name) {
2136
+ let registry = registries.get(bus);
2137
+ if (!registry) {
2138
+ registry = /* @__PURE__ */ new Map();
2139
+ registries.set(bus, registry);
2140
+ }
2024
2141
  if (registry.get(name)?.active === true) {
2025
2142
  throw new Error(
2026
2143
  `[devtools-store-registry] Store name "${name}" is already registered and active`
@@ -2041,75 +2158,77 @@ function registerStore(bus, source, name) {
2041
2158
  unsubscribeSrc();
2042
2159
  };
2043
2160
  }
2044
- function getRegisteredStores() {
2045
- return Array.from(registry.values()).map((e) => ({ name: e.name, active: e.active }));
2161
+ function getRegisteredStores(bus) {
2162
+ const entries = bus ? [...registries.get(bus)?.values() ?? []] : [...registries.values()].flatMap((registry) => [...registry.values()]);
2163
+ return entries.map((entry) => ({ name: entry.name, active: entry.active }));
2046
2164
  }
2047
2165
 
2048
- // src/devtools-time-travel.ts
2049
- var capacity = 50;
2050
- var ring = [];
2051
- var seqCounter = 0;
2052
- var cursor = null;
2053
- var listeners = /* @__PURE__ */ new Set();
2054
- function notify() {
2055
- for (const l of listeners) l();
2166
+ // src/devtools-compatibility-bridge.ts
2167
+ var BRIDGE_KEY = /* @__PURE__ */ Symbol.for("prometheus.entity-graph.devtools.compatibility.v1");
2168
+ var bridgeHost = globalThis;
2169
+ var existingBridge = bridgeHost[BRIDGE_KEY];
2170
+ var bridgeState = existingBridge?.version === 1 ? existingBridge : { version: 1, entries: /* @__PURE__ */ new WeakMap(), factory: null };
2171
+ bridgeHost[BRIDGE_KEY] = bridgeState;
2172
+ function getOrCreateEntry(store) {
2173
+ let entry = bridgeState.entries.get(store);
2174
+ if (!entry) {
2175
+ entry = {
2176
+ delegate: null,
2177
+ listeners: /* @__PURE__ */ new Set(),
2178
+ unsubscribeDelegate: null,
2179
+ compatibilityDetach: null
2180
+ };
2181
+ bridgeState.entries.set(store, entry);
2182
+ }
2183
+ return entry;
2056
2184
  }
2057
- function cloneSlice(s) {
2058
- return {
2059
- entities: structuredClone(s.entities),
2060
- patches: structuredClone(s.patches),
2061
- entityStates: structuredClone(s.entityStates),
2062
- syncMetadata: structuredClone(s.syncMetadata),
2063
- lists: structuredClone(s.lists)
2185
+ function ensureDelegate(store, entry) {
2186
+ const factory = bridgeState.factory;
2187
+ if (entry.delegate || entry.compatibilityDetach || !factory) return;
2188
+ entry.compatibilityDetach = factory(store);
2189
+ }
2190
+ function getGraphDevtoolsCompatibilityDelegate(store) {
2191
+ const entry = getOrCreateEntry(store);
2192
+ ensureDelegate(store, entry);
2193
+ return entry.delegate;
2194
+ }
2195
+ function subscribeGraphDevtoolsCompatibility(store, listener) {
2196
+ const entry = getOrCreateEntry(store);
2197
+ entry.listeners.add(listener);
2198
+ ensureDelegate(store, entry);
2199
+ return () => {
2200
+ entry.listeners.delete(listener);
2201
+ if (entry.listeners.size === 0 && entry.delegate === null) bridgeState.entries.delete(store);
2064
2202
  };
2065
2203
  }
2066
- function configureTimeTravel(opts) {
2067
- if (opts.capacity && opts.capacity > 0) capacity = opts.capacity;
2068
- if (ring.length > capacity) ring = ring.slice(-capacity);
2069
- notify();
2204
+
2205
+ // src/devtools-time-travel.ts
2206
+ function configureTimeTravel(opts, storeApi = graphStore) {
2207
+ if (opts.capacity && opts.capacity > 0) {
2208
+ getGraphDevtoolsCompatibilityDelegate(storeApi)?.configure(opts.capacity);
2209
+ }
2070
2210
  }
2071
2211
  function recordGraphSnapshot(label, storeApi = graphStore) {
2072
- const s = storeApi.getState();
2073
- const snap = {
2074
- seq: seqCounter++,
2075
- at: Date.now(),
2076
- ...label !== void 0 ? { label } : {},
2077
- data: cloneSlice(s)
2078
- };
2079
- ring.push(snap);
2080
- if (ring.length > capacity) ring = ring.slice(-capacity);
2081
- cursor = null;
2082
- notify();
2083
- return snap.seq;
2212
+ return getGraphDevtoolsCompatibilityDelegate(storeApi)?.capture(label) ?? -1;
2084
2213
  }
2085
2214
  function restoreGraphSnapshot(index, storeApi = graphStore) {
2086
- const snap = ring[index];
2087
- if (!snap) return false;
2088
- const clone2 = cloneSlice(snap.data);
2089
- storeApi.setState(clone2);
2090
- cursor = index;
2091
- notify();
2092
- return true;
2215
+ return getGraphDevtoolsCompatibilityDelegate(storeApi)?.restoreByIndex(index) ?? false;
2093
2216
  }
2094
2217
  function restoreGraphSnapshotBySeq(seq, storeApi = graphStore) {
2095
- const index = ring.findIndex((s) => s.seq === seq);
2096
- return index === -1 ? false : restoreGraphSnapshot(index, storeApi);
2218
+ return getGraphDevtoolsCompatibilityDelegate(storeApi)?.restoreByCursor(seq) ?? false;
2097
2219
  }
2098
2220
  function stepTimeTravel(delta, storeApi = graphStore) {
2099
- const base = cursor ?? ring.length - 1;
2100
- const target = Math.max(0, Math.min(ring.length - 1, base + delta));
2101
- return restoreGraphSnapshot(target, storeApi);
2221
+ return getGraphDevtoolsCompatibilityDelegate(storeApi)?.step(delta) ?? false;
2102
2222
  }
2103
- function getTimeTravelState() {
2104
- return {
2105
- snapshots: ring.map(({ data: _data, ...meta }) => meta),
2106
- cursor,
2107
- capacity
2223
+ function getTimeTravelState(storeApi = graphStore) {
2224
+ return getGraphDevtoolsCompatibilityDelegate(storeApi)?.getState() ?? {
2225
+ snapshots: [],
2226
+ cursor: null,
2227
+ capacity: 50
2108
2228
  };
2109
2229
  }
2110
- function subscribeTimeTravel(cb) {
2111
- listeners.add(cb);
2112
- return () => listeners.delete(cb);
2230
+ function subscribeTimeTravel(cb, storeApi = graphStore) {
2231
+ return subscribeGraphDevtoolsCompatibility(storeApi, cb);
2113
2232
  }
2114
2233
 
2115
2234
  // src/view/incremental.ts
@@ -3956,6 +4075,7 @@ exports.IncrementalView = IncrementalView;
3956
4075
  exports.RealtimeManager = RealtimeManager;
3957
4076
  exports.TerminalError = TerminalError;
3958
4077
  exports.TransientError = TransientError;
4078
+ exports.__resetActiveGraphStore = __resetActiveGraphStore;
3959
4079
  exports.__resetEntityTransports = __resetEntityTransports;
3960
4080
  exports.__resetStoreRegistry = __resetStoreRegistry;
3961
4081
  exports.applyAgUiDelta = applyAgUiDelta;
@@ -4033,6 +4153,7 @@ exports.matchesFilter = matchesFilter;
4033
4153
  exports.matchesSearch = matchesSearch;
4034
4154
  exports.parseCreateTable = parseCreateTable;
4035
4155
  exports.persistGraphToStorage = persistGraphToStorage;
4156
+ exports.prepareGraphStoreScope = prepareGraphStoreScope;
4036
4157
  exports.publishFlintMutation = publishFlintMutation;
4037
4158
  exports.queryOnce = queryOnce;
4038
4159
  exports.readRelations = readRelations;
@@ -4049,10 +4170,13 @@ exports.registryKey = registryKey;
4049
4170
  exports.renderMarkdownToHtml = renderMarkdownToHtml;
4050
4171
  exports.replayActionWithRetry = replayActionWithRetry;
4051
4172
  exports.resetRealtimeManager = resetRealtimeManager;
4173
+ exports.resolveActiveGraphStore = resolveActiveGraphStore;
4052
4174
  exports.restoreGraphSnapshot = restoreGraphSnapshot;
4053
4175
  exports.restoreGraphSnapshotBySeq = restoreGraphSnapshotBySeq;
4176
+ exports.runWithGraphStore = runWithGraphStore;
4054
4177
  exports.selectGraph = selectGraph;
4055
4178
  exports.serializeKey = serializeKey;
4179
+ exports.setActiveGraphStore = setActiveGraphStore;
4056
4180
  exports.setDefaultMergeStrategy = setDefaultMergeStrategy;
4057
4181
  exports.setValueAtPath = setValueAtPath;
4058
4182
  exports.sqlTypeToJsonSchema = sqlTypeToJsonSchema;