@marktiderman/genesis-core 1.5.0 → 1.6.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.
Files changed (51) hide show
  1. package/README.md +143 -10
  2. package/dist/chunk-ADD2RIGA.js +108 -0
  3. package/dist/chunk-ADD2RIGA.js.map +1 -0
  4. package/dist/{chunk-IZT2O57C.js → chunk-CKGGE6WA.js} +36 -16
  5. package/dist/chunk-CKGGE6WA.js.map +1 -0
  6. package/dist/{chunk-DVW5GXWZ.js → chunk-KV6KH3L2.js} +16 -11
  7. package/dist/chunk-KV6KH3L2.js.map +1 -0
  8. package/dist/chunk-KXVHDK3Q.js +71 -0
  9. package/dist/chunk-KXVHDK3Q.js.map +1 -0
  10. package/dist/chunk-OBUBP4BY.js +556 -0
  11. package/dist/chunk-OBUBP4BY.js.map +1 -0
  12. package/dist/chunk-V3GDAELU.js +3 -0
  13. package/dist/{chunk-HT43MPKW.js.map → chunk-V3GDAELU.js.map} +1 -1
  14. package/dist/{chunk-RLY7TZUY.js → chunk-Y475Z7BM.js} +247 -22
  15. package/dist/chunk-Y475Z7BM.js.map +1 -0
  16. package/dist/{chunk-ZK5C3OOT.js → chunk-ZKMAU6NN.js} +12 -3
  17. package/dist/chunk-ZKMAU6NN.js.map +1 -0
  18. package/dist/hooks/index.d.ts +4 -2
  19. package/dist/hooks/index.js +7 -5
  20. package/dist/hooks/mutation-mode.d.ts +40 -30
  21. package/dist/hooks/mutation-mode.js +4 -1
  22. package/dist/hooks/related-keys.d.ts +2 -2
  23. package/dist/hooks/use-capabilities.d.ts +1 -1
  24. package/dist/hooks/use-infinite-resource.d.ts +50 -0
  25. package/dist/hooks/use-infinite-resource.js +6 -0
  26. package/dist/hooks/use-infinite-resource.js.map +1 -0
  27. package/dist/hooks/use-live.d.ts +27 -0
  28. package/dist/hooks/use-live.js +5 -0
  29. package/dist/hooks/use-live.js.map +1 -0
  30. package/dist/hooks/use-one.d.ts +9 -2
  31. package/dist/hooks/use-one.js +2 -1
  32. package/dist/hooks/use-resource-form.d.ts +10 -2
  33. package/dist/hooks/use-resource-form.js +5 -3
  34. package/dist/hooks/use-resource.d.ts +14 -2
  35. package/dist/hooks/use-resource.js +4 -2
  36. package/dist/index.d.ts +6 -4
  37. package/dist/index.js +9 -7
  38. package/dist/provider/index.d.ts +59 -7
  39. package/dist/provider/index.js +1 -1
  40. package/dist/{read-outcome-BQkYcV4t.d.ts → read-outcome-C_9HbXvb.d.ts} +1 -1
  41. package/dist/schema/index.d.ts +4 -4
  42. package/dist/{types-C5bjsbxz.d.ts → types-C7FJtFXV.d.ts} +20 -0
  43. package/dist/{types-Dsqqr9s7.d.ts → types-CS9UpxQS.d.ts} +73 -4
  44. package/package.json +1 -1
  45. package/dist/chunk-DVW5GXWZ.js.map +0 -1
  46. package/dist/chunk-HT43MPKW.js +0 -3
  47. package/dist/chunk-IZT2O57C.js.map +0 -1
  48. package/dist/chunk-QH4P5MI2.js +0 -278
  49. package/dist/chunk-QH4P5MI2.js.map +0 -1
  50. package/dist/chunk-RLY7TZUY.js.map +0 -1
  51. package/dist/chunk-ZK5C3OOT.js.map +0 -1
