@prometheus-ags/prometheus-entity-management 1.0.0 → 1.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.js CHANGED
@@ -23,6 +23,11 @@ var EMPTY_ENTITY_STATE = {
23
23
  error: null,
24
24
  stale: false
25
25
  };
26
+ var EMPTY_SYNC_METADATA = {
27
+ synced: true,
28
+ origin: "server",
29
+ updatedAt: null
30
+ };
26
31
  var EMPTY_LIST_STATE = {
27
32
  ids: EMPTY_IDS,
28
33
  total: null,
@@ -41,6 +46,9 @@ var EMPTY_LIST_STATE = {
41
46
  function defaultEntityState() {
42
47
  return { ...EMPTY_ENTITY_STATE };
43
48
  }
49
+ function defaultSyncMetadata() {
50
+ return { ...EMPTY_SYNC_METADATA };
51
+ }
44
52
  function defaultListState() {
45
53
  return { ...EMPTY_LIST_STATE, ids: [] };
46
54
  }
@@ -53,24 +61,33 @@ var useGraphStore = zustand.create()(
53
61
  entities: {},
54
62
  patches: {},
55
63
  entityStates: {},
64
+ syncMetadata: {},
56
65
  lists: {},
57
66
  upsertEntity: (type, id, data) => set((s) => {
58
67
  if (!s.entities[type]) s.entities[type] = {};
59
68
  s.entities[type][id] = { ...s.entities[type][id] ?? {}, ...data };
69
+ const key = ek(type, id);
70
+ if (!s.syncMetadata[key]) s.syncMetadata[key] = defaultSyncMetadata();
60
71
  }),
61
72
  upsertEntities: (type, entries) => set((s) => {
62
73
  if (!s.entities[type]) s.entities[type] = {};
63
- for (const { id, data } of entries)
74
+ for (const { id, data } of entries) {
64
75
  s.entities[type][id] = { ...s.entities[type][id] ?? {}, ...data };
76
+ const key = ek(type, id);
77
+ if (!s.syncMetadata[key]) s.syncMetadata[key] = defaultSyncMetadata();
78
+ }
65
79
  }),
66
80
  replaceEntity: (type, id, data) => set((s) => {
67
81
  if (!s.entities[type]) s.entities[type] = {};
68
82
  s.entities[type][id] = data;
83
+ const key = ek(type, id);
84
+ if (!s.syncMetadata[key]) s.syncMetadata[key] = defaultSyncMetadata();
69
85
  }),
70
86
  removeEntity: (type, id) => set((s) => {
71
87
  delete s.entities[type]?.[id];
72
88
  delete s.patches[type]?.[id];
73
89
  delete s.entityStates[ek(type, id)];
90
+ delete s.syncMetadata[ek(type, id)];
74
91
  }),
75
92
  patchEntity: (type, id, patch) => set((s) => {
76
93
  if (!s.patches[type]) s.patches[type] = {};
@@ -102,12 +119,20 @@ var useGraphStore = zustand.create()(
102
119
  s.entityStates[k].isFetching = false;
103
120
  s.entityStates[k].error = null;
104
121
  s.entityStates[k].stale = false;
122
+ s.syncMetadata[k] = { ...s.syncMetadata[k] ?? defaultSyncMetadata(), synced: true, origin: "server", updatedAt: Date.now() };
105
123
  }),
106
124
  setEntityStale: (type, id, stale) => set((s) => {
107
125
  const k = ek(type, id);
108
126
  if (!s.entityStates[k]) s.entityStates[k] = defaultEntityState();
109
127
  s.entityStates[k].stale = stale;
110
128
  }),
129
+ setEntitySyncMetadata: (type, id, metadata) => set((s) => {
130
+ const k = ek(type, id);
131
+ s.syncMetadata[k] = { ...s.syncMetadata[k] ?? defaultSyncMetadata(), ...metadata };
132
+ }),
133
+ clearEntitySyncMetadata: (type, id) => set((s) => {
134
+ delete s.syncMetadata[ek(type, id)];
135
+ }),
111
136
  setListResult: (key, ids, meta) => set((s) => {
112
137
  const ex = s.lists[key] ?? defaultListState();
113
138
  s.lists[key] = { ...ex, ...meta, ids, isFetching: false, isFetchingMore: false, error: null, stale: false, lastFetched: Date.now() };
@@ -179,11 +204,264 @@ var useGraphStore = zustand.create()(
179
204
  if (!base) return null;
180
205
  const patch = s.patches[type]?.[id];
181
206
  return patch ? { ...base, ...patch } : base;
207
+ },
208
+ readEntitySnapshot: (type, id) => {
209
+ const s = get();
210
+ const base = s.entities[type]?.[id];
211
+ if (!base) return null;
212
+ const patch = s.patches[type]?.[id];
213
+ const metadata = s.syncMetadata[ek(type, id)] ?? EMPTY_SYNC_METADATA;
214
+ return {
215
+ ...patch ? { ...base, ...patch } : base,
216
+ $synced: metadata.synced,
217
+ $origin: metadata.origin,
218
+ $updatedAt: metadata.updatedAt
219
+ };
182
220
  }
183
221
  }))
184
222
  )
185
223
  );
186
224
 
225
+ // src/graph-query.ts
226
+ function queryOnce(opts) {
227
+ const store = useGraphStore.getState();
228
+ const ids = resolveCandidateIds(store, opts);
229
+ let rows = ids.map((id) => store.readEntitySnapshot(opts.type, id)).filter((row) => row !== null);
230
+ if (opts.where) rows = rows.filter(opts.where);
231
+ if (opts.sort) rows = [...rows].sort(opts.sort);
232
+ const projected = rows.map((row) => applySelection(projectRow(row, opts.include, store), opts.select));
233
+ if (opts.id) return projected[0] ?? null;
234
+ return projected;
235
+ }
236
+ var selectGraph = queryOnce;
237
+ function resolveCandidateIds(store, opts) {
238
+ if (opts.id) return [opts.id];
239
+ if (opts.ids) return opts.ids;
240
+ if (opts.listKey) return store.lists[opts.listKey]?.ids ?? [];
241
+ return Object.keys(store.entities[opts.type] ?? {});
242
+ }
243
+ function projectRow(row, include, store) {
244
+ if (!include) return row;
245
+ const projected = { ...row };
246
+ for (const [key, relation] of Object.entries(include)) {
247
+ const related = resolveRelation(row, relation, store);
248
+ projected[key] = related;
249
+ }
250
+ return projected;
251
+ }
252
+ function resolveRelation(entity, relation, store) {
253
+ const include = relation.include;
254
+ switch (relation.via.kind) {
255
+ case "field": {
256
+ const relatedId = entity[relation.via.field];
257
+ if (typeof relatedId !== "string") return null;
258
+ const related = store.readEntitySnapshot(relation.type, relatedId);
259
+ return related ? projectRow(related, include, store) : null;
260
+ }
261
+ case "array": {
262
+ const ids = entity[relation.via.field];
263
+ if (!Array.isArray(ids)) return [];
264
+ return ids.map((id) => typeof id === "string" ? store.readEntitySnapshot(relation.type, id) : null).filter((row) => row !== null).map((row) => projectRow(row, include, store));
265
+ }
266
+ case "list": {
267
+ const key = typeof relation.via.key === "function" ? relation.via.key(entity) : relation.via.key;
268
+ if (!key) return [];
269
+ const ids = store.lists[key]?.ids ?? [];
270
+ return ids.map((id) => store.readEntitySnapshot(relation.type, id)).filter((row) => row !== null).map((row) => projectRow(row, include, store));
271
+ }
272
+ case "resolver": {
273
+ const resolved = relation.via.resolve(entity, store);
274
+ if (Array.isArray(resolved)) {
275
+ return resolved.map((id) => store.readEntitySnapshot(relation.type, id)).filter((row) => row !== null).map((row) => projectRow(row, include, store));
276
+ }
277
+ if (typeof resolved !== "string") return null;
278
+ const related = store.readEntitySnapshot(relation.type, resolved);
279
+ return related ? projectRow(related, include, store) : null;
280
+ }
281
+ }
282
+ }
283
+ function applySelection(row, select) {
284
+ if (!select) return row;
285
+ if (typeof select === "function") {
286
+ const result = select(row);
287
+ return result && typeof result === "object" ? result : { value: result };
288
+ }
289
+ const picked = {};
290
+ for (const key of select) {
291
+ if (key in row) picked[key] = row[key];
292
+ }
293
+ return picked;
294
+ }
295
+
296
+ // src/graph-actions.ts
297
+ function createGraphTransaction() {
298
+ const baseline = cloneGraphData();
299
+ let closed = false;
300
+ const tx = {
301
+ upsertEntity(type, id, data) {
302
+ useGraphStore.getState().upsertEntity(type, id, data);
303
+ return tx;
304
+ },
305
+ replaceEntity(type, id, data) {
306
+ useGraphStore.getState().replaceEntity(type, id, data);
307
+ return tx;
308
+ },
309
+ removeEntity(type, id) {
310
+ useGraphStore.getState().removeEntity(type, id);
311
+ return tx;
312
+ },
313
+ patchEntity(type, id, patch) {
314
+ useGraphStore.getState().patchEntity(type, id, patch);
315
+ return tx;
316
+ },
317
+ clearPatch(type, id) {
318
+ useGraphStore.getState().clearPatch(type, id);
319
+ return tx;
320
+ },
321
+ insertIdInList(key, id, position) {
322
+ useGraphStore.getState().insertIdInList(key, id, position);
323
+ return tx;
324
+ },
325
+ removeIdFromAllLists(type, id) {
326
+ useGraphStore.getState().removeIdFromAllLists(type, id);
327
+ return tx;
328
+ },
329
+ setEntitySyncMetadata(type, id, metadata) {
330
+ useGraphStore.getState().setEntitySyncMetadata(type, id, metadata);
331
+ return tx;
332
+ },
333
+ markEntityPending(type, id, origin = "optimistic") {
334
+ useGraphStore.getState().setEntitySyncMetadata(type, id, {
335
+ synced: false,
336
+ origin,
337
+ updatedAt: Date.now()
338
+ });
339
+ return tx;
340
+ },
341
+ markEntitySynced(type, id, origin = "server") {
342
+ useGraphStore.getState().setEntitySyncMetadata(type, id, {
343
+ synced: true,
344
+ origin,
345
+ updatedAt: Date.now()
346
+ });
347
+ return tx;
348
+ },
349
+ commit() {
350
+ closed = true;
351
+ },
352
+ rollback() {
353
+ if (closed) return;
354
+ useGraphStore.setState(cloneGraphData(baseline));
355
+ closed = true;
356
+ },
357
+ snapshot() {
358
+ return cloneGraphData();
359
+ }
360
+ };
361
+ return tx;
362
+ }
363
+ function createGraphAction(opts) {
364
+ return async (input) => {
365
+ const tx = createGraphTransaction();
366
+ try {
367
+ opts.optimistic?.(tx, input);
368
+ const result = await opts.run(tx, input);
369
+ opts.onSuccess?.(result, input, tx);
370
+ tx.commit();
371
+ return result;
372
+ } catch (error) {
373
+ tx.rollback();
374
+ const normalized = error instanceof Error ? error : new Error(String(error));
375
+ opts.onError?.(normalized, input);
376
+ throw normalized;
377
+ }
378
+ };
379
+ }
380
+ function cloneGraphData(source = useGraphStore.getState()) {
381
+ return {
382
+ entities: structuredClone(source.entities),
383
+ patches: structuredClone(source.patches),
384
+ entityStates: structuredClone(source.entityStates),
385
+ syncMetadata: structuredClone(source.syncMetadata),
386
+ lists: structuredClone(source.lists)
387
+ };
388
+ }
389
+
390
+ // src/graph-effects.ts
391
+ function createGraphEffect(opts) {
392
+ const getKey = opts.getKey ?? defaultGetKey;
393
+ const isEqual = opts.isEqual ?? defaultIsEqual;
394
+ let initialized = false;
395
+ let previous = /* @__PURE__ */ new Map();
396
+ const evaluate = () => {
397
+ const nextValues = normalizeQueryResult(opts.query());
398
+ const next = /* @__PURE__ */ new Map();
399
+ nextValues.forEach((value, index) => {
400
+ next.set(getKey(value, index), value);
401
+ });
402
+ if (!initialized) {
403
+ initialized = true;
404
+ previous = next;
405
+ if (opts.skipInitial) return;
406
+ }
407
+ for (const [key, value] of next.entries()) {
408
+ const previousValue = previous.get(key);
409
+ if (previousValue === void 0) {
410
+ opts.onEnter?.({ key, value });
411
+ continue;
412
+ }
413
+ if (!isEqual(previousValue, value)) {
414
+ opts.onUpdate?.({ key, value, previousValue });
415
+ }
416
+ }
417
+ for (const [key, previousValue] of previous.entries()) {
418
+ if (!next.has(key)) opts.onExit?.({ key, previousValue });
419
+ }
420
+ previous = next;
421
+ };
422
+ evaluate();
423
+ const unsubscribe = useGraphStore.subscribe(() => {
424
+ evaluate();
425
+ });
426
+ return {
427
+ dispose: () => {
428
+ unsubscribe();
429
+ }
430
+ };
431
+ }
432
+ function normalizeQueryResult(value) {
433
+ if (value == null) return [];
434
+ return Array.isArray(value) ? value : [value];
435
+ }
436
+ function defaultGetKey(value, index) {
437
+ if (value && typeof value === "object") {
438
+ const record = value;
439
+ if (typeof record.id === "string") return record.id;
440
+ if (typeof record.$key === "string") return record.$key;
441
+ }
442
+ return String(index);
443
+ }
444
+ function defaultIsEqual(previousValue, nextValue) {
445
+ return JSON.stringify(previousValue) === JSON.stringify(nextValue);
446
+ }
447
+
448
+ // src/ai-interop.ts
449
+ function exportGraphSnapshot(opts) {
450
+ const payload = {
451
+ scope: opts.scope,
452
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
453
+ data: opts.data
454
+ };
455
+ return JSON.stringify(payload, null, opts.pretty === false ? 0 : 2);
456
+ }
457
+ function createGraphTool(handler) {
458
+ return (input) => handler(input, {
459
+ store: useGraphStore.getState(),
460
+ queryOnce,
461
+ exportGraphSnapshot
462
+ });
463
+ }
464
+
187
465
  // src/engine.ts
188
466
  function serializeKey(key) {
189
467
  return JSON.stringify(key, (_, v) => v && typeof v === "object" && !Array.isArray(v) ? Object.fromEntries(Object.entries(v).sort()) : v);
@@ -451,10 +729,7 @@ function useEntity(opts) {
451
729
  normalizeRef.current = opts.normalize;
452
730
  const data = zustand.useStore(useGraphStore, shallow.useShallow((state) => {
453
731
  if (!id) return null;
454
- const base = state.entities[type]?.[id];
455
- if (!base) return null;
456
- const patch = state.patches[type]?.[id];
457
- return patch ? { ...base, ...patch } : base;
732
+ return state.readEntitySnapshot(type, id);
458
733
  }));
459
734
  const entityState = zustand.useStore(useGraphStore, React5.useCallback(
460
735
  (state) => state.entityStates[`${type}:${id}`] ?? EMPTY_ENTITY_STATE,
@@ -492,12 +767,7 @@ function useEntityList(opts) {
492
767
  useGraphStore,
493
768
  shallow.useShallow((state) => {
494
769
  const ids = state.lists[key]?.ids ?? EMPTY_IDS;
495
- return ids.map((id) => {
496
- const base = state.entities[type]?.[id];
497
- if (!base) return null;
498
- const patch = state.patches[type]?.[id];
499
- return patch ? { ...base, ...patch } : base;
500
- }).filter((x) => x !== null);
770
+ return ids.map((id) => state.readEntitySnapshot(type, id)).filter((x) => x !== null);
501
771
  })
502
772
  );
503
773
  const doFetch = React5.useCallback((params = {}) => {
@@ -534,18 +804,28 @@ function useEntityMutation(opts) {
534
804
  const { id, patch } = opt;
535
805
  const store = useGraphStore.getState();
536
806
  const previous = { ...store.patches[type]?.[id] };
807
+ const previousSync = store.syncMetadata[`${type}:${id}`];
537
808
  store.patchEntity(type, id, patch);
538
- rollback = () => Object.keys(previous).length > 0 ? useGraphStore.getState().patchEntity(type, id, previous) : useGraphStore.getState().clearPatch(type, id);
809
+ store.setEntitySyncMetadata(type, id, { synced: false, origin: "optimistic", updatedAt: Date.now() });
810
+ rollback = () => {
811
+ const currentStore = useGraphStore.getState();
812
+ if (Object.keys(previous).length > 0) currentStore.patchEntity(type, id, previous);
813
+ else currentStore.clearPatch(type, id);
814
+ if (previousSync) currentStore.setEntitySyncMetadata(type, id, previousSync);
815
+ else currentStore.clearEntitySyncMetadata(type, id);
816
+ };
539
817
  }
540
818
  }
541
819
  try {
542
820
  const result = await apiFn(input);
543
821
  if (normalize) {
544
822
  const { id, data } = normalize(result, input);
545
- useGraphStore.getState().upsertEntity(type, id, data);
823
+ const store = useGraphStore.getState();
824
+ store.upsertEntity(type, id, data);
825
+ store.setEntitySyncMetadata(type, id, { synced: true, origin: "server", updatedAt: Date.now() });
546
826
  if (optimistic) {
547
827
  const opt = optimistic(input);
548
- if (opt) useGraphStore.getState().clearPatch(type, opt.id);
828
+ if (opt) store.clearPatch(type, opt.id);
549
829
  }
550
830
  }
551
831
  if (invalidateLists) for (const k of invalidateLists) useGraphStore.getState().invalidateLists(k);
@@ -1047,12 +1327,7 @@ function useEntityView(opts) {
1047
1327
  shallow.useShallow((state) => {
1048
1328
  const list = state.lists[baseKey] ?? EMPTY_LIST_STATE;
1049
1329
  const sourceIds = completenessMode !== "remote" && remoteResultKey ? state.lists[remoteResultKey]?.ids ?? EMPTY_IDS : list.ids;
1050
- const getEntity = (id) => {
1051
- const base = state.entities[type]?.[id];
1052
- if (!base) return null;
1053
- const patch = state.patches[type]?.[id];
1054
- return patch ? { ...base, ...patch } : base;
1055
- };
1330
+ const getEntity = (id) => state.readEntitySnapshot(type, id);
1056
1331
  return applyView(
1057
1332
  sourceIds,
1058
1333
  getEntity,
@@ -1065,12 +1340,7 @@ function useEntityView(opts) {
1065
1340
  const items = zustand.useStore(
1066
1341
  useGraphStore,
1067
1342
  shallow.useShallow(
1068
- (state) => localViewIds.map((id) => {
1069
- const base = state.entities[type]?.[id];
1070
- if (!base) return null;
1071
- const patch = state.patches[type]?.[id];
1072
- return patch ? { ...base, ...patch } : base;
1073
- }).filter((item) => item !== null)
1343
+ (state) => localViewIds.map((id) => state.readEntitySnapshot(type, id)).filter((item) => item !== null)
1074
1344
  )
1075
1345
  );
1076
1346
  const fireRemoteFetch = React5.useCallback(async (view, cursor) => {
@@ -1125,17 +1395,11 @@ function useEntityView(opts) {
1125
1395
  const isPresent = id in newEntities;
1126
1396
  if (!isPresent) continue;
1127
1397
  const entity = newEntities[id];
1128
- const patch = store.patches[type]?.[id];
1129
- const merged = patch ? { ...entity, ...patch } : entity;
1398
+ const merged = store.readEntitySnapshot(type, id) ?? entity;
1130
1399
  const matches = (!view.filter || matchesFilter(merged, view.filter)) && (!view.search?.query || matchesSearch(merged, view.search.query, view.search.fields));
1131
1400
  if (matches && !list.ids.includes(id)) {
1132
1401
  if (view.sort && view.sort.length > 0) {
1133
- const idx = findInsertionIndex(merged, list.ids, (eid) => {
1134
- const b = store.entities[type]?.[eid];
1135
- if (!b) return null;
1136
- const p = store.patches[type]?.[eid];
1137
- return p ? { ...b, ...p } : b;
1138
- }, view.sort);
1402
+ const idx = findInsertionIndex(merged, list.ids, (eid) => store.readEntitySnapshot(type, eid), view.sort);
1139
1403
  store.insertIdInList(baseKey, id, idx);
1140
1404
  } else store.insertIdInList(baseKey, id, "start");
1141
1405
  }
@@ -1313,25 +1577,33 @@ function useEntityCRUD(opts) {
1313
1577
  }, [resetBuffer, selectedId]);
1314
1578
  const applyOptimistic = React5.useCallback(() => {
1315
1579
  if (!selectedId) return;
1316
- useGraphStore.getState().patchEntity(type, selectedId, editBuffer);
1580
+ const store = useGraphStore.getState();
1581
+ store.patchEntity(type, selectedId, editBuffer);
1582
+ store.setEntitySyncMetadata(type, selectedId, { synced: false, origin: "optimistic", updatedAt: Date.now() });
1317
1583
  }, [type, selectedId, editBuffer]);
1318
1584
  const save = React5.useCallback(async () => {
1319
1585
  if (!selectedId || !onUpdate) return null;
1320
1586
  setIsSaving(true);
1321
1587
  setSaveError(null);
1322
- const previous = useGraphStore.getState().readEntity(type, selectedId);
1323
- useGraphStore.getState().upsertEntity(type, selectedId, editBuffer);
1588
+ const store = useGraphStore.getState();
1589
+ const previous = store.readEntity(type, selectedId);
1590
+ const previousSync = store.syncMetadata[`${type}:${selectedId}`];
1591
+ store.upsertEntity(type, selectedId, editBuffer);
1592
+ store.setEntitySyncMetadata(type, selectedId, { synced: false, origin: "optimistic", updatedAt: Date.now() });
1324
1593
  try {
1325
1594
  const result = await onUpdate(selectedId, editBuffer);
1326
1595
  const { id, data } = normalize(result);
1327
- useGraphStore.getState().replaceEntity(type, id, data);
1328
- useGraphStore.getState().clearPatch(type, id);
1596
+ store.replaceEntity(type, id, data);
1597
+ store.clearPatch(type, id);
1598
+ store.setEntitySyncMetadata(type, id, { synced: true, origin: "server", updatedAt: Date.now() });
1329
1599
  cascadeInvalidation({ type, id: selectedId, previous, next: data, op: "update" });
1330
1600
  setMode("detail");
1331
1601
  optsRef.current.onUpdateSuccess?.(result);
1332
1602
  return result;
1333
1603
  } catch (err) {
1334
- if (previous) useGraphStore.getState().replaceEntity(type, selectedId, previous);
1604
+ if (previous) store.replaceEntity(type, selectedId, previous);
1605
+ if (previousSync) store.setEntitySyncMetadata(type, selectedId, previousSync);
1606
+ else store.clearEntitySyncMetadata(type, selectedId);
1335
1607
  const error = err instanceof Error ? err : new Error(String(err));
1336
1608
  setSaveError(error.message);
1337
1609
  optsRef.current.onError?.("update", error);
@@ -1362,15 +1634,17 @@ function useEntityCRUD(opts) {
1362
1634
  setCreateError(null);
1363
1635
  const tempId = `__temp__${Date.now()}`;
1364
1636
  const optimisticData = { ...createBuffer, id: tempId, _optimistic: true };
1365
- useGraphStore.getState().upsertEntity(type, tempId, optimisticData);
1366
- useGraphStore.getState().insertIdInList(serializeKey(listQueryKey), tempId, "start");
1637
+ const store = useGraphStore.getState();
1638
+ store.upsertEntity(type, tempId, optimisticData);
1639
+ store.setEntitySyncMetadata(type, tempId, { synced: false, origin: "optimistic", updatedAt: Date.now() });
1640
+ store.insertIdInList(serializeKey(listQueryKey), tempId, "start");
1367
1641
  try {
1368
1642
  const result = await onCreate(createBuffer);
1369
1643
  const { id: realId, data } = normalize(result);
1370
- useGraphStore.getState().removeEntity(type, tempId);
1371
- useGraphStore.getState().upsertEntity(type, realId, data);
1372
- useGraphStore.getState().setEntityFetched(type, realId);
1373
- const store = useGraphStore.getState();
1644
+ store.removeEntity(type, tempId);
1645
+ store.upsertEntity(type, realId, data);
1646
+ store.setEntityFetched(type, realId);
1647
+ store.setEntitySyncMetadata(type, realId, { synced: true, origin: "server", updatedAt: Date.now() });
1374
1648
  for (const key of Object.keys(store.lists)) {
1375
1649
  const list2 = store.lists[key];
1376
1650
  const idx = list2.ids.indexOf(tempId);
@@ -1388,8 +1662,8 @@ function useEntityCRUD(opts) {
1388
1662
  optsRef.current.onCreateSuccess?.(result);
1389
1663
  return result;
1390
1664
  } catch (err) {
1391
- useGraphStore.getState().removeEntity(type, tempId);
1392
- useGraphStore.getState().removeIdFromAllLists(type, tempId);
1665
+ store.removeEntity(type, tempId);
1666
+ store.removeIdFromAllLists(type, tempId);
1393
1667
  const error = err instanceof Error ? err : new Error(String(err));
1394
1668
  setCreateError(error.message);
1395
1669
  optsRef.current.onError?.("create", error);
@@ -2193,10 +2467,7 @@ function useGQLEntity(opts) {
2193
2467
  optsRef.current = opts;
2194
2468
  const data = zustand.useStore(useGraphStore, shallow.useShallow((s) => {
2195
2469
  if (!id) return null;
2196
- const base = s.entities[type]?.[id];
2197
- if (!base) return null;
2198
- const patch = s.patches[type]?.[id];
2199
- return patch ? { ...base, ...patch } : base;
2470
+ return s.readEntitySnapshot(type, id);
2200
2471
  }));
2201
2472
  const entityState = zustand.useStore(useGraphStore, React5.useCallback(
2202
2473
  (s) => s.entityStates[`${type}:${id}`] ?? EMPTY_ENTITY_STATE,
@@ -2242,12 +2513,7 @@ function useGQLList(opts) {
2242
2513
  useGraphStore,
2243
2514
  shallow.useShallow((s) => {
2244
2515
  const ids = s.lists[key]?.ids ?? EMPTY_IDS;
2245
- return ids.map((id) => {
2246
- const base = s.entities[type]?.[id];
2247
- if (!base) return null;
2248
- const p = s.patches[type]?.[id];
2249
- return p ? { ...base, ...p } : base;
2250
- }).filter((x) => x !== null);
2516
+ return ids.map((id) => s.readEntitySnapshot(type, id)).filter((x) => x !== null);
2251
2517
  })
2252
2518
  );
2253
2519
  const doFetch = React5.useCallback((cursor, append = false) => {
@@ -7978,7 +8244,11 @@ exports.configureEngine = configureEngine;
7978
8244
  exports.createConvexAdapter = createConvexAdapter;
7979
8245
  exports.createElectricAdapter = createElectricAdapter;
7980
8246
  exports.createGQLClient = createGQLClient;
8247
+ exports.createGraphAction = createGraphAction;
8248
+ exports.createGraphEffect = createGraphEffect;
7981
8249
  exports.createGraphQLSubscriptionAdapter = createGraphQLSubscriptionAdapter;
8250
+ exports.createGraphTool = createGraphTool;
8251
+ exports.createGraphTransaction = createGraphTransaction;
7982
8252
  exports.createPresetStore = createPresetStore;
7983
8253
  exports.createPrismaEntityConfig = createPrismaEntityConfig;
7984
8254
  exports.createRow = createRow;
@@ -7991,6 +8261,7 @@ exports.deleteAction = deleteAction;
7991
8261
  exports.editAction = editAction;
7992
8262
  exports.enumColumn = enumColumn;
7993
8263
  exports.executeGQL = executeGQL;
8264
+ exports.exportGraphSnapshot = exportGraphSnapshot;
7994
8265
  exports.fetchEntity = fetchEntity;
7995
8266
  exports.fetchList = fetchList;
7996
8267
  exports.flattenClauses = flattenClauses;
@@ -8019,9 +8290,11 @@ exports.pureEnumColumn = enumColumn2;
8019
8290
  exports.pureNumberColumn = numberColumn2;
8020
8291
  exports.pureSelectionColumn = selectionColumn2;
8021
8292
  exports.pureTextColumn = textColumn2;
8293
+ exports.queryOnce = queryOnce;
8022
8294
  exports.readRelations = readRelations;
8023
8295
  exports.registerSchema = registerSchema;
8024
8296
  exports.resetRealtimeManager = resetRealtimeManager;
8297
+ exports.selectGraph = selectGraph;
8025
8298
  exports.selectionColumn = selectionColumn;
8026
8299
  exports.serializeKey = serializeKey;
8027
8300
  exports.startGarbageCollector = startGarbageCollector;