foldkit-remote 0.1.0 → 0.3.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,5 +1,5 @@
1
1
  import { Context, Duration, Effect, Layer, Option, Schema, Stream } from "effect";
2
- import { ActiveSurface, ConnectionRequirement, Contract, Invalid, ModelRef, Projection, RelationRequirement, RelationRequirement as RelationRequirement$1, Requirement, Requirement as Requirement$1, Surface } from "foldkit-surface";
2
+ import { ActiveSurface, Contract, Invalid, Metadata, ModelRef, Projection, Surface } from "foldkit-surface";
3
3
  import { Rpc, RpcGroup } from "effect/unstable/rpc";
4
4
  import { KeyValueStore } from "effect/unstable/persistence";
5
5
  import { Command } from "foldkit/command";
@@ -171,6 +171,130 @@ export declare const Mutation: {
171
171
  }) => MutationDescriptor<Name, TypeOf<Input>, TypeOf<Output>>;
172
172
  };
173
173
  //#endregion
174
+ //#region src/query.d.ts
175
+ export type LiveInsertion = 'visible' | 'boundary' | 'invalidate' | 'ignore';
176
+ export interface LivePolicy {
177
+ readonly prepend?: LiveInsertion;
178
+ readonly append?: LiveInsertion;
179
+ }
180
+ export interface ConnectionSpec<Entity extends string = string> {
181
+ readonly entity: Entity;
182
+ readonly edgeKey?: Schema.Schema<unknown>;
183
+ readonly live?: LivePolicy;
184
+ }
185
+ export interface QueryWindow {
186
+ readonly first?: number | undefined;
187
+ readonly last?: number | undefined;
188
+ readonly after?: Cursor | undefined;
189
+ readonly before?: Cursor | undefined;
190
+ }
191
+ export interface QueryRef<Name extends string, Input> {
192
+ readonly query: Name;
193
+ readonly input: Input;
194
+ /** The input codec, so a `QueryRef` can encode itself without its descriptor. */
195
+ readonly Input: Schema.Codec<Input>;
196
+ readonly window: QueryWindow;
197
+ /** Connection identity: descriptor + canonical input, excluding the window. */
198
+ readonly identity: string;
199
+ }
200
+ export interface QueryDescriptor<Name extends string, Input, Result> {
201
+ readonly name: Name;
202
+ readonly Input: Schema.Codec<Input>;
203
+ readonly Result: Result;
204
+ readonly ref: (input: Input) => QueryRef<Name, Input>;
205
+ }
206
+ /** Stable stringify: object keys sorted, undefined-valued keys dropped, so equal inputs encode equally. */
207
+ export declare const stableStringify: (value: unknown) => string;
208
+ /**
209
+ * `Result: Project` (or anything with the entity's `name`, as `Query.connection`
210
+ * takes) means a connection over it; a `ConnectionSpec` names its `entity`
211
+ * instead and is the result as given.
212
+ */
213
+ export type ResultOf<Result> = Result extends {
214
+ readonly name: infer Entity extends string;
215
+ } ? ConnectionSpec<Entity> : Result;
216
+ export declare const Query: {
217
+ /** Declares the entity and options a query's result is a connection over. */
218
+ connection: <Entity extends string>(entity: {
219
+ readonly name: Entity;
220
+ }, options?: {
221
+ readonly edgeKey?: Schema.Schema<unknown>;
222
+ readonly live?: LivePolicy;
223
+ }) => ConnectionSpec<Entity>;
224
+ /**
225
+ * Declares a query. `Input` is a codec or the fields of the `Schema.Struct` it
226
+ * would be; `Result` is a `Query.connection(...)`, or the entity the result is
227
+ * a connection over.
228
+ */
229
+ make: <const Name extends string, Input extends SchemaOrFields, Result>(name: Name, config: {
230
+ readonly Input: Input;
231
+ readonly Result: Result;
232
+ }) => QueryDescriptor<Name, TypeOf<Input>, ResultOf<Result>>;
233
+ first: (count: number) => <R extends QueryRef<string, unknown>>(ref: R) => R;
234
+ last: (count: number) => <R extends QueryRef<string, unknown>>(ref: R) => R;
235
+ after: (cursor: Cursor) => <R extends QueryRef<string, unknown>>(ref: R) => R;
236
+ before: (cursor: Cursor) => <R extends QueryRef<string, unknown>>(ref: R) => R;
237
+ };
238
+ //#endregion
239
+ //#region src/requirement.d.ts
240
+ /**
241
+ * The slice required of an entity. `windows` carries a pagination window per
242
+ * relation field, and `relations` the slice required of each relation's
243
+ * target, so one requirement describes a whole selection graph.
244
+ */
245
+ export interface RelationRequirement {
246
+ readonly entity: string;
247
+ readonly fields: readonly string[];
248
+ readonly windows?: Readonly<Record<string, QueryWindow>> | undefined;
249
+ readonly relations?: Readonly<Record<string, RelationRequirement>> | undefined;
250
+ }
251
+ export interface Requirement extends RelationRequirement {
252
+ readonly id: string;
253
+ /** The projection also subscribes to changes of this entity (`Data.live`). */
254
+ readonly live?: boolean | undefined;
255
+ }
256
+ /**
257
+ * A query connection a projection reads: the page it first asks for and the
258
+ * slice it selects of each item. `identity` is the connection's (query plus
259
+ * canonical input, excluding the window).
260
+ */
261
+ export interface ConnectionRequirement {
262
+ readonly identity: string;
263
+ readonly window: QueryWindow;
264
+ readonly select: RelationRequirement;
265
+ }
266
+ /**
267
+ * Merges connection requirements for the same connection and window into one,
268
+ * unioning what they select of each item; the first keeps its other properties.
269
+ */
270
+ declare function mergeConnections(connections: readonly ConnectionRequirement[]): readonly ConnectionRequirement[];
271
+ /** Unions two relation slices for the same target: fields, windows, and nested relations. */
272
+ declare function mergeRelation(current: RelationRequirement, next: RelationRequirement): RelationRequirement;
273
+ /** Unions requirements for the same entity + id, dropping duplicate fields. */
274
+ declare function mergeRequirements(requirements: readonly Requirement[]): readonly Requirement[];
275
+ /**
276
+ * Requirement helpers, for the packages that plan and serve requirements.
277
+ * `merge` unions same-entity+id requirements; `mergeRelation` unions two
278
+ * slices of one target (fields, windows, nested relations).
279
+ */
280
+ export declare const Requirement: {
281
+ merge: typeof mergeRequirements;
282
+ mergeRelation: typeof mergeRelation;
283
+ mergeConnections: typeof mergeConnections;
284
+ };
285
+ /** Remote's entity requirements on a Projection, unioned per entity and id as nodes compose. */
286
+ export declare const RemoteRequirements: import("foldkit-surface").MetadataKey<Requirement>;
287
+ /** Remote's query connections on a Projection, unioned per connection and window. */
288
+ export declare const RemoteConnections: import("foldkit-surface").MetadataKey<ConnectionRequirement>;
289
+ /** The entity requirements a Projection carries. */
290
+ export declare const requirementsOf: (projection: {
291
+ readonly metadata: Metadata;
292
+ }) => ReadonlyArray<Requirement>;
293
+ /** The query connections a Projection carries. */
294
+ export declare const connectionsOf: (projection: {
295
+ readonly metadata: Metadata;
296
+ }) => ReadonlyArray<ConnectionRequirement>;
297
+ //#endregion
174
298
  //#region src/optimistic.d.ts
175
299
  export interface EntityLayer {
176
300
  readonly id: string;
@@ -275,71 +399,6 @@ export declare const settleFailure: (optimistic: OptimisticState, requestId: str
275
399
  */
276
400
  export declare const visibleItems: (connection: Connection, connectionId: string, overlays: ReadonlyArray<ConnectionOverlay>, store?: EntityStore) => ReadonlyArray<Edge>;
277
401
  //#endregion
278
- //#region src/query.d.ts
279
- export type LiveInsertion = 'visible' | 'boundary' | 'invalidate' | 'ignore';
280
- export interface LivePolicy {
281
- readonly prepend?: LiveInsertion;
282
- readonly append?: LiveInsertion;
283
- }
284
- export interface ConnectionSpec<Entity extends string = string> {
285
- readonly entity: Entity;
286
- readonly edgeKey?: Schema.Schema<unknown>;
287
- readonly live?: LivePolicy;
288
- }
289
- export interface QueryWindow {
290
- readonly first?: number | undefined;
291
- readonly last?: number | undefined;
292
- readonly after?: Cursor | undefined;
293
- readonly before?: Cursor | undefined;
294
- }
295
- export interface QueryRef<Name extends string, Input> {
296
- readonly query: Name;
297
- readonly input: Input;
298
- /** The input codec, so a `QueryRef` can encode itself without its descriptor. */
299
- readonly Input: Schema.Codec<Input>;
300
- readonly window: QueryWindow;
301
- /** Connection identity: descriptor + canonical input, excluding the window. */
302
- readonly identity: string;
303
- }
304
- export interface QueryDescriptor<Name extends string, Input, Result> {
305
- readonly name: Name;
306
- readonly Input: Schema.Codec<Input>;
307
- readonly Result: Result;
308
- readonly ref: (input: Input) => QueryRef<Name, Input>;
309
- }
310
- /** Stable stringify: object keys sorted, undefined-valued keys dropped, so equal inputs encode equally. */
311
- export declare const stableStringify: (value: unknown) => string;
312
- /**
313
- * `Result: Project` (or anything with the entity's `name`, as `Query.connection`
314
- * takes) means a connection over it; a `ConnectionSpec` names its `entity`
315
- * instead and is the result as given.
316
- */
317
- export type ResultOf<Result> = Result extends {
318
- readonly name: infer Entity extends string;
319
- } ? ConnectionSpec<Entity> : Result;
320
- export declare const Query: {
321
- /** Declares the entity and options a query's result is a connection over. */
322
- connection: <Entity extends string>(entity: {
323
- readonly name: Entity;
324
- }, options?: {
325
- readonly edgeKey?: Schema.Schema<unknown>;
326
- readonly live?: LivePolicy;
327
- }) => ConnectionSpec<Entity>;
328
- /**
329
- * Declares a query. `Input` is a codec or the fields of the `Schema.Struct` it
330
- * would be; `Result` is a `Query.connection(...)`, or the entity the result is
331
- * a connection over.
332
- */
333
- make: <const Name extends string, Input extends SchemaOrFields, Result>(name: Name, config: {
334
- readonly Input: Input;
335
- readonly Result: Result;
336
- }) => QueryDescriptor<Name, TypeOf<Input>, ResultOf<Result>>;
337
- first: (count: number) => <R extends QueryRef<string, unknown>>(ref: R) => R;
338
- last: (count: number) => <R extends QueryRef<string, unknown>>(ref: R) => R;
339
- after: (cursor: Cursor) => <R extends QueryRef<string, unknown>>(ref: R) => R;
340
- before: (cursor: Cursor) => <R extends QueryRef<string, unknown>>(ref: R) => R;
341
- };
342
- //#endregion
343
402
  //#region src/live.d.ts
344
403
  export type LiveCursor = number;
345
404
  export type LiveEvent = {
@@ -427,10 +486,10 @@ export declare const liveHasNext: (connection: Connection, state: LiveState, con
427
486
  /** A connection to keep: its identity, and what a page of it selects of each item, if a projection reads it. */
428
487
  export interface ConnectionRoot {
429
488
  readonly identity: string;
430
- readonly select?: RelationRequirement$1 | undefined;
489
+ readonly select?: RelationRequirement | undefined;
431
490
  }
432
491
  export interface RetentionRoots {
433
- readonly requirements: ReadonlyArray<Requirement$1>;
492
+ readonly requirements: ReadonlyArray<Requirement>;
434
493
  /** Connections (`QueryRef.identity`) to keep, with their edges' targets and what `select` reaches through them. */
435
494
  readonly connections: ReadonlyArray<ConnectionRoot>;
436
495
  }
@@ -468,9 +527,17 @@ export interface SnapshotOptions {
468
527
  }
469
528
  /** How a snapshot meets a store that already has entries. */
470
529
  export type MergePolicy = 'replace' | 'preserve-existing';
530
+ /**
531
+ * The snapshot text, or `undefined` when it would exceed `maxBytes`. Only the
532
+ * entity store goes in; pass `model.entities`, never the whole `RemoteModel`.
533
+ */
534
+ declare function dehydrate(store: EntityStore, options?: SnapshotOptions & {
535
+ readonly maxBytes?: undefined;
536
+ }): string;
537
+ declare function dehydrate(store: EntityStore, options?: SnapshotOptions): string | undefined;
471
538
  export declare const RemotePersistence: {
472
539
  /** The snapshot text, or `undefined` when it would exceed `maxBytes`. */
473
- dehydrate: (store: EntityStore, options?: SnapshotOptions) => string | undefined;
540
+ dehydrate: typeof dehydrate;
474
541
  /** The store a snapshot text holds, or `undefined` when it is refused. */
475
542
  hydrate: (raw: string | undefined | null, options?: SnapshotOptions) => EntityStore | undefined;
476
543
  /** A snapshot brought into a store by policy. */
@@ -531,11 +598,11 @@ export declare const WindowSchema: Schema.Struct<{
531
598
  export declare const MAX_FIELDS_PER_REQUEST = 256;
532
599
  /** A selection may not nest relations deeper than this; the wire refuses more. */
533
600
  export declare const MAX_RELATION_DEPTH = 8;
534
- export declare const RelationRequest: Schema.Codec<RelationRequirement$1, RelationRequirement$1, never, never>;
535
- export declare const ReadRequest: Schema.Codec<Requirement$1, Requirement$1>;
601
+ export declare const RelationRequest: Schema.Codec<RelationRequirement, RelationRequirement, never, never>;
602
+ export declare const ReadRequest: Schema.Codec<Requirement, Requirement>;
536
603
  export declare const ReadBatch: Schema.Struct<{
537
604
  readonly version: Schema.Number;
538
- readonly requests: Schema.$Array<Schema.Codec<Requirement$1, Requirement$1, never, never>>;
605
+ readonly requests: Schema.$Array<Schema.Codec<Requirement, Requirement, never, never>>;
539
606
  }>;
540
607
  export declare const NormalizedEntity: Schema.Struct<{
541
608
  readonly entity: Schema.String;
@@ -608,7 +675,7 @@ export declare const MutationResult: Schema.Struct<{
608
675
  }>;
609
676
  export declare const LiveRequirement: Schema.Struct<{
610
677
  readonly version: Schema.Number;
611
- readonly requirements: Schema.$Array<Schema.Codec<Requirement$1, Requirement$1, never, never>>;
678
+ readonly requirements: Schema.$Array<Schema.Codec<Requirement, Requirement, never, never>>;
612
679
  /** Resume cursor; events at or before it are duplicates. */
613
680
  readonly after: Schema.Number;
614
681
  }>;
@@ -654,7 +721,7 @@ export declare const LiveChange: Schema.Union<readonly [Schema.Struct<{
654
721
  }>]>;
655
722
  export declare const Read: Rpc.Rpc<"FoldkitRemoteRead", Schema.Struct<{
656
723
  readonly version: Schema.Number;
657
- readonly requests: Schema.$Array<Schema.Codec<Requirement$1, Requirement$1, never, never>>;
724
+ readonly requests: Schema.$Array<Schema.Codec<Requirement, Requirement, never, never>>;
658
725
  }>, Schema.Struct<{
659
726
  readonly entities: Schema.$Array<Schema.Struct<{
660
727
  readonly entity: Schema.String;
@@ -696,7 +763,7 @@ export declare const Mutate: Rpc.Rpc<"FoldkitRemoteMutate", Schema.Struct<{
696
763
  }>, typeof RemoteMutationError, never, never>;
697
764
  export declare const Live: Rpc.Rpc<"FoldkitRemoteLive", Schema.Struct<{
698
765
  readonly version: Schema.Number;
699
- readonly requirements: Schema.$Array<Schema.Codec<Requirement$1, Requirement$1, never, never>>;
766
+ readonly requirements: Schema.$Array<Schema.Codec<Requirement, Requirement, never, never>>;
700
767
  /** Resume cursor; events at or before it are duplicates. */
701
768
  readonly after: Schema.Number;
702
769
  }>, import("effect/unstable/rpc/RpcSchema").Stream<Schema.Union<readonly [Schema.Struct<{
@@ -819,7 +886,7 @@ export declare const QueryRpc: Rpc.Rpc<"FoldkitRemoteQuery", Schema.Struct<{
819
886
  }>, typeof RemoteQueryError, never, never>;
820
887
  export declare const RemoteRpc: RpcGroup.RpcGroup<Rpc.Rpc<"FoldkitRemoteRead", Schema.Struct<{
821
888
  readonly version: Schema.Number;
822
- readonly requests: Schema.$Array<Schema.Codec<Requirement$1, Requirement$1, never, never>>;
889
+ readonly requests: Schema.$Array<Schema.Codec<Requirement, Requirement, never, never>>;
823
890
  }>, Schema.Struct<{
824
891
  readonly entities: Schema.$Array<Schema.Struct<{
825
892
  readonly entity: Schema.String;
@@ -859,7 +926,7 @@ export declare const RemoteRpc: RpcGroup.RpcGroup<Rpc.Rpc<"FoldkitRemoteRead", S
859
926
  }>]>>>;
860
927
  }>, typeof RemoteMutationError, never, never> | Rpc.Rpc<"FoldkitRemoteLive", Schema.Struct<{
861
928
  readonly version: Schema.Number;
862
- readonly requirements: Schema.$Array<Schema.Codec<Requirement$1, Requirement$1, never, never>>;
929
+ readonly requirements: Schema.$Array<Schema.Codec<Requirement, Requirement, never, never>>;
863
930
  /** Resume cursor; events at or before it are duplicates. */
864
931
  readonly after: Schema.Number;
865
932
  }>, import("effect/unstable/rpc/RpcSchema").Stream<Schema.Union<readonly [Schema.Struct<{
@@ -956,7 +1023,7 @@ export declare const remoteErrorSchema: Schema.Struct<{
956
1023
  readonly _tag: Schema.String;
957
1024
  readonly message: Schema.String;
958
1025
  }>;
959
- export declare const remoteDataSchema: <A>(value: Schema.Schema<A>) => Schema.Schema<RemoteData<A>>;
1026
+ export declare const remoteDataSchema: <A>(value: Schema.Schema<A>) => Schema.Codec<RemoteData<A>, unknown>;
960
1027
  export declare const RemoteData: {
961
1028
  /**
962
1029
  * The `RemoteData` schema for a value schema. Exposed so a `RemoteData` can
@@ -977,8 +1044,8 @@ export declare const RemoteData: {
977
1044
  //#endregion
978
1045
  //#region src/model.d.ts
979
1046
  /**
980
- * The normalized cache as a Foldkit Submodel. `Remote.make` returns a schema and
981
- * an `update` for this shape; `Remote.update` reconciles every producer.
1047
+ * The normalized cache as a Foldkit Submodel. `Remote.define` returns a schema
1048
+ * and an `update` for this shape; `updateRemote` reconciles every producer.
982
1049
  */
983
1050
  export interface RemoteModel {
984
1051
  /** Entity values, presence, staleness, tombstones, and applied windows. */
@@ -993,24 +1060,47 @@ export interface RemoteModel {
993
1060
  readonly mutations: MutationState;
994
1061
  /** Streams whose live cursor fell behind; the caller should resync or refetch. */
995
1062
  readonly gaps: ReadonlySet<string>;
1063
+ /**
1064
+ * The `entity\0id\0field` marks a read is currently fetching. A field absent
1065
+ * from the store reads as `Loading` while its mark is here and `Initial`
1066
+ * otherwise, which is what separates "being fetched" from "nothing is
1067
+ * fetching this".
1068
+ */
1069
+ readonly loading: ReadonlySet<string>;
1070
+ /**
1071
+ * `Remote.refresh` generations. The read entries restart when `requested`
1072
+ * moves, so a read sent before a refresh cannot land after it; `started` is
1073
+ * the latest generation a read began under.
1074
+ */
1075
+ readonly refresh: {
1076
+ readonly requested: number;
1077
+ readonly started: number;
1078
+ };
996
1079
  }
997
1080
  export declare const initialRemoteModel: RemoteModel;
998
- export declare const remoteModelSchema: () => Schema.Schema<RemoteModel>;
999
- /** The submodel's Messages; each reduces to `RemoteModel` through `Remote.update`. */
1081
+ export declare const remoteModelSchema: () => Schema.Codec<RemoteModel, unknown>;
1082
+ /** The submodel's Messages; each reduces to `RemoteModel` through `updateRemote`. */
1000
1083
  export type RemoteMessage = {
1001
1084
  readonly _tag: 'ReadReceived';
1002
- readonly requests: readonly Requirement$1[];
1085
+ readonly requests: readonly Requirement[];
1003
1086
  readonly result: Schema.Schema.Type<typeof ReadBatchResult>;
1004
1087
  readonly now: number;
1005
1088
  } | {
1006
1089
  readonly _tag: 'ReadFailed';
1007
- readonly requests: readonly Requirement$1[];
1090
+ readonly requests: readonly Requirement[];
1008
1091
  readonly error: RemoteError;
1009
1092
  } |
1010
1093
  /** A refetch of present fields began; they read as `Refreshing` until it lands. */
1011
1094
  {
1012
1095
  readonly _tag: 'RefreshStarted';
1013
- readonly requests: readonly Requirement$1[];
1096
+ readonly requests: readonly Requirement[];
1097
+ } |
1098
+ /** A read began; the fields it asks for that the store lacks read as `Loading`. */
1099
+ {
1100
+ readonly _tag: 'ReadStarted';
1101
+ readonly requests: readonly Requirement[];
1102
+ /** The refresh generation the read was planned under. */
1103
+ readonly refresh?: number | undefined;
1014
1104
  } |
1015
1105
  /** The active Surfaces' roots changed; everything they do not reach is collected. */
1016
1106
  {
@@ -1067,10 +1157,10 @@ export type RemoteMessage = {
1067
1157
  readonly error: RemoteError;
1068
1158
  };
1069
1159
  export declare const retentionRootsSchema: Schema.Struct<{
1070
- readonly requirements: Schema.$Array<Schema.Codec<Requirement$1, Requirement$1, never, never>>;
1160
+ readonly requirements: Schema.$Array<Schema.Codec<Requirement, Requirement, never, never>>;
1071
1161
  readonly connections: Schema.$Array<Schema.Struct<{
1072
1162
  readonly identity: Schema.String;
1073
- readonly select: Schema.optional<Schema.Codec<import("foldkit-surface").RelationRequirement, import("foldkit-surface").RelationRequirement, never, never>>;
1163
+ readonly select: Schema.optional<Schema.Codec<RelationRequirement, RelationRequirement, never, never>>;
1074
1164
  }>>;
1075
1165
  }>;
1076
1166
  /**
@@ -1079,7 +1169,7 @@ export declare const retentionRootsSchema: Schema.Struct<{
1079
1169
  */
1080
1170
  export declare const remoteMessageCases: {
1081
1171
  ReadReceived: {
1082
- requests: Schema.$Array<Schema.Codec<Requirement$1, Requirement$1, never, never>>;
1172
+ requests: Schema.$Array<Schema.Codec<Requirement, Requirement, never, never>>;
1083
1173
  result: Schema.Struct<{
1084
1174
  readonly entities: Schema.$Array<Schema.Struct<{
1085
1175
  readonly entity: Schema.String;
@@ -1090,21 +1180,25 @@ export declare const remoteMessageCases: {
1090
1180
  now: Schema.Number;
1091
1181
  };
1092
1182
  ReadFailed: {
1093
- requests: Schema.$Array<Schema.Codec<Requirement$1, Requirement$1, never, never>>;
1183
+ requests: Schema.$Array<Schema.Codec<Requirement, Requirement, never, never>>;
1094
1184
  error: Schema.Struct<{
1095
1185
  readonly _tag: Schema.String;
1096
1186
  readonly message: Schema.String;
1097
1187
  }>;
1098
1188
  };
1099
1189
  RefreshStarted: {
1100
- requests: Schema.$Array<Schema.Codec<Requirement$1, Requirement$1, never, never>>;
1190
+ requests: Schema.$Array<Schema.Codec<Requirement, Requirement, never, never>>;
1191
+ };
1192
+ ReadStarted: {
1193
+ requests: Schema.$Array<Schema.Codec<Requirement, Requirement, never, never>>;
1194
+ refresh: Schema.optional<Schema.Number>;
1101
1195
  };
1102
1196
  RetentionChanged: {
1103
1197
  roots: Schema.Struct<{
1104
- readonly requirements: Schema.$Array<Schema.Codec<Requirement$1, Requirement$1, never, never>>;
1198
+ readonly requirements: Schema.$Array<Schema.Codec<Requirement, Requirement, never, never>>;
1105
1199
  readonly connections: Schema.$Array<Schema.Struct<{
1106
1200
  readonly identity: Schema.String;
1107
- readonly select: Schema.optional<Schema.Codec<import("foldkit-surface").RelationRequirement, import("foldkit-surface").RelationRequirement, never, never>>;
1201
+ readonly select: Schema.optional<Schema.Codec<RelationRequirement, RelationRequirement, never, never>>;
1108
1202
  }>>;
1109
1203
  }>;
1110
1204
  };
@@ -1179,6 +1273,12 @@ export declare const isRemoteMessage: <M extends {
1179
1273
  readonly _tag: RemoteMessageTag;
1180
1274
  }>;
1181
1275
  export declare const remoteMessageSchema: Schema.Schema<RemoteMessage>;
1276
+ /**
1277
+ * Whether a read is fetching any of `fields` for this entity. Consulted only
1278
+ * when the store lacks the value, which is what separates `Loading` from
1279
+ * `Initial`.
1280
+ */
1281
+ export declare const isLoading: (model: RemoteModel, entity: string, id: string, fields: ReadonlyArray<string>) => boolean;
1182
1282
  /**
1183
1283
  * The pure reducer all four producers share. A live event that arrives ahead of
1184
1284
  * its cursor is a gap: it is not applied, and the stream is recorded so the host
@@ -1192,7 +1292,7 @@ export declare const updateRemote: (model: RemoteModel, message: RemoteMessage)
1192
1292
  * accumulates rather than replaces. Every read path should use this rather than
1193
1293
  * reducing `writeEntity` by hand.
1194
1294
  */
1195
- export declare const writeRead: (store: EntityStore, requests: ReadonlyArray<Requirement$1>, result: Schema.Schema.Type<typeof ReadBatchResult>, now?: number) => EntityStore;
1295
+ export declare const writeRead: (store: EntityStore, requests: ReadonlyArray<Requirement>, result: Schema.Schema.Type<typeof ReadBatchResult>, now?: number) => EntityStore;
1196
1296
  //#endregion
1197
1297
  //#region src/coalesce.d.ts
1198
1298
  type Batch = Schema.Schema.Type<typeof ReadBatch>;
@@ -1210,7 +1310,7 @@ export interface CoalesceOptions {
1210
1310
  readonly window?: Duration.Input | undefined;
1211
1311
  }
1212
1312
  /** A stable key for a requirement, so an equal requirement in flight is joined. */
1213
- export declare const requirementKey: (requirement: Requirement$1) => string;
1313
+ export declare const requirementKey: (requirement: Requirement) => string;
1214
1314
  /**
1215
1315
  * Wraps a raw query so that an identical query in flight (same query, input,
1216
1316
  * and window) is joined rather than run again; distinct queries run
@@ -1230,7 +1330,7 @@ declare const RemoteClient_base: Context.ServiceClass<RemoteClient, "foldkit-rem
1230
1330
  readonly query: (request: Schema.Schema.Type<typeof QueryRequest>) => Effect.Effect<Schema.Schema.Type<typeof QueryResult>, RemoteQueryError>;
1231
1331
  readonly mutate: (request: Schema.Schema.Type<typeof MutationRequest>) => Effect.Effect<Schema.Schema.Type<typeof MutationResult>, RemoteMutationError>;
1232
1332
  readonly live: (request: {
1233
- readonly requirements: ReadonlyArray<Requirement$1>;
1333
+ readonly requirements: ReadonlyArray<Requirement>;
1234
1334
  readonly after: LiveCursor;
1235
1335
  }) => Stream.Stream<LiveEvent, RemoteLiveError | RemoteProtocolError>;
1236
1336
  }>;
@@ -1305,7 +1405,7 @@ export declare const refParts: (encoded: string) => RefParts;
1305
1405
  */
1306
1406
  export declare const refsIn: (value: unknown) => ReadonlyArray<RefParts>;
1307
1407
  /** The refs in a relation value that name the relation's target entity, and no other. */
1308
- export declare const targetsOf: (value: unknown, relation: RelationRequirement$1) => ReadonlyArray<RefParts>;
1408
+ export declare const targetsOf: (value: unknown, relation: RelationRequirement) => ReadonlyArray<RefParts>;
1309
1409
  export declare const isRefPage: (value: unknown) => value is RefPageValue;
1310
1410
  /**
1311
1411
  * The relation shape an entity field schema declares: a ref (`one`), a nullable
@@ -1334,7 +1434,7 @@ export interface Selection<Value, Name extends string = string, Shape extends 'e
1334
1434
  /** Pagination windows for nested relation fields, keyed by field name. */
1335
1435
  readonly connections?: Readonly<Record<string, QueryWindow>> | undefined;
1336
1436
  /** The slice required of each nested relation's target, keyed by field name. */
1337
- readonly relations?: Readonly<Record<string, RelationRequirement$1>> | undefined;
1437
+ readonly relations?: Readonly<Record<string, RelationRequirement>> | undefined;
1338
1438
  /** Present when this selection is itself a paginated relation. */
1339
1439
  readonly window?: QueryWindow | undefined;
1340
1440
  /** Phantom: see `Shape`. */
@@ -1356,7 +1456,7 @@ type NestedValue<Field, Sel> = Sel extends Selection<infer V, string, 'connectio
1356
1456
  export 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]>; }>;
1357
1457
  export declare const pageSchema: (item: AnySchema) => AnySchema;
1358
1458
  /** The requirement a nested selection contributes for its relation's target. */
1359
- export declare const relationOf: (selection: Selection<unknown>) => RelationRequirement$1;
1459
+ export declare const relationOf: (selection: Selection<unknown>) => RelationRequirement;
1360
1460
  export declare const Selection: {
1361
1461
  /**
1362
1462
  * The fields to read of `entity`. A nested `Selection` on a relation field
@@ -1383,7 +1483,7 @@ interface Assembled {
1383
1483
  * not present yet. A tombstoned target reads as `null` (or is dropped from a
1384
1484
  * list), so the Selection's codec decides whether that is a failure.
1385
1485
  */
1386
- export declare const assemble: (store: EntityStore, key: string, requirement: RelationRequirement$1) => Assembled | undefined;
1486
+ export declare const assemble: (store: EntityStore, key: string, requirement: RelationRequirement) => Assembled | undefined;
1387
1487
  //#endregion
1388
1488
  //#region src/entity.d.ts
1389
1489
  export type AnySchema = Schema.Schema<unknown>;
@@ -1483,7 +1583,7 @@ export declare const inspectRemote: (model: RemoteModel) => RemoteInspection;
1483
1583
  export declare const inspectEntity: (model: RemoteModel, key: string) => RemoteInspection["entities"][number] | undefined;
1484
1584
  //#endregion
1485
1585
  //#region src/plan.d.ts
1486
- type Window = NonNullable<Requirement$1['windows']>[string];
1586
+ type Window = NonNullable<Requirement['windows']>[string];
1487
1587
  /** A stable key for a window, so two equal windows compare equal. */
1488
1588
  export declare const windowKey: (window: Window) => string;
1489
1589
  export interface PlanFreshness {
@@ -1496,7 +1596,7 @@ export interface PlanOptions {
1496
1596
  /** Plan every requested field, present or not, tombstoned or not. */
1497
1597
  readonly force?: boolean | undefined;
1498
1598
  }
1499
- export declare const plan: (store: EntityStore, requirements: readonly Requirement$1[], options?: PlanOptions) => ReadonlyArray<Requirement$1>;
1599
+ export declare const plan: (store: EntityStore, requirements: readonly Requirement[], options?: PlanOptions) => ReadonlyArray<Requirement>;
1500
1600
  //#endregion
1501
1601
  //#region src/policy.d.ts
1502
1602
  export type RemotePolicy =
@@ -1555,7 +1655,7 @@ export interface RemoteDescriptor<Entities extends readonly EntityDescriptor<any
1555
1655
  readonly entities: Entities;
1556
1656
  readonly queries: Queries;
1557
1657
  readonly mutations: Mutations;
1558
- readonly Model: Schema.Schema<RemoteModel>;
1658
+ readonly Model: Schema.Codec<RemoteModel, unknown>;
1559
1659
  readonly initial: RemoteModel;
1560
1660
  readonly Message: Schema.Schema<RemoteMessage>;
1561
1661
  readonly update: (model: RemoteModel, message: RemoteMessage) => RemoteModel;
@@ -1592,6 +1692,14 @@ export interface DomainMutateOptions {
1592
1692
  }
1593
1693
  /** A Foldkit Subscription entry of the Remote domain, emitting its Messages through `RemoteClient`. */
1594
1694
  export type RemoteEntry<AppModel, Dependencies> = EntryWithoutKeepAlive<AppModel, RemoteMessage, Dependencies, RemoteClient>;
1695
+ /**
1696
+ * The entries `RemoteDomain.subscriptions` returns for an active record: a read
1697
+ * and a live entry per key (the live one idles when nothing is read live), and
1698
+ * `retain`.
1699
+ */
1700
+ export type SubscriptionEntries<AppModel, Active> = { readonly [K in keyof Active & (string | number) as `${K}.read` | `${K}.live`]: RemoteEntry<AppModel, any>; } & {
1701
+ readonly retain: RemoteEntry<AppModel, any>;
1702
+ };
1595
1703
  export interface SubscriptionsOptions extends ObserveOptions, LiveOptions, RetainOptions {}
1596
1704
  /** What `RemoteDomain.mutate` hands `update`: the Model with the request started, and the Command that settles it. */
1597
1705
  export interface MutationStarted<AppModel> {
@@ -1630,6 +1738,8 @@ export interface RemoteDomain<AppModel, Store extends RemoteModel, Entities exte
1630
1738
  previous<Name extends string, Input>(model: AppModel, projection: QueryProjection<AppModel, any, Name, Input>): QueryRef<Name, Input> | undefined;
1631
1739
  /** A Command that runs the query and yields the `ConnectionMerged` (or `QueryFailed`) that reduces it: "load more". */
1632
1740
  fetch(ref: QueryRef<string, unknown>): Command<RemoteMessage, never, RemoteClient>;
1741
+ /** `Remote.refresh`: marks what a projection or a Surface requires as due, for its read entry to refetch. */
1742
+ refresh(model: AppModel, target: Projection<AppModel, unknown> | Surface<AppModel, any, any, void>): AppModel;
1633
1743
  /**
1634
1744
  * The Foldkit Subscription entries for the active Surfaces, keyed for
1635
1745
  * `Subscription.make`: a read entry per Surface (`Remote.observe`), a live
@@ -1638,9 +1748,9 @@ export interface RemoteDomain<AppModel, Store extends RemoteModel, Entities exte
1638
1748
  * Surface's params are a function of the Model (`Surface.at`), so what is
1639
1749
  * fetched, subscribed, and retained follows the Model.
1640
1750
  */
1641
- subscriptions(active: Readonly<Record<string, ActiveSurface<AppModel> | Surface<AppModel, any, any, void>>>, options?: SubscriptionsOptions): Readonly<Record<string, RemoteEntry<AppModel, any>>>;
1751
+ subscriptions<const Active extends Readonly<Record<string, ActiveSurface<AppModel> | Surface<AppModel, any, any, void>>>>(active: Active, options?: SubscriptionsOptions): SubscriptionEntries<AppModel, Active>;
1642
1752
  /** `Remote.plan`: the requirements the store does not satisfy. */
1643
- plan<Value>(model: AppModel, projection: Projection<AppModel, Value>, options?: PlanOptions): ReadonlyArray<Requirement$1>;
1753
+ plan<Value>(model: AppModel, projection: Projection<AppModel, Value>, options?: PlanOptions): ReadonlyArray<Requirement>;
1644
1754
  /** `Remote.storeOf`: the visible store, base under the pending optimistic layers. */
1645
1755
  storeOf(model: AppModel): EntityStore;
1646
1756
  /**
@@ -1656,7 +1766,7 @@ export interface RemoteDomain<AppModel, Store extends RemoteModel, Entities exte
1656
1766
  * mutation sequence unless `options.requestId` is given.
1657
1767
  */
1658
1768
  mutate<M extends MutationDescriptor<any, any, any>>(model: AppModel, mutation: M & Registered<M['name'], MutationName<Mutations[number]>, 'Mutation'>, input: MutationInput<M>, options?: DomainMutateOptions): MutationStarted<AppModel>;
1659
- /** `Remote.update` on the bound slice: reduces one of Remote's Messages, as `RemoteMessage` or as the application's union constructs it. */
1769
+ /** `updateRemote` on the bound slice: reduces one of Remote's Messages, as `RemoteMessage` or as the application's union constructs it. */
1660
1770
  reduce(model: AppModel, message: RemoteMessage | RemoteMessageInput): AppModel;
1661
1771
  /** `Remote.inspect` of the bound slice. */
1662
1772
  inspect(model: AppModel): RemoteInspection;
@@ -1723,12 +1833,14 @@ declare const PlannedQuery: Schema.Struct<{
1723
1833
  }>;
1724
1834
  /** The dependencies of the read entry: the entity fields to read and the queries to run. */
1725
1835
  export interface ReadDependencies {
1726
- readonly requirements: ReadonlyArray<Requirement$1>;
1836
+ readonly requirements: ReadonlyArray<Requirement>;
1727
1837
  readonly queries: ReadonlyArray<Schema.Schema.Type<typeof PlannedQuery>>;
1838
+ /** The Model's requested refresh generation: a refresh restarts the entry. */
1839
+ readonly refresh: number;
1728
1840
  }
1729
1841
  export declare const Remote: {
1730
1842
  /** The submodel's schema, the same for every domain; embed it in the application Model. */
1731
- Model: Schema.Schema<RemoteModel>;
1843
+ Model: Schema.Codec<RemoteModel, unknown, never, never>;
1732
1844
  /** The submodel's initial value. */
1733
1845
  initial: RemoteModel;
1734
1846
  /**
@@ -1738,7 +1850,7 @@ export declare const Remote: {
1738
1850
  */
1739
1851
  messages: {
1740
1852
  ReadReceived: {
1741
- requests: Schema.$Array<Schema.Codec<Requirement$1, Requirement$1, never, never>>;
1853
+ requests: Schema.$Array<Schema.Codec<Requirement, Requirement, never, never>>;
1742
1854
  result: Schema.Struct<{
1743
1855
  readonly entities: Schema.$Array<Schema.Struct<{
1744
1856
  readonly entity: Schema.String;
@@ -1749,18 +1861,22 @@ export declare const Remote: {
1749
1861
  now: Schema.Number;
1750
1862
  };
1751
1863
  ReadFailed: {
1752
- requests: Schema.$Array<Schema.Codec<Requirement$1, Requirement$1, never, never>>;
1864
+ requests: Schema.$Array<Schema.Codec<Requirement, Requirement, never, never>>;
1753
1865
  error: Schema.Struct<{
1754
1866
  readonly _tag: Schema.String;
1755
1867
  readonly message: Schema.String;
1756
1868
  }>;
1757
1869
  };
1758
1870
  RefreshStarted: {
1759
- requests: Schema.$Array<Schema.Codec<Requirement$1, Requirement$1, never, never>>;
1871
+ requests: Schema.$Array<Schema.Codec<Requirement, Requirement, never, never>>;
1872
+ };
1873
+ ReadStarted: {
1874
+ requests: Schema.$Array<Schema.Codec<Requirement, Requirement, never, never>>;
1875
+ refresh: Schema.optional<Schema.Number>;
1760
1876
  };
1761
1877
  RetentionChanged: {
1762
1878
  roots: Schema.Struct<{
1763
- readonly requirements: Schema.$Array<Schema.Codec<Requirement$1, Requirement$1, never, never>>;
1879
+ readonly requirements: Schema.$Array<Schema.Codec<Requirement, Requirement, never, never>>;
1764
1880
  readonly connections: Schema.$Array<Schema.Struct<{
1765
1881
  readonly identity: Schema.String;
1766
1882
  readonly select: Schema.optional<Schema.Codec<RelationRequirement, RelationRequirement, never, never>>;
@@ -1865,7 +1981,7 @@ export declare const Remote: {
1865
1981
  * remote store does not satisfy, under `options` (freshness, force). A
1866
1982
  * Surface's projection is `surface.projection(params)`.
1867
1983
  */
1868
- plan: <AppModel, Store extends RemoteModel, Value>(bound: BoundRemote<AppModel, Store>, model: AppModel, projection: Projection<AppModel, Value>, options?: PlanOptions) => ReadonlyArray<Requirement$1>;
1984
+ plan: <AppModel, Store extends RemoteModel, Value>(bound: BoundRemote<AppModel, Store>, model: AppModel, projection: Projection<AppModel, Value>, options?: PlanOptions) => ReadonlyArray<Requirement>;
1869
1985
  /**
1870
1986
  * The queries a projection needs run before its connections read: those the
1871
1987
  * Model does not hold, or holds stale (every one under `force`).
@@ -1884,11 +2000,30 @@ export declare const Remote: {
1884
2000
  * is the clock it reads, so the planner itself stays pure and time-injected.
1885
2001
  */
1886
2002
  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>>, RemoteReadError | RemoteProtocolError, RemoteClient>;
2003
+ /**
2004
+ * Marks what a projection, or a Surface without params, requires as due again.
2005
+ * Called from `update`: every selected field the store holds reads `Refreshing`,
2006
+ * and every loaded query connection is invalidated. Nothing is fetched here.
2007
+ * The read entries `Data.subscriptions` derives refetch it, because a stale
2008
+ * field or connection is planned again under every policy, so a refresh never
2009
+ * sends a request beside the entry already observing the same data.
2010
+ *
2011
+ * A refreshed connection's page replaces its pages, so items the server removed
2012
+ * or reordered follow it; pages loaded past the first are dropped and paged
2013
+ * again with `next`/`previous`. The read entries restart, so a read or query
2014
+ * sent before the refresh is not applied after it.
2015
+ *
2016
+ * The projection must be observed, which it is while it is on screen. Live
2017
+ * subscriptions are left as they are. For data nothing observes (SSR, tests),
2018
+ * use `Remote.prefetch` with `RemotePolicy.networkOnly`. Refreshing what is
2019
+ * already being refreshed returns the same Model.
2020
+ */
2021
+ refresh: <AppModel, Store extends RemoteModel>(bound: BoundRemote<AppModel, Store>, model: AppModel, target: Projection<AppModel, unknown> | Surface<AppModel, any, any, void>) => AppModel;
1887
2022
  /**
1888
2023
  * Writes a read result into the store, recording each field's applied window.
1889
2024
  * `requests` are the planned requirements the result answers.
1890
2025
  */
1891
- writeRead: (store: EntityStore, requests: ReadonlyArray<Requirement$1>, result: Schema.Schema.Type<typeof ReadBatchResult>, now?: number) => EntityStore;
2026
+ writeRead: (store: EntityStore, requests: ReadonlyArray<Requirement>, result: Schema.Schema.Type<typeof ReadBatchResult>, now?: number) => EntityStore;
1892
2027
  /**
1893
2028
  * Adapts an Effect RPC client for `RemoteRpc` to `RemoteClient`, so an
1894
2029
  * application provides the transport's RPC layer instead of writing the
@@ -1951,7 +2086,7 @@ export declare const Remote: {
1951
2086
  /**
1952
2087
  * Runs a mutation through `RemoteClient`, decoding its typed Output and
1953
2088
  * returning the result's normalized patches so the caller can reconcile them
1954
- * through `Remote.update`'s `MutationSucceeded` (or `Remote.mutateInto`).
2089
+ * through `updateRemote`'s `MutationSucceeded` (or `Remote.mutateInto`).
1955
2090
  */
1956
2091
  mutate: <Name extends string, Input, Output>(mutation: MutationDescriptor<Name, Input, Output>, input: Input, requestId: string) => Effect.Effect<{
1957
2092
  output: Output;
@@ -1991,10 +2126,10 @@ export declare const Remote: {
1991
2126
  * from `RemoteModel.live`, so the application tracks no cursor of its own.
1992
2127
  */
1993
2128
  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, {
1994
- readonly requirements: ReadonlyArray<Requirement$1>;
2129
+ readonly requirements: ReadonlyArray<Requirement>;
1995
2130
  readonly cursor: LiveCursor;
1996
2131
  }, RemoteClient>;
1997
2132
  };
1998
2133
  //#endregion
1999
- export { Entity$1 as Entity, type RelationRequirement, type Requirement };
2134
+ export { Entity$1 as Entity };
2000
2135
  //# sourceMappingURL=index.d.mts.map