@@ -0,0 +1,556 @@
1
+ import { capabilitiesQueryKey } from './chunk-BQOJX67X.js';
2
+
3
+ // src/hooks/mutation-mode.ts
4
+ var DEFAULT_UNDO_DELAY_MS = 5e3;
5
+ function sameId(a, b) {
6
+ return a != null && b != null && String(a) === String(b);
7
+ }
8
+ function isListResult(value) {
9
+ return typeof value === "object" && value !== null && Array.isArray(value.data);
10
+ }
11
+ function isInfiniteLists(value) {
12
+ return typeof value === "object" && value !== null && Array.isArray(value.pages) && Array.isArray(value.pageParams);
13
+ }
14
+ function mapLists(key, hash, value, fn) {
15
+ if (key[1] === "list") return isListResult(value) ? fn(value, hash) : value;
16
+ if (key[1] !== "infinite" || !isInfiniteLists(value)) return value;
17
+ let changed = false;
18
+ let rowDelta = 0;
19
+ const pages = value.pages.map((page, i) => {
20
+ if (!isListResult(page)) return page;
21
+ const next = fn(page, `${hash}#${i}`);
22
+ if (next !== page) {
23
+ changed = true;
24
+ rowDelta += next.data.length - page.data.length;
25
+ }
26
+ return next;
27
+ });
28
+ if (!changed) return value;
29
+ const withTotals = value.pages.map((orig, i) => {
30
+ const page = pages[i];
31
+ if (!isListResult(page) || !isListResult(orig) || typeof orig.total !== "number") return page;
32
+ return { ...page, total: orig.total < 0 ? orig.total : Math.max(0, orig.total + rowDelta) };
33
+ });
34
+ return { ...value, pages: withTotals };
35
+ }
36
+ function listsIn(key, value) {
37
+ if (key[1] === "list") return isListResult(value) ? [value] : [];
38
+ if (key[1] === "infinite" && isInfiniteLists(value)) return value.pages.filter(isListResult);
39
+ return [];
40
+ }
41
+ function isDetailKeyFor(key, resource, id) {
42
+ return key[0] === resource && key[1] === "detail" && sameId(key[2], id);
43
+ }
44
+ function sameValue(a, b) {
45
+ if (Object.is(a, b)) return true;
46
+ if (a instanceof Date && b instanceof Date) return a.getTime() === b.getTime();
47
+ if (typeof a !== "object" || typeof b !== "object" || a === null || b === null) return false;
48
+ if (Array.isArray(a) !== Array.isArray(b)) return false;
49
+ if (Array.isArray(a)) {
50
+ const other = b;
51
+ return a.length === other.length && a.every((item, i) => sameValue(item, other[i]));
52
+ }
53
+ const aKeys = Object.keys(a);
54
+ const bKeys = Object.keys(b);
55
+ if (aKeys.length !== bKeys.length) return false;
56
+ return aKeys.every(
57
+ (k) => Object.prototype.hasOwnProperty.call(b, k) && sameValue(a[k], b[k])
58
+ );
59
+ }
60
+ function asError(error) {
61
+ return error instanceof Error ? error : new Error(String(error));
62
+ }
63
+ function cachedRecord(queryClient, resource, id) {
64
+ for (const [key, value] of queryClient.getQueriesData({ queryKey: [resource, "detail"] })) {
65
+ if (isDetailKeyFor(key, resource, id) && value && typeof value === "object") return value;
66
+ }
67
+ for (const [key, value] of queryClient.getQueriesData({ queryKey: [resource] })) {
68
+ for (const list of listsIn(key, value)) {
69
+ const found = list.data.find((row) => sameId(row.id, id));
70
+ if (found) return found;
71
+ }
72
+ }
73
+ return void 0;
74
+ }
75
+ var registries = /* @__PURE__ */ new WeakMap();
76
+ var nextSeq = 0;
77
+ function registryFor(queryClient) {
78
+ let reg = registries.get(queryClient);
79
+ if (!reg) {
80
+ reg = {
81
+ pending: /* @__PURE__ */ new Map(),
82
+ patches: [],
83
+ writes: [],
84
+ rowTails: /* @__PURE__ */ new Map(),
85
+ produced: /* @__PURE__ */ new WeakSet(),
86
+ writing: false
87
+ };
88
+ registries.set(queryClient, reg);
89
+ }
90
+ return reg;
91
+ }
92
+ function pendingWriteCount(queryClient, resource) {
93
+ return registries.get(queryClient)?.pending.get(resource) ?? 0;
94
+ }
95
+ function beginPending(reg, resource) {
96
+ reg.pending.set(resource, (reg.pending.get(resource) ?? 0) + 1);
97
+ }
98
+ function endPending(reg, resource) {
99
+ const left = Math.max(0, (reg.pending.get(resource) ?? 0) - 1);
100
+ if (left === 0) reg.pending.delete(resource);
101
+ else reg.pending.set(resource, left);
102
+ }
103
+ function invalidateResource(queryClient, resource) {
104
+ return queryClient.invalidateQueries({ queryKey: [resource] });
105
+ }
106
+ function setProduced(reg, queryClient, key, value) {
107
+ const wasWriting = reg.writing;
108
+ reg.writing = true;
109
+ try {
110
+ queryClient.setQueryData(key, value);
111
+ } finally {
112
+ reg.writing = wasWriting;
113
+ }
114
+ const stored = queryClient.getQueryData(key);
115
+ if (stored && typeof stored === "object") reg.produced.add(stored);
116
+ }
117
+ function applyUpdate(row, data) {
118
+ return { ...row, ...data, id: row.id };
119
+ }
120
+ function revertFields(row, data, before) {
121
+ const out = { ...row };
122
+ for (const k of Object.keys(data)) {
123
+ if (k === "id") continue;
124
+ if (k in before) out[k] = before[k];
125
+ else delete out[k];
126
+ }
127
+ return out;
128
+ }
129
+ function applyPatches(key, hash, value, patches) {
130
+ if (key[1] === "list" || key[1] === "infinite") {
131
+ return mapLists(key, hash, value, (list, listHash) => {
132
+ let rows = list.data;
133
+ let total = list.total;
134
+ let changed = false;
135
+ for (const p of patches) {
136
+ const index = rows.findIndex((row) => sameId(row.id, p.id));
137
+ if (index < 0) continue;
138
+ p.before.set(listHash, { row: rows[index], index });
139
+ if (p.kind === "update") {
140
+ rows = rows.slice();
141
+ rows[index] = applyUpdate(rows[index], p.data ?? {});
142
+ } else {
143
+ rows = rows.filter((_, i) => i !== index);
144
+ if (typeof total === "number" && total >= 0) total = Math.max(0, total - 1);
145
+ }
146
+ changed = true;
147
+ }
148
+ if (!changed) return list;
149
+ return typeof total === "number" ? { ...list, data: rows, total } : { ...list, data: rows };
150
+ });
151
+ }
152
+ if (key[1] === "detail" && value && typeof value === "object") {
153
+ let row = value;
154
+ let changed = false;
155
+ for (const p of patches) {
156
+ if (p.kind !== "update" || !sameId(key[2], p.id)) continue;
157
+ p.before.set(hash, { row, index: -1 });
158
+ row = applyUpdate(row, p.data ?? {});
159
+ changed = true;
160
+ }
161
+ return changed ? row : value;
162
+ }
163
+ return value;
164
+ }
165
+ function ensureListener(reg, queryClient) {
166
+ if (reg.unsubscribe) return;
167
+ reg.unsubscribe = queryClient.getQueryCache().subscribe((event) => {
168
+ if (reg.writing || event.type !== "updated" || event.action.type !== "success") return;
169
+ const { queryKey: key, queryHash: hash } = event.query;
170
+ const resource = key[0];
171
+ if (typeof resource !== "string") return;
172
+ const data = event.query.state.data;
173
+ if (!data || typeof data !== "object" || reg.produced.has(data)) return;
174
+ const patches = reg.patches.filter((p) => p.resource === resource);
175
+ if (patches.length === 0) return;
176
+ const next = applyPatches(key, hash, data, patches);
177
+ if (next !== data) setProduced(reg, queryClient, key, next);
178
+ });
179
+ }
180
+ function addPatch(reg, queryClient, patch) {
181
+ reg.patches.push(patch);
182
+ ensureListener(reg, queryClient);
183
+ for (const query of queryClient.getQueryCache().findAll({ queryKey: [patch.resource] })) {
184
+ const value = query.state.data;
185
+ const next = applyPatches(query.queryKey, query.queryHash, value, [patch]);
186
+ if (next !== value) setProduced(reg, queryClient, query.queryKey, next);
187
+ }
188
+ }
189
+ function dropPatchRecord(reg, patch) {
190
+ const index = reg.patches.indexOf(patch);
191
+ if (index >= 0) reg.patches.splice(index, 1);
192
+ if (reg.patches.length === 0 && reg.unsubscribe) {
193
+ reg.unsubscribe();
194
+ reg.unsubscribe = void 0;
195
+ }
196
+ return index;
197
+ }
198
+ function firstBefore(patch) {
199
+ for (const value of patch.before.values()) return value;
200
+ return void 0;
201
+ }
202
+ function retractPatch(reg, queryClient, patch) {
203
+ const index = dropPatchRecord(reg, patch);
204
+ if (index < 0) return;
205
+ const onRow = (p) => p.resource === patch.resource && sameId(p.id, patch.id);
206
+ const remaining = reg.patches.filter(onRow);
207
+ if (patch.kind === "update" && patch.data) {
208
+ for (const later of reg.patches.slice(index).filter(onRow)) {
209
+ for (const [hash, b] of later.before) {
210
+ const src = patch.before.get(hash) ?? firstBefore(patch);
211
+ if (src) later.before.set(hash, { ...b, row: revertFields(b.row, patch.data, src.row) });
212
+ }
213
+ }
214
+ }
215
+ const reapply = (row) => remaining.reduce((r, p) => p.kind === "update" ? applyUpdate(r, p.data ?? {}) : r, row);
216
+ for (const query of queryClient.getQueryCache().findAll({ queryKey: [patch.resource] })) {
217
+ const key = query.queryKey;
218
+ const value = query.state.data;
219
+ let next = value;
220
+ if (key[1] === "list" || key[1] === "infinite") {
221
+ next = mapLists(key, query.queryHash, value, (list, listHash) => {
222
+ const b = patch.before.get(listHash);
223
+ const at = list.data.findIndex((row) => sameId(row.id, patch.id));
224
+ if (patch.kind === "update") {
225
+ const src = b ?? firstBefore(patch);
226
+ if (at < 0 || !src || !patch.data) return list;
227
+ const rows2 = list.data.slice();
228
+ rows2[at] = reapply(revertFields(rows2[at], patch.data, src.row));
229
+ return { ...list, data: rows2 };
230
+ }
231
+ if (at >= 0 || !b || remaining.some((p) => p.kind === "delete")) return list;
232
+ const rows = list.data.slice();
233
+ rows.splice(Math.min(b.index, rows.length), 0, reapply(b.row));
234
+ return {
235
+ ...list,
236
+ data: rows,
237
+ ...typeof list.total === "number" && list.total >= 0 ? { total: list.total + 1 } : {}
238
+ };
239
+ });
240
+ } else if (key[1] === "detail" && patch.kind === "update" && patch.data && sameId(key[2], patch.id) && value && typeof value === "object") {
241
+ const src = patch.before.get(query.queryHash) ?? firstBefore(patch);
242
+ if (!src) continue;
243
+ next = reapply(revertFields(value, patch.data, src.row));
244
+ }
245
+ if (next !== value) setProduced(reg, queryClient, key, next);
246
+ }
247
+ }
248
+ function takeBack(reg, write) {
249
+ if (write.patch) retractPatch(reg, write.queryClient, write.patch);
250
+ if (!write.rowKey || write.kind !== "update" || !write.patch?.data) return;
251
+ const undone = write.patch.data;
252
+ const before = write.baseline ?? firstBefore(write.patch)?.row ?? {};
253
+ const later = reg.writes.filter(
254
+ (w) => w.seq > write.seq && w.rowKey === write.rowKey && w.kind === "update" && (w.state === "waiting" || w.state === "queued")
255
+ );
256
+ for (const w of later) {
257
+ if (!w.data) {
258
+ w.drop();
259
+ continue;
260
+ }
261
+ const rebased = {};
262
+ const removed = [];
263
+ for (const k of Object.keys(undone)) {
264
+ if (k === "id" || !(k in w.data)) continue;
265
+ const kept = sameValue(w.data[k], undone[k]) && w.baseline != null && sameValue(w.baseline[k], undone[k]);
266
+ if (!kept) continue;
267
+ if (k in before) rebased[k] = before[k];
268
+ else removed.push(k);
269
+ }
270
+ if (Object.keys(rebased).length === 0 && removed.length === 0) continue;
271
+ Object.assign(w.data, rebased);
272
+ for (const k of removed) delete w.data[k];
273
+ if (w.baseline) w.baseline = revertFields(w.baseline, undone, before);
274
+ if (w.patch) refreshFields(reg, w.queryClient, w.patch, [...Object.keys(rebased), ...removed]);
275
+ }
276
+ }
277
+ function refreshFields(reg, queryClient, patch, keys) {
278
+ const index = reg.patches.indexOf(patch);
279
+ if (index < 0 || !patch.data) return;
280
+ const data = patch.data;
281
+ const overridden = /* @__PURE__ */ new Set();
282
+ for (const p of reg.patches.slice(index + 1)) {
283
+ if (p.resource === patch.resource && sameId(p.id, patch.id) && p.data) {
284
+ for (const k of Object.keys(p.data)) overridden.add(k);
285
+ }
286
+ }
287
+ const fields = keys.filter((k) => !overridden.has(k));
288
+ if (fields.length === 0) return;
289
+ const fix = (row, hash) => {
290
+ const out = { ...row };
291
+ const before = (patch.before.get(hash) ?? firstBefore(patch))?.row;
292
+ for (const k of fields) {
293
+ if (k in data) out[k] = data[k];
294
+ else if (before && k in before) out[k] = before[k];
295
+ else delete out[k];
296
+ }
297
+ return out;
298
+ };
299
+ for (const query of queryClient.getQueryCache().findAll({ queryKey: [patch.resource] })) {
300
+ const key = query.queryKey;
301
+ const value = query.state.data;
302
+ let next = value;
303
+ if (key[1] === "list" || key[1] === "infinite") {
304
+ next = mapLists(key, query.queryHash, value, (list, listHash) => {
305
+ const at = list.data.findIndex((row) => sameId(row.id, patch.id));
306
+ if (at < 0) return list;
307
+ const rows = list.data.slice();
308
+ rows[at] = fix(rows[at], listHash);
309
+ return { ...list, data: rows };
310
+ });
311
+ } else if (key[1] === "detail" && sameId(key[2], patch.id) && value && typeof value === "object") {
312
+ next = fix(value, query.queryHash);
313
+ }
314
+ if (next !== value) setProduced(reg, queryClient, key, next);
315
+ }
316
+ }
317
+ var allWrites = /* @__PURE__ */ new Set();
318
+ function servesKeepalive(write) {
319
+ const caps = write.queryClient.getQueryData(
320
+ capabilitiesQueryKey(write.provider, write.resource)
321
+ );
322
+ return caps?.keepaliveWrites === true;
323
+ }
324
+ function flushForUnload() {
325
+ const writes = [...allWrites].sort((a, b) => a.seq - b.seq);
326
+ for (const write of writes) {
327
+ if (write.state === "waiting" || write.state === "queued") write.flush(true);
328
+ }
329
+ }
330
+ function onBeforeUnload(event) {
331
+ flushForUnload();
332
+ const atRisk = [...allWrites].some((w) => w.state === "sending" && !(w.keepalive && servesKeepalive(w)));
333
+ if (!atRisk) return;
334
+ event.preventDefault();
335
+ event.returnValue = "";
336
+ }
337
+ function onPageHide() {
338
+ flushForUnload();
339
+ }
340
+ function track(write) {
341
+ allWrites.add(write);
342
+ if (allWrites.size === 1 && typeof window !== "undefined") {
343
+ window.addEventListener("beforeunload", onBeforeUnload);
344
+ window.addEventListener("pagehide", onPageHide);
345
+ }
346
+ }
347
+ function untrack(write) {
348
+ if (!allWrites.delete(write)) return;
349
+ if (allWrites.size === 0 && typeof window !== "undefined") {
350
+ window.removeEventListener("beforeunload", onBeforeUnload);
351
+ window.removeEventListener("pagehide", onPageHide);
352
+ }
353
+ }
354
+ var CAPABILITIES_QUERY = (provider, resource) => ({
355
+ // The same key as useCapabilities, so the answer is shared.
356
+ queryKey: capabilitiesQueryKey(provider, resource),
357
+ queryFn: () => provider.capabilities(resource),
358
+ staleTime: Infinity,
359
+ // A write may wait on this answer; an app's default retries would hold
360
+ // it for seconds.
361
+ retry: false
362
+ });
363
+ async function providerServesUndo(queryClient, provider, resource) {
364
+ if (!provider.undo || !provider.capabilities) return false;
365
+ try {
366
+ const caps = await queryClient.fetchQuery(CAPABILITIES_QUERY(provider, resource));
367
+ return caps?.undo === true;
368
+ } catch {
369
+ return false;
370
+ }
371
+ }
372
+ var DROPPED_WRITE_MESSAGE = "This change was not saved: it was made on top of an earlier change that was undone or failed. Please make it again.";
373
+ async function executeWrite(args) {
374
+ const { queryClient, provider, resource, kind, id, mode, send, committed, failedLater } = args;
375
+ if (mode === "pessimistic") {
376
+ const { record } = await send({ data: args.data, keepalive: false });
377
+ await committed(record);
378
+ return { status: "committed", record };
379
+ }
380
+ const viaProviderUndo = mode === "undoable" && await providerServesUndo(queryClient, provider, resource);
381
+ if (provider.capabilities && !queryClient.getQueryData(capabilitiesQueryKey(provider, resource))) {
382
+ void queryClient.prefetchQuery(CAPABILITIES_QUERY(provider, resource));
383
+ }
384
+ const reg = registryFor(queryClient);
385
+ const hasRow = kind !== "create" && id != null;
386
+ const data = args.data ? { ...args.data } : void 0;
387
+ let patch;
388
+ if (hasRow && kind === "update" && data) patch = { resource, id, kind: "update", data, before: /* @__PURE__ */ new Map() };
389
+ else if (hasRow && kind === "delete") patch = { resource, id, kind: "delete", before: /* @__PURE__ */ new Map() };
390
+ const delayMs = Math.max(0, args.undoDelayMs ?? DEFAULT_UNDO_DELAY_MS);
391
+ let timer;
392
+ let resolveSent = () => {
393
+ };
394
+ const sent = new Promise((resolve) => {
395
+ resolveSent = resolve;
396
+ });
397
+ const write = {
398
+ seq: ++nextSeq,
399
+ queryClient,
400
+ provider,
401
+ resource,
402
+ kind,
403
+ rowKey: hasRow ? `${resource}\0${String(id)}` : void 0,
404
+ patch,
405
+ data: kind === "update" ? data : void 0,
406
+ baseline: hasRow ? cachedRecord(queryClient, resource, id) : void 0,
407
+ state: "queued",
408
+ keepalive: false,
409
+ dropped: false,
410
+ flush: () => {
411
+ },
412
+ drop: () => {
413
+ }
414
+ };
415
+ const settle = () => {
416
+ write.state = "done";
417
+ clearTimeout(timer);
418
+ const at = reg.writes.indexOf(write);
419
+ if (at >= 0) reg.writes.splice(at, 1);
420
+ untrack(write);
421
+ endPending(reg, resource);
422
+ };
423
+ const sendNow = async (keepalive) => {
424
+ if (write.state !== "queued") return;
425
+ write.state = "sending";
426
+ write.keepalive = keepalive;
427
+ let result;
428
+ try {
429
+ result = await send({ data: write.data, keepalive });
430
+ } catch (error) {
431
+ settle();
432
+ takeBack(reg, write);
433
+ void invalidateResource(queryClient, resource);
434
+ failedLater(asError(error));
435
+ resolveSent(void 0);
436
+ return;
437
+ }
438
+ if (patch) dropPatchRecord(reg, patch);
439
+ settle();
440
+ try {
441
+ await committed(result.record);
442
+ } catch (error) {
443
+ console.error(`[genesis] after-write callback failed for "${resource}":`, error);
444
+ }
445
+ resolveSent(result);
446
+ };
447
+ const enqueue = () => {
448
+ if (!write.rowKey) {
449
+ void sendNow(false);
450
+ return;
451
+ }
452
+ const key = write.rowKey;
453
+ const previous = reg.rowTails.get(key);
454
+ const run = () => sendNow(false);
455
+ const next = previous ? previous.then(run, run) : run();
456
+ reg.rowTails.set(key, next);
457
+ void next.finally(() => {
458
+ if (reg.rowTails.get(key) === next) reg.rowTails.delete(key);
459
+ });
460
+ };
461
+ write.flush = (unloading = false) => {
462
+ if (write.state === "waiting") {
463
+ clearTimeout(timer);
464
+ if (!unloading) {
465
+ for (const earlier of reg.writes) {
466
+ if (earlier.seq < write.seq && earlier.rowKey === write.rowKey && earlier.rowKey && earlier.state === "waiting") {
467
+ earlier.flush();
468
+ }
469
+ }
470
+ }
471
+ write.state = "queued";
472
+ if (unloading) void sendNow(true);
473
+ else enqueue();
474
+ } else if (write.state === "queued" && unloading) {
475
+ void sendNow(true);
476
+ }
477
+ };
478
+ write.drop = () => {
479
+ if (write.state !== "waiting" && write.state !== "queued") return;
480
+ write.dropped = true;
481
+ settle();
482
+ takeBack(reg, write);
483
+ void invalidateResource(queryClient, resource);
484
+ failedLater(new Error(DROPPED_WRITE_MESSAGE));
485
+ resolveSent(void 0);
486
+ };
487
+ beginPending(reg, resource);
488
+ reg.writes.push(write);
489
+ track(write);
490
+ if (patch) addPatch(reg, queryClient, patch);
491
+ if (mode === "optimistic") {
492
+ enqueue();
493
+ return { status: "applied" };
494
+ }
495
+ if (viaProviderUndo) {
496
+ enqueue();
497
+ void sent.then((result) => {
498
+ const auditId = result?.audit?.id;
499
+ if (!result || !args.onUndoable) return;
500
+ if (!auditId) {
501
+ console.warn(
502
+ `[genesis] "${resource}" serves undo, but its ${kind} returned no audit entry; no undo offered.`
503
+ );
504
+ return;
505
+ }
506
+ let undone = false;
507
+ args.onUndoable({
508
+ resource,
509
+ kind,
510
+ id: id ?? result.record?.id,
511
+ strategy: "provider",
512
+ delayMs,
513
+ commit: () => {
514
+ },
515
+ undo: async () => {
516
+ if (undone) return false;
517
+ undone = true;
518
+ let ok = true;
519
+ try {
520
+ await provider.undo(resource, { auditId });
521
+ } catch (error) {
522
+ ok = false;
523
+ failedLater(asError(error));
524
+ }
525
+ await invalidateResource(queryClient, resource);
526
+ return ok;
527
+ }
528
+ });
529
+ });
530
+ return { status: "applied" };
531
+ }
532
+ write.state = "waiting";
533
+ timer = setTimeout(() => write.flush(), delayMs);
534
+ args.onUndoable?.({
535
+ resource,
536
+ kind,
537
+ id,
538
+ strategy: "delay",
539
+ delayMs,
540
+ commit: () => write.flush(),
541
+ undo: async () => {
542
+ if (write.dropped) return true;
543
+ if (write.state !== "waiting") return false;
544
+ settle();
545
+ takeBack(reg, write);
546
+ void invalidateResource(queryClient, resource);
547
+ resolveSent(void 0);
548
+ return true;
549
+ }
550
+ });
551
+ return { status: "applied" };
552
+ }
553
+
554
+ export { DEFAULT_UNDO_DELAY_MS, DROPPED_WRITE_MESSAGE, cachedRecord, executeWrite, invalidateResource, pendingWriteCount, providerServesUndo, sameValue };
555
+ //# sourceMappingURL=chunk-OBUBP4BY.js.map
556
+ //# sourceMappingURL=chunk-OBUBP4BY.js.map