foldkit-remote 0.2.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
@@ -1000,29 +1067,40 @@ export interface RemoteModel {
1000
1067
  * fetching this".
1001
1068
  */
1002
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
+ };
1003
1079
  }
1004
1080
  export declare const initialRemoteModel: RemoteModel;
1005
- export declare const remoteModelSchema: () => Schema.Schema<RemoteModel>;
1081
+ export declare const remoteModelSchema: () => Schema.Codec<RemoteModel, unknown>;
1006
1082
  /** The submodel's Messages; each reduces to `RemoteModel` through `updateRemote`. */
1007
1083
  export type RemoteMessage = {
1008
1084
  readonly _tag: 'ReadReceived';
1009
- readonly requests: readonly Requirement$1[];
1085
+ readonly requests: readonly Requirement[];
1010
1086
  readonly result: Schema.Schema.Type<typeof ReadBatchResult>;
1011
1087
  readonly now: number;
1012
1088
  } | {
1013
1089
  readonly _tag: 'ReadFailed';
1014
- readonly requests: readonly Requirement$1[];
1090
+ readonly requests: readonly Requirement[];
1015
1091
  readonly error: RemoteError;
1016
1092
  } |
1017
1093
  /** A refetch of present fields began; they read as `Refreshing` until it lands. */
1018
1094
  {
1019
1095
  readonly _tag: 'RefreshStarted';
1020
- readonly requests: readonly Requirement$1[];
1096
+ readonly requests: readonly Requirement[];
1021
1097
  } |
1022
1098
  /** A read began; the fields it asks for that the store lacks read as `Loading`. */
1023
1099
  {
1024
1100
  readonly _tag: 'ReadStarted';
1025
- readonly requests: readonly Requirement$1[];
1101
+ readonly requests: readonly Requirement[];
1102
+ /** The refresh generation the read was planned under. */
1103
+ readonly refresh?: number | undefined;
1026
1104
  } |
1027
1105
  /** The active Surfaces' roots changed; everything they do not reach is collected. */
1028
1106
  {
@@ -1079,10 +1157,10 @@ export type RemoteMessage = {
1079
1157
  readonly error: RemoteError;
1080
1158
  };
1081
1159
  export declare const retentionRootsSchema: Schema.Struct<{
1082
- readonly requirements: Schema.$Array<Schema.Codec<Requirement$1, Requirement$1, never, never>>;
1160
+ readonly requirements: Schema.$Array<Schema.Codec<Requirement, Requirement, never, never>>;
1083
1161
  readonly connections: Schema.$Array<Schema.Struct<{
1084
1162
  readonly identity: Schema.String;
1085
- 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>>;
1086
1164
  }>>;
1087
1165
  }>;
1088
1166
  /**
@@ -1091,7 +1169,7 @@ export declare const retentionRootsSchema: Schema.Struct<{
1091
1169
  */
1092
1170
  export declare const remoteMessageCases: {
1093
1171
  ReadReceived: {
1094
- requests: Schema.$Array<Schema.Codec<Requirement$1, Requirement$1, never, never>>;
1172
+ requests: Schema.$Array<Schema.Codec<Requirement, Requirement, never, never>>;
1095
1173
  result: Schema.Struct<{
1096
1174
  readonly entities: Schema.$Array<Schema.Struct<{
1097
1175
  readonly entity: Schema.String;
@@ -1102,24 +1180,25 @@ export declare const remoteMessageCases: {
1102
1180
  now: Schema.Number;
1103
1181
  };
1104
1182
  ReadFailed: {
1105
- requests: Schema.$Array<Schema.Codec<Requirement$1, Requirement$1, never, never>>;
1183
+ requests: Schema.$Array<Schema.Codec<Requirement, Requirement, never, never>>;
1106
1184
  error: Schema.Struct<{
1107
1185
  readonly _tag: Schema.String;
1108
1186
  readonly message: Schema.String;
1109
1187
  }>;
1110
1188
  };
1111
1189
  RefreshStarted: {
1112
- requests: Schema.$Array<Schema.Codec<Requirement$1, Requirement$1, never, never>>;
1190
+ requests: Schema.$Array<Schema.Codec<Requirement, Requirement, never, never>>;
1113
1191
  };
1114
1192
  ReadStarted: {
1115
- requests: Schema.$Array<Schema.Codec<Requirement$1, Requirement$1, never, never>>;
1193
+ requests: Schema.$Array<Schema.Codec<Requirement, Requirement, never, never>>;
1194
+ refresh: Schema.optional<Schema.Number>;
1116
1195
  };
1117
1196
  RetentionChanged: {
1118
1197
  roots: Schema.Struct<{
1119
- readonly requirements: Schema.$Array<Schema.Codec<Requirement$1, Requirement$1, never, never>>;
1198
+ readonly requirements: Schema.$Array<Schema.Codec<Requirement, Requirement, never, never>>;
1120
1199
  readonly connections: Schema.$Array<Schema.Struct<{
1121
1200
  readonly identity: Schema.String;
1122
- 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>>;
1123
1202
  }>>;
1124
1203
  }>;
1125
1204
  };
@@ -1213,7 +1292,7 @@ export declare const updateRemote: (model: RemoteModel, message: RemoteMessage)
1213
1292
  * accumulates rather than replaces. Every read path should use this rather than
1214
1293
  * reducing `writeEntity` by hand.
1215
1294
  */
1216
- 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;
1217
1296
  //#endregion
1218
1297
  //#region src/coalesce.d.ts
1219
1298
  type Batch = Schema.Schema.Type<typeof ReadBatch>;
@@ -1231,7 +1310,7 @@ export interface CoalesceOptions {
1231
1310
  readonly window?: Duration.Input | undefined;
1232
1311
  }
1233
1312
  /** A stable key for a requirement, so an equal requirement in flight is joined. */
1234
- export declare const requirementKey: (requirement: Requirement$1) => string;
1313
+ export declare const requirementKey: (requirement: Requirement) => string;
1235
1314
  /**
1236
1315
  * Wraps a raw query so that an identical query in flight (same query, input,
1237
1316
  * and window) is joined rather than run again; distinct queries run
@@ -1251,7 +1330,7 @@ declare const RemoteClient_base: Context.ServiceClass<RemoteClient, "foldkit-rem
1251
1330
  readonly query: (request: Schema.Schema.Type<typeof QueryRequest>) => Effect.Effect<Schema.Schema.Type<typeof QueryResult>, RemoteQueryError>;
1252
1331
  readonly mutate: (request: Schema.Schema.Type<typeof MutationRequest>) => Effect.Effect<Schema.Schema.Type<typeof MutationResult>, RemoteMutationError>;
1253
1332
  readonly live: (request: {
1254
- readonly requirements: ReadonlyArray<Requirement$1>;
1333
+ readonly requirements: ReadonlyArray<Requirement>;
1255
1334
  readonly after: LiveCursor;
1256
1335
  }) => Stream.Stream<LiveEvent, RemoteLiveError | RemoteProtocolError>;
1257
1336
  }>;
@@ -1326,7 +1405,7 @@ export declare const refParts: (encoded: string) => RefParts;
1326
1405
  */
1327
1406
  export declare const refsIn: (value: unknown) => ReadonlyArray<RefParts>;
1328
1407
  /** The refs in a relation value that name the relation's target entity, and no other. */
1329
- export declare const targetsOf: (value: unknown, relation: RelationRequirement$1) => ReadonlyArray<RefParts>;
1408
+ export declare const targetsOf: (value: unknown, relation: RelationRequirement) => ReadonlyArray<RefParts>;
1330
1409
  export declare const isRefPage: (value: unknown) => value is RefPageValue;
1331
1410
  /**
1332
1411
  * The relation shape an entity field schema declares: a ref (`one`), a nullable
@@ -1355,7 +1434,7 @@ export interface Selection<Value, Name extends string = string, Shape extends 'e
1355
1434
  /** Pagination windows for nested relation fields, keyed by field name. */
1356
1435
  readonly connections?: Readonly<Record<string, QueryWindow>> | undefined;
1357
1436
  /** The slice required of each nested relation's target, keyed by field name. */
1358
- readonly relations?: Readonly<Record<string, RelationRequirement$1>> | undefined;
1437
+ readonly relations?: Readonly<Record<string, RelationRequirement>> | undefined;
1359
1438
  /** Present when this selection is itself a paginated relation. */
1360
1439
  readonly window?: QueryWindow | undefined;
1361
1440
  /** Phantom: see `Shape`. */
@@ -1377,7 +1456,7 @@ type NestedValue<Field, Sel> = Sel extends Selection<infer V, string, 'connectio
1377
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]>; }>;
1378
1457
  export declare const pageSchema: (item: AnySchema) => AnySchema;
1379
1458
  /** The requirement a nested selection contributes for its relation's target. */
1380
- export declare const relationOf: (selection: Selection<unknown>) => RelationRequirement$1;
1459
+ export declare const relationOf: (selection: Selection<unknown>) => RelationRequirement;
1381
1460
  export declare const Selection: {
1382
1461
  /**
1383
1462
  * The fields to read of `entity`. A nested `Selection` on a relation field
@@ -1404,7 +1483,7 @@ interface Assembled {
1404
1483
  * not present yet. A tombstoned target reads as `null` (or is dropped from a
1405
1484
  * list), so the Selection's codec decides whether that is a failure.
1406
1485
  */
1407
- 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;
1408
1487
  //#endregion
1409
1488
  //#region src/entity.d.ts
1410
1489
  export type AnySchema = Schema.Schema<unknown>;
@@ -1504,7 +1583,7 @@ export declare const inspectRemote: (model: RemoteModel) => RemoteInspection;
1504
1583
  export declare const inspectEntity: (model: RemoteModel, key: string) => RemoteInspection["entities"][number] | undefined;
1505
1584
  //#endregion
1506
1585
  //#region src/plan.d.ts
1507
- type Window = NonNullable<Requirement$1['windows']>[string];
1586
+ type Window = NonNullable<Requirement['windows']>[string];
1508
1587
  /** A stable key for a window, so two equal windows compare equal. */
1509
1588
  export declare const windowKey: (window: Window) => string;
1510
1589
  export interface PlanFreshness {
@@ -1517,7 +1596,7 @@ export interface PlanOptions {
1517
1596
  /** Plan every requested field, present or not, tombstoned or not. */
1518
1597
  readonly force?: boolean | undefined;
1519
1598
  }
1520
- 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>;
1521
1600
  //#endregion
1522
1601
  //#region src/policy.d.ts
1523
1602
  export type RemotePolicy =
@@ -1576,7 +1655,7 @@ export interface RemoteDescriptor<Entities extends readonly EntityDescriptor<any
1576
1655
  readonly entities: Entities;
1577
1656
  readonly queries: Queries;
1578
1657
  readonly mutations: Mutations;
1579
- readonly Model: Schema.Schema<RemoteModel>;
1658
+ readonly Model: Schema.Codec<RemoteModel, unknown>;
1580
1659
  readonly initial: RemoteModel;
1581
1660
  readonly Message: Schema.Schema<RemoteMessage>;
1582
1661
  readonly update: (model: RemoteModel, message: RemoteMessage) => RemoteModel;
@@ -1613,6 +1692,14 @@ export interface DomainMutateOptions {
1613
1692
  }
1614
1693
  /** A Foldkit Subscription entry of the Remote domain, emitting its Messages through `RemoteClient`. */
1615
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
+ };
1616
1703
  export interface SubscriptionsOptions extends ObserveOptions, LiveOptions, RetainOptions {}
1617
1704
  /** What `RemoteDomain.mutate` hands `update`: the Model with the request started, and the Command that settles it. */
1618
1705
  export interface MutationStarted<AppModel> {
@@ -1651,6 +1738,8 @@ export interface RemoteDomain<AppModel, Store extends RemoteModel, Entities exte
1651
1738
  previous<Name extends string, Input>(model: AppModel, projection: QueryProjection<AppModel, any, Name, Input>): QueryRef<Name, Input> | undefined;
1652
1739
  /** A Command that runs the query and yields the `ConnectionMerged` (or `QueryFailed`) that reduces it: "load more". */
1653
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;
1654
1743
  /**
1655
1744
  * The Foldkit Subscription entries for the active Surfaces, keyed for
1656
1745
  * `Subscription.make`: a read entry per Surface (`Remote.observe`), a live
@@ -1659,9 +1748,9 @@ export interface RemoteDomain<AppModel, Store extends RemoteModel, Entities exte
1659
1748
  * Surface's params are a function of the Model (`Surface.at`), so what is
1660
1749
  * fetched, subscribed, and retained follows the Model.
1661
1750
  */
1662
- 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>;
1663
1752
  /** `Remote.plan`: the requirements the store does not satisfy. */
1664
- 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>;
1665
1754
  /** `Remote.storeOf`: the visible store, base under the pending optimistic layers. */
1666
1755
  storeOf(model: AppModel): EntityStore;
1667
1756
  /**
@@ -1744,12 +1833,14 @@ declare const PlannedQuery: Schema.Struct<{
1744
1833
  }>;
1745
1834
  /** The dependencies of the read entry: the entity fields to read and the queries to run. */
1746
1835
  export interface ReadDependencies {
1747
- readonly requirements: ReadonlyArray<Requirement$1>;
1836
+ readonly requirements: ReadonlyArray<Requirement>;
1748
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;
1749
1840
  }
1750
1841
  export declare const Remote: {
1751
1842
  /** The submodel's schema, the same for every domain; embed it in the application Model. */
1752
- Model: Schema.Schema<RemoteModel>;
1843
+ Model: Schema.Codec<RemoteModel, unknown, never, never>;
1753
1844
  /** The submodel's initial value. */
1754
1845
  initial: RemoteModel;
1755
1846
  /**
@@ -1759,7 +1850,7 @@ export declare const Remote: {
1759
1850
  */
1760
1851
  messages: {
1761
1852
  ReadReceived: {
1762
- requests: Schema.$Array<Schema.Codec<Requirement$1, Requirement$1, never, never>>;
1853
+ requests: Schema.$Array<Schema.Codec<Requirement, Requirement, never, never>>;
1763
1854
  result: Schema.Struct<{
1764
1855
  readonly entities: Schema.$Array<Schema.Struct<{
1765
1856
  readonly entity: Schema.String;
@@ -1770,21 +1861,22 @@ export declare const Remote: {
1770
1861
  now: Schema.Number;
1771
1862
  };
1772
1863
  ReadFailed: {
1773
- requests: Schema.$Array<Schema.Codec<Requirement$1, Requirement$1, never, never>>;
1864
+ requests: Schema.$Array<Schema.Codec<Requirement, Requirement, never, never>>;
1774
1865
  error: Schema.Struct<{
1775
1866
  readonly _tag: Schema.String;
1776
1867
  readonly message: Schema.String;
1777
1868
  }>;
1778
1869
  };
1779
1870
  RefreshStarted: {
1780
- requests: Schema.$Array<Schema.Codec<Requirement$1, Requirement$1, never, never>>;
1871
+ requests: Schema.$Array<Schema.Codec<Requirement, Requirement, never, never>>;
1781
1872
  };
1782
1873
  ReadStarted: {
1783
- requests: Schema.$Array<Schema.Codec<Requirement$1, Requirement$1, never, never>>;
1874
+ requests: Schema.$Array<Schema.Codec<Requirement, Requirement, never, never>>;
1875
+ refresh: Schema.optional<Schema.Number>;
1784
1876
  };
1785
1877
  RetentionChanged: {
1786
1878
  roots: Schema.Struct<{
1787
- readonly requirements: Schema.$Array<Schema.Codec<Requirement$1, Requirement$1, never, never>>;
1879
+ readonly requirements: Schema.$Array<Schema.Codec<Requirement, Requirement, never, never>>;
1788
1880
  readonly connections: Schema.$Array<Schema.Struct<{
1789
1881
  readonly identity: Schema.String;
1790
1882
  readonly select: Schema.optional<Schema.Codec<RelationRequirement, RelationRequirement, never, never>>;
@@ -1889,7 +1981,7 @@ export declare const Remote: {
1889
1981
  * remote store does not satisfy, under `options` (freshness, force). A
1890
1982
  * Surface's projection is `surface.projection(params)`.
1891
1983
  */
1892
- 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>;
1893
1985
  /**
1894
1986
  * The queries a projection needs run before its connections read: those the
1895
1987
  * Model does not hold, or holds stale (every one under `force`).
@@ -1908,11 +2000,30 @@ export declare const Remote: {
1908
2000
  * is the clock it reads, so the planner itself stays pure and time-injected.
1909
2001
  */
1910
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;
1911
2022
  /**
1912
2023
  * Writes a read result into the store, recording each field's applied window.
1913
2024
  * `requests` are the planned requirements the result answers.
1914
2025
  */
1915
- 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;
1916
2027
  /**
1917
2028
  * Adapts an Effect RPC client for `RemoteRpc` to `RemoteClient`, so an
1918
2029
  * application provides the transport's RPC layer instead of writing the
@@ -2015,10 +2126,10 @@ export declare const Remote: {
2015
2126
  * from `RemoteModel.live`, so the application tracks no cursor of its own.
2016
2127
  */
2017
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, {
2018
- readonly requirements: ReadonlyArray<Requirement$1>;
2129
+ readonly requirements: ReadonlyArray<Requirement>;
2019
2130
  readonly cursor: LiveCursor;
2020
2131
  }, RemoteClient>;
2021
2132
  };
2022
2133
  //#endregion
2023
- export { Entity$1 as Entity, type RelationRequirement, type Requirement };
2134
+ export { Entity$1 as Entity };
2024
2135
  //# sourceMappingURL=index.d.mts.map