foldkit-remote 0.15.0 → 0.16.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 +4 -1
- package/dist/{index-elfivniT.d.mts → index-eVkkbhPA.d.mts} +1649 -1668
- package/dist/index-eVkkbhPA.d.mts.map +1 -0
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +1 -2
- package/dist/port.d.mts +2 -2
- package/dist/port.d.mts.map +1 -1
- package/dist/port.mjs +1 -1
- package/dist/{src-DA70XPfy.mjs → src-oVKXbiTu.mjs} +11 -5
- package/dist/{src-DA70XPfy.mjs.map → src-oVKXbiTu.mjs.map} +1 -1
- package/package.json +12 -11
- package/dist/index-elfivniT.d.mts.map +0 -1
|
@@ -7,57 +7,91 @@ import { EntryWithoutKeepAlive } from "foldkit/subscription";
|
|
|
7
7
|
import { ActiveSurface, Contract, Invalid, Metadata, ModelRef, Projection, Surface, SurfaceSource, Wiring } from "foldkit-surface";
|
|
8
8
|
import { Rpc, RpcClientError, RpcGroup } from "effect/rpc";
|
|
9
9
|
import * as Update from "foldkit/update";
|
|
10
|
-
//#region
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
readonly
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
readonly
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
10
|
+
//#region src/store.d.ts
|
|
11
|
+
type EntityKey = string;
|
|
12
|
+
interface EntityEntry {
|
|
13
|
+
readonly values: Readonly<Record<string, unknown>>;
|
|
14
|
+
readonly present: ReadonlySet<string>;
|
|
15
|
+
/** Present fields whose value may be outdated (revalidation needed). */
|
|
16
|
+
readonly stale: ReadonlySet<string>;
|
|
17
|
+
/**
|
|
18
|
+
* Fields the server settled without a value: asked for, and answered without
|
|
19
|
+
* them, whatever the reason. Not planned again until a refresh asks; a later
|
|
20
|
+
* write of the field clears it.
|
|
21
|
+
*/
|
|
22
|
+
readonly unavailable: ReadonlySet<string>;
|
|
23
|
+
/** The entity is known to be absent; a later write clears this. */
|
|
24
|
+
readonly tombstone: boolean;
|
|
25
|
+
/** Injected clock reading of the last write; never read from ambient state. */
|
|
26
|
+
readonly updatedAt: number;
|
|
27
|
+
/**
|
|
28
|
+
* The canonical window each present field was fetched with (`""` for none).
|
|
29
|
+
* A later request with a *known, different* window is planned as missing.
|
|
30
|
+
*/
|
|
31
|
+
readonly windows: Readonly<Record<string, string>>;
|
|
32
|
+
}
|
|
33
|
+
type EntityStore = Readonly<Record<EntityKey, EntityEntry>>;
|
|
34
|
+
/** The separator keeps an untrusted id from colliding with a prototype key. */
|
|
35
|
+
declare const entityKey: (entity: string, id: string) => EntityKey;
|
|
36
|
+
declare const emptyStore: EntityStore;
|
|
37
|
+
interface EntityWrite {
|
|
38
|
+
readonly key: EntityKey;
|
|
39
|
+
readonly values: Readonly<Record<string, unknown>>;
|
|
40
|
+
/** The canonical window each written relation field was fetched with. */
|
|
41
|
+
readonly windows?: Readonly<Record<string, string>> | undefined;
|
|
42
|
+
/** When this write dates the entity, instead of the batch's `now`. */
|
|
43
|
+
readonly updatedAt?: number | undefined;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Merges known values into many entities, copying the store once. Every
|
|
47
|
+
* written field becomes present and non-stale; a tombstone is cleared because
|
|
48
|
+
* the entity is evidently not absent.
|
|
49
|
+
*/
|
|
50
|
+
declare const writeEntities: (store: EntityStore, writes: ReadonlyArray<EntityWrite>, now?: number) => EntityStore;
|
|
51
|
+
/** `writeEntities` for one entity. */
|
|
52
|
+
declare const writeEntity: (store: EntityStore, key: EntityKey, values: Readonly<Record<string, unknown>>, now?: number, windows?: Readonly<Record<string, string>>) => EntityStore;
|
|
53
|
+
/**
|
|
54
|
+
* Marks present fields stale (`true`) or ends their staleness (`false`) across
|
|
55
|
+
* many entities, copying the store once. A field never present stays missing;
|
|
56
|
+
* an unknown entity is left alone.
|
|
57
|
+
*/
|
|
58
|
+
declare const setStale: (store: EntityStore, marks: ReadonlyArray<readonly [key: EntityKey, fields: Iterable<string>]>, stale: boolean) => EntityStore;
|
|
59
|
+
/** Marks present fields stale; fields that were never present stay missing. */
|
|
60
|
+
declare const markStale: (store: EntityStore, key: EntityKey, fields: Iterable<string>) => EntityStore;
|
|
61
|
+
/** Ends a refresh that did not land: the fields are present again as they were. */
|
|
62
|
+
declare const clearStale: (store: EntityStore, key: EntityKey, fields: Iterable<string>) => EntityStore;
|
|
63
|
+
/**
|
|
64
|
+
* Records fields the server settled without a value. A present field is left
|
|
65
|
+
* as it is: the answer that settled it was not about the value it holds.
|
|
66
|
+
* `false` forgets the marks, which is how a refresh asks again.
|
|
67
|
+
*/
|
|
68
|
+
declare const setUnavailable: (store: EntityStore, marks: ReadonlyArray<readonly [key: EntityKey, fields: Iterable<string>]>, unavailable: boolean) => EntityStore;
|
|
69
|
+
/** Whether the server settled this field without a value, and nothing has written it since. */
|
|
70
|
+
declare const isFieldUnavailable: (store: EntityStore, key: EntityKey, field: string) => boolean;
|
|
71
|
+
/** Records that the entity is known to be absent, so it is not refetched. */
|
|
72
|
+
declare const tombstone: (store: EntityStore, key: EntityKey) => EntityStore;
|
|
73
|
+
/** Forgets everything known about the entity, including a tombstone. */
|
|
74
|
+
declare const remove: (store: EntityStore, key: EntityKey) => EntityStore;
|
|
75
|
+
declare const entry: (store: EntityStore, key: EntityKey) => Option.Option<EntityEntry>;
|
|
76
|
+
declare const isTombstone: (store: EntityStore, key: EntityKey) => boolean;
|
|
77
|
+
/** A field is known only if it is present and not stale. */
|
|
78
|
+
declare const hasField: (store: EntityStore, key: EntityKey, field: string) => boolean;
|
|
79
|
+
/** A present field whose value may be outdated; absent fields are not stale. */
|
|
80
|
+
declare const isFieldStale: (store: EntityStore, key: EntityKey, field: string) => boolean;
|
|
81
|
+
/**
|
|
82
|
+
* Reads a present field, stale or not. Absence and a tombstone both yield
|
|
83
|
+
* `Option.none()`; a present `undefined`/`null` yields `Option.some`.
|
|
84
|
+
*/
|
|
85
|
+
declare const readField: (store: EntityStore, key: EntityKey, field: string) => Option.Option<unknown>;
|
|
86
|
+
/**
|
|
87
|
+
* The fields the planner must fetch. A tombstone makes every field known
|
|
88
|
+
* (absent), so it returns an empty list and the entity is not refetched, and a
|
|
89
|
+
* field the server settled without a value is known the same way. A
|
|
90
|
+
* requested window that differs from the one a field was fetched with also
|
|
91
|
+
* marks it missing, but only when the stored window is known (never `""`), so a
|
|
92
|
+
* writer that does not record windows cannot cause a refetch loop.
|
|
93
|
+
*/
|
|
94
|
+
declare const missingFields: (store: EntityStore, key: EntityKey, fields: Iterable<string>, windows?: Readonly<Record<string, string>>) => ReadonlyArray<string>;
|
|
61
95
|
//#endregion
|
|
62
96
|
//#region src/connection.d.ts
|
|
63
97
|
interface EdgeRef {
|
|
@@ -208,92 +242,6 @@ declare const RemoteData: {
|
|
|
208
242
|
map: <A, B>(data: RemoteData<A>, f: (value: A) => B) => RemoteData<B>;
|
|
209
243
|
};
|
|
210
244
|
//#endregion
|
|
211
|
-
//#region src/store.d.ts
|
|
212
|
-
type EntityKey = string;
|
|
213
|
-
interface EntityEntry {
|
|
214
|
-
readonly values: Readonly<Record<string, unknown>>;
|
|
215
|
-
readonly present: ReadonlySet<string>;
|
|
216
|
-
/** Present fields whose value may be outdated (revalidation needed). */
|
|
217
|
-
readonly stale: ReadonlySet<string>;
|
|
218
|
-
/**
|
|
219
|
-
* Fields the server settled without a value: asked for, and answered without
|
|
220
|
-
* them, whatever the reason. Not planned again until a refresh asks; a later
|
|
221
|
-
* write of the field clears it.
|
|
222
|
-
*/
|
|
223
|
-
readonly unavailable: ReadonlySet<string>;
|
|
224
|
-
/** The entity is known to be absent; a later write clears this. */
|
|
225
|
-
readonly tombstone: boolean;
|
|
226
|
-
/** Injected clock reading of the last write; never read from ambient state. */
|
|
227
|
-
readonly updatedAt: number;
|
|
228
|
-
/**
|
|
229
|
-
* The canonical window each present field was fetched with (`""` for none).
|
|
230
|
-
* A later request with a *known, different* window is planned as missing.
|
|
231
|
-
*/
|
|
232
|
-
readonly windows: Readonly<Record<string, string>>;
|
|
233
|
-
}
|
|
234
|
-
type EntityStore = Readonly<Record<EntityKey, EntityEntry>>;
|
|
235
|
-
/** The separator keeps an untrusted id from colliding with a prototype key. */
|
|
236
|
-
declare const entityKey: (entity: string, id: string) => EntityKey;
|
|
237
|
-
declare const emptyStore: EntityStore;
|
|
238
|
-
interface EntityWrite {
|
|
239
|
-
readonly key: EntityKey;
|
|
240
|
-
readonly values: Readonly<Record<string, unknown>>;
|
|
241
|
-
/** The canonical window each written relation field was fetched with. */
|
|
242
|
-
readonly windows?: Readonly<Record<string, string>> | undefined;
|
|
243
|
-
/** When this write dates the entity, instead of the batch's `now`. */
|
|
244
|
-
readonly updatedAt?: number | undefined;
|
|
245
|
-
}
|
|
246
|
-
/**
|
|
247
|
-
* Merges known values into many entities, copying the store once. Every
|
|
248
|
-
* written field becomes present and non-stale; a tombstone is cleared because
|
|
249
|
-
* the entity is evidently not absent.
|
|
250
|
-
*/
|
|
251
|
-
declare const writeEntities: (store: EntityStore, writes: ReadonlyArray<EntityWrite>, now?: number) => EntityStore;
|
|
252
|
-
/** `writeEntities` for one entity. */
|
|
253
|
-
declare const writeEntity: (store: EntityStore, key: EntityKey, values: Readonly<Record<string, unknown>>, now?: number, windows?: Readonly<Record<string, string>>) => EntityStore;
|
|
254
|
-
/**
|
|
255
|
-
* Marks present fields stale (`true`) or ends their staleness (`false`) across
|
|
256
|
-
* many entities, copying the store once. A field never present stays missing;
|
|
257
|
-
* an unknown entity is left alone.
|
|
258
|
-
*/
|
|
259
|
-
declare const setStale: (store: EntityStore, marks: ReadonlyArray<readonly [key: EntityKey, fields: Iterable<string>]>, stale: boolean) => EntityStore;
|
|
260
|
-
/** Marks present fields stale; fields that were never present stay missing. */
|
|
261
|
-
declare const markStale: (store: EntityStore, key: EntityKey, fields: Iterable<string>) => EntityStore;
|
|
262
|
-
/** Ends a refresh that did not land: the fields are present again as they were. */
|
|
263
|
-
declare const clearStale: (store: EntityStore, key: EntityKey, fields: Iterable<string>) => EntityStore;
|
|
264
|
-
/**
|
|
265
|
-
* Records fields the server settled without a value. A present field is left
|
|
266
|
-
* as it is: the answer that settled it was not about the value it holds.
|
|
267
|
-
* `false` forgets the marks, which is how a refresh asks again.
|
|
268
|
-
*/
|
|
269
|
-
declare const setUnavailable: (store: EntityStore, marks: ReadonlyArray<readonly [key: EntityKey, fields: Iterable<string>]>, unavailable: boolean) => EntityStore;
|
|
270
|
-
/** Whether the server settled this field without a value, and nothing has written it since. */
|
|
271
|
-
declare const isFieldUnavailable: (store: EntityStore, key: EntityKey, field: string) => boolean;
|
|
272
|
-
/** Records that the entity is known to be absent, so it is not refetched. */
|
|
273
|
-
declare const tombstone: (store: EntityStore, key: EntityKey) => EntityStore;
|
|
274
|
-
/** Forgets everything known about the entity, including a tombstone. */
|
|
275
|
-
declare const remove: (store: EntityStore, key: EntityKey) => EntityStore;
|
|
276
|
-
declare const entry: (store: EntityStore, key: EntityKey) => Option.Option<EntityEntry>;
|
|
277
|
-
declare const isTombstone: (store: EntityStore, key: EntityKey) => boolean;
|
|
278
|
-
/** A field is known only if it is present and not stale. */
|
|
279
|
-
declare const hasField: (store: EntityStore, key: EntityKey, field: string) => boolean;
|
|
280
|
-
/** A present field whose value may be outdated; absent fields are not stale. */
|
|
281
|
-
declare const isFieldStale: (store: EntityStore, key: EntityKey, field: string) => boolean;
|
|
282
|
-
/**
|
|
283
|
-
* Reads a present field, stale or not. Absence and a tombstone both yield
|
|
284
|
-
* `Option.none()`; a present `undefined`/`null` yields `Option.some`.
|
|
285
|
-
*/
|
|
286
|
-
declare const readField: (store: EntityStore, key: EntityKey, field: string) => Option.Option<unknown>;
|
|
287
|
-
/**
|
|
288
|
-
* The fields the planner must fetch. A tombstone makes every field known
|
|
289
|
-
* (absent), so it returns an empty list and the entity is not refetched, and a
|
|
290
|
-
* field the server settled without a value is known the same way. A
|
|
291
|
-
* requested window that differs from the one a field was fetched with also
|
|
292
|
-
* marks it missing, but only when the stored window is known (never `""`), so a
|
|
293
|
-
* writer that does not record windows cannot cause a refetch loop.
|
|
294
|
-
*/
|
|
295
|
-
declare const missingFields: (store: EntityStore, key: EntityKey, fields: Iterable<string>, windows?: Readonly<Record<string, string>>) => ReadonlyArray<string>;
|
|
296
|
-
//#endregion
|
|
297
245
|
//#region src/mutation.d.ts
|
|
298
246
|
interface NormalizedPatch {
|
|
299
247
|
readonly entity: string;
|
|
@@ -429,59 +377,171 @@ declare const Mutation: {
|
|
|
429
377
|
}) => MutationDescriptor<Name, TypeOf<Input>, TypeOf<Output>, Refused>;
|
|
430
378
|
};
|
|
431
379
|
//#endregion
|
|
432
|
-
//#region src/
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
readonly
|
|
436
|
-
readonly append?: LiveInsertion;
|
|
437
|
-
}
|
|
438
|
-
interface ConnectionSpec<Entity extends string = string> {
|
|
439
|
-
readonly entity: Entity;
|
|
440
|
-
readonly edgeKey?: Schema.Schema<unknown>;
|
|
441
|
-
readonly live?: LivePolicy;
|
|
442
|
-
}
|
|
443
|
-
interface QueryWindow {
|
|
444
|
-
readonly first?: number | undefined;
|
|
445
|
-
readonly last?: number | undefined;
|
|
446
|
-
readonly after?: Cursor | undefined;
|
|
447
|
-
readonly before?: Cursor | undefined;
|
|
448
|
-
}
|
|
449
|
-
interface QueryRef<Name extends string, Input> {
|
|
450
|
-
readonly query: Name;
|
|
451
|
-
readonly input: Input;
|
|
452
|
-
/** The input codec, so a `QueryRef` can encode itself without its descriptor. */
|
|
453
|
-
readonly Input: Schema.Codec<Input>;
|
|
454
|
-
readonly window: QueryWindow;
|
|
455
|
-
/** Connection identity: descriptor + canonical input, excluding the window. */
|
|
456
|
-
readonly identity: string;
|
|
457
|
-
}
|
|
458
|
-
interface QueryDescriptor<Name extends string, Input, Result> {
|
|
459
|
-
readonly name: Name;
|
|
460
|
-
readonly Input: Schema.Codec<Input>;
|
|
461
|
-
readonly Result: Result;
|
|
462
|
-
readonly ref: (input: Input) => QueryRef<Name, Input>;
|
|
463
|
-
/**
|
|
464
|
-
* What the query means, when it was declared with `Query.define`: the rows it
|
|
465
|
-
* is about, as a value an interpreter can compile. A descriptor from
|
|
466
|
-
* `Query.make` has none — its meaning lives in whatever the server registered
|
|
467
|
-
* to answer it — and a source is free to answer either.
|
|
468
|
-
*/
|
|
469
|
-
readonly body?: AnyQuery | undefined;
|
|
380
|
+
//#region src/optimistic.d.ts
|
|
381
|
+
interface EntityLayer {
|
|
382
|
+
readonly id: string;
|
|
383
|
+
readonly patches: ReadonlyArray<NormalizedPatch>;
|
|
470
384
|
}
|
|
471
385
|
/**
|
|
472
|
-
*
|
|
473
|
-
*
|
|
474
|
-
*
|
|
386
|
+
* Edges placed outside a connection's server-known region (`prepend`,
|
|
387
|
+
* `append`) or hidden from it (`remove`), owned by the request or live event
|
|
388
|
+
* that produced them.
|
|
475
389
|
*/
|
|
476
|
-
|
|
390
|
+
interface ConnectionOverlay {
|
|
391
|
+
readonly id: string;
|
|
392
|
+
readonly connection: string;
|
|
393
|
+
readonly edges: ReadonlyArray<Edge>;
|
|
394
|
+
readonly position: 'prepend' | 'append' | 'remove';
|
|
395
|
+
}
|
|
396
|
+
/** A connection change a request makes optimistically or a mutation result confirms. */
|
|
397
|
+
type ConnectionChange = {
|
|
398
|
+
readonly _tag: 'Insert';
|
|
399
|
+
readonly connection: string;
|
|
400
|
+
readonly position: 'prepend' | 'append';
|
|
401
|
+
readonly edge: Edge;
|
|
402
|
+
} | {
|
|
403
|
+
readonly _tag: 'Remove';
|
|
404
|
+
readonly connection: string;
|
|
405
|
+
readonly edge: Edge;
|
|
406
|
+
};
|
|
407
|
+
/** What a request changes before the server answers: entity patches and connection changes. */
|
|
408
|
+
type OptimisticOperation = NormalizedPatch | ConnectionChange;
|
|
409
|
+
/** A connection, by identity string or by anything that carries one (a `QueryRef`). */
|
|
410
|
+
type ConnectionIdentity = string | {
|
|
411
|
+
readonly identity: string;
|
|
412
|
+
};
|
|
413
|
+
declare const connectionIdentity: (connection: ConnectionIdentity) => string;
|
|
477
414
|
/**
|
|
478
|
-
*
|
|
479
|
-
*
|
|
480
|
-
*
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
415
|
+
* Constructors for connection changes: what a request shows optimistically
|
|
416
|
+
* (`MutateOptions.optimistic`) and what a mutation source reports it made
|
|
417
|
+
* (`MutationOutcome.connections`).
|
|
418
|
+
*/
|
|
419
|
+
declare const ConnectionChange: {
|
|
420
|
+
/** Show `ref` at the front of the connection until the request settles. */
|
|
421
|
+
prepend: (connection: ConnectionIdentity, ref: {
|
|
422
|
+
readonly entity: string;
|
|
423
|
+
readonly id: string;
|
|
424
|
+
}) => ConnectionChange;
|
|
425
|
+
/** Show `ref` at the end of the connection until the request settles. */
|
|
426
|
+
append: (connection: ConnectionIdentity, ref: {
|
|
427
|
+
readonly entity: string;
|
|
428
|
+
readonly id: string;
|
|
429
|
+
}) => ConnectionChange;
|
|
430
|
+
/** Hide `ref` from the connection until the request settles. */
|
|
431
|
+
remove: (connection: ConnectionIdentity, ref: {
|
|
432
|
+
readonly entity: string;
|
|
433
|
+
readonly id: string;
|
|
434
|
+
}) => ConnectionChange;
|
|
435
|
+
};
|
|
436
|
+
/**
|
|
437
|
+
* Applies a request's operations: its patches become one layer and its
|
|
438
|
+
* connection changes become overlays, all owned by `requestId` so settling
|
|
439
|
+
* removes every one of them together.
|
|
440
|
+
*/
|
|
441
|
+
declare const beginOptimistic: (optimistic: OptimisticState, requestId: string, operations: ReadonlyArray<OptimisticOperation>) => OptimisticState;
|
|
442
|
+
/** Pending entity layers and connection overlays over the base store. */
|
|
443
|
+
interface OptimisticState {
|
|
444
|
+
readonly layers: ReadonlyArray<EntityLayer>;
|
|
445
|
+
readonly overlays: ReadonlyArray<ConnectionOverlay>;
|
|
446
|
+
}
|
|
447
|
+
declare const emptyOptimistic: OptimisticState;
|
|
448
|
+
declare const addLayer: (optimistic: OptimisticState, layer: EntityLayer) => OptimisticState;
|
|
449
|
+
declare const removeLayer: (optimistic: OptimisticState, id: string) => OptimisticState;
|
|
450
|
+
declare const addOverlay: (optimistic: OptimisticState, overlay: ConnectionOverlay) => OptimisticState;
|
|
451
|
+
declare const removeOverlay: (optimistic: OptimisticState, id: string) => OptimisticState;
|
|
452
|
+
/**
|
|
453
|
+
* Base store with every layer applied in order. Later layers win. A patch is
|
|
454
|
+
* not news from the server, so it keeps the date of the value under it; an
|
|
455
|
+
* entity only a request holds (a temporary id) is not the server's to age, and
|
|
456
|
+
* is dated `Infinity`, so freshness never plans or times it.
|
|
457
|
+
*/
|
|
458
|
+
declare const visibleStore: (base: EntityStore, optimistic: OptimisticState) => EntityStore;
|
|
459
|
+
/**
|
|
460
|
+
* A fresh page is newer than any settled overlay it covers: an edge the page
|
|
461
|
+
* carries no longer needs an insert overlay, and a `remove` overlay hiding it
|
|
462
|
+
* is stale (the server says it is back). A pending request's overlays are
|
|
463
|
+
* left alone; they settle with the request.
|
|
464
|
+
*/
|
|
465
|
+
declare const pruneOverlays: (optimistic: OptimisticState, connection: string, covered: ReadonlySet<string>, pending: ReadonlySet<string>) => OptimisticState;
|
|
466
|
+
/**
|
|
467
|
+
* Applies a result idempotently and releases the request's layer and overlays:
|
|
468
|
+
* a later layer re-wins on rebase. Confirmed connection changes are recorded
|
|
469
|
+
* once per request, as overlays the request no longer owns.
|
|
470
|
+
*/
|
|
471
|
+
declare const settleSuccess: (base: EntityStore, optimistic: OptimisticState, state: MutationState, requestId: string, answer: MutationAnswer & {
|
|
472
|
+
/** Connection changes the server confirmed; they replace the request's own. */
|
|
473
|
+
readonly connections?: ReadonlyArray<ConnectionChange> | undefined;
|
|
474
|
+
}) => {
|
|
475
|
+
readonly store: EntityStore;
|
|
476
|
+
readonly state: MutationState;
|
|
477
|
+
readonly optimistic: OptimisticState;
|
|
478
|
+
};
|
|
479
|
+
/** A failed request's layer and overlays are dropped; the base was never mutated. */
|
|
480
|
+
declare const settleFailure: (optimistic: OptimisticState, requestId: string) => OptimisticState;
|
|
481
|
+
/**
|
|
482
|
+
* Visible edges for one connection: applicable overlays are placed outside the
|
|
483
|
+
* server-known segments and de-duplicated by edge identity (across overlays too),
|
|
484
|
+
* so a pending insert never corrupts server-known ordering; a `remove` overlay
|
|
485
|
+
* hides its edges wherever they are, and so does a tombstone on the edge's
|
|
486
|
+
* target when `store` is given. A later prepend lands before an earlier one, a
|
|
487
|
+
* later append after, so pending inserts read in the order they were made.
|
|
488
|
+
* Boundaries still come from the connection's segments.
|
|
489
|
+
*/
|
|
490
|
+
declare const visibleItems: (connection: Connection, connectionId: string, overlays: ReadonlyArray<ConnectionOverlay>, store?: EntityStore) => ReadonlyArray<Edge>;
|
|
491
|
+
//#endregion
|
|
492
|
+
//#region src/query.d.ts
|
|
493
|
+
type LiveInsertion = 'visible' | 'boundary' | 'invalidate' | 'ignore';
|
|
494
|
+
interface LivePolicy {
|
|
495
|
+
readonly prepend?: LiveInsertion;
|
|
496
|
+
readonly append?: LiveInsertion;
|
|
497
|
+
}
|
|
498
|
+
interface ConnectionSpec<Entity extends string = string> {
|
|
499
|
+
readonly entity: Entity;
|
|
500
|
+
readonly edgeKey?: Schema.Schema<unknown>;
|
|
501
|
+
readonly live?: LivePolicy;
|
|
502
|
+
}
|
|
503
|
+
interface QueryWindow {
|
|
504
|
+
readonly first?: number | undefined;
|
|
505
|
+
readonly last?: number | undefined;
|
|
506
|
+
readonly after?: Cursor | undefined;
|
|
507
|
+
readonly before?: Cursor | undefined;
|
|
508
|
+
}
|
|
509
|
+
interface QueryRef<Name extends string, Input> {
|
|
510
|
+
readonly query: Name;
|
|
511
|
+
readonly input: Input;
|
|
512
|
+
/** The input codec, so a `QueryRef` can encode itself without its descriptor. */
|
|
513
|
+
readonly Input: Schema.Codec<Input>;
|
|
514
|
+
readonly window: QueryWindow;
|
|
515
|
+
/** Connection identity: descriptor + canonical input, excluding the window. */
|
|
516
|
+
readonly identity: string;
|
|
517
|
+
}
|
|
518
|
+
interface QueryDescriptor<Name extends string, Input, Result> {
|
|
519
|
+
readonly name: Name;
|
|
520
|
+
readonly Input: Schema.Codec<Input>;
|
|
521
|
+
readonly Result: Result;
|
|
522
|
+
readonly ref: (input: Input) => QueryRef<Name, Input>;
|
|
523
|
+
/**
|
|
524
|
+
* What the query means, when it was declared with `Query.define`: the rows it
|
|
525
|
+
* is about, as a value an interpreter can compile. A descriptor from
|
|
526
|
+
* `Query.make` has none — its meaning lives in whatever the server registered
|
|
527
|
+
* to answer it — and a source is free to answer either.
|
|
528
|
+
*/
|
|
529
|
+
readonly body?: AnyQuery | undefined;
|
|
530
|
+
}
|
|
531
|
+
/**
|
|
532
|
+
* The inputs of a definition's body: one placeholder per key of `Input`, typed
|
|
533
|
+
* as that key's value. A body is built once, so these stand for what the query
|
|
534
|
+
* will be given rather than being it.
|
|
535
|
+
*/
|
|
536
|
+
type QueryInputs<Input> = { readonly [K in keyof Input & string]: InputExpr<Input[K]>; };
|
|
537
|
+
/**
|
|
538
|
+
* What separates a query's name from its canonical encoded input in a
|
|
539
|
+
* connection identity.
|
|
540
|
+
*
|
|
541
|
+
* NUL, because `JSON.stringify` escapes it inside a string (`\u0000`) and a
|
|
542
|
+
* query name is a TypeScript identifier — so the first occurrence is always the
|
|
543
|
+
* boundary, and a name or an input value cannot forge one. Anything recovering
|
|
544
|
+
* the query from an identity splits here.
|
|
485
545
|
*/
|
|
486
546
|
declare const IDENTITY_SEPARATOR = "\0";
|
|
487
547
|
/** Stable stringify: object keys sorted, undefined-valued keys dropped, so equal inputs encode equally. */
|
|
@@ -497,6 +557,14 @@ type ResultOf<Result> = Result extends {
|
|
|
497
557
|
/** A relational `Query` over one Entity, as `Query.define`'s body returns. */
|
|
498
558
|
type RelationalQueryOf<E extends AnyEntity> = ReturnType<typeof Query.from<E>>;
|
|
499
559
|
declare const Query$1: {
|
|
560
|
+
from: <E extends AnyEntity>(entity: E) => Query<E>;
|
|
561
|
+
where: (...predicates: ReadonlyArray<import("foldkit-entity").Predicate>) => <E extends AnyEntity>(self: Query<E>) => Query<E>;
|
|
562
|
+
orderBy: (...terms: ReadonlyArray<import("foldkit-entity").Ordering>) => <E extends AnyEntity>(self: Query<E>) => Query<E>;
|
|
563
|
+
orderFor: (self: AnyQuery, input: Readonly<Record<string, unknown>>) => ReadonlyArray<import("foldkit-entity").OrderTerm>;
|
|
564
|
+
placement: (self: AnyQuery, input: Readonly<Record<string, unknown>>) => import("foldkit-entity").Placement;
|
|
565
|
+
dependencies: (self: AnyQuery) => import("foldkit-entity").QueryDependencies;
|
|
566
|
+
unsupported: (self: AnyQuery, supported: ReadonlyArray<import("foldkit-entity").Operation>) => ReadonlyArray<import("foldkit-entity").Operation>;
|
|
567
|
+
show: (self: AnyQuery) => string;
|
|
500
568
|
/** Declares the entity and options a query's result is a connection over. */
|
|
501
569
|
connection: <Entity extends string>(entity: {
|
|
502
570
|
readonly name: Entity;
|
|
@@ -542,19 +610,11 @@ declare const Query$1: {
|
|
|
542
610
|
}) => RelationalQueryOf<E>, options?: {
|
|
543
611
|
readonly edgeKey?: Schema.Schema<unknown>;
|
|
544
612
|
readonly live?: LivePolicy;
|
|
545
|
-
}) => QueryDescriptor<Name, TypeOf<Input>, ConnectionSpec<E[
|
|
613
|
+
}) => QueryDescriptor<Name, TypeOf<Input>, ConnectionSpec<E['name']>>;
|
|
546
614
|
first: (count: number) => <R extends QueryRef<string, unknown>>(ref: R) => R;
|
|
547
615
|
last: (count: number) => <R extends QueryRef<string, unknown>>(ref: R) => R;
|
|
548
616
|
after: (cursor: Cursor) => <R extends QueryRef<string, unknown>>(ref: R) => R;
|
|
549
617
|
before: (cursor: Cursor) => <R extends QueryRef<string, unknown>>(ref: R) => R;
|
|
550
|
-
from: <E extends AnyEntity>(entity: E) => Query<E>;
|
|
551
|
-
where: (...predicates: ReadonlyArray<import("foldkit-entity").Predicate>) => <E extends AnyEntity>(self: Query<E>) => Query<E>;
|
|
552
|
-
orderBy: (...terms: ReadonlyArray<import("foldkit-entity").Ordering>) => <E extends AnyEntity>(self: Query<E>) => Query<E>;
|
|
553
|
-
orderFor: (self: AnyQuery, input: Readonly<Record<string, unknown>>) => ReadonlyArray<import("foldkit-entity").OrderTerm>;
|
|
554
|
-
placement: (self: AnyQuery, input: Readonly<Record<string, unknown>>) => import("foldkit-entity").Placement;
|
|
555
|
-
dependencies: (self: AnyQuery) => import("foldkit-entity").QueryDependencies;
|
|
556
|
-
unsupported: (self: AnyQuery, supported: ReadonlyArray<import("foldkit-entity").Operation>) => ReadonlyArray<import("foldkit-entity").Operation>;
|
|
557
|
-
show: (self: AnyQuery) => string;
|
|
558
618
|
};
|
|
559
619
|
//#endregion
|
|
560
620
|
//#region src/requirement.d.ts
|
|
@@ -604,9 +664,9 @@ declare const Requirement: {
|
|
|
604
664
|
mergeConnections: typeof mergeConnections;
|
|
605
665
|
};
|
|
606
666
|
/** Remote's entity requirements on a Projection, unioned per entity and id as nodes compose. */
|
|
607
|
-
declare const RemoteRequirements: MetadataKey<Requirement>;
|
|
667
|
+
declare const RemoteRequirements: import("foldkit-metadata").MetadataKey<Requirement>;
|
|
608
668
|
/** Remote's query connections on a Projection, unioned per connection and window. */
|
|
609
|
-
declare const RemoteConnections: MetadataKey<ConnectionRequirement>;
|
|
669
|
+
declare const RemoteConnections: import("foldkit-metadata").MetadataKey<ConnectionRequirement>;
|
|
610
670
|
/** The entity requirements a Projection carries. */
|
|
611
671
|
declare const requirementsOf: (projection: {
|
|
612
672
|
readonly metadata: Metadata;
|
|
@@ -616,372 +676,67 @@ declare const connectionsOf: (projection: {
|
|
|
616
676
|
readonly metadata: Metadata;
|
|
617
677
|
}) => ReadonlyArray<ConnectionRequirement>;
|
|
618
678
|
//#endregion
|
|
619
|
-
//#region src/
|
|
620
|
-
interface EntityLayer {
|
|
621
|
-
readonly id: string;
|
|
622
|
-
readonly patches: ReadonlyArray<NormalizedPatch>;
|
|
623
|
-
}
|
|
679
|
+
//#region src/wire.d.ts
|
|
624
680
|
/**
|
|
625
|
-
*
|
|
626
|
-
*
|
|
627
|
-
*
|
|
681
|
+
* The read/live protocol version. A batch names the version it speaks and the
|
|
682
|
+
* server refuses a mismatch with `RemoteProtocolError`, so a shape change never
|
|
683
|
+
* drifts silently: bump it whenever `ReadRequest` or `LiveRequirement` change.
|
|
628
684
|
*/
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
readonly
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
readonly
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
readonly
|
|
643
|
-
|
|
644
|
-
readonly edge: Edge;
|
|
645
|
-
};
|
|
646
|
-
/** What a request changes before the server answers: entity patches and connection changes. */
|
|
647
|
-
type OptimisticOperation = NormalizedPatch | ConnectionChange;
|
|
648
|
-
/** A connection, by identity string or by anything that carries one (a `QueryRef`). */
|
|
649
|
-
type ConnectionIdentity = string | {
|
|
650
|
-
readonly identity: string;
|
|
651
|
-
};
|
|
652
|
-
declare const connectionIdentity: (connection: ConnectionIdentity) => string;
|
|
685
|
+
declare const REMOTE_PROTOCOL_VERSION = 4;
|
|
686
|
+
declare const RemoteReadError_base: Schema.Class<RemoteReadError, Schema.TaggedStruct<"RemoteReadError", {
|
|
687
|
+
readonly message: Schema.String;
|
|
688
|
+
}>, import("effect/Cause").YieldableError>;
|
|
689
|
+
declare class RemoteReadError extends RemoteReadError_base {}
|
|
690
|
+
declare const RemoteMutationError_base: Schema.Class<RemoteMutationError, Schema.TaggedStruct<"RemoteMutationError", {
|
|
691
|
+
readonly message: Schema.String;
|
|
692
|
+
/** The mutation's own `Refusal`, encoded, when the server refused it as one. */
|
|
693
|
+
readonly refusal: Schema.optional<Schema.Unknown>;
|
|
694
|
+
}>, import("effect/Cause").YieldableError>;
|
|
695
|
+
declare class RemoteMutationError extends RemoteMutationError_base {}
|
|
696
|
+
declare const RemoteForbidden_base: Schema.Class<RemoteForbidden, Schema.TaggedStruct<"Forbidden", {
|
|
697
|
+
readonly message: Schema.String;
|
|
698
|
+
readonly reason: Schema.optional<Schema.String>;
|
|
699
|
+
}>, import("effect/Cause").YieldableError>;
|
|
653
700
|
/**
|
|
654
|
-
*
|
|
655
|
-
*
|
|
656
|
-
*
|
|
701
|
+
* The server refused who is asking: they may not do this, and asking again
|
|
702
|
+
* as the same principal is refused again. `reason` is the application's own
|
|
703
|
+
* words for the person who asked; without it the refusal says nothing of why.
|
|
657
704
|
*/
|
|
658
|
-
declare
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
declare const
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
* not news from the server, so it keeps the date of the value under it; an
|
|
694
|
-
* entity only a request holds (a temporary id) is not the server's to age, and
|
|
695
|
-
* is dated `Infinity`, so freshness never plans or times it.
|
|
696
|
-
*/
|
|
697
|
-
declare const visibleStore: (base: EntityStore, optimistic: OptimisticState) => EntityStore;
|
|
698
|
-
/**
|
|
699
|
-
* A fresh page is newer than any settled overlay it covers: an edge the page
|
|
700
|
-
* carries no longer needs an insert overlay, and a `remove` overlay hiding it
|
|
701
|
-
* is stale (the server says it is back). A pending request's overlays are
|
|
702
|
-
* left alone; they settle with the request.
|
|
703
|
-
*/
|
|
704
|
-
declare const pruneOverlays: (optimistic: OptimisticState, connection: string, covered: ReadonlySet<string>, pending: ReadonlySet<string>) => OptimisticState;
|
|
705
|
-
/**
|
|
706
|
-
* Applies a result idempotently and releases the request's layer and overlays:
|
|
707
|
-
* a later layer re-wins on rebase. Confirmed connection changes are recorded
|
|
708
|
-
* once per request, as overlays the request no longer owns.
|
|
709
|
-
*/
|
|
710
|
-
declare const settleSuccess: (base: EntityStore, optimistic: OptimisticState, state: MutationState, requestId: string, answer: MutationAnswer & {
|
|
711
|
-
/** Connection changes the server confirmed; they replace the request's own. */
|
|
712
|
-
readonly connections?: ReadonlyArray<ConnectionChange> | undefined;
|
|
713
|
-
}) => {
|
|
714
|
-
readonly store: EntityStore;
|
|
715
|
-
readonly state: MutationState;
|
|
716
|
-
readonly optimistic: OptimisticState;
|
|
717
|
-
};
|
|
718
|
-
/** A failed request's layer and overlays are dropped; the base was never mutated. */
|
|
719
|
-
declare const settleFailure: (optimistic: OptimisticState, requestId: string) => OptimisticState;
|
|
720
|
-
/**
|
|
721
|
-
* Visible edges for one connection: applicable overlays are placed outside the
|
|
722
|
-
* server-known segments and de-duplicated by edge identity (across overlays too),
|
|
723
|
-
* so a pending insert never corrupts server-known ordering; a `remove` overlay
|
|
724
|
-
* hides its edges wherever they are, and so does a tombstone on the edge's
|
|
725
|
-
* target when `store` is given. A later prepend lands before an earlier one, a
|
|
726
|
-
* later append after, so pending inserts read in the order they were made.
|
|
727
|
-
* Boundaries still come from the connection's segments.
|
|
728
|
-
*/
|
|
729
|
-
declare const visibleItems: (connection: Connection, connectionId: string, overlays: ReadonlyArray<ConnectionOverlay>, store?: EntityStore) => ReadonlyArray<Edge>;
|
|
730
|
-
//#endregion
|
|
731
|
-
//#region src/live.d.ts
|
|
732
|
-
type LiveCursor = number;
|
|
733
|
-
type LiveEvent = {
|
|
734
|
-
readonly _tag: 'EntityPatched';
|
|
735
|
-
readonly ref: EdgeRef;
|
|
736
|
-
readonly values: Readonly<Record<string, unknown>>;
|
|
737
|
-
readonly changed: ReadonlyArray<string>;
|
|
738
|
-
readonly cursor: LiveCursor;
|
|
739
|
-
} | {
|
|
740
|
-
readonly _tag: 'EntityDeleted';
|
|
741
|
-
readonly ref: EdgeRef;
|
|
742
|
-
readonly cursor: LiveCursor;
|
|
743
|
-
} | {
|
|
744
|
-
readonly _tag: 'ConnectionInsert';
|
|
745
|
-
readonly connection: string;
|
|
746
|
-
readonly position: 'prepend' | 'append';
|
|
747
|
-
readonly edge: Edge;
|
|
748
|
-
readonly cursor: LiveCursor;
|
|
749
|
-
} | {
|
|
750
|
-
readonly _tag: 'ConnectionRemove';
|
|
751
|
-
readonly connection: string;
|
|
752
|
-
readonly edge: Edge;
|
|
753
|
-
readonly cursor: LiveCursor;
|
|
754
|
-
} | {
|
|
755
|
-
readonly _tag: 'ConnectionInvalidate';
|
|
756
|
-
readonly connection: string;
|
|
757
|
-
readonly cursor: LiveCursor;
|
|
758
|
-
};
|
|
759
|
-
interface BoundaryCounts {
|
|
760
|
-
readonly before: number;
|
|
761
|
-
readonly after: number;
|
|
762
|
-
}
|
|
763
|
-
interface LiveState {
|
|
764
|
-
/** Last applied cursor for this stream. */
|
|
765
|
-
readonly cursor: LiveCursor;
|
|
766
|
-
/** Edges recorded outside the loaded boundary, not visible in `items`. */
|
|
767
|
-
readonly boundary: Readonly<Record<string, BoundaryCounts>>;
|
|
768
|
-
}
|
|
769
|
-
declare const emptyLiveState: LiveState;
|
|
770
|
-
type LiveOutcome = 'applied' | 'duplicate' | 'gap';
|
|
771
|
-
/**
|
|
772
|
-
* Ordering per stream: at most the next cursor; ahead is a gap. Cursor `0` means
|
|
773
|
-
* "no baseline yet" (cursors are 1-based), so the first event of a stream is
|
|
774
|
-
* accepted whatever its cursor — otherwise a stream not starting at 1 would be a
|
|
775
|
-
* spurious gap.
|
|
776
|
-
*/
|
|
777
|
-
declare const classifyLive: (state: LiveState, cursor: LiveCursor) => LiveOutcome;
|
|
778
|
-
interface EntityApplied {
|
|
779
|
-
readonly state: LiveState;
|
|
780
|
-
readonly store: EntityStore;
|
|
781
|
-
readonly outcome: LiveOutcome;
|
|
782
|
-
}
|
|
783
|
-
declare const applyEntityEvent: (state: LiveState, store: EntityStore, event: Extract<LiveEvent, {
|
|
784
|
-
_tag: "EntityPatched" | "EntityDeleted";
|
|
785
|
-
}>, now?: number) => EntityApplied;
|
|
786
|
-
interface ConnectionApplied {
|
|
787
|
-
readonly state: LiveState;
|
|
788
|
-
readonly optimistic: OptimisticState;
|
|
789
|
-
readonly outcome: LiveOutcome;
|
|
790
|
-
/**
|
|
791
|
-
* A connection the event says to refetch. Reported rather than recorded here,
|
|
792
|
-
* because staleness lives on the connection in the Model — which is what the
|
|
793
|
-
* planner and every read consult. It used to be written into a set on this
|
|
794
|
-
* live state that nothing read, so an invalidating event did nothing at all.
|
|
795
|
-
*/
|
|
796
|
-
readonly invalidated?: string | undefined;
|
|
797
|
-
}
|
|
798
|
-
declare const applyConnectionEvent: (state: LiveState, optimistic: OptimisticState, event: Extract<LiveEvent, {
|
|
799
|
-
_tag: "ConnectionInsert" | "ConnectionRemove" | "ConnectionInvalidate";
|
|
800
|
-
}>, policy?: LivePolicy) => ConnectionApplied;
|
|
801
|
-
/** `hasPrevious` accounting for edges recorded outside the loaded boundary. */
|
|
802
|
-
declare const liveHasPrevious: (connection: Connection, state: LiveState, connectionId: string) => boolean;
|
|
803
|
-
/** `hasNext` accounting for edges recorded outside the loaded boundary. */
|
|
804
|
-
declare const liveHasNext: (connection: Connection, state: LiveState, connectionId: string) => boolean;
|
|
805
|
-
//#endregion
|
|
806
|
-
//#region src/retain.d.ts
|
|
807
|
-
/** A connection to keep: its identity, and what a page of it selects of each item, if a projection reads it. */
|
|
808
|
-
interface ConnectionRoot {
|
|
809
|
-
readonly identity: string;
|
|
810
|
-
readonly select?: RelationRequirement | undefined;
|
|
811
|
-
}
|
|
812
|
-
interface RetentionRoots {
|
|
813
|
-
readonly requirements: ReadonlyArray<Requirement>;
|
|
814
|
-
/** Connections (`QueryRef.identity`) to keep, with their edges' targets and what `select` reaches through them. */
|
|
815
|
-
readonly connections: ReadonlyArray<ConnectionRoot>;
|
|
816
|
-
}
|
|
817
|
-
interface Retained {
|
|
818
|
-
readonly entities: EntityStore;
|
|
819
|
-
readonly connections: Readonly<Record<string, Connection>>;
|
|
820
|
-
readonly optimistic: OptimisticState;
|
|
821
|
-
}
|
|
822
|
-
/**
|
|
823
|
-
* The entity keys the roots reach: each root entity, the targets its retained
|
|
824
|
-
* fields refer to, and recursively the targets a nested relation selects.
|
|
825
|
-
*/
|
|
826
|
-
declare const reachable: (store: EntityStore, roots: RetentionRoots, connections: Readonly<Record<string, Connection>>, optimistic: OptimisticState, pending: ReadonlySet<string>) => ReadonlySet<EntityKey>;
|
|
827
|
-
/**
|
|
828
|
-
* Drops every entity the roots do not reach, every connection they do not
|
|
829
|
-
* name, and every settled overlay on a dropped connection, keeping whatever a
|
|
830
|
-
* pending request's layer or overlays touch. Pure.
|
|
831
|
-
*/
|
|
832
|
-
declare const gc: (state: Retained & {
|
|
833
|
-
readonly mutations: MutationState;
|
|
834
|
-
}, roots: RetentionRoots) => Retained;
|
|
835
|
-
//#endregion
|
|
836
|
-
//#region src/persistence.d.ts
|
|
837
|
-
/** Bump when the serialized shape changes; a mismatch discards the cache. */
|
|
838
|
-
declare const REMOTE_CACHE_VERSION = 5;
|
|
839
|
-
/**
|
|
840
|
-
* What survives a reload: the entity cache, and the connections an application
|
|
841
|
-
* **declared** should come back.
|
|
842
|
-
*
|
|
843
|
-
* Connections are session state by default and stay that way — `snapshotOf`
|
|
844
|
-
* takes none unless asked. What a declared one keeps is its **edges and
|
|
845
|
-
* nothing else**, which is not a simplification but the rule: a connection's
|
|
846
|
-
* boundaries are cursors the server minted, and a cursor may name server state
|
|
847
|
-
* that no longer exists. Restoring one would be claiming to know where the
|
|
848
|
-
* page ends. The shape has nowhere to put a cursor, so that cannot be got
|
|
849
|
-
* wrong later.
|
|
850
|
-
*/
|
|
851
|
-
interface Snapshot {
|
|
852
|
-
readonly entities: EntityStore;
|
|
853
|
-
/**
|
|
854
|
-
* By connection identity: its **segments**, each a run of edges. No
|
|
855
|
-
* boundaries.
|
|
856
|
-
*
|
|
857
|
-
* Segments rather than one flat list, because the gaps between them are
|
|
858
|
-
* knowledge too. A connection paged from both ends holds a head and a tail
|
|
859
|
-
* with an unfetched middle; flattening those into one run would claim the
|
|
860
|
-
* tail's first row follows the head's last, which is a thing the client was
|
|
861
|
-
* never told.
|
|
862
|
-
*/
|
|
863
|
-
readonly connections: Readonly<Record<string, ReadonlyArray<ReadonlyArray<Edge>>>>;
|
|
864
|
-
}
|
|
865
|
-
interface SnapshotOptions {
|
|
866
|
-
/**
|
|
867
|
-
* Who or what the snapshot is for (a user id, a tenant, a build). A snapshot
|
|
868
|
-
* taken under another scope is discarded rather than shown to the wrong
|
|
869
|
-
* reader. Default: unscoped, which only matches unscoped.
|
|
870
|
-
*/
|
|
871
|
-
readonly scope?: string | undefined;
|
|
872
|
-
/** Snapshots larger than this many bytes are not written and not read. */
|
|
873
|
-
readonly maxBytes?: number | undefined;
|
|
874
|
-
}
|
|
875
|
-
/** How a snapshot meets a store that already has entries. */
|
|
876
|
-
type MergePolicy = 'replace' | 'preserve-existing';
|
|
877
|
-
/**
|
|
878
|
-
* The snapshot text, or `undefined` when it would exceed `maxBytes`. Build the
|
|
879
|
-
* snapshot with `snapshotOf`, which is where the connections that survive are
|
|
880
|
-
* named; runtime state — optimistic layers, live cursors, gaps, the mutation
|
|
881
|
-
* ledger — is never in one.
|
|
882
|
-
*/
|
|
883
|
-
declare function dehydrate(snapshot: Snapshot, options?: SnapshotOptions & {
|
|
884
|
-
readonly maxBytes?: undefined;
|
|
885
|
-
}): string;
|
|
886
|
-
declare function dehydrate(snapshot: Snapshot, options?: SnapshotOptions): string | undefined;
|
|
887
|
-
declare const RemotePersistence: {
|
|
888
|
-
/** The snapshot text, or `undefined` when it would exceed `maxBytes`. */
|
|
889
|
-
dehydrate: typeof dehydrate;
|
|
890
|
-
/** What a snapshot text holds, or `undefined` when it is refused. */
|
|
891
|
-
hydrate: (raw: string | undefined | null, options?: SnapshotOptions) => Snapshot | undefined;
|
|
892
|
-
/**
|
|
893
|
-
* What to keep of a Model, naming the connections that should survive a
|
|
894
|
-
* reload. Naming none is the default and is what Remote always did.
|
|
895
|
-
*/
|
|
896
|
-
snapshotOf: (model: {
|
|
897
|
-
readonly entities: EntityStore;
|
|
898
|
-
readonly connections: Readonly<Record<string, Connection>>;
|
|
899
|
-
}, options?: {
|
|
900
|
-
readonly connections?: ReadonlyArray<ConnectionIdentity> | undefined;
|
|
901
|
-
}) => Snapshot;
|
|
902
|
-
/** A snapshot's entities brought into a store by policy. */
|
|
903
|
-
mergeStores: (current: EntityStore, snapshot: EntityStore, policy: MergePolicy) => EntityStore;
|
|
904
|
-
/** The cache as a fresh session has it; what a refused `restore` yields. */
|
|
905
|
-
emptySnapshot: Snapshot;
|
|
906
|
-
/**
|
|
907
|
-
* Writes the snapshot under `key`, or removes the key when the store would
|
|
908
|
-
* exceed `maxBytes`, so a stale smaller snapshot cannot outlive a larger
|
|
909
|
-
* store it no longer describes.
|
|
910
|
-
*/
|
|
911
|
-
save: (snapshot: Snapshot, options: {
|
|
912
|
-
readonly key: string;
|
|
913
|
-
} & SnapshotOptions) => Effect.Effect<void, KeyValueStore.KeyValueStoreError, KeyValueStore.KeyValueStore>;
|
|
914
|
-
/**
|
|
915
|
-
* Reads the cache. A missing key yields `emptySnapshot`; anything `hydrate`
|
|
916
|
-
* refuses yields `emptySnapshot` and removes the key, so the next `restore`
|
|
917
|
-
* is clean and the planner refetches.
|
|
918
|
-
*/
|
|
919
|
-
restore: (options: {
|
|
920
|
-
readonly key: string;
|
|
921
|
-
} & SnapshotOptions) => Effect.Effect<Snapshot, KeyValueStore.KeyValueStoreError, KeyValueStore.KeyValueStore>;
|
|
922
|
-
};
|
|
923
|
-
//#endregion
|
|
924
|
-
//#region src/wire.d.ts
|
|
925
|
-
/**
|
|
926
|
-
* The read/live protocol version. A batch names the version it speaks and the
|
|
927
|
-
* server refuses a mismatch with `RemoteProtocolError`, so a shape change never
|
|
928
|
-
* drifts silently: bump it whenever `ReadRequest` or `LiveRequirement` change.
|
|
929
|
-
*/
|
|
930
|
-
declare const REMOTE_PROTOCOL_VERSION = 4;
|
|
931
|
-
declare const RemoteReadError_base: Schema.Class<RemoteReadError, Schema.TaggedStruct<"RemoteReadError", {
|
|
932
|
-
readonly message: Schema.String;
|
|
933
|
-
}>, import("effect/Cause").YieldableError>;
|
|
934
|
-
declare class RemoteReadError extends RemoteReadError_base {}
|
|
935
|
-
declare const RemoteMutationError_base: Schema.Class<RemoteMutationError, Schema.TaggedStruct<"RemoteMutationError", {
|
|
936
|
-
readonly message: Schema.String;
|
|
937
|
-
/** The mutation's own `Refusal`, encoded, when the server refused it as one. */
|
|
938
|
-
readonly refusal: Schema.optional<Schema.Unknown>;
|
|
939
|
-
}>, import("effect/Cause").YieldableError>;
|
|
940
|
-
declare class RemoteMutationError extends RemoteMutationError_base {}
|
|
941
|
-
declare const RemoteForbidden_base: Schema.Class<RemoteForbidden, Schema.TaggedStruct<"Forbidden", {
|
|
942
|
-
readonly message: Schema.String;
|
|
943
|
-
readonly reason: Schema.optional<Schema.String>;
|
|
944
|
-
}>, import("effect/Cause").YieldableError>;
|
|
945
|
-
/**
|
|
946
|
-
* The server refused who is asking: they may not do this, and asking again
|
|
947
|
-
* as the same principal is refused again. `reason` is the application's own
|
|
948
|
-
* words for the person who asked; without it the refusal says nothing of why.
|
|
949
|
-
*/
|
|
950
|
-
declare class RemoteForbidden extends RemoteForbidden_base {}
|
|
951
|
-
declare const RemoteLiveError_base: Schema.Class<RemoteLiveError, Schema.TaggedStruct<"RemoteLiveError", {
|
|
952
|
-
readonly message: Schema.String;
|
|
953
|
-
}>, import("effect/Cause").YieldableError>;
|
|
954
|
-
declare class RemoteLiveError extends RemoteLiveError_base {}
|
|
955
|
-
declare const RemoteProtocolError_base: Schema.Class<RemoteProtocolError, Schema.TaggedStruct<"RemoteProtocolError", {
|
|
956
|
-
readonly message: Schema.String;
|
|
957
|
-
readonly expected: Schema.Number;
|
|
958
|
-
readonly received: Schema.Number;
|
|
959
|
-
}>, import("effect/Cause").YieldableError>;
|
|
960
|
-
/** The peer speaks another protocol version; nothing was read. */
|
|
961
|
-
declare class RemoteProtocolError extends RemoteProtocolError_base {}
|
|
962
|
-
/** A page size: a non-negative integer, refused at decode otherwise rather than defaulted. */
|
|
963
|
-
declare const PageSize: Schema.Number;
|
|
964
|
-
declare const WindowSchema: Schema.Struct<{
|
|
965
|
-
readonly first: Schema.optional<Schema.Number>;
|
|
966
|
-
readonly last: Schema.optional<Schema.Number>;
|
|
967
|
-
readonly after: Schema.optional<Schema.String>;
|
|
968
|
-
readonly before: Schema.optional<Schema.String>;
|
|
969
|
-
}>;
|
|
970
|
-
/** A request may not name more fields of one entity than this; a selection never does. */
|
|
971
|
-
declare const MAX_FIELDS_PER_REQUEST = 256;
|
|
972
|
-
/** A selection may not nest relations deeper than this; the wire refuses more. */
|
|
973
|
-
declare const MAX_RELATION_DEPTH = 8;
|
|
974
|
-
declare const RelationRequest: Schema.Codec<RelationRequirement, RelationRequirement, never, never>;
|
|
975
|
-
declare const ReadRequest: Schema.Codec<Requirement, Requirement>;
|
|
976
|
-
declare const ReadBatch: Schema.Struct<{
|
|
977
|
-
readonly version: Schema.Number;
|
|
978
|
-
readonly requests: Schema.$Array<Schema.Codec<Requirement, Requirement, never, never>>;
|
|
979
|
-
}>;
|
|
980
|
-
declare const NormalizedEntity: Schema.Struct<{
|
|
981
|
-
readonly entity: Schema.String;
|
|
982
|
-
readonly id: Schema.String;
|
|
983
|
-
readonly values: Schema.$Record<Schema.String, Schema.Unknown>;
|
|
984
|
-
}>;
|
|
705
|
+
declare class RemoteForbidden extends RemoteForbidden_base {}
|
|
706
|
+
declare const RemoteLiveError_base: Schema.Class<RemoteLiveError, Schema.TaggedStruct<"RemoteLiveError", {
|
|
707
|
+
readonly message: Schema.String;
|
|
708
|
+
}>, import("effect/Cause").YieldableError>;
|
|
709
|
+
declare class RemoteLiveError extends RemoteLiveError_base {}
|
|
710
|
+
declare const RemoteProtocolError_base: Schema.Class<RemoteProtocolError, Schema.TaggedStruct<"RemoteProtocolError", {
|
|
711
|
+
readonly message: Schema.String;
|
|
712
|
+
readonly expected: Schema.Number;
|
|
713
|
+
readonly received: Schema.Number;
|
|
714
|
+
}>, import("effect/Cause").YieldableError>;
|
|
715
|
+
/** The peer speaks another protocol version; nothing was read. */
|
|
716
|
+
declare class RemoteProtocolError extends RemoteProtocolError_base {}
|
|
717
|
+
/** A page size: a non-negative integer, refused at decode otherwise rather than defaulted. */
|
|
718
|
+
declare const PageSize: Schema.Number;
|
|
719
|
+
declare const WindowSchema: Schema.Struct<{
|
|
720
|
+
readonly first: Schema.optional<Schema.Number>;
|
|
721
|
+
readonly last: Schema.optional<Schema.Number>;
|
|
722
|
+
readonly after: Schema.optional<Schema.String>;
|
|
723
|
+
readonly before: Schema.optional<Schema.String>;
|
|
724
|
+
}>;
|
|
725
|
+
/** A request may not name more fields of one entity than this; a selection never does. */
|
|
726
|
+
declare const MAX_FIELDS_PER_REQUEST = 256;
|
|
727
|
+
/** A selection may not nest relations deeper than this; the wire refuses more. */
|
|
728
|
+
declare const MAX_RELATION_DEPTH = 8;
|
|
729
|
+
declare const RelationRequest: Schema.Codec<RelationRequirement, RelationRequirement, never, never>;
|
|
730
|
+
declare const ReadRequest: Schema.Codec<Requirement, Requirement>;
|
|
731
|
+
declare const ReadBatch: Schema.Struct<{
|
|
732
|
+
readonly version: Schema.Number;
|
|
733
|
+
readonly requests: Schema.$Array<Schema.Codec<Requirement, Requirement, never, never>>;
|
|
734
|
+
}>;
|
|
735
|
+
declare const NormalizedEntity: Schema.Struct<{
|
|
736
|
+
readonly entity: Schema.String;
|
|
737
|
+
readonly id: Schema.String;
|
|
738
|
+
readonly values: Schema.$Record<Schema.String, Schema.Unknown>;
|
|
739
|
+
}>;
|
|
985
740
|
/**
|
|
986
741
|
* Fields a read asked for that the server answered without, and will not
|
|
987
742
|
* answer with: the client stops asking. It says nothing about why, so a rule
|
|
@@ -1333,59 +1088,7 @@ declare const QueryRpc: Rpc.Rpc<"FoldkitRemoteQuery", Schema.Struct<{
|
|
|
1333
1088
|
readonly fields: Schema.$Array<Schema.String>;
|
|
1334
1089
|
}>>>;
|
|
1335
1090
|
}>, typeof RemoteQueryError, never, never>;
|
|
1336
|
-
declare const RemoteRpc: RpcGroup.RpcGroup<Rpc.Rpc<"
|
|
1337
|
-
readonly version: Schema.Number;
|
|
1338
|
-
readonly requests: Schema.$Array<Schema.Codec<Requirement, Requirement, never, never>>;
|
|
1339
|
-
}>, Schema.Struct<{
|
|
1340
|
-
readonly entities: Schema.$Array<Schema.Struct<{
|
|
1341
|
-
readonly entity: Schema.String;
|
|
1342
|
-
readonly id: Schema.String;
|
|
1343
|
-
readonly values: Schema.$Record<Schema.String, Schema.Unknown>;
|
|
1344
|
-
}>>;
|
|
1345
|
-
readonly settled: Schema.$Array<Schema.Struct<{
|
|
1346
|
-
readonly entity: Schema.String;
|
|
1347
|
-
readonly id: Schema.String;
|
|
1348
|
-
readonly fields: Schema.$Array<Schema.String>;
|
|
1349
|
-
}>>;
|
|
1350
|
-
}>, Schema.Union<readonly [typeof RemoteReadError, typeof RemoteProtocolError]>, never, never> | Rpc.Rpc<"FoldkitRemoteMutate", Schema.Struct<{
|
|
1351
|
-
/** Stable across transport retries so a mutation is not applied twice. */
|
|
1352
|
-
readonly requestId: Schema.String;
|
|
1353
|
-
readonly mutation: Schema.String;
|
|
1354
|
-
readonly input: Schema.Unknown;
|
|
1355
|
-
/** For a mutation that is a `Write`: the input keys to write, when not all of them. */
|
|
1356
|
-
readonly keys: Schema.optional<Schema.$Array<Schema.String>>;
|
|
1357
|
-
}>, Schema.Struct<{
|
|
1358
|
-
readonly output: Schema.Unknown;
|
|
1359
|
-
readonly entities: Schema.$Array<Schema.Struct<{
|
|
1360
|
-
readonly entity: Schema.String;
|
|
1361
|
-
readonly id: Schema.String;
|
|
1362
|
-
readonly values: Schema.$Record<Schema.String, Schema.Unknown>;
|
|
1363
|
-
}>>;
|
|
1364
|
-
/** Connection changes the mutation made, applied alongside its entity patches. */
|
|
1365
|
-
readonly connections: Schema.optional<Schema.$Array<Schema.Union<readonly [Schema.Struct<{
|
|
1366
|
-
readonly _tag: Schema.Literal<"Insert">;
|
|
1367
|
-
readonly connection: Schema.String;
|
|
1368
|
-
readonly position: Schema.Union<readonly [Schema.Literal<"prepend">, Schema.Literal<"append">]>;
|
|
1369
|
-
readonly edge: Schema.Struct<{
|
|
1370
|
-
readonly entity: Schema.String;
|
|
1371
|
-
readonly id: Schema.String;
|
|
1372
|
-
readonly key: Schema.String;
|
|
1373
|
-
}>;
|
|
1374
|
-
}>, Schema.Struct<{
|
|
1375
|
-
readonly _tag: Schema.Literal<"Remove">;
|
|
1376
|
-
readonly connection: Schema.String;
|
|
1377
|
-
readonly edge: Schema.Struct<{
|
|
1378
|
-
readonly entity: Schema.String;
|
|
1379
|
-
readonly id: Schema.String;
|
|
1380
|
-
readonly key: Schema.String;
|
|
1381
|
-
}>;
|
|
1382
|
-
}>]>>>;
|
|
1383
|
-
/** Entities the mutation deleted; the client knows them absent from then on. */
|
|
1384
|
-
readonly deleted: Schema.optional<Schema.$Array<Schema.Struct<{
|
|
1385
|
-
readonly entity: Schema.String;
|
|
1386
|
-
readonly id: Schema.String;
|
|
1387
|
-
}>>>;
|
|
1388
|
-
}>, Schema.Union<readonly [typeof RemoteMutationError, typeof RemoteForbidden]>, never, never> | Rpc.Rpc<"FoldkitRemoteLive", Schema.Struct<{
|
|
1091
|
+
declare const RemoteRpc: RpcGroup.RpcGroup<Rpc.Rpc<"FoldkitRemoteLive", Schema.Struct<{
|
|
1389
1092
|
readonly version: Schema.Number;
|
|
1390
1093
|
readonly requirements: Schema.$Array<Schema.Codec<Requirement, Requirement, never, never>>;
|
|
1391
1094
|
/** Resume cursor; events at or before it are duplicates. */
|
|
@@ -1425,1076 +1128,1284 @@ declare const RemoteRpc: RpcGroup.RpcGroup<Rpc.Rpc<"FoldkitRemoteRead", Schema.S
|
|
|
1425
1128
|
readonly _tag: Schema.Literal<"ConnectionInvalidate">;
|
|
1426
1129
|
readonly cursor: Schema.Number;
|
|
1427
1130
|
readonly connection: Schema.String;
|
|
1428
|
-
}>]>, Schema.Union<readonly [typeof RemoteLiveError, typeof RemoteProtocolError]>>, Schema.Never, never, never> | Rpc.Rpc<"
|
|
1429
|
-
|
|
1131
|
+
}>]>, Schema.Union<readonly [typeof RemoteLiveError, typeof RemoteProtocolError]>>, Schema.Never, never, never> | Rpc.Rpc<"FoldkitRemoteMutate", Schema.Struct<{
|
|
1132
|
+
/** Stable across transport retries so a mutation is not applied twice. */
|
|
1133
|
+
readonly requestId: Schema.String;
|
|
1134
|
+
readonly mutation: Schema.String;
|
|
1430
1135
|
readonly input: Schema.Unknown;
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
readonly
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
readonly
|
|
1453
|
-
readonly
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
readonly
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
readonly
|
|
1472
|
-
|
|
1473
|
-
readonly values: Schema.$Record<Schema.String, Schema.Unknown>;
|
|
1474
|
-
}>>>;
|
|
1475
|
-
readonly settled: Schema.optional<Schema.$Array<Schema.Struct<{
|
|
1476
|
-
readonly entity: Schema.String;
|
|
1477
|
-
readonly id: Schema.String;
|
|
1478
|
-
readonly fields: Schema.$Array<Schema.String>;
|
|
1479
|
-
}>>>;
|
|
1480
|
-
}>, typeof RemoteQueryError, never, never>>;
|
|
1481
|
-
//#endregion
|
|
1482
|
-
//#region src/model.d.ts
|
|
1483
|
-
/**
|
|
1484
|
-
* The normalized cache as a Foldkit Submodel. `Remote.define` returns a schema
|
|
1485
|
-
* and an `update` for this shape; `updateRemote` reconciles every producer.
|
|
1486
|
-
*/
|
|
1487
|
-
interface RemoteModel {
|
|
1488
|
-
/** Entity values, presence, staleness, tombstones, and applied windows. */
|
|
1489
|
-
readonly entities: EntityStore;
|
|
1490
|
-
/** Normalized ordered connections, keyed by connection identity. */
|
|
1491
|
-
readonly connections: Readonly<Record<string, Connection>>;
|
|
1492
|
-
/**
|
|
1493
|
-
* How many rows `Data.more` has grown a read's window to, by
|
|
1494
|
-
* `windowGrowthKey`. A read shows at most its window, so this is what "load
|
|
1495
|
-
* more" changes; it lives here because a list holds no state of its own.
|
|
1496
|
-
*/
|
|
1497
|
-
readonly grown: Readonly<Record<string, number>>;
|
|
1498
|
-
/** Optimistic entity layers and connection overlays over the base store. */
|
|
1499
|
-
readonly optimistic: OptimisticState;
|
|
1500
|
-
/** Live cursor and boundary state, keyed by the subscription's stream key. */
|
|
1501
|
-
readonly live: Readonly<Record<string, LiveState>>;
|
|
1502
|
-
/** Mutation pending/applied/failed ledger. */
|
|
1503
|
-
readonly mutations: MutationState;
|
|
1504
|
-
/**
|
|
1505
|
-
* Streams that broke, by an error or by an event ahead of their cursor, and
|
|
1506
|
-
* have not resubscribed since: changes after the break are missed until the
|
|
1507
|
-
* live entry restarts them and refetches what they cover.
|
|
1508
|
-
*/
|
|
1509
|
-
readonly gaps: ReadonlySet<string>;
|
|
1510
|
-
/** Each live stream that has ever broken, by stream key. One that never broke is absent. */
|
|
1511
|
-
readonly streams: Readonly<Record<string, StreamHealth>>;
|
|
1512
|
-
/**
|
|
1513
|
-
* The `entity\0id\0field` marks a read is currently fetching. A field absent
|
|
1514
|
-
* from the store reads as `Loading` while its mark is here and `Initial`
|
|
1515
|
-
* otherwise, which is what separates "being fetched" from "nothing is
|
|
1516
|
-
* fetching this".
|
|
1517
|
-
*/
|
|
1518
|
-
readonly loading: ReadonlySet<string>;
|
|
1519
|
-
/**
|
|
1520
|
-
* `Remote.refresh` generations, held per field rather than for the store as a
|
|
1521
|
-
* whole. A read entry restarts when the generation over the fields it plans
|
|
1522
|
-
* moves, so a read sent before a refresh of its own fields cannot land after
|
|
1523
|
-
* it, and a refresh of one field leaves every other entry's read alone.
|
|
1524
|
-
*/
|
|
1525
|
-
readonly refresh: RefreshState;
|
|
1526
|
-
/**
|
|
1527
|
-
* The reads that failed and have not been settled since, so a read can say
|
|
1528
|
-
* `Failed` rather than `Initial`.
|
|
1529
|
-
*
|
|
1530
|
-
* Without them a read that failed before anything loaded read exactly like
|
|
1531
|
-
* one nobody had asked for, and nothing asked again: a read entry restarts
|
|
1532
|
-
* only when what it plans changes, and a failure changed nothing. What failed
|
|
1533
|
-
* is not planned again on its own either, so a persistent error is not
|
|
1534
|
-
* retried every time an unrelated read restarts the entry. `Remote.refresh`
|
|
1535
|
-
* is how a view retries.
|
|
1536
|
-
*/
|
|
1537
|
-
readonly failures: Failures;
|
|
1538
|
-
/**
|
|
1539
|
-
* The storage key the cache was last restored from, through `Hydrated`'s
|
|
1540
|
-
* `from`. `Data.persistence` saves under a key only once its own snapshot is
|
|
1541
|
-
* in, so a save at startup cannot write the empty store over it.
|
|
1542
|
-
*/
|
|
1543
|
-
readonly restoredFrom: Option.Option<string>;
|
|
1544
|
-
/**
|
|
1545
|
-
* Under `verifyPlacement`, the order each list was placed in, by edge key,
|
|
1546
|
-
* and the rows placing took out of its first page, until the server's page
|
|
1547
|
-
* for it arrives to be compared with them.
|
|
1548
|
-
*/
|
|
1549
|
-
readonly placed: Readonly<Record<string, PlacedOrder>>;
|
|
1550
|
-
/** Under `verifyPlacement`, each placement the server's page disagreed with. */
|
|
1551
|
-
readonly placementMismatches: ReadonlyArray<PlacementMismatch>;
|
|
1552
|
-
}
|
|
1553
|
-
/** A list's first page as the client placed it: its keys in order, and those it took out. */
|
|
1554
|
-
interface PlacedOrder {
|
|
1555
|
-
readonly order: ReadonlyArray<string>;
|
|
1556
|
-
readonly dropped: ReadonlyArray<string>;
|
|
1557
|
-
}
|
|
1558
|
-
/** A list the client placed rows in one order and the server then listed in another. */
|
|
1559
|
-
interface PlacementMismatch {
|
|
1560
|
-
readonly connection: string;
|
|
1561
|
-
/** The keys as placed, as far as the server's page reached. */
|
|
1562
|
-
readonly placed: ReadonlyArray<string>;
|
|
1563
|
-
/** The keys of the server's first page. */
|
|
1564
|
-
readonly served: ReadonlyArray<string>;
|
|
1565
|
-
}
|
|
1566
|
-
/** How a live stream has fared, as its live entry restarts it. */
|
|
1567
|
-
interface StreamHealth {
|
|
1568
|
-
/** How many times it broke. The live entry restarts on each, so it only grows. */
|
|
1569
|
-
readonly restarts: number;
|
|
1570
|
-
/** Breaks since its last applied event: what the restart's backoff grows with. */
|
|
1571
|
-
readonly failures: number;
|
|
1572
|
-
/** Why it last broke; none for a gap, which is not an error. */
|
|
1573
|
-
readonly error: Option.Option<RemoteError>;
|
|
1574
|
-
}
|
|
1575
|
-
/**
|
|
1576
|
-
* Failed reads, as the errors they failed with.
|
|
1577
|
-
*
|
|
1578
|
-
* `connections` is by connection identity, and is settled by a page arriving,
|
|
1579
|
-
* an invalidation, or retention dropping the connection. `fields` is by field
|
|
1580
|
-
* mark (`entity\0id\0field`, the marks `loading` uses), and is settled by the
|
|
1581
|
-
* field being written again, a refresh asking for it, or retention dropping the
|
|
1582
|
-
* entity. A read of it in flight reads `Loading` over the failure without
|
|
1583
|
-
* settling it: if that read fails too, the failure is simply recorded again.
|
|
1584
|
-
*/
|
|
1585
|
-
interface Failures {
|
|
1586
|
-
readonly connections: Readonly<Record<string, RemoteError>>;
|
|
1587
|
-
readonly fields: Readonly<Record<string, RemoteError>>;
|
|
1588
|
-
}
|
|
1589
|
-
declare const noFailures: Failures;
|
|
1590
|
-
/**
|
|
1591
|
-
* `Remote.refresh` generations, per field mark. `generation` is the last one
|
|
1592
|
-
* handed out; `requested` is when each field was last refreshed, and `started`
|
|
1593
|
-
* is the generation each field's read last began under. Holding them per field
|
|
1594
|
-
* is what keeps one Projection's refresh from restarting every read entry.
|
|
1595
|
-
*/
|
|
1596
|
-
interface RefreshState {
|
|
1597
|
-
readonly generation: number;
|
|
1598
|
-
readonly requested: ReadonlyMap<string, number>;
|
|
1599
|
-
readonly started: ReadonlyMap<string, number>;
|
|
1136
|
+
/** For a mutation that is a `Write`: the input keys to write, when not all of them. */
|
|
1137
|
+
readonly keys: Schema.optional<Schema.$Array<Schema.String>>;
|
|
1138
|
+
}>, Schema.Struct<{
|
|
1139
|
+
readonly output: Schema.Unknown;
|
|
1140
|
+
readonly entities: Schema.$Array<Schema.Struct<{
|
|
1141
|
+
readonly entity: Schema.String;
|
|
1142
|
+
readonly id: Schema.String;
|
|
1143
|
+
readonly values: Schema.$Record<Schema.String, Schema.Unknown>;
|
|
1144
|
+
}>>;
|
|
1145
|
+
/** Connection changes the mutation made, applied alongside its entity patches. */
|
|
1146
|
+
readonly connections: Schema.optional<Schema.$Array<Schema.Union<readonly [Schema.Struct<{
|
|
1147
|
+
readonly _tag: Schema.Literal<"Insert">;
|
|
1148
|
+
readonly connection: Schema.String;
|
|
1149
|
+
readonly position: Schema.Union<readonly [Schema.Literal<"prepend">, Schema.Literal<"append">]>;
|
|
1150
|
+
readonly edge: Schema.Struct<{
|
|
1151
|
+
readonly entity: Schema.String;
|
|
1152
|
+
readonly id: Schema.String;
|
|
1153
|
+
readonly key: Schema.String;
|
|
1154
|
+
}>;
|
|
1155
|
+
}>, Schema.Struct<{
|
|
1156
|
+
readonly _tag: Schema.Literal<"Remove">;
|
|
1157
|
+
readonly connection: Schema.String;
|
|
1158
|
+
readonly edge: Schema.Struct<{
|
|
1159
|
+
readonly entity: Schema.String;
|
|
1160
|
+
readonly id: Schema.String;
|
|
1161
|
+
readonly key: Schema.String;
|
|
1162
|
+
}>;
|
|
1163
|
+
}>]>>>;
|
|
1164
|
+
/** Entities the mutation deleted; the client knows them absent from then on. */
|
|
1165
|
+
readonly deleted: Schema.optional<Schema.$Array<Schema.Struct<{
|
|
1166
|
+
readonly entity: Schema.String;
|
|
1167
|
+
readonly id: Schema.String;
|
|
1168
|
+
}>>>;
|
|
1169
|
+
}>, Schema.Union<readonly [typeof RemoteMutationError, typeof RemoteForbidden]>, never, never> | Rpc.Rpc<"FoldkitRemoteQuery", Schema.Struct<{
|
|
1170
|
+
readonly query: Schema.String;
|
|
1171
|
+
readonly input: Schema.Unknown;
|
|
1172
|
+
readonly window: Schema.Struct<{
|
|
1173
|
+
readonly first: Schema.optional<Schema.Number>;
|
|
1174
|
+
readonly last: Schema.optional<Schema.Number>;
|
|
1175
|
+
readonly after: Schema.optional<Schema.String>;
|
|
1176
|
+
readonly before: Schema.optional<Schema.String>;
|
|
1177
|
+
}>;
|
|
1600
1178
|
/**
|
|
1601
|
-
*
|
|
1602
|
-
*
|
|
1603
|
-
* the
|
|
1179
|
+
* What to read of each item, as the relation slice a read asks the server
|
|
1180
|
+
* for. Absent for a query run without one: the response carries only edges,
|
|
1181
|
+
* and the client's read entry fetches fields in a second round trip.
|
|
1604
1182
|
*/
|
|
1605
|
-
readonly
|
|
1606
|
-
}
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
readonly
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1183
|
+
readonly select: Schema.optional<Schema.Codec<RelationRequirement, RelationRequirement, never, never>>;
|
|
1184
|
+
}>, Schema.Struct<{
|
|
1185
|
+
readonly edges: Schema.$Array<Schema.Struct<{
|
|
1186
|
+
readonly entity: Schema.String;
|
|
1187
|
+
readonly id: Schema.String;
|
|
1188
|
+
readonly key: Schema.String;
|
|
1189
|
+
}>>;
|
|
1190
|
+
readonly start: Schema.Union<readonly [Schema.Struct<{
|
|
1191
|
+
readonly _tag: Schema.Literal<"Terminal">;
|
|
1192
|
+
}>, Schema.Struct<{
|
|
1193
|
+
readonly _tag: Schema.Literal<"Cursor">;
|
|
1194
|
+
readonly cursor: Schema.String;
|
|
1195
|
+
}>, Schema.Struct<{
|
|
1196
|
+
readonly _tag: Schema.Literal<"Unknown">;
|
|
1197
|
+
}>]>;
|
|
1198
|
+
readonly end: Schema.Union<readonly [Schema.Struct<{
|
|
1199
|
+
readonly _tag: Schema.Literal<"Terminal">;
|
|
1200
|
+
}>, Schema.Struct<{
|
|
1201
|
+
readonly _tag: Schema.Literal<"Cursor">;
|
|
1202
|
+
readonly cursor: Schema.String;
|
|
1203
|
+
}>, Schema.Struct<{
|
|
1204
|
+
readonly _tag: Schema.Literal<"Unknown">;
|
|
1205
|
+
}>]>;
|
|
1628
1206
|
/**
|
|
1629
|
-
* The
|
|
1630
|
-
*
|
|
1631
|
-
*
|
|
1632
|
-
* entry restarts it.
|
|
1207
|
+
* The selected fields of the page's items, in the same shape a read batch
|
|
1208
|
+
* returns. Absent from a server that predates payload planning: the client
|
|
1209
|
+
* reads it as empty and fetches fields as before.
|
|
1633
1210
|
*/
|
|
1634
|
-
readonly
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
readonly
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
readonly
|
|
1647
|
-
}
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
{
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1211
|
+
readonly entities: Schema.optional<Schema.$Array<Schema.Struct<{
|
|
1212
|
+
readonly entity: Schema.String;
|
|
1213
|
+
readonly id: Schema.String;
|
|
1214
|
+
readonly values: Schema.$Record<Schema.String, Schema.Unknown>;
|
|
1215
|
+
}>>>;
|
|
1216
|
+
readonly settled: Schema.optional<Schema.$Array<Schema.Struct<{
|
|
1217
|
+
readonly entity: Schema.String;
|
|
1218
|
+
readonly id: Schema.String;
|
|
1219
|
+
readonly fields: Schema.$Array<Schema.String>;
|
|
1220
|
+
}>>>;
|
|
1221
|
+
}>, typeof RemoteQueryError, never, never> | Rpc.Rpc<"FoldkitRemoteRead", Schema.Struct<{
|
|
1222
|
+
readonly version: Schema.Number;
|
|
1223
|
+
readonly requests: Schema.$Array<Schema.Codec<Requirement, Requirement, never, never>>;
|
|
1224
|
+
}>, Schema.Struct<{
|
|
1225
|
+
readonly entities: Schema.$Array<Schema.Struct<{
|
|
1226
|
+
readonly entity: Schema.String;
|
|
1227
|
+
readonly id: Schema.String;
|
|
1228
|
+
readonly values: Schema.$Record<Schema.String, Schema.Unknown>;
|
|
1229
|
+
}>>;
|
|
1230
|
+
readonly settled: Schema.$Array<Schema.Struct<{
|
|
1231
|
+
readonly entity: Schema.String;
|
|
1232
|
+
readonly id: Schema.String;
|
|
1233
|
+
readonly fields: Schema.$Array<Schema.String>;
|
|
1234
|
+
}>>;
|
|
1235
|
+
}>, Schema.Union<readonly [typeof RemoteReadError, typeof RemoteProtocolError]>, never, never>>;
|
|
1236
|
+
//#endregion
|
|
1237
|
+
//#region src/coalesce.d.ts
|
|
1238
|
+
type Batch = Schema.Schema.Type<typeof ReadBatch>;
|
|
1239
|
+
type BatchResult = Schema.Schema.Type<typeof ReadBatchResult>;
|
|
1240
|
+
type ReadError = RemoteReadError | RemoteProtocolError;
|
|
1241
|
+
type BatchRead = (batch: Batch) => Effect.Effect<BatchResult, ReadError>;
|
|
1242
|
+
type QueryRequestValue = Schema.Schema.Type<typeof QueryRequest>;
|
|
1243
|
+
type QueryResultValue = Schema.Schema.Type<typeof QueryResult>;
|
|
1244
|
+
type QueryRun = (request: QueryRequestValue) => Effect.Effect<QueryResultValue, RemoteQueryError>;
|
|
1245
|
+
interface CoalesceOptions {
|
|
1666
1246
|
/**
|
|
1667
|
-
*
|
|
1668
|
-
*
|
|
1669
|
-
* at once and the connection is refetched, because a persisted cursor
|
|
1670
|
-
* may name server state that is gone.
|
|
1247
|
+
* How long a batch waits to collect more requirements before it runs.
|
|
1248
|
+
* Requirements issued concurrently coalesce even at the default of no wait.
|
|
1671
1249
|
*/
|
|
1672
|
-
readonly
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
} |
|
|
1677
|
-
/** A mutation began; its optimistic operations show until it settles. */
|
|
1678
|
-
{
|
|
1679
|
-
readonly _tag: 'MutationStarted';
|
|
1680
|
-
readonly requestId: string;
|
|
1681
|
-
readonly optimistic?: ReadonlyArray<OptimisticOperation> | undefined;
|
|
1682
|
-
} | {
|
|
1683
|
-
readonly _tag: 'MutationSucceeded';
|
|
1684
|
-
readonly requestId: string;
|
|
1685
|
-
readonly entities: readonly NormalizedPatch[];
|
|
1686
|
-
/** Connection changes the server confirmed; they replace the request's own. */
|
|
1687
|
-
readonly connections?: ReadonlyArray<ConnectionChange> | undefined;
|
|
1688
|
-
/** Entities the mutation deleted: tombstoned, which hides them from every connection. */
|
|
1689
|
-
readonly deleted?: ReadonlyArray<{
|
|
1690
|
-
readonly entity: string;
|
|
1691
|
-
readonly id: string;
|
|
1692
|
-
}> | undefined;
|
|
1693
|
-
/** Injected clock reading of the answer: how fresh the written entities are. */
|
|
1694
|
-
readonly now: number;
|
|
1695
|
-
} | {
|
|
1696
|
-
readonly _tag: 'MutationFailed';
|
|
1697
|
-
readonly requestId: string;
|
|
1698
|
-
readonly error: RemoteError;
|
|
1699
|
-
} |
|
|
1250
|
+
readonly window?: Duration.Input | undefined;
|
|
1251
|
+
}
|
|
1252
|
+
/** A stable key for a requirement, so an equal requirement in flight is joined. */
|
|
1253
|
+
declare const requirementKey: (requirement: Requirement) => string;
|
|
1700
1254
|
/**
|
|
1701
|
-
*
|
|
1702
|
-
*
|
|
1703
|
-
*
|
|
1704
|
-
*/
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
readonly
|
|
1717
|
-
readonly
|
|
1718
|
-
|
|
1719
|
-
readonly
|
|
1720
|
-
readonly
|
|
1721
|
-
readonly event: LiveEvent;
|
|
1722
|
-
readonly policy?: LivePolicy;
|
|
1723
|
-
readonly now: number;
|
|
1724
|
-
} | {
|
|
1725
|
-
readonly _tag: 'GapCleared';
|
|
1726
|
-
readonly stream: string;
|
|
1727
|
-
} | {
|
|
1728
|
-
readonly _tag: 'ConnectionMerged';
|
|
1729
|
-
readonly connection: string;
|
|
1730
|
-
readonly page: Segment;
|
|
1731
|
-
/** The page answers the connection's refresh, so the merge also clears `stale`. */
|
|
1732
|
-
readonly refreshes?: boolean | undefined;
|
|
1733
|
-
/**
|
|
1734
|
-
* The slice the query asked for, so the payload's entities write like a
|
|
1735
|
-
* read: one request per matching edge. Absent for a page from a server
|
|
1736
|
-
* that predates payload planning, which carries edges only.
|
|
1737
|
-
*/
|
|
1738
|
-
readonly select?: RelationRequirement | undefined;
|
|
1739
|
-
/** The selected fields of the page's items, as a read batch returns them. */
|
|
1740
|
-
readonly entities?: ReadonlyArray<{
|
|
1741
|
-
readonly entity: string;
|
|
1742
|
-
readonly id: string;
|
|
1743
|
-
readonly values: Record<string, unknown>;
|
|
1744
|
-
}> | undefined;
|
|
1745
|
-
/** Fields asked for that the server settled without a value. */
|
|
1746
|
-
readonly settled?: ReadonlyArray<{
|
|
1747
|
-
readonly entity: string;
|
|
1748
|
-
readonly id: string;
|
|
1749
|
-
readonly fields: ReadonlyArray<string>;
|
|
1750
|
-
}> | undefined;
|
|
1751
|
-
/** Injected clock reading of the answer, dating what the payload writes. */
|
|
1752
|
-
readonly now?: number | undefined;
|
|
1255
|
+
* Wraps a raw query so that an identical query in flight (same query, input,
|
|
1256
|
+
* and window) is joined rather than run again; distinct queries run
|
|
1257
|
+
* concurrently. A page answers exactly one window, so nothing is batched.
|
|
1258
|
+
*/
|
|
1259
|
+
declare const coalesceQueries: (query: QueryRun, options?: CoalesceOptions) => Effect.Effect<QueryRun>;
|
|
1260
|
+
/**
|
|
1261
|
+
* Wraps a raw batch read so that requirements are batched and deduplicated.
|
|
1262
|
+
* Every waiter receives the whole batch result; `Remote.writeRead` is
|
|
1263
|
+
* idempotent, so writing it more than once is harmless.
|
|
1264
|
+
*/
|
|
1265
|
+
declare const coalesceReads: (read: BatchRead, options?: CoalesceOptions) => Effect.Effect<BatchRead>;
|
|
1266
|
+
//#endregion
|
|
1267
|
+
//#region src/live.d.ts
|
|
1268
|
+
type LiveCursor = number;
|
|
1269
|
+
type LiveEvent = {
|
|
1270
|
+
readonly _tag: 'EntityPatched';
|
|
1271
|
+
readonly ref: EdgeRef;
|
|
1272
|
+
readonly values: Readonly<Record<string, unknown>>;
|
|
1273
|
+
readonly changed: ReadonlyArray<string>;
|
|
1274
|
+
readonly cursor: LiveCursor;
|
|
1753
1275
|
} | {
|
|
1754
|
-
readonly _tag: '
|
|
1276
|
+
readonly _tag: 'EntityDeleted';
|
|
1277
|
+
readonly ref: EdgeRef;
|
|
1278
|
+
readonly cursor: LiveCursor;
|
|
1279
|
+
} | {
|
|
1280
|
+
readonly _tag: 'ConnectionInsert';
|
|
1755
1281
|
readonly connection: string;
|
|
1282
|
+
readonly position: 'prepend' | 'append';
|
|
1283
|
+
readonly edge: Edge;
|
|
1284
|
+
readonly cursor: LiveCursor;
|
|
1756
1285
|
} | {
|
|
1757
|
-
readonly _tag: '
|
|
1286
|
+
readonly _tag: 'ConnectionRemove';
|
|
1758
1287
|
readonly connection: string;
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
* arrives, it is invalidated, or retention drops it.
|
|
1764
|
-
*/
|
|
1765
|
-
{
|
|
1766
|
-
readonly _tag: 'QueryFailed';
|
|
1288
|
+
readonly edge: Edge;
|
|
1289
|
+
readonly cursor: LiveCursor;
|
|
1290
|
+
} | {
|
|
1291
|
+
readonly _tag: 'ConnectionInvalidate';
|
|
1767
1292
|
readonly connection: string;
|
|
1768
|
-
readonly
|
|
1769
|
-
} |
|
|
1770
|
-
/**
|
|
1771
|
-
* What `Data.preload` read for a destination, as the Messages that wrote it,
|
|
1772
|
-
* reduced in order into the Model as it is now rather than the one the
|
|
1773
|
-
* preload started from.
|
|
1774
|
-
*/
|
|
1775
|
-
{
|
|
1776
|
-
readonly _tag: 'Preloaded';
|
|
1777
|
-
readonly messages: ReadonlyArray<RemoteMessage>;
|
|
1293
|
+
readonly cursor: LiveCursor;
|
|
1778
1294
|
};
|
|
1779
|
-
|
|
1780
|
-
readonly
|
|
1781
|
-
readonly
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1295
|
+
interface BoundaryCounts {
|
|
1296
|
+
readonly before: number;
|
|
1297
|
+
readonly after: number;
|
|
1298
|
+
}
|
|
1299
|
+
interface LiveState {
|
|
1300
|
+
/** Last applied cursor for this stream. */
|
|
1301
|
+
readonly cursor: LiveCursor;
|
|
1302
|
+
/** Edges recorded outside the loaded boundary, not visible in `items`. */
|
|
1303
|
+
readonly boundary: Readonly<Record<string, BoundaryCounts>>;
|
|
1304
|
+
}
|
|
1305
|
+
declare const emptyLiveState: LiveState;
|
|
1306
|
+
type LiveOutcome = 'applied' | 'duplicate' | 'gap';
|
|
1786
1307
|
/**
|
|
1787
|
-
*
|
|
1788
|
-
*
|
|
1308
|
+
* Ordering per stream: at most the next cursor; ahead is a gap. Cursor `0` means
|
|
1309
|
+
* "no baseline yet" (cursors are 1-based), so the first event of a stream is
|
|
1310
|
+
* accepted whatever its cursor — otherwise a stream not starting at 1 would be a
|
|
1311
|
+
* spurious gap.
|
|
1789
1312
|
*/
|
|
1790
|
-
declare const
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
readonly requirements: Schema.$Array<Schema.Codec<Requirement, Requirement, never, never>>;
|
|
1830
|
-
readonly connections: Schema.$Array<Schema.Struct<{
|
|
1831
|
-
readonly identity: Schema.String;
|
|
1832
|
-
readonly select: Schema.optional<Schema.Codec<RelationRequirement, RelationRequirement, never, never>>;
|
|
1833
|
-
}>>;
|
|
1834
|
-
}>;
|
|
1835
|
-
};
|
|
1836
|
-
Hydrated: {
|
|
1837
|
-
connections: Schema.optional<Schema.Unknown>;
|
|
1838
|
-
from: Schema.optional<Schema.String>;
|
|
1839
|
-
entities: Schema.Unknown;
|
|
1840
|
-
merge: Schema.Union<readonly [Schema.Literal<"replace">, Schema.Literal<"preserve-existing">]>;
|
|
1841
|
-
};
|
|
1842
|
-
MutationStarted: {
|
|
1843
|
-
requestId: Schema.String;
|
|
1844
|
-
optimistic: Schema.optional<Schema.$Array<Schema.Unknown>>;
|
|
1845
|
-
};
|
|
1846
|
-
MutationSucceeded: {
|
|
1847
|
-
requestId: Schema.String;
|
|
1848
|
-
entities: Schema.$Array<Schema.Struct<{
|
|
1849
|
-
readonly entity: Schema.String;
|
|
1850
|
-
readonly id: Schema.String;
|
|
1851
|
-
readonly values: Schema.$Record<Schema.String, Schema.Unknown>;
|
|
1852
|
-
}>>;
|
|
1853
|
-
connections: Schema.optional<Schema.$Array<Schema.Unknown>>;
|
|
1854
|
-
deleted: Schema.optional<Schema.$Array<Schema.Struct<{
|
|
1855
|
-
readonly entity: Schema.String;
|
|
1856
|
-
readonly id: Schema.String;
|
|
1857
|
-
}>>>;
|
|
1858
|
-
now: Schema.Number;
|
|
1859
|
-
};
|
|
1860
|
-
MutationFailed: {
|
|
1861
|
-
requestId: Schema.String;
|
|
1862
|
-
error: Schema.Struct<{
|
|
1863
|
-
readonly _tag: Schema.String;
|
|
1864
|
-
readonly message: Schema.String;
|
|
1865
|
-
readonly refusal: Schema.optional<Schema.Unknown>;
|
|
1866
|
-
readonly reason: Schema.optional<Schema.String>;
|
|
1867
|
-
}>;
|
|
1868
|
-
};
|
|
1869
|
-
OverlayShown: {
|
|
1870
|
-
id: Schema.String;
|
|
1871
|
-
optimistic: Schema.$Array<Schema.Unknown>;
|
|
1872
|
-
};
|
|
1873
|
-
OverlayLifted: {
|
|
1874
|
-
id: Schema.String;
|
|
1875
|
-
};
|
|
1876
|
-
WindowGrown: {
|
|
1877
|
-
window: Schema.String;
|
|
1878
|
-
size: Schema.Number;
|
|
1879
|
-
};
|
|
1880
|
-
LiveReceived: {
|
|
1881
|
-
stream: Schema.String;
|
|
1882
|
-
event: Schema.Unknown;
|
|
1883
|
-
policy: Schema.optional<Schema.Unknown>;
|
|
1884
|
-
now: Schema.Number;
|
|
1885
|
-
};
|
|
1886
|
-
GapCleared: {
|
|
1887
|
-
stream: Schema.String;
|
|
1888
|
-
};
|
|
1889
|
-
ConnectionMerged: {
|
|
1890
|
-
connection: Schema.String;
|
|
1891
|
-
page: Schema.Unknown;
|
|
1892
|
-
refreshes: Schema.optional<Schema.Boolean>;
|
|
1893
|
-
select: Schema.optional<Schema.Codec<RelationRequirement, RelationRequirement, never, never>>;
|
|
1894
|
-
entities: Schema.optional<Schema.$Array<Schema.Struct<{
|
|
1895
|
-
readonly entity: Schema.String;
|
|
1896
|
-
readonly id: Schema.String;
|
|
1897
|
-
readonly values: Schema.$Record<Schema.String, Schema.Unknown>;
|
|
1898
|
-
}>>>;
|
|
1899
|
-
settled: Schema.optional<Schema.$Array<Schema.Struct<{
|
|
1900
|
-
readonly entity: Schema.String;
|
|
1901
|
-
readonly id: Schema.String;
|
|
1902
|
-
readonly fields: Schema.$Array<Schema.String>;
|
|
1903
|
-
}>>>;
|
|
1904
|
-
now: Schema.optional<Schema.Number>;
|
|
1905
|
-
};
|
|
1906
|
-
ConnectionInvalidated: {
|
|
1907
|
-
connection: Schema.String;
|
|
1908
|
-
};
|
|
1909
|
-
ConnectionRefreshed: {
|
|
1910
|
-
connection: Schema.String;
|
|
1911
|
-
};
|
|
1912
|
-
QueryFailed: {
|
|
1913
|
-
connection: Schema.String;
|
|
1914
|
-
error: Schema.Struct<{
|
|
1915
|
-
readonly _tag: Schema.String;
|
|
1916
|
-
readonly message: Schema.String;
|
|
1917
|
-
readonly refusal: Schema.optional<Schema.Unknown>;
|
|
1918
|
-
readonly reason: Schema.optional<Schema.String>;
|
|
1919
|
-
}>;
|
|
1920
|
-
};
|
|
1921
|
-
Preloaded: {
|
|
1922
|
-
messages: Schema.$Array<Schema.suspend<Schema.Codec<RemoteMessage, unknown, never, never>>>;
|
|
1923
|
-
};
|
|
1924
|
-
};
|
|
1925
|
-
type RemoteMessageTag = RemoteMessage['_tag'];
|
|
1313
|
+
declare const classifyLive: (state: LiveState, cursor: LiveCursor) => LiveOutcome;
|
|
1314
|
+
interface EntityApplied {
|
|
1315
|
+
readonly state: LiveState;
|
|
1316
|
+
readonly store: EntityStore;
|
|
1317
|
+
readonly outcome: LiveOutcome;
|
|
1318
|
+
}
|
|
1319
|
+
declare const applyEntityEvent: (state: LiveState, store: EntityStore, event: Extract<LiveEvent, {
|
|
1320
|
+
_tag: 'EntityPatched' | 'EntityDeleted';
|
|
1321
|
+
}>, now?: number) => EntityApplied;
|
|
1322
|
+
interface ConnectionApplied {
|
|
1323
|
+
readonly state: LiveState;
|
|
1324
|
+
readonly optimistic: OptimisticState;
|
|
1325
|
+
readonly outcome: LiveOutcome;
|
|
1326
|
+
/**
|
|
1327
|
+
* A connection the event says to refetch. Reported rather than recorded here,
|
|
1328
|
+
* because staleness lives on the connection in the Model — which is what the
|
|
1329
|
+
* planner and every read consult. It used to be written into a set on this
|
|
1330
|
+
* live state that nothing read, so an invalidating event did nothing at all.
|
|
1331
|
+
*/
|
|
1332
|
+
readonly invalidated?: string | undefined;
|
|
1333
|
+
}
|
|
1334
|
+
declare const applyConnectionEvent: (state: LiveState, optimistic: OptimisticState, event: Extract<LiveEvent, {
|
|
1335
|
+
_tag: 'ConnectionInsert' | 'ConnectionRemove' | 'ConnectionInvalidate';
|
|
1336
|
+
}>, policy?: LivePolicy) => ConnectionApplied;
|
|
1337
|
+
/** `hasPrevious` accounting for edges recorded outside the loaded boundary. */
|
|
1338
|
+
declare const liveHasPrevious: (connection: Connection, state: LiveState, connectionId: string) => boolean;
|
|
1339
|
+
/** `hasNext` accounting for edges recorded outside the loaded boundary. */
|
|
1340
|
+
declare const liveHasNext: (connection: Connection, state: LiveState, connectionId: string) => boolean;
|
|
1341
|
+
//#endregion
|
|
1342
|
+
//#region src/client.d.ts
|
|
1343
|
+
declare const RemoteClient_base: Context.ServiceClass<RemoteClient, "foldkit-remote/RemoteClient", {
|
|
1344
|
+
readonly read: (batch: Schema.Schema.Type<typeof ReadBatch>) => Effect.Effect<Schema.Schema.Type<typeof ReadBatchResult>, RemoteReadError | RemoteProtocolError>;
|
|
1345
|
+
readonly query: (request: Schema.Schema.Type<typeof QueryRequest>) => Effect.Effect<Schema.Schema.Type<typeof QueryResult>, RemoteQueryError>;
|
|
1346
|
+
readonly mutate: (request: Schema.Schema.Type<typeof MutationRequest>) => Effect.Effect<Schema.Schema.Type<typeof MutationResult>, RemoteMutationError | RemoteForbidden>;
|
|
1347
|
+
readonly live: (request: {
|
|
1348
|
+
readonly requirements: ReadonlyArray<Requirement>;
|
|
1349
|
+
readonly after: LiveCursor;
|
|
1350
|
+
}) => Stream.Stream<LiveEvent, RemoteLiveError | RemoteProtocolError>;
|
|
1351
|
+
}>;
|
|
1926
1352
|
/**
|
|
1927
|
-
*
|
|
1928
|
-
*
|
|
1929
|
-
* slots (`entities`, `event`, `page`) typed `unknown` by their schemas.
|
|
1353
|
+
* The transport boundary. `foldkit-remote` never talks to a transport directly;
|
|
1354
|
+
* it depends on this Effect service, which a later phase wires to Effect RPC.
|
|
1930
1355
|
*/
|
|
1931
|
-
|
|
1932
|
-
readonly _tag: Tag;
|
|
1933
|
-
} & Schema.Struct.Type<(typeof remoteMessageCases)[Tag]>; }[RemoteMessageTag];
|
|
1356
|
+
declare class RemoteClient extends RemoteClient_base {}
|
|
1934
1357
|
/**
|
|
1935
|
-
*
|
|
1936
|
-
*
|
|
1358
|
+
* The methods an Effect RPC client for `RemoteRpc` exposes. `R` is the
|
|
1359
|
+
* environment a handler needs; it defaults to `never`, which is what a real
|
|
1360
|
+
* transport client satisfies. `RemoteServer.handlers` returns this type with its
|
|
1361
|
+
* own `R`, so the two sides cannot drift apart.
|
|
1362
|
+
*
|
|
1363
|
+
* `TransportError` is what the transport itself may fail with on top of the
|
|
1364
|
+
* declared errors: `RpcClientError` for the client `RpcClient.make` builds,
|
|
1365
|
+
* `never` for in-process handlers.
|
|
1937
1366
|
*/
|
|
1938
|
-
|
|
1367
|
+
interface RemoteRpcClient<R = never, TransportError = never> {
|
|
1368
|
+
readonly FoldkitRemoteRead: (payload: Schema.Schema.Type<typeof ReadBatch>) => Effect.Effect<Schema.Schema.Type<typeof ReadBatchResult>, RemoteReadError | RemoteProtocolError | TransportError, R>;
|
|
1369
|
+
readonly FoldkitRemoteQuery: (payload: Schema.Schema.Type<typeof QueryRequest>) => Effect.Effect<Schema.Schema.Type<typeof QueryResult>, RemoteQueryError | TransportError, R>;
|
|
1370
|
+
readonly FoldkitRemoteMutate: (payload: Schema.Schema.Type<typeof MutationRequest>) => Effect.Effect<Schema.Schema.Type<typeof MutationResult>, RemoteMutationError | RemoteForbidden | TransportError, R>;
|
|
1371
|
+
readonly FoldkitRemoteLive: (payload: Schema.Schema.Type<typeof LiveRequirement>) => Stream.Stream<Schema.Schema.Type<typeof LiveChange>, RemoteLiveError | RemoteProtocolError | TransportError, R>;
|
|
1372
|
+
}
|
|
1373
|
+
/** Reconstructs the client's `ConnectionChange` from the wire's flattened edge. */
|
|
1374
|
+
declare const connectionChangeOf: (change: Schema.Schema.Type<typeof ConnectionChangeSchema>) => ConnectionChange;
|
|
1375
|
+
/** Reconstructs the client's `LiveEvent` from the wire's flattened `LiveChange`. */
|
|
1376
|
+
declare const liveEventOf: (change: Schema.Schema.Type<typeof LiveChange>) => LiveEvent;
|
|
1377
|
+
declare const remoteError: (error: {
|
|
1939
1378
|
readonly _tag: string;
|
|
1940
|
-
|
|
1941
|
-
readonly
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
declare const overlaysOf: (model: RemoteModel) => ReadonlyArray<string>;
|
|
1946
|
-
/** The key a grown window is kept under: its connection and the window first asked for. */
|
|
1947
|
-
declare const windowGrowthKey: (identity: string, window: string) => string;
|
|
1379
|
+
readonly message: string;
|
|
1380
|
+
readonly refusal?: unknown;
|
|
1381
|
+
readonly reason?: string | undefined;
|
|
1382
|
+
}) => RemoteError;
|
|
1383
|
+
declare const coalescedLayer: <E, R>(layer: Layer.Layer<RemoteClient, E, R>, options?: CoalesceOptions) => Layer.Layer<RemoteClient, E, R>;
|
|
1948
1384
|
/**
|
|
1949
|
-
*
|
|
1950
|
-
*
|
|
1951
|
-
* overlay leaves out.
|
|
1385
|
+
* `Remote.mutate` as a standalone effect, so `Remote.mutateInto` can reuse it
|
|
1386
|
+
* without the object literal referencing itself.
|
|
1952
1387
|
*/
|
|
1953
|
-
declare const
|
|
1954
|
-
|
|
1955
|
-
|
|
1388
|
+
declare const mutateRemote: <Name extends string, Input, Output>(mutation: MutationDescriptor<Name, Input, Output, unknown>, input: Input, requestId: string, keys?: readonly string[] | undefined) => Effect.Effect<{
|
|
1389
|
+
output: Output;
|
|
1390
|
+
entities: readonly {
|
|
1391
|
+
readonly entity: string;
|
|
1392
|
+
readonly id: string;
|
|
1393
|
+
readonly values: {
|
|
1394
|
+
readonly [x: string]: unknown;
|
|
1395
|
+
};
|
|
1396
|
+
}[];
|
|
1397
|
+
connections: ConnectionChange[];
|
|
1398
|
+
deleted: readonly {
|
|
1399
|
+
readonly entity: string;
|
|
1400
|
+
readonly id: string;
|
|
1401
|
+
}[];
|
|
1402
|
+
}, RemoteForbidden | RemoteMutationError, RemoteClient>;
|
|
1403
|
+
//#endregion
|
|
1404
|
+
//#region src/relation.d.ts
|
|
1405
|
+
interface RefParts {
|
|
1406
|
+
readonly entity: string;
|
|
1407
|
+
readonly id: string;
|
|
1408
|
+
}
|
|
1409
|
+
/** A page of refs as the wire and the store hold it. */
|
|
1410
|
+
interface RefPageValue {
|
|
1411
|
+
readonly refs: ReadonlyArray<string>;
|
|
1412
|
+
readonly hasNext: boolean;
|
|
1413
|
+
readonly hasPrevious: boolean;
|
|
1414
|
+
}
|
|
1415
|
+
/** Marks the codec `Entity.ref`/`Entity.refPage` produce, so a Selection can tell the field's shape. */
|
|
1416
|
+
declare const RelationAnnotation = "foldkitRemoteRelation";
|
|
1417
|
+
/** The target entity's name on the same codec, so a nested selection of another entity is refused. */
|
|
1418
|
+
declare const RelationEntityAnnotation = "foldkitRemoteRelationEntity";
|
|
1419
|
+
type RelationKind = 'one' | 'many' | 'page';
|
|
1420
|
+
interface RelationShape {
|
|
1421
|
+
readonly kind: RelationKind;
|
|
1422
|
+
readonly nullable: boolean;
|
|
1423
|
+
readonly entity: string;
|
|
1424
|
+
}
|
|
1956
1425
|
/**
|
|
1957
|
-
*
|
|
1958
|
-
*
|
|
1959
|
-
*
|
|
1960
|
-
*
|
|
1426
|
+
* A page of a relation that is a whole list is read under a name of its own, the
|
|
1427
|
+
* field's name and the page's size: `comments@first=10`. It is a field like any
|
|
1428
|
+
* other to the store, the planner, and the wire, so the whole list and a page of
|
|
1429
|
+
* it are held side by side, merged, marked stale, and persisted each on its own.
|
|
1430
|
+
* The server reads the name apart, reads the relation with the window, and
|
|
1431
|
+
* answers under the alias. A cursor is not part of the name: a page read from a
|
|
1432
|
+
* cursor merges onto the page it continues.
|
|
1961
1433
|
*/
|
|
1962
|
-
declare const
|
|
1434
|
+
declare const RELATION_ALIAS = "@";
|
|
1435
|
+
/** The alias a relation field is read under, for a window of this size. */
|
|
1436
|
+
declare const relationAlias: (field: string, window: {
|
|
1437
|
+
readonly first?: number | undefined;
|
|
1438
|
+
readonly last?: number | undefined;
|
|
1439
|
+
readonly after?: string | undefined;
|
|
1440
|
+
readonly before?: string | undefined;
|
|
1441
|
+
}) => string;
|
|
1442
|
+
/** The field an alias reads; a name that is no alias is its own field. */
|
|
1443
|
+
declare const aliasedField: (name: string) => string;
|
|
1444
|
+
/** Splits a ref key (`"Entity:id"`, the store key too) back into its parts. */
|
|
1445
|
+
declare const refParts: (encoded: string) => RefParts;
|
|
1963
1446
|
/**
|
|
1964
|
-
*
|
|
1965
|
-
*
|
|
1447
|
+
* The refs a relation value carries, in order. Anything that is not a ref key,
|
|
1448
|
+
* an array of them, or a page of them contributes nothing.
|
|
1966
1449
|
*/
|
|
1967
|
-
declare const
|
|
1450
|
+
declare const refsIn: (value: unknown) => ReadonlyArray<RefParts>;
|
|
1451
|
+
/** The refs in a relation value that name the relation's target entity, and no other. */
|
|
1452
|
+
declare const targetsOf: (value: unknown, relation: RelationRequirement) => ReadonlyArray<RefParts>;
|
|
1453
|
+
declare const isRefPage: (value: unknown) => value is RefPageValue;
|
|
1968
1454
|
/**
|
|
1969
|
-
*
|
|
1970
|
-
*
|
|
1971
|
-
*
|
|
1455
|
+
* The relation shape an entity field schema declares: a ref (`one`), a nullable
|
|
1456
|
+
* ref, an array of refs (`many`), or a page of refs (`page`). `undefined` for a
|
|
1457
|
+
* scalar field, which cannot take a nested selection.
|
|
1972
1458
|
*/
|
|
1973
|
-
declare const
|
|
1459
|
+
declare const relationShape: (schema: Schema.Top) => RelationShape | undefined;
|
|
1460
|
+
//#endregion
|
|
1461
|
+
//#region src/selection.d.ts
|
|
1462
|
+
/** One page of a paginated relation with each target assembled through a nested selection. */
|
|
1463
|
+
interface Page<Item> {
|
|
1464
|
+
readonly items: ReadonlyArray<Item>;
|
|
1465
|
+
readonly hasNext: boolean;
|
|
1466
|
+
readonly hasPrevious: boolean;
|
|
1467
|
+
}
|
|
1974
1468
|
/**
|
|
1975
|
-
*
|
|
1976
|
-
*
|
|
1977
|
-
*
|
|
1978
|
-
* field is already stale.
|
|
1469
|
+
* The fields a projection reads of one entity, plus the slice it reads of each
|
|
1470
|
+
* relation's target. `Shape` tells a parent selection whether this one is a
|
|
1471
|
+
* paginated relation (`connection`) or a plain entity slice (`entity`).
|
|
1979
1472
|
*/
|
|
1980
|
-
|
|
1473
|
+
interface Selection<Value, Name extends string = string, Shape extends 'entity' | 'connection' = 'entity' | 'connection'> {
|
|
1474
|
+
readonly entity: Name;
|
|
1475
|
+
readonly fields: readonly string[];
|
|
1476
|
+
/** A pure codec: entity fields carry no decoding or encoding services. */
|
|
1477
|
+
readonly schema: Schema.Codec<Value, unknown, never, never>;
|
|
1478
|
+
/** Pagination windows for nested relation fields, keyed by field name. */
|
|
1479
|
+
readonly connections?: Readonly<Record<string, QueryWindow>> | undefined;
|
|
1480
|
+
/** The slice required of each nested relation's target, keyed by field name. */
|
|
1481
|
+
readonly relations?: Readonly<Record<string, RelationRequirement>> | undefined;
|
|
1482
|
+
/** Present when this selection is itself a paginated relation. */
|
|
1483
|
+
readonly window?: QueryWindow | undefined;
|
|
1484
|
+
/** Phantom: see `Shape`. */
|
|
1485
|
+
readonly shape?: Shape | undefined;
|
|
1486
|
+
}
|
|
1487
|
+
/** Flattens a mapped type so hovers show the picked fields, not the machinery. */
|
|
1488
|
+
type Simplify<T> = T extends (infer O) ? { readonly [K in keyof O]: O[K]; } : never;
|
|
1489
|
+
/** The value a Selection reads: `Selection.Value<typeof ProjectCard>`. */
|
|
1490
|
+
type SelectionValueOf<S> = S extends Selection<infer V, any, any> ? V : never;
|
|
1491
|
+
/** The nested selections a relation field admits: of its target entity, in the shapes its value takes. */
|
|
1492
|
+
type NestedFor<Field> = Exclude<Field, null | undefined> extends ReadonlyArray<EntityRef<infer Name, any>> ? Selection<unknown, Name, 'entity' | 'connection'> : Exclude<Field, null | undefined> extends RefPage<infer Name, any> ? Selection<unknown, Name, 'entity' | 'connection'> : Exclude<Field, null | undefined> extends EntityRef<infer Name, any> ? Selection<unknown, Name, 'entity'> : never;
|
|
1493
|
+
/** What a selection of an entity with fields `F` may pick: `true` per scalar, a nested selection per relation. */
|
|
1494
|
+
type SelectionOf<F extends Schema.Struct.Fields> = { readonly [K in keyof F]?: true | NestedFor<Schema.Schema.Type<F[K]>>; };
|
|
1495
|
+
/** `T | null` when the field admits `null` or `undefined` (both assemble to `null`). */
|
|
1496
|
+
type Nullable<Field, T> = [Extract<Field, null | undefined>] extends [never] ? T : T | null;
|
|
1497
|
+
/** A nested selection's value takes the shape of the field it selects through. */
|
|
1498
|
+
type NestedValue<Field, Sel> = Sel extends Selection<infer V, string, 'connection'> ? Nullable<Field, V> : Sel extends Selection<infer V, string, 'entity'> ? Nullable<Field, Exclude<Field, null | undefined> extends ReadonlyArray<EntityRef<any, any>> ? ReadonlyArray<V> : Exclude<Field, null | undefined> extends RefPage<any, any> ? Page<V> : V> : never;
|
|
1499
|
+
/** The value a selection `Sel` of fields `F` reads. */
|
|
1500
|
+
type SelectionValue<F extends Schema.Struct.Fields, Sel> = Simplify<{ readonly [K in keyof Sel & keyof F]: Sel[K] extends true ? Schema.Schema.Type<F[K]> : NestedValue<Schema.Schema.Type<F[K]>, Sel[K]>; }>;
|
|
1501
|
+
declare const pageSchema: (item: AnySchema) => AnySchema;
|
|
1502
|
+
/** The requirement a nested selection contributes for its relation's target. */
|
|
1503
|
+
declare const relationOf: (selection: Selection<unknown>) => RelationRequirement;
|
|
1504
|
+
declare const Selection: {
|
|
1505
|
+
/**
|
|
1506
|
+
* The Remote Selection of a `foldkit-entity` Selection: the same members, read
|
|
1507
|
+
* from Remote's store. `true` on a relation reads its refs, as it does here.
|
|
1508
|
+
*/
|
|
1509
|
+
from: <Name extends string, S extends Schema.Constraint>(selection: Domain.Selection<Name, unknown, S>) => Selection<S['Type'], Name, 'entity'>;
|
|
1510
|
+
/**
|
|
1511
|
+
* The fields to read of `entity`. A nested `Selection` on a relation field
|
|
1512
|
+
* reads through the ref (or refs, or page of refs) the field holds into the
|
|
1513
|
+
* target's fields; a scalar field cannot take one.
|
|
1514
|
+
*/
|
|
1515
|
+
make: <Name extends string, F extends Schema.Struct.Fields, const Sel extends SelectionOf<F>>(entity: EntityDescriptor<Name, F>, selection: Sel) => Selection<SelectionValue<F, Sel>, Name, 'entity'>;
|
|
1516
|
+
/**
|
|
1517
|
+
* A paginated relation: one page of refs to `entity` with a window, or, with
|
|
1518
|
+
* a nested selection, one page of targets assembled through it.
|
|
1519
|
+
*/
|
|
1520
|
+
connection: {
|
|
1521
|
+
<Name extends string, F extends Schema.Struct.Fields>(entity: EntityDescriptor<Name, F>, window: QueryWindow): Selection<RefPage<Name, F>, Name, 'connection'>;
|
|
1522
|
+
<Name extends string, F extends Schema.Struct.Fields, Item>(entity: EntityDescriptor<Name, F>, window: QueryWindow, selection: Selection<Item, NoInfer<Name>, 'entity'>): Selection<Page<Item>, Name, 'connection'>;
|
|
1523
|
+
};
|
|
1524
|
+
};
|
|
1525
|
+
interface Assembled {
|
|
1526
|
+
readonly values: unknown;
|
|
1527
|
+
readonly refreshing: boolean;
|
|
1528
|
+
}
|
|
1981
1529
|
/**
|
|
1982
|
-
*
|
|
1983
|
-
*
|
|
1984
|
-
* `
|
|
1530
|
+
* Reads an entity's selected fields out of the store, following each nested
|
|
1531
|
+
* relation into its targets. `undefined` means some field, at any depth, is
|
|
1532
|
+
* not present yet. A tombstoned target reads as `null` (or is dropped from a
|
|
1533
|
+
* list), so the Selection's codec decides whether that is a failure.
|
|
1985
1534
|
*/
|
|
1986
|
-
declare const
|
|
1535
|
+
declare const assemble: (store: EntityStore, key: string, requirement: RelationRequirement) => Assembled | undefined;
|
|
1987
1536
|
/**
|
|
1988
|
-
* The
|
|
1989
|
-
*
|
|
1990
|
-
*
|
|
1991
|
-
* touches keeps its identity, `model` itself is returned, so the application's
|
|
1992
|
-
* root keeps its identity and Foldkit does not render. The common no-ops do (a
|
|
1993
|
-
* duplicate live event, a repeated `ReadStarted`); a repeated failure, or a
|
|
1994
|
-
* `preserve-existing` restore of entities already held, still makes an equal
|
|
1995
|
-
* copy.
|
|
1537
|
+
* The first field `requirement` reads, at any depth, that the server settled
|
|
1538
|
+
* without a value, as `Entity:id.field`. A read that consults it says why it
|
|
1539
|
+
* cannot assemble rather than waiting on a field nothing will fetch.
|
|
1996
1540
|
*/
|
|
1997
|
-
declare const
|
|
1998
|
-
|
|
1999
|
-
|
|
1541
|
+
declare const unavailableOf: (store: EntityStore, key: string, requirement: RelationRequirement) => string | undefined;
|
|
1542
|
+
//#endregion
|
|
1543
|
+
//#region src/entity.d.ts
|
|
1544
|
+
type AnySchema = Schema.Schema<unknown>;
|
|
1545
|
+
declare const entityRefFields: unique symbol;
|
|
2000
1546
|
/**
|
|
2001
|
-
*
|
|
2002
|
-
*
|
|
2003
|
-
* is being fetched is loading, not `Initial`: nothing is missing a fetch.
|
|
1547
|
+
* A normalized reference to an entity. `F` is a phantom carrying the entity's
|
|
1548
|
+
* fields so `Entity.patch` can type its patch; it is absent at runtime.
|
|
2004
1549
|
*/
|
|
2005
|
-
|
|
1550
|
+
interface EntityRef<Name extends string, F extends Schema.Struct.Fields = Schema.Struct.Fields> {
|
|
1551
|
+
readonly entity: Name;
|
|
1552
|
+
readonly id: string;
|
|
1553
|
+
readonly [entityRefFields]?: F;
|
|
1554
|
+
}
|
|
1555
|
+
interface EntityDescriptor<Name extends string, F extends Schema.Struct.Fields> {
|
|
1556
|
+
readonly name: Name;
|
|
1557
|
+
readonly schema: Schema.Struct<F>;
|
|
1558
|
+
readonly fields: F;
|
|
1559
|
+
ref(id: Schema.Schema.Type<F['id']>): EntityRef<Name, F>;
|
|
1560
|
+
/** The fields a Surface reads of this entity; `Selection.make(entity, …)` with the entity as receiver. */
|
|
1561
|
+
select<const Sel extends SelectionOf<F>>(selection: Sel): Selection<SelectionValue<F, Sel>, Name, 'entity'>;
|
|
1562
|
+
/** A patch of this entity's wire-shaped values; `Entity.patch(entity.ref(id), values)`. */
|
|
1563
|
+
patch(id: Schema.Schema.Type<F['id']>, values: Partial<Schema.Struct.Encoded<F>>): EntityPatch<Name, F>;
|
|
1564
|
+
}
|
|
2006
1565
|
/**
|
|
2007
|
-
*
|
|
2008
|
-
*
|
|
2009
|
-
*
|
|
2010
|
-
*
|
|
1566
|
+
* Relations are **references**, never inline target schemas: a ref cannot
|
|
1567
|
+
* reconstruct a full entity, and dereferencing is a store concern. Because the
|
|
1568
|
+
* target schema is not inlined, recursive relations cannot arise through the
|
|
1569
|
+
* schema graph.
|
|
2011
1570
|
*/
|
|
2012
|
-
declare const failureOf: (model: RemoteModel, entity: string, id: string, relation: RelationRequirement) => RemoteError | undefined;
|
|
2013
1571
|
/**
|
|
2014
|
-
*
|
|
2015
|
-
*
|
|
2016
|
-
*
|
|
2017
|
-
* accumulates rather than replaces. Every read path should use this rather than
|
|
2018
|
-
* reducing `writeEntity` by hand.
|
|
1572
|
+
* A ref as the wire carries it: the target's name, a colon, and its id. Typed
|
|
1573
|
+
* with the name, so a relation to `User` takes `'User:u1'` and not a ref to
|
|
1574
|
+
* some other entity, which would read as a row that does not exist.
|
|
2019
1575
|
*/
|
|
2020
|
-
|
|
2021
|
-
//#endregion
|
|
2022
|
-
//#region src/resume.d.ts
|
|
1576
|
+
type RefKey<Name extends string> = `${Name}:${string}`;
|
|
2023
1577
|
/**
|
|
2024
|
-
* A
|
|
2025
|
-
*
|
|
1578
|
+
* A patch in wire shape: the values the store holds, so a relation is its ref
|
|
1579
|
+
* key (`Entity.refKey`), not an `EntityRef`.
|
|
2026
1580
|
*/
|
|
2027
|
-
interface
|
|
1581
|
+
interface EntityPatch<Name extends string, F extends Schema.Struct.Fields> {
|
|
1582
|
+
readonly entity: Name;
|
|
2028
1583
|
readonly id: string;
|
|
2029
|
-
|
|
2030
|
-
readonly covers: ReadonlyArray<string>;
|
|
2031
|
-
readonly capture: (model: AppModel, projections: ReadonlyArray<{
|
|
2032
|
-
readonly metadata: Metadata;
|
|
2033
|
-
}>) => unknown;
|
|
2034
|
-
readonly restore: (model: AppModel, value: unknown) => Result.Result<AppModel, string>;
|
|
2035
|
-
/** Whether a Subscription entry is one of this domain's, which may start late. */
|
|
2036
|
-
readonly deferrable: (key: string, entry: unknown) => boolean;
|
|
2037
|
-
}
|
|
2038
|
-
//#endregion
|
|
2039
|
-
//#region src/coalesce.d.ts
|
|
2040
|
-
type Batch = Schema.Schema.Type<typeof ReadBatch>;
|
|
2041
|
-
type BatchResult = Schema.Schema.Type<typeof ReadBatchResult>;
|
|
2042
|
-
type ReadError = RemoteReadError | RemoteProtocolError;
|
|
2043
|
-
type BatchRead = (batch: Batch) => Effect.Effect<BatchResult, ReadError>;
|
|
2044
|
-
type QueryRequestValue = Schema.Schema.Type<typeof QueryRequest>;
|
|
2045
|
-
type QueryResultValue = Schema.Schema.Type<typeof QueryResult>;
|
|
2046
|
-
type QueryRun = (request: QueryRequestValue) => Effect.Effect<QueryResultValue, RemoteQueryError>;
|
|
2047
|
-
interface CoalesceOptions {
|
|
2048
|
-
/**
|
|
2049
|
-
* How long a batch waits to collect more requirements before it runs.
|
|
2050
|
-
* Requirements issued concurrently coalesce even at the default of no wait.
|
|
2051
|
-
*/
|
|
2052
|
-
readonly window?: Duration.Input | undefined;
|
|
1584
|
+
readonly values: Partial<Schema.Struct.Encoded<F>>;
|
|
2053
1585
|
}
|
|
2054
|
-
/** A stable key for a requirement, so an equal requirement in flight is joined. */
|
|
2055
|
-
declare const requirementKey: (requirement: Requirement) => string;
|
|
2056
1586
|
/**
|
|
2057
|
-
*
|
|
2058
|
-
*
|
|
2059
|
-
*
|
|
1587
|
+
* One authoritative page of a paginated relation: refs (never inline entities)
|
|
1588
|
+
* plus whether more exist. The adapter does not hold segmentation state; the
|
|
1589
|
+
* client merges pages.
|
|
2060
1590
|
*/
|
|
2061
|
-
|
|
1591
|
+
interface RefPage<Name extends string, F extends Schema.Struct.Fields = Schema.Struct.Fields> {
|
|
1592
|
+
readonly refs: ReadonlyArray<EntityRef<Name, F>>;
|
|
1593
|
+
readonly hasNext: boolean;
|
|
1594
|
+
readonly hasPrevious: boolean;
|
|
1595
|
+
}
|
|
1596
|
+
/** The ref codec a `foldkit-entity` relation becomes: one ref, a nullable ref, or an array of refs. */
|
|
1597
|
+
type RelationField<Spec> = Spec extends Domain.RelationSpec<infer Target, infer Cardinality, infer Optional> ? Cardinality extends 'many' ? Schema.$Array<Schema.Codec<EntityRef<Target['name']>, RefKey<Target['name']>>> : Optional extends true ? Schema.NullOr<Schema.Codec<EntityRef<Target['name']>, RefKey<Target['name']>>> : Schema.Codec<EntityRef<Target['name']>, RefKey<Target['name']>> : never;
|
|
2062
1598
|
/**
|
|
2063
|
-
*
|
|
2064
|
-
*
|
|
2065
|
-
*
|
|
1599
|
+
* The fields Remote stores for a `foldkit-entity` Entity: its own fields, its
|
|
1600
|
+
* derived members (the server supplies them like any field), and each relation
|
|
1601
|
+
* as refs.
|
|
2066
1602
|
*/
|
|
2067
|
-
|
|
1603
|
+
type FieldsFrom<E> = E extends Domain.Entity<any, infer Fields, infer Relations, infer Derived> ? Fields & { readonly [K in keyof Derived]: Derived[K]['schema']; } & { readonly [K in keyof Relations]: RelationField<Relations[K]>; } : never;
|
|
1604
|
+
declare const Entity$2: {
|
|
1605
|
+
/**
|
|
1606
|
+
* The Remote descriptor of a `foldkit-entity` Entity. Relations declared with
|
|
1607
|
+
* `Entity.relate` become ref fields, so the store, the planner, and the server
|
|
1608
|
+
* treat it as they treat an `Entity.make` descriptor.
|
|
1609
|
+
*/
|
|
1610
|
+
from: <E extends Domain.AnyEntity>(entity: E & {
|
|
1611
|
+
readonly fields: {
|
|
1612
|
+
readonly id: unknown;
|
|
1613
|
+
};
|
|
1614
|
+
}) => EntityDescriptor<E['name'], FieldsFrom<E>>;
|
|
1615
|
+
make: <const Name extends string, const F extends Schema.Struct.Fields & {
|
|
1616
|
+
readonly id: Schema.Schema<unknown>;
|
|
1617
|
+
}>(name: Name, schema: Schema.Struct<F>) => EntityDescriptor<Name, F>;
|
|
1618
|
+
/** A relation to a known entity, decoded as a reference. */
|
|
1619
|
+
ref: <Name extends string, F extends Schema.Struct.Fields>(entity: EntityDescriptor<Name, F>) => Schema.Codec<EntityRef<Name, F>, RefKey<Name>>;
|
|
1620
|
+
/** A relation by name, for recursive or forward references. */
|
|
1621
|
+
refTo: <Name extends string>(name: Name) => Schema.Codec<EntityRef<Name>, RefKey<Name>>;
|
|
1622
|
+
/**
|
|
1623
|
+
* The wire key a relation field carries (`"Entity:id"`). Adapters that read a
|
|
1624
|
+
* foreign key emit this so the ref codec can decode it on the client.
|
|
1625
|
+
*/
|
|
1626
|
+
refKey: (ref: {
|
|
1627
|
+
readonly entity: string;
|
|
1628
|
+
readonly id: string;
|
|
1629
|
+
}) => string;
|
|
1630
|
+
/** Splits a ref key back into its entity and id. */
|
|
1631
|
+
refParts: typeof refParts;
|
|
1632
|
+
/** A relation value of one authoritative page of refs. */
|
|
1633
|
+
refPage: <Name extends string, F extends Schema.Struct.Fields>(entity: EntityDescriptor<Name, F>) => Schema.Codec<RefPage<Name, F>, {
|
|
1634
|
+
readonly refs: ReadonlyArray<string>;
|
|
1635
|
+
readonly hasNext: boolean;
|
|
1636
|
+
readonly hasPrevious: boolean;
|
|
1637
|
+
}>;
|
|
1638
|
+
/**
|
|
1639
|
+
* A normalized patch for an entity, typed against its fields' wire shape:
|
|
1640
|
+
* write a relation as its ref key (`Entity.refKey`), as the server does.
|
|
1641
|
+
*/
|
|
1642
|
+
patch: <Name extends string, F extends Schema.Struct.Fields>(ref: EntityRef<Name, F>, patch: Partial<Schema.Struct.Encoded<F>>) => EntityPatch<Name, F>;
|
|
1643
|
+
};
|
|
2068
1644
|
//#endregion
|
|
2069
|
-
//#region src/
|
|
2070
|
-
declare const RemoteClient_base: Context.ServiceClass<RemoteClient, "foldkit-remote/RemoteClient", {
|
|
2071
|
-
readonly read: (batch: Schema.Schema.Type<typeof ReadBatch>) => Effect.Effect<Schema.Schema.Type<typeof ReadBatchResult>, RemoteReadError | RemoteProtocolError>;
|
|
2072
|
-
readonly query: (request: Schema.Schema.Type<typeof QueryRequest>) => Effect.Effect<Schema.Schema.Type<typeof QueryResult>, RemoteQueryError>;
|
|
2073
|
-
readonly mutate: (request: Schema.Schema.Type<typeof MutationRequest>) => Effect.Effect<Schema.Schema.Type<typeof MutationResult>, RemoteMutationError | RemoteForbidden>;
|
|
2074
|
-
readonly live: (request: {
|
|
2075
|
-
readonly requirements: ReadonlyArray<Requirement>;
|
|
2076
|
-
readonly after: LiveCursor;
|
|
2077
|
-
}) => Stream.Stream<LiveEvent, RemoteLiveError | RemoteProtocolError>;
|
|
2078
|
-
}>;
|
|
1645
|
+
//#region src/matching.d.ts
|
|
2079
1646
|
/**
|
|
2080
|
-
*
|
|
2081
|
-
*
|
|
1647
|
+
* An answer over the rows the client holds — never over the rows that exist.
|
|
1648
|
+
*
|
|
1649
|
+
* `complete` is the whole reason this is its own type rather than a `Page`. A
|
|
1650
|
+
* `Page` carries `hasNext`/`hasPrevious`, which are facts the *server* stated
|
|
1651
|
+
* about rows beyond the ones it delivered; a local answer has no such facts and
|
|
1652
|
+
* cannot invent them. What it can say is whether the population it judged was
|
|
1653
|
+
* all of it, which is decidable: a connection terminal at both ends is wholly
|
|
1654
|
+
* held, and nothing was skipped for a missing field.
|
|
1655
|
+
*
|
|
1656
|
+
* An incomplete answer is not wrong — it is an answer about less than the
|
|
1657
|
+
* caller may have meant, and the flag is what lets a view say "3 so far" rather
|
|
1658
|
+
* than "3", or a caller decide to ask the server.
|
|
2082
1659
|
*/
|
|
2083
|
-
|
|
1660
|
+
interface Matched<A> {
|
|
1661
|
+
readonly items: ReadonlyArray<A>;
|
|
1662
|
+
/** Whether every row the answer is about was judged. */
|
|
1663
|
+
readonly complete: boolean;
|
|
1664
|
+
}
|
|
2084
1665
|
/**
|
|
2085
|
-
*
|
|
2086
|
-
* environment a handler needs; it defaults to `never`, which is what a real
|
|
2087
|
-
* transport client satisfies. `RemoteServer.handlers` returns this type with its
|
|
2088
|
-
* own `R`, so the two sides cannot drift apart.
|
|
1666
|
+
* What the rows the client holds had to say about a body.
|
|
2089
1667
|
*
|
|
2090
|
-
* `
|
|
2091
|
-
*
|
|
2092
|
-
*
|
|
1668
|
+
* `skipped` is the honest half. A row missing a field the body reads cannot be
|
|
1669
|
+
* judged — it is neither a match nor a non-match — and dropping it silently
|
|
1670
|
+
* would turn "I could not tell" into "no". Naming it lets a caller decide:
|
|
1671
|
+
* fetch the field, ask the server, or say the answer is partial.
|
|
2093
1672
|
*/
|
|
2094
|
-
interface
|
|
2095
|
-
|
|
2096
|
-
readonly
|
|
2097
|
-
|
|
2098
|
-
readonly
|
|
1673
|
+
interface Judged {
|
|
1674
|
+
/** Keys of the rows that satisfy the body, in the order the body asks for. */
|
|
1675
|
+
readonly matched: ReadonlyArray<EntityKey>;
|
|
1676
|
+
/** Keys of rows held but missing a field the body reads, so not judged either way. */
|
|
1677
|
+
readonly skipped: ReadonlyArray<EntityKey>;
|
|
1678
|
+
}
|
|
1679
|
+
interface MatchingOptions {
|
|
1680
|
+
/**
|
|
1681
|
+
* Judge only these keys. Without it every row of the body's Entity in the
|
|
1682
|
+
* store is a candidate, which is what a filter over a loaded list wants; with
|
|
1683
|
+
* it, a caller narrows to a connection's own edges.
|
|
1684
|
+
*/
|
|
1685
|
+
readonly among?: ReadonlyArray<EntityKey> | undefined;
|
|
2099
1686
|
}
|
|
2100
|
-
/** Reconstructs the client's `ConnectionChange` from the wire's flattened edge. */
|
|
2101
|
-
declare const connectionChangeOf: (change: Schema.Schema.Type<typeof ConnectionChangeSchema>) => ConnectionChange;
|
|
2102
|
-
/** Reconstructs the client's `LiveEvent` from the wire's flattened `LiveChange`. */
|
|
2103
|
-
declare const liveEventOf: (change: Schema.Schema.Type<typeof LiveChange>) => LiveEvent;
|
|
2104
|
-
declare const remoteError: (error: {
|
|
2105
|
-
readonly _tag: string;
|
|
2106
|
-
readonly message: string;
|
|
2107
|
-
readonly refusal?: unknown;
|
|
2108
|
-
readonly reason?: string | undefined;
|
|
2109
|
-
}) => RemoteError;
|
|
2110
|
-
declare const coalescedLayer: <E, R>(layer: Layer.Layer<RemoteClient, E, R>, options?: CoalesceOptions) => Layer.Layer<RemoteClient, E, R>;
|
|
2111
1687
|
/**
|
|
2112
|
-
* `
|
|
2113
|
-
*
|
|
1688
|
+
* The rows of `descriptor`'s Entity that the store holds and the body matches.
|
|
1689
|
+
*
|
|
1690
|
+
* The input is given **decoded**, as an application writes it, and encoded here
|
|
1691
|
+
* through the descriptor's own `Input` codec. That is deliberate and is the
|
|
1692
|
+
* whole reason this takes a descriptor rather than a bare body: a store holds
|
|
1693
|
+
* wire values, so an input has to be in that space to be compared against them,
|
|
1694
|
+
* and handing an interpreter a decoded value is a mistake **no runtime error
|
|
1695
|
+
* catches** — the reference interpreter answers "no rows", which reads exactly
|
|
1696
|
+
* like a correct answer. Doing it here means a caller cannot get it wrong.
|
|
1697
|
+
*
|
|
1698
|
+
* Literals in the body are deliberately *not* encoded. A literal written in
|
|
1699
|
+
* domain terms is already broken against the server, which binds it to a column
|
|
1700
|
+
* the same way; accepting it here would make the client quietly more permissive
|
|
1701
|
+
* than the thing it has to agree with.
|
|
2114
1702
|
*/
|
|
2115
|
-
declare const
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
1703
|
+
declare const matching: <Name extends string, Input>(store: EntityStore, descriptor: QueryDescriptor<Name, Input, unknown>, input: Input, options?: MatchingOptions) => Judged;
|
|
1704
|
+
/**
|
|
1705
|
+
* Whether one row belongs to a query, as far as the client can tell.
|
|
1706
|
+
*
|
|
1707
|
+
* Three answers, and the third is the one that makes this usable. `'unknown'`
|
|
1708
|
+
* is a row the store never fetched, or holds without a field the body reads —
|
|
1709
|
+
* a caller that collapses it into `'no'` has turned "I could not tell" into an
|
|
1710
|
+
* answer, which is the mistake this whole module is shaped against.
|
|
1711
|
+
*
|
|
1712
|
+
* **Membership is decidable where position is not.** Whether a row satisfies
|
|
1713
|
+
* `eq`, `isNull` or `contains` needs no collation: equality on text is exact
|
|
1714
|
+
* and `contains` folds ASCII, both stated in §6.0.1. *Where* a row sorts needs
|
|
1715
|
+
* text collation, which is the backend's and deliberately outside the
|
|
1716
|
+
* conformant subset — so this answers the half that is always answerable, and
|
|
1717
|
+
* ordering stays the server's until a query can declare its collation.
|
|
1718
|
+
*/
|
|
1719
|
+
type Belongs = 'yes' | 'no' | 'unknown';
|
|
1720
|
+
/**
|
|
1721
|
+
* Takes the input **encoded**, because its one caller holds it that way: a
|
|
1722
|
+
* connection identity carries the canonical encoded input, and making it decode
|
|
1723
|
+
* only for this to encode again would be a round trip that can fail.
|
|
1724
|
+
*
|
|
1725
|
+
* A decoded-input sibling was written first and deleted: nothing called it. The
|
|
1726
|
+
* asymmetry with `matching`, which takes a decoded input, is the shape of the
|
|
1727
|
+
* two real callers rather than an oversight.
|
|
1728
|
+
*/
|
|
1729
|
+
declare const belongsEncoded: <Name extends string, Input>(store: EntityStore, descriptor: QueryDescriptor<Name, Input, unknown>, encoded: Readonly<Record<string, unknown>>, key: EntityKey) => Belongs;
|
|
2130
1730
|
//#endregion
|
|
2131
|
-
//#region src/
|
|
2132
|
-
/** A
|
|
2133
|
-
|
|
2134
|
-
readonly
|
|
2135
|
-
readonly
|
|
2136
|
-
}
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
readonly
|
|
2141
|
-
}
|
|
2142
|
-
|
|
2143
|
-
readonly
|
|
2144
|
-
|
|
2145
|
-
readonly
|
|
2146
|
-
|
|
2147
|
-
}>>;
|
|
2148
|
-
}>]>;
|
|
2149
|
-
type RemoteJsonAnswer = typeof RemoteJsonAnswer.Type;
|
|
1731
|
+
//#region src/retain.d.ts
|
|
1732
|
+
/** A connection to keep: its identity, and what a page of it selects of each item, if a projection reads it. */
|
|
1733
|
+
interface ConnectionRoot {
|
|
1734
|
+
readonly identity: string;
|
|
1735
|
+
readonly select?: RelationRequirement | undefined;
|
|
1736
|
+
}
|
|
1737
|
+
interface RetentionRoots {
|
|
1738
|
+
readonly requirements: ReadonlyArray<Requirement>;
|
|
1739
|
+
/** Connections (`QueryRef.identity`) to keep, with their edges' targets and what `select` reaches through them. */
|
|
1740
|
+
readonly connections: ReadonlyArray<ConnectionRoot>;
|
|
1741
|
+
}
|
|
1742
|
+
interface Retained {
|
|
1743
|
+
readonly entities: EntityStore;
|
|
1744
|
+
readonly connections: Readonly<Record<string, Connection>>;
|
|
1745
|
+
readonly optimistic: OptimisticState;
|
|
1746
|
+
}
|
|
2150
1747
|
/**
|
|
2151
|
-
*
|
|
2152
|
-
*
|
|
1748
|
+
* The entity keys the roots reach: each root entity, the targets its retained
|
|
1749
|
+
* fields refer to, and recursively the targets a nested relation selects.
|
|
2153
1750
|
*/
|
|
2154
|
-
|
|
2155
|
-
//#endregion
|
|
2156
|
-
//#region src/sse.d.ts
|
|
2157
|
-
/** What `httpWithLive` needs besides the URL. Mirrors `http`, plus the fetch itself. */
|
|
2158
|
-
interface LiveFetchOptions {
|
|
2159
|
-
/** Read per request, so a token that changes reaches the next call. */
|
|
2160
|
-
readonly headers?: (() => Readonly<Record<string, string>>) | undefined;
|
|
2161
|
-
/** Defaults to the global one; pass it to route calls elsewhere in tests. */
|
|
2162
|
-
readonly fetch?: typeof fetch | undefined;
|
|
2163
|
-
}
|
|
2164
|
-
/** Remote's live stream over fetch: `serveFetch`'s live open as a `Stream`. */
|
|
2165
|
-
declare const liveFetch: (url: string, options?: LiveFetchOptions) => RemoteRpcClient["FoldkitRemoteLive"];
|
|
1751
|
+
declare const reachable: (store: EntityStore, roots: RetentionRoots, connections: Readonly<Record<string, Connection>>, optimistic: OptimisticState, pending: ReadonlySet<string>) => ReadonlySet<EntityKey>;
|
|
2166
1752
|
/**
|
|
2167
|
-
*
|
|
2168
|
-
*
|
|
1753
|
+
* Drops every entity the roots do not reach, every connection they do not
|
|
1754
|
+
* name, and every settled overlay on a dropped connection, keeping whatever a
|
|
1755
|
+
* pending request's layer or overlays touch. Pure.
|
|
2169
1756
|
*/
|
|
2170
|
-
declare const
|
|
1757
|
+
declare const gc: (state: Retained & {
|
|
1758
|
+
readonly mutations: MutationState;
|
|
1759
|
+
}, roots: RetentionRoots) => Retained;
|
|
2171
1760
|
//#endregion
|
|
2172
|
-
//#region src/
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
1761
|
+
//#region src/persistence.d.ts
|
|
1762
|
+
/** Bump when the serialized shape changes; a mismatch discards the cache. */
|
|
1763
|
+
declare const REMOTE_CACHE_VERSION = 5;
|
|
1764
|
+
/**
|
|
1765
|
+
* What survives a reload: the entity cache, and the connections an application
|
|
1766
|
+
* **declared** should come back.
|
|
1767
|
+
*
|
|
1768
|
+
* Connections are session state by default and stay that way — `snapshotOf`
|
|
1769
|
+
* takes none unless asked. What a declared one keeps is its **edges and
|
|
1770
|
+
* nothing else**, which is not a simplification but the rule: a connection's
|
|
1771
|
+
* boundaries are cursors the server minted, and a cursor may name server state
|
|
1772
|
+
* that no longer exists. Restoring one would be claiming to know where the
|
|
1773
|
+
* page ends. The shape has nowhere to put a cursor, so that cannot be got
|
|
1774
|
+
* wrong later.
|
|
1775
|
+
*/
|
|
1776
|
+
interface Snapshot {
|
|
1777
|
+
readonly entities: EntityStore;
|
|
1778
|
+
/**
|
|
1779
|
+
* By connection identity: its **segments**, each a run of edges. No
|
|
1780
|
+
* boundaries.
|
|
1781
|
+
*
|
|
1782
|
+
* Segments rather than one flat list, because the gaps between them are
|
|
1783
|
+
* knowledge too. A connection paged from both ends holds a head and a tail
|
|
1784
|
+
* with an unfetched middle; flattening those into one run would claim the
|
|
1785
|
+
* tail's first row follows the head's last, which is a thing the client was
|
|
1786
|
+
* never told.
|
|
1787
|
+
*/
|
|
1788
|
+
readonly connections: Readonly<Record<string, ReadonlyArray<ReadonlyArray<Edge>>>>;
|
|
2182
1789
|
}
|
|
2183
|
-
/**
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
readonly
|
|
1790
|
+
/**
|
|
1791
|
+
* A snapshot of a Model, keeping the entity cache and only the connections
|
|
1792
|
+
* named.
|
|
1793
|
+
*
|
|
1794
|
+
* Naming none — the default — is exactly what Remote did before connections
|
|
1795
|
+
* could be kept at all: a disposable, server-derived cache.
|
|
1796
|
+
*/
|
|
1797
|
+
declare const snapshotOf: (model: {
|
|
1798
|
+
readonly entities: EntityStore;
|
|
1799
|
+
readonly connections: Readonly<Record<string, Connection>>;
|
|
1800
|
+
}, options?: {
|
|
1801
|
+
readonly connections?: ReadonlyArray<ConnectionIdentity> | undefined;
|
|
1802
|
+
}) => Snapshot;
|
|
1803
|
+
interface SnapshotOptions {
|
|
1804
|
+
/**
|
|
1805
|
+
* Who or what the snapshot is for (a user id, a tenant, a build). A snapshot
|
|
1806
|
+
* taken under another scope is discarded rather than shown to the wrong
|
|
1807
|
+
* reader. Default: unscoped, which only matches unscoped.
|
|
1808
|
+
*/
|
|
1809
|
+
readonly scope?: string | undefined;
|
|
1810
|
+
/** Snapshots larger than this many bytes are not written and not read. */
|
|
1811
|
+
readonly maxBytes?: number | undefined;
|
|
2192
1812
|
}
|
|
1813
|
+
/** How a snapshot meets a store that already has entries. */
|
|
1814
|
+
type MergePolicy = 'replace' | 'preserve-existing';
|
|
2193
1815
|
/**
|
|
2194
|
-
*
|
|
2195
|
-
*
|
|
2196
|
-
*
|
|
2197
|
-
*
|
|
2198
|
-
* The server reads the name apart, reads the relation with the window, and
|
|
2199
|
-
* answers under the alias. A cursor is not part of the name: a page read from a
|
|
2200
|
-
* cursor merges onto the page it continues.
|
|
1816
|
+
* The snapshot text, or `undefined` when it would exceed `maxBytes`. Build the
|
|
1817
|
+
* snapshot with `snapshotOf`, which is where the connections that survive are
|
|
1818
|
+
* named; runtime state — optimistic layers, live cursors, gaps, the mutation
|
|
1819
|
+
* ledger — is never in one.
|
|
2201
1820
|
*/
|
|
2202
|
-
declare
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
readonly last?: number | undefined;
|
|
2207
|
-
readonly after?: string | undefined;
|
|
2208
|
-
readonly before?: string | undefined;
|
|
2209
|
-
}) => string;
|
|
2210
|
-
/** The field an alias reads; a name that is no alias is its own field. */
|
|
2211
|
-
declare const aliasedField: (name: string) => string;
|
|
2212
|
-
/** Splits a ref key (`"Entity:id"`, the store key too) back into its parts. */
|
|
2213
|
-
declare const refParts: (encoded: string) => RefParts;
|
|
1821
|
+
declare function dehydrate(snapshot: Snapshot, options?: SnapshotOptions & {
|
|
1822
|
+
readonly maxBytes?: undefined;
|
|
1823
|
+
}): string;
|
|
1824
|
+
declare function dehydrate(snapshot: Snapshot, options?: SnapshotOptions): string | undefined;
|
|
2214
1825
|
/**
|
|
2215
|
-
* The
|
|
2216
|
-
*
|
|
1826
|
+
* The store a snapshot text holds, or `undefined` when the text is missing,
|
|
1827
|
+
* oversized, not this version, for another scope, or malformed. Hydrating the
|
|
1828
|
+
* same text twice yields equal stores.
|
|
2217
1829
|
*/
|
|
2218
|
-
declare const
|
|
2219
|
-
/** The refs in a relation value that name the relation's target entity, and no other. */
|
|
2220
|
-
declare const targetsOf: (value: unknown, relation: RelationRequirement) => ReadonlyArray<RefParts>;
|
|
2221
|
-
declare const isRefPage: (value: unknown) => value is RefPageValue;
|
|
1830
|
+
declare const hydrate: (raw: string | undefined | null, options?: SnapshotOptions) => Snapshot | undefined;
|
|
2222
1831
|
/**
|
|
2223
|
-
*
|
|
2224
|
-
*
|
|
2225
|
-
*
|
|
1832
|
+
* Brings a snapshot into a store that may already hold entries. `replace`
|
|
1833
|
+
* takes the snapshot's entry for every key it has; `preserve-existing` keeps
|
|
1834
|
+
* an entry the current store already has (it is at least as fresh as a
|
|
1835
|
+
* snapshot taken earlier). Either way, keys only the current store has stay.
|
|
2226
1836
|
*/
|
|
2227
|
-
declare const
|
|
1837
|
+
declare const mergeStores: (current: EntityStore, snapshot: EntityStore, policy: MergePolicy) => EntityStore;
|
|
1838
|
+
declare const RemotePersistence: {
|
|
1839
|
+
/** The snapshot text, or `undefined` when it would exceed `maxBytes`. */
|
|
1840
|
+
dehydrate: typeof dehydrate;
|
|
1841
|
+
/** What a snapshot text holds, or `undefined` when it is refused. */
|
|
1842
|
+
hydrate: typeof hydrate;
|
|
1843
|
+
/**
|
|
1844
|
+
* What to keep of a Model, naming the connections that should survive a
|
|
1845
|
+
* reload. Naming none is the default and is what Remote always did.
|
|
1846
|
+
*/
|
|
1847
|
+
snapshotOf: typeof snapshotOf;
|
|
1848
|
+
/** A snapshot's entities brought into a store by policy. */
|
|
1849
|
+
mergeStores: typeof mergeStores;
|
|
1850
|
+
/** The cache as a fresh session has it; what a refused `restore` yields. */
|
|
1851
|
+
emptySnapshot: Snapshot;
|
|
1852
|
+
/**
|
|
1853
|
+
* Writes the snapshot under `key`, or removes the key when the store would
|
|
1854
|
+
* exceed `maxBytes`, so a stale smaller snapshot cannot outlive a larger
|
|
1855
|
+
* store it no longer describes.
|
|
1856
|
+
*/
|
|
1857
|
+
save: (snapshot: Snapshot, options: {
|
|
1858
|
+
readonly key: string;
|
|
1859
|
+
} & SnapshotOptions) => Effect.Effect<void, KeyValueStore.KeyValueStoreError, KeyValueStore.KeyValueStore>;
|
|
1860
|
+
/**
|
|
1861
|
+
* Reads the cache. A missing key yields `emptySnapshot`; anything `hydrate`
|
|
1862
|
+
* refuses yields `emptySnapshot` and removes the key, so the next `restore`
|
|
1863
|
+
* is clean and the planner refetches.
|
|
1864
|
+
*/
|
|
1865
|
+
restore: (options: {
|
|
1866
|
+
readonly key: string;
|
|
1867
|
+
} & SnapshotOptions) => Effect.Effect<Snapshot, KeyValueStore.KeyValueStoreError, KeyValueStore.KeyValueStore>;
|
|
1868
|
+
};
|
|
2228
1869
|
//#endregion
|
|
2229
|
-
//#region src/
|
|
2230
|
-
/** One page of a paginated relation with each target assembled through a nested selection. */
|
|
2231
|
-
interface Page<Item> {
|
|
2232
|
-
readonly items: ReadonlyArray<Item>;
|
|
2233
|
-
readonly hasNext: boolean;
|
|
2234
|
-
readonly hasPrevious: boolean;
|
|
2235
|
-
}
|
|
1870
|
+
//#region src/model.d.ts
|
|
2236
1871
|
/**
|
|
2237
|
-
* The
|
|
2238
|
-
*
|
|
2239
|
-
* paginated relation (`connection`) or a plain entity slice (`entity`).
|
|
1872
|
+
* The normalized cache as a Foldkit Submodel. `Remote.define` returns a schema
|
|
1873
|
+
* and an `update` for this shape; `updateRemote` reconciles every producer.
|
|
2240
1874
|
*/
|
|
2241
|
-
interface
|
|
2242
|
-
|
|
2243
|
-
readonly
|
|
2244
|
-
/**
|
|
2245
|
-
readonly
|
|
2246
|
-
/**
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
readonly
|
|
2252
|
-
/**
|
|
2253
|
-
readonly
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
1875
|
+
interface RemoteModel {
|
|
1876
|
+
/** Entity values, presence, staleness, tombstones, and applied windows. */
|
|
1877
|
+
readonly entities: EntityStore;
|
|
1878
|
+
/** Normalized ordered connections, keyed by connection identity. */
|
|
1879
|
+
readonly connections: Readonly<Record<string, Connection>>;
|
|
1880
|
+
/**
|
|
1881
|
+
* How many rows `Data.more` has grown a read's window to, by
|
|
1882
|
+
* `windowGrowthKey`. A read shows at most its window, so this is what "load
|
|
1883
|
+
* more" changes; it lives here because a list holds no state of its own.
|
|
1884
|
+
*/
|
|
1885
|
+
readonly grown: Readonly<Record<string, number>>;
|
|
1886
|
+
/** Optimistic entity layers and connection overlays over the base store. */
|
|
1887
|
+
readonly optimistic: OptimisticState;
|
|
1888
|
+
/** Live cursor and boundary state, keyed by the subscription's stream key. */
|
|
1889
|
+
readonly live: Readonly<Record<string, LiveState>>;
|
|
1890
|
+
/** Mutation pending/applied/failed ledger. */
|
|
1891
|
+
readonly mutations: MutationState;
|
|
1892
|
+
/**
|
|
1893
|
+
* Streams that broke, by an error or by an event ahead of their cursor, and
|
|
1894
|
+
* have not resubscribed since: changes after the break are missed until the
|
|
1895
|
+
* live entry restarts them and refetches what they cover.
|
|
1896
|
+
*/
|
|
1897
|
+
readonly gaps: ReadonlySet<string>;
|
|
1898
|
+
/** Each live stream that has ever broken, by stream key. One that never broke is absent. */
|
|
1899
|
+
readonly streams: Readonly<Record<string, StreamHealth>>;
|
|
1900
|
+
/**
|
|
1901
|
+
* The `entity\0id\0field` marks a read is currently fetching. A field absent
|
|
1902
|
+
* from the store reads as `Loading` while its mark is here and `Initial`
|
|
1903
|
+
* otherwise, which is what separates "being fetched" from "nothing is
|
|
1904
|
+
* fetching this".
|
|
1905
|
+
*/
|
|
1906
|
+
readonly loading: ReadonlySet<string>;
|
|
1907
|
+
/**
|
|
1908
|
+
* `Remote.refresh` generations, held per field rather than for the store as a
|
|
1909
|
+
* whole. A read entry restarts when the generation over the fields it plans
|
|
1910
|
+
* moves, so a read sent before a refresh of its own fields cannot land after
|
|
1911
|
+
* it, and a refresh of one field leaves every other entry's read alone.
|
|
1912
|
+
*/
|
|
1913
|
+
readonly refresh: RefreshState;
|
|
1914
|
+
/**
|
|
1915
|
+
* The reads that failed and have not been settled since, so a read can say
|
|
1916
|
+
* `Failed` rather than `Initial`.
|
|
1917
|
+
*
|
|
1918
|
+
* Without them a read that failed before anything loaded read exactly like
|
|
1919
|
+
* one nobody had asked for, and nothing asked again: a read entry restarts
|
|
1920
|
+
* only when what it plans changes, and a failure changed nothing. What failed
|
|
1921
|
+
* is not planned again on its own either, so a persistent error is not
|
|
1922
|
+
* retried every time an unrelated read restarts the entry. `Remote.refresh`
|
|
1923
|
+
* is how a view retries.
|
|
1924
|
+
*/
|
|
1925
|
+
readonly failures: Failures;
|
|
2273
1926
|
/**
|
|
2274
|
-
* The
|
|
2275
|
-
* from
|
|
1927
|
+
* The storage key the cache was last restored from, through `Hydrated`'s
|
|
1928
|
+
* `from`. `Data.persistence` saves under a key only once its own snapshot is
|
|
1929
|
+
* in, so a save at startup cannot write the empty store over it.
|
|
2276
1930
|
*/
|
|
2277
|
-
|
|
1931
|
+
readonly restoredFrom: Option.Option<string>;
|
|
2278
1932
|
/**
|
|
2279
|
-
*
|
|
2280
|
-
*
|
|
2281
|
-
*
|
|
1933
|
+
* Under `verifyPlacement`, the order each list was placed in, by edge key,
|
|
1934
|
+
* and the rows placing took out of its first page, until the server's page
|
|
1935
|
+
* for it arrives to be compared with them.
|
|
2282
1936
|
*/
|
|
2283
|
-
|
|
1937
|
+
readonly placed: Readonly<Record<string, PlacedOrder>>;
|
|
1938
|
+
/** Under `verifyPlacement`, each placement the server's page disagreed with. */
|
|
1939
|
+
readonly placementMismatches: ReadonlyArray<PlacementMismatch>;
|
|
1940
|
+
}
|
|
1941
|
+
/** A list's first page as the client placed it: its keys in order, and those it took out. */
|
|
1942
|
+
interface PlacedOrder {
|
|
1943
|
+
readonly order: ReadonlyArray<string>;
|
|
1944
|
+
readonly dropped: ReadonlyArray<string>;
|
|
1945
|
+
}
|
|
1946
|
+
/** A list the client placed rows in one order and the server then listed in another. */
|
|
1947
|
+
interface PlacementMismatch {
|
|
1948
|
+
readonly connection: string;
|
|
1949
|
+
/** The keys as placed, as far as the server's page reached. */
|
|
1950
|
+
readonly placed: ReadonlyArray<string>;
|
|
1951
|
+
/** The keys of the server's first page. */
|
|
1952
|
+
readonly served: ReadonlyArray<string>;
|
|
1953
|
+
}
|
|
1954
|
+
/** How a live stream has fared, as its live entry restarts it. */
|
|
1955
|
+
interface StreamHealth {
|
|
1956
|
+
/** How many times it broke. The live entry restarts on each, so it only grows. */
|
|
1957
|
+
readonly restarts: number;
|
|
1958
|
+
/** Breaks since its last applied event: what the restart's backoff grows with. */
|
|
1959
|
+
readonly failures: number;
|
|
1960
|
+
/** Why it last broke; none for a gap, which is not an error. */
|
|
1961
|
+
readonly error: Option.Option<RemoteError>;
|
|
1962
|
+
}
|
|
1963
|
+
/**
|
|
1964
|
+
* Failed reads, as the errors they failed with.
|
|
1965
|
+
*
|
|
1966
|
+
* `connections` is by connection identity, and is settled by a page arriving,
|
|
1967
|
+
* an invalidation, or retention dropping the connection. `fields` is by field
|
|
1968
|
+
* mark (`entity\0id\0field`, the marks `loading` uses), and is settled by the
|
|
1969
|
+
* field being written again, a refresh asking for it, or retention dropping the
|
|
1970
|
+
* entity. A read of it in flight reads `Loading` over the failure without
|
|
1971
|
+
* settling it: if that read fails too, the failure is simply recorded again.
|
|
1972
|
+
*/
|
|
1973
|
+
interface Failures {
|
|
1974
|
+
readonly connections: Readonly<Record<string, RemoteError>>;
|
|
1975
|
+
readonly fields: Readonly<Record<string, RemoteError>>;
|
|
1976
|
+
}
|
|
1977
|
+
declare const noFailures: Failures;
|
|
1978
|
+
/**
|
|
1979
|
+
* `Remote.refresh` generations, per field mark. `generation` is the last one
|
|
1980
|
+
* handed out; `requested` is when each field was last refreshed, and `started`
|
|
1981
|
+
* is the generation each field's read last began under. Holding them per field
|
|
1982
|
+
* is what keeps one Projection's refresh from restarting every read entry.
|
|
1983
|
+
*/
|
|
1984
|
+
interface RefreshState {
|
|
1985
|
+
readonly generation: number;
|
|
1986
|
+
readonly requested: ReadonlyMap<string, number>;
|
|
1987
|
+
readonly started: ReadonlyMap<string, number>;
|
|
2284
1988
|
/**
|
|
2285
|
-
*
|
|
2286
|
-
*
|
|
1989
|
+
* The generation every entry is refreshed at, at least: `forgetRemote` moves
|
|
1990
|
+
* it, so every read and live entry restarts, and a read or stream begun for
|
|
1991
|
+
* the principal before is interrupted rather than landing after.
|
|
2287
1992
|
*/
|
|
2288
|
-
|
|
2289
|
-
<Name extends string, F extends Schema.Struct.Fields>(entity: EntityDescriptor<Name, F>, window: QueryWindow): Selection<RefPage<Name, F>, Name, "connection">;
|
|
2290
|
-
<Name extends string, F extends Schema.Struct.Fields, Item>(entity: EntityDescriptor<Name, F>, window: QueryWindow, selection: Selection<Item, NoInfer<Name>, "entity">): Selection<Page<Item>, Name, "connection">;
|
|
2291
|
-
};
|
|
2292
|
-
};
|
|
2293
|
-
interface Assembled {
|
|
2294
|
-
readonly values: unknown;
|
|
2295
|
-
readonly refreshing: boolean;
|
|
1993
|
+
readonly floor: number;
|
|
2296
1994
|
}
|
|
1995
|
+
declare const emptyRefresh: RefreshState;
|
|
2297
1996
|
/**
|
|
2298
|
-
*
|
|
2299
|
-
*
|
|
2300
|
-
*
|
|
2301
|
-
*
|
|
1997
|
+
* The Model with everything the server told it forgotten: values, tombstones,
|
|
1998
|
+
* unavailable fields, connections, live cursors, failures, and what is in
|
|
1999
|
+
* flight. For a change of principal, whose facts these were. The mutation
|
|
2000
|
+
* sequence is kept, so request ids stay unique, and a mutation in flight is
|
|
2001
|
+
* treated as applied: its answer, when it comes, writes nothing here.
|
|
2302
2002
|
*/
|
|
2303
|
-
declare const
|
|
2003
|
+
declare const forgetRemote: (model: RemoteModel) => RemoteModel;
|
|
2004
|
+
declare const initialRemoteModel: RemoteModel;
|
|
2005
|
+
declare const remoteModelSchema: () => Schema.Codec<RemoteModel, unknown>;
|
|
2006
|
+
/** The submodel's Messages; each reduces to `RemoteModel` through `updateRemote`. */
|
|
2007
|
+
type RemoteMessage = {
|
|
2008
|
+
readonly _tag: 'ReadReceived';
|
|
2009
|
+
readonly requests: readonly Requirement[];
|
|
2010
|
+
readonly result: Schema.Schema.Type<typeof ReadBatchResult>;
|
|
2011
|
+
readonly now: number;
|
|
2012
|
+
} | {
|
|
2013
|
+
readonly _tag: 'ReadFailed';
|
|
2014
|
+
readonly requests: readonly Requirement[];
|
|
2015
|
+
readonly error: RemoteError;
|
|
2016
|
+
/**
|
|
2017
|
+
* The live stream whose subscription broke, when this is that rather
|
|
2018
|
+
* than a read. Nothing was being read, so no field failed: the stream is
|
|
2019
|
+
* recorded as a gap, since changes after it are missed until the live
|
|
2020
|
+
* entry restarts it.
|
|
2021
|
+
*/
|
|
2022
|
+
readonly stream?: string | undefined;
|
|
2023
|
+
} |
|
|
2024
|
+
/** A refetch of present fields began; they read as `Refreshing` until it lands. */
|
|
2025
|
+
{
|
|
2026
|
+
readonly _tag: 'RefreshStarted';
|
|
2027
|
+
readonly requests: readonly Requirement[];
|
|
2028
|
+
} |
|
|
2029
|
+
/** A read began; the fields it asks for that the store lacks read as `Loading`. */
|
|
2030
|
+
{
|
|
2031
|
+
readonly _tag: 'ReadStarted';
|
|
2032
|
+
readonly requests: readonly Requirement[];
|
|
2033
|
+
/** The refresh generation the read was planned under. */
|
|
2034
|
+
readonly refresh?: number | undefined;
|
|
2035
|
+
} |
|
|
2304
2036
|
/**
|
|
2305
|
-
*
|
|
2306
|
-
*
|
|
2307
|
-
*
|
|
2037
|
+
* Queries for these connections were sent. One with nothing to show reads
|
|
2038
|
+
* `Loading` until its page or its failure arrives, which is what separates
|
|
2039
|
+
* "being fetched" from `Initial`, "nothing is fetching this".
|
|
2308
2040
|
*/
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2041
|
+
{
|
|
2042
|
+
readonly _tag: 'QueryStarted';
|
|
2043
|
+
readonly connections: readonly string[];
|
|
2044
|
+
} |
|
|
2045
|
+
/** The active Surfaces' roots changed; everything they do not reach is collected. */
|
|
2046
|
+
{
|
|
2047
|
+
readonly _tag: 'RetentionChanged';
|
|
2048
|
+
readonly roots: RetentionRoots;
|
|
2049
|
+
} |
|
|
2050
|
+
/** A restored snapshot meets the store; runtime state is untouched. */
|
|
2051
|
+
{
|
|
2052
|
+
readonly _tag: 'Hydrated';
|
|
2053
|
+
readonly entities: EntityStore;
|
|
2054
|
+
/**
|
|
2055
|
+
* The connections a snapshot declared should survive, as their edges.
|
|
2056
|
+
* Restored with **unknown** boundaries and marked stale: the rows show
|
|
2057
|
+
* at once and the connection is refetched, because a persisted cursor
|
|
2058
|
+
* may name server state that is gone.
|
|
2059
|
+
*/
|
|
2060
|
+
readonly connections?: Readonly<Record<string, ReadonlyArray<ReadonlyArray<Edge>>>> | undefined;
|
|
2061
|
+
readonly merge: MergePolicy;
|
|
2062
|
+
/** The storage key the snapshot was read from, when it was: `restoredFrom`. */
|
|
2063
|
+
readonly from?: string | undefined;
|
|
2064
|
+
} |
|
|
2065
|
+
/** A mutation began; its optimistic operations show until it settles. */
|
|
2066
|
+
{
|
|
2067
|
+
readonly _tag: 'MutationStarted';
|
|
2068
|
+
readonly requestId: string;
|
|
2069
|
+
readonly optimistic?: ReadonlyArray<OptimisticOperation> | undefined;
|
|
2070
|
+
} | {
|
|
2071
|
+
readonly _tag: 'MutationSucceeded';
|
|
2072
|
+
readonly requestId: string;
|
|
2073
|
+
readonly entities: readonly NormalizedPatch[];
|
|
2074
|
+
/** Connection changes the server confirmed; they replace the request's own. */
|
|
2075
|
+
readonly connections?: ReadonlyArray<ConnectionChange> | undefined;
|
|
2076
|
+
/** Entities the mutation deleted: tombstoned, which hides them from every connection. */
|
|
2077
|
+
readonly deleted?: ReadonlyArray<{
|
|
2078
|
+
readonly entity: string;
|
|
2079
|
+
readonly id: string;
|
|
2080
|
+
}> | undefined;
|
|
2081
|
+
/** Injected clock reading of the answer: how fresh the written entities are. */
|
|
2082
|
+
readonly now: number;
|
|
2083
|
+
} | {
|
|
2084
|
+
readonly _tag: 'MutationFailed';
|
|
2085
|
+
readonly requestId: string;
|
|
2086
|
+
readonly error: RemoteError;
|
|
2087
|
+
} |
|
|
2314
2088
|
/**
|
|
2315
|
-
*
|
|
2316
|
-
*
|
|
2089
|
+
* Operations shown over the store with no request behind them, until they are
|
|
2090
|
+
* lifted: a preview of a change nobody has made. Showing an id again replaces
|
|
2091
|
+
* what it showed.
|
|
2317
2092
|
*/
|
|
2318
|
-
|
|
2319
|
-
readonly
|
|
2093
|
+
{
|
|
2094
|
+
readonly _tag: 'OverlayShown';
|
|
2320
2095
|
readonly id: string;
|
|
2321
|
-
readonly
|
|
2322
|
-
}
|
|
2323
|
-
|
|
2324
|
-
readonly
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2096
|
+
readonly optimistic: ReadonlyArray<OptimisticOperation>;
|
|
2097
|
+
} | {
|
|
2098
|
+
readonly _tag: 'OverlayLifted';
|
|
2099
|
+
readonly id: string;
|
|
2100
|
+
} |
|
|
2101
|
+
/** `Data.more`: a read's window, by `windowGrowthKey`, now shows `size` rows. */
|
|
2102
|
+
{
|
|
2103
|
+
readonly _tag: 'WindowGrown';
|
|
2104
|
+
readonly window: string;
|
|
2105
|
+
readonly size: number;
|
|
2106
|
+
} | {
|
|
2107
|
+
readonly _tag: 'LiveReceived';
|
|
2108
|
+
readonly stream: string;
|
|
2109
|
+
readonly event: LiveEvent;
|
|
2110
|
+
readonly policy?: LivePolicy;
|
|
2111
|
+
readonly now: number;
|
|
2112
|
+
} | {
|
|
2113
|
+
readonly _tag: 'GapCleared';
|
|
2114
|
+
readonly stream: string;
|
|
2115
|
+
} | {
|
|
2116
|
+
readonly _tag: 'ConnectionMerged';
|
|
2117
|
+
readonly connection: string;
|
|
2118
|
+
readonly page: Segment;
|
|
2119
|
+
/** The page answers the connection's refresh, so the merge also clears `stale`. */
|
|
2120
|
+
readonly refreshes?: boolean | undefined;
|
|
2121
|
+
/**
|
|
2122
|
+
* The slice the query asked for, so the payload's entities write like a
|
|
2123
|
+
* read: one request per matching edge. Absent for a page from a server
|
|
2124
|
+
* that predates payload planning, which carries edges only.
|
|
2125
|
+
*/
|
|
2126
|
+
readonly select?: RelationRequirement | undefined;
|
|
2127
|
+
/** The selected fields of the page's items, as a read batch returns them. */
|
|
2128
|
+
readonly entities?: ReadonlyArray<{
|
|
2129
|
+
readonly entity: string;
|
|
2130
|
+
readonly id: string;
|
|
2131
|
+
readonly values: Record<string, unknown>;
|
|
2132
|
+
}> | undefined;
|
|
2133
|
+
/** Fields asked for that the server settled without a value. */
|
|
2134
|
+
readonly settled?: ReadonlyArray<{
|
|
2135
|
+
readonly entity: string;
|
|
2136
|
+
readonly id: string;
|
|
2137
|
+
readonly fields: ReadonlyArray<string>;
|
|
2138
|
+
}> | undefined;
|
|
2139
|
+
/** Injected clock reading of the answer, dating what the payload writes. */
|
|
2140
|
+
readonly now?: number | undefined;
|
|
2141
|
+
} | {
|
|
2142
|
+
readonly _tag: 'ConnectionInvalidated';
|
|
2143
|
+
readonly connection: string;
|
|
2144
|
+
} | {
|
|
2145
|
+
readonly _tag: 'ConnectionRefreshed';
|
|
2146
|
+
readonly connection: string;
|
|
2147
|
+
} |
|
|
2148
|
+
/**
|
|
2149
|
+
* A query for the connection failed. Its pages stay as they were, and it
|
|
2150
|
+
* reads `Failed` — with them as `previous`, when it has any — until a page
|
|
2151
|
+
* arrives, it is invalidated, or retention drops it.
|
|
2152
|
+
*/
|
|
2153
|
+
{
|
|
2154
|
+
readonly _tag: 'QueryFailed';
|
|
2155
|
+
readonly connection: string;
|
|
2156
|
+
readonly error: RemoteError;
|
|
2157
|
+
} |
|
|
2333
2158
|
/**
|
|
2334
|
-
*
|
|
2335
|
-
*
|
|
2336
|
-
*
|
|
2337
|
-
* schema graph.
|
|
2159
|
+
* What `Data.preload` read for a destination, as the Messages that wrote it,
|
|
2160
|
+
* reduced in order into the Model as it is now rather than the one the
|
|
2161
|
+
* preload started from.
|
|
2338
2162
|
*/
|
|
2163
|
+
{
|
|
2164
|
+
readonly _tag: 'Preloaded';
|
|
2165
|
+
readonly messages: ReadonlyArray<RemoteMessage>;
|
|
2166
|
+
};
|
|
2167
|
+
declare const retentionRootsSchema: Schema.Struct<{
|
|
2168
|
+
readonly requirements: Schema.$Array<Schema.Codec<Requirement, Requirement, never, never>>;
|
|
2169
|
+
readonly connections: Schema.$Array<Schema.Struct<{
|
|
2170
|
+
readonly identity: Schema.String;
|
|
2171
|
+
readonly select: Schema.optional<Schema.Codec<RelationRequirement, RelationRequirement, never, never>>;
|
|
2172
|
+
}>>;
|
|
2173
|
+
}>;
|
|
2339
2174
|
/**
|
|
2340
|
-
*
|
|
2341
|
-
*
|
|
2342
|
-
* some other entity, which would read as a row that does not exist.
|
|
2175
|
+
* The Messages' fields by tag, without `_tag`: what `defineMessageUnion` takes,
|
|
2176
|
+
* so an application spreads them into its own union (`Remote.messages`).
|
|
2343
2177
|
*/
|
|
2344
|
-
|
|
2178
|
+
declare const remoteMessageCases: {
|
|
2179
|
+
ReadReceived: {
|
|
2180
|
+
requests: Schema.$Array<Schema.Codec<Requirement, Requirement, never, never>>;
|
|
2181
|
+
result: Schema.Struct<{
|
|
2182
|
+
readonly entities: Schema.$Array<Schema.Struct<{
|
|
2183
|
+
readonly entity: Schema.String;
|
|
2184
|
+
readonly id: Schema.String;
|
|
2185
|
+
readonly values: Schema.$Record<Schema.String, Schema.Unknown>;
|
|
2186
|
+
}>>;
|
|
2187
|
+
readonly settled: Schema.$Array<Schema.Struct<{
|
|
2188
|
+
readonly entity: Schema.String;
|
|
2189
|
+
readonly id: Schema.String;
|
|
2190
|
+
readonly fields: Schema.$Array<Schema.String>;
|
|
2191
|
+
}>>;
|
|
2192
|
+
}>;
|
|
2193
|
+
now: Schema.Number;
|
|
2194
|
+
};
|
|
2195
|
+
ReadFailed: {
|
|
2196
|
+
requests: Schema.$Array<Schema.Codec<Requirement, Requirement, never, never>>;
|
|
2197
|
+
error: Schema.Struct<{
|
|
2198
|
+
readonly _tag: Schema.String;
|
|
2199
|
+
readonly message: Schema.String;
|
|
2200
|
+
readonly refusal: Schema.optional<Schema.Unknown>;
|
|
2201
|
+
readonly reason: Schema.optional<Schema.String>;
|
|
2202
|
+
}>;
|
|
2203
|
+
stream: Schema.optional<Schema.String>;
|
|
2204
|
+
};
|
|
2205
|
+
RefreshStarted: {
|
|
2206
|
+
requests: Schema.$Array<Schema.Codec<Requirement, Requirement, never, never>>;
|
|
2207
|
+
};
|
|
2208
|
+
ReadStarted: {
|
|
2209
|
+
requests: Schema.$Array<Schema.Codec<Requirement, Requirement, never, never>>;
|
|
2210
|
+
refresh: Schema.optional<Schema.Number>;
|
|
2211
|
+
};
|
|
2212
|
+
QueryStarted: {
|
|
2213
|
+
connections: Schema.$Array<Schema.String>;
|
|
2214
|
+
};
|
|
2215
|
+
RetentionChanged: {
|
|
2216
|
+
roots: Schema.Struct<{
|
|
2217
|
+
readonly requirements: Schema.$Array<Schema.Codec<Requirement, Requirement, never, never>>;
|
|
2218
|
+
readonly connections: Schema.$Array<Schema.Struct<{
|
|
2219
|
+
readonly identity: Schema.String;
|
|
2220
|
+
readonly select: Schema.optional<Schema.Codec<RelationRequirement, RelationRequirement, never, never>>;
|
|
2221
|
+
}>>;
|
|
2222
|
+
}>;
|
|
2223
|
+
};
|
|
2224
|
+
Hydrated: {
|
|
2225
|
+
connections: Schema.optional<Schema.Unknown>;
|
|
2226
|
+
from: Schema.optional<Schema.String>;
|
|
2227
|
+
entities: Schema.Unknown;
|
|
2228
|
+
merge: Schema.Union<readonly [Schema.Literal<"replace">, Schema.Literal<"preserve-existing">]>;
|
|
2229
|
+
};
|
|
2230
|
+
MutationStarted: {
|
|
2231
|
+
requestId: Schema.String;
|
|
2232
|
+
optimistic: Schema.optional<Schema.$Array<Schema.Unknown>>;
|
|
2233
|
+
};
|
|
2234
|
+
MutationSucceeded: {
|
|
2235
|
+
requestId: Schema.String;
|
|
2236
|
+
entities: Schema.$Array<Schema.Struct<{
|
|
2237
|
+
readonly entity: Schema.String;
|
|
2238
|
+
readonly id: Schema.String;
|
|
2239
|
+
readonly values: Schema.$Record<Schema.String, Schema.Unknown>;
|
|
2240
|
+
}>>;
|
|
2241
|
+
connections: Schema.optional<Schema.$Array<Schema.Unknown>>;
|
|
2242
|
+
deleted: Schema.optional<Schema.$Array<Schema.Struct<{
|
|
2243
|
+
readonly entity: Schema.String;
|
|
2244
|
+
readonly id: Schema.String;
|
|
2245
|
+
}>>>;
|
|
2246
|
+
now: Schema.Number;
|
|
2247
|
+
};
|
|
2248
|
+
MutationFailed: {
|
|
2249
|
+
requestId: Schema.String;
|
|
2250
|
+
error: Schema.Struct<{
|
|
2251
|
+
readonly _tag: Schema.String;
|
|
2252
|
+
readonly message: Schema.String;
|
|
2253
|
+
readonly refusal: Schema.optional<Schema.Unknown>;
|
|
2254
|
+
readonly reason: Schema.optional<Schema.String>;
|
|
2255
|
+
}>;
|
|
2256
|
+
};
|
|
2257
|
+
OverlayShown: {
|
|
2258
|
+
id: Schema.String;
|
|
2259
|
+
optimistic: Schema.$Array<Schema.Unknown>;
|
|
2260
|
+
};
|
|
2261
|
+
OverlayLifted: {
|
|
2262
|
+
id: Schema.String;
|
|
2263
|
+
};
|
|
2264
|
+
WindowGrown: {
|
|
2265
|
+
window: Schema.String;
|
|
2266
|
+
size: Schema.Number;
|
|
2267
|
+
};
|
|
2268
|
+
LiveReceived: {
|
|
2269
|
+
stream: Schema.String;
|
|
2270
|
+
event: Schema.Unknown;
|
|
2271
|
+
policy: Schema.optional<Schema.Unknown>;
|
|
2272
|
+
now: Schema.Number;
|
|
2273
|
+
};
|
|
2274
|
+
GapCleared: {
|
|
2275
|
+
stream: Schema.String;
|
|
2276
|
+
};
|
|
2277
|
+
ConnectionMerged: {
|
|
2278
|
+
connection: Schema.String;
|
|
2279
|
+
page: Schema.Unknown;
|
|
2280
|
+
refreshes: Schema.optional<Schema.Boolean>;
|
|
2281
|
+
select: Schema.optional<Schema.Codec<RelationRequirement, RelationRequirement, never, never>>;
|
|
2282
|
+
entities: Schema.optional<Schema.$Array<Schema.Struct<{
|
|
2283
|
+
readonly entity: Schema.String;
|
|
2284
|
+
readonly id: Schema.String;
|
|
2285
|
+
readonly values: Schema.$Record<Schema.String, Schema.Unknown>;
|
|
2286
|
+
}>>>;
|
|
2287
|
+
settled: Schema.optional<Schema.$Array<Schema.Struct<{
|
|
2288
|
+
readonly entity: Schema.String;
|
|
2289
|
+
readonly id: Schema.String;
|
|
2290
|
+
readonly fields: Schema.$Array<Schema.String>;
|
|
2291
|
+
}>>>;
|
|
2292
|
+
now: Schema.optional<Schema.Number>;
|
|
2293
|
+
};
|
|
2294
|
+
ConnectionInvalidated: {
|
|
2295
|
+
connection: Schema.String;
|
|
2296
|
+
};
|
|
2297
|
+
ConnectionRefreshed: {
|
|
2298
|
+
connection: Schema.String;
|
|
2299
|
+
};
|
|
2300
|
+
QueryFailed: {
|
|
2301
|
+
connection: Schema.String;
|
|
2302
|
+
error: Schema.Struct<{
|
|
2303
|
+
readonly _tag: Schema.String;
|
|
2304
|
+
readonly message: Schema.String;
|
|
2305
|
+
readonly refusal: Schema.optional<Schema.Unknown>;
|
|
2306
|
+
readonly reason: Schema.optional<Schema.String>;
|
|
2307
|
+
}>;
|
|
2308
|
+
};
|
|
2309
|
+
Preloaded: {
|
|
2310
|
+
messages: Schema.$Array<Schema.suspend<Schema.Codec<RemoteMessage, unknown, never, never>>>;
|
|
2311
|
+
};
|
|
2312
|
+
};
|
|
2313
|
+
type RemoteMessageTag = RemoteMessage['_tag'];
|
|
2345
2314
|
/**
|
|
2346
|
-
* A
|
|
2347
|
-
*
|
|
2315
|
+
* A Remote Message as an application's union constructs it from
|
|
2316
|
+
* `remoteMessageCases`: the same shape `RemoteMessage` names, with the runtime
|
|
2317
|
+
* slots (`entities`, `event`, `page`) typed `unknown` by their schemas.
|
|
2348
2318
|
*/
|
|
2349
|
-
|
|
2350
|
-
readonly
|
|
2351
|
-
|
|
2352
|
-
readonly values: Partial<Schema.Struct.Encoded<F>>;
|
|
2353
|
-
}
|
|
2319
|
+
type RemoteMessageInput = { [Tag in RemoteMessageTag]: {
|
|
2320
|
+
readonly _tag: Tag;
|
|
2321
|
+
} & Schema.Struct.Type<(typeof remoteMessageCases)[Tag]>; }[RemoteMessageTag];
|
|
2354
2322
|
/**
|
|
2355
|
-
*
|
|
2356
|
-
*
|
|
2357
|
-
* client merges pages.
|
|
2323
|
+
* Whether a Message is one of Remote's, by tag. Narrows an application's union
|
|
2324
|
+
* to its Remote cases, and its complement to the application's own.
|
|
2358
2325
|
*/
|
|
2359
|
-
|
|
2360
|
-
readonly
|
|
2361
|
-
|
|
2362
|
-
readonly
|
|
2363
|
-
}
|
|
2364
|
-
|
|
2365
|
-
|
|
2326
|
+
declare const isRemoteMessage: <M extends {
|
|
2327
|
+
readonly _tag: string;
|
|
2328
|
+
}>(message: M) => message is Extract<M, {
|
|
2329
|
+
readonly _tag: RemoteMessageTag;
|
|
2330
|
+
}>;
|
|
2331
|
+
declare const remoteMessageSchema: Schema.Codec<RemoteMessage, unknown>;
|
|
2332
|
+
/** The ids `OverlayShown` shows now, each once, in the order they are drawn. */
|
|
2333
|
+
declare const overlaysOf: (model: RemoteModel) => ReadonlyArray<string>;
|
|
2334
|
+
/** The key a grown window is kept under: its connection and the window first asked for. */
|
|
2335
|
+
declare const windowGrowthKey: (identity: string, window: string) => string;
|
|
2366
2336
|
/**
|
|
2367
|
-
*
|
|
2368
|
-
*
|
|
2369
|
-
*
|
|
2337
|
+
* Whether only overlays show this entity: the server has told the store
|
|
2338
|
+
* nothing of it and no request's layer writes it, so nothing will fetch what an
|
|
2339
|
+
* overlay leaves out.
|
|
2370
2340
|
*/
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
};
|
|
2382
|
-
}) => EntityDescriptor<E["name"], FieldsFrom<E>>;
|
|
2383
|
-
make: <const Name extends string, const F extends Schema.Struct.Fields & {
|
|
2384
|
-
readonly id: Schema.Schema<unknown>;
|
|
2385
|
-
}>(name: Name, schema: Schema.Struct<F>) => EntityDescriptor<Name, F>;
|
|
2386
|
-
/** A relation to a known entity, decoded as a reference. */
|
|
2387
|
-
ref: <Name extends string, F extends Schema.Struct.Fields>(entity: EntityDescriptor<Name, F>) => Schema.Codec<EntityRef<Name, F>, RefKey<Name>>;
|
|
2388
|
-
/** A relation by name, for recursive or forward references. */
|
|
2389
|
-
refTo: <Name extends string>(name: Name) => Schema.Codec<EntityRef<Name>, RefKey<Name>>;
|
|
2390
|
-
/**
|
|
2391
|
-
* The wire key a relation field carries (`"Entity:id"`). Adapters that read a
|
|
2392
|
-
* foreign key emit this so the ref codec can decode it on the client.
|
|
2393
|
-
*/
|
|
2394
|
-
refKey: (ref: {
|
|
2395
|
-
readonly entity: string;
|
|
2396
|
-
readonly id: string;
|
|
2397
|
-
}) => string;
|
|
2398
|
-
/** Splits a ref key back into its entity and id. */
|
|
2399
|
-
refParts: (encoded: string) => RefParts;
|
|
2400
|
-
/** A relation value of one authoritative page of refs. */
|
|
2401
|
-
refPage: <Name extends string, F extends Schema.Struct.Fields>(entity: EntityDescriptor<Name, F>) => Schema.Codec<RefPage<Name, F>, {
|
|
2402
|
-
readonly refs: ReadonlyArray<string>;
|
|
2403
|
-
readonly hasNext: boolean;
|
|
2404
|
-
readonly hasPrevious: boolean;
|
|
2405
|
-
}>;
|
|
2406
|
-
/**
|
|
2407
|
-
* A normalized patch for an entity, typed against its fields' wire shape:
|
|
2408
|
-
* write a relation as its ref key (`Entity.refKey`), as the server does.
|
|
2409
|
-
*/
|
|
2410
|
-
patch: <Name extends string, F extends Schema.Struct.Fields>(ref: EntityRef<Name, F>, patch: Partial<Schema.Struct.Encoded<F>>) => EntityPatch<Name, F>;
|
|
2411
|
-
};
|
|
2412
|
-
//#endregion
|
|
2413
|
-
//#region src/matching.d.ts
|
|
2341
|
+
declare const onlyOverlaid: (model: RemoteModel, entity: string, id: string) => boolean;
|
|
2342
|
+
/** Whether a query for this connection is in flight. */
|
|
2343
|
+
declare const isQueryLoading: (model: RemoteModel, identity: string) => boolean;
|
|
2344
|
+
/**
|
|
2345
|
+
* The generation a read is planned under: the highest any of the fields it
|
|
2346
|
+
* reads or connections it runs was refreshed at. A read entry carries this as a
|
|
2347
|
+
* dependency, so it restarts when what it observes is refreshed and not when
|
|
2348
|
+
* anything else is.
|
|
2349
|
+
*/
|
|
2350
|
+
declare const refreshedAt: (refresh: RefreshState, requests: ReadonlyArray<Requirement>, connections?: ReadonlyArray<string>) => number;
|
|
2414
2351
|
/**
|
|
2415
|
-
*
|
|
2416
|
-
*
|
|
2417
|
-
* `complete` is the whole reason this is its own type rather than a `Page`. A
|
|
2418
|
-
* `Page` carries `hasNext`/`hasPrevious`, which are facts the *server* stated
|
|
2419
|
-
* about rows beyond the ones it delivered; a local answer has no such facts and
|
|
2420
|
-
* cannot invent them. What it can say is whether the population it judged was
|
|
2421
|
-
* all of it, which is decidable: a connection terminal at both ends is wholly
|
|
2422
|
-
* held, and nothing was skipped for a missing field.
|
|
2423
|
-
*
|
|
2424
|
-
* An incomplete answer is not wrong — it is an answer about less than the
|
|
2425
|
-
* caller may have meant, and the flag is what lets a view say "3 so far" rather
|
|
2426
|
-
* than "3", or a caller decide to ask the server.
|
|
2352
|
+
* Marks every field of `requests` and every connection in `connections`
|
|
2353
|
+
* refreshed at the next generation.
|
|
2427
2354
|
*/
|
|
2428
|
-
|
|
2429
|
-
readonly items: ReadonlyArray<A>;
|
|
2430
|
-
/** Whether every row the answer is about was judged. */
|
|
2431
|
-
readonly complete: boolean;
|
|
2432
|
-
}
|
|
2355
|
+
declare const withRefreshRequested: (refresh: RefreshState, requests: ReadonlyArray<Requirement>, connections?: ReadonlyArray<string>) => RefreshState;
|
|
2433
2356
|
/**
|
|
2434
|
-
*
|
|
2435
|
-
*
|
|
2436
|
-
*
|
|
2437
|
-
* judged — it is neither a match nor a non-match — and dropping it silently
|
|
2438
|
-
* would turn "I could not tell" into "no". Naming it lets a caller decide:
|
|
2439
|
-
* fetch the field, ask the server, or say the answer is partial.
|
|
2357
|
+
* Marks connections as needing to be asked again, and restarts a read of them
|
|
2358
|
+
* already in flight: a page requested before what invalidated it could land
|
|
2359
|
+
* after it, saying what is no longer true. `Remote.refresh` marks the same way.
|
|
2440
2360
|
*/
|
|
2441
|
-
|
|
2442
|
-
/** Keys of the rows that satisfy the body, in the order the body asks for. */
|
|
2443
|
-
readonly matched: ReadonlyArray<EntityKey>;
|
|
2444
|
-
/** Keys of rows held but missing a field the body reads, so not judged either way. */
|
|
2445
|
-
readonly skipped: ReadonlyArray<EntityKey>;
|
|
2446
|
-
}
|
|
2447
|
-
interface MatchingOptions {
|
|
2448
|
-
/**
|
|
2449
|
-
* Judge only these keys. Without it every row of the body's Entity in the
|
|
2450
|
-
* store is a candidate, which is what a filter over a loaded list wants; with
|
|
2451
|
-
* it, a caller narrows to a connection's own edges.
|
|
2452
|
-
*/
|
|
2453
|
-
readonly among?: ReadonlyArray<EntityKey> | undefined;
|
|
2454
|
-
}
|
|
2361
|
+
declare const invalidateConnections: (model: RemoteModel, connections: ReadonlyArray<string>) => RemoteModel;
|
|
2455
2362
|
/**
|
|
2456
|
-
*
|
|
2457
|
-
*
|
|
2458
|
-
*
|
|
2459
|
-
*
|
|
2460
|
-
* whole reason this takes a descriptor rather than a bare body: a store holds
|
|
2461
|
-
* wire values, so an input has to be in that space to be compared against them,
|
|
2462
|
-
* and handing an interpreter a decoded value is a mistake **no runtime error
|
|
2463
|
-
* catches** — the reference interpreter answers "no rows", which reads exactly
|
|
2464
|
-
* like a correct answer. Doing it here means a caller cannot get it wrong.
|
|
2465
|
-
*
|
|
2466
|
-
* Literals in the body are deliberately *not* encoded. A literal written in
|
|
2467
|
-
* domain terms is already broken against the server, which binds it to a column
|
|
2468
|
-
* the same way; accepting it here would make the client quietly more permissive
|
|
2469
|
-
* than the thing it has to agree with.
|
|
2363
|
+
* Whether one field's read already began under the generation that field was
|
|
2364
|
+
* last refreshed at. Such a read is in flight and would otherwise outlive the
|
|
2365
|
+
* refresh, so the refresh takes a new generation to restart it even though the
|
|
2366
|
+
* field is already stale.
|
|
2470
2367
|
*/
|
|
2471
|
-
declare const
|
|
2368
|
+
declare const refreshIsInFlight: (refresh: RefreshState, request: Requirement, field: string) => boolean;
|
|
2472
2369
|
/**
|
|
2473
|
-
* Whether
|
|
2474
|
-
*
|
|
2475
|
-
*
|
|
2476
|
-
* is a row the store never fetched, or holds without a field the body reads —
|
|
2477
|
-
* a caller that collapses it into `'no'` has turned "I could not tell" into an
|
|
2478
|
-
* answer, which is the mistake this whole module is shaped against.
|
|
2479
|
-
*
|
|
2480
|
-
* **Membership is decidable where position is not.** Whether a row satisfies
|
|
2481
|
-
* `eq`, `isNull` or `contains` needs no collation: equality on text is exact
|
|
2482
|
-
* and `contains` folds ASCII, both stated in §6.0.1. *Where* a row sorts needs
|
|
2483
|
-
* text collation, which is the backend's and deliberately outside the
|
|
2484
|
-
* conformant subset — so this answers the half that is always answerable, and
|
|
2485
|
-
* ordering stays the server's until a query can declare its collation.
|
|
2370
|
+
* Whether a read is fetching any of `fields` for this entity. Consulted only
|
|
2371
|
+
* when the store lacks the value, which is what separates `Loading` from
|
|
2372
|
+
* `Initial`.
|
|
2486
2373
|
*/
|
|
2487
|
-
|
|
2374
|
+
declare const isLoading: (model: RemoteModel, entity: string, id: string, fields: ReadonlyArray<string>) => boolean;
|
|
2488
2375
|
/**
|
|
2489
|
-
*
|
|
2490
|
-
*
|
|
2491
|
-
*
|
|
2492
|
-
*
|
|
2493
|
-
*
|
|
2494
|
-
*
|
|
2495
|
-
*
|
|
2376
|
+
* The pure reducer all four producers share. A live event that arrives ahead of
|
|
2377
|
+
* its cursor is a gap: it is not applied, and the stream is recorded so its
|
|
2378
|
+
* live entry restarts it rather than silently missing facts. When every field a Message
|
|
2379
|
+
* touches keeps its identity, `model` itself is returned, so the application's
|
|
2380
|
+
* root keeps its identity and Foldkit does not render. The common no-ops do (a
|
|
2381
|
+
* duplicate live event, a repeated `ReadStarted`); a repeated failure, or a
|
|
2382
|
+
* `preserve-existing` restore of entities already held, still makes an equal
|
|
2383
|
+
* copy.
|
|
2496
2384
|
*/
|
|
2497
|
-
declare const
|
|
2385
|
+
declare const updateRemote: (model: RemoteModel, message: RemoteMessage) => RemoteModel;
|
|
2386
|
+
/** Whether this field's last read failed and nothing has settled it since. */
|
|
2387
|
+
declare const isFieldFailed: (model: RemoteModel, entity: string, id: string, field: string) => boolean;
|
|
2388
|
+
/**
|
|
2389
|
+
* Whether a read is fetching any field `relation` reads of an entity, its own
|
|
2390
|
+
* or, through each relation the store holds, its targets'. A value whose owner
|
|
2391
|
+
* is being fetched is loading, not `Initial`: nothing is missing a fetch.
|
|
2392
|
+
*/
|
|
2393
|
+
declare const isLoadingThrough: (model: RemoteModel, entity: string, id: string, relation: RelationRequirement) => boolean;
|
|
2394
|
+
/**
|
|
2395
|
+
* The first failure among the fields `relation` reads of an entity, following
|
|
2396
|
+
* each relation the store holds into its targets, as `assemble` does. A read
|
|
2397
|
+
* that consults it shows `Failed` for a related entity's failure too, rather
|
|
2398
|
+
* than waiting on a field nothing will fetch.
|
|
2399
|
+
*/
|
|
2400
|
+
declare const failureOf: (model: RemoteModel, entity: string, id: string, relation: RelationRequirement) => RemoteError | undefined;
|
|
2401
|
+
/**
|
|
2402
|
+
* Writes a read result into the store, recording each field's applied window so
|
|
2403
|
+
* a later request with a different window refetches. A relation page requested
|
|
2404
|
+
* with a cursor is merged onto the page already stored, so "load more"
|
|
2405
|
+
* accumulates rather than replaces. Every read path should use this rather than
|
|
2406
|
+
* reducing `writeEntity` by hand.
|
|
2407
|
+
*/
|
|
2408
|
+
declare const writeRead: (store: EntityStore, requests: ReadonlyArray<Requirement>, result: Schema.Schema.Type<typeof ReadBatchResult>, now?: number) => EntityStore;
|
|
2498
2409
|
//#endregion
|
|
2499
2410
|
//#region src/inspect.d.ts
|
|
2500
2411
|
/** A query body's `Dependencies` as plain data: each field by its Entity's name and key. */
|
|
@@ -2544,7 +2455,7 @@ interface RemoteInspection {
|
|
|
2544
2455
|
/** A pure, serializable view of the whole cache. */
|
|
2545
2456
|
declare const inspectRemote: (model: RemoteModel) => RemoteInspection;
|
|
2546
2457
|
/** A pure, serializable view of one entity, or `undefined` if unknown. */
|
|
2547
|
-
declare const inspectEntity: (model: RemoteModel, key: string) => RemoteInspection[
|
|
2458
|
+
declare const inspectEntity: (model: RemoteModel, key: string) => RemoteInspection['entities'][number] | undefined;
|
|
2548
2459
|
/**
|
|
2549
2460
|
* Why a read shows what it shows, in words, and for `Initial`, which of the
|
|
2550
2461
|
* usual mistakes it is.
|
|
@@ -2696,7 +2607,7 @@ type RemotePolicy =
|
|
|
2696
2607
|
declare const RemotePolicy: {
|
|
2697
2608
|
/** Fetch only what the store lacks. */
|
|
2698
2609
|
cacheFirst: {
|
|
2699
|
-
readonly _tag:
|
|
2610
|
+
readonly _tag: 'CacheFirst';
|
|
2700
2611
|
};
|
|
2701
2612
|
/** Keep present values visible, read as `Refreshing`, and refetch an entry older than `maxAge` ms. */
|
|
2702
2613
|
staleWhileRevalidate: (options: {
|
|
@@ -2704,7 +2615,7 @@ declare const RemotePolicy: {
|
|
|
2704
2615
|
}) => RemotePolicy;
|
|
2705
2616
|
/** Fetch every selected field regardless of coverage; cached values stay visible meanwhile. */
|
|
2706
2617
|
networkOnly: {
|
|
2707
|
-
readonly _tag:
|
|
2618
|
+
readonly _tag: 'NetworkOnly';
|
|
2708
2619
|
};
|
|
2709
2620
|
/** The planner options a policy compiles to at `now`. */
|
|
2710
2621
|
toPlan: (policy: RemotePolicy, now: number) => PlanOptions;
|
|
@@ -2716,6 +2627,89 @@ declare const RemotePolicy: {
|
|
|
2716
2627
|
refreshes: (policy: RemotePolicy) => boolean;
|
|
2717
2628
|
};
|
|
2718
2629
|
//#endregion
|
|
2630
|
+
//#region src/resume.d.ts
|
|
2631
|
+
/**
|
|
2632
|
+
* A resume part, in the shape `foldkit-ssr`'s `SSR.plan` takes in `parts`.
|
|
2633
|
+
* Declared here structurally, so neither package depends on the other.
|
|
2634
|
+
*/
|
|
2635
|
+
interface RemoteResumePart<AppModel> {
|
|
2636
|
+
readonly id: string;
|
|
2637
|
+
/** The metadata key names whose reads this part resumes. */
|
|
2638
|
+
readonly covers: ReadonlyArray<string>;
|
|
2639
|
+
readonly capture: (model: AppModel, projections: ReadonlyArray<{
|
|
2640
|
+
readonly metadata: Metadata;
|
|
2641
|
+
}>) => unknown;
|
|
2642
|
+
readonly restore: (model: AppModel, value: unknown) => Result.Result<AppModel, string>;
|
|
2643
|
+
/** Whether a Subscription entry is one of this domain's, which may start late. */
|
|
2644
|
+
readonly deferrable: (key: string, entry: unknown) => boolean;
|
|
2645
|
+
}
|
|
2646
|
+
/**
|
|
2647
|
+
* The resume part for a Remote domain, for `SSR.plan`'s `parts`: it sends
|
|
2648
|
+
* what the plan's active Surfaces read from this domain's store, and nothing
|
|
2649
|
+
* else of it. `id` tells two domains in one application apart.
|
|
2650
|
+
*/
|
|
2651
|
+
declare const resumePart: <AppModel, Store extends RemoteModel>(domain: {
|
|
2652
|
+
readonly store: ModelRef<AppModel, Store>;
|
|
2653
|
+
}, options?: {
|
|
2654
|
+
readonly id?: string | undefined;
|
|
2655
|
+
}) => RemoteResumePart<AppModel>;
|
|
2656
|
+
//#endregion
|
|
2657
|
+
//#region src/json.d.ts
|
|
2658
|
+
/** A request: which of Remote's calls, and its payload as the RPC protocol encodes it. */
|
|
2659
|
+
declare const RemoteJsonRequest: Schema.Struct<{
|
|
2660
|
+
readonly operation: Schema.Literals<readonly ["read", "query", "mutate"]>;
|
|
2661
|
+
readonly payload: Schema.Unknown;
|
|
2662
|
+
}>;
|
|
2663
|
+
type RemoteJsonRequest = typeof RemoteJsonRequest.Type;
|
|
2664
|
+
/** An answer: the call's result, or why there is none. */
|
|
2665
|
+
declare const RemoteJsonAnswer: Schema.Union<readonly [Schema.Struct<{
|
|
2666
|
+
readonly result: Schema.Unknown;
|
|
2667
|
+
}>, Schema.Struct<{
|
|
2668
|
+
readonly error: Schema.String;
|
|
2669
|
+
readonly refusal: Schema.optional<Schema.Unknown>;
|
|
2670
|
+
/** Who is asking was refused, with the application's reason when it gave one. */
|
|
2671
|
+
readonly forbidden: Schema.optional<Schema.Struct<{
|
|
2672
|
+
readonly reason: Schema.optional<Schema.String>;
|
|
2673
|
+
}>>;
|
|
2674
|
+
}>]>;
|
|
2675
|
+
type RemoteJsonAnswer = typeof RemoteJsonAnswer.Type;
|
|
2676
|
+
/**
|
|
2677
|
+
* Sends one request's text and resolves with its answer as parsed JSON. It
|
|
2678
|
+
* rejects when nothing came back, and the call fails with that message.
|
|
2679
|
+
*/
|
|
2680
|
+
type RemoteJsonSend = (request: string) => Promise<unknown>;
|
|
2681
|
+
/** Remote's client over `send`, for `Remote.clientLayer`. */
|
|
2682
|
+
declare const json: (send: RemoteJsonSend) => RemoteRpcClient;
|
|
2683
|
+
/**
|
|
2684
|
+
* Remote's client over HTTP: each call a `POST` of the request to `url`, the
|
|
2685
|
+
* answer its JSON body whatever its status. A 403 is answered as forbidden
|
|
2686
|
+
* unless its body says so itself, as the server's own does: one from
|
|
2687
|
+
* middleware in front of Remote (a refused session) may say only `{ error }`.
|
|
2688
|
+
* `headers` is read per request, so a token that changes reaches the next
|
|
2689
|
+
* call. `fetch` defaults to the global one; pass it to route calls through
|
|
2690
|
+
* another implementation in tests.
|
|
2691
|
+
*/
|
|
2692
|
+
declare const http: (url: string, options?: {
|
|
2693
|
+
readonly headers?: (() => Readonly<Record<string, string>>) | undefined;
|
|
2694
|
+
readonly fetch?: typeof fetch | undefined;
|
|
2695
|
+
}) => RemoteRpcClient;
|
|
2696
|
+
//#endregion
|
|
2697
|
+
//#region src/sse.d.ts
|
|
2698
|
+
/** What `httpWithLive` needs besides the URL. Mirrors `http`, plus the fetch itself. */
|
|
2699
|
+
interface LiveFetchOptions {
|
|
2700
|
+
/** Read per request, so a token that changes reaches the next call. */
|
|
2701
|
+
readonly headers?: (() => Readonly<Record<string, string>>) | undefined;
|
|
2702
|
+
/** Defaults to the global one; pass it to route calls elsewhere in tests. */
|
|
2703
|
+
readonly fetch?: typeof fetch | undefined;
|
|
2704
|
+
}
|
|
2705
|
+
/** Remote's live stream over fetch: `serveFetch`'s live open as a `Stream`. */
|
|
2706
|
+
declare const liveFetch: (url: string, options?: LiveFetchOptions) => RemoteRpcClient['FoldkitRemoteLive'];
|
|
2707
|
+
/**
|
|
2708
|
+
* Remote's client over HTTP with live: reads, queries, and mutations as
|
|
2709
|
+
* `http` sends them, live as `liveFetch` streams it, for `clientLayer`.
|
|
2710
|
+
*/
|
|
2711
|
+
declare const httpWithLive: (url: string, options?: LiveFetchOptions) => RemoteRpcClient;
|
|
2712
|
+
//#endregion
|
|
2719
2713
|
//#region src/placement.d.ts
|
|
2720
2714
|
/** Where a row falls in a loaded list. */
|
|
2721
2715
|
type RowPlace = Data.TaggedEnum<{
|
|
@@ -2736,65 +2730,43 @@ type RowPlace = Data.TaggedEnum<{
|
|
|
2736
2730
|
};
|
|
2737
2731
|
}>;
|
|
2738
2732
|
declare const RowPlace: {
|
|
2739
|
-
readonly Unknown:
|
|
2740
|
-
readonly _tag: "Unknown";
|
|
2741
|
-
readonly reason: string;
|
|
2742
|
-
}, "_tag">;
|
|
2743
|
-
readonly Before: Data.TaggedEnum.ConstructorFrom<{
|
|
2733
|
+
readonly $is: <Tag extends "Before" | "End" | "Outside" | "Unknown">(tag: Tag) => (u: unknown) => u is Extract<{
|
|
2744
2734
|
readonly _tag: "Before";
|
|
2745
2735
|
readonly segment: number;
|
|
2746
2736
|
readonly edge: string;
|
|
2747
|
-
}, "_tag">;
|
|
2748
|
-
readonly End: Data.TaggedEnum.ConstructorFrom<{
|
|
2749
|
-
readonly _tag: "End";
|
|
2750
|
-
readonly segment: number;
|
|
2751
|
-
}, "_tag">;
|
|
2752
|
-
readonly Outside: Data.TaggedEnum.ConstructorFrom<{
|
|
2753
|
-
readonly _tag: "Outside";
|
|
2754
|
-
}, "_tag">;
|
|
2755
|
-
readonly $is: <Tag extends "Unknown" | "Before" | "End" | "Outside">(tag: Tag) => (u: unknown) => u is Extract<{
|
|
2756
|
-
readonly _tag: "Unknown";
|
|
2757
|
-
readonly reason: string;
|
|
2758
2737
|
}, {
|
|
2759
2738
|
readonly _tag: Tag;
|
|
2760
2739
|
}> | Extract<{
|
|
2761
|
-
readonly _tag: "
|
|
2740
|
+
readonly _tag: "End";
|
|
2762
2741
|
readonly segment: number;
|
|
2763
|
-
readonly edge: string;
|
|
2764
2742
|
}, {
|
|
2765
2743
|
readonly _tag: Tag;
|
|
2766
2744
|
}> | Extract<{
|
|
2767
|
-
readonly _tag: "
|
|
2768
|
-
readonly segment: number;
|
|
2745
|
+
readonly _tag: "Outside";
|
|
2769
2746
|
}, {
|
|
2770
2747
|
readonly _tag: Tag;
|
|
2771
2748
|
}> | Extract<{
|
|
2772
|
-
readonly _tag: "
|
|
2749
|
+
readonly _tag: "Unknown";
|
|
2750
|
+
readonly reason: string;
|
|
2773
2751
|
}, {
|
|
2774
2752
|
readonly _tag: Tag;
|
|
2775
2753
|
}>;
|
|
2776
2754
|
readonly $match: {
|
|
2777
|
-
<Cases extends {
|
|
2778
|
-
readonly
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
readonly
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
readonly End: (args: {
|
|
2788
|
-
readonly _tag: "End";
|
|
2789
|
-
readonly segment: number;
|
|
2790
|
-
}) => any;
|
|
2791
|
-
readonly Outside: (args: {
|
|
2792
|
-
readonly _tag: "Outside";
|
|
2793
|
-
}) => any;
|
|
2794
|
-
}>(cases: Cases): (value: {
|
|
2755
|
+
<Cases extends { readonly [Tag in "Before" | "End" | "Outside" | "Unknown"]: (args: Extract<{
|
|
2756
|
+
readonly _tag: "Before";
|
|
2757
|
+
readonly segment: number;
|
|
2758
|
+
readonly edge: string;
|
|
2759
|
+
} | {
|
|
2760
|
+
readonly _tag: "End";
|
|
2761
|
+
readonly segment: number;
|
|
2762
|
+
} | {
|
|
2763
|
+
readonly _tag: "Outside";
|
|
2764
|
+
} | {
|
|
2795
2765
|
readonly _tag: "Unknown";
|
|
2796
2766
|
readonly reason: string;
|
|
2797
|
-
}
|
|
2767
|
+
}, {
|
|
2768
|
+
readonly _tag: Tag;
|
|
2769
|
+
}>) => any; }>(cases: Cases): (value: {
|
|
2798
2770
|
readonly _tag: "Before";
|
|
2799
2771
|
readonly segment: number;
|
|
2800
2772
|
readonly edge: string;
|
|
@@ -2803,28 +2775,25 @@ declare const RowPlace: {
|
|
|
2803
2775
|
readonly segment: number;
|
|
2804
2776
|
} | {
|
|
2805
2777
|
readonly _tag: "Outside";
|
|
2806
|
-
}
|
|
2807
|
-
<Cases extends {
|
|
2808
|
-
readonly Unknown: (args: {
|
|
2809
|
-
readonly _tag: "Unknown";
|
|
2810
|
-
readonly reason: string;
|
|
2811
|
-
}) => any;
|
|
2812
|
-
readonly Before: (args: {
|
|
2813
|
-
readonly _tag: "Before";
|
|
2814
|
-
readonly segment: number;
|
|
2815
|
-
readonly edge: string;
|
|
2816
|
-
}) => any;
|
|
2817
|
-
readonly End: (args: {
|
|
2818
|
-
readonly _tag: "End";
|
|
2819
|
-
readonly segment: number;
|
|
2820
|
-
}) => any;
|
|
2821
|
-
readonly Outside: (args: {
|
|
2822
|
-
readonly _tag: "Outside";
|
|
2823
|
-
}) => any;
|
|
2824
|
-
}>(value: {
|
|
2778
|
+
} | {
|
|
2825
2779
|
readonly _tag: "Unknown";
|
|
2826
2780
|
readonly reason: string;
|
|
2781
|
+
}) => import("effect/Unify").Unify<ReturnType<Cases["Before" | "End" | "Outside" | "Unknown"]>>;
|
|
2782
|
+
<Cases extends { readonly [Tag in "Before" | "End" | "Outside" | "Unknown"]: (args: Extract<{
|
|
2783
|
+
readonly _tag: "Before";
|
|
2784
|
+
readonly segment: number;
|
|
2785
|
+
readonly edge: string;
|
|
2827
2786
|
} | {
|
|
2787
|
+
readonly _tag: "End";
|
|
2788
|
+
readonly segment: number;
|
|
2789
|
+
} | {
|
|
2790
|
+
readonly _tag: "Outside";
|
|
2791
|
+
} | {
|
|
2792
|
+
readonly _tag: "Unknown";
|
|
2793
|
+
readonly reason: string;
|
|
2794
|
+
}, {
|
|
2795
|
+
readonly _tag: Tag;
|
|
2796
|
+
}>) => any; }>(value: {
|
|
2828
2797
|
readonly _tag: "Before";
|
|
2829
2798
|
readonly segment: number;
|
|
2830
2799
|
readonly edge: string;
|
|
@@ -2833,8 +2802,27 @@ declare const RowPlace: {
|
|
|
2833
2802
|
readonly segment: number;
|
|
2834
2803
|
} | {
|
|
2835
2804
|
readonly _tag: "Outside";
|
|
2836
|
-
}
|
|
2805
|
+
} | {
|
|
2806
|
+
readonly _tag: "Unknown";
|
|
2807
|
+
readonly reason: string;
|
|
2808
|
+
}, cases: Cases): import("effect/Unify").Unify<ReturnType<Cases["Before" | "End" | "Outside" | "Unknown"]>>;
|
|
2837
2809
|
};
|
|
2810
|
+
readonly Before: Data.TaggedEnum.ConstructorFrom<{
|
|
2811
|
+
readonly _tag: "Before";
|
|
2812
|
+
readonly segment: number;
|
|
2813
|
+
readonly edge: string;
|
|
2814
|
+
}, "_tag">;
|
|
2815
|
+
readonly End: Data.TaggedEnum.ConstructorFrom<{
|
|
2816
|
+
readonly _tag: "End";
|
|
2817
|
+
readonly segment: number;
|
|
2818
|
+
}, "_tag">;
|
|
2819
|
+
readonly Outside: Data.TaggedEnum.ConstructorFrom<{
|
|
2820
|
+
readonly _tag: "Outside";
|
|
2821
|
+
}, "_tag">;
|
|
2822
|
+
readonly Unknown: Data.TaggedEnum.ConstructorFrom<{
|
|
2823
|
+
readonly _tag: "Unknown";
|
|
2824
|
+
readonly reason: string;
|
|
2825
|
+
}, "_tag">;
|
|
2838
2826
|
};
|
|
2839
2827
|
/**
|
|
2840
2828
|
* Where the row stored under `key` falls in `connection`, by `body`'s order
|
|
@@ -3352,6 +3340,14 @@ interface LiveOptions {
|
|
|
3352
3340
|
/** The longest a restart waits. Default `5 seconds`. */
|
|
3353
3341
|
readonly maxRetryDelay?: Duration.Input | undefined;
|
|
3354
3342
|
}
|
|
3343
|
+
/** Declares a Remote domain: its entities, queries, and mutations, plus the submodel. */
|
|
3344
|
+
declare const defineRemote: <const Entities extends readonly EntityLike[], const Queries extends readonly QueryDescriptor<any, any, any>[] = readonly QueryDescriptor<any, any, any>[], const Mutations extends readonly MutationDescriptor<any, any, any>[] = readonly MutationDescriptor<any, any, any>[]>(config: {
|
|
3345
|
+
readonly entities: Entities;
|
|
3346
|
+
readonly queries?: Queries;
|
|
3347
|
+
readonly mutations?: Mutations;
|
|
3348
|
+
}) => RemoteDescriptor<Entities, Queries, Mutations>;
|
|
3349
|
+
/** Binds a Remote domain to its store's location in the application Model. */
|
|
3350
|
+
declare const bindRemote: <AppModel, Store extends RemoteModel, Entities extends readonly EntityLike[], Queries extends readonly QueryDescriptor<any, any, any>[], Mutations extends readonly MutationDescriptor<any, any, any>[]>(definition: RemoteDescriptor<Entities, Queries, Mutations>, store: ModelRef<AppModel, Store>) => BoundRemote<AppModel, Store, EntityName<Entities[number]>>;
|
|
3355
3351
|
/** A query the read entry runs, as its dependencies carry it: plain data Foldkit compares. */
|
|
3356
3352
|
declare const PlannedQuery: Schema.Struct<{
|
|
3357
3353
|
readonly identity: Schema.String;
|
|
@@ -3528,11 +3524,7 @@ declare const Remote: {
|
|
|
3528
3524
|
};
|
|
3529
3525
|
};
|
|
3530
3526
|
/** Whether a Message is one of Remote's, by tag. */
|
|
3531
|
-
reduces:
|
|
3532
|
-
readonly _tag: string;
|
|
3533
|
-
}>(message: M) => message is Extract<M, {
|
|
3534
|
-
readonly _tag: RemoteMessageTag;
|
|
3535
|
-
}>;
|
|
3527
|
+
reduces: typeof isRemoteMessage;
|
|
3536
3528
|
/**
|
|
3537
3529
|
* Remote's Messages as one Schema, for a wrapper variant in the
|
|
3538
3530
|
* application's union: `GotRemoteMessage: { message: Remote.Message }`. With
|
|
@@ -3737,28 +3729,20 @@ declare const Remote: {
|
|
|
3737
3729
|
* mutations, plus the submodel. For a domain reused across applications or
|
|
3738
3730
|
* bound in a test; `Remote.make` is the one-step form.
|
|
3739
3731
|
*/
|
|
3740
|
-
define:
|
|
3741
|
-
readonly entities: Entities;
|
|
3742
|
-
readonly queries?: Queries;
|
|
3743
|
-
readonly mutations?: Mutations;
|
|
3744
|
-
}) => RemoteDescriptor<Entities, Queries, Mutations>;
|
|
3732
|
+
define: typeof defineRemote;
|
|
3745
3733
|
/**
|
|
3746
3734
|
* Binds a Remote domain to its store's location in the application Model. The
|
|
3747
3735
|
* registered entity names are carried on the returned value, so `Remote.select`
|
|
3748
3736
|
* rejects a selection for an entity this domain never declared.
|
|
3749
3737
|
*/
|
|
3750
|
-
at:
|
|
3738
|
+
at: typeof bindRemote;
|
|
3751
3739
|
/**
|
|
3752
3740
|
* The resume part for this domain, for `foldkit-ssr`'s `SSR.plan({ parts })`:
|
|
3753
3741
|
* a server render sends what the plan's active Surfaces read from the store,
|
|
3754
3742
|
* each connection with its boundaries and each live cursor with it, and
|
|
3755
3743
|
* nothing else. `id` tells two domains in one application apart.
|
|
3756
3744
|
*/
|
|
3757
|
-
resume:
|
|
3758
|
-
readonly store: ModelRef<AppModel, Store>;
|
|
3759
|
-
}, options?: {
|
|
3760
|
-
readonly id?: string | undefined;
|
|
3761
|
-
}) => RemoteResumePart<AppModel>;
|
|
3745
|
+
resume: typeof resumePart;
|
|
3762
3746
|
/**
|
|
3763
3747
|
* A Projection node that reads a `RemoteData` value out of the store. The id
|
|
3764
3748
|
* is supplied by the caller, usually from a Surface's params. The assembled
|
|
@@ -3766,7 +3750,7 @@ declare const Remote: {
|
|
|
3766
3750
|
* as `Failed` instead of being asserted into `Value`. A present value with a
|
|
3767
3751
|
* stale field reads as `Refreshing`: an observer is refetching it.
|
|
3768
3752
|
*/
|
|
3769
|
-
select: <AppModel, Store extends RemoteModel, Names extends string, Value, Name extends string>(bound: BoundRemote<AppModel, Store, Names>, given: EntitySelection<Value, Name> & Registered<Name, Names,
|
|
3753
|
+
select: <AppModel, Store extends RemoteModel, Names extends string, Value, Name extends string>(bound: BoundRemote<AppModel, Store, Names>, given: EntitySelection<Value, Name> & Registered<Name, Names, 'Entity'>) => (id: string) => Projection<AppModel, RemoteData<Value>>;
|
|
3770
3754
|
/**
|
|
3771
3755
|
* When what a projection shows was last received, and whether it is stale
|
|
3772
3756
|
* or loading. Pure, so a view can show "updated 5s ago" without I/O.
|
|
@@ -3807,7 +3791,7 @@ declare const Remote: {
|
|
|
3807
3791
|
* `policy` decides what a present field means (default cache-first); `now`
|
|
3808
3792
|
* is the clock it reads, so the planner itself stays pure and time-injected.
|
|
3809
3793
|
*/
|
|
3810
|
-
prefetch: <AppModel, Store extends RemoteModel, Value>(bound: BoundRemote<AppModel, Store, string>, model: AppModel, projection: Projection<AppModel, Value>, options?: ObserveOptions | undefined) => Effect.Effect<Readonly<Record<string, EntityEntry>>,
|
|
3794
|
+
prefetch: <AppModel, Store extends RemoteModel, Value>(bound: BoundRemote<AppModel, Store, string>, model: AppModel, projection: Projection<AppModel, Value>, options?: ObserveOptions | undefined) => Effect.Effect<Readonly<Record<string, EntityEntry>>, RemoteProtocolError | RemoteReadError, RemoteClient>;
|
|
3811
3795
|
/**
|
|
3812
3796
|
* Marks what a projection, or a Surface without params, requires as due again.
|
|
3813
3797
|
* Called from `update`: every selected field the store holds reads `Refreshing`,
|
|
@@ -3845,7 +3829,7 @@ declare const Remote: {
|
|
|
3845
3829
|
* Writes a read result into the store, recording each field's applied window.
|
|
3846
3830
|
* `requests` are the planned requirements the result answers.
|
|
3847
3831
|
*/
|
|
3848
|
-
writeRead:
|
|
3832
|
+
writeRead: typeof writeRead;
|
|
3849
3833
|
/**
|
|
3850
3834
|
* Adapts an Effect RPC client for `RemoteRpc` to `RemoteClient`, so an
|
|
3851
3835
|
* application provides the transport's RPC layer instead of writing the
|
|
@@ -3859,29 +3843,26 @@ declare const Remote: {
|
|
|
3859
3843
|
* answer, for `clientLayer`: a worker, a server in the page. `RemoteServer.answer`
|
|
3860
3844
|
* is the other end.
|
|
3861
3845
|
*/
|
|
3862
|
-
json:
|
|
3846
|
+
json: typeof json;
|
|
3863
3847
|
/**
|
|
3864
3848
|
* Remote's client over HTTP, for `clientLayer`: each call a `POST` of the JSON
|
|
3865
3849
|
* request to `url`. `headers` is read per request (a session token, say).
|
|
3866
3850
|
*/
|
|
3867
|
-
http:
|
|
3868
|
-
readonly headers?: (() => Readonly<Record<string, string>>) | undefined;
|
|
3869
|
-
readonly fetch?: typeof fetch | undefined;
|
|
3870
|
-
}) => RemoteRpcClient;
|
|
3851
|
+
http: typeof http;
|
|
3871
3852
|
/**
|
|
3872
3853
|
* Remote's client over HTTP with live, for `clientLayer`: reads, queries,
|
|
3873
3854
|
* and mutations as `http` sends them, live requirements as one `POST`
|
|
3874
3855
|
* answered with a `text/event-stream` of changes.
|
|
3875
3856
|
* `foldkit-remote-server/fetch` is the other end.
|
|
3876
3857
|
*/
|
|
3877
|
-
httpWithLive:
|
|
3858
|
+
httpWithLive: typeof httpWithLive;
|
|
3878
3859
|
clientLayer: <R = never>(client: RemoteRpcClient<R, RpcClientError.RpcClientError>, options?: CoalesceOptions) => Layer.Layer<RemoteClient, never, R>;
|
|
3879
3860
|
/**
|
|
3880
3861
|
* Wraps a `RemoteClient` layer so its reads coalesce: requirements issued
|
|
3881
3862
|
* together become one batch, and a requirement already in flight is joined.
|
|
3882
3863
|
* `Remote.clientLayer` applies this; use it on a hand-written client.
|
|
3883
3864
|
*/
|
|
3884
|
-
coalesced:
|
|
3865
|
+
coalesced: typeof coalescedLayer;
|
|
3885
3866
|
/**
|
|
3886
3867
|
* A Foldkit Subscription entry that keeps the cache to what the active
|
|
3887
3868
|
* Surfaces reach. `projections` are the ones the application observes (the
|
|
@@ -3934,9 +3915,9 @@ declare const Remote: {
|
|
|
3934
3915
|
/** The `RemoteMessage` that merges a query page into its connection. */
|
|
3935
3916
|
queryMessage: <Name extends string, Input>(ref: QueryRef<Name, Input>, result: Schema.Schema.Type<typeof QueryResult>, select?: RelationRequirement | undefined, now?: number | undefined) => RemoteMessage;
|
|
3936
3917
|
/** A pure, serializable view of the whole cache. */
|
|
3937
|
-
inspect:
|
|
3918
|
+
inspect: typeof inspectRemote;
|
|
3938
3919
|
/** A pure, serializable view of one entity, or `undefined` if unknown. */
|
|
3939
|
-
inspectEntity:
|
|
3920
|
+
inspectEntity: typeof inspectEntity;
|
|
3940
3921
|
/**
|
|
3941
3922
|
* Runs a mutation through `RemoteClient`, decoding its typed Output and
|
|
3942
3923
|
* returning the result's normalized patches so the caller can reconcile them
|
|
@@ -3956,7 +3937,7 @@ declare const Remote: {
|
|
|
3956
3937
|
readonly entity: string;
|
|
3957
3938
|
readonly id: string;
|
|
3958
3939
|
}[];
|
|
3959
|
-
},
|
|
3940
|
+
}, RemoteForbidden | RemoteMutationError, RemoteClient>;
|
|
3960
3941
|
/**
|
|
3961
3942
|
* Runs a mutation and reconciles its patches into a `RemoteModel` in one step,
|
|
3962
3943
|
* returning the new model alongside the typed Output. The model-level form of
|
|
@@ -3967,7 +3948,7 @@ declare const Remote: {
|
|
|
3967
3948
|
mutateInto: <Name extends string, Input, Output, AppModel, Store extends RemoteModel>(bound: BoundRemote<AppModel, Store, string>, model: AppModel, mutation: MutationDescriptor<Name, Input, Output, unknown>, input: Input, requestId: string, options?: MutateOptions | undefined) => Effect.Effect<{
|
|
3968
3949
|
output: Output;
|
|
3969
3950
|
model: AppModel;
|
|
3970
|
-
},
|
|
3951
|
+
}, RemoteForbidden | RemoteMutationError, RemoteClient>;
|
|
3971
3952
|
/**
|
|
3972
3953
|
* A Foldkit Subscription entry that plans a Surface's missing fields from the
|
|
3973
3954
|
* Model and fetches them through `RemoteClient`, emitting a `RemoteMessage`
|
|
@@ -3989,5 +3970,5 @@ declare const Remote: {
|
|
|
3989
3970
|
live: <AppModel, Store extends RemoteModel, Names extends string, Model, SurfaceMessage, Params, Message = RemoteMessage>(bound: BoundRemote<AppModel, Store, Names>, surface: Surface<AppModel, Model, SurfaceMessage, Params>, params: Params, toMessage?: (message: RemoteMessage) => Message, options?: LiveOptions) => EntryWithoutKeepAlive<AppModel, Message, LiveDependencies, RemoteClient>;
|
|
3990
3971
|
};
|
|
3991
3972
|
//#endregion
|
|
3992
|
-
export {
|
|
3993
|
-
//# sourceMappingURL=index-
|
|
3973
|
+
export { QueryExplanation as $, terminal as $i, coalesceReads as $n, ConnectionOverlay as $r, Entity$2 as $t, RemoteWiring as A, patchOfWrite as Ai, RemoteRpcClient as An, SettledFields as Ar, remoteModelSchema as At, liveFetch as B, Cursor as Bi, LiveEvent as Bn, ConnectionSpec as Br, SnapshotOptions as Bt, Registered as C, SchemaOrFields as Ci, isRefPage as Cn, RemoteForbidden as Cr, noFailures as Ct, RemoteEntry as D, failMutation as Di, relationShape as Dn, RemoteQueryError as Dr, refreshedAt as Dt, RemoteDomain as E, emptyMutationState as Ei, relationAlias as En, RemoteProtocolError as Er, refreshIsInFlight as Et, SubscriptionsOptions as F, RemoteError as Fi, remoteError as Fn, RemoteConnections as Fr, writeRead as Ft, RemotePolicy as G, edge as Gi, classifyLive as Gn, QueryDescriptor as Gr, reachable as Gt, RemoteJsonRequest as H, EdgeRef as Hi, LiveState as Hn, LiveInsertion as Hr, Retained as Ht, RowPlace as I, remoteDataSchema as Ii, BoundaryCounts as In, RemoteRequirements as Ir, MergePolicy as It, PlanOptions as J, hasPrevious as Ji, liveHasPrevious as Jn, QueryWindow as Jr, Matched as Jt, Deadline as K, emptyConnection as Ki, emptyLiveState as Kn, QueryInputs as Kr, Belongs as Kt, placeIn as L, remoteErrorSchema as Li, ConnectionApplied as Ln, Requirement as Lr, REMOTE_CACHE_VERSION as Lt, SatisfyOptions as M, schemaOf as Mi, connectionChangeOf as Mn, WireBoundary as Mr, updateRemote as Mt, SelectsEntity as N, Freshness as Ni, liveEventOf as Nn, ConnectionRequirement as Nr, windowGrowthKey as Nt, RemoteFold as O, mutationStatus as Oi, targetsOf as On, RemoteReadError as Or, remoteMessageCases as Ot, SubscriptionEntries as P, RemoteData as Pi, mutateRemote as Pn, RelationRequirement as Pr, withRefreshRequested as Pt, ExplainedDependencies as Q, segment as Qi, coalesceQueries as Qn, ConnectionIdentity as Qr, AnySchema as Qt, LiveFetchOptions as R, Boundary as Ri, EntityApplied as Rn, connectionsOf as Rr, RemotePersistence as Rt, ReadMeta as S, Refusal as Si, aliasedField as Sn, RelationRequest as Sr, isRemoteMessage as St, RemoteDescriptor as T, beginMutation as Ti, refsIn as Tn, RemoteMutationError as Tr, overlaysOf as Tt, RemoteJsonSend as U, Segment as Ui, applyConnectionEvent as Un, LivePolicy as Ur, RetentionRoots as Ut, RemoteJsonAnswer as V, Edge as Vi, LiveOutcome as Vn, IDENTITY_SEPARATOR as Vr, ConnectionRoot as Vt, RemoteResumePart as W, cursor as Wi, applyEntityEvent as Wn, Query$1 as Wr, gc as Wt, plan as X, items as Xi, CoalesceOptions as Xn, stableStringify as Xr, belongsEncoded as Xt, deadlineOf as Y, isGapped as Yi, BatchRead as Yn, ResultOf as Yr, MatchingOptions as Yt, windowKey as Z, merge as Zi, QueryRun as Zn, ConnectionChange as Zr, matching as Zt, QueryOptions as _, setStale as _a, MutationDescriptor as _i, RefParts as _n, REMOTE_PROTOCOL_VERSION as _r, invalidateConnections as _t, EntitySelection as a, clearStale as aa, beginOptimistic as ai, RefPage as an, LiveRequirement as ar, PlacedOrder as at, QueryWindowOptions as b, writeEntities as ba, NormalizedPatch as bi, RelationKind as bn, ReadBatchResult as br, isLoadingThrough as bt, LiveOptions as c, entry as ca, pruneOverlays as ci, SelectionOf as cn, Mutate as cr, RemoteMessage as ct, MutateOptions as d, isFieldUnavailable as da, settleFailure as di, assemble as dn, NormalizedEntity as dr, RemoteModel as dt, unknown as ea, EntityLayer as ei, EntityDescriptor as en, requirementKey as er, ReadDiagnosis as et, MutationStarted as f, isTombstone as fa, settleSuccess as fi, pageSchema as fn, PageSize as fr, StreamHealth as ft, QueryInput as g, remove as ga, MutationAnswer as gi, RefPageValue as gn, QueryRpc as gr, initialRemoteModel as gt, QueryEntity as h, readField as ha, Mutation as hi, RELATION_ALIAS as hn, QueryResult as hr, forgetRemote as ht, EntityLike as i, EntityWrite as ia, addOverlay as ii, RefKey as in, LiveEdge as ir, Failures as it, RetainOptions as j, reconcileMutation as ji, coalescedLayer as jn, WindowSchema as jr, retentionRootsSchema as jt, RemoteUnsatisfied as k, optimisticOfWrite as ki, RemoteClient as kn, RemoteRpc as kr, remoteMessageSchema as kt, LiveStatus as l, hasField as la, removeLayer as li, SelectionValue as ln, MutationRequest as lr, RemoteMessageInput as lt, PersistenceOptions as m, missingFields as ma, visibleStore as mi, unavailableOf as mn, QueryRequest as mr, failureOf as mt, BoundRemote as n, EntityKey as na, OptimisticState as ni, EntityRef as nn, Live as nr, inspectEntity as nt, FoldedMutationStarted as o, emptyStore as oa, connectionIdentity as oi, Page as on, MAX_FIELDS_PER_REQUEST as or, PlacementMismatch as ot, ObserveOptions as p, markStale as pa, visibleItems as pi, relationOf as pn, QueryEdge as pr, emptyRefresh as pt, PlanFreshness as q, hasNext as qi, liveHasNext as qn, QueryRef as qr, Judged as qt, DomainMutateOptions as r, EntityStore as ra, addLayer as ri, FieldsFrom as rn, LiveChange as rr, inspectRemote as rt, LiveDependencies as s, entityKey as sa, emptyOptimistic as si, Selection as sn, MAX_RELATION_DEPTH as sr, RefreshState as st, ActiveEntry as t, EntityEntry as ta, OptimisticOperation as ti, EntityPatch as tn, ConnectionChangeSchema as tr, RemoteInspection as tt, ModelOfDomain as u, isFieldStale as ua, removeOverlay as ui, SelectionValueOf as un, MutationResult as ur, RemoteMessageTag as ut, QueryProjection as v, setUnavailable as va, MutationState as vi, RelationAnnotation as vn, Read as vr, isFieldFailed as vt, Remote as w, TypeOf as wi, refParts as wn, RemoteLiveError as wr, onlyOverlaid as wt, ReadDependencies as x, writeEntity as xa, Reconciled as xi, RelationShape as xn, ReadRequest as xr, isQueryLoading as xt, QueryRequirement as y, tombstone as ya, MutationStatus as yi, RelationEntityAnnotation as yn, ReadBatch as yr, isLoading as yt, httpWithLive as z, Connection as zi, LiveCursor as zn, requirementsOf as zr, Snapshot as zt };
|
|
3974
|
+
//# sourceMappingURL=index-eVkkbhPA.d.mts.map
|