@kontsedal/olas-core 0.0.1-rc.1 → 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +2 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +13 -2
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +13 -2
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +2 -2
- package/dist/index.mjs.map +1 -1
- package/dist/{root-BImHnGj1.mjs → root-BCZDC5Fv.mjs} +442 -139
- package/dist/root-BCZDC5Fv.mjs.map +1 -0
- package/dist/{root-Bazp5_Ik.cjs → root-DXV1gVbQ.cjs} +447 -138
- package/dist/root-DXV1gVbQ.cjs.map +1 -0
- package/dist/testing.cjs +1 -1
- package/dist/testing.d.cts +1 -1
- package/dist/testing.d.mts +1 -1
- package/dist/testing.mjs +1 -1
- package/dist/{types-CAMgqCMz.d.mts → types-CffZ1QXt.d.cts} +82 -10
- package/dist/types-CffZ1QXt.d.cts.map +1 -0
- package/dist/{types-emq_lZd7.d.cts → types-DSlDowpE.d.mts} +82 -10
- package/dist/types-DSlDowpE.d.mts.map +1 -0
- package/package.json +1 -1
- package/src/controller/instance.ts +115 -15
- package/src/controller/root.ts +9 -1
- package/src/controller/types.ts +17 -7
- package/src/forms/field.ts +73 -8
- package/src/forms/form-types.ts +16 -0
- package/src/forms/form.ts +171 -21
- package/src/index.ts +5 -0
- package/src/query/client.ts +161 -6
- package/src/query/define.ts +14 -0
- package/src/query/entry.ts +64 -42
- package/src/query/infinite.ts +77 -55
- package/src/query/mutation.ts +11 -21
- package/src/query/plugin.ts +50 -0
- package/src/query/use.ts +80 -3
- package/src/utils.ts +24 -0
- package/dist/root-BImHnGj1.mjs.map +0 -1
- package/dist/root-Bazp5_Ik.cjs.map +0 -1
- package/dist/types-CAMgqCMz.d.mts.map +0 -1
- package/dist/types-emq_lZd7.d.cts.map +0 -1
|
@@ -222,6 +222,29 @@ function isAbortError(err) {
|
|
|
222
222
|
if (typeof DOMException !== "undefined" && err instanceof DOMException) return err.name === "AbortError";
|
|
223
223
|
return false;
|
|
224
224
|
}
|
|
225
|
+
/**
|
|
226
|
+
* `setTimeout` wrapped in a promise that rejects with `AbortError` if the
|
|
227
|
+
* passed signal fires. Internal — used by the retry loops in `Entry`,
|
|
228
|
+
* `InfiniteEntry`, and `Mutation` so a slow backoff never blocks a supersede.
|
|
229
|
+
*/
|
|
230
|
+
function abortableSleep(ms, signal) {
|
|
231
|
+
return new Promise((resolve, reject) => {
|
|
232
|
+
if (signal.aborted) {
|
|
233
|
+
reject(new DOMException("Aborted", "AbortError"));
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
const timer = setTimeout(() => {
|
|
237
|
+
signal.removeEventListener("abort", onAbort);
|
|
238
|
+
resolve();
|
|
239
|
+
}, ms);
|
|
240
|
+
const onAbort = () => {
|
|
241
|
+
clearTimeout(timer);
|
|
242
|
+
signal.removeEventListener("abort", onAbort);
|
|
243
|
+
reject(new DOMException("Aborted", "AbortError"));
|
|
244
|
+
};
|
|
245
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
246
|
+
});
|
|
247
|
+
}
|
|
225
248
|
//#endregion
|
|
226
249
|
//#region src/query/entry.ts
|
|
227
250
|
/**
|
|
@@ -250,18 +273,37 @@ var Entry = class {
|
|
|
250
273
|
nextSnapshotId = 0;
|
|
251
274
|
disposed = false;
|
|
252
275
|
events;
|
|
276
|
+
onSuccessData;
|
|
253
277
|
fetchStartTime = 0;
|
|
278
|
+
/**
|
|
279
|
+
* Promises returned by `firstValue()` that haven't settled. Rejected on
|
|
280
|
+
* `dispose()` so awaiters (most notably `prefetch` and `subscription.firstValue`)
|
|
281
|
+
* don't hang when the controller tree is torn down mid-fetch.
|
|
282
|
+
*/
|
|
283
|
+
pendingFirstValueRejects = [];
|
|
254
284
|
constructor(options) {
|
|
255
285
|
this.fetcherProvider = options.fetcher;
|
|
256
286
|
this.staleTime = options.staleTime ?? 0;
|
|
257
287
|
this.retry = options.retry ?? 0;
|
|
258
288
|
this.retryDelay = options.retryDelay ?? 1e3;
|
|
259
289
|
this.events = options.events ?? {};
|
|
290
|
+
this.onSuccessData = options.onSuccessData;
|
|
260
291
|
this.data = signal(options.initialData);
|
|
261
292
|
if (options.initialData !== void 0) {
|
|
262
293
|
this.status = signal("success");
|
|
263
|
-
this.
|
|
264
|
-
|
|
294
|
+
if (this.staleTime === 0) this.isStale.set(true);
|
|
295
|
+
else {
|
|
296
|
+
const last = options.initialUpdatedAt;
|
|
297
|
+
const alreadyStale = last === void 0 || Date.now() - last >= this.staleTime;
|
|
298
|
+
this.isStale.set(alreadyStale);
|
|
299
|
+
if (!alreadyStale) {
|
|
300
|
+
const remaining = this.staleTime - (Date.now() - last);
|
|
301
|
+
this.staleTimer = setTimeout(() => {
|
|
302
|
+
this.staleTimer = null;
|
|
303
|
+
if (!this.disposed) this.isStale.set(true);
|
|
304
|
+
}, remaining);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
265
307
|
} else this.status = signal("idle");
|
|
266
308
|
this.lastUpdatedAt = signal(options.initialUpdatedAt);
|
|
267
309
|
}
|
|
@@ -294,7 +336,7 @@ var Entry = class {
|
|
|
294
336
|
} catch (err) {
|
|
295
337
|
if (myId !== this.currentFetchId || this.disposed || isAbortError(err)) throw err;
|
|
296
338
|
if (!this.shouldRetry(attempt, err)) return this.applyFailure(err);
|
|
297
|
-
await abortableSleep
|
|
339
|
+
await abortableSleep(this.computeDelay(attempt), abort.signal);
|
|
298
340
|
attempt += 1;
|
|
299
341
|
}
|
|
300
342
|
}
|
|
@@ -323,6 +365,7 @@ var Entry = class {
|
|
|
323
365
|
try {
|
|
324
366
|
this.events.onFetchSuccess?.(Date.now() - this.fetchStartTime);
|
|
325
367
|
} catch {}
|
|
368
|
+
this.onSuccessData?.(result);
|
|
326
369
|
return result;
|
|
327
370
|
}
|
|
328
371
|
applyFailure(err) {
|
|
@@ -401,26 +444,24 @@ var Entry = class {
|
|
|
401
444
|
}
|
|
402
445
|
};
|
|
403
446
|
}
|
|
404
|
-
finalizeSnapshot(snapshot) {
|
|
405
|
-
const id = snapshotIds.get(snapshot);
|
|
406
|
-
if (id === void 0) return;
|
|
407
|
-
const record = this.snapshots.find((s) => s.live && s.id === id);
|
|
408
|
-
if (!record) return;
|
|
409
|
-
record.live = false;
|
|
410
|
-
this.snapshots = this.snapshots.filter((s) => s !== record);
|
|
411
|
-
if (!this.snapshots.some((s) => s.live)) this.hasPendingMutations.set(false);
|
|
412
|
-
}
|
|
413
447
|
firstValue() {
|
|
448
|
+
if (this.disposed) return Promise.reject(new DOMException("Entry disposed", "AbortError"));
|
|
414
449
|
if (this.status.peek() === "success") return Promise.resolve(this.data.peek());
|
|
415
450
|
if (this.status.peek() === "error") return Promise.reject(this.error.peek());
|
|
416
451
|
return new Promise((resolve, reject) => {
|
|
452
|
+
const tracked = (err) => {
|
|
453
|
+
this.pendingFirstValueRejects = this.pendingFirstValueRejects.filter((f) => f !== tracked);
|
|
454
|
+
reject(err);
|
|
455
|
+
};
|
|
456
|
+
this.pendingFirstValueRejects.push(tracked);
|
|
417
457
|
const unsub = this.status.subscribe((s) => {
|
|
418
458
|
if (s === "success") {
|
|
419
459
|
unsub();
|
|
460
|
+
this.pendingFirstValueRejects = this.pendingFirstValueRejects.filter((f) => f !== tracked);
|
|
420
461
|
resolve(this.data.peek());
|
|
421
462
|
} else if (s === "error") {
|
|
422
463
|
unsub();
|
|
423
|
-
|
|
464
|
+
tracked(this.error.peek());
|
|
424
465
|
}
|
|
425
466
|
});
|
|
426
467
|
});
|
|
@@ -443,27 +484,14 @@ var Entry = class {
|
|
|
443
484
|
}
|
|
444
485
|
this.currentAbort?.abort();
|
|
445
486
|
this.currentAbort = null;
|
|
487
|
+
if (this.pendingFirstValueRejects.length > 0) {
|
|
488
|
+
const disposed = new DOMException("Entry disposed", "AbortError");
|
|
489
|
+
const rejects = this.pendingFirstValueRejects;
|
|
490
|
+
this.pendingFirstValueRejects = [];
|
|
491
|
+
for (const fn of rejects) fn(disposed);
|
|
492
|
+
}
|
|
446
493
|
}
|
|
447
494
|
};
|
|
448
|
-
const snapshotIds = /* @__PURE__ */ new WeakMap();
|
|
449
|
-
function abortableSleep$2(ms, signal) {
|
|
450
|
-
return new Promise((resolve, reject) => {
|
|
451
|
-
if (signal.aborted) {
|
|
452
|
-
reject(new DOMException("Aborted", "AbortError"));
|
|
453
|
-
return;
|
|
454
|
-
}
|
|
455
|
-
const timer = setTimeout(() => {
|
|
456
|
-
signal.removeEventListener("abort", onAbort);
|
|
457
|
-
resolve();
|
|
458
|
-
}, ms);
|
|
459
|
-
const onAbort = () => {
|
|
460
|
-
clearTimeout(timer);
|
|
461
|
-
signal.removeEventListener("abort", onAbort);
|
|
462
|
-
reject(new DOMException("Aborted", "AbortError"));
|
|
463
|
-
};
|
|
464
|
-
signal.addEventListener("abort", onAbort, { once: true });
|
|
465
|
-
});
|
|
466
|
-
}
|
|
467
495
|
//#endregion
|
|
468
496
|
//#region src/query/focus-online.ts
|
|
469
497
|
const focusSubs = /* @__PURE__ */ new Set();
|
|
@@ -539,6 +567,8 @@ var InfiniteEntry = class {
|
|
|
539
567
|
snapshots = [];
|
|
540
568
|
nextSnapshotId = 0;
|
|
541
569
|
disposed = false;
|
|
570
|
+
/** Mirrors `Entry.pendingFirstValueRejects` — see that field for context. */
|
|
571
|
+
pendingFirstValueRejects = [];
|
|
542
572
|
fetcher;
|
|
543
573
|
initialPageParam;
|
|
544
574
|
getNextPageParam;
|
|
@@ -547,6 +577,13 @@ var InfiniteEntry = class {
|
|
|
547
577
|
retry;
|
|
548
578
|
retryDelay;
|
|
549
579
|
itemsOf;
|
|
580
|
+
/**
|
|
581
|
+
* Mirrors `Entry.onSuccessData`. Fires from `applyFetchSuccess`-equivalent
|
|
582
|
+
* branches AFTER `pages.set(...)` settles. Used by `InfiniteClientEntry`
|
|
583
|
+
* to emit `SetDataEvent { kind: 'infinite', source: 'fetch' }` for
|
|
584
|
+
* `QueryClientPlugin`s (e.g. entity normalization).
|
|
585
|
+
*/
|
|
586
|
+
onSuccessData;
|
|
550
587
|
constructor(opts) {
|
|
551
588
|
this.fetcher = opts.fetcher;
|
|
552
589
|
this.initialPageParam = opts.initialPageParam;
|
|
@@ -556,6 +593,7 @@ var InfiniteEntry = class {
|
|
|
556
593
|
this.staleTime = opts.staleTime ?? 0;
|
|
557
594
|
this.retry = opts.retry ?? 0;
|
|
558
595
|
this.retryDelay = opts.retryDelay ?? 1e3;
|
|
596
|
+
this.onSuccessData = opts.onSuccessData;
|
|
559
597
|
this.pageParams = signal([]);
|
|
560
598
|
this.data = computed(() => {
|
|
561
599
|
const ps = this.pages.value;
|
|
@@ -607,6 +645,7 @@ var InfiniteEntry = class {
|
|
|
607
645
|
this.isStale.set(this.staleTime === 0);
|
|
608
646
|
});
|
|
609
647
|
if (this.staleTime > 0) this.scheduleStaleness();
|
|
648
|
+
this.onSuccessData?.(this.pages.peek());
|
|
610
649
|
}, "initial");
|
|
611
650
|
}
|
|
612
651
|
fetchNextPage() {
|
|
@@ -633,6 +672,7 @@ var InfiniteEntry = class {
|
|
|
633
672
|
this.isFetching.set(false);
|
|
634
673
|
this.lastUpdatedAt.set(Date.now());
|
|
635
674
|
});
|
|
675
|
+
this.onSuccessData?.(this.pages.peek());
|
|
636
676
|
}, "next").then(() => {});
|
|
637
677
|
}
|
|
638
678
|
fetchPreviousPage() {
|
|
@@ -660,36 +700,46 @@ var InfiniteEntry = class {
|
|
|
660
700
|
this.isFetching.set(false);
|
|
661
701
|
this.lastUpdatedAt.set(Date.now());
|
|
662
702
|
});
|
|
703
|
+
this.onSuccessData?.(this.pages.peek());
|
|
663
704
|
}, "prev").then(() => {});
|
|
664
705
|
}
|
|
665
706
|
async runFetch(myId, signal, pageParam, onSuccess, direction) {
|
|
666
707
|
let attempt = 0;
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
const page = await this.fetcher({
|
|
671
|
-
pageParam,
|
|
672
|
-
signal
|
|
673
|
-
});
|
|
708
|
+
let succeeded = false;
|
|
709
|
+
try {
|
|
710
|
+
while (true) {
|
|
674
711
|
if (myId !== this.currentFetchId || this.disposed) throw new DOMException("Superseded", "AbortError");
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
if (!(typeof this.retry === "number" ? attempt < this.retry : this.retry(attempt, err))) {
|
|
680
|
-
batch(() => {
|
|
681
|
-
this.error.set(err);
|
|
682
|
-
this.status.set("error");
|
|
683
|
-
this.isLoading.set(false);
|
|
684
|
-
this.isFetching.set(false);
|
|
685
|
-
if (direction === "next") this.isFetchingNextPage.set(false);
|
|
686
|
-
if (direction === "prev") this.isFetchingPreviousPage.set(false);
|
|
712
|
+
try {
|
|
713
|
+
const page = await this.fetcher({
|
|
714
|
+
pageParam,
|
|
715
|
+
signal
|
|
687
716
|
});
|
|
688
|
-
throw
|
|
717
|
+
if (myId !== this.currentFetchId || this.disposed) throw new DOMException("Superseded", "AbortError");
|
|
718
|
+
onSuccess(page, pageParam);
|
|
719
|
+
succeeded = true;
|
|
720
|
+
return page;
|
|
721
|
+
} catch (err) {
|
|
722
|
+
if (myId !== this.currentFetchId || this.disposed || isAbortError(err)) throw err;
|
|
723
|
+
if (!(typeof this.retry === "number" ? attempt < this.retry : this.retry(attempt, err))) {
|
|
724
|
+
batch(() => {
|
|
725
|
+
this.error.set(err);
|
|
726
|
+
this.status.set("error");
|
|
727
|
+
this.isLoading.set(false);
|
|
728
|
+
this.isFetching.set(false);
|
|
729
|
+
if (direction === "next") this.isFetchingNextPage.set(false);
|
|
730
|
+
if (direction === "prev") this.isFetchingPreviousPage.set(false);
|
|
731
|
+
});
|
|
732
|
+
throw err;
|
|
733
|
+
}
|
|
734
|
+
await abortableSleep(typeof this.retryDelay === "function" ? this.retryDelay(attempt) : this.retryDelay, signal);
|
|
735
|
+
attempt += 1;
|
|
689
736
|
}
|
|
690
|
-
await abortableSleep$1(typeof this.retryDelay === "function" ? this.retryDelay(attempt) : this.retryDelay, signal);
|
|
691
|
-
attempt += 1;
|
|
692
737
|
}
|
|
738
|
+
} finally {
|
|
739
|
+
if (!succeeded) batch(() => {
|
|
740
|
+
if (direction === "next") this.isFetchingNextPage.set(false);
|
|
741
|
+
if (direction === "prev") this.isFetchingPreviousPage.set(false);
|
|
742
|
+
});
|
|
693
743
|
}
|
|
694
744
|
}
|
|
695
745
|
refetch() {
|
|
@@ -749,16 +799,23 @@ var InfiniteEntry = class {
|
|
|
749
799
|
};
|
|
750
800
|
}
|
|
751
801
|
firstValue() {
|
|
802
|
+
if (this.disposed) return Promise.reject(new DOMException("Entry disposed", "AbortError"));
|
|
752
803
|
if (this.status.peek() === "success") return Promise.resolve(this.pages.peek());
|
|
753
804
|
if (this.status.peek() === "error") return Promise.reject(this.error.peek());
|
|
754
805
|
return new Promise((resolve, reject) => {
|
|
806
|
+
const tracked = (err) => {
|
|
807
|
+
this.pendingFirstValueRejects = this.pendingFirstValueRejects.filter((f) => f !== tracked);
|
|
808
|
+
reject(err);
|
|
809
|
+
};
|
|
810
|
+
this.pendingFirstValueRejects.push(tracked);
|
|
755
811
|
const unsub = this.status.subscribe((s) => {
|
|
756
812
|
if (s === "success") {
|
|
757
813
|
unsub();
|
|
814
|
+
this.pendingFirstValueRejects = this.pendingFirstValueRejects.filter((f) => f !== tracked);
|
|
758
815
|
resolve(this.pages.peek());
|
|
759
816
|
} else if (s === "error") {
|
|
760
817
|
unsub();
|
|
761
|
-
|
|
818
|
+
tracked(this.error.peek());
|
|
762
819
|
}
|
|
763
820
|
});
|
|
764
821
|
});
|
|
@@ -784,26 +841,14 @@ var InfiniteEntry = class {
|
|
|
784
841
|
}
|
|
785
842
|
this.currentAbort?.abort();
|
|
786
843
|
this.currentAbort = null;
|
|
844
|
+
if (this.pendingFirstValueRejects.length > 0) {
|
|
845
|
+
const disposed = new DOMException("Entry disposed", "AbortError");
|
|
846
|
+
const rejects = this.pendingFirstValueRejects;
|
|
847
|
+
this.pendingFirstValueRejects = [];
|
|
848
|
+
for (const fn of rejects) fn(disposed);
|
|
849
|
+
}
|
|
787
850
|
}
|
|
788
851
|
};
|
|
789
|
-
function abortableSleep$1(ms, signal) {
|
|
790
|
-
return new Promise((resolve, reject) => {
|
|
791
|
-
if (signal.aborted) {
|
|
792
|
-
reject(new DOMException("Aborted", "AbortError"));
|
|
793
|
-
return;
|
|
794
|
-
}
|
|
795
|
-
const timer = setTimeout(() => {
|
|
796
|
-
signal.removeEventListener("abort", onAbort);
|
|
797
|
-
resolve();
|
|
798
|
-
}, ms);
|
|
799
|
-
const onAbort = () => {
|
|
800
|
-
clearTimeout(timer);
|
|
801
|
-
signal.removeEventListener("abort", onAbort);
|
|
802
|
-
reject(new DOMException("Aborted", "AbortError"));
|
|
803
|
-
};
|
|
804
|
-
signal.addEventListener("abort", onAbort, { once: true });
|
|
805
|
-
});
|
|
806
|
-
}
|
|
807
852
|
//#endregion
|
|
808
853
|
//#region src/query/keys.ts
|
|
809
854
|
/**
|
|
@@ -870,7 +915,7 @@ var ClientEntry = class {
|
|
|
870
915
|
refetchInterval;
|
|
871
916
|
refetchOnWindowFocus;
|
|
872
917
|
refetchOnReconnect;
|
|
873
|
-
constructor(client, query, callArgs, keyArgs, spec, hydrated) {
|
|
918
|
+
constructor(client, query, callArgs, keyArgs, spec, hydrated, onFetchSuccess) {
|
|
874
919
|
this.client = client;
|
|
875
920
|
this.query = query;
|
|
876
921
|
this.callArgs = callArgs;
|
|
@@ -893,7 +938,8 @@ var ClientEntry = class {
|
|
|
893
938
|
retryDelay: spec.retryDelay,
|
|
894
939
|
initialData: hydrated?.data,
|
|
895
940
|
initialUpdatedAt: hydrated?.lastUpdatedAt,
|
|
896
|
-
events: void 0
|
|
941
|
+
events: void 0,
|
|
942
|
+
onSuccessData: onFetchSuccess
|
|
897
943
|
});
|
|
898
944
|
}
|
|
899
945
|
acquire() {
|
|
@@ -993,7 +1039,7 @@ var InfiniteClientEntry = class {
|
|
|
993
1039
|
intervalTimer = null;
|
|
994
1040
|
gcTime;
|
|
995
1041
|
refetchInterval;
|
|
996
|
-
constructor(client, query, callArgs, keyArgs, spec) {
|
|
1042
|
+
constructor(client, query, callArgs, keyArgs, spec, onFetchSuccess) {
|
|
997
1043
|
this.client = client;
|
|
998
1044
|
this.query = query;
|
|
999
1045
|
this.callArgs = callArgs;
|
|
@@ -1014,7 +1060,8 @@ var InfiniteClientEntry = class {
|
|
|
1014
1060
|
itemsOf: spec.itemsOf,
|
|
1015
1061
|
staleTime: spec.staleTime,
|
|
1016
1062
|
retry: spec.retry,
|
|
1017
|
-
retryDelay: spec.retryDelay
|
|
1063
|
+
retryDelay: spec.retryDelay,
|
|
1064
|
+
onSuccessData: onFetchSuccess
|
|
1018
1065
|
});
|
|
1019
1066
|
}
|
|
1020
1067
|
acquire() {
|
|
@@ -1130,6 +1177,9 @@ var QueryClient = class {
|
|
|
1130
1177
|
applyRemoteInvalidate(queryId, keyArgs) {
|
|
1131
1178
|
self.applyRemoteInvalidate(queryId, keyArgs);
|
|
1132
1179
|
},
|
|
1180
|
+
setEntryData(queryId, keyArgs, updater) {
|
|
1181
|
+
self.setEntryData(queryId, keyArgs, updater);
|
|
1182
|
+
},
|
|
1133
1183
|
subscribedKeys(queryId) {
|
|
1134
1184
|
return self.subscribedKeysFor(queryId);
|
|
1135
1185
|
}
|
|
@@ -1146,7 +1196,24 @@ var QueryClient = class {
|
|
|
1146
1196
|
});
|
|
1147
1197
|
}
|
|
1148
1198
|
}
|
|
1149
|
-
|
|
1199
|
+
/**
|
|
1200
|
+
* Emit a `SetDataEvent` to every installed plugin. The `source` field
|
|
1201
|
+
* tells layered plugins where the write originated:
|
|
1202
|
+
* - `'set'`: explicit `client.setData`, including mutations and plugin-
|
|
1203
|
+
* initiated `setEntryData` calls (e.g. entity backpropagation).
|
|
1204
|
+
* - `'fetch'`: a query fetcher resolved successfully (`Entry.applySuccess`
|
|
1205
|
+
* reaches this through `onSuccessData`), or hydrated data was first
|
|
1206
|
+
* bound (a per-tab arrival of pre-fetched data; cross-tab skips
|
|
1207
|
+
* `'fetch'` so this stays a per-tab concern).
|
|
1208
|
+
* - `'remote'`: `applyRemoteSetData` — cross-tab / server-push. Mirrors
|
|
1209
|
+
* `isRemote === true`.
|
|
1210
|
+
*
|
|
1211
|
+
* Private — fetcher-success emission goes through the `onFetchSuccess`
|
|
1212
|
+
* closure that `bindEntry` builds and hands to each new `ClientEntry`.
|
|
1213
|
+
* Hydrated emission goes through this method directly from `bindEntry`.
|
|
1214
|
+
* Mutation / remote paths call it from within QueryClient methods.
|
|
1215
|
+
*/
|
|
1216
|
+
emitSetData(query, keyArgs, data, kind, source) {
|
|
1150
1217
|
if (this.plugins.length === 0) return;
|
|
1151
1218
|
const queryId = query.__spec.queryId;
|
|
1152
1219
|
if (queryId == null) return;
|
|
@@ -1155,7 +1222,8 @@ var QueryClient = class {
|
|
|
1155
1222
|
keyArgs,
|
|
1156
1223
|
data,
|
|
1157
1224
|
kind,
|
|
1158
|
-
isRemote: this.applyingRemote
|
|
1225
|
+
isRemote: this.applyingRemote,
|
|
1226
|
+
source
|
|
1159
1227
|
};
|
|
1160
1228
|
for (const plugin of this.plugins) if (plugin.onSetData) {
|
|
1161
1229
|
const cb = plugin.onSetData;
|
|
@@ -1233,11 +1301,44 @@ var QueryClient = class {
|
|
|
1233
1301
|
this.applyingRemote = true;
|
|
1234
1302
|
try {
|
|
1235
1303
|
entry.entry.setData(() => data);
|
|
1236
|
-
this.emitSetData(internal, entry.keyArgs, data, "data");
|
|
1304
|
+
this.emitSetData(internal, entry.keyArgs, data, "data", "remote");
|
|
1237
1305
|
} finally {
|
|
1238
1306
|
this.applyingRemote = false;
|
|
1239
1307
|
}
|
|
1240
1308
|
}
|
|
1309
|
+
/**
|
|
1310
|
+
* Local-originated `setData` keyed by `queryId + keyArgs`. Plugin-facing
|
|
1311
|
+
* (exposed via `QueryClientPluginApi.setEntryData`); used by the
|
|
1312
|
+
* `@kontsedal/olas-entities` plugin to backpropagate entity patches into
|
|
1313
|
+
* every query holding the entity, without forcing the plugin to recover
|
|
1314
|
+
* the original `callArgs`.
|
|
1315
|
+
*
|
|
1316
|
+
* Drops silently in the same cases as `applyRemoteSetData` (unknown
|
|
1317
|
+
* queryId / infinite query / no local entry). Emits `SetDataEvent` with
|
|
1318
|
+
* `isRemote: false`, `source: 'set'` — cross-tab WILL rebroadcast.
|
|
1319
|
+
*/
|
|
1320
|
+
setEntryData(queryId, keyArgs, updater) {
|
|
1321
|
+
const query = lookupRegisteredQuery(queryId);
|
|
1322
|
+
if (!query) return;
|
|
1323
|
+
const hash = stableHash(keyArgs);
|
|
1324
|
+
if (query.__olas === "query") {
|
|
1325
|
+
const internal = query;
|
|
1326
|
+
const map = this.maps.get(internal);
|
|
1327
|
+
if (!map) return;
|
|
1328
|
+
const entry = map.get(hash);
|
|
1329
|
+
if (!entry) return;
|
|
1330
|
+
entry.entry.setData(updater);
|
|
1331
|
+
this.emitSetData(internal, entry.keyArgs, entry.entry.data.peek(), "data", "set");
|
|
1332
|
+
return;
|
|
1333
|
+
}
|
|
1334
|
+
const internal = query;
|
|
1335
|
+
const map = this.infiniteMaps.get(internal);
|
|
1336
|
+
if (!map) return;
|
|
1337
|
+
const entry = map.get(hash);
|
|
1338
|
+
if (!entry) return;
|
|
1339
|
+
entry.entry.setData(updater);
|
|
1340
|
+
this.emitSetData(internal, entry.keyArgs, entry.entry.pages.peek(), "infinite", "set");
|
|
1341
|
+
}
|
|
1241
1342
|
applyRemoteInvalidate(queryId, keyArgs) {
|
|
1242
1343
|
const query = lookupRegisteredQuery(queryId);
|
|
1243
1344
|
if (!query) return;
|
|
@@ -1251,6 +1352,7 @@ var QueryClient = class {
|
|
|
1251
1352
|
this.applyingRemote = true;
|
|
1252
1353
|
try {
|
|
1253
1354
|
entry.entry.invalidate().catch((err) => {
|
|
1355
|
+
if (isAbortError(err)) return;
|
|
1254
1356
|
dispatchError(this.onError, err, {
|
|
1255
1357
|
kind: "cache",
|
|
1256
1358
|
controllerPath: [],
|
|
@@ -1346,9 +1448,11 @@ var QueryClient = class {
|
|
|
1346
1448
|
if (!entry) {
|
|
1347
1449
|
const hydrated = this.hydratedData.get(hash);
|
|
1348
1450
|
if (hydrated) this.hydratedData.delete(hash);
|
|
1349
|
-
|
|
1451
|
+
const onFetchSuccess = internal.__spec.queryId != null ? (data) => this.emitSetData(internal, keyArgs, data, "data", "fetch") : void 0;
|
|
1452
|
+
entry = new ClientEntry(this, internal, args, keyArgs, internal.__spec, hydrated, onFetchSuccess);
|
|
1350
1453
|
map.set(hash, entry);
|
|
1351
1454
|
entry.scheduleGcIfOrphan();
|
|
1455
|
+
if (hydrated !== void 0) this.emitSetData(internal, keyArgs, hydrated.data, "data", "fetch");
|
|
1352
1456
|
}
|
|
1353
1457
|
return entry;
|
|
1354
1458
|
}
|
|
@@ -1371,6 +1475,7 @@ var QueryClient = class {
|
|
|
1371
1475
|
const entry = map.get(hash);
|
|
1372
1476
|
if (!entry) return;
|
|
1373
1477
|
entry.entry.invalidate().catch((err) => {
|
|
1478
|
+
if (isAbortError(err)) return;
|
|
1374
1479
|
dispatchError(this.onError, err, {
|
|
1375
1480
|
kind: "cache",
|
|
1376
1481
|
controllerPath: [],
|
|
@@ -1385,6 +1490,7 @@ var QueryClient = class {
|
|
|
1385
1490
|
if (!map) return;
|
|
1386
1491
|
for (const [hash, entry] of map) {
|
|
1387
1492
|
entry.entry.invalidate().catch((err) => {
|
|
1493
|
+
if (isAbortError(err)) return;
|
|
1388
1494
|
dispatchError(this.onError, err, {
|
|
1389
1495
|
kind: "cache",
|
|
1390
1496
|
controllerPath: [],
|
|
@@ -1397,7 +1503,7 @@ var QueryClient = class {
|
|
|
1397
1503
|
setData(query, args, updater) {
|
|
1398
1504
|
const entry = this.bindEntry(query, args);
|
|
1399
1505
|
const snapshot = entry.entry.setData(updater);
|
|
1400
|
-
this.emitSetData(entry.query, entry.keyArgs, entry.entry.data.peek(), "data");
|
|
1506
|
+
this.emitSetData(entry.query, entry.keyArgs, entry.entry.data.peek(), "data", "set");
|
|
1401
1507
|
return snapshot;
|
|
1402
1508
|
}
|
|
1403
1509
|
bindInfiniteEntry(query, args) {
|
|
@@ -1413,7 +1519,8 @@ var QueryClient = class {
|
|
|
1413
1519
|
const hash = stableHash(keyArgs);
|
|
1414
1520
|
let entry = map.get(hash);
|
|
1415
1521
|
if (!entry) {
|
|
1416
|
-
|
|
1522
|
+
const onFetchSuccess = internal.__spec.queryId != null ? (pages) => this.emitSetData(internal, keyArgs, pages, "infinite", "fetch") : void 0;
|
|
1523
|
+
entry = new InfiniteClientEntry(this, internal, args, keyArgs, internal.__spec, onFetchSuccess);
|
|
1417
1524
|
map.set(hash, entry);
|
|
1418
1525
|
entry.scheduleGcIfOrphan();
|
|
1419
1526
|
}
|
|
@@ -1438,6 +1545,7 @@ var QueryClient = class {
|
|
|
1438
1545
|
const entry = map.get(hash);
|
|
1439
1546
|
if (!entry) return;
|
|
1440
1547
|
entry.entry.invalidate().catch((err) => {
|
|
1548
|
+
if (isAbortError(err)) return;
|
|
1441
1549
|
dispatchError(this.onError, err, {
|
|
1442
1550
|
kind: "cache",
|
|
1443
1551
|
controllerPath: [],
|
|
@@ -1452,6 +1560,7 @@ var QueryClient = class {
|
|
|
1452
1560
|
if (!map) return;
|
|
1453
1561
|
for (const entry of map.values()) {
|
|
1454
1562
|
entry.entry.invalidate().catch((err) => {
|
|
1563
|
+
if (isAbortError(err)) return;
|
|
1455
1564
|
dispatchError(this.onError, err, {
|
|
1456
1565
|
kind: "cache",
|
|
1457
1566
|
controllerPath: [],
|
|
@@ -1464,7 +1573,7 @@ var QueryClient = class {
|
|
|
1464
1573
|
setInfiniteData(query, args, updater) {
|
|
1465
1574
|
const entry = this.bindInfiniteEntry(query, args);
|
|
1466
1575
|
const snapshot = entry.entry.setData(updater);
|
|
1467
|
-
this.emitSetData(entry.query, entry.keyArgs, entry.entry.pages.peek(), "infinite");
|
|
1576
|
+
this.emitSetData(entry.query, entry.keyArgs, entry.entry.pages.peek(), "infinite", "set");
|
|
1468
1577
|
return snapshot;
|
|
1469
1578
|
}
|
|
1470
1579
|
prefetchInfinite(query, args) {
|
|
@@ -1587,9 +1696,11 @@ var FieldImpl = class {
|
|
|
1587
1696
|
runId = 0;
|
|
1588
1697
|
disposed = false;
|
|
1589
1698
|
devtoolsOwner = null;
|
|
1590
|
-
|
|
1699
|
+
onValidatorError = null;
|
|
1700
|
+
constructor(initial, validators = [], options) {
|
|
1591
1701
|
this.initial = initial;
|
|
1592
1702
|
this.validators = validators;
|
|
1703
|
+
this.onValidatorError = options?.onValidatorError ?? null;
|
|
1593
1704
|
this.value$ = signal(initial);
|
|
1594
1705
|
this.errors$ = signal([]);
|
|
1595
1706
|
this.touched$ = signal(false);
|
|
@@ -1601,6 +1712,13 @@ var FieldImpl = class {
|
|
|
1601
1712
|
this.runValidators();
|
|
1602
1713
|
});
|
|
1603
1714
|
}
|
|
1715
|
+
/**
|
|
1716
|
+
* Internal hook for `ctx.field` / `createForm` to route synchronous
|
|
1717
|
+
* validator throws through `root.onError`. See `ValidatorErrorReporter`.
|
|
1718
|
+
*/
|
|
1719
|
+
bindValidatorErrorReporter(reporter) {
|
|
1720
|
+
this.onValidatorError = reporter;
|
|
1721
|
+
}
|
|
1604
1722
|
get value() {
|
|
1605
1723
|
return this.value$.value;
|
|
1606
1724
|
}
|
|
@@ -1707,10 +1825,15 @@ var FieldImpl = class {
|
|
|
1707
1825
|
const myId = ++this.runId;
|
|
1708
1826
|
const syncErrors = [];
|
|
1709
1827
|
const asyncPromises = [];
|
|
1710
|
-
for (const validator of this.validators) {
|
|
1828
|
+
for (const validator of this.validators) try {
|
|
1711
1829
|
const result = validator(value, abort.signal);
|
|
1712
1830
|
if (result instanceof Promise) asyncPromises.push(result);
|
|
1713
1831
|
else if (result != null) syncErrors.push(result);
|
|
1832
|
+
} catch (err) {
|
|
1833
|
+
try {
|
|
1834
|
+
this.onValidatorError?.(err);
|
|
1835
|
+
} catch {}
|
|
1836
|
+
syncErrors.push(err instanceof Error ? err.message : String(err));
|
|
1714
1837
|
}
|
|
1715
1838
|
if (syncErrors.length > 0) {
|
|
1716
1839
|
batch(() => {
|
|
@@ -1758,8 +1881,18 @@ function bindFieldDevtoolsOwner(field, owner) {
|
|
|
1758
1881
|
const impl = field;
|
|
1759
1882
|
if (typeof impl.bindDevtoolsOwner === "function") impl.bindDevtoolsOwner(owner);
|
|
1760
1883
|
}
|
|
1761
|
-
|
|
1762
|
-
|
|
1884
|
+
/**
|
|
1885
|
+
* Internal — install a synchronous-validator-throw reporter on a `Field`
|
|
1886
|
+
* (matched structurally to keep the public `Field<T>` surface stable).
|
|
1887
|
+
* Called by `ctx.field` and `bindTreeToDevtools` so leaves inside a form/
|
|
1888
|
+
* field-array tree get the same reporting as a standalone field.
|
|
1889
|
+
*/
|
|
1890
|
+
function bindFieldValidatorErrorReporter(field, reporter) {
|
|
1891
|
+
const impl = field;
|
|
1892
|
+
if (typeof impl.bindValidatorErrorReporter === "function") impl.bindValidatorErrorReporter(reporter);
|
|
1893
|
+
}
|
|
1894
|
+
function createField(initial, validators, options) {
|
|
1895
|
+
return new FieldImpl(initial, validators, options);
|
|
1763
1896
|
}
|
|
1764
1897
|
/**
|
|
1765
1898
|
* Wrap an async validator with a debounce. The debounce timer resets on every
|
|
@@ -1806,17 +1939,40 @@ var FormImpl = class {
|
|
|
1806
1939
|
validators;
|
|
1807
1940
|
options;
|
|
1808
1941
|
validatorDispose = null;
|
|
1942
|
+
initialDispose = null;
|
|
1809
1943
|
currentValidatorRun = 0;
|
|
1810
1944
|
currentValidatorAbort = null;
|
|
1811
1945
|
disposed = false;
|
|
1812
|
-
|
|
1946
|
+
onValidatorError = null;
|
|
1947
|
+
/** Internal — wire a sync-throw reporter for the top-level validators. */
|
|
1948
|
+
bindValidatorErrorReporter(reporter) {
|
|
1949
|
+
this.onValidatorError = reporter;
|
|
1950
|
+
}
|
|
1951
|
+
constructor(schema, options, internalOptions) {
|
|
1813
1952
|
this.fields = schema;
|
|
1814
1953
|
this.options = options;
|
|
1815
1954
|
this.validators = options?.validators ?? [];
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1955
|
+
this.onValidatorError = internalOptions?.onValidatorError ?? null;
|
|
1956
|
+
if (options?.initial !== void 0) if (typeof options.initial === "function") {
|
|
1957
|
+
const initialFn = options.initial;
|
|
1958
|
+
const mode = options.resetOnInitialChange ?? "when-clean";
|
|
1959
|
+
let firstRun = true;
|
|
1960
|
+
this.initialDispose = effect(() => {
|
|
1961
|
+
const ini = initialFn();
|
|
1962
|
+
if (ini === void 0) return;
|
|
1963
|
+
untracked(() => {
|
|
1964
|
+
if (this.disposed) return;
|
|
1965
|
+
if (firstRun) {
|
|
1966
|
+
firstRun = false;
|
|
1967
|
+
this.applyPartial(ini, true);
|
|
1968
|
+
return;
|
|
1969
|
+
}
|
|
1970
|
+
if (mode === "never") return;
|
|
1971
|
+
if (mode === "when-clean" && this.isDirty.peek()) return;
|
|
1972
|
+
this.applyPartial(ini, true);
|
|
1973
|
+
});
|
|
1974
|
+
});
|
|
1975
|
+
} else this.applyPartial(options.initial, true);
|
|
1820
1976
|
this.value = computed(() => this.computeValue());
|
|
1821
1977
|
this.errors = computed(() => this.computeErrors());
|
|
1822
1978
|
this.isDirty = computed(() => this.computeBool("isDirty"));
|
|
@@ -1873,6 +2029,7 @@ var FormImpl = class {
|
|
|
1873
2029
|
for (const [k, val] of Object.entries(partial)) {
|
|
1874
2030
|
const child = this.fields[k];
|
|
1875
2031
|
if (!child) continue;
|
|
2032
|
+
if (val === void 0) continue;
|
|
1876
2033
|
if (isForm(child)) if (asInitial) child.resetWithInitial(val);
|
|
1877
2034
|
else child.set(val);
|
|
1878
2035
|
else if (isFieldArray(child)) {
|
|
@@ -1915,6 +2072,7 @@ var FormImpl = class {
|
|
|
1915
2072
|
for (const child of Object.values(this.fields)) if (isForm(child) || isFieldArray(child)) tasks.push(child.validate());
|
|
1916
2073
|
else tasks.push(child.revalidate());
|
|
1917
2074
|
await Promise.all(tasks);
|
|
2075
|
+
if (this.validators.length > 0) this.runTopLevelValidators();
|
|
1918
2076
|
if (this.topLevelValidating$.peek()) await new Promise((resolve) => {
|
|
1919
2077
|
const unsub = this.topLevelValidating$.subscribe((v) => {
|
|
1920
2078
|
if (!v) {
|
|
@@ -1929,6 +2087,7 @@ var FormImpl = class {
|
|
|
1929
2087
|
if (this.disposed) return;
|
|
1930
2088
|
this.disposed = true;
|
|
1931
2089
|
this.validatorDispose?.();
|
|
2090
|
+
this.initialDispose?.();
|
|
1932
2091
|
this.currentValidatorAbort?.abort();
|
|
1933
2092
|
for (const child of Object.values(this.fields)) child.dispose?.();
|
|
1934
2093
|
}
|
|
@@ -1941,10 +2100,15 @@ var FormImpl = class {
|
|
|
1941
2100
|
const myId = ++this.currentValidatorRun;
|
|
1942
2101
|
const syncErrors = [];
|
|
1943
2102
|
const asyncPromises = [];
|
|
1944
|
-
for (const v of this.validators) {
|
|
2103
|
+
for (const v of this.validators) try {
|
|
1945
2104
|
const r = v(value, abort.signal);
|
|
1946
2105
|
if (r instanceof Promise) asyncPromises.push(r);
|
|
1947
2106
|
else if (r != null) syncErrors.push(r);
|
|
2107
|
+
} catch (err) {
|
|
2108
|
+
try {
|
|
2109
|
+
this.onValidatorError?.(err);
|
|
2110
|
+
} catch {}
|
|
2111
|
+
syncErrors.push(err instanceof Error ? err.message : String(err));
|
|
1948
2112
|
}
|
|
1949
2113
|
if (syncErrors.length > 0) {
|
|
1950
2114
|
batch(() => {
|
|
@@ -2038,9 +2202,15 @@ var FieldArrayImpl = class {
|
|
|
2038
2202
|
currentValidatorAbort = null;
|
|
2039
2203
|
validatorDispose = null;
|
|
2040
2204
|
disposed = false;
|
|
2041
|
-
|
|
2205
|
+
onValidatorError = null;
|
|
2206
|
+
/** Internal — see `FormImpl.bindValidatorErrorReporter`. */
|
|
2207
|
+
bindValidatorErrorReporter(reporter) {
|
|
2208
|
+
this.onValidatorError = reporter;
|
|
2209
|
+
}
|
|
2210
|
+
constructor(itemFactory, options, internalOptions) {
|
|
2042
2211
|
this.itemFactory = itemFactory;
|
|
2043
2212
|
this.validators = options?.validators ?? [];
|
|
2213
|
+
this.onValidatorError = internalOptions?.onValidatorError ?? null;
|
|
2044
2214
|
this.items$ = signal([]);
|
|
2045
2215
|
if (options?.initial) {
|
|
2046
2216
|
this.initialItems = options.initial;
|
|
@@ -2131,6 +2301,7 @@ var FieldArrayImpl = class {
|
|
|
2131
2301
|
for (const item of this.items$.peek()) if (isForm(item)) tasks.push(item.validate());
|
|
2132
2302
|
else tasks.push(item.revalidate());
|
|
2133
2303
|
await Promise.all(tasks);
|
|
2304
|
+
if (this.validators.length > 0) this.runTopLevelValidators();
|
|
2134
2305
|
if (this.topLevelValidating$.peek()) await new Promise((resolve) => {
|
|
2135
2306
|
const unsub = this.topLevelValidating$.subscribe((v) => {
|
|
2136
2307
|
if (!v) {
|
|
@@ -2157,10 +2328,15 @@ var FieldArrayImpl = class {
|
|
|
2157
2328
|
const myId = ++this.currentValidatorRun;
|
|
2158
2329
|
const syncErrors = [];
|
|
2159
2330
|
const asyncPromises = [];
|
|
2160
|
-
for (const v of this.validators) {
|
|
2331
|
+
for (const v of this.validators) try {
|
|
2161
2332
|
const r = v(value, abort.signal);
|
|
2162
2333
|
if (r instanceof Promise) asyncPromises.push(r);
|
|
2163
2334
|
else if (r != null) syncErrors.push(r);
|
|
2335
|
+
} catch (err) {
|
|
2336
|
+
try {
|
|
2337
|
+
this.onValidatorError?.(err);
|
|
2338
|
+
} catch {}
|
|
2339
|
+
syncErrors.push(err instanceof Error ? err.message : String(err));
|
|
2164
2340
|
}
|
|
2165
2341
|
if (syncErrors.length > 0) {
|
|
2166
2342
|
batch(() => {
|
|
@@ -2191,11 +2367,11 @@ var FieldArrayImpl = class {
|
|
|
2191
2367
|
});
|
|
2192
2368
|
}
|
|
2193
2369
|
};
|
|
2194
|
-
function createForm(schema, options) {
|
|
2195
|
-
return new FormImpl(schema, options);
|
|
2370
|
+
function createForm(schema, options, internalOptions) {
|
|
2371
|
+
return new FormImpl(schema, options, internalOptions);
|
|
2196
2372
|
}
|
|
2197
|
-
function createFieldArray(itemFactory, options) {
|
|
2198
|
-
return new FieldArrayImpl(itemFactory, options);
|
|
2373
|
+
function createFieldArray(itemFactory, options, internalOptions) {
|
|
2374
|
+
return new FieldArrayImpl(itemFactory, options, internalOptions);
|
|
2199
2375
|
}
|
|
2200
2376
|
/**
|
|
2201
2377
|
* Recursively wire every leaf `Field` in a form / field-array tree to a
|
|
@@ -2222,12 +2398,24 @@ function bindTreeToDevtoolsInto(node, prefix, controllerPath, emitter, disposers
|
|
|
2222
2398
|
}
|
|
2223
2399
|
if (isFieldArray(node)) {
|
|
2224
2400
|
const arr = node;
|
|
2401
|
+
let perPass = [];
|
|
2225
2402
|
const stop = effect(() => {
|
|
2226
|
-
arr.items.value
|
|
2227
|
-
|
|
2403
|
+
const items = arr.items.value;
|
|
2404
|
+
for (const d of perPass) try {
|
|
2405
|
+
d();
|
|
2406
|
+
} catch {}
|
|
2407
|
+
perPass = [];
|
|
2408
|
+
items.forEach((item, idx) => {
|
|
2409
|
+
bindTreeToDevtoolsInto(item, `${prefix}[${idx}]`, controllerPath, emitter, perPass);
|
|
2228
2410
|
});
|
|
2229
2411
|
});
|
|
2230
2412
|
disposers.push(stop);
|
|
2413
|
+
disposers.push(() => {
|
|
2414
|
+
for (const d of perPass) try {
|
|
2415
|
+
d();
|
|
2416
|
+
} catch {}
|
|
2417
|
+
perPass = [];
|
|
2418
|
+
});
|
|
2231
2419
|
return;
|
|
2232
2420
|
}
|
|
2233
2421
|
bindFieldDevtoolsOwner(node, {
|
|
@@ -2236,6 +2424,26 @@ function bindTreeToDevtoolsInto(node, prefix, controllerPath, emitter, disposers
|
|
|
2236
2424
|
emitter
|
|
2237
2425
|
});
|
|
2238
2426
|
}
|
|
2427
|
+
/**
|
|
2428
|
+
* Walk a Form/FieldArray subtree and install `reporter` on every level —
|
|
2429
|
+
* leaf fields, nested forms' top-level validators, and field-arrays' top-level
|
|
2430
|
+
* validators. Called by `ctx.form` / `ctx.fieldArray` so synchronous validator
|
|
2431
|
+
* throws anywhere in the tree route through `root.onError`. See
|
|
2432
|
+
* `ValidatorErrorReporter` in `./field.ts`.
|
|
2433
|
+
*/
|
|
2434
|
+
function bindTreeValidatorErrorReporter(node, reporter) {
|
|
2435
|
+
if (isForm(node)) {
|
|
2436
|
+
node.bindValidatorErrorReporter?.(reporter);
|
|
2437
|
+
for (const child of Object.values(node.fields)) bindTreeValidatorErrorReporter(child, reporter);
|
|
2438
|
+
return;
|
|
2439
|
+
}
|
|
2440
|
+
if (isFieldArray(node)) {
|
|
2441
|
+
node.bindValidatorErrorReporter?.(reporter);
|
|
2442
|
+
for (const item of node.items.value) bindTreeValidatorErrorReporter(item, reporter);
|
|
2443
|
+
return;
|
|
2444
|
+
}
|
|
2445
|
+
bindFieldValidatorErrorReporter(node, reporter);
|
|
2446
|
+
}
|
|
2239
2447
|
//#endregion
|
|
2240
2448
|
//#region src/query/local.ts
|
|
2241
2449
|
var LocalCacheImpl = class {
|
|
@@ -2469,7 +2677,13 @@ var MutationImpl = class {
|
|
|
2469
2677
|
reset() {
|
|
2470
2678
|
if (this.disposed) return;
|
|
2471
2679
|
for (const handle of this.inflight) handle.abort.abort();
|
|
2472
|
-
this.serialQueue.length
|
|
2680
|
+
if (this.serialQueue.length > 0) {
|
|
2681
|
+
const aborted = new DOMException("Aborted", "AbortError");
|
|
2682
|
+
const queue = this.serialQueue;
|
|
2683
|
+
this.serialQueue = [];
|
|
2684
|
+
for (const queued of queue) queued.reject(aborted);
|
|
2685
|
+
}
|
|
2686
|
+
this.serialActive = false;
|
|
2473
2687
|
batch(() => {
|
|
2474
2688
|
this.data.set(void 0);
|
|
2475
2689
|
this.error.set(void 0);
|
|
@@ -2517,24 +2731,6 @@ function raceAbort(promise, signal) {
|
|
|
2517
2731
|
});
|
|
2518
2732
|
});
|
|
2519
2733
|
}
|
|
2520
|
-
function abortableSleep(ms, signal) {
|
|
2521
|
-
return new Promise((resolve, reject) => {
|
|
2522
|
-
if (signal.aborted) {
|
|
2523
|
-
reject(new DOMException("Aborted", "AbortError"));
|
|
2524
|
-
return;
|
|
2525
|
-
}
|
|
2526
|
-
const timer = setTimeout(() => {
|
|
2527
|
-
signal.removeEventListener("abort", onAbort);
|
|
2528
|
-
resolve();
|
|
2529
|
-
}, ms);
|
|
2530
|
-
const onAbort = () => {
|
|
2531
|
-
clearTimeout(timer);
|
|
2532
|
-
signal.removeEventListener("abort", onAbort);
|
|
2533
|
-
reject(new DOMException("Aborted", "AbortError"));
|
|
2534
|
-
};
|
|
2535
|
-
signal.addEventListener("abort", onAbort, { once: true });
|
|
2536
|
-
});
|
|
2537
|
-
}
|
|
2538
2734
|
//#endregion
|
|
2539
2735
|
//#region src/query/use.ts
|
|
2540
2736
|
var SubscriptionImpl = class {
|
|
@@ -2605,7 +2801,9 @@ function createUse(client, query, keyOrOptions) {
|
|
|
2605
2801
|
const enabledFn = typeof keyOrOptions === "object" && keyOrOptions !== null ? keyOrOptions.enabled : void 0;
|
|
2606
2802
|
const sub = new SubscriptionImpl(keepPreviousData);
|
|
2607
2803
|
let currentEntry = null;
|
|
2804
|
+
let suspended = false;
|
|
2608
2805
|
const effectDispose = effect(() => {
|
|
2806
|
+
if (suspended) return;
|
|
2609
2807
|
if (!(enabledFn ? enabledFn() : true)) {
|
|
2610
2808
|
untracked(() => {
|
|
2611
2809
|
if (currentEntry) {
|
|
@@ -2636,9 +2834,31 @@ function createUse(client, query, keyOrOptions) {
|
|
|
2636
2834
|
}
|
|
2637
2835
|
sub.detach();
|
|
2638
2836
|
};
|
|
2837
|
+
const suspend = () => {
|
|
2838
|
+
if (suspended) return;
|
|
2839
|
+
suspended = true;
|
|
2840
|
+
if (currentEntry) {
|
|
2841
|
+
currentEntry.release();
|
|
2842
|
+
currentEntry = null;
|
|
2843
|
+
}
|
|
2844
|
+
};
|
|
2845
|
+
const resume = () => {
|
|
2846
|
+
if (!suspended) return;
|
|
2847
|
+
suspended = false;
|
|
2848
|
+
if (!(enabledFn ? enabledFn() : true)) return;
|
|
2849
|
+
const args = keyFn ? keyFn() : [];
|
|
2850
|
+
const entry = client.bindEntry(query, args);
|
|
2851
|
+
entry.acquire();
|
|
2852
|
+
currentEntry = entry;
|
|
2853
|
+
sub.attach(entry);
|
|
2854
|
+
const status = entry.entry.status.peek();
|
|
2855
|
+
if (status === "idle" || entry.entry.isStaleNow() || status === "error") entry.entry.startFetch().catch(() => {});
|
|
2856
|
+
};
|
|
2639
2857
|
return {
|
|
2640
2858
|
subscription: sub,
|
|
2641
|
-
dispose
|
|
2859
|
+
dispose,
|
|
2860
|
+
suspend,
|
|
2861
|
+
resume
|
|
2642
2862
|
};
|
|
2643
2863
|
}
|
|
2644
2864
|
var InfiniteSubscriptionImpl = class {
|
|
@@ -2738,7 +2958,9 @@ function createInfiniteUse(client, query, keyOrOptions) {
|
|
|
2738
2958
|
const enabledFn = typeof keyOrOptions === "object" && keyOrOptions !== null ? keyOrOptions.enabled : void 0;
|
|
2739
2959
|
const sub = new InfiniteSubscriptionImpl(keepPreviousData);
|
|
2740
2960
|
let currentEntry = null;
|
|
2961
|
+
let suspended = false;
|
|
2741
2962
|
const effectDispose = effect(() => {
|
|
2963
|
+
if (suspended) return;
|
|
2742
2964
|
if (!(enabledFn ? enabledFn() : true)) {
|
|
2743
2965
|
untracked(() => {
|
|
2744
2966
|
if (currentEntry) {
|
|
@@ -2769,9 +2991,31 @@ function createInfiniteUse(client, query, keyOrOptions) {
|
|
|
2769
2991
|
}
|
|
2770
2992
|
sub.detach();
|
|
2771
2993
|
};
|
|
2994
|
+
const suspend = () => {
|
|
2995
|
+
if (suspended) return;
|
|
2996
|
+
suspended = true;
|
|
2997
|
+
if (currentEntry) {
|
|
2998
|
+
currentEntry.release();
|
|
2999
|
+
currentEntry = null;
|
|
3000
|
+
}
|
|
3001
|
+
};
|
|
3002
|
+
const resume = () => {
|
|
3003
|
+
if (!suspended) return;
|
|
3004
|
+
suspended = false;
|
|
3005
|
+
if (!(enabledFn ? enabledFn() : true)) return;
|
|
3006
|
+
const args = keyFn ? keyFn() : [];
|
|
3007
|
+
const entry = client.bindInfiniteEntry(query, args);
|
|
3008
|
+
entry.acquire();
|
|
3009
|
+
currentEntry = entry;
|
|
3010
|
+
sub.attach(entry);
|
|
3011
|
+
const status = entry.entry.status.peek();
|
|
3012
|
+
if (status === "idle" || entry.entry.isStaleNow() || status === "error") entry.entry.startFetch().catch(() => {});
|
|
3013
|
+
};
|
|
2772
3014
|
return {
|
|
2773
3015
|
subscription: sub,
|
|
2774
|
-
dispose
|
|
3016
|
+
dispose,
|
|
3017
|
+
suspend,
|
|
3018
|
+
resume
|
|
2775
3019
|
};
|
|
2776
3020
|
}
|
|
2777
3021
|
//#endregion
|
|
@@ -2821,7 +3065,6 @@ var ControllerInstance = class ControllerInstance {
|
|
|
2821
3065
|
}
|
|
2822
3066
|
dispose() {
|
|
2823
3067
|
if (this.state === "disposed") return;
|
|
2824
|
-
this.state;
|
|
2825
3068
|
this.state = "disposed";
|
|
2826
3069
|
for (let i = this.entries.length - 1; i >= 0; i--) {
|
|
2827
3070
|
const entry = this.entries[i];
|
|
@@ -2847,6 +3090,9 @@ var ControllerInstance = class ControllerInstance {
|
|
|
2847
3090
|
case "cleanup":
|
|
2848
3091
|
entry.dispose();
|
|
2849
3092
|
break;
|
|
3093
|
+
case "subscription-cache":
|
|
3094
|
+
entry.dispose();
|
|
3095
|
+
break;
|
|
2850
3096
|
case "child":
|
|
2851
3097
|
entry.instance.dispose();
|
|
2852
3098
|
break;
|
|
@@ -2872,6 +3118,9 @@ var ControllerInstance = class ControllerInstance {
|
|
|
2872
3118
|
entry.dispose?.();
|
|
2873
3119
|
entry.dispose = null;
|
|
2874
3120
|
break;
|
|
3121
|
+
case "subscription-cache":
|
|
3122
|
+
entry.suspend();
|
|
3123
|
+
break;
|
|
2875
3124
|
case "child":
|
|
2876
3125
|
entry.instance.suspend();
|
|
2877
3126
|
break;
|
|
@@ -2896,6 +3145,9 @@ var ControllerInstance = class ControllerInstance {
|
|
|
2896
3145
|
case "effect":
|
|
2897
3146
|
entry.dispose = effect(entry.factory);
|
|
2898
3147
|
break;
|
|
3148
|
+
case "subscription-cache":
|
|
3149
|
+
entry.resume();
|
|
3150
|
+
break;
|
|
2899
3151
|
case "child":
|
|
2900
3152
|
entry.instance.resume();
|
|
2901
3153
|
break;
|
|
@@ -2936,7 +3188,7 @@ var ControllerInstance = class ControllerInstance {
|
|
|
2936
3188
|
}
|
|
2937
3189
|
};
|
|
2938
3190
|
entry.factory = wrapped;
|
|
2939
|
-
entry.dispose = effect(wrapped);
|
|
3191
|
+
if (self.state !== "suspended") entry.dispose = effect(wrapped);
|
|
2940
3192
|
self.entries.push(entry);
|
|
2941
3193
|
},
|
|
2942
3194
|
cache(fetcher, options) {
|
|
@@ -2949,19 +3201,23 @@ var ControllerInstance = class ControllerInstance {
|
|
|
2949
3201
|
},
|
|
2950
3202
|
use(query, keyOrOptions) {
|
|
2951
3203
|
if (query.__olas === "infiniteQuery") {
|
|
2952
|
-
const
|
|
3204
|
+
const handle = createInfiniteUse(self.rootShared.queryClient, query, keyOrOptions);
|
|
2953
3205
|
self.entries.push({
|
|
2954
|
-
kind: "
|
|
2955
|
-
dispose:
|
|
3206
|
+
kind: "subscription-cache",
|
|
3207
|
+
dispose: handle.dispose,
|
|
3208
|
+
suspend: handle.suspend,
|
|
3209
|
+
resume: handle.resume
|
|
2956
3210
|
});
|
|
2957
|
-
return subscription;
|
|
3211
|
+
return handle.subscription;
|
|
2958
3212
|
}
|
|
2959
|
-
const
|
|
3213
|
+
const handle = createUse(self.rootShared.queryClient, query, keyOrOptions);
|
|
2960
3214
|
self.entries.push({
|
|
2961
|
-
kind: "
|
|
2962
|
-
dispose:
|
|
3215
|
+
kind: "subscription-cache",
|
|
3216
|
+
dispose: handle.dispose,
|
|
3217
|
+
suspend: handle.suspend,
|
|
3218
|
+
resume: handle.resume
|
|
2963
3219
|
});
|
|
2964
|
-
return subscription;
|
|
3220
|
+
return handle.subscription;
|
|
2965
3221
|
},
|
|
2966
3222
|
mutation(spec) {
|
|
2967
3223
|
const m = createMutation(spec, self.rootShared.onError, self.path, self.rootShared.queryClient.mutationsInflight$, self.rootShared.devtools);
|
|
@@ -2980,7 +3236,12 @@ var ControllerInstance = class ControllerInstance {
|
|
|
2980
3236
|
return e;
|
|
2981
3237
|
},
|
|
2982
3238
|
field(initial, validators) {
|
|
2983
|
-
const f = createField(initial, validators)
|
|
3239
|
+
const f = createField(initial, validators, { onValidatorError: (err) => {
|
|
3240
|
+
dispatchError(self.rootShared.onError, err, {
|
|
3241
|
+
kind: "effect",
|
|
3242
|
+
controllerPath: self.path
|
|
3243
|
+
});
|
|
3244
|
+
} });
|
|
2984
3245
|
self.entries.push({
|
|
2985
3246
|
kind: "cleanup",
|
|
2986
3247
|
dispose: () => f.dispose()
|
|
@@ -2993,7 +3254,13 @@ var ControllerInstance = class ControllerInstance {
|
|
|
2993
3254
|
return f;
|
|
2994
3255
|
},
|
|
2995
3256
|
form(schema, options) {
|
|
2996
|
-
const
|
|
3257
|
+
const reporter = (err) => {
|
|
3258
|
+
dispatchError(self.rootShared.onError, err, {
|
|
3259
|
+
kind: "effect",
|
|
3260
|
+
controllerPath: self.path
|
|
3261
|
+
});
|
|
3262
|
+
};
|
|
3263
|
+
const f = createForm(schema, options, { onValidatorError: reporter });
|
|
2997
3264
|
self.entries.push({
|
|
2998
3265
|
kind: "cleanup",
|
|
2999
3266
|
dispose: () => f.dispose()
|
|
@@ -3003,10 +3270,17 @@ var ControllerInstance = class ControllerInstance {
|
|
|
3003
3270
|
kind: "cleanup",
|
|
3004
3271
|
dispose: stop
|
|
3005
3272
|
});
|
|
3273
|
+
bindTreeValidatorErrorReporter(f, reporter);
|
|
3006
3274
|
return f;
|
|
3007
3275
|
},
|
|
3008
3276
|
fieldArray(itemFactory, options) {
|
|
3009
|
-
const
|
|
3277
|
+
const reporter = (err) => {
|
|
3278
|
+
dispatchError(self.rootShared.onError, err, {
|
|
3279
|
+
kind: "effect",
|
|
3280
|
+
controllerPath: self.path
|
|
3281
|
+
});
|
|
3282
|
+
};
|
|
3283
|
+
const fa = createFieldArray(itemFactory, options, { onValidatorError: reporter });
|
|
3010
3284
|
self.entries.push({
|
|
3011
3285
|
kind: "cleanup",
|
|
3012
3286
|
dispose: () => fa.dispose()
|
|
@@ -3016,6 +3290,7 @@ var ControllerInstance = class ControllerInstance {
|
|
|
3016
3290
|
kind: "cleanup",
|
|
3017
3291
|
dispose: stop
|
|
3018
3292
|
});
|
|
3293
|
+
bindTreeValidatorErrorReporter(fa, reporter);
|
|
3019
3294
|
return fa;
|
|
3020
3295
|
},
|
|
3021
3296
|
provide(scope, value) {
|
|
@@ -3095,6 +3370,28 @@ var ControllerInstance = class ControllerInstance {
|
|
|
3095
3370
|
controllerPath: self.path
|
|
3096
3371
|
});
|
|
3097
3372
|
}
|
|
3373
|
+
},
|
|
3374
|
+
suspend: () => {
|
|
3375
|
+
if (disposed) return;
|
|
3376
|
+
try {
|
|
3377
|
+
childInstance.suspend();
|
|
3378
|
+
} catch (err) {
|
|
3379
|
+
dispatchError(self.rootShared.onError, err, {
|
|
3380
|
+
kind: "effect",
|
|
3381
|
+
controllerPath: self.path
|
|
3382
|
+
});
|
|
3383
|
+
}
|
|
3384
|
+
},
|
|
3385
|
+
resume: () => {
|
|
3386
|
+
if (disposed) return;
|
|
3387
|
+
try {
|
|
3388
|
+
childInstance.resume();
|
|
3389
|
+
} catch (err) {
|
|
3390
|
+
dispatchError(self.rootShared.onError, err, {
|
|
3391
|
+
kind: "effect",
|
|
3392
|
+
controllerPath: self.path
|
|
3393
|
+
});
|
|
3394
|
+
}
|
|
3098
3395
|
}
|
|
3099
3396
|
};
|
|
3100
3397
|
},
|
|
@@ -3186,7 +3483,13 @@ function createRootWithProps(def, props, options) {
|
|
|
3186
3483
|
onError: options.onError,
|
|
3187
3484
|
queryClient
|
|
3188
3485
|
}, "root", options.deps);
|
|
3189
|
-
|
|
3486
|
+
let api;
|
|
3487
|
+
try {
|
|
3488
|
+
api = instance.construct(getFactory(def), props);
|
|
3489
|
+
} catch (err) {
|
|
3490
|
+
queryClient.dispose();
|
|
3491
|
+
throw err;
|
|
3492
|
+
}
|
|
3190
3493
|
if (typeof api !== "object" || api === null) return attachRootControls({ value: api }, instance, devtools, queryClient);
|
|
3191
3494
|
return attachRootControls(api, instance, devtools, queryClient);
|
|
3192
3495
|
}
|
|
@@ -3337,6 +3640,12 @@ Object.defineProperty(exports, "signal", {
|
|
|
3337
3640
|
return signal;
|
|
3338
3641
|
}
|
|
3339
3642
|
});
|
|
3643
|
+
Object.defineProperty(exports, "stableHash", {
|
|
3644
|
+
enumerable: true,
|
|
3645
|
+
get: function() {
|
|
3646
|
+
return stableHash;
|
|
3647
|
+
}
|
|
3648
|
+
});
|
|
3340
3649
|
Object.defineProperty(exports, "untracked", {
|
|
3341
3650
|
enumerable: true,
|
|
3342
3651
|
get: function() {
|
|
@@ -3344,4 +3653,4 @@ Object.defineProperty(exports, "untracked", {
|
|
|
3344
3653
|
}
|
|
3345
3654
|
});
|
|
3346
3655
|
|
|
3347
|
-
//# sourceMappingURL=root-
|
|
3656
|
+
//# sourceMappingURL=root-DXV1gVbQ.cjs.map
|