@prometheus-ags/entity-graph-core 3.0.5 → 3.2.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
@@ -1039,6 +1039,7 @@ function collectDirtyPaths(current, original, prefix = "", acc = /* @__PURE__ */
1039
1039
 
1040
1040
  // src/merge/loro.ts
1041
1041
  var ROOT_MAP = "entity";
1042
+ var LORO_MODULE_SPECIFIER = "loro-crdt";
1042
1043
  var docs = /* @__PURE__ */ new Map();
1043
1044
  function ek2(type, id) {
1044
1045
  return `${type}:${id}`;
@@ -1048,7 +1049,7 @@ async function createLoroMergeStrategy(loadLoro) {
1048
1049
  try {
1049
1050
  const loaded = loadLoro ? await loadLoro() : await import(
1050
1051
  /* @vite-ignore */
1051
- 'loro-crdt'
1052
+ LORO_MODULE_SPECIFIER
1052
1053
  );
1053
1054
  if (typeof loaded.LoroDoc !== "function") {
1054
1055
  throw new TypeError("loro-crdt did not export a LoroDoc constructor");
@@ -1150,270 +1151,24 @@ function applySelection(row, select) {
1150
1151
  return picked;
1151
1152
  }
1152
1153
 
1153
- // src/engine.ts
1154
- function serializeKey(key) {
1155
- return JSON.stringify(key, (_, v) => v && typeof v === "object" && !Array.isArray(v) ? Object.fromEntries(Object.entries(v).sort()) : v);
1156
- }
1157
- function sleep(ms) {
1158
- return new Promise((r) => setTimeout(r, ms));
1159
- }
1160
- var inflightByStore = /* @__PURE__ */ new WeakMap();
1161
- function inflightFor(store) {
1162
- let inflight = inflightByStore.get(store);
1163
- if (!inflight) {
1164
- inflight = /* @__PURE__ */ new Map();
1165
- inflightByStore.set(store, inflight);
1166
- }
1167
- return inflight;
1168
- }
1169
- function dedupe(key, fn, store = graphStore) {
1170
- const inflight = inflightFor(store);
1171
- if (inflight.has(key)) return inflight.get(key);
1172
- const p = fn().finally(() => inflight.delete(key));
1173
- inflight.set(key, p);
1174
- return p;
1175
- }
1176
- var subscriberStatsListeners = /* @__PURE__ */ new Set();
1177
- function emitSubscriberStatsChange() {
1178
- for (const cb of subscriberStatsListeners) cb();
1179
- }
1180
- var subscribersByStore = /* @__PURE__ */ new WeakMap();
1181
- function subscribersFor(store) {
1182
- let subscribers = subscribersByStore.get(store);
1183
- if (!subscribers) {
1184
- subscribers = /* @__PURE__ */ new Map();
1185
- subscribersByStore.set(store, subscribers);
1186
- }
1187
- return subscribers;
1188
- }
1189
- function registerSubscriber(key, store = graphStore) {
1190
- const subscribers = subscribersFor(store);
1191
- const token = Symbol(key);
1192
- if (!subscribers.has(key)) subscribers.set(key, /* @__PURE__ */ new Set());
1193
- subscribers.get(key).add(token);
1194
- emitSubscriberStatsChange();
1195
- return token;
1196
- }
1197
- function unregisterSubscriber(key, token, store = graphStore) {
1198
- const subscribers = subscribersFor(store);
1199
- const set = subscribers.get(key);
1200
- if (!set) return;
1201
- set.delete(token);
1202
- if (set.size === 0) subscribers.delete(key);
1203
- emitSubscriberStatsChange();
1204
- }
1205
- function hasSubscribers(key, store = graphStore) {
1206
- return (subscribersFor(store).get(key)?.size ?? 0) > 0;
1207
- }
1208
- var DEFAULT_OPTIONS = {
1209
- defaultStaleTime: 3e4,
1210
- defaultGcTime: 3e5,
1211
- gcInterval: 6e4,
1212
- maxRetries: 3,
1213
- retryBaseDelay: 1e3,
1214
- revalidateOnFocus: true,
1215
- revalidateOnReconnect: true
1216
- };
1217
- var engineOptions = { ...DEFAULT_OPTIONS };
1218
- function subscribeSubscriberStats(onChange) {
1219
- subscriberStatsListeners.add(onChange);
1220
- return () => subscriberStatsListeners.delete(onChange);
1221
- }
1222
- function getActiveSubscriberCount(store = graphStore) {
1223
- let n = 0;
1224
- for (const set of subscribersFor(store).values()) n += set.size;
1225
- return n;
1226
- }
1227
- var devtoolsSubscribers = /* @__PURE__ */ new Set();
1228
- function subscribeDevtoolsEvent(cb) {
1229
- devtoolsSubscribers.add(cb);
1230
- return () => {
1231
- devtoolsSubscribers.delete(cb);
1232
- };
1233
- }
1234
- function notifyDevtools(event) {
1235
- if (devtoolsSubscribers.size === 0) return;
1236
- const listeners2 = [...devtoolsSubscribers];
1237
- for (const cb of listeners2) {
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]) {
1238
1159
  try {
1239
- cb(event);
1240
- } catch (err) {
1241
- console.warn("[engine] devtools subscriber threw:", err);
1242
- }
1243
- }
1244
- }
1245
- var gcIntervalsByStore = /* @__PURE__ */ new Map();
1246
- function runGarbageCollection(storeApi = graphStore) {
1247
- const store = storeApi.getState();
1248
- const { defaultGcTime: gcTime } = getEngineOptions();
1249
- const now = Date.now();
1250
- const toRemove = [];
1251
- for (const type of Object.keys(store.entities)) {
1252
- const bucket = store.entities[type];
1253
- if (!bucket) continue;
1254
- for (const id of Object.keys(bucket)) {
1255
- const key = `${type}:${id}`;
1256
- if (hasSubscribers(key, storeApi)) continue;
1257
- const patch = store.patches[type]?.[id];
1258
- if (patch !== void 0 && Object.keys(patch).length > 0) continue;
1259
- const entityState = store.entityStates[key];
1260
- if (entityState?.isFetching) continue;
1261
- const lastFetched = entityState?.lastFetched;
1262
- if (lastFetched == null) continue;
1263
- if (now - lastFetched <= gcTime) continue;
1264
- toRemove.push({ type, id });
1160
+ listener(event);
1161
+ } catch {
1162
+ console.warn("[engine] devtools subscriber threw");
1265
1163
  }
1266
1164
  }
1267
- for (const { type, id } of toRemove) {
1268
- store.removeEntity(type, id);
1269
- store.removeIdFromAllLists(type, id);
1270
- }
1271
- }
1272
- function stopGarbageCollector(storeApi = graphStore) {
1273
- const intervalId = gcIntervalsByStore.get(storeApi);
1274
- if (intervalId != null && typeof clearInterval !== "undefined") {
1275
- clearInterval(intervalId);
1276
- gcIntervalsByStore.delete(storeApi);
1277
- }
1278
- }
1279
- function startGarbageCollector(storeApi = graphStore) {
1280
- stopGarbageCollector(storeApi);
1281
- if (typeof window === "undefined" || typeof setInterval === "undefined") return () => {
1282
- };
1283
- const intervalId = setInterval(
1284
- () => runGarbageCollection(storeApi),
1285
- getEngineOptions().gcInterval
1286
- );
1287
- gcIntervalsByStore.set(storeApi, intervalId);
1288
- return () => stopGarbageCollector(storeApi);
1289
- }
1290
- function restartGarbageCollector(storeApi = graphStore) {
1291
- startGarbageCollector(storeApi);
1292
- }
1293
- function configureEngine(opts) {
1294
- engineOptions = { ...DEFAULT_OPTIONS, ...opts };
1295
- const activeStores = [...gcIntervalsByStore.keys()];
1296
- if (activeStores.length === 0) restartGarbageCollector();
1297
- else for (const storeApi of activeStores) restartGarbageCollector(storeApi);
1298
1165
  }
1299
- function getEngineOptions() {
1300
- return engineOptions;
1166
+ function subscribeLegacyDevtoolsEvent(listener) {
1167
+ listeners.add(listener);
1168
+ return () => listeners.delete(listener);
1301
1169
  }
1302
- async function fetchEntity(opts, engineOpts, storeApi = graphStore) {
1303
- const { type, id, fetch, normalize, sideEffects, idField = "id" } = opts;
1304
- if (!id) return;
1305
- storeApi.getState().setEntityFetching(type, id, true);
1306
- const attempt = async (retries) => {
1307
- try {
1308
- const raw = await fetch(id);
1309
- const normalized = normalize(raw);
1310
- const resolvedId = normalized[idField] ?? id;
1311
- storeApi.getState().upsertEntity(type, resolvedId, normalized);
1312
- storeApi.getState().setEntityFetched(type, resolvedId);
1313
- if (sideEffects) sideEffects(raw, storeApi);
1314
- opts.onSuccess?.(normalized);
1315
- } catch (err) {
1316
- const error = err instanceof Error ? err : new Error(String(err));
1317
- if (retries < engineOpts.maxRetries) {
1318
- await sleep(engineOpts.retryBaseDelay * Math.pow(2, retries));
1319
- return attempt(retries + 1);
1320
- }
1321
- storeApi.getState().setEntityError(type, id, error.message);
1322
- opts.onError?.(error);
1323
- }
1324
- };
1325
- await dedupe(`${type}:${id}`, () => attempt(0), storeApi);
1326
- }
1327
- async function fetchList(opts, params, engineOpts, isLoadMore = false, storeApi = graphStore) {
1328
- const { type, queryKey, fetch, normalize, sideEffects, mode = "replace" } = opts;
1329
- const key = serializeKey(queryKey);
1330
- const store = storeApi.getState();
1331
- if (!fetch || !normalize) return;
1332
- if (isLoadMore) store.setListFetchingMore(key, true);
1333
- else store.setListFetching(key, true);
1334
- const attempt = async (retries) => {
1335
- try {
1336
- const response = await fetch(params);
1337
- const normalized = response.items.map(normalize);
1338
- 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 };
1339
- storeApi.getState().ingestFetchedList(
1340
- type,
1341
- normalized.map(({ id, data }) => ({ id, data })),
1342
- { lists: [{ key, mode: mode === "append" && isLoadMore ? "append" : "replace", meta }] }
1343
- );
1344
- if (sideEffects) sideEffects(response.items, storeApi);
1345
- opts.onSuccess?.(response);
1346
- } catch (err) {
1347
- const error = err instanceof Error ? err : new Error(String(err));
1348
- if (retries < engineOpts.maxRetries) {
1349
- await sleep(engineOpts.retryBaseDelay * Math.pow(2, retries));
1350
- return attempt(retries + 1);
1351
- }
1352
- storeApi.getState().setListError(key, error.message);
1353
- opts.onError?.(error);
1354
- }
1355
- };
1356
- await dedupe(isLoadMore ? `${key}:more` : key, () => attempt(0), storeApi);
1357
- }
1358
- var globalListenerAttachments = /* @__PURE__ */ new WeakMap();
1359
- function releaseGlobalListenerAttachment(storeApi, attachment) {
1360
- let released = false;
1361
- return () => {
1362
- if (released) return;
1363
- released = true;
1364
- const current = globalListenerAttachments.get(storeApi);
1365
- if (current !== attachment) return;
1366
- current.references -= 1;
1367
- if (current.references > 0) return;
1368
- current.dispose();
1369
- globalListenerAttachments.delete(storeApi);
1370
- };
1371
- }
1372
- function attachGlobalListeners(storeApi = graphStore) {
1373
- if (typeof window === "undefined") return () => {
1374
- };
1375
- const existing = globalListenerAttachments.get(storeApi);
1376
- if (existing) {
1377
- existing.references += 1;
1378
- return releaseGlobalListenerAttachment(storeApi, existing);
1379
- }
1380
- const stopGc = startGarbageCollector(storeApi);
1381
- const revalidateAll = () => {
1382
- const state = storeApi.getState();
1383
- for (const key of subscribersFor(storeApi).keys()) {
1384
- if (!hasSubscribers(key, storeApi)) continue;
1385
- const colonIdx = key.indexOf(":");
1386
- if (colonIdx === -1) continue;
1387
- const type = key.slice(0, colonIdx);
1388
- const id = key.slice(colonIdx + 1);
1389
- state.setEntityStale(type, id, true);
1390
- }
1391
- };
1392
- const revalidateWhenVisible = () => {
1393
- if (document.visibilityState === "visible") revalidateAll();
1394
- };
1395
- const revalidateOnFocus = engineOptions.revalidateOnFocus;
1396
- const revalidateOnReconnect = engineOptions.revalidateOnReconnect;
1397
- if (revalidateOnFocus) {
1398
- window.addEventListener("visibilitychange", revalidateWhenVisible);
1399
- window.addEventListener("focus", revalidateAll);
1400
- }
1401
- if (revalidateOnReconnect) window.addEventListener("online", revalidateAll);
1402
- const attachment = {
1403
- references: 1,
1404
- dispose: () => {
1405
- if (revalidateOnFocus) {
1406
- window.removeEventListener("visibilitychange", revalidateWhenVisible);
1407
- window.removeEventListener("focus", revalidateAll);
1408
- }
1409
- if (revalidateOnReconnect) {
1410
- window.removeEventListener("online", revalidateAll);
1411
- }
1412
- stopGc();
1413
- }
1414
- };
1415
- globalListenerAttachments.set(storeApi, attachment);
1416
- return releaseGlobalListenerAttachment(storeApi, attachment);
1170
+ function emitLegacyDevtoolsEvent(event) {
1171
+ publish(event);
1417
1172
  }
1418
1173
 
1419
1174
  // src/graph-actions.ts
@@ -1426,7 +1181,7 @@ function createGraphTransaction(storeApi = graphStore) {
1426
1181
  upsertEntity(type, id, data) {
1427
1182
  storeApi.getState().upsertEntity(type, id, data);
1428
1183
  if (typeof process === "undefined" || process.env.NODE_ENV !== "production") {
1429
- notifyDevtools({ kind: "upsert", type, id, data, at: (/* @__PURE__ */ new Date()).toISOString() });
1184
+ emitLegacyDevtoolsEvent({ kind: "upsert", type, id, data, at: (/* @__PURE__ */ new Date()).toISOString() });
1430
1185
  }
1431
1186
  return tx;
1432
1187
  },
@@ -1441,14 +1196,14 @@ function createGraphTransaction(storeApi = graphStore) {
1441
1196
  patchEntity(type, id, patch) {
1442
1197
  storeApi.getState().patchEntity(type, id, patch);
1443
1198
  if (typeof process === "undefined" || process.env.NODE_ENV !== "production") {
1444
- notifyDevtools({ kind: "patch", type, id, patch, at: (/* @__PURE__ */ new Date()).toISOString() });
1199
+ emitLegacyDevtoolsEvent({ kind: "patch", type, id, patch, at: (/* @__PURE__ */ new Date()).toISOString() });
1445
1200
  }
1446
1201
  return tx;
1447
1202
  },
1448
1203
  clearPatch(type, id) {
1449
1204
  storeApi.getState().clearPatch(type, id);
1450
1205
  if (typeof process === "undefined" || process.env.NODE_ENV !== "production") {
1451
- notifyDevtools({ kind: "clearPatch", type, id, at: (/* @__PURE__ */ new Date()).toISOString() });
1206
+ emitLegacyDevtoolsEvent({ kind: "clearPatch", type, id, at: (/* @__PURE__ */ new Date()).toISOString() });
1452
1207
  }
1453
1208
  return tx;
1454
1209
  },
@@ -1931,7 +1686,7 @@ function computeDelay(policy, attempt) {
1931
1686
  return base / 2 + Math.random() * (base / 2);
1932
1687
  }
1933
1688
  }
1934
- function sleep2(ms) {
1689
+ function sleep(ms) {
1935
1690
  if (ms <= 0) return Promise.resolve();
1936
1691
  return new Promise((r) => setTimeout(r, ms));
1937
1692
  }
@@ -1944,7 +1699,7 @@ async function replayActionWithRetry(action, policy, storeApi = graphStore) {
1944
1699
  } catch (error) {
1945
1700
  lastError = error;
1946
1701
  if (attempt >= policy.maxAttempts) break;
1947
- await sleep2(computeDelay(policy, attempt));
1702
+ await sleep(computeDelay(policy, attempt));
1948
1703
  }
1949
1704
  }
1950
1705
  try {
@@ -1976,11 +1731,275 @@ function getDefaultOnlineSource() {
1976
1731
  };
1977
1732
  }
1978
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
+
1979
1998
  // src/devtools-event-bus.ts
1980
1999
  var _busInjectMap = /* @__PURE__ */ new WeakMap();
1981
- var registry = /* @__PURE__ */ new Map();
2000
+ var registries = /* @__PURE__ */ new Map();
1982
2001
  function __resetStoreRegistry() {
1983
- registry.clear();
2002
+ registries.clear();
1984
2003
  }
1985
2004
  function createDevtoolsEventBus(opts) {
1986
2005
  const bufferSize = opts?.bufferSize ?? 500;
@@ -2095,8 +2114,10 @@ function createDevtoolsEventBus(opts) {
2095
2114
  pendingBurst = [];
2096
2115
  dispatchedCount = 0;
2097
2116
  flushScheduled = false;
2098
- for (const entry of registry.values()) {
2099
- 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;
2100
2121
  entry.active = false;
2101
2122
  try {
2102
2123
  entry.unsubscribeSrc();
@@ -2104,12 +2125,19 @@ function createDevtoolsEventBus(opts) {
2104
2125
  }
2105
2126
  }
2106
2127
  }
2128
+ registries.delete(bus);
2129
+ _busInjectMap.delete(bus);
2107
2130
  }
2108
2131
  };
2109
2132
  _busInjectMap.set(bus, handleEngineEvent);
2110
2133
  return bus;
2111
2134
  }
2112
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
+ }
2113
2141
  if (registry.get(name)?.active === true) {
2114
2142
  throw new Error(
2115
2143
  `[devtools-store-registry] Store name "${name}" is already registered and active`
@@ -2130,75 +2158,77 @@ function registerStore(bus, source, name) {
2130
2158
  unsubscribeSrc();
2131
2159
  };
2132
2160
  }
2133
- function getRegisteredStores() {
2134
- 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 }));
2135
2164
  }
2136
2165
 
2137
- // src/devtools-time-travel.ts
2138
- var capacity = 50;
2139
- var ring = [];
2140
- var seqCounter = 0;
2141
- var cursor = null;
2142
- var listeners = /* @__PURE__ */ new Set();
2143
- function notify() {
2144
- 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;
2145
2184
  }
2146
- function cloneSlice(s) {
2147
- return {
2148
- entities: structuredClone(s.entities),
2149
- patches: structuredClone(s.patches),
2150
- entityStates: structuredClone(s.entityStates),
2151
- syncMetadata: structuredClone(s.syncMetadata),
2152
- 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);
2153
2202
  };
2154
2203
  }
2155
- function configureTimeTravel(opts) {
2156
- if (opts.capacity && opts.capacity > 0) capacity = opts.capacity;
2157
- if (ring.length > capacity) ring = ring.slice(-capacity);
2158
- 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
+ }
2159
2210
  }
2160
2211
  function recordGraphSnapshot(label, storeApi = graphStore) {
2161
- const s = storeApi.getState();
2162
- const snap = {
2163
- seq: seqCounter++,
2164
- at: Date.now(),
2165
- ...label !== void 0 ? { label } : {},
2166
- data: cloneSlice(s)
2167
- };
2168
- ring.push(snap);
2169
- if (ring.length > capacity) ring = ring.slice(-capacity);
2170
- cursor = null;
2171
- notify();
2172
- return snap.seq;
2212
+ return getGraphDevtoolsCompatibilityDelegate(storeApi)?.capture(label) ?? -1;
2173
2213
  }
2174
2214
  function restoreGraphSnapshot(index, storeApi = graphStore) {
2175
- const snap = ring[index];
2176
- if (!snap) return false;
2177
- const clone2 = cloneSlice(snap.data);
2178
- storeApi.setState(clone2);
2179
- cursor = index;
2180
- notify();
2181
- return true;
2215
+ return getGraphDevtoolsCompatibilityDelegate(storeApi)?.restoreByIndex(index) ?? false;
2182
2216
  }
2183
2217
  function restoreGraphSnapshotBySeq(seq, storeApi = graphStore) {
2184
- const index = ring.findIndex((s) => s.seq === seq);
2185
- return index === -1 ? false : restoreGraphSnapshot(index, storeApi);
2218
+ return getGraphDevtoolsCompatibilityDelegate(storeApi)?.restoreByCursor(seq) ?? false;
2186
2219
  }
2187
2220
  function stepTimeTravel(delta, storeApi = graphStore) {
2188
- const base = cursor ?? ring.length - 1;
2189
- const target = Math.max(0, Math.min(ring.length - 1, base + delta));
2190
- return restoreGraphSnapshot(target, storeApi);
2221
+ return getGraphDevtoolsCompatibilityDelegate(storeApi)?.step(delta) ?? false;
2191
2222
  }
2192
- function getTimeTravelState() {
2193
- return {
2194
- snapshots: ring.map(({ data: _data, ...meta }) => meta),
2195
- cursor,
2196
- capacity
2223
+ function getTimeTravelState(storeApi = graphStore) {
2224
+ return getGraphDevtoolsCompatibilityDelegate(storeApi)?.getState() ?? {
2225
+ snapshots: [],
2226
+ cursor: null,
2227
+ capacity: 50
2197
2228
  };
2198
2229
  }
2199
- function subscribeTimeTravel(cb) {
2200
- listeners.add(cb);
2201
- return () => listeners.delete(cb);
2230
+ function subscribeTimeTravel(cb, storeApi = graphStore) {
2231
+ return subscribeGraphDevtoolsCompatibility(storeApi, cb);
2202
2232
  }
2203
2233
 
2204
2234
  // src/view/incremental.ts