foldkit-remote 0.6.0 → 0.8.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 +471 -159
- package/dist/index.d.mts +378 -9
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +644 -49
- package/dist/index.mjs.map +1 -1
- package/package.json +9 -8
package/dist/index.d.mts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import { Context, Duration, Effect, Layer, Option, Schema, Stream } from "effect";
|
|
1
|
+
import { Context, Duration, Effect, Layer, Option, Result, Schema, Stream } from "effect";
|
|
2
2
|
import * as Domain from "foldkit-entity";
|
|
3
3
|
import { AnyEntity, AnyQuery, Dependencies, InputExpr, Query as Query$1 } from "foldkit-entity";
|
|
4
|
+
import { Command } from "foldkit/command";
|
|
4
5
|
import { EntryWithoutKeepAlive } from "foldkit/subscription";
|
|
5
6
|
import { ActiveSurface, Contract, Invalid, Metadata, ModelRef, Projection, Surface, Wiring } from "foldkit-surface";
|
|
6
7
|
import { Rpc, RpcGroup } from "effect/unstable/rpc";
|
|
7
8
|
import { KeyValueStore } from "effect/unstable/persistence";
|
|
8
|
-
import
|
|
9
|
+
import * as Update from "foldkit/update";
|
|
9
10
|
//#region ../metadata/dist/index.d.mts
|
|
10
11
|
//#region src/index.d.ts
|
|
11
12
|
/** Marks a `Metadata` value. Its entries are private to this module. */
|
|
@@ -202,6 +203,12 @@ export interface EntityEntry {
|
|
|
202
203
|
readonly present: ReadonlySet<string>;
|
|
203
204
|
/** Present fields whose value may be outdated (revalidation needed). */
|
|
204
205
|
readonly stale: ReadonlySet<string>;
|
|
206
|
+
/**
|
|
207
|
+
* Fields the server settled without a value: asked for, and answered without
|
|
208
|
+
* them, whatever the reason. Not planned again until a refresh asks; a later
|
|
209
|
+
* write of the field clears it.
|
|
210
|
+
*/
|
|
211
|
+
readonly unavailable: ReadonlySet<string>;
|
|
205
212
|
/** The entity is known to be absent; a later write clears this. */
|
|
206
213
|
readonly tombstone: boolean;
|
|
207
214
|
/** Injected clock reading of the last write; never read from ambient state. */
|
|
@@ -240,6 +247,14 @@ export declare const setStale: (store: EntityStore, marks: ReadonlyArray<readonl
|
|
|
240
247
|
export declare const markStale: (store: EntityStore, key: EntityKey, fields: Iterable<string>) => EntityStore;
|
|
241
248
|
/** Ends a refresh that did not land: the fields are present again as they were. */
|
|
242
249
|
export declare const clearStale: (store: EntityStore, key: EntityKey, fields: Iterable<string>) => EntityStore;
|
|
250
|
+
/**
|
|
251
|
+
* Records fields the server settled without a value. A present field is left
|
|
252
|
+
* as it is: the answer that settled it was not about the value it holds.
|
|
253
|
+
* `false` forgets the marks, which is how a refresh asks again.
|
|
254
|
+
*/
|
|
255
|
+
export declare const setUnavailable: (store: EntityStore, marks: ReadonlyArray<readonly [key: EntityKey, fields: Iterable<string>]>, unavailable: boolean) => EntityStore;
|
|
256
|
+
/** Whether the server settled this field without a value, and nothing has written it since. */
|
|
257
|
+
export declare const isFieldUnavailable: (store: EntityStore, key: EntityKey, field: string) => boolean;
|
|
243
258
|
/** Records that the entity is known to be absent, so it is not refetched. */
|
|
244
259
|
export declare const tombstone: (store: EntityStore, key: EntityKey) => EntityStore;
|
|
245
260
|
/** Forgets everything known about the entity, including a tombstone. */
|
|
@@ -257,7 +272,8 @@ export declare const isFieldStale: (store: EntityStore, key: EntityKey, field: s
|
|
|
257
272
|
export declare const readField: (store: EntityStore, key: EntityKey, field: string) => Option.Option<unknown>;
|
|
258
273
|
/**
|
|
259
274
|
* The fields the planner must fetch. A tombstone makes every field known
|
|
260
|
-
* (absent), so it returns an empty list and the entity is not refetched
|
|
275
|
+
* (absent), so it returns an empty list and the entity is not refetched, and a
|
|
276
|
+
* field the server settled without a value is known the same way. A
|
|
261
277
|
* requested window that differs from the one a field was fetched with also
|
|
262
278
|
* marks it missing, but only when the stored window is known (never `""`), so a
|
|
263
279
|
* writer that does not record windows cannot cause a refetch loop.
|
|
@@ -740,7 +756,7 @@ export declare const gc: (state: Retained & {
|
|
|
740
756
|
//#endregion
|
|
741
757
|
//#region src/persistence.d.ts
|
|
742
758
|
/** Bump when the serialized shape changes; a mismatch discards the cache. */
|
|
743
|
-
export declare const REMOTE_CACHE_VERSION =
|
|
759
|
+
export declare const REMOTE_CACHE_VERSION = 5;
|
|
744
760
|
/**
|
|
745
761
|
* What survives a reload: the entity cache, and the connections an application
|
|
746
762
|
* **declared** should come back.
|
|
@@ -832,7 +848,7 @@ export declare const RemotePersistence: {
|
|
|
832
848
|
* server refuses a mismatch with `RemoteProtocolError`, so a shape change never
|
|
833
849
|
* drifts silently: bump it whenever `ReadRequest` or `LiveRequirement` change.
|
|
834
850
|
*/
|
|
835
|
-
export declare const REMOTE_PROTOCOL_VERSION =
|
|
851
|
+
export declare const REMOTE_PROTOCOL_VERSION = 4;
|
|
836
852
|
declare const RemoteReadError_base: Schema.Class<RemoteReadError, Schema.TaggedStruct<"RemoteReadError", {
|
|
837
853
|
readonly message: Schema.String;
|
|
838
854
|
}>, import("effect/Cause").YieldableError>;
|
|
@@ -875,12 +891,27 @@ export declare const NormalizedEntity: Schema.Struct<{
|
|
|
875
891
|
readonly id: Schema.String;
|
|
876
892
|
readonly values: Schema.$Record<Schema.String, Schema.Unknown>;
|
|
877
893
|
}>;
|
|
894
|
+
/**
|
|
895
|
+
* Fields a read asked for that the server answered without, and will not
|
|
896
|
+
* answer with: the client stops asking. It says nothing about why, so a rule
|
|
897
|
+
* that withholds a field is not itself on the wire.
|
|
898
|
+
*/
|
|
899
|
+
export declare const SettledFields: Schema.Struct<{
|
|
900
|
+
readonly entity: Schema.String;
|
|
901
|
+
readonly id: Schema.String;
|
|
902
|
+
readonly fields: Schema.$Array<Schema.String>;
|
|
903
|
+
}>;
|
|
878
904
|
export declare const ReadBatchResult: Schema.Struct<{
|
|
879
905
|
readonly entities: Schema.$Array<Schema.Struct<{
|
|
880
906
|
readonly entity: Schema.String;
|
|
881
907
|
readonly id: Schema.String;
|
|
882
908
|
readonly values: Schema.$Record<Schema.String, Schema.Unknown>;
|
|
883
909
|
}>>;
|
|
910
|
+
readonly settled: Schema.$Array<Schema.Struct<{
|
|
911
|
+
readonly entity: Schema.String;
|
|
912
|
+
readonly id: Schema.String;
|
|
913
|
+
readonly fields: Schema.$Array<Schema.String>;
|
|
914
|
+
}>>;
|
|
884
915
|
}>;
|
|
885
916
|
export declare const MutationRequest: Schema.Struct<{
|
|
886
917
|
/** Stable across transport retries so a mutation is not applied twice. */
|
|
@@ -999,6 +1030,11 @@ export declare const Read: Rpc.Rpc<"FoldkitRemoteRead", Schema.Struct<{
|
|
|
999
1030
|
readonly id: Schema.String;
|
|
1000
1031
|
readonly values: Schema.$Record<Schema.String, Schema.Unknown>;
|
|
1001
1032
|
}>>;
|
|
1033
|
+
readonly settled: Schema.$Array<Schema.Struct<{
|
|
1034
|
+
readonly entity: Schema.String;
|
|
1035
|
+
readonly id: Schema.String;
|
|
1036
|
+
readonly fields: Schema.$Array<Schema.String>;
|
|
1037
|
+
}>>;
|
|
1002
1038
|
}>, Schema.Union<readonly [typeof RemoteReadError, typeof RemoteProtocolError]>, never, never>;
|
|
1003
1039
|
export declare const Mutate: Rpc.Rpc<"FoldkitRemoteMutate", Schema.Struct<{
|
|
1004
1040
|
/** Stable across transport retries so a mutation is not applied twice. */
|
|
@@ -1169,6 +1205,11 @@ export declare const RemoteRpc: RpcGroup.RpcGroup<Rpc.Rpc<"FoldkitRemoteRead", S
|
|
|
1169
1205
|
readonly id: Schema.String;
|
|
1170
1206
|
readonly values: Schema.$Record<Schema.String, Schema.Unknown>;
|
|
1171
1207
|
}>>;
|
|
1208
|
+
readonly settled: Schema.$Array<Schema.Struct<{
|
|
1209
|
+
readonly entity: Schema.String;
|
|
1210
|
+
readonly id: Schema.String;
|
|
1211
|
+
readonly fields: Schema.$Array<Schema.String>;
|
|
1212
|
+
}>>;
|
|
1172
1213
|
}>, Schema.Union<readonly [typeof RemoteReadError, typeof RemoteProtocolError]>, never, never> | Rpc.Rpc<"FoldkitRemoteMutate", Schema.Struct<{
|
|
1173
1214
|
/** Stable across transport retries so a mutation is not applied twice. */
|
|
1174
1215
|
readonly requestId: Schema.String;
|
|
@@ -1348,8 +1389,22 @@ export interface RefreshState {
|
|
|
1348
1389
|
readonly generation: number;
|
|
1349
1390
|
readonly requested: ReadonlyMap<string, number>;
|
|
1350
1391
|
readonly started: ReadonlyMap<string, number>;
|
|
1392
|
+
/**
|
|
1393
|
+
* The generation every entry is refreshed at, at least: `forgetRemote` moves
|
|
1394
|
+
* it, so every read and live entry restarts, and a read or stream begun for
|
|
1395
|
+
* the principal before is interrupted rather than landing after.
|
|
1396
|
+
*/
|
|
1397
|
+
readonly floor: number;
|
|
1351
1398
|
}
|
|
1352
1399
|
export declare const emptyRefresh: RefreshState;
|
|
1400
|
+
/**
|
|
1401
|
+
* The Model with everything the server told it forgotten: values, tombstones,
|
|
1402
|
+
* unavailable fields, connections, live cursors, failures, and what is in
|
|
1403
|
+
* flight. For a change of principal, whose facts these were. The mutation
|
|
1404
|
+
* sequence is kept, so request ids stay unique, and a mutation in flight is
|
|
1405
|
+
* treated as applied: its answer, when it comes, writes nothing here.
|
|
1406
|
+
*/
|
|
1407
|
+
export declare const forgetRemote: (model: RemoteModel) => RemoteModel;
|
|
1353
1408
|
export declare const initialRemoteModel: RemoteModel;
|
|
1354
1409
|
export declare const remoteModelSchema: () => Schema.Codec<RemoteModel, unknown>;
|
|
1355
1410
|
/** The submodel's Messages; each reduces to `RemoteModel` through `updateRemote`. */
|
|
@@ -1494,6 +1549,11 @@ export declare const remoteMessageCases: {
|
|
|
1494
1549
|
readonly id: Schema.String;
|
|
1495
1550
|
readonly values: Schema.$Record<Schema.String, Schema.Unknown>;
|
|
1496
1551
|
}>>;
|
|
1552
|
+
readonly settled: Schema.$Array<Schema.Struct<{
|
|
1553
|
+
readonly entity: Schema.String;
|
|
1554
|
+
readonly id: Schema.String;
|
|
1555
|
+
readonly fields: Schema.$Array<Schema.String>;
|
|
1556
|
+
}>>;
|
|
1497
1557
|
}>;
|
|
1498
1558
|
now: Schema.Number;
|
|
1499
1559
|
};
|
|
@@ -1642,6 +1702,12 @@ export declare const isLoading: (model: RemoteModel, entity: string, id: string,
|
|
|
1642
1702
|
export declare const updateRemote: (model: RemoteModel, message: RemoteMessage) => RemoteModel;
|
|
1643
1703
|
/** Whether this field's last read failed and nothing has settled it since. */
|
|
1644
1704
|
export declare const isFieldFailed: (model: RemoteModel, entity: string, id: string, field: string) => boolean;
|
|
1705
|
+
/**
|
|
1706
|
+
* Whether a read is fetching any field `relation` reads of an entity, its own
|
|
1707
|
+
* or, through each relation the store holds, its targets'. A value whose owner
|
|
1708
|
+
* is being fetched is loading, not `Initial`: nothing is missing a fetch.
|
|
1709
|
+
*/
|
|
1710
|
+
export declare const isLoadingThrough: (model: RemoteModel, entity: string, id: string, relation: RelationRequirement) => boolean;
|
|
1645
1711
|
/**
|
|
1646
1712
|
* The first failure among the fields `relation` reads of an entity, following
|
|
1647
1713
|
* each relation the store holds into its targets, as `assemble` does. A read
|
|
@@ -1658,6 +1724,23 @@ export declare const failureOf: (model: RemoteModel, entity: string, id: string,
|
|
|
1658
1724
|
*/
|
|
1659
1725
|
export declare const writeRead: (store: EntityStore, requests: ReadonlyArray<Requirement>, result: Schema.Schema.Type<typeof ReadBatchResult>, now?: number) => EntityStore;
|
|
1660
1726
|
//#endregion
|
|
1727
|
+
//#region src/resume.d.ts
|
|
1728
|
+
/**
|
|
1729
|
+
* A resume part, in the shape `foldkit-ssr`'s `SSR.plan` takes in `parts`.
|
|
1730
|
+
* Declared here structurally, so neither package depends on the other.
|
|
1731
|
+
*/
|
|
1732
|
+
interface RemoteResumePart<AppModel> {
|
|
1733
|
+
readonly id: string;
|
|
1734
|
+
/** The metadata key names whose reads this part resumes. */
|
|
1735
|
+
readonly covers: ReadonlyArray<string>;
|
|
1736
|
+
readonly capture: (model: AppModel, projections: ReadonlyArray<{
|
|
1737
|
+
readonly metadata: Metadata;
|
|
1738
|
+
}>) => unknown;
|
|
1739
|
+
readonly restore: (model: AppModel, value: unknown) => Result.Result<AppModel, string>;
|
|
1740
|
+
/** Whether a Subscription entry is one of this domain's, which may start late. */
|
|
1741
|
+
readonly deferrable: (key: string, entry: unknown) => boolean;
|
|
1742
|
+
}
|
|
1743
|
+
//#endregion
|
|
1661
1744
|
//#region src/coalesce.d.ts
|
|
1662
1745
|
type Batch = Schema.Schema.Type<typeof ReadBatch>;
|
|
1663
1746
|
type BatchResult = Schema.Schema.Type<typeof ReadBatchResult>;
|
|
@@ -1876,6 +1959,12 @@ interface Assembled {
|
|
|
1876
1959
|
* list), so the Selection's codec decides whether that is a failure.
|
|
1877
1960
|
*/
|
|
1878
1961
|
export declare const assemble: (store: EntityStore, key: string, requirement: RelationRequirement) => Assembled | undefined;
|
|
1962
|
+
/**
|
|
1963
|
+
* The first field `requirement` reads, at any depth, that the server settled
|
|
1964
|
+
* without a value, as `Entity:id.field`. A read that consults it says why it
|
|
1965
|
+
* cannot assemble rather than waiting on a field nothing will fetch.
|
|
1966
|
+
*/
|
|
1967
|
+
export declare const unavailableOf: (store: EntityStore, key: string, requirement: RelationRequirement) => string | undefined;
|
|
1879
1968
|
//#endregion
|
|
1880
1969
|
//#region src/entity.d.ts
|
|
1881
1970
|
export type AnySchema = Schema.Schema<unknown>;
|
|
@@ -1899,6 +1988,18 @@ export interface EntityDescriptor<Name extends string, F extends Schema.Struct.F
|
|
|
1899
1988
|
/** A patch of this entity's wire-shaped values; `Entity.patch(entity.ref(id), values)`. */
|
|
1900
1989
|
patch(id: Schema.Schema.Type<F['id']>, values: Partial<Schema.Struct.Encoded<F>>): EntityPatch<Name, F>;
|
|
1901
1990
|
}
|
|
1991
|
+
/**
|
|
1992
|
+
* Relations are **references**, never inline target schemas: a ref cannot
|
|
1993
|
+
* reconstruct a full entity, and dereferencing is a store concern. Because the
|
|
1994
|
+
* target schema is not inlined, recursive relations cannot arise through the
|
|
1995
|
+
* schema graph.
|
|
1996
|
+
*/
|
|
1997
|
+
/**
|
|
1998
|
+
* A ref as the wire carries it: the target's name, a colon, and its id. Typed
|
|
1999
|
+
* with the name, so a relation to `User` takes `'User:u1'` and not a ref to
|
|
2000
|
+
* some other entity, which would read as a row that does not exist.
|
|
2001
|
+
*/
|
|
2002
|
+
export type RefKey<Name extends string> = `${Name}:${string}`;
|
|
1902
2003
|
/**
|
|
1903
2004
|
* A patch in wire shape: the values the store holds, so a relation is its ref
|
|
1904
2005
|
* key (`Entity.refKey`), not an `EntityRef`.
|
|
@@ -1919,7 +2020,7 @@ export interface RefPage<Name extends string, F extends Schema.Struct.Fields = S
|
|
|
1919
2020
|
readonly hasPrevious: boolean;
|
|
1920
2021
|
}
|
|
1921
2022
|
/** The ref codec a `foldkit-entity` relation becomes: one ref, a nullable ref, or an array of refs. */
|
|
1922
|
-
type RelationField<Spec> = Spec extends Domain.RelationSpec<infer Target, infer Cardinality, infer Optional> ? Cardinality extends 'many' ? Schema.$Array<Schema.Codec<EntityRef<Target['name']>,
|
|
2023
|
+
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;
|
|
1923
2024
|
/**
|
|
1924
2025
|
* The fields Remote stores for a `foldkit-entity` Entity: its own fields, its
|
|
1925
2026
|
* derived members (the server supplies them like any field), and each relation
|
|
@@ -1941,9 +2042,9 @@ declare const Entity$1: {
|
|
|
1941
2042
|
readonly id: Schema.Schema<unknown>;
|
|
1942
2043
|
}>(name: Name, schema: Schema.Struct<F>) => EntityDescriptor<Name, F>;
|
|
1943
2044
|
/** A relation to a known entity, decoded as a reference. */
|
|
1944
|
-
ref: <Name extends string, F extends Schema.Struct.Fields>(entity: EntityDescriptor<Name, F>) => Schema.Codec<EntityRef<Name, F>,
|
|
2045
|
+
ref: <Name extends string, F extends Schema.Struct.Fields>(entity: EntityDescriptor<Name, F>) => Schema.Codec<EntityRef<Name, F>, RefKey<Name>>;
|
|
1945
2046
|
/** A relation by name, for recursive or forward references. */
|
|
1946
|
-
refTo: <Name extends string>(name: Name) => Schema.Codec<EntityRef<Name>,
|
|
2047
|
+
refTo: <Name extends string>(name: Name) => Schema.Codec<EntityRef<Name>, RefKey<Name>>;
|
|
1947
2048
|
/**
|
|
1948
2049
|
* The wire key a relation field carries (`"Entity:id"`). Adapters that read a
|
|
1949
2050
|
* foreign key emit this so the ref codec can decode it on the client.
|
|
@@ -2060,6 +2161,7 @@ export interface RemoteInspection {
|
|
|
2060
2161
|
readonly key: string;
|
|
2061
2162
|
readonly present: ReadonlyArray<string>;
|
|
2062
2163
|
readonly stale: ReadonlyArray<string>;
|
|
2164
|
+
readonly unavailable: ReadonlyArray<string>;
|
|
2063
2165
|
readonly tombstone: boolean;
|
|
2064
2166
|
readonly updatedAt: number;
|
|
2065
2167
|
readonly windows: Readonly<Record<string, string>>;
|
|
@@ -2090,6 +2192,29 @@ export interface RemoteInspection {
|
|
|
2090
2192
|
export declare const inspectRemote: (model: RemoteModel) => RemoteInspection;
|
|
2091
2193
|
/** A pure, serializable view of one entity, or `undefined` if unknown. */
|
|
2092
2194
|
export declare const inspectEntity: (model: RemoteModel, key: string) => RemoteInspection["entities"][number] | undefined;
|
|
2195
|
+
/**
|
|
2196
|
+
* Why a read shows what it shows, in words, and for `Initial`, which of the
|
|
2197
|
+
* usual mistakes it is.
|
|
2198
|
+
*
|
|
2199
|
+
* `Initial` means nothing is fetching the read, and that is almost always
|
|
2200
|
+
* wiring rather than the network: no active Surface reads it, or one does and
|
|
2201
|
+
* Remote's Subscriptions are not running. The Model can tell those apart when
|
|
2202
|
+
* it is given the active record, so this says which, and what to do.
|
|
2203
|
+
*/
|
|
2204
|
+
export interface ReadDiagnosis {
|
|
2205
|
+
readonly state: RemoteData<unknown>['_tag'];
|
|
2206
|
+
/**
|
|
2207
|
+
* For `Initial` only. `NotObserved`: no active Surface reads it.
|
|
2208
|
+
* `NotFetching`: one does, and nothing is fetching it, so Remote's
|
|
2209
|
+
* Subscriptions are most likely not installed. `Unknown`: no active record
|
|
2210
|
+
* was given to tell.
|
|
2211
|
+
*/
|
|
2212
|
+
readonly reason?: 'NotObserved' | 'NotFetching' | 'Unknown' | undefined;
|
|
2213
|
+
/** One or two sentences a developer can act on. */
|
|
2214
|
+
readonly message: string;
|
|
2215
|
+
/** The active Surfaces reading it, when the active record was given. */
|
|
2216
|
+
readonly surfaces?: ReadonlyArray<string> | undefined;
|
|
2217
|
+
}
|
|
2093
2218
|
/**
|
|
2094
2219
|
* What one query read is, and what it currently is — data-query-DESIGN §29.1.
|
|
2095
2220
|
*
|
|
@@ -2174,6 +2299,20 @@ export interface PlanOptions {
|
|
|
2174
2299
|
/** Plan every requested field, present or not, tombstoned or not. */
|
|
2175
2300
|
readonly force?: boolean | undefined;
|
|
2176
2301
|
}
|
|
2302
|
+
/** When present values next go stale, and the requirements of the entities that do. */
|
|
2303
|
+
export interface Deadline {
|
|
2304
|
+
readonly at: number;
|
|
2305
|
+
readonly due: ReadonlyArray<Requirement>;
|
|
2306
|
+
}
|
|
2307
|
+
/**
|
|
2308
|
+
* The earliest moment a value `requirements` read, following the relations the
|
|
2309
|
+
* store holds as `plan` does, goes stale under `freshness`, with the
|
|
2310
|
+
* requirements of the entities that go stale then. Nothing when no value is
|
|
2311
|
+
* both present and still fresh: what is already expired or stale is planned,
|
|
2312
|
+
* not awaited, which is what keeps a timer from firing on what it already
|
|
2313
|
+
* fired on.
|
|
2314
|
+
*/
|
|
2315
|
+
export declare const deadlineOf: (store: EntityStore, requirements: readonly Requirement[], freshness: PlanFreshness) => Deadline | undefined;
|
|
2177
2316
|
export declare const plan: (store: EntityStore, requirements: readonly Requirement[], options?: PlanOptions) => ReadonlyArray<Requirement>;
|
|
2178
2317
|
//#endregion
|
|
2179
2318
|
//#region src/policy.d.ts
|
|
@@ -2227,6 +2366,10 @@ export type EntityLike = EntityDescriptor<any, any> | Domain.AnyEntity;
|
|
|
2227
2366
|
export type EntitySelection<Value, Name extends string, Id extends string = string> = Selection<Value, Name, 'entity'> | Domain.Selection<Name, unknown, Schema.Constraint & {
|
|
2228
2367
|
readonly Type: Value;
|
|
2229
2368
|
}, Id>;
|
|
2369
|
+
/** The Remote descriptor either kind of entity reads as, at the type level. */
|
|
2370
|
+
type DescriptorOf<E extends EntityLike> = E extends EntityDescriptor<any, any> ? E : E extends Domain.AnyEntity ? EntityDescriptor<E['name'], FieldsFrom<E>> : never;
|
|
2371
|
+
type NameOf<E extends EntityLike> = DescriptorOf<E> extends EntityDescriptor<infer N, any> ? N : never;
|
|
2372
|
+
type FieldsOf<E extends EntityLike> = DescriptorOf<E> extends EntityDescriptor<any, infer F> ? F : never;
|
|
2230
2373
|
type EntityName<D> = D extends {
|
|
2231
2374
|
readonly name: infer Name extends string;
|
|
2232
2375
|
} ? Name : never;
|
|
@@ -2345,6 +2488,18 @@ export interface RemoteDomain<AppModel, Store extends RemoteModel, Entities exte
|
|
|
2345
2488
|
explain<Name extends string, Input>(model: AppModel, projection: QueryProjection<AppModel, any, Name, Input>, options?: {
|
|
2346
2489
|
readonly surfaces?: Readonly<Record<string, ActiveSurface<AppModel>>> | undefined;
|
|
2347
2490
|
}): QueryExplanation;
|
|
2491
|
+
/**
|
|
2492
|
+
* Why a read shows what it shows, in words. Any Remote read: `Data.get`,
|
|
2493
|
+
* `Data.live` or `Data.query`.
|
|
2494
|
+
*
|
|
2495
|
+
* Its point is `Initial`, which means nothing is fetching the read and is
|
|
2496
|
+
* almost always a wiring mistake. Given `surfaces`, the same active record
|
|
2497
|
+
* `subscriptions` takes, it says which: no active Surface reads it, or one
|
|
2498
|
+
* does and Remote's Subscriptions are not running. Pure, like `explain`.
|
|
2499
|
+
*/
|
|
2500
|
+
why(model: AppModel, projection: Projection<AppModel, RemoteData<unknown>>, options?: {
|
|
2501
|
+
readonly surfaces?: Readonly<Record<string, ActiveSurface<AppModel>>> | undefined;
|
|
2502
|
+
}): ReadDiagnosis;
|
|
2348
2503
|
/**
|
|
2349
2504
|
* The rows of a loaded list that a body matches, decoded as the list decodes
|
|
2350
2505
|
* them — a filter that asks the server nothing.
|
|
@@ -2378,6 +2533,8 @@ export interface RemoteDomain<AppModel, Store extends RemoteModel, Entities exte
|
|
|
2378
2533
|
lift(model: AppModel, id: string): AppModel;
|
|
2379
2534
|
/** `Remote.refresh`: marks what a projection or a Surface requires as due, for its read entry to refetch. */
|
|
2380
2535
|
refresh(model: AppModel, target: Projection<AppModel, unknown> | Surface<AppModel, any, any, void>): AppModel;
|
|
2536
|
+
/** `Remote.forget`: the Model with every server-derived fact gone, for a change of principal. */
|
|
2537
|
+
forget(model: AppModel): AppModel;
|
|
2381
2538
|
/**
|
|
2382
2539
|
* The Foldkit Subscription entries for the active Surfaces, keyed for
|
|
2383
2540
|
* `Subscription.make`: a read entry per Surface (`Remote.observe`), a live
|
|
@@ -2446,6 +2603,30 @@ export interface RemoteDomain<AppModel, Store extends RemoteModel, Entities exte
|
|
|
2446
2603
|
/** `Remote.inspect` of the bound slice. */
|
|
2447
2604
|
inspect(model: AppModel): RemoteInspection;
|
|
2448
2605
|
}
|
|
2606
|
+
/** What `Remote.fold(Data, …).mutate` starts: `Data.mutate`'s result with its Command lifted. */
|
|
2607
|
+
export type FoldedMutationStarted<AppModel, ParentMessage> = Omit<MutationStarted<AppModel>, 'command'> & {
|
|
2608
|
+
readonly command: Command<ParentMessage, never, RemoteClient>;
|
|
2609
|
+
};
|
|
2610
|
+
/**
|
|
2611
|
+
* A bound domain folded under one of the application's own Message variants,
|
|
2612
|
+
* from `Remote.fold`. Calling it reduces a Remote Message into the Model; the
|
|
2613
|
+
* members that produce Messages, `fetch`, `mutate`, and `subscriptions`,
|
|
2614
|
+
* yield the wrapper Message instead of `RemoteMessage`, with the lift recorded
|
|
2615
|
+
* so Story and Scene can resolve a fetch or a mutation by Remote's own answer.
|
|
2616
|
+
* Members that only read or write the Model (`get`, `refresh`, `next`,
|
|
2617
|
+
* `overlay`, and the rest) stay on the domain itself.
|
|
2618
|
+
*/
|
|
2619
|
+
export interface RemoteFold<AppModel, ParentMessage, Domain extends RemoteDomain<AppModel, any, any, any, any>> {
|
|
2620
|
+
(model: AppModel, message: RemoteMessage | RemoteMessageInput): Update.Return<AppModel, ParentMessage>;
|
|
2621
|
+
/** `Data.fetch`, lifted. */
|
|
2622
|
+
readonly fetch: (ref: QueryRef<string, unknown>) => Command<ParentMessage, never, RemoteClient>;
|
|
2623
|
+
/** `Data.mutate`, its Command lifted. */
|
|
2624
|
+
readonly mutate: (...args: Parameters<Domain['mutate']>) => FoldedMutationStarted<AppModel, ParentMessage>;
|
|
2625
|
+
/** `Data.subscriptions`, every entry lifted to the wrapper Message. */
|
|
2626
|
+
readonly subscriptions: <const Active extends Readonly<Record<string, ActiveSurface<AppModel> | Surface<AppModel, any, any, void>>>>(active: Active, options?: SubscriptionsOptions) => { readonly [K in keyof SubscriptionEntries<AppModel, Active>]: EntryWithoutKeepAlive<AppModel, ParentMessage, any, RemoteClient>; };
|
|
2627
|
+
}
|
|
2628
|
+
/** The application Model a bound domain is over. */
|
|
2629
|
+
export type ModelOfDomain<Domain> = Domain extends RemoteDomain<infer AppModel, any, any, any, any> ? AppModel : never;
|
|
2449
2630
|
/** A projection's connection requirement with the `QueryRef` that runs it. */
|
|
2450
2631
|
export interface QueryRequirement extends ConnectionRequirement {
|
|
2451
2632
|
readonly ref: QueryRef<string, unknown>;
|
|
@@ -2518,6 +2699,14 @@ export interface ReadDependencies {
|
|
|
2518
2699
|
* leaves it running.
|
|
2519
2700
|
*/
|
|
2520
2701
|
readonly refresh: number;
|
|
2702
|
+
/**
|
|
2703
|
+
* Under a refreshing policy, when a value the entry holds next ages out and
|
|
2704
|
+
* what is due then. Time reaches Remote only as a Message: the entry sleeps
|
|
2705
|
+
* until then and emits `RefreshStarted`, which marks the fields stale, and
|
|
2706
|
+
* the plan that follows fetches them. A Model that does not change past its
|
|
2707
|
+
* `maxAge` is otherwise never looked at again.
|
|
2708
|
+
*/
|
|
2709
|
+
readonly expires: Deadline | null;
|
|
2521
2710
|
}
|
|
2522
2711
|
export declare const Remote: {
|
|
2523
2712
|
/** The submodel's schema, the same for every domain; embed it in the application Model. */
|
|
@@ -2538,6 +2727,11 @@ export declare const Remote: {
|
|
|
2538
2727
|
readonly id: Schema.String;
|
|
2539
2728
|
readonly values: Schema.$Record<Schema.String, Schema.Unknown>;
|
|
2540
2729
|
}>>;
|
|
2730
|
+
readonly settled: Schema.$Array<Schema.Struct<{
|
|
2731
|
+
readonly entity: Schema.String;
|
|
2732
|
+
readonly id: Schema.String;
|
|
2733
|
+
readonly fields: Schema.$Array<Schema.String>;
|
|
2734
|
+
}>>;
|
|
2541
2735
|
}>;
|
|
2542
2736
|
now: Schema.Number;
|
|
2543
2737
|
};
|
|
@@ -2638,6 +2832,158 @@ export declare const Remote: {
|
|
|
2638
2832
|
}>(message: M) => message is Extract<M, {
|
|
2639
2833
|
readonly _tag: RemoteMessageTag;
|
|
2640
2834
|
}>;
|
|
2835
|
+
/**
|
|
2836
|
+
* Remote's Messages as one Schema, for a wrapper variant in the
|
|
2837
|
+
* application's union: `GotRemoteMessage: { message: Remote.Message }`. With
|
|
2838
|
+
* a wrapper, `update` matches the application's union exhaustively and
|
|
2839
|
+
* `Remote.fold` reduces what arrives inside it.
|
|
2840
|
+
*/
|
|
2841
|
+
Message: import("foldkit/message").MessageUnion<{
|
|
2842
|
+
ReadReceived: {
|
|
2843
|
+
requests: Schema.$Array<Schema.Codec<Requirement, Requirement, never, never>>;
|
|
2844
|
+
result: Schema.Struct<{
|
|
2845
|
+
readonly entities: Schema.$Array<Schema.Struct<{
|
|
2846
|
+
readonly entity: Schema.String;
|
|
2847
|
+
readonly id: Schema.String;
|
|
2848
|
+
readonly values: Schema.$Record<Schema.String, Schema.Unknown>;
|
|
2849
|
+
}>>;
|
|
2850
|
+
readonly settled: Schema.$Array<Schema.Struct<{
|
|
2851
|
+
readonly entity: Schema.String;
|
|
2852
|
+
readonly id: Schema.String;
|
|
2853
|
+
readonly fields: Schema.$Array<Schema.String>;
|
|
2854
|
+
}>>;
|
|
2855
|
+
}>;
|
|
2856
|
+
now: Schema.Number;
|
|
2857
|
+
};
|
|
2858
|
+
ReadFailed: {
|
|
2859
|
+
requests: Schema.$Array<Schema.Codec<Requirement, Requirement, never, never>>;
|
|
2860
|
+
error: Schema.Struct<{
|
|
2861
|
+
readonly _tag: Schema.String;
|
|
2862
|
+
readonly message: Schema.String;
|
|
2863
|
+
}>;
|
|
2864
|
+
stream: Schema.optional<Schema.String>;
|
|
2865
|
+
};
|
|
2866
|
+
RefreshStarted: {
|
|
2867
|
+
requests: Schema.$Array<Schema.Codec<Requirement, Requirement, never, never>>;
|
|
2868
|
+
};
|
|
2869
|
+
ReadStarted: {
|
|
2870
|
+
requests: Schema.$Array<Schema.Codec<Requirement, Requirement, never, never>>;
|
|
2871
|
+
refresh: Schema.optional<Schema.Number>;
|
|
2872
|
+
};
|
|
2873
|
+
QueryStarted: {
|
|
2874
|
+
connections: Schema.$Array<Schema.String>;
|
|
2875
|
+
};
|
|
2876
|
+
RetentionChanged: {
|
|
2877
|
+
roots: Schema.Struct<{
|
|
2878
|
+
readonly requirements: Schema.$Array<Schema.Codec<Requirement, Requirement, never, never>>;
|
|
2879
|
+
readonly connections: Schema.$Array<Schema.Struct<{
|
|
2880
|
+
readonly identity: Schema.String;
|
|
2881
|
+
readonly select: Schema.optional<Schema.Codec<RelationRequirement, RelationRequirement, never, never>>;
|
|
2882
|
+
}>>;
|
|
2883
|
+
}>;
|
|
2884
|
+
};
|
|
2885
|
+
Hydrated: {
|
|
2886
|
+
connections: Schema.optional<Schema.Unknown>;
|
|
2887
|
+
entities: Schema.Unknown;
|
|
2888
|
+
merge: Schema.Union<readonly [Schema.Literal<"replace">, Schema.Literal<"preserve-existing">]>;
|
|
2889
|
+
};
|
|
2890
|
+
MutationStarted: {
|
|
2891
|
+
requestId: Schema.String;
|
|
2892
|
+
optimistic: Schema.optional<Schema.$Array<Schema.Unknown>>;
|
|
2893
|
+
};
|
|
2894
|
+
MutationSucceeded: {
|
|
2895
|
+
requestId: Schema.String;
|
|
2896
|
+
entities: Schema.$Array<Schema.Struct<{
|
|
2897
|
+
readonly entity: Schema.String;
|
|
2898
|
+
readonly id: Schema.String;
|
|
2899
|
+
readonly values: Schema.$Record<Schema.String, Schema.Unknown>;
|
|
2900
|
+
}>>;
|
|
2901
|
+
connections: Schema.optional<Schema.$Array<Schema.Unknown>>;
|
|
2902
|
+
deleted: Schema.optional<Schema.$Array<Schema.Struct<{
|
|
2903
|
+
readonly entity: Schema.String;
|
|
2904
|
+
readonly id: Schema.String;
|
|
2905
|
+
}>>>;
|
|
2906
|
+
};
|
|
2907
|
+
MutationFailed: {
|
|
2908
|
+
requestId: Schema.String;
|
|
2909
|
+
error: Schema.Struct<{
|
|
2910
|
+
readonly _tag: Schema.String;
|
|
2911
|
+
readonly message: Schema.String;
|
|
2912
|
+
}>;
|
|
2913
|
+
};
|
|
2914
|
+
OverlayShown: {
|
|
2915
|
+
id: Schema.String;
|
|
2916
|
+
optimistic: Schema.$Array<Schema.Unknown>;
|
|
2917
|
+
};
|
|
2918
|
+
OverlayLifted: {
|
|
2919
|
+
id: Schema.String;
|
|
2920
|
+
};
|
|
2921
|
+
LiveReceived: {
|
|
2922
|
+
stream: Schema.String;
|
|
2923
|
+
event: Schema.Unknown;
|
|
2924
|
+
policy: Schema.optional<Schema.Unknown>;
|
|
2925
|
+
now: Schema.Number;
|
|
2926
|
+
};
|
|
2927
|
+
GapCleared: {
|
|
2928
|
+
stream: Schema.String;
|
|
2929
|
+
};
|
|
2930
|
+
ConnectionMerged: {
|
|
2931
|
+
connection: Schema.String;
|
|
2932
|
+
page: Schema.Unknown;
|
|
2933
|
+
refreshes: Schema.optional<Schema.Boolean>;
|
|
2934
|
+
};
|
|
2935
|
+
ConnectionInvalidated: {
|
|
2936
|
+
connection: Schema.String;
|
|
2937
|
+
};
|
|
2938
|
+
ConnectionRefreshed: {
|
|
2939
|
+
connection: Schema.String;
|
|
2940
|
+
};
|
|
2941
|
+
QueryFailed: {
|
|
2942
|
+
connection: Schema.String;
|
|
2943
|
+
error: Schema.Struct<{
|
|
2944
|
+
readonly _tag: Schema.String;
|
|
2945
|
+
readonly message: Schema.String;
|
|
2946
|
+
}>;
|
|
2947
|
+
};
|
|
2948
|
+
}>;
|
|
2949
|
+
/**
|
|
2950
|
+
* A bound domain under one wrapper variant of the application's union, the
|
|
2951
|
+
* same shape Foldkit gives a Submodel: `toParentMessage` wraps Remote's
|
|
2952
|
+
* Messages, the fold reduces them, and its `fetch`, `mutate`, and
|
|
2953
|
+
* `subscriptions` yield the wrapper. Spreading `Remote.messages` and
|
|
2954
|
+
* narrowing with `Remote.reduces` remains the shorter path for an
|
|
2955
|
+
* application whose `update` only reduces.
|
|
2956
|
+
*
|
|
2957
|
+
* @example
|
|
2958
|
+
* ```ts
|
|
2959
|
+
* const Message = defineMessageUnion({ GotRemoteMessage: { message: Remote.Message } })
|
|
2960
|
+
* const foldData = Remote.fold(Data, message => Message.GotRemoteMessage({ message }))
|
|
2961
|
+
*
|
|
2962
|
+
* const update = (model: Model, message: Message): Return =>
|
|
2963
|
+
* Message.match(message, {
|
|
2964
|
+
* GotRemoteMessage: ({ message }) => foldData(model, message),
|
|
2965
|
+
* })
|
|
2966
|
+
* const subscriptions = Subscription.make<Model, Message, RemoteClient>()(() =>
|
|
2967
|
+
* foldData.subscriptions({ page: Surface.at(ProjectPage, …) }),
|
|
2968
|
+
* )
|
|
2969
|
+
* ```
|
|
2970
|
+
*/
|
|
2971
|
+
fold: <Domain extends RemoteDomain<any, any, any, any, any>, ParentMessage>(domain: Domain, toParentMessage: (message: RemoteMessage) => ParentMessage) => RemoteFold<ModelOfDomain<Domain>, ParentMessage, Domain>;
|
|
2972
|
+
/**
|
|
2973
|
+
* A patch of one entity's values, for a mutation's `optimistic` list or an
|
|
2974
|
+
* overlay. Takes a `foldkit-entity` Entity or a Remote descriptor alike, so
|
|
2975
|
+
* the domain you declared once is the one you patch:
|
|
2976
|
+
* `Remote.patch(Project, 'p1', { name: 'Apollo II' })`. Values are the
|
|
2977
|
+
* fields' wire shape, as the server writes them; a relation is its ref key
|
|
2978
|
+
* (`'User:u1'`).
|
|
2979
|
+
*/
|
|
2980
|
+
patch: <E extends EntityLike>(entity: E, id: string, values: Partial<Schema.Struct.Encoded<FieldsOf<E>>>) => EntityPatch<NameOf<E>, FieldsOf<E>>;
|
|
2981
|
+
/**
|
|
2982
|
+
* A reference to one entity, for `ConnectionChange` and anything else that
|
|
2983
|
+
* names a row: `Remote.ref(Project, 'p3')`. Either kind of entity, as with
|
|
2984
|
+
* `Remote.patch`.
|
|
2985
|
+
*/
|
|
2986
|
+
ref: <E extends EntityLike>(entity: E, id: string) => EntityRef<NameOf<E>, FieldsOf<E>>;
|
|
2641
2987
|
/**
|
|
2642
2988
|
* Declares a Remote domain and binds it to its place in the application Model
|
|
2643
2989
|
* in one step; the result carries the application-facing operations. The
|
|
@@ -2665,6 +3011,17 @@ export declare const Remote: {
|
|
|
2665
3011
|
* rejects a selection for an entity this domain never declared.
|
|
2666
3012
|
*/
|
|
2667
3013
|
at: <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]>>;
|
|
3014
|
+
/**
|
|
3015
|
+
* The resume part for this domain, for `foldkit-ssr`'s `SSR.plan({ parts })`:
|
|
3016
|
+
* a server render sends what the plan's active Surfaces read from the store,
|
|
3017
|
+
* each connection with its boundaries and each live cursor with it, and
|
|
3018
|
+
* nothing else. `id` tells two domains in one application apart.
|
|
3019
|
+
*/
|
|
3020
|
+
resume: <AppModel, Store extends RemoteModel>(domain: {
|
|
3021
|
+
readonly store: ModelRef<AppModel, Store>;
|
|
3022
|
+
}, options?: {
|
|
3023
|
+
readonly id?: string | undefined;
|
|
3024
|
+
}) => RemoteResumePart<AppModel>;
|
|
2668
3025
|
/**
|
|
2669
3026
|
* A Projection node that reads a `RemoteData` value out of the store. The id
|
|
2670
3027
|
* is supplied by the caller, usually from a Surface's params. The assembled
|
|
@@ -2725,6 +3082,17 @@ export declare const Remote: {
|
|
|
2725
3082
|
* already being refreshed returns the same Model.
|
|
2726
3083
|
*/
|
|
2727
3084
|
refresh: <AppModel, Store extends RemoteModel>(bound: BoundRemote<AppModel, Store>, model: AppModel, target: Projection<AppModel, unknown> | Surface<AppModel, any, any, void>) => AppModel;
|
|
3085
|
+
/**
|
|
3086
|
+
* Forgets everything the server told this Model: values, tombstones,
|
|
3087
|
+
* unavailable fields, connections, live cursors, failures. What is known is
|
|
3088
|
+
* known for a principal, and this is the one boundary for a login, a logout
|
|
3089
|
+
* or a switch of organization: called from `update`, it performs no I/O, and
|
|
3090
|
+
* every active Surface's read and live entries restart, so the screen asks
|
|
3091
|
+
* again as whoever the client now is. A read or stream begun before is
|
|
3092
|
+
* interrupted rather than landing after. A mutation in flight is treated as
|
|
3093
|
+
* applied: its answer writes nothing here.
|
|
3094
|
+
*/
|
|
3095
|
+
forget: <AppModel, Store extends RemoteModel>(bound: BoundRemote<AppModel, Store>, model: AppModel) => AppModel;
|
|
2728
3096
|
/**
|
|
2729
3097
|
* Writes a read result into the store, recording each field's applied window.
|
|
2730
3098
|
* `requests` are the planned requirements the result answers.
|
|
@@ -2840,8 +3208,9 @@ export declare const Remote: {
|
|
|
2840
3208
|
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, {
|
|
2841
3209
|
readonly requirements: ReadonlyArray<Requirement>;
|
|
2842
3210
|
readonly cursor: LiveCursor;
|
|
3211
|
+
readonly floor: number;
|
|
2843
3212
|
}, RemoteClient>;
|
|
2844
3213
|
};
|
|
2845
3214
|
//#endregion
|
|
2846
|
-
export { Entity$1 as Entity };
|
|
3215
|
+
export { Entity$1 as Entity, type RemoteResumePart };
|
|
2847
3216
|
//# sourceMappingURL=index.d.mts.map
|