@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/CHANGELOG.md +30 -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 +572 -448
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +131 -1059
- package/dist/index.d.ts +131 -1059
- package/dist/index.mjs +127 -910
- 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.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
|
-
|
|
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/
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
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
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
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
|
|
1134
|
-
|
|
1135
|
-
for (const set of subscribersFor(store).values()) n += set.size;
|
|
1136
|
-
return n;
|
|
1170
|
+
function emitLegacyDevtoolsEvent(event) {
|
|
1171
|
+
publish(event);
|
|
1137
1172
|
}
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
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
|
|
1146
|
-
if (
|
|
1147
|
-
|
|
1148
|
-
|
|
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
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
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
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
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
|
|
1184
|
-
const
|
|
1185
|
-
if (
|
|
1186
|
-
|
|
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
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
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
|
|
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
|
|
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
|
|
2000
|
+
var registries = /* @__PURE__ */ new Map();
|
|
1893
2001
|
function __resetStoreRegistry() {
|
|
1894
|
-
|
|
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
|
-
|
|
2010
|
-
|
|
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
|
-
|
|
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-
|
|
2049
|
-
var
|
|
2050
|
-
var
|
|
2051
|
-
var
|
|
2052
|
-
var
|
|
2053
|
-
|
|
2054
|
-
function
|
|
2055
|
-
|
|
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
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
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
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
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;
|