@syncular/react 0.4.1 → 0.5.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/README.md +134 -117
- package/dist/client.d.ts +13 -1
- package/dist/client.js +4 -0
- package/dist/index.d.ts +5 -4
- package/dist/index.js +5 -4
- package/dist/provider.d.ts +6 -1
- package/dist/provider.js +53 -4
- package/dist/resource.d.ts +18 -0
- package/dist/resource.js +56 -0
- package/dist/use-client.d.ts +2 -0
- package/dist/use-client.js +8 -1
- package/dist/use-conflicts.d.ts +2 -5
- package/dist/use-conflicts.js +12 -35
- package/dist/use-mutation.d.ts +22 -12
- package/dist/use-mutation.js +61 -21
- package/dist/use-query.d.ts +10 -3
- package/dist/use-query.js +7 -2
- package/dist/use-raw-sql.d.ts +15 -29
- package/dist/use-raw-sql.js +54 -186
- package/dist/use-sync-status.d.ts +1 -18
- package/dist/use-sync-status.js +15 -65
- package/dist/use-window.d.ts +13 -25
- package/dist/use-window.js +55 -91
- package/package.json +4 -4
- package/src/client.ts +29 -0
- package/src/index.ts +26 -4
- package/src/provider.ts +89 -7
- package/src/resource.ts +77 -0
- package/src/use-client.ts +12 -1
- package/src/use-conflicts.ts +18 -37
- package/src/use-mutation.ts +126 -25
- package/src/use-query.ts +27 -8
- package/src/use-raw-sql.ts +83 -212
- package/src/use-sync-status.ts +20 -73
- package/src/use-window.ts +90 -103
- package/dist/query-churn.d.ts +0 -109
- package/dist/query-churn.js +0 -276
- package/src/query-churn.ts +0 -317
package/src/use-sync-status.ts
CHANGED
|
@@ -1,87 +1,34 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `useSyncStatus` — the client's operational status: outbox depth,
|
|
3
|
-
* upgrading (§7.4.5), leaseState (§7.3.5), schemaFloor (§1.6), and
|
|
4
|
-
* syncNeeded (§8.4). Status is derived by polling the normalized accessors
|
|
5
|
-
* after every apply batch (invalidation is the natural "something changed"
|
|
6
|
-
* signal) plus an initial read; `refresh()` re-reads on demand.
|
|
7
|
-
*
|
|
8
|
-
* `online` is not a protocol concept the client exposes directly (§1.3
|
|
9
|
-
* transport-owned), so it is reported as `undefined` unless a future
|
|
10
|
-
* accessor lands — the hook surfaces what the core actually knows, never a
|
|
11
|
-
* guessed value.
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
1
|
import type { LeaseState, SchemaFloor } from '@syncular/client';
|
|
15
|
-
import { useCallback,
|
|
16
|
-
import {
|
|
2
|
+
import { useCallback, useSyncExternalStore } from 'react';
|
|
3
|
+
import { useReactiveStore } from './use-client';
|
|
17
4
|
|
|
18
5
|
export interface SyncStatus {
|
|
19
|
-
/** Pending outbox commits (unpushed local writes). */
|
|
20
6
|
readonly outbox: number;
|
|
21
|
-
/** §7.4.5: a schema-bump reset + first re-bootstrap is in flight. */
|
|
22
7
|
readonly upgrading: boolean;
|
|
23
|
-
/** §7.3.5: opaque auth-lease state, or undefined. */
|
|
24
8
|
readonly leaseState: LeaseState | undefined;
|
|
25
|
-
/** §1.6: server-declared schema floor (syncing stopped), or undefined. */
|
|
26
9
|
readonly schemaFloor: SchemaFloor | undefined;
|
|
27
|
-
/** §8.4: the host loop should run a pull soon. */
|
|
28
10
|
readonly syncNeeded: boolean;
|
|
29
|
-
/** True until the first status read resolves. */
|
|
30
11
|
readonly isLoading: boolean;
|
|
12
|
+
readonly error: Error | undefined;
|
|
31
13
|
readonly refresh: () => void;
|
|
32
14
|
}
|
|
33
15
|
|
|
34
|
-
const INITIAL: Omit<SyncStatus, 'refresh'> = {
|
|
35
|
-
outbox: 0,
|
|
36
|
-
upgrading: false,
|
|
37
|
-
leaseState: undefined,
|
|
38
|
-
schemaFloor: undefined,
|
|
39
|
-
syncNeeded: false,
|
|
40
|
-
isLoading: true,
|
|
41
|
-
};
|
|
42
|
-
|
|
43
16
|
export function useSyncStatus(): SyncStatus {
|
|
44
|
-
const
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
.then(([pending, upgrading, leaseState, schemaFloor, syncNeeded]) => {
|
|
62
|
-
if (cancelled) return;
|
|
63
|
-
setState({
|
|
64
|
-
outbox: pending.length,
|
|
65
|
-
upgrading,
|
|
66
|
-
leaseState,
|
|
67
|
-
schemaFloor,
|
|
68
|
-
syncNeeded,
|
|
69
|
-
isLoading: false,
|
|
70
|
-
});
|
|
71
|
-
})
|
|
72
|
-
.catch(() => {
|
|
73
|
-
if (!cancelled) setState((s) => ({ ...s, isLoading: false }));
|
|
74
|
-
});
|
|
75
|
-
};
|
|
76
|
-
readRef.current = read;
|
|
77
|
-
read();
|
|
78
|
-
// Re-read on every apply batch — the "state changed" edge.
|
|
79
|
-
const unsubscribe = client.onInvalidate(read);
|
|
80
|
-
return () => {
|
|
81
|
-
cancelled = true;
|
|
82
|
-
unsubscribe();
|
|
83
|
-
};
|
|
84
|
-
}, [client]);
|
|
85
|
-
|
|
86
|
-
return { ...state, refresh };
|
|
17
|
+
const entry = useReactiveStore().status;
|
|
18
|
+
const snapshot = useSyncExternalStore(
|
|
19
|
+
entry.subscribe,
|
|
20
|
+
entry.getSnapshot,
|
|
21
|
+
entry.getSnapshot,
|
|
22
|
+
);
|
|
23
|
+
const refresh = useCallback(() => entry.refresh(), [entry]);
|
|
24
|
+
return {
|
|
25
|
+
outbox: snapshot.status?.outbox ?? 0,
|
|
26
|
+
upgrading: snapshot.status?.upgrading ?? false,
|
|
27
|
+
leaseState: snapshot.status?.leaseState,
|
|
28
|
+
schemaFloor: snapshot.status?.schemaFloor,
|
|
29
|
+
syncNeeded: snapshot.status?.syncNeeded ?? false,
|
|
30
|
+
isLoading: snapshot.isLoading,
|
|
31
|
+
error: snapshot.error,
|
|
32
|
+
refresh,
|
|
33
|
+
};
|
|
87
34
|
}
|
package/src/use-window.ts
CHANGED
|
@@ -1,129 +1,116 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `useWindow(base)` — the windowed-sync surface for a component
|
|
3
|
-
* (SPEC.md §4.8 / DESIGN-eviction.md W1, I3). It manages the live window
|
|
4
|
-
* units for a base and exposes the **completeness oracle**: which scope
|
|
5
|
-
* values are held locally in full, so a consumer can render "this data may
|
|
6
|
-
* be partial" honestly instead of silently serving a partial replica as
|
|
7
|
-
* complete.
|
|
8
|
-
*
|
|
9
|
-
* - `setWindow(units)` swaps the live set (added units bootstrap via the
|
|
10
|
-
* image lane; removed units are evicted, fused with unsubscription).
|
|
11
|
-
* - `units` is the current windowed-in set, `pending` the subset whose
|
|
12
|
-
* bootstrap has not yet landed (re-read on mount and whenever the base's
|
|
13
|
-
* table is invalidated — a deferred eviction draining, a re-entry
|
|
14
|
-
* bootstrapping, or a bootstrap completing all update the verdict).
|
|
15
|
-
* - `isComplete(unit)` is the per-value verdict: registered AND
|
|
16
|
-
* bootstrap-complete. A live query whose scope footprint includes a
|
|
17
|
-
* non-`isComplete` unit is a **window miss or still loading** — widen,
|
|
18
|
-
* wait, or show partial, never claim complete. Between `setWindow` and
|
|
19
|
-
* the unit's bootstrap landing the verdict is `false` (the local replica
|
|
20
|
-
* is empty or partial there — never a false "empty" render).
|
|
21
|
-
*/
|
|
22
1
|
import {
|
|
2
|
+
canonicalValue,
|
|
23
3
|
type WindowBase,
|
|
24
|
-
type WindowState,
|
|
25
4
|
windowComplete,
|
|
26
5
|
} from '@syncular/client';
|
|
27
|
-
import {
|
|
28
|
-
|
|
29
|
-
|
|
6
|
+
import {
|
|
7
|
+
useCallback,
|
|
8
|
+
useEffect,
|
|
9
|
+
useMemo,
|
|
10
|
+
useRef,
|
|
11
|
+
useState,
|
|
12
|
+
useSyncExternalStore,
|
|
13
|
+
} from 'react';
|
|
14
|
+
import { useReactiveStore } from './use-client';
|
|
30
15
|
|
|
31
16
|
export interface UseWindowResult {
|
|
32
|
-
/** The scope values currently windowed-in for this base. */
|
|
33
17
|
readonly units: readonly string[];
|
|
34
|
-
/** Registered units whose bootstrap has not yet completed (§4.8). */
|
|
35
18
|
readonly pending: readonly string[];
|
|
36
|
-
/**
|
|
19
|
+
/** Update this component's claim; the store applies the union of all claims. */
|
|
37
20
|
readonly setWindow: (units: readonly string[]) => Promise<void>;
|
|
38
|
-
/** True iff `unit` is windowed-in AND bootstrapped (answerable, I3). */
|
|
39
21
|
readonly isComplete: (unit: string) => boolean;
|
|
40
22
|
}
|
|
41
23
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
24
|
+
export interface UseRetainedWindowResult {
|
|
25
|
+
/** True until the retained working set has reached the core window. */
|
|
26
|
+
readonly isPending: boolean;
|
|
27
|
+
/** Registration failure, if the host rejects the window change. */
|
|
28
|
+
readonly error: Error | undefined;
|
|
29
|
+
}
|
|
47
30
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
31
|
+
/**
|
|
32
|
+
* Retain a known working set for the lifetime of this component. Retention
|
|
33
|
+
* composes with generated query coverage and other owners; cleanup releases
|
|
34
|
+
* only this hook's claim. Ordinary selected queries still claim themselves.
|
|
35
|
+
*/
|
|
36
|
+
export function useRetainedWindow(
|
|
37
|
+
base: WindowBase,
|
|
38
|
+
units: readonly string[],
|
|
39
|
+
): UseRetainedWindowResult {
|
|
40
|
+
const store = useReactiveStore();
|
|
41
|
+
const baseIdentity = canonicalValue(base);
|
|
42
|
+
const unitsIdentity = canonicalValue([...new Set(units)].sort());
|
|
43
|
+
// biome-ignore lint/correctness/useExhaustiveDependencies: canonical identities represent the complete values
|
|
44
|
+
const stableBase = useMemo(() => base, [baseIdentity]);
|
|
45
|
+
// biome-ignore lint/correctness/useExhaustiveDependencies: unitsIdentity represents the normalized unit set
|
|
46
|
+
const stableUnits = useMemo(
|
|
47
|
+
() => [...new Set(units)].sort(),
|
|
48
|
+
[unitsIdentity],
|
|
49
|
+
);
|
|
50
|
+
const [state, setState] = useState<UseRetainedWindowResult>({
|
|
51
|
+
isPending: true,
|
|
52
|
+
error: undefined,
|
|
53
|
+
});
|
|
57
54
|
|
|
58
|
-
// `baseKey` re-keys the effect without being read in the body (biome cannot
|
|
59
|
-
// see the ref indirection) — the dep list is pinned deliberately.
|
|
60
|
-
// biome-ignore lint/correctness/useExhaustiveDependencies: baseKey re-keys the effect for a fresh base object
|
|
61
55
|
useEffect(() => {
|
|
62
|
-
let
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
56
|
+
let active = true;
|
|
57
|
+
setState((current) =>
|
|
58
|
+
current.isPending && current.error === undefined
|
|
59
|
+
? current
|
|
60
|
+
: { isPending: true, error: undefined },
|
|
61
|
+
);
|
|
62
|
+
const retention = store.retainWindow(stableBase, stableUnits);
|
|
63
|
+
void retention.ready.then(
|
|
64
|
+
() => {
|
|
65
|
+
if (active) setState({ isPending: false, error: undefined });
|
|
66
|
+
},
|
|
67
|
+
(caught: unknown) => {
|
|
68
|
+
if (!active) return;
|
|
69
|
+
setState({
|
|
70
|
+
isPending: false,
|
|
71
|
+
error: caught instanceof Error ? caught : new Error(String(caught)),
|
|
70
72
|
});
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
// (E1) completing, a re-entry bootstrapping, or a bootstrap completing
|
|
74
|
-
// all invalidate it. The re-read is frame-coalesced like the query
|
|
75
|
-
// hooks' re-runs, then deferred ONE MORE boundary before issuing: every
|
|
76
|
-
// query re-run for the same event has issued its read by then, so on
|
|
77
|
-
// the in-order client channel the pendency verdict resolves AFTER the
|
|
78
|
-
// rows it vouches for. Bootstrap-completion commit order is therefore
|
|
79
|
-
// rows first, pending→complete second — a consumer gating "empty" on
|
|
80
|
-
// `isComplete` never paints a false empty over a stale result (§4.8
|
|
81
|
-
// honesty at the render boundary, not just in the oracle). Both
|
|
82
|
-
// boundaries run INSIDE the scheduler (a two-phase callback re-arming
|
|
83
|
-
// itself once) so a fire parked in a suspended rAF stays covered by the
|
|
84
|
-
// hidden-document rescue — a bare second rAF would not be.
|
|
85
|
-
let armed = false;
|
|
86
|
-
const scheduler = new FrameScheduler(() => {
|
|
87
|
-
if (!armed) {
|
|
88
|
-
armed = true;
|
|
89
|
-
scheduler.schedule(); // boundary two via the dirty/re-run contract
|
|
90
|
-
return;
|
|
91
|
-
}
|
|
92
|
-
armed = false;
|
|
93
|
-
return read();
|
|
94
|
-
});
|
|
95
|
-
const unsubscribe = client.onInvalidate((event) => {
|
|
96
|
-
if (event.tables.has(baseRef.current.table)) scheduler.schedule();
|
|
97
|
-
});
|
|
73
|
+
},
|
|
74
|
+
);
|
|
98
75
|
return () => {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
unsubscribe();
|
|
76
|
+
active = false;
|
|
77
|
+
retention.release();
|
|
102
78
|
};
|
|
103
|
-
}, [
|
|
79
|
+
}, [stableBase, stableUnits, store]);
|
|
104
80
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
81
|
+
return state;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function useWindow(base: WindowBase): UseWindowResult {
|
|
85
|
+
const store = useReactiveStore();
|
|
86
|
+
const owner = useRef(Symbol('useWindow'));
|
|
87
|
+
const baseIdentity = canonicalValue(base);
|
|
88
|
+
// Window bases are value objects commonly recreated during render. Preserve
|
|
89
|
+
// the prior object while its canonical value is unchanged.
|
|
90
|
+
// biome-ignore lint/correctness/useExhaustiveDependencies: baseIdentity represents the complete value
|
|
91
|
+
const stableBase = useMemo(() => base, [baseIdentity]);
|
|
92
|
+
const entry = useMemo(() => store.window(stableBase), [store, stableBase]);
|
|
93
|
+
const state = useSyncExternalStore(
|
|
94
|
+
entry.subscribe,
|
|
95
|
+
entry.getSnapshot,
|
|
96
|
+
entry.getSnapshot,
|
|
121
97
|
);
|
|
122
98
|
|
|
99
|
+
useEffect(() => () => store.releaseWindowClaims(owner.current), [store]);
|
|
100
|
+
|
|
101
|
+
const setWindow = useCallback(
|
|
102
|
+
(units: readonly string[]) =>
|
|
103
|
+
store.setWindowClaim(owner.current, stableBase, units),
|
|
104
|
+
[stableBase, store],
|
|
105
|
+
);
|
|
123
106
|
const isComplete = useCallback(
|
|
124
107
|
(unit: string) => windowComplete(state, unit),
|
|
125
108
|
[state],
|
|
126
109
|
);
|
|
127
|
-
|
|
128
|
-
|
|
110
|
+
return {
|
|
111
|
+
units: state.units,
|
|
112
|
+
pending: state.pending,
|
|
113
|
+
setWindow,
|
|
114
|
+
isComplete,
|
|
115
|
+
};
|
|
129
116
|
}
|
package/dist/query-churn.d.ts
DELETED
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Live-query churn hardening — the three cheap levers the query hooks share
|
|
3
|
-
* (block 4 "live-query churn" item). Under constant sync churn a naive live
|
|
4
|
-
* query re-renders and re-queries once per invalidation event; these levers
|
|
5
|
-
* cap both without reaching for IVM:
|
|
6
|
-
*
|
|
7
|
-
* 1. {@link reconcileRows} — result stability. After a re-run, compare the new
|
|
8
|
-
* result to the previous one. Whole-result equal → the caller skips
|
|
9
|
-
* `setRows` entirely (zero re-render). Otherwise build the new array but
|
|
10
|
-
* REUSE the previous row object for every row whose content is unchanged,
|
|
11
|
-
* so `React.memo`'d row components keyed by row identity skip re-render.
|
|
12
|
-
* 2. {@link FrameScheduler} — frame-coalesced re-query scheduling. Many
|
|
13
|
-
* invalidation events between paints collapse to ONE re-run per query.
|
|
14
|
-
* 3. scope-key filtering lives in {@link ../use-raw-sql} `eventMatches`
|
|
15
|
-
* (it needs the event + the hook's options), documented there.
|
|
16
|
-
*
|
|
17
|
-
* Row identity mechanism (the honest key): the hook knows no primary key —
|
|
18
|
-
* rows are plain JSON-able objects out of SQLite — so per-row content equality
|
|
19
|
-
* IS the identity. We hash each row once with a stable JSON serialization
|
|
20
|
-
* (sorted keys, so column order can't spuriously differ) and match by index:
|
|
21
|
-
* a live query's ORDER BY makes index the stable position, and an unchanged
|
|
22
|
-
* row at position i keeps its object so memoized components skip. This is O(n)
|
|
23
|
-
* in the row count with one string hash per row — bounded and measured (~0.2ms
|
|
24
|
-
* for 1k narrow rows in bun; see query-churn.test.ts).
|
|
25
|
-
*/
|
|
26
|
-
/**
|
|
27
|
-
* A stable content hash for one row: JSON with keys sorted, so two rows with
|
|
28
|
-
* the same columns in a different order hash equal (SQLite projection order is
|
|
29
|
-
* stable per query, but sorting removes any dependence on it and is cheap for
|
|
30
|
-
* the narrow rows a row-component renders). Uint8Array values (rare in a
|
|
31
|
-
* projection) serialize by byte view so a fresh copy of equal bytes hashes
|
|
32
|
-
* equal rather than to `{}`.
|
|
33
|
-
*/
|
|
34
|
-
export declare function hashRow(row: unknown): string;
|
|
35
|
-
/** The precomputed hash carrier for the previous result, so we hash once. */
|
|
36
|
-
export interface HashedRows<Row> {
|
|
37
|
-
readonly rows: readonly Row[];
|
|
38
|
-
readonly hashes: readonly string[];
|
|
39
|
-
}
|
|
40
|
-
export declare function hashRows<Row>(rows: readonly Row[]): HashedRows<Row>;
|
|
41
|
-
export interface ReconcileResult<Row> {
|
|
42
|
-
/**
|
|
43
|
-
* `undefined` → the whole result is unchanged; the caller MUST NOT call
|
|
44
|
-
* setRows (zero re-render, lever 1a). Otherwise the reconciled array to set,
|
|
45
|
-
* with previous row objects reused wherever a row's content was unchanged
|
|
46
|
-
* (lever 1b).
|
|
47
|
-
*/
|
|
48
|
-
readonly next: HashedRows<Row> | undefined;
|
|
49
|
-
}
|
|
50
|
-
/**
|
|
51
|
-
* Reconcile a freshly-queried result against the previous hashed result.
|
|
52
|
-
* Returns `next: undefined` when nothing changed at all; otherwise a new
|
|
53
|
-
* hashed result whose row objects are the PREVIOUS objects wherever content is
|
|
54
|
-
* unchanged (matched by index), so identity-keyed memo components skip.
|
|
55
|
-
*/
|
|
56
|
-
export declare function reconcileRows<Row>(prev: HashedRows<Row> | undefined, fresh: readonly Row[]): ReconcileResult<Row>;
|
|
57
|
-
/**
|
|
58
|
-
* A per-query re-run scheduler that coalesces bursts of invalidation events
|
|
59
|
-
* into ONE run per paint. Multiple `schedule()` calls before the next flush
|
|
60
|
-
* run the callback once. A `schedule()` that arrives WHILE the callback is
|
|
61
|
-
* running (re-entrant, or an event during an async re-query) marks dirty and
|
|
62
|
-
* runs the callback exactly once more after — never lost, never concurrent.
|
|
63
|
-
*
|
|
64
|
-
* Timing source: `requestAnimationFrame` when the host has it AND the document
|
|
65
|
-
* is visible (a real browser paints one frame; the coalescing window is a
|
|
66
|
-
* frame), else a microtask via a resolved promise (bun tests have no rAF —
|
|
67
|
-
* this keeps them deterministic and timer-free, honoring the no-timers
|
|
68
|
-
* doctrine: it's a readiness turn, not a wall-clock sleep). {@link flush} runs
|
|
69
|
-
* any pending callback synchronously for tests, so no arbitrary sleeps are
|
|
70
|
-
* needed to observe coalescing.
|
|
71
|
-
*
|
|
72
|
-
* Hidden documents: browsers SUSPEND rAF while a page is hidden (background
|
|
73
|
-
* tab, occluded webview, headless embed), so a frame parked there fires only
|
|
74
|
-
* when the page becomes visible again — and a page that is never visible would
|
|
75
|
-
* freeze its live queries forever while invalidations keep arriving. Two
|
|
76
|
-
* guards keep the schedule honest: a `schedule()` issued while hidden goes to
|
|
77
|
-
* the microtask boundary (there is no paint to coalesce against anyway), and a
|
|
78
|
-
* visible → hidden transition re-dispatches any frame already parked in rAF to
|
|
79
|
-
* a microtask (the stale rAF callback later no-ops via the `#scheduled` guard
|
|
80
|
-
* in {@link #fire}).
|
|
81
|
-
*/
|
|
82
|
-
export declare class FrameScheduler {
|
|
83
|
-
#private;
|
|
84
|
-
constructor(callback: () => void | Promise<void>);
|
|
85
|
-
/** Request a run. Coalesces until the next frame/microtask boundary. */
|
|
86
|
-
schedule(): void;
|
|
87
|
-
/**
|
|
88
|
-
* @internal — the visible → hidden transition hands a frame parked in the
|
|
89
|
-
* (now suspended) rAF to the microtask boundary, so live queries keep
|
|
90
|
-
* converging off-screen. A no-op when nothing is pending.
|
|
91
|
-
*/
|
|
92
|
-
redispatchPending(): void;
|
|
93
|
-
/**
|
|
94
|
-
* Synchronously run any pending scheduled callback NOW (test determinism).
|
|
95
|
-
* Returns whatever the callback returned (a Promise for the async host path)
|
|
96
|
-
* so a test can await the re-query settling without a sleep. A no-op when
|
|
97
|
-
* nothing is pending.
|
|
98
|
-
*/
|
|
99
|
-
flush(): void | Promise<void>;
|
|
100
|
-
/** Drop the callback so a torn-down hook's pending frame is a no-op. */
|
|
101
|
-
dispose(): void;
|
|
102
|
-
}
|
|
103
|
-
/**
|
|
104
|
-
* TEST-ONLY: synchronously flush every live scheduler's pending frame, so a
|
|
105
|
-
* test can observe the coalesced re-query without a wall-clock sleep (the
|
|
106
|
-
* no-timers doctrine — a readiness flush, injected, not a timer). Returns a
|
|
107
|
-
* Promise that settles when every flushed async re-query has settled.
|
|
108
|
-
*/
|
|
109
|
-
export declare function flushQuerySchedulers(): Promise<void>;
|