sibujs 3.4.0 → 4.0.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/LICENSE +21 -21
- package/README.md +118 -15
- package/dist/browser.cjs +410 -101
- package/dist/browser.d.cts +50 -5
- package/dist/browser.d.ts +50 -5
- package/dist/browser.js +8 -6
- package/dist/build.cjs +777 -385
- package/dist/build.js +15 -15
- package/dist/cdn.global.js +11 -7
- package/dist/{chunk-JCO7AHNU.js → chunk-3EKJK5FZ.js} +73 -34
- package/dist/chunk-4MFZMLUU.js +147 -0
- package/dist/chunk-52XFPGSN.js +123 -0
- package/dist/chunk-5INI7D2L.js +194 -0
- package/dist/{chunk-OAVXAFAY.js → chunk-6LG2T7LO.js} +5 -5
- package/dist/{chunk-OOUQUCNN.js → chunk-ACBT6UZS.js} +57 -22
- package/dist/{chunk-F7HW2NI5.js → chunk-AW6HPJ3L.js} +1 -1
- package/dist/chunk-DHDZ7IVN.js +109 -0
- package/dist/{chunk-JNGGM2XE.js → chunk-DQVUBVNX.js} +16 -25
- package/dist/{chunk-AXFWGY32.js → chunk-DR7H6UGM.js} +232 -67
- package/dist/{chunk-WSGLJYFI.js → chunk-E62UADKL.js} +9 -3
- package/dist/{chunk-3Z2NGWQI.js → chunk-ES2GERY2.js} +1 -1
- package/dist/{chunk-S3V6ZXPI.js → chunk-EWEE7NBF.js} +115 -41
- package/dist/{chunk-3DVGCW7O.js → chunk-F64ZPCFE.js} +8 -8
- package/dist/{chunk-APFRCIRI.js → chunk-GCSGGA3Q.js} +295 -198
- package/dist/{chunk-EX77FXTT.js → chunk-HYCCIYNS.js} +228 -51
- package/dist/{chunk-ELXMRSVN.js → chunk-NF3LYCQH.js} +259 -124
- package/dist/chunk-O3QQ3XOL.js +61 -0
- package/dist/{chunk-BUKYCWHX.js → chunk-PBQEW5VW.js} +14 -10
- package/dist/{chunk-H5FI5WDP.js → chunk-RRIDJQ5C.js} +3 -3
- package/dist/{chunk-S3NFJO6L.js → chunk-TIRZCERI.js} +36 -5
- package/dist/{chunk-ODXVQ3CS.js → chunk-VFHIVEPF.js} +309 -188
- package/dist/chunk-VPP2FONR.js +142 -0
- package/dist/{chunk-KZBEVFXZ.js → chunk-W55YHPEP.js} +5 -3
- package/dist/{chunk-FKQG7FTY.js → chunk-WN6WDFCB.js} +4 -4
- package/dist/{chunk-CEBWDD63.js → chunk-WOOMUJFT.js} +228 -86
- package/dist/{chunk-ITWCTZYH.js → chunk-Z2CE42DB.js} +19 -25
- package/dist/{contracts-ey_Qh8ef.d.cts → contracts-Hg6ITAEF.d.cts} +6 -6
- package/dist/{contracts-ey_Qh8ef.d.ts → contracts-Hg6ITAEF.d.ts} +6 -6
- package/dist/{customElement-CPfIrbvg.d.ts → customElement-CKLoO63w.d.cts} +73 -11
- package/dist/{customElement-CPfIrbvg.d.cts → customElement-CKLoO63w.d.ts} +73 -11
- package/dist/data.cjs +483 -205
- package/dist/data.d.cts +139 -7
- package/dist/data.d.ts +139 -7
- package/dist/data.js +9 -9
- package/dist/devtools.cjs +222 -87
- package/dist/devtools.js +8 -8
- package/dist/dispose-4FZ6OFXR.js +19 -0
- package/dist/ecosystem.cjs +419 -175
- package/dist/ecosystem.js +11 -11
- package/dist/extras.cjs +1981 -765
- package/dist/extras.d.cts +7 -7
- package/dist/extras.d.ts +7 -7
- package/dist/extras.js +29 -26
- package/dist/index.cjs +784 -385
- package/dist/index.d.cts +299 -28
- package/dist/index.d.ts +299 -28
- package/dist/index.js +33 -18
- package/dist/motion.cjs +180 -50
- package/dist/motion.d.cts +26 -0
- package/dist/motion.d.ts +26 -0
- package/dist/motion.js +5 -5
- package/dist/patterns.cjs +437 -170
- package/dist/patterns.d.cts +60 -13
- package/dist/patterns.d.ts +60 -13
- package/dist/patterns.js +7 -7
- package/dist/performance.cjs +333 -148
- package/dist/performance.d.cts +55 -7
- package/dist/performance.d.ts +55 -7
- package/dist/performance.js +9 -9
- package/dist/plugins.cjs +1374 -576
- package/dist/plugins.d.cts +227 -9
- package/dist/plugins.d.ts +227 -9
- package/dist/plugins.js +613 -321
- package/dist/{ssr-D62yFwuw.d.cts → ssr-BiPRdZ6n.d.cts} +44 -15
- package/dist/{ssr-D62yFwuw.d.ts → ssr-BiPRdZ6n.d.ts} +44 -15
- package/dist/{ssr-2PPULEK2.js → ssr-JFP5535P.js} +5 -4
- package/dist/ssr.cjs +1104 -443
- package/dist/ssr.d.cts +230 -20
- package/dist/ssr.d.ts +230 -20
- package/dist/ssr.js +15 -12
- package/dist/{startup-0Qv6aosO.d.cts → startup-BMpaiMhP.d.cts} +1 -1
- package/dist/{startup-0Qv6aosO.d.ts → startup-BMpaiMhP.d.ts} +1 -1
- package/dist/testing.cjs +205 -63
- package/dist/testing.d.cts +1 -1
- package/dist/testing.d.ts +1 -1
- package/dist/testing.js +8 -10
- package/dist/ui.cjs +640 -199
- package/dist/ui.d.cts +2 -2
- package/dist/ui.d.ts +2 -2
- package/dist/ui.js +10 -10
- package/dist/widgets.cjs +207 -82
- package/dist/widgets.js +9 -9
- package/package.json +12 -7
- package/dist/chunk-5VGSK6D2.js +0 -80
- package/dist/chunk-COY6PUD2.js +0 -34
- package/dist/chunk-K35BAR4P.js +0 -96
- package/dist/chunk-L3GAGWCC.js +0 -111
- package/dist/chunk-RT3IYFN2.js +0 -38
package/dist/data.d.cts
CHANGED
|
@@ -13,7 +13,15 @@ interface RetryOptions {
|
|
|
13
13
|
maxDelay?: number;
|
|
14
14
|
/** Jitter factor (0-1) to randomize delay. Default: 0.1 */
|
|
15
15
|
jitter?: number;
|
|
16
|
-
/**
|
|
16
|
+
/**
|
|
17
|
+
* Predicate deciding whether a **non-cancellation** failure is retryable.
|
|
18
|
+
* Default: `() => true`.
|
|
19
|
+
*
|
|
20
|
+
* Cancellation never reaches this callback. An aborted signal, or a rejection
|
|
21
|
+
* that is itself an `AbortError`, bypasses retry policy entirely — so a
|
|
22
|
+
* `shouldRetry` returning `true` for everything still cannot resurrect a
|
|
23
|
+
* cancelled operation.
|
|
24
|
+
*/
|
|
17
25
|
shouldRetry?: (error: unknown, attempt: number) => boolean;
|
|
18
26
|
}
|
|
19
27
|
/**
|
|
@@ -24,9 +32,16 @@ declare function calculateDelay(attempt: number, strategy: "exponential" | "line
|
|
|
24
32
|
* Execute an async function with retry logic.
|
|
25
33
|
* Returns the result or throws after all retries are exhausted.
|
|
26
34
|
*
|
|
35
|
+
* CANCELLATION OUTRANKS RETRY POLICY. A cancelled operation — signalled either
|
|
36
|
+
* by `signal.aborted` or by a rejection that is itself an `AbortError` — skips
|
|
37
|
+
* `shouldRetry`, `onRetry` and the backoff, and rejects immediately.
|
|
38
|
+
*
|
|
27
39
|
* @param fn The async function to execute
|
|
28
40
|
* @param options Retry configuration
|
|
29
|
-
* @param onRetry
|
|
41
|
+
* @param onRetry Fired only when another attempt WILL actually be scheduled —
|
|
42
|
+
* never on the final failure, never for a rejection `shouldRetry` declined,
|
|
43
|
+
* and never for a cancellation. Receives the error, the attempt index, and
|
|
44
|
+
* the delay before the next attempt.
|
|
30
45
|
* @param signal AbortSignal to cancel retries
|
|
31
46
|
*
|
|
32
47
|
* @example
|
|
@@ -40,6 +55,44 @@ declare function calculateDelay(attempt: number, strategy: "exponential" | "line
|
|
|
40
55
|
*/
|
|
41
56
|
declare function withRetry<T>(fn: () => Promise<T>, options?: RetryOptions, onRetry?: (error: unknown, attempt: number, delay: number) => void, signal?: AbortSignal): Promise<T>;
|
|
42
57
|
|
|
58
|
+
/**
|
|
59
|
+
* ## Callback semantics
|
|
60
|
+
*
|
|
61
|
+
* These rules are shared by `query()`, `resource()`, `infiniteQuery()`, and
|
|
62
|
+
* `mutation()`.
|
|
63
|
+
*
|
|
64
|
+
* **A callback exception is not an operation failure.** Exceptions thrown by
|
|
65
|
+
* lifecycle callbacks (`onSuccess`, `onError`, `onSettled`, `onStart`) and by
|
|
66
|
+
* `select` do not retroactively change the success/failure state of the
|
|
67
|
+
* underlying request. A fetch that succeeded stays successful, its data stays
|
|
68
|
+
* available, and the shared cache keeps the value it committed:
|
|
69
|
+
*
|
|
70
|
+
* ```text
|
|
71
|
+
* network success → cache commit → onSuccess throws → still a success
|
|
72
|
+
* ```
|
|
73
|
+
*
|
|
74
|
+
* **Callback errors are surfaced separately.** They are reported via
|
|
75
|
+
* `console.error` (prefixed `[SibuJS data]`), never silently swallowed, and
|
|
76
|
+
* never routed into the operation's own error channel — a throwing `onSuccess`
|
|
77
|
+
* does not trigger `onError`, and does not populate `error()`.
|
|
78
|
+
*
|
|
79
|
+
* **Observers are isolated from each other.** Multiple observers sharing one
|
|
80
|
+
* cache key are notified independently: one observer's `select` throwing cannot
|
|
81
|
+
* prevent the others from receiving the shared result, and cannot poison the
|
|
82
|
+
* shared cache entry.
|
|
83
|
+
*
|
|
84
|
+
* **A throwing `select` keeps the previous data.** `select` is the observer's
|
|
85
|
+
* own transform, not part of the request. If it throws, this observer retains
|
|
86
|
+
* whatever data it already had rather than committing a value that was never
|
|
87
|
+
* produced; the request is still recorded as successful.
|
|
88
|
+
*
|
|
89
|
+
* **Ordering is guaranteed.** State is committed first, then `onSuccess` /
|
|
90
|
+
* `onError`, then `onSettled`. `onSettled` runs even when the callback before
|
|
91
|
+
* it threw.
|
|
92
|
+
*
|
|
93
|
+
* The one exception is `mutation()`'s `onMutate`, which is a step *of* the
|
|
94
|
+
* mutation rather than a notification — see `MutationOptions.onMutate`.
|
|
95
|
+
*/
|
|
43
96
|
interface QueryOptions<T> {
|
|
44
97
|
/** Time in ms before cached data is considered stale. Default: 0 (always stale) */
|
|
45
98
|
staleTime?: number;
|
|
@@ -103,10 +156,33 @@ declare function clearQueryCache(): void;
|
|
|
103
156
|
*/
|
|
104
157
|
declare function __resetQueryCache(): void;
|
|
105
158
|
|
|
159
|
+
/**
|
|
160
|
+
* Lifecycle callbacks follow the shared data-layer contract: an exception
|
|
161
|
+
* thrown by `onSuccess`, `onError`, or `onSettled` never changes the status of
|
|
162
|
+
* the mutation itself — a mutation that succeeded stays `success` and
|
|
163
|
+
* `mutateAsync` still resolves — and is reported separately via
|
|
164
|
+
* `console.error`. See `QueryOptions` for the full statement.
|
|
165
|
+
*
|
|
166
|
+
* `onMutate` is the deliberate exception; see its own note below.
|
|
167
|
+
*/
|
|
106
168
|
interface MutationOptions<TData, TVariables, TContext = unknown> {
|
|
107
169
|
/** Retry options for failed mutations */
|
|
108
170
|
retry?: RetryOptions;
|
|
109
|
-
/**
|
|
171
|
+
/**
|
|
172
|
+
* Called before the mutation — return context for rollback in `onError`.
|
|
173
|
+
*
|
|
174
|
+
* Unlike the notification callbacks, `onMutate` is a **step of** the
|
|
175
|
+
* mutation: it produces the rollback context everything downstream depends
|
|
176
|
+
* on. An ordinary exception therefore fails the mutation — there is no
|
|
177
|
+
* context, and the optimistic update it was meant to apply never happened.
|
|
178
|
+
*
|
|
179
|
+
* An `AbortError` is the one exception, and it is cancellation rather than
|
|
180
|
+
* failure — the same rule `mutationFn` follows. `isAbortError()` is
|
|
181
|
+
* authoritative for the whole operation, so where a cancellation is raised
|
|
182
|
+
* inside it does not change what it means: no error state, no `onError`, and
|
|
183
|
+
* the state the mutation replaced is restored. Throw an ordinary `Error` from
|
|
184
|
+
* `onMutate` to fail the mutation.
|
|
185
|
+
*/
|
|
110
186
|
onMutate?: (variables: TVariables) => TContext | Promise<TContext>;
|
|
111
187
|
/** Called on successful mutation */
|
|
112
188
|
onSuccess?: (data: TData, variables: TVariables, context: TContext) => void;
|
|
@@ -135,6 +211,13 @@ interface MutationResult<TData, TVariables> {
|
|
|
135
211
|
}
|
|
136
212
|
declare function mutation<TData, TVariables = void, TContext = unknown>(mutationFn: (variables: TVariables, signal?: AbortSignal) => Promise<TData>, options?: MutationOptions<TData, TVariables, TContext>): MutationResult<TData, TVariables>;
|
|
137
213
|
|
|
214
|
+
/**
|
|
215
|
+
* Lifecycle callbacks follow the shared data-layer contract: an exception
|
|
216
|
+
* thrown by `onSuccess` or `onError` never changes the success/failure state of
|
|
217
|
+
* the page fetch itself — a page that was appended stays appended — and is
|
|
218
|
+
* reported separately via `console.error`. See `QueryOptions` for the full
|
|
219
|
+
* statement.
|
|
220
|
+
*/
|
|
138
221
|
interface InfiniteQueryOptions<TData, TPageParam = number> {
|
|
139
222
|
/** Get the param for the next page. Return undefined to signal end. */
|
|
140
223
|
getNextPageParam: (lastPage: TData, allPages: TData[]) => TPageParam | undefined;
|
|
@@ -245,6 +328,12 @@ declare function debounce<T>(getter: () => T, delay: number): () => T;
|
|
|
245
328
|
*/
|
|
246
329
|
declare function throttle<T>(getter: () => T, interval: number): () => T;
|
|
247
330
|
|
|
331
|
+
/**
|
|
332
|
+
* Lifecycle callbacks follow the shared data-layer contract: an exception
|
|
333
|
+
* thrown by `onStart`, `onSuccess`, `onError`, or `onSettled` never changes the
|
|
334
|
+
* success/failure state of the fetch itself, and is reported separately via
|
|
335
|
+
* `console.error`. See `QueryOptions` for the full statement.
|
|
336
|
+
*/
|
|
248
337
|
interface ResourceOptions<T> {
|
|
249
338
|
/** Initial data value before first fetch. Default: undefined */
|
|
250
339
|
initialValue?: T;
|
|
@@ -318,13 +407,53 @@ interface OfflineStoreOptions<T> {
|
|
|
318
407
|
/** Auto-sync when online status changes (default: true) */
|
|
319
408
|
autoSync?: boolean;
|
|
320
409
|
}
|
|
410
|
+
/**
|
|
411
|
+
* How a pulled remote record is reconciled with a local record that still has
|
|
412
|
+
* an unpushed change queued for the same key. Records with no queued local
|
|
413
|
+
* change are never conflicts — the remote value always applies.
|
|
414
|
+
*/
|
|
415
|
+
type ConflictStrategy = "client-wins" | "server-wins" | "manual";
|
|
416
|
+
/** The state handed to a `manual` resolver for one conflicting key. */
|
|
417
|
+
interface SyncConflict<T> {
|
|
418
|
+
/** Value of the store's `keyPath` for the conflicting record. */
|
|
419
|
+
key: unknown;
|
|
420
|
+
/** Current local item — `undefined` when the queued local change is a delete. */
|
|
421
|
+
local: T | undefined;
|
|
422
|
+
/** The record as returned by the remote `pull()`. */
|
|
423
|
+
remote: T;
|
|
424
|
+
/** The queued local change that makes this key a conflict. */
|
|
425
|
+
pending: SyncChange<T>;
|
|
426
|
+
}
|
|
321
427
|
interface SyncAdapter<T> {
|
|
322
428
|
/** Push local changes to remote */
|
|
323
429
|
push: (changes: SyncChange<T>[]) => Promise<SyncResult>;
|
|
324
430
|
/** Pull remote changes since last sync */
|
|
325
431
|
pull: (since: number | null) => Promise<T[]>;
|
|
326
|
-
/**
|
|
327
|
-
|
|
432
|
+
/**
|
|
433
|
+
* Conflict resolution strategy. Defaults to `"client-wins"`.
|
|
434
|
+
*
|
|
435
|
+
* - `client-wins` — a pulled record is DISCARDED for any key that still has
|
|
436
|
+
* an unpushed local change. The local edit survives and is pushed on the
|
|
437
|
+
* next round. This is the safe default: it never destroys work made
|
|
438
|
+
* offline.
|
|
439
|
+
* - `server-wins` — the pulled record OVERWRITES local state even for keys
|
|
440
|
+
* with queued changes. The queued change itself is **retained**, not
|
|
441
|
+
* dropped: it still has to reach the server, and discarding it here would
|
|
442
|
+
* turn a display-precedence choice into silent data loss. A subsequent
|
|
443
|
+
* successful push therefore re-asserts the local edit remotely.
|
|
444
|
+
* - `manual` — `resolveConflict` decides per key. Without a `resolveConflict`
|
|
445
|
+
* implementation this degrades to `client-wins` and warns, because the only
|
|
446
|
+
* other option is to guess with the user's unsynced data.
|
|
447
|
+
*/
|
|
448
|
+
conflictStrategy?: ConflictStrategy;
|
|
449
|
+
/**
|
|
450
|
+
* Resolve one conflicting key. Required for `conflictStrategy: "manual"`.
|
|
451
|
+
*
|
|
452
|
+
* Return the value to store locally, or `undefined` to keep the local record
|
|
453
|
+
* untouched. May be async. A resolver that throws falls back to keeping the
|
|
454
|
+
* local record — the conservative direction.
|
|
455
|
+
*/
|
|
456
|
+
resolveConflict?: (conflict: SyncConflict<T>) => T | undefined | Promise<T | undefined>;
|
|
328
457
|
}
|
|
329
458
|
interface SyncChange<T> {
|
|
330
459
|
type: "put" | "delete";
|
|
@@ -369,6 +498,9 @@ interface OfflineStore<T> {
|
|
|
369
498
|
* adapter: syncAdapter({
|
|
370
499
|
* push: (changes) => fetch("/api/sync", { method: "POST", body: JSON.stringify(changes) }),
|
|
371
500
|
* pull: (since) => fetch(`/api/todos?since=${since}`).then(r => r.json()),
|
|
501
|
+
* // client-wins (the default) never lets a pulled record overwrite a key
|
|
502
|
+
* // that still has an unpushed local edit. Use "server-wins" to let the
|
|
503
|
+
* // remote value win, or "manual" plus a `resolveConflict` resolver.
|
|
372
504
|
* conflictStrategy: "client-wins",
|
|
373
505
|
* }),
|
|
374
506
|
* });
|
|
@@ -377,7 +509,7 @@ interface OfflineStore<T> {
|
|
|
377
509
|
* store.data(); // [{ id: "1", text: "Buy milk", done: false }]
|
|
378
510
|
* ```
|
|
379
511
|
*/
|
|
380
|
-
declare function offlineStore<T extends
|
|
512
|
+
declare function offlineStore<T extends object>(options: OfflineStoreOptions<T>): Promise<OfflineStore<T>>;
|
|
381
513
|
/**
|
|
382
514
|
* Helper to create a sync adapter configuration.
|
|
383
515
|
*/
|
|
@@ -464,4 +596,4 @@ declare function stream(url: string, options?: {
|
|
|
464
596
|
dispose: () => void;
|
|
465
597
|
};
|
|
466
598
|
|
|
467
|
-
export { type InfiniteQueryOptions, type InfiniteQueryResult, type LoaderRoute, type MutationOptions, type MutationResult, type OfflineStore, type OfflineStoreOptions, type QueryOptions, type QueryResult, type Resource, type ResourceOptions, type RetryOptions, type RouteLoaderFn, type SyncAdapter, type SyncChange, type SyncResult, __resetQueryCache, calculateDelay, clearQueryCache, debounce, executeLoader, getQueryData, infiniteQuery, invalidateQueries, loaderData, mutation, offlineStore, preloadRoute, previous, query, resource, setQueryData, socket, stream, syncAdapter, throttle, withRetry };
|
|
599
|
+
export { type ConflictStrategy, type InfiniteQueryOptions, type InfiniteQueryResult, type LoaderRoute, type MutationOptions, type MutationResult, type OfflineStore, type OfflineStoreOptions, type QueryOptions, type QueryResult, type Resource, type ResourceOptions, type RetryOptions, type RouteLoaderFn, type SyncAdapter, type SyncChange, type SyncConflict, type SyncResult, __resetQueryCache, calculateDelay, clearQueryCache, debounce, executeLoader, getQueryData, infiniteQuery, invalidateQueries, loaderData, mutation, offlineStore, preloadRoute, previous, query, resource, setQueryData, socket, stream, syncAdapter, throttle, withRetry };
|
package/dist/data.d.ts
CHANGED
|
@@ -13,7 +13,15 @@ interface RetryOptions {
|
|
|
13
13
|
maxDelay?: number;
|
|
14
14
|
/** Jitter factor (0-1) to randomize delay. Default: 0.1 */
|
|
15
15
|
jitter?: number;
|
|
16
|
-
/**
|
|
16
|
+
/**
|
|
17
|
+
* Predicate deciding whether a **non-cancellation** failure is retryable.
|
|
18
|
+
* Default: `() => true`.
|
|
19
|
+
*
|
|
20
|
+
* Cancellation never reaches this callback. An aborted signal, or a rejection
|
|
21
|
+
* that is itself an `AbortError`, bypasses retry policy entirely — so a
|
|
22
|
+
* `shouldRetry` returning `true` for everything still cannot resurrect a
|
|
23
|
+
* cancelled operation.
|
|
24
|
+
*/
|
|
17
25
|
shouldRetry?: (error: unknown, attempt: number) => boolean;
|
|
18
26
|
}
|
|
19
27
|
/**
|
|
@@ -24,9 +32,16 @@ declare function calculateDelay(attempt: number, strategy: "exponential" | "line
|
|
|
24
32
|
* Execute an async function with retry logic.
|
|
25
33
|
* Returns the result or throws after all retries are exhausted.
|
|
26
34
|
*
|
|
35
|
+
* CANCELLATION OUTRANKS RETRY POLICY. A cancelled operation — signalled either
|
|
36
|
+
* by `signal.aborted` or by a rejection that is itself an `AbortError` — skips
|
|
37
|
+
* `shouldRetry`, `onRetry` and the backoff, and rejects immediately.
|
|
38
|
+
*
|
|
27
39
|
* @param fn The async function to execute
|
|
28
40
|
* @param options Retry configuration
|
|
29
|
-
* @param onRetry
|
|
41
|
+
* @param onRetry Fired only when another attempt WILL actually be scheduled —
|
|
42
|
+
* never on the final failure, never for a rejection `shouldRetry` declined,
|
|
43
|
+
* and never for a cancellation. Receives the error, the attempt index, and
|
|
44
|
+
* the delay before the next attempt.
|
|
30
45
|
* @param signal AbortSignal to cancel retries
|
|
31
46
|
*
|
|
32
47
|
* @example
|
|
@@ -40,6 +55,44 @@ declare function calculateDelay(attempt: number, strategy: "exponential" | "line
|
|
|
40
55
|
*/
|
|
41
56
|
declare function withRetry<T>(fn: () => Promise<T>, options?: RetryOptions, onRetry?: (error: unknown, attempt: number, delay: number) => void, signal?: AbortSignal): Promise<T>;
|
|
42
57
|
|
|
58
|
+
/**
|
|
59
|
+
* ## Callback semantics
|
|
60
|
+
*
|
|
61
|
+
* These rules are shared by `query()`, `resource()`, `infiniteQuery()`, and
|
|
62
|
+
* `mutation()`.
|
|
63
|
+
*
|
|
64
|
+
* **A callback exception is not an operation failure.** Exceptions thrown by
|
|
65
|
+
* lifecycle callbacks (`onSuccess`, `onError`, `onSettled`, `onStart`) and by
|
|
66
|
+
* `select` do not retroactively change the success/failure state of the
|
|
67
|
+
* underlying request. A fetch that succeeded stays successful, its data stays
|
|
68
|
+
* available, and the shared cache keeps the value it committed:
|
|
69
|
+
*
|
|
70
|
+
* ```text
|
|
71
|
+
* network success → cache commit → onSuccess throws → still a success
|
|
72
|
+
* ```
|
|
73
|
+
*
|
|
74
|
+
* **Callback errors are surfaced separately.** They are reported via
|
|
75
|
+
* `console.error` (prefixed `[SibuJS data]`), never silently swallowed, and
|
|
76
|
+
* never routed into the operation's own error channel — a throwing `onSuccess`
|
|
77
|
+
* does not trigger `onError`, and does not populate `error()`.
|
|
78
|
+
*
|
|
79
|
+
* **Observers are isolated from each other.** Multiple observers sharing one
|
|
80
|
+
* cache key are notified independently: one observer's `select` throwing cannot
|
|
81
|
+
* prevent the others from receiving the shared result, and cannot poison the
|
|
82
|
+
* shared cache entry.
|
|
83
|
+
*
|
|
84
|
+
* **A throwing `select` keeps the previous data.** `select` is the observer's
|
|
85
|
+
* own transform, not part of the request. If it throws, this observer retains
|
|
86
|
+
* whatever data it already had rather than committing a value that was never
|
|
87
|
+
* produced; the request is still recorded as successful.
|
|
88
|
+
*
|
|
89
|
+
* **Ordering is guaranteed.** State is committed first, then `onSuccess` /
|
|
90
|
+
* `onError`, then `onSettled`. `onSettled` runs even when the callback before
|
|
91
|
+
* it threw.
|
|
92
|
+
*
|
|
93
|
+
* The one exception is `mutation()`'s `onMutate`, which is a step *of* the
|
|
94
|
+
* mutation rather than a notification — see `MutationOptions.onMutate`.
|
|
95
|
+
*/
|
|
43
96
|
interface QueryOptions<T> {
|
|
44
97
|
/** Time in ms before cached data is considered stale. Default: 0 (always stale) */
|
|
45
98
|
staleTime?: number;
|
|
@@ -103,10 +156,33 @@ declare function clearQueryCache(): void;
|
|
|
103
156
|
*/
|
|
104
157
|
declare function __resetQueryCache(): void;
|
|
105
158
|
|
|
159
|
+
/**
|
|
160
|
+
* Lifecycle callbacks follow the shared data-layer contract: an exception
|
|
161
|
+
* thrown by `onSuccess`, `onError`, or `onSettled` never changes the status of
|
|
162
|
+
* the mutation itself — a mutation that succeeded stays `success` and
|
|
163
|
+
* `mutateAsync` still resolves — and is reported separately via
|
|
164
|
+
* `console.error`. See `QueryOptions` for the full statement.
|
|
165
|
+
*
|
|
166
|
+
* `onMutate` is the deliberate exception; see its own note below.
|
|
167
|
+
*/
|
|
106
168
|
interface MutationOptions<TData, TVariables, TContext = unknown> {
|
|
107
169
|
/** Retry options for failed mutations */
|
|
108
170
|
retry?: RetryOptions;
|
|
109
|
-
/**
|
|
171
|
+
/**
|
|
172
|
+
* Called before the mutation — return context for rollback in `onError`.
|
|
173
|
+
*
|
|
174
|
+
* Unlike the notification callbacks, `onMutate` is a **step of** the
|
|
175
|
+
* mutation: it produces the rollback context everything downstream depends
|
|
176
|
+
* on. An ordinary exception therefore fails the mutation — there is no
|
|
177
|
+
* context, and the optimistic update it was meant to apply never happened.
|
|
178
|
+
*
|
|
179
|
+
* An `AbortError` is the one exception, and it is cancellation rather than
|
|
180
|
+
* failure — the same rule `mutationFn` follows. `isAbortError()` is
|
|
181
|
+
* authoritative for the whole operation, so where a cancellation is raised
|
|
182
|
+
* inside it does not change what it means: no error state, no `onError`, and
|
|
183
|
+
* the state the mutation replaced is restored. Throw an ordinary `Error` from
|
|
184
|
+
* `onMutate` to fail the mutation.
|
|
185
|
+
*/
|
|
110
186
|
onMutate?: (variables: TVariables) => TContext | Promise<TContext>;
|
|
111
187
|
/** Called on successful mutation */
|
|
112
188
|
onSuccess?: (data: TData, variables: TVariables, context: TContext) => void;
|
|
@@ -135,6 +211,13 @@ interface MutationResult<TData, TVariables> {
|
|
|
135
211
|
}
|
|
136
212
|
declare function mutation<TData, TVariables = void, TContext = unknown>(mutationFn: (variables: TVariables, signal?: AbortSignal) => Promise<TData>, options?: MutationOptions<TData, TVariables, TContext>): MutationResult<TData, TVariables>;
|
|
137
213
|
|
|
214
|
+
/**
|
|
215
|
+
* Lifecycle callbacks follow the shared data-layer contract: an exception
|
|
216
|
+
* thrown by `onSuccess` or `onError` never changes the success/failure state of
|
|
217
|
+
* the page fetch itself — a page that was appended stays appended — and is
|
|
218
|
+
* reported separately via `console.error`. See `QueryOptions` for the full
|
|
219
|
+
* statement.
|
|
220
|
+
*/
|
|
138
221
|
interface InfiniteQueryOptions<TData, TPageParam = number> {
|
|
139
222
|
/** Get the param for the next page. Return undefined to signal end. */
|
|
140
223
|
getNextPageParam: (lastPage: TData, allPages: TData[]) => TPageParam | undefined;
|
|
@@ -245,6 +328,12 @@ declare function debounce<T>(getter: () => T, delay: number): () => T;
|
|
|
245
328
|
*/
|
|
246
329
|
declare function throttle<T>(getter: () => T, interval: number): () => T;
|
|
247
330
|
|
|
331
|
+
/**
|
|
332
|
+
* Lifecycle callbacks follow the shared data-layer contract: an exception
|
|
333
|
+
* thrown by `onStart`, `onSuccess`, `onError`, or `onSettled` never changes the
|
|
334
|
+
* success/failure state of the fetch itself, and is reported separately via
|
|
335
|
+
* `console.error`. See `QueryOptions` for the full statement.
|
|
336
|
+
*/
|
|
248
337
|
interface ResourceOptions<T> {
|
|
249
338
|
/** Initial data value before first fetch. Default: undefined */
|
|
250
339
|
initialValue?: T;
|
|
@@ -318,13 +407,53 @@ interface OfflineStoreOptions<T> {
|
|
|
318
407
|
/** Auto-sync when online status changes (default: true) */
|
|
319
408
|
autoSync?: boolean;
|
|
320
409
|
}
|
|
410
|
+
/**
|
|
411
|
+
* How a pulled remote record is reconciled with a local record that still has
|
|
412
|
+
* an unpushed change queued for the same key. Records with no queued local
|
|
413
|
+
* change are never conflicts — the remote value always applies.
|
|
414
|
+
*/
|
|
415
|
+
type ConflictStrategy = "client-wins" | "server-wins" | "manual";
|
|
416
|
+
/** The state handed to a `manual` resolver for one conflicting key. */
|
|
417
|
+
interface SyncConflict<T> {
|
|
418
|
+
/** Value of the store's `keyPath` for the conflicting record. */
|
|
419
|
+
key: unknown;
|
|
420
|
+
/** Current local item — `undefined` when the queued local change is a delete. */
|
|
421
|
+
local: T | undefined;
|
|
422
|
+
/** The record as returned by the remote `pull()`. */
|
|
423
|
+
remote: T;
|
|
424
|
+
/** The queued local change that makes this key a conflict. */
|
|
425
|
+
pending: SyncChange<T>;
|
|
426
|
+
}
|
|
321
427
|
interface SyncAdapter<T> {
|
|
322
428
|
/** Push local changes to remote */
|
|
323
429
|
push: (changes: SyncChange<T>[]) => Promise<SyncResult>;
|
|
324
430
|
/** Pull remote changes since last sync */
|
|
325
431
|
pull: (since: number | null) => Promise<T[]>;
|
|
326
|
-
/**
|
|
327
|
-
|
|
432
|
+
/**
|
|
433
|
+
* Conflict resolution strategy. Defaults to `"client-wins"`.
|
|
434
|
+
*
|
|
435
|
+
* - `client-wins` — a pulled record is DISCARDED for any key that still has
|
|
436
|
+
* an unpushed local change. The local edit survives and is pushed on the
|
|
437
|
+
* next round. This is the safe default: it never destroys work made
|
|
438
|
+
* offline.
|
|
439
|
+
* - `server-wins` — the pulled record OVERWRITES local state even for keys
|
|
440
|
+
* with queued changes. The queued change itself is **retained**, not
|
|
441
|
+
* dropped: it still has to reach the server, and discarding it here would
|
|
442
|
+
* turn a display-precedence choice into silent data loss. A subsequent
|
|
443
|
+
* successful push therefore re-asserts the local edit remotely.
|
|
444
|
+
* - `manual` — `resolveConflict` decides per key. Without a `resolveConflict`
|
|
445
|
+
* implementation this degrades to `client-wins` and warns, because the only
|
|
446
|
+
* other option is to guess with the user's unsynced data.
|
|
447
|
+
*/
|
|
448
|
+
conflictStrategy?: ConflictStrategy;
|
|
449
|
+
/**
|
|
450
|
+
* Resolve one conflicting key. Required for `conflictStrategy: "manual"`.
|
|
451
|
+
*
|
|
452
|
+
* Return the value to store locally, or `undefined` to keep the local record
|
|
453
|
+
* untouched. May be async. A resolver that throws falls back to keeping the
|
|
454
|
+
* local record — the conservative direction.
|
|
455
|
+
*/
|
|
456
|
+
resolveConflict?: (conflict: SyncConflict<T>) => T | undefined | Promise<T | undefined>;
|
|
328
457
|
}
|
|
329
458
|
interface SyncChange<T> {
|
|
330
459
|
type: "put" | "delete";
|
|
@@ -369,6 +498,9 @@ interface OfflineStore<T> {
|
|
|
369
498
|
* adapter: syncAdapter({
|
|
370
499
|
* push: (changes) => fetch("/api/sync", { method: "POST", body: JSON.stringify(changes) }),
|
|
371
500
|
* pull: (since) => fetch(`/api/todos?since=${since}`).then(r => r.json()),
|
|
501
|
+
* // client-wins (the default) never lets a pulled record overwrite a key
|
|
502
|
+
* // that still has an unpushed local edit. Use "server-wins" to let the
|
|
503
|
+
* // remote value win, or "manual" plus a `resolveConflict` resolver.
|
|
372
504
|
* conflictStrategy: "client-wins",
|
|
373
505
|
* }),
|
|
374
506
|
* });
|
|
@@ -377,7 +509,7 @@ interface OfflineStore<T> {
|
|
|
377
509
|
* store.data(); // [{ id: "1", text: "Buy milk", done: false }]
|
|
378
510
|
* ```
|
|
379
511
|
*/
|
|
380
|
-
declare function offlineStore<T extends
|
|
512
|
+
declare function offlineStore<T extends object>(options: OfflineStoreOptions<T>): Promise<OfflineStore<T>>;
|
|
381
513
|
/**
|
|
382
514
|
* Helper to create a sync adapter configuration.
|
|
383
515
|
*/
|
|
@@ -464,4 +596,4 @@ declare function stream(url: string, options?: {
|
|
|
464
596
|
dispose: () => void;
|
|
465
597
|
};
|
|
466
598
|
|
|
467
|
-
export { type InfiniteQueryOptions, type InfiniteQueryResult, type LoaderRoute, type MutationOptions, type MutationResult, type OfflineStore, type OfflineStoreOptions, type QueryOptions, type QueryResult, type Resource, type ResourceOptions, type RetryOptions, type RouteLoaderFn, type SyncAdapter, type SyncChange, type SyncResult, __resetQueryCache, calculateDelay, clearQueryCache, debounce, executeLoader, getQueryData, infiniteQuery, invalidateQueries, loaderData, mutation, offlineStore, preloadRoute, previous, query, resource, setQueryData, socket, stream, syncAdapter, throttle, withRetry };
|
|
599
|
+
export { type ConflictStrategy, type InfiniteQueryOptions, type InfiniteQueryResult, type LoaderRoute, type MutationOptions, type MutationResult, type OfflineStore, type OfflineStoreOptions, type QueryOptions, type QueryResult, type Resource, type ResourceOptions, type RetryOptions, type RouteLoaderFn, type SyncAdapter, type SyncChange, type SyncConflict, type SyncResult, __resetQueryCache, calculateDelay, clearQueryCache, debounce, executeLoader, getQueryData, infiniteQuery, invalidateQueries, loaderData, mutation, offlineStore, preloadRoute, previous, query, resource, setQueryData, socket, stream, syncAdapter, throttle, withRetry };
|
package/dist/data.js
CHANGED
|
@@ -20,16 +20,16 @@ import {
|
|
|
20
20
|
syncAdapter,
|
|
21
21
|
throttle,
|
|
22
22
|
withRetry
|
|
23
|
-
} from "./chunk-
|
|
24
|
-
import "./chunk-
|
|
25
|
-
import "./chunk-
|
|
23
|
+
} from "./chunk-NF3LYCQH.js";
|
|
24
|
+
import "./chunk-O3QQ3XOL.js";
|
|
25
|
+
import "./chunk-Z2CE42DB.js";
|
|
26
|
+
import "./chunk-5INI7D2L.js";
|
|
26
27
|
import "./chunk-7ZHH77QA.js";
|
|
27
|
-
import "./chunk-
|
|
28
|
-
import "./chunk-
|
|
29
|
-
import "./chunk-
|
|
30
|
-
import "./chunk-
|
|
31
|
-
import "./chunk-
|
|
32
|
-
import "./chunk-COY6PUD2.js";
|
|
28
|
+
import "./chunk-PBQEW5VW.js";
|
|
29
|
+
import "./chunk-TIRZCERI.js";
|
|
30
|
+
import "./chunk-W55YHPEP.js";
|
|
31
|
+
import "./chunk-ACBT6UZS.js";
|
|
32
|
+
import "./chunk-VPP2FONR.js";
|
|
33
33
|
export {
|
|
34
34
|
__resetQueryCache,
|
|
35
35
|
calculateDelay,
|