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/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 { Command } from "foldkit/command";
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. A
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 = 4;
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 = 3;
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']>, string>> : Optional extends true ? Schema.NullOr<Schema.Codec<EntityRef<Target['name']>, string>> : Schema.Codec<EntityRef<Target['name']>, string> : never;
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>, string>;
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>, string>;
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