local-replica 0.0.1 → 0.0.3
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/{keys-BTsqwfEa.js → createReplicaEngine-CdR-URHo.js} +275 -243
- package/dist/createReplicaEngine-CdR-URHo.js.map +1 -0
- package/dist/{index-CFbCHM7Q.d.ts → index-DsElitHD.d.ts} +296 -311
- package/dist/index.d.ts +2 -2
- package/dist/index.js +97 -55
- package/dist/index.js.map +1 -1
- package/dist/zustand/index.d.ts +38 -47
- package/dist/zustand/index.js +383 -322
- package/dist/zustand/index.js.map +1 -1
- package/package.json +2 -1
- package/dist/keys-BTsqwfEa.js.map +0 -1
|
@@ -1,18 +1,50 @@
|
|
|
1
|
+
/** Address of one persisted row: the resource's row key inside an identity scope. */
|
|
2
|
+
interface ReplicaRowKey {
|
|
3
|
+
queryKey: string;
|
|
4
|
+
scope: string;
|
|
5
|
+
}
|
|
6
|
+
interface ReplicaRow<T> {
|
|
7
|
+
data: T;
|
|
8
|
+
updatedAt: number;
|
|
9
|
+
}
|
|
1
10
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* - `forward`: the view is head-first, next pages append at the END
|
|
8
|
-
* (sidebar lists: newest topic first, "load more" below).
|
|
9
|
-
* - `backward`: the view is oldest-first, next pages PREPEND at the start
|
|
10
|
-
* (chat transcripts: newest message last, "load older" on scroll-up).
|
|
11
|
-
* - `offset` mode: the cursor is the page index (0 = head).
|
|
12
|
-
* `cursor` mode: the server hands back an opaque `nextCursor`.
|
|
13
|
-
*
|
|
14
|
-
* Every function is pure; the store binding owns staleness and scope.
|
|
11
|
+
* Where a replica keeps its rows between sessions (IndexedDB, localStorage …).
|
|
12
|
+
* The server stays the durable source of truth, so `get` is best-effort: it
|
|
13
|
+
* swallows its own I/O failures and reports a miss. Writes are not: `set` and
|
|
14
|
+
* `remove` must reject when they did not land (see each method), so the
|
|
15
|
+
* engine can retry them instead of treating a dropped row as persisted.
|
|
15
16
|
*/
|
|
17
|
+
interface ReplicaStorage<T> {
|
|
18
|
+
get: (key: ReplicaRowKey) => Promise<ReplicaRow<T> | undefined>;
|
|
19
|
+
/**
|
|
20
|
+
* Strict read for destructive passes: like `get`, but **rejects** when the
|
|
21
|
+
* row could not be read (I/O error, unparsable value) instead of reporting a
|
|
22
|
+
* miss. The engine reads its per-scope index (and every row it patches)
|
|
23
|
+
* through it before `remove` / `clear` / an entity patch / an eviction, so an
|
|
24
|
+
* unreadable index fails the pass rather than passing for an empty one.
|
|
25
|
+
*
|
|
26
|
+
* Optional only for compatibility: a storage without it still hydrates and
|
|
27
|
+
* persists, but every destructive pass **fails** (`clear()` resolves `false`,
|
|
28
|
+
* a removal stays unpurged and is retried, an entity guard is kept) because
|
|
29
|
+
* the engine cannot tell a miss from a failed read through `get`.
|
|
30
|
+
*/
|
|
31
|
+
getStrict?: (key: ReplicaRowKey) => Promise<ReplicaRow<T> | undefined>;
|
|
32
|
+
/**
|
|
33
|
+
* Delete a row. Must **reject** when the delete did not land (a closed
|
|
34
|
+
* IndexedDB, a quota or security error): the engine keeps the row indexed
|
|
35
|
+
* and the removal unpurged, so the next `remove` / `clear` retries it instead
|
|
36
|
+
* of letting a row the user deleted hydrate again. A missing row is a success.
|
|
37
|
+
*/
|
|
38
|
+
remove: (key: ReplicaRowKey) => Promise<void>;
|
|
39
|
+
/**
|
|
40
|
+
* Write a row. Should **reject** when the write did not land: the engine only
|
|
41
|
+
* treats a row as holding a value after `set` resolved, so a rejected write is
|
|
42
|
+
* retried by the next persist rather than skipped as redundant. A storage that
|
|
43
|
+
* swallows failures still works, but a dropped write then survives until the
|
|
44
|
+
* value changes or the next session.
|
|
45
|
+
*/
|
|
46
|
+
set: (key: ReplicaRowKey, row: ReplicaRow<T>) => Promise<void>;
|
|
47
|
+
}
|
|
16
48
|
type ReplicaPagingMode = 'cursor' | 'offset';
|
|
17
49
|
type ReplicaPagingDirection = 'backward' | 'forward';
|
|
18
50
|
interface ReplicaPagingConfig<TItem, TCursor = any> {
|
|
@@ -109,115 +141,6 @@ interface ReplicaPagingContext<TItem> {
|
|
|
109
141
|
*/
|
|
110
142
|
isClientOnly?: (item: TItem) => boolean;
|
|
111
143
|
}
|
|
112
|
-
/** Concatenate in view order: forward = head first, backward = head last. */
|
|
113
|
-
declare const joinPages: <TItem>(head: TItem[], rest: TItem[], direction: ReplicaPagingDirection) => TItem[];
|
|
114
|
-
/** The paging config one entry reads with: `directionOf` resolved for `key`. */
|
|
115
|
-
declare const pagingFor: <TItem, TCursor>(config: ReplicaPagingConfig<TItem, TCursor>, key: string) => ReplicaPagingConfig<TItem, TCursor>;
|
|
116
|
-
/**
|
|
117
|
-
* Cursor to request the next page with; `null` → exhausted, `undefined` →
|
|
118
|
-
* cannot page yet. Offset views without bookkeeping (seeded by legacy code or
|
|
119
|
-
* an older persisted shape) derive it from `currentPage` / `hasMore`.
|
|
120
|
-
*/
|
|
121
|
-
declare const getNextPageCursor: <TItem, TCursor>(data: ReplicaPagedData<TItem, TCursor> | undefined, config: Pick<ReplicaPagingConfig<TItem, TCursor>, 'deriveCursor' | 'direction' | 'isCursorable' | 'mode'>) => TCursor | null | undefined;
|
|
122
|
-
/**
|
|
123
|
-
* Fold a fresh HEAD page into the view.
|
|
124
|
-
*
|
|
125
|
-
* - First load, a query change (`reset`) or an unextended view → the head page.
|
|
126
|
-
* - Extended view, `offset` mode → keep the loaded depth: fresh head first,
|
|
127
|
-
* then previously loaded rows not in it (shifted offsets self-heal by id).
|
|
128
|
-
* - Extended view, `cursor` mode → keep older pages only when the fresh head
|
|
129
|
-
* still contains the join anchor and the next cursor is known; otherwise
|
|
130
|
-
* collapse to the head page (never show a hidden gap).
|
|
131
|
-
*
|
|
132
|
-
* Client-only rows still in the view are kept on the head side.
|
|
133
|
-
*/
|
|
134
|
-
declare const applyHeadPage: <TItem, TCursor>(current: ReplicaPagedData<TItem, TCursor> | undefined, page: ReplicaPageResult<TItem, TCursor>, options: {
|
|
135
|
-
pageSize: number;
|
|
136
|
-
reset?: boolean;
|
|
137
|
-
}, config: ReplicaPagingConfig<TItem, TCursor>, ctx?: ReplicaPagingContext<TItem>) => ReplicaPagedData<TItem, TCursor>;
|
|
138
|
-
/** Merge the NEXT page (fetched with `getNextPageCursor`) into the view. */
|
|
139
|
-
declare const applyNextPage: <TItem, TCursor>(current: ReplicaPagedData<TItem, TCursor>, page: ReplicaPageResult<TItem, TCursor>, config: ReplicaPagingConfig<TItem, TCursor>, ctx?: ReplicaPagingContext<TItem>) => ReplicaPagedData<TItem, TCursor>;
|
|
140
|
-
/**
|
|
141
|
-
* Insert new rows on the HEAD side (a streamed message, a created topic).
|
|
142
|
-
* Cursors point at the far end, so they stay valid; offsets shift and are
|
|
143
|
-
* healed by id de-duplication on the next page merge.
|
|
144
|
-
*/
|
|
145
|
-
declare const insertHeadItems: <TItem, TCursor>(current: ReplicaPagedData<TItem, TCursor>, items: TItem[], config: ReplicaPagingConfig<TItem, TCursor>) => ReplicaPagedData<TItem, TCursor>;
|
|
146
|
-
/**
|
|
147
|
-
* Patch (or remove, when `fn` returns `undefined`) one row by id. Returns the
|
|
148
|
-
* same reference when the row is absent or unchanged.
|
|
149
|
-
*/
|
|
150
|
-
declare const mapPagedItem: <TItem, TCursor, TData extends ReplicaPagedData<TItem, TCursor>>(current: TData, id: string, fn: (item: TItem) => TItem | undefined, config: Pick<ReplicaPagingConfig<TItem>, 'getId'>) => TData;
|
|
151
|
-
/** Whether a paged view holds a row. */
|
|
152
|
-
declare const hasPagedItem: <TItem>(data: ReplicaPagedData<TItem, unknown> | undefined, id: string, config: Pick<ReplicaPagingConfig<TItem>, 'getId'>) => boolean;
|
|
153
|
-
/** Drop every loaded page but the head (e.g. after an edit inside older history). */
|
|
154
|
-
declare const collapseToHead: <TItem, TCursor>(current: ReplicaPagedData<TItem, TCursor>, config: ReplicaPagingConfig<TItem, TCursor>) => ReplicaPagedData<TItem, TCursor>;
|
|
155
|
-
/**
|
|
156
|
-
* What survives a reload: whole pages from the head side, bounded by
|
|
157
|
-
* `persist.pages` and `persist.maxItems`, client-only rows and transient
|
|
158
|
-
* flags stripped. Offset views are cut at page-size boundaries so the
|
|
159
|
-
* persisted `currentPage` stays a valid offset; a cut inside a cursor page
|
|
160
|
-
* forgets the cursor (pagination resumes after the next server head page).
|
|
161
|
-
*/
|
|
162
|
-
declare const toPersistedPage: <TItem, TCursor, TData extends ReplicaPagedData<TItem, TCursor>>(data: TData, config: ReplicaPagingConfig<TItem, TCursor>, ctx?: ReplicaPagingContext<TItem>) => TData;
|
|
163
|
-
/** Address of one persisted row: the resource's row key inside an identity scope. */
|
|
164
|
-
interface ReplicaRowKey {
|
|
165
|
-
queryKey: string;
|
|
166
|
-
scope: string;
|
|
167
|
-
}
|
|
168
|
-
interface ReplicaRow<T> {
|
|
169
|
-
data: T;
|
|
170
|
-
updatedAt: number;
|
|
171
|
-
}
|
|
172
|
-
/**
|
|
173
|
-
* Where a replica keeps its rows between sessions (IndexedDB, localStorage …).
|
|
174
|
-
* The server stays the durable source of truth, so `get` is best-effort: it
|
|
175
|
-
* swallows its own I/O failures and reports a miss. Writes are not: `set` and
|
|
176
|
-
* `remove` must reject when they did not land (see each method), so the
|
|
177
|
-
* engine can retry them instead of treating a dropped row as persisted.
|
|
178
|
-
*/
|
|
179
|
-
interface ReplicaStorage<T> {
|
|
180
|
-
get: (key: ReplicaRowKey) => Promise<ReplicaRow<T> | undefined>;
|
|
181
|
-
/**
|
|
182
|
-
* Strict read for destructive passes: like `get`, but **rejects** when the
|
|
183
|
-
* row could not be read (I/O error, unparsable value) instead of reporting a
|
|
184
|
-
* miss. The engine reads its per-scope index (and every row it patches)
|
|
185
|
-
* through it before `remove` / `clear` / an entity patch / an eviction, so an
|
|
186
|
-
* unreadable index fails the pass rather than passing for an empty one.
|
|
187
|
-
*
|
|
188
|
-
* Optional only for compatibility: a storage without it still hydrates and
|
|
189
|
-
* persists, but every destructive pass **fails** (`clear()` resolves `false`,
|
|
190
|
-
* a removal stays unpurged and is retried, an entity guard is kept) because
|
|
191
|
-
* the engine cannot tell a miss from a failed read through `get`.
|
|
192
|
-
*/
|
|
193
|
-
getStrict?: (key: ReplicaRowKey) => Promise<ReplicaRow<T> | undefined>;
|
|
194
|
-
/**
|
|
195
|
-
* Delete a row. Must **reject** when the delete did not land (a closed
|
|
196
|
-
* IndexedDB, a quota or security error): the engine keeps the row indexed
|
|
197
|
-
* and the removal unpurged, so the next `remove` / `clear` retries it instead
|
|
198
|
-
* of letting a row the user deleted hydrate again. A missing row is a success.
|
|
199
|
-
*/
|
|
200
|
-
remove: (key: ReplicaRowKey) => Promise<void>;
|
|
201
|
-
/**
|
|
202
|
-
* Write a row. Should **reject** when the write did not land: the engine only
|
|
203
|
-
* treats a row as holding a value after `set` resolved, so a rejected write is
|
|
204
|
-
* retried by the next persist rather than skipped as redundant. A storage that
|
|
205
|
-
* swallows failures still works, but a dropped write then survives until the
|
|
206
|
-
* value changes or the next session.
|
|
207
|
-
*/
|
|
208
|
-
set: (key: ReplicaRowKey, row: ReplicaRow<T>) => Promise<void>;
|
|
209
|
-
}
|
|
210
|
-
/**
|
|
211
|
-
* Where the confirmed value of an entry came from.
|
|
212
|
-
* - `storage`: hydrated from the persisted projection (may be stale)
|
|
213
|
-
* - `server`: confirmed by a network response
|
|
214
|
-
* - `local`: written locally before any hydrate/replace landed
|
|
215
|
-
* - `seed`: a provisional local row (e.g. a list row standing in for a detail so
|
|
216
|
-
* an object stays resolvable). Unlike `local` it is *not* authoritative: a
|
|
217
|
-
* persisted `storage` value may replace it on hydrate, and it never persists —
|
|
218
|
-
* so it cannot overwrite the detail it is only standing in for.
|
|
219
|
-
*/
|
|
220
|
-
type ReplicaSource = 'local' | 'seed' | 'server' | 'storage';
|
|
221
144
|
/**
|
|
222
145
|
* Identity partition of the persisted projection (user + workspace by default).
|
|
223
146
|
* `use` feeds the sync hook, `get` imperative actions, `canPersist` gates writes
|
|
@@ -236,60 +159,6 @@ interface ReplicaScope {
|
|
|
236
159
|
get: () => string;
|
|
237
160
|
use: () => string;
|
|
238
161
|
}
|
|
239
|
-
interface ReplicaPendingMutation<T> {
|
|
240
|
-
apply: (data: T) => T;
|
|
241
|
-
id: number;
|
|
242
|
-
/**
|
|
243
|
-
* Query the overlay was started under. An entry key can be reused for another
|
|
244
|
-
* query (a navigation), and an overlay only ever describes the query that
|
|
245
|
-
* created it — carrying it over would apply (and later persist) one query's
|
|
246
|
-
* row inside another.
|
|
247
|
-
*/
|
|
248
|
-
query?: string;
|
|
249
|
-
}
|
|
250
|
-
interface ReplicaEntryMeta<T> {
|
|
251
|
-
/**
|
|
252
|
-
* Confirmed snapshot. Only kept while optimistic mutations are in flight —
|
|
253
|
-
* otherwise the view itself is the confirmed value.
|
|
254
|
-
*/
|
|
255
|
-
base?: T;
|
|
256
|
-
/** Params of the last hydrate/replace — what `loadMore` pages with. */
|
|
257
|
-
params?: unknown;
|
|
258
|
-
pending: ReplicaPendingMutation<T>[];
|
|
259
|
-
/** Stable query identity beyond the key (filters, page size). */
|
|
260
|
-
query?: string;
|
|
261
|
-
/**
|
|
262
|
-
* Every write made while the entry is still a `seed`, in order — the seed
|
|
263
|
-
* writes themselves and the confirmed writes made over them. A hydrate that
|
|
264
|
-
* replaces the seed replays them onto the stored value: the seed's fresher
|
|
265
|
-
* fields (a list row's renamed title) and a confirmed edit made before
|
|
266
|
-
* storage answered both survive, instead of storage painting older values.
|
|
267
|
-
*/
|
|
268
|
-
seedPatches?: ReplicaSeedPatch<T>[];
|
|
269
|
-
source: ReplicaSource;
|
|
270
|
-
updatedAt: number;
|
|
271
|
-
}
|
|
272
|
-
interface ReplicaSeedPatch<T> {
|
|
273
|
-
/**
|
|
274
|
-
* Replayed onto the stored value, so a seed write must merge the fields it
|
|
275
|
-
* owns into its input rather than ignore it (its first run sees `undefined`).
|
|
276
|
-
*/
|
|
277
|
-
apply: (data: T | undefined) => T | undefined;
|
|
278
|
-
persist: boolean;
|
|
279
|
-
/** A provisional seed write: replayed, but never makes the result confirmed. */
|
|
280
|
-
seed?: boolean;
|
|
281
|
-
/**
|
|
282
|
-
* Source of a seed write. A later seed of the same tag supersedes this one
|
|
283
|
-
* (since the last confirmed write), so it must carry every field this one set.
|
|
284
|
-
*/
|
|
285
|
-
tag?: string;
|
|
286
|
-
}
|
|
287
|
-
/** Bookkeeping slot a replica keeps inside its domain store. */
|
|
288
|
-
interface ReplicaState<T> {
|
|
289
|
-
entries: Record<string, ReplicaEntryMeta<T>>;
|
|
290
|
-
/** The scope every entry in memory belongs to. */
|
|
291
|
-
scope?: string;
|
|
292
|
-
}
|
|
293
162
|
interface ReplicaResource<TParams, TData, TFetched = TData, TCursor = any> {
|
|
294
163
|
/** Paged resources receive the page cursor (`undefined` = head page). */
|
|
295
164
|
fetcher?: (params: TParams, cursor?: TCursor) => Promise<TFetched>;
|
|
@@ -319,6 +188,89 @@ interface ReplicaResource<TParams, TData, TFetched = TData, TCursor = any> {
|
|
|
319
188
|
syncKey?: (params: TParams) => readonly unknown[];
|
|
320
189
|
version: number;
|
|
321
190
|
}
|
|
191
|
+
/** Builds the storage of one resource from its namespace (`replica:<name>:v<version>`). */
|
|
192
|
+
type ReplicaStorageFactory<TData> = (namespace: string) => ReplicaStorage<TData> | undefined;
|
|
193
|
+
interface DefineReplicaOptions<TParams, TData, TFetched = TData, TCursor = any> {
|
|
194
|
+
/** Default network fetcher; a store binding may override it. */
|
|
195
|
+
fetcher?: (params: TParams, cursor?: TCursor) => Promise<TFetched>;
|
|
196
|
+
/** Entry identity inside one scope (memory bucket + persisted row). */
|
|
197
|
+
key: (params: TParams) => string;
|
|
198
|
+
name: string;
|
|
199
|
+
paging?: ReplicaPagingConfig<any, TCursor>;
|
|
200
|
+
/**
|
|
201
|
+
* Bound what the resource keeps in storage. `maxEntries` caps the persisted
|
|
202
|
+
* rows (storage keys: one per entry key and query) per scope; past it the
|
|
203
|
+
* least recently used rows are evicted — their persisted row, index entry
|
|
204
|
+
* and, when nothing holds it, their memory entry. Use rows (a hydrate or a
|
|
205
|
+
* persist, including a skipped unchanged one) count as recent, and the order
|
|
206
|
+
* lives in the persisted index, so it survives reloads. A row on screen (a
|
|
207
|
+
* mounted `useSync`, see `retain`), with pending optimistic writes or a
|
|
208
|
+
* `loadMore` in flight is never evicted. Unbounded by default.
|
|
209
|
+
*/
|
|
210
|
+
persist?: {
|
|
211
|
+
maxEntries?: number;
|
|
212
|
+
};
|
|
213
|
+
/**
|
|
214
|
+
* Entries that never touch storage (no hydrate, no persist) — e.g. scoped
|
|
215
|
+
* buckets whose rows must not land under an ordinary entry. Defaults to all.
|
|
216
|
+
*/
|
|
217
|
+
persistKey?: (key: string) => boolean;
|
|
218
|
+
/**
|
|
219
|
+
* Query identity beyond `key` (filters, sort, page size) — anything that
|
|
220
|
+
* changes the rows without changing the key. Persisted rows are kept per
|
|
221
|
+
* query, so a projection taken under other filters never hydrates; in memory
|
|
222
|
+
* a query change resets loaded pages.
|
|
223
|
+
*/
|
|
224
|
+
query?: (params: TParams) => unknown;
|
|
225
|
+
/** Identity partition of memory and persisted rows. Defaults to one static scope. */
|
|
226
|
+
scope?: ReplicaScope;
|
|
227
|
+
/** Persisted copy of the replica. Omit for a memory-only replica (no first-frame paint). */
|
|
228
|
+
storage?: ReplicaStorage<TData> | ReplicaStorageFactory<TData>;
|
|
229
|
+
/**
|
|
230
|
+
* Query-cache key of the network sync, for a resource adopting an existing
|
|
231
|
+
* key that other code already revalidates by. Defaults to `replicaKeys.sync`.
|
|
232
|
+
* With a custom key, revalidate through that key — `slice.revalidate` only
|
|
233
|
+
* matches default keys.
|
|
234
|
+
*/
|
|
235
|
+
syncKey?: (params: TParams) => readonly unknown[];
|
|
236
|
+
/** Bump to invalidate every persisted row written by older shapes. */
|
|
237
|
+
version: number;
|
|
238
|
+
}
|
|
239
|
+
declare const defineReplica: <TParams, TData, TFetched = TData, TCursor = any>(options: DefineReplicaOptions<TParams, TData, TFetched, TCursor>) => ReplicaResource<TParams, TData, TFetched, TCursor>;
|
|
240
|
+
interface DefinePagedReplicaOptions<TParams, TItem, TCursor> extends Omit<DefineReplicaOptions<TParams, ReplicaPagedData<TItem, TCursor>, ReplicaPageResult<TItem, TCursor>>, 'fetcher' | 'paging' | 'storage'> {
|
|
241
|
+
/** `cursor` is `undefined` for the head page. */
|
|
242
|
+
fetchPage?: (params: TParams, cursor: TCursor | undefined) => Promise<ReplicaPageResult<TItem, TCursor>>;
|
|
243
|
+
paging: ReplicaPagingConfig<TItem, TCursor>;
|
|
244
|
+
storage?: ReplicaStorage<any> | ReplicaStorageFactory<any>;
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* A resource whose value is a paged list ({@link ReplicaPagedData}). The
|
|
248
|
+
* store view type may extend the paged shape with domain fields.
|
|
249
|
+
*/
|
|
250
|
+
declare const definePagedReplica: <TParams, TItem, TCursor = number, TData extends ReplicaPagedData<TItem, TCursor> = ReplicaPagedData<TItem, TCursor>>(options: DefinePagedReplicaOptions<TParams, TItem, TCursor>) => ReplicaResource<TParams, TData, ReplicaPageResult<TItem, TCursor>, TCursor>;
|
|
251
|
+
/**
|
|
252
|
+
* Keys of the queries a fetch adapter runs for a replica. Hosts that sweep
|
|
253
|
+
* their query cache by prefix (e.g. "refetch everything after a restore")
|
|
254
|
+
* match on {@link REPLICA_KEY_PREFIX}.
|
|
255
|
+
*/
|
|
256
|
+
declare const REPLICA_KEY_PREFIX = "replica:";
|
|
257
|
+
declare const replicaKeys: {
|
|
258
|
+
/** One-shot read of the persisted row for `storageKey`. */
|
|
259
|
+
hydrate: (name: string, version: number, scope: string, storageKey: string) => readonly ['replica:hydrate', string, number, string, string];
|
|
260
|
+
/** Network sync of the head of one entry. */
|
|
261
|
+
sync: (name: string, version: number, scope: string, key: string, params: unknown) => readonly ['replica:sync', string, number, string, string, unknown];
|
|
262
|
+
};
|
|
263
|
+
/** Match a resource's sync keys, optionally narrowed to one scope and/or entry key. */
|
|
264
|
+
declare const isReplicaSyncKey: (queryKey: unknown, name: string, filter?: {
|
|
265
|
+
key?: string;
|
|
266
|
+
scope?: string;
|
|
267
|
+
}) => boolean;
|
|
268
|
+
/**
|
|
269
|
+
* A fixed identity partition. The default for apps without accounts; an app
|
|
270
|
+
* with sign-in passes a scope derived from the signed-in user / workspace so
|
|
271
|
+
* one browser profile never mixes two identities' rows.
|
|
272
|
+
*/
|
|
273
|
+
declare const staticReplicaScope: (scope?: string) => ReplicaScope;
|
|
322
274
|
/** Persisted row key: one row per entry key and query. */
|
|
323
275
|
declare const replicaStorageKey: (key: string, query?: string) => string;
|
|
324
276
|
/**
|
|
@@ -336,71 +288,70 @@ declare const isReplicaStorageKeyOf: (storageKey: string, key: string) => boolea
|
|
|
336
288
|
/** Deterministic JSON: sorted keys, `undefined` dropped. */
|
|
337
289
|
declare const stableQueryKey: (value: unknown) => string;
|
|
338
290
|
/**
|
|
339
|
-
*
|
|
340
|
-
*
|
|
341
|
-
*
|
|
291
|
+
* Where the confirmed value of an entry came from.
|
|
292
|
+
* - `storage`: hydrated from the persisted projection (may be stale)
|
|
293
|
+
* - `server`: confirmed by a network response
|
|
294
|
+
* - `local`: written locally before any hydrate/replace landed
|
|
295
|
+
* - `seed`: a provisional local row (e.g. a list row standing in for a detail so
|
|
296
|
+
* an object stays resolvable). Unlike `local` it is *not* authoritative: a
|
|
297
|
+
* persisted `storage` value may replace it on hydrate, and it never persists —
|
|
298
|
+
* so it cannot overwrite the detail it is only standing in for.
|
|
342
299
|
*/
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
/** Default network fetcher; a store binding may override it. */
|
|
348
|
-
fetcher?: (params: TParams, cursor?: TCursor) => Promise<TFetched>;
|
|
349
|
-
/** Entry identity inside one scope (memory bucket + persisted row). */
|
|
350
|
-
key: (params: TParams) => string;
|
|
351
|
-
name: string;
|
|
352
|
-
paging?: ReplicaPagingConfig<any, TCursor>;
|
|
300
|
+
type ReplicaSource = 'local' | 'seed' | 'server' | 'storage';
|
|
301
|
+
interface ReplicaPendingMutation<T> {
|
|
302
|
+
apply: (data: T) => T;
|
|
303
|
+
id: number;
|
|
353
304
|
/**
|
|
354
|
-
*
|
|
355
|
-
*
|
|
356
|
-
*
|
|
357
|
-
*
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
305
|
+
* Query the overlay was started under. An entry key can be reused for another
|
|
306
|
+
* query (a navigation), and an overlay only ever describes the query that
|
|
307
|
+
* created it — carrying it over would apply (and later persist) one query's
|
|
308
|
+
* row inside another.
|
|
309
|
+
*/
|
|
310
|
+
query?: string;
|
|
311
|
+
}
|
|
312
|
+
interface ReplicaEntryMeta<T> {
|
|
313
|
+
/**
|
|
314
|
+
* Confirmed snapshot. Only kept while optimistic mutations are in flight —
|
|
315
|
+
* otherwise the view itself is the confirmed value.
|
|
362
316
|
*/
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
317
|
+
base?: T;
|
|
318
|
+
/** Params of the last hydrate/replace — what `loadMore` pages with. */
|
|
319
|
+
params?: unknown;
|
|
320
|
+
pending: ReplicaPendingMutation<T>[];
|
|
321
|
+
/** Stable query identity beyond the key (filters, page size). */
|
|
322
|
+
query?: string;
|
|
366
323
|
/**
|
|
367
|
-
*
|
|
368
|
-
*
|
|
324
|
+
* Every write made while the entry is still a `seed`, in order — the seed
|
|
325
|
+
* writes themselves and the confirmed writes made over them. A hydrate that
|
|
326
|
+
* replaces the seed replays them onto the stored value: the seed's fresher
|
|
327
|
+
* fields (a list row's renamed title) and a confirmed edit made before
|
|
328
|
+
* storage answered both survive, instead of storage painting older values.
|
|
369
329
|
*/
|
|
370
|
-
|
|
330
|
+
seedPatches?: ReplicaSeedPatch<T>[];
|
|
331
|
+
source: ReplicaSource;
|
|
332
|
+
updatedAt: number;
|
|
333
|
+
}
|
|
334
|
+
interface ReplicaSeedPatch<T> {
|
|
371
335
|
/**
|
|
372
|
-
*
|
|
373
|
-
*
|
|
374
|
-
* query, so a projection taken under other filters never hydrates; in memory
|
|
375
|
-
* a query change resets loaded pages.
|
|
336
|
+
* Replayed onto the stored value, so a seed write must merge the fields it
|
|
337
|
+
* owns into its input rather than ignore it (its first run sees `undefined`).
|
|
376
338
|
*/
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
storage?: ReplicaStorage<TData> | ReplicaStorageFactory<TData>;
|
|
339
|
+
apply: (data: T | undefined) => T | undefined;
|
|
340
|
+
persist: boolean;
|
|
341
|
+
/** A provisional seed write: replayed, but never makes the result confirmed. */
|
|
342
|
+
seed?: boolean;
|
|
382
343
|
/**
|
|
383
|
-
*
|
|
384
|
-
*
|
|
385
|
-
* With a custom key, revalidate through that key — `slice.revalidate` only
|
|
386
|
-
* matches default keys.
|
|
344
|
+
* Source of a seed write. A later seed of the same tag supersedes this one
|
|
345
|
+
* (since the last confirmed write), so it must carry every field this one set.
|
|
387
346
|
*/
|
|
388
|
-
|
|
389
|
-
/** Bump to invalidate every persisted row written by older shapes. */
|
|
390
|
-
version: number;
|
|
347
|
+
tag?: string;
|
|
391
348
|
}
|
|
392
|
-
|
|
393
|
-
interface
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
storage?: ReplicaStorage<any> | ReplicaStorageFactory<any>;
|
|
349
|
+
/** Bookkeeping slot a replica keeps inside its domain store. */
|
|
350
|
+
interface ReplicaState<T> {
|
|
351
|
+
entries: Record<string, ReplicaEntryMeta<T>>;
|
|
352
|
+
/** The scope every entry in memory belongs to. */
|
|
353
|
+
scope?: string;
|
|
398
354
|
}
|
|
399
|
-
/**
|
|
400
|
-
* A resource whose value is a paged list ({@link ReplicaPagedData}). The
|
|
401
|
-
* store view type may extend the paged shape with domain fields.
|
|
402
|
-
*/
|
|
403
|
-
declare const definePagedReplica: <TParams, TItem, TCursor = number, TData extends ReplicaPagedData<TItem, TCursor> = ReplicaPagedData<TItem, TCursor>>(options: DefinePagedReplicaOptions<TParams, TItem, TCursor>) => ReplicaResource<TParams, TData, ReplicaPageResult<TItem, TCursor>, TCursor>;
|
|
404
355
|
/**
|
|
405
356
|
* Pure transition core of a replica. It never touches the store
|
|
406
357
|
* or storage: it returns the next bookkeeping slot, the view writes the store
|
|
@@ -500,41 +451,21 @@ interface ReplicaTransition<T> {
|
|
|
500
451
|
declare const createReplicaState: <T>() => ReplicaState<T>;
|
|
501
452
|
declare const replicaReducer: <T>(state: ReplicaState<T>, action: ReplicaAction<T>, readView: (key: string) => T | undefined, now?: number) => ReplicaTransition<T>;
|
|
502
453
|
/**
|
|
503
|
-
*
|
|
504
|
-
*
|
|
505
|
-
*
|
|
506
|
-
*
|
|
507
|
-
*/
|
|
508
|
-
declare const readReplicaRowStrict: <T>(storage: ReplicaStorage<T>, key: ReplicaRowKey) => Promise<ReplicaRow<T> | undefined>;
|
|
509
|
-
/**
|
|
510
|
-
* Serializes writes for one projection key. A later snapshot can never be overwritten by an
|
|
511
|
-
* earlier, slower IndexedDB write.
|
|
454
|
+
* How entities (e.g. topics) appear inside a non-paged resource value. Paged
|
|
455
|
+
* resources get this for free from `paging.getId`. Build one with
|
|
456
|
+
* `singleEntity` (a detail value), `arrayEntity` (a plain list) or by hand for
|
|
457
|
+
* nested shapes (grouped lists).
|
|
512
458
|
*/
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
/** Resolves `false` when storage rejected the delete (see `ReplicaStorage.remove`). */
|
|
517
|
-
remove: (key: ReplicaRowKey) => Promise<boolean>;
|
|
518
|
-
set: (key: ReplicaRowKey, value: ReplicaRow<T>) => Promise<boolean>;
|
|
459
|
+
interface ReplicaEntityAdapter<TData, TItem> {
|
|
460
|
+
/** Whether `data` holds the entity. */
|
|
461
|
+
has: (data: TData, id: string) => boolean;
|
|
519
462
|
/**
|
|
520
|
-
*
|
|
521
|
-
*
|
|
522
|
-
*
|
|
523
|
-
*
|
|
524
|
-
* `read` picks how the row is read:
|
|
525
|
-
* - `'get'` (default): best-effort `get`, a failed read passes for a miss.
|
|
526
|
-
* - `'prefer-strict'`: `getStrict` when the storage has one, so a read failure
|
|
527
|
-
* fails the update (resolves `false`) instead of passing for a missing row
|
|
528
|
-
* that `fn` would then overwrite; `get` otherwise.
|
|
529
|
-
* - `'strict'`: `getStrict` only; a storage without it fails the update.
|
|
530
|
-
* Required for destructive passes (see `readReplicaRowStrict`).
|
|
463
|
+
* Map the entity inside `data` (`fn` returning `undefined` deletes it).
|
|
464
|
+
* Return `data` itself when nothing changed, or `undefined` when the whole
|
|
465
|
+
* value goes away with the entity (a detail value).
|
|
531
466
|
*/
|
|
532
|
-
|
|
533
|
-
read?: 'get' | 'prefer-strict' | 'strict';
|
|
534
|
-
}) => Promise<boolean>;
|
|
467
|
+
map: (data: TData, id: string, fn: (item: TItem) => TItem | undefined) => TData | undefined;
|
|
535
468
|
}
|
|
536
|
-
/** Reserved storage key of the per-scope index of persisted rows. */
|
|
537
|
-
declare const REPLICA_INDEX_KEY = "__replica:index";
|
|
538
469
|
/**
|
|
539
470
|
* The engine's view of the host store. The engine never owns the rendered
|
|
540
471
|
* value — the host does (a Zustand slice, a signal, a plain object) — it only
|
|
@@ -553,22 +484,6 @@ interface ReplicaStorePort<TData> {
|
|
|
553
484
|
/** Materialized value of one entry, as the UI sees it. */
|
|
554
485
|
read: (key: string) => TData | undefined;
|
|
555
486
|
}
|
|
556
|
-
/**
|
|
557
|
-
* How entities (e.g. topics) appear inside a non-paged resource value. Paged
|
|
558
|
-
* resources get this for free from `paging.getId`. Build one with
|
|
559
|
-
* `singleEntity` (a detail value), `arrayEntity` (a plain list) or by hand for
|
|
560
|
-
* nested shapes (grouped lists).
|
|
561
|
-
*/
|
|
562
|
-
interface ReplicaEntityAdapter<TData, TItem> {
|
|
563
|
-
/** Whether `data` holds the entity. */
|
|
564
|
-
has: (data: TData, id: string) => boolean;
|
|
565
|
-
/**
|
|
566
|
-
* Map the entity inside `data` (`fn` returning `undefined` deletes it).
|
|
567
|
-
* Return `data` itself when nothing changed, or `undefined` when the whole
|
|
568
|
-
* value goes away with the entity (a detail value).
|
|
569
|
-
*/
|
|
570
|
-
map: (data: TData, id: string, fn: (item: TItem) => TItem | undefined) => TData | undefined;
|
|
571
|
-
}
|
|
572
487
|
interface ReplicaEngineOptions<TParams, TData, TFetched> {
|
|
573
488
|
/** Label prefix of host updates (devtools action names). Defaults to the resource name. */
|
|
574
489
|
actionPrefix?: string;
|
|
@@ -664,7 +579,7 @@ declare const createReplicaEngine: <TParams, TData, TFetched = TData>(resource:
|
|
|
664
579
|
persist?: boolean | undefined;
|
|
665
580
|
}) => boolean;
|
|
666
581
|
isMissingHead: (incoming: TFetched) => boolean;
|
|
667
|
-
loadMore: (key: string, fallbackParams?: TParams) => Promise<void>;
|
|
582
|
+
loadMore: (key: string, fallbackParams?: TParams | undefined) => Promise<void>;
|
|
668
583
|
optimistic: <TResult>(key: string, apply: (data: TData) => TData, serverCall: () => Promise<TResult>, mutationOptions?: OptimisticMutationOptions<TData, TResult>) => Promise<TResult>;
|
|
669
584
|
patchStoredEntity: <TItem>(id: string, fn: (item: TItem) => TItem | undefined) => Promise<void>;
|
|
670
585
|
patchStoredRows: (fn: (data: TData) => TData | null | undefined) => Promise<void>;
|
|
@@ -697,6 +612,67 @@ declare const createReplicaEngine: <TParams, TData, TFetched = TData>(resource:
|
|
|
697
612
|
}) => boolean;
|
|
698
613
|
};
|
|
699
614
|
type ReplicaEngine<TParams, TData, TFetched = TData> = ReturnType<typeof createReplicaEngine<TParams, TData, TFetched>>;
|
|
615
|
+
/** Concatenate in view order: forward = head first, backward = head last. */
|
|
616
|
+
declare const joinPages: <TItem>(head: TItem[], rest: TItem[], direction: ReplicaPagingDirection) => TItem[];
|
|
617
|
+
/** The paging config one entry reads with: `directionOf` resolved for `key`. */
|
|
618
|
+
declare const pagingFor: <TItem, TCursor>(config: ReplicaPagingConfig<TItem, TCursor>, key: string) => ReplicaPagingConfig<TItem, TCursor>;
|
|
619
|
+
/**
|
|
620
|
+
* Cursor to request the next page with; `null` → exhausted, `undefined` →
|
|
621
|
+
* cannot page yet. Offset views without bookkeeping (seeded by legacy code or
|
|
622
|
+
* an older persisted shape) derive it from `currentPage` / `hasMore`.
|
|
623
|
+
*/
|
|
624
|
+
declare const getNextPageCursor: <TItem, TCursor>(data: ReplicaPagedData<TItem, TCursor> | undefined, config: Pick<ReplicaPagingConfig<TItem, TCursor>, 'deriveCursor' | 'direction' | 'isCursorable' | 'mode'>) => TCursor | null | undefined;
|
|
625
|
+
/**
|
|
626
|
+
* Fold a fresh HEAD page into the view.
|
|
627
|
+
*
|
|
628
|
+
* - First load, a query change (`reset`) or an unextended view → the head page.
|
|
629
|
+
* - Extended view, `offset` mode → keep the loaded depth: fresh head first,
|
|
630
|
+
* then previously loaded rows not in it (shifted offsets self-heal by id).
|
|
631
|
+
* - Extended view, `cursor` mode → keep older pages only when the fresh head
|
|
632
|
+
* still contains the join anchor and the next cursor is known; otherwise
|
|
633
|
+
* collapse to the head page (never show a hidden gap).
|
|
634
|
+
*
|
|
635
|
+
* Client-only rows still in the view are kept on the head side.
|
|
636
|
+
*/
|
|
637
|
+
declare const applyHeadPage: <TItem, TCursor>(current: ReplicaPagedData<TItem, TCursor> | undefined, page: ReplicaPageResult<TItem, TCursor>, options: {
|
|
638
|
+
pageSize: number;
|
|
639
|
+
reset?: boolean;
|
|
640
|
+
}, config: ReplicaPagingConfig<TItem, TCursor>, ctx?: ReplicaPagingContext<TItem>) => ReplicaPagedData<TItem, TCursor>;
|
|
641
|
+
/** Merge the NEXT page (fetched with `getNextPageCursor`) into the view. */
|
|
642
|
+
declare const applyNextPage: <TItem, TCursor>(current: ReplicaPagedData<TItem, TCursor>, page: ReplicaPageResult<TItem, TCursor>, config: ReplicaPagingConfig<TItem, TCursor>, ctx?: ReplicaPagingContext<TItem>) => ReplicaPagedData<TItem, TCursor>;
|
|
643
|
+
/**
|
|
644
|
+
* Insert new rows on the HEAD side (a streamed message, a created topic).
|
|
645
|
+
* Cursors point at the far end, so they stay valid; offsets shift and are
|
|
646
|
+
* healed by id de-duplication on the next page merge.
|
|
647
|
+
*/
|
|
648
|
+
declare const insertHeadItems: <TItem, TCursor>(current: ReplicaPagedData<TItem, TCursor>, items: TItem[], config: ReplicaPagingConfig<TItem, TCursor>) => ReplicaPagedData<TItem, TCursor>;
|
|
649
|
+
/**
|
|
650
|
+
* Patch (or remove, when `fn` returns `undefined`) one row by id. Returns the
|
|
651
|
+
* same reference when the row is absent or unchanged.
|
|
652
|
+
*/
|
|
653
|
+
declare const mapPagedItem: <TItem, TCursor, TData extends ReplicaPagedData<TItem, TCursor>>(current: TData, id: string, fn: (item: TItem) => TItem | undefined, config: Pick<ReplicaPagingConfig<TItem>, 'getId'>) => TData;
|
|
654
|
+
/** Whether a paged view holds a row. */
|
|
655
|
+
declare const hasPagedItem: <TItem>(data: ReplicaPagedData<TItem, unknown> | undefined, id: string, config: Pick<ReplicaPagingConfig<TItem>, 'getId'>) => boolean;
|
|
656
|
+
/** Drop every loaded page but the head (e.g. after an edit inside older history). */
|
|
657
|
+
declare const collapseToHead: <TItem, TCursor>(current: ReplicaPagedData<TItem, TCursor>, config: ReplicaPagingConfig<TItem, TCursor>) => ReplicaPagedData<TItem, TCursor>;
|
|
658
|
+
/**
|
|
659
|
+
* What survives a reload: whole pages from the head side, bounded by
|
|
660
|
+
* `persist.pages` and `persist.maxItems`, client-only rows and transient
|
|
661
|
+
* flags stripped. Offset views are cut at page-size boundaries so the
|
|
662
|
+
* persisted `currentPage` stays a valid offset; a cut inside a cursor page
|
|
663
|
+
* forgets the cursor (pagination resumes after the next server head page).
|
|
664
|
+
*/
|
|
665
|
+
declare const toPersistedPage: <TItem, TCursor, TData extends ReplicaPagedData<TItem, TCursor>>(data: TData, config: ReplicaPagingConfig<TItem, TCursor>, ctx?: ReplicaPagingContext<TItem>) => TData;
|
|
666
|
+
/**
|
|
667
|
+
* The value IS one entity, or wraps one (`{ project, members }`). Deleting the
|
|
668
|
+
* entity drops the whole value.
|
|
669
|
+
*/
|
|
670
|
+
declare const singleEntity: <TData, TItem = TData>(getId: (item: TItem) => string, lens?: {
|
|
671
|
+
get: (data: TData) => TItem;
|
|
672
|
+
set: (data: TData, item: TItem) => TData;
|
|
673
|
+
}) => ReplicaEntityAdapter<TData, TItem>;
|
|
674
|
+
/** The value is a plain list of entities; deleting one filters it out. */
|
|
675
|
+
declare const arrayEntity: <TItem>(getId: (item: TItem) => string) => ReplicaEntityAdapter<TItem[], TItem>;
|
|
700
676
|
/** The part of a slice entity propagation needs (structural, so any slice fits). */
|
|
701
677
|
interface ReplicaEntityTarget {
|
|
702
678
|
beginEntityOptimistic: <TItem>(id: string, fn: (item: TItem) => TItem | undefined) => ReplicaOptimisticToken<any>[];
|
|
@@ -739,40 +715,41 @@ declare const linkReplicaEntity: <TItem>(targets: ReplicaEntityTarget[]) => {
|
|
|
739
715
|
}) => void;
|
|
740
716
|
};
|
|
741
717
|
/**
|
|
742
|
-
*
|
|
743
|
-
*
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
get: (data: TData) => TItem;
|
|
747
|
-
set: (data: TData, item: TItem) => TData;
|
|
748
|
-
}) => ReplicaEntityAdapter<TData, TItem>;
|
|
749
|
-
/** The value is a plain list of entities; deleting one filters it out. */
|
|
750
|
-
declare const arrayEntity: <TItem>(getId: (item: TItem) => string) => ReplicaEntityAdapter<TItem[], TItem>;
|
|
751
|
-
/**
|
|
752
|
-
* Keys of the queries a fetch adapter runs for a replica. Hosts that sweep
|
|
753
|
-
* their query cache by prefix (e.g. "refetch everything after a restore")
|
|
754
|
-
* match on {@link REPLICA_KEY_PREFIX}.
|
|
718
|
+
* Strict read of one row (see `ReplicaStorage.getStrict`). Rejects when the
|
|
719
|
+
* storage has no strict read: its `get` swallows I/O failures into a miss, so a
|
|
720
|
+
* destructive pass reading through it could take an unreadable row (the index)
|
|
721
|
+
* for an empty one and report success for rows it never saw.
|
|
755
722
|
*/
|
|
756
|
-
declare const
|
|
757
|
-
declare const replicaKeys: {
|
|
758
|
-
/** One-shot read of the persisted row for `storageKey`. */
|
|
759
|
-
hydrate: (name: string, version: number, scope: string, storageKey: string) => readonly ['replica:hydrate', string, number, string, string];
|
|
760
|
-
/** Network sync of the head of one entry. */
|
|
761
|
-
sync: (name: string, version: number, scope: string, key: string, params: unknown) => readonly ['replica:sync', string, number, string, string, unknown];
|
|
762
|
-
};
|
|
763
|
-
/** Match a resource's sync keys, optionally narrowed to one scope and/or entry key. */
|
|
764
|
-
declare const isReplicaSyncKey: (queryKey: unknown, name: string, filter?: {
|
|
765
|
-
key?: string;
|
|
766
|
-
scope?: string;
|
|
767
|
-
}) => boolean;
|
|
723
|
+
declare const readReplicaRowStrict: <T>(storage: ReplicaStorage<T>, key: ReplicaRowKey) => Promise<ReplicaRow<T> | undefined>;
|
|
768
724
|
/**
|
|
769
|
-
*
|
|
770
|
-
*
|
|
771
|
-
* Bounds a pre-paint hydrate in a route loader: the persisted read is usually
|
|
772
|
-
* sub-millisecond, and the deadline only fires on a pathological storage stall,
|
|
773
|
-
* so a route transition never waits longer than `ms` for it.
|
|
725
|
+
* Serializes writes for one projection key. A later snapshot can never be overwritten by an
|
|
726
|
+
* earlier, slower IndexedDB write.
|
|
774
727
|
*/
|
|
775
|
-
declare
|
|
728
|
+
declare class ReplicaWriteQueue<T> {
|
|
729
|
+
#private;
|
|
730
|
+
constructor(storage: ReplicaStorage<T>);
|
|
731
|
+
/** Resolves `false` when storage rejected the delete (see `ReplicaStorage.remove`). */
|
|
732
|
+
remove: (key: ReplicaRowKey) => Promise<boolean>;
|
|
733
|
+
set: (key: ReplicaRowKey, value: ReplicaRow<T>) => Promise<boolean>;
|
|
734
|
+
/**
|
|
735
|
+
* Serialized read-modify-write: `fn` sees the row as of every earlier queued
|
|
736
|
+
* write for this key. Return a projection to write it, `null` to remove the
|
|
737
|
+
* row, or `undefined` to leave it untouched (a missing row stays missing).
|
|
738
|
+
*
|
|
739
|
+
* `read` picks how the row is read:
|
|
740
|
+
* - `'get'` (default): best-effort `get`, a failed read passes for a miss.
|
|
741
|
+
* - `'prefer-strict'`: `getStrict` when the storage has one, so a read failure
|
|
742
|
+
* fails the update (resolves `false`) instead of passing for a missing row
|
|
743
|
+
* that `fn` would then overwrite; `get` otherwise.
|
|
744
|
+
* - `'strict'`: `getStrict` only; a storage without it fails the update.
|
|
745
|
+
* Required for destructive passes (see `readReplicaRowStrict`).
|
|
746
|
+
*/
|
|
747
|
+
update: (key: ReplicaRowKey, fn: (current: ReplicaRow<T> | undefined) => ReplicaRow<T> | null | undefined, { read }?: {
|
|
748
|
+
read?: 'get' | 'prefer-strict' | 'strict';
|
|
749
|
+
}) => Promise<boolean>;
|
|
750
|
+
}
|
|
751
|
+
/** Reserved storage key of the per-scope index of persisted rows. */
|
|
752
|
+
declare const REPLICA_INDEX_KEY = "__replica:index";
|
|
776
753
|
/**
|
|
777
754
|
* The entry storage keys this resource has a persisted row for in the active
|
|
778
755
|
* scope, oldest first.
|
|
@@ -788,6 +765,14 @@ declare const settleWithin: (promise: Promise<unknown>, ms: number) => Promise<v
|
|
|
788
765
|
* `query` unset.
|
|
789
766
|
*/
|
|
790
767
|
declare const readReplicaStoredKeys: (resource: Pick<ReplicaResource<any, any, any>, 'scope' | 'storage'>) => Promise<string[]>;
|
|
768
|
+
/**
|
|
769
|
+
* Resolve once `promise` settles or `ms` elapses — never rejects, never hangs.
|
|
770
|
+
*
|
|
771
|
+
* Bounds a pre-paint hydrate in a route loader: the persisted read is usually
|
|
772
|
+
* sub-millisecond, and the deadline only fires on a pathological storage stall,
|
|
773
|
+
* so a route transition never waits longer than `ms` for it.
|
|
774
|
+
*/
|
|
775
|
+
declare const settleWithin: (promise: Promise<unknown>, ms: number) => Promise<void>;
|
|
791
776
|
/**
|
|
792
777
|
* An async key-value store a replica can persist into: an IndexedDB object
|
|
793
778
|
* store, a SQLite table behind IPC, a `Map` in tests. Values are stored as
|
|
@@ -867,5 +852,5 @@ declare class LocalStorageReplicaStorage<T> implements ReplicaStorage<T> {
|
|
|
867
852
|
*/
|
|
868
853
|
set: (key: ReplicaRowKey, row: ReplicaRow<T>) => Promise<void>;
|
|
869
854
|
}
|
|
870
|
-
export {
|
|
871
|
-
//# sourceMappingURL=index-
|
|
855
|
+
export { DefinePagedReplicaOptions as $, ReplicaEngineOptions as A, replicaReducer as B, joinPages as C, ReplicaEngine as D, toPersistedPage as E, ReplicaAction as F, ReplicaState as G, ReplicaPendingMutation as H, ReplicaEffect as I, stableQueryKey as J, isReplicaStorageKeyOf as K, ReplicaTransition as L, ReplicaOptimisticToken as M, ReplicaStorePort as N, createReplicaEngine as O, ReplicaEntityAdapter as P, replicaKeys as Q, ReplicaViewWrite as R, insertHeadItems as S, pagingFor as T, ReplicaSeedPatch as U, ReplicaEntryMeta as V, ReplicaSource as W, REPLICA_KEY_PREFIX as X, staticReplicaScope as Y, isReplicaSyncKey as Z, applyHeadPage as _, KeyValueReplicaStorageOptions as a, ReplicaScope as at, getNextPageCursor as b, settleWithin as c, ReplicaPagedData as ct, ReplicaWriteQueue as d, ReplicaPagingDirection as dt, DefineReplicaOptions as et, readReplicaRowStrict as f, ReplicaPagingMode as ft, singleEntity as g, arrayEntity as h, ReplicaStorage as ht, KeyValueReplicaStorage as i, ReplicaResource as it, ReplicaHeadOutcome as j, OptimisticMutationOptions as k, readReplicaStoredKeys as l, ReplicaPagingConfig as lt, linkReplicaEntity as m, ReplicaRowKey as mt, LocalStorageReplicaStorageOptions as n, definePagedReplica as nt, ReplicaKeyValueStore as o, ReplicaPageInfo as ot, ReplicaEntityTarget as p, ReplicaRow as pt, replicaStorageKey as q, ReplicaRowCodec as r, defineReplica as rt, replicaRowStoreKey as s, ReplicaPageResult as st, LocalStorageReplicaStorage as t, ReplicaStorageFactory as tt, REPLICA_INDEX_KEY as u, ReplicaPagingContext as ut, applyNextPage as v, mapPagedItem as w, hasPagedItem as x, collapseToHead as y, createReplicaState as z };
|
|
856
|
+
//# sourceMappingURL=index-DsElitHD.d.ts.map
|