foldkit-remote 0.8.0 → 0.10.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
@@ -4,11 +4,10 @@ import { AnyEntity, AnyQuery, Dependencies, InputExpr, Query as Query$1 } from "
4
4
  import { Command } from "foldkit/command";
5
5
  import { EntryWithoutKeepAlive } from "foldkit/subscription";
6
6
  import { ActiveSurface, Contract, Invalid, Metadata, ModelRef, Projection, Surface, Wiring } from "foldkit-surface";
7
- import { Rpc, RpcGroup } from "effect/unstable/rpc";
8
- import { KeyValueStore } from "effect/unstable/persistence";
7
+ import { Rpc, RpcGroup } from "effect/rpc";
8
+ import { KeyValueStore } from "effect/persistence";
9
9
  import * as Update from "foldkit/update";
10
- //#region ../metadata/dist/index.d.mts
11
- //#region src/index.d.ts
10
+ //#region ../metadata/src/index.d.ts
12
11
  /** Marks a `Metadata` value. Its entries are private to this module. */
13
12
  declare const MetadataTypeId: unique symbol;
14
13
  type MetadataTypeId = typeof MetadataTypeId;
@@ -60,7 +59,6 @@ declare const Metadata$1: {
60
59
  summarize: (metadata: Metadata$1) => readonly MetadataSummary[];
61
60
  };
62
61
  //#endregion
63
- //#endregion
64
62
  //#region src/connection.d.ts
65
63
  export interface EdgeRef {
66
64
  readonly entity: string;
@@ -228,6 +226,8 @@ export interface EntityWrite {
228
226
  readonly values: Readonly<Record<string, unknown>>;
229
227
  /** The canonical window each written relation field was fetched with. */
230
228
  readonly windows?: Readonly<Record<string, string>> | undefined;
229
+ /** When this write dates the entity, instead of the batch's `now`. */
230
+ readonly updatedAt?: number | undefined;
231
231
  }
232
232
  /**
233
233
  * Merges known values into many entities, copying the store once. Every
@@ -320,15 +320,23 @@ export interface Reconciled {
320
320
  readonly store: EntityStore;
321
321
  readonly state: MutationState;
322
322
  }
323
+ /** What a settled mutation wrote, as `MutationSucceeded` carries it. */
324
+ export interface MutationAnswer {
325
+ readonly entities: ReadonlyArray<NormalizedPatch>;
326
+ /** Entities the mutation deleted; each is tombstoned. */
327
+ readonly deleted?: ReadonlyArray<{
328
+ readonly entity: string;
329
+ readonly id: string;
330
+ }> | undefined;
331
+ /** Injected clock reading of the answer, which dates what it writes. */
332
+ readonly now: number;
333
+ }
323
334
  /**
324
335
  * Applies a mutation result once and marks the request settled. A second call
325
336
  * with the same `requestId` (a retry, or a live event describing the same change)
326
337
  * does not re-apply the entities, but still clears `pending`.
327
338
  */
328
- export declare const reconcileMutation: (store: EntityStore, state: MutationState, requestId: string, entities: ReadonlyArray<NormalizedPatch>, deleted?: ReadonlyArray<{
329
- readonly entity: string;
330
- readonly id: string;
331
- }>) => Reconciled;
339
+ export declare const reconcileMutation: (store: EntityStore, state: MutationState, requestId: string, answer: MutationAnswer) => Reconciled;
332
340
  /** A mutation an application declares: its name and input/output codecs. */
333
341
  export interface MutationDescriptor<Name extends string, Input, Output> {
334
342
  readonly name: Name;
@@ -609,7 +617,12 @@ export declare const addLayer: (optimistic: OptimisticState, layer: EntityLayer)
609
617
  export declare const removeLayer: (optimistic: OptimisticState, id: string) => OptimisticState;
610
618
  export declare const addOverlay: (optimistic: OptimisticState, overlay: ConnectionOverlay) => OptimisticState;
611
619
  export declare const removeOverlay: (optimistic: OptimisticState, id: string) => OptimisticState;
612
- /** Base store with every layer applied in order. Later layers win. */
620
+ /**
621
+ * Base store with every layer applied in order. Later layers win. A patch is
622
+ * not news from the server, so it keeps the date of the value under it; an
623
+ * entity only a request holds (a temporary id) is not the server's to age, and
624
+ * is dated `Infinity`, so freshness never plans or times it.
625
+ */
613
626
  export declare const visibleStore: (base: EntityStore, optimistic: OptimisticState) => EntityStore;
614
627
  /**
615
628
  * A fresh page is newer than any settled overlay it covers: an edge the page
@@ -623,10 +636,10 @@ export declare const pruneOverlays: (optimistic: OptimisticState, connection: st
623
636
  * a later layer re-wins on rebase. Confirmed connection changes are recorded
624
637
  * once per request, as overlays the request no longer owns.
625
638
  */
626
- export declare const settleSuccess: (base: EntityStore, optimistic: OptimisticState, state: MutationState, requestId: string, entities: ReadonlyArray<NormalizedPatch>, connections?: ReadonlyArray<ConnectionChange>, deleted?: ReadonlyArray<{
627
- readonly entity: string;
628
- readonly id: string;
629
- }>) => {
639
+ export declare const settleSuccess: (base: EntityStore, optimistic: OptimisticState, state: MutationState, requestId: string, answer: MutationAnswer & {
640
+ /** Connection changes the server confirmed; they replace the request's own. */
641
+ readonly connections?: ReadonlyArray<ConnectionChange> | undefined;
642
+ }) => {
630
643
  readonly store: EntityStore;
631
644
  readonly state: MutationState;
632
645
  readonly optimistic: OptimisticState;
@@ -691,11 +704,6 @@ export type LiveOutcome = 'applied' | 'duplicate' | 'gap';
691
704
  * spurious gap.
692
705
  */
693
706
  export declare const classifyLive: (state: LiveState, cursor: LiveCursor) => LiveOutcome;
694
- /**
695
- * A subscriber is woken only if the event changed a field it selects. A
696
- * change to an unselected field is skipped entirely.
697
- */
698
- export declare const shouldWake: (changed: ReadonlyArray<string>, selected: ReadonlyArray<string>) => boolean;
699
707
  export interface EntityApplied {
700
708
  readonly state: LiveState;
701
709
  readonly store: EntityStore;
@@ -1078,7 +1086,7 @@ export declare const Live: Rpc.Rpc<"FoldkitRemoteLive", Schema.Struct<{
1078
1086
  readonly requirements: Schema.$Array<Schema.Codec<Requirement, Requirement, never, never>>;
1079
1087
  /** Resume cursor; events at or before it are duplicates. */
1080
1088
  readonly after: Schema.Number;
1081
- }>, import("effect/unstable/rpc/RpcSchema").Stream<Schema.Union<readonly [Schema.Struct<{
1089
+ }>, import("effect/rpc/RpcSchema").Stream<Schema.Union<readonly [Schema.Struct<{
1082
1090
  readonly _tag: Schema.Literal<"EntityPatched">;
1083
1091
  readonly cursor: Schema.Number;
1084
1092
  readonly entity: Schema.String;
@@ -1135,6 +1143,12 @@ export declare const QueryRequest: Schema.Struct<{
1135
1143
  readonly after: Schema.optional<Schema.String>;
1136
1144
  readonly before: Schema.optional<Schema.String>;
1137
1145
  }>;
1146
+ /**
1147
+ * What to read of each item, as the relation slice a read asks the server
1148
+ * for. Absent for a query run without one: the response carries only edges,
1149
+ * and the client's read entry fetches fields in a second round trip.
1150
+ */
1151
+ readonly select: Schema.optional<Schema.Codec<RelationRequirement, RelationRequirement, never, never>>;
1138
1152
  }>;
1139
1153
  export declare const QueryEdge: Schema.Struct<{
1140
1154
  readonly entity: Schema.String;
@@ -1163,6 +1177,21 @@ export declare const QueryResult: Schema.Struct<{
1163
1177
  }>, Schema.Struct<{
1164
1178
  readonly _tag: Schema.Literal<"Unknown">;
1165
1179
  }>]>;
1180
+ /**
1181
+ * The selected fields of the page's items, in the same shape a read batch
1182
+ * returns. Absent from a server that predates payload planning: the client
1183
+ * reads it as empty and fetches fields as before.
1184
+ */
1185
+ readonly entities: Schema.optional<Schema.$Array<Schema.Struct<{
1186
+ readonly entity: Schema.String;
1187
+ readonly id: Schema.String;
1188
+ readonly values: Schema.$Record<Schema.String, Schema.Unknown>;
1189
+ }>>>;
1190
+ readonly settled: Schema.optional<Schema.$Array<Schema.Struct<{
1191
+ readonly entity: Schema.String;
1192
+ readonly id: Schema.String;
1193
+ readonly fields: Schema.$Array<Schema.String>;
1194
+ }>>>;
1166
1195
  }>;
1167
1196
  export declare const QueryRpc: Rpc.Rpc<"FoldkitRemoteQuery", Schema.Struct<{
1168
1197
  readonly query: Schema.String;
@@ -1173,6 +1202,12 @@ export declare const QueryRpc: Rpc.Rpc<"FoldkitRemoteQuery", Schema.Struct<{
1173
1202
  readonly after: Schema.optional<Schema.String>;
1174
1203
  readonly before: Schema.optional<Schema.String>;
1175
1204
  }>;
1205
+ /**
1206
+ * What to read of each item, as the relation slice a read asks the server
1207
+ * for. Absent for a query run without one: the response carries only edges,
1208
+ * and the client's read entry fetches fields in a second round trip.
1209
+ */
1210
+ readonly select: Schema.optional<Schema.Codec<RelationRequirement, RelationRequirement, never, never>>;
1176
1211
  }>, Schema.Struct<{
1177
1212
  readonly edges: Schema.$Array<Schema.Struct<{
1178
1213
  readonly entity: Schema.String;
@@ -1195,6 +1230,21 @@ export declare const QueryRpc: Rpc.Rpc<"FoldkitRemoteQuery", Schema.Struct<{
1195
1230
  }>, Schema.Struct<{
1196
1231
  readonly _tag: Schema.Literal<"Unknown">;
1197
1232
  }>]>;
1233
+ /**
1234
+ * The selected fields of the page's items, in the same shape a read batch
1235
+ * returns. Absent from a server that predates payload planning: the client
1236
+ * reads it as empty and fetches fields as before.
1237
+ */
1238
+ readonly entities: Schema.optional<Schema.$Array<Schema.Struct<{
1239
+ readonly entity: Schema.String;
1240
+ readonly id: Schema.String;
1241
+ readonly values: Schema.$Record<Schema.String, Schema.Unknown>;
1242
+ }>>>;
1243
+ readonly settled: Schema.optional<Schema.$Array<Schema.Struct<{
1244
+ readonly entity: Schema.String;
1245
+ readonly id: Schema.String;
1246
+ readonly fields: Schema.$Array<Schema.String>;
1247
+ }>>>;
1198
1248
  }>, typeof RemoteQueryError, never, never>;
1199
1249
  export declare const RemoteRpc: RpcGroup.RpcGroup<Rpc.Rpc<"FoldkitRemoteRead", Schema.Struct<{
1200
1250
  readonly version: Schema.Number;
@@ -1251,7 +1301,7 @@ export declare const RemoteRpc: RpcGroup.RpcGroup<Rpc.Rpc<"FoldkitRemoteRead", S
1251
1301
  readonly requirements: Schema.$Array<Schema.Codec<Requirement, Requirement, never, never>>;
1252
1302
  /** Resume cursor; events at or before it are duplicates. */
1253
1303
  readonly after: Schema.Number;
1254
- }>, import("effect/unstable/rpc/RpcSchema").Stream<Schema.Union<readonly [Schema.Struct<{
1304
+ }>, import("effect/rpc/RpcSchema").Stream<Schema.Union<readonly [Schema.Struct<{
1255
1305
  readonly _tag: Schema.Literal<"EntityPatched">;
1256
1306
  readonly cursor: Schema.Number;
1257
1307
  readonly entity: Schema.String;
@@ -1295,6 +1345,12 @@ export declare const RemoteRpc: RpcGroup.RpcGroup<Rpc.Rpc<"FoldkitRemoteRead", S
1295
1345
  readonly after: Schema.optional<Schema.String>;
1296
1346
  readonly before: Schema.optional<Schema.String>;
1297
1347
  }>;
1348
+ /**
1349
+ * What to read of each item, as the relation slice a read asks the server
1350
+ * for. Absent for a query run without one: the response carries only edges,
1351
+ * and the client's read entry fetches fields in a second round trip.
1352
+ */
1353
+ readonly select: Schema.optional<Schema.Codec<RelationRequirement, RelationRequirement, never, never>>;
1298
1354
  }>, Schema.Struct<{
1299
1355
  readonly edges: Schema.$Array<Schema.Struct<{
1300
1356
  readonly entity: Schema.String;
@@ -1317,6 +1373,21 @@ export declare const RemoteRpc: RpcGroup.RpcGroup<Rpc.Rpc<"FoldkitRemoteRead", S
1317
1373
  }>, Schema.Struct<{
1318
1374
  readonly _tag: Schema.Literal<"Unknown">;
1319
1375
  }>]>;
1376
+ /**
1377
+ * The selected fields of the page's items, in the same shape a read batch
1378
+ * returns. Absent from a server that predates payload planning: the client
1379
+ * reads it as empty and fetches fields as before.
1380
+ */
1381
+ readonly entities: Schema.optional<Schema.$Array<Schema.Struct<{
1382
+ readonly entity: Schema.String;
1383
+ readonly id: Schema.String;
1384
+ readonly values: Schema.$Record<Schema.String, Schema.Unknown>;
1385
+ }>>>;
1386
+ readonly settled: Schema.optional<Schema.$Array<Schema.Struct<{
1387
+ readonly entity: Schema.String;
1388
+ readonly id: Schema.String;
1389
+ readonly fields: Schema.$Array<Schema.String>;
1390
+ }>>>;
1320
1391
  }>, typeof RemoteQueryError, never, never>>;
1321
1392
  //#endregion
1322
1393
  //#region src/model.d.ts
@@ -1329,6 +1400,12 @@ export interface RemoteModel {
1329
1400
  readonly entities: EntityStore;
1330
1401
  /** Normalized ordered connections, keyed by connection identity. */
1331
1402
  readonly connections: Readonly<Record<string, Connection>>;
1403
+ /**
1404
+ * How many rows `Data.more` has grown a read's window to, by
1405
+ * `windowGrowthKey`. A read shows at most its window, so this is what "load
1406
+ * more" changes; it lives here because a list holds no state of its own.
1407
+ */
1408
+ readonly grown: Readonly<Record<string, number>>;
1332
1409
  /** Optimistic entity layers and connection overlays over the base store. */
1333
1410
  readonly optimistic: OptimisticState;
1334
1411
  /** Live cursor and boundary state, keyed by the subscription's stream key. */
@@ -1480,6 +1557,8 @@ export type RemoteMessage = {
1480
1557
  readonly entity: string;
1481
1558
  readonly id: string;
1482
1559
  }> | undefined;
1560
+ /** Injected clock reading of the answer: how fresh the written entities are. */
1561
+ readonly now: number;
1483
1562
  } | {
1484
1563
  readonly _tag: 'MutationFailed';
1485
1564
  readonly requestId: string;
@@ -1497,6 +1576,12 @@ export type RemoteMessage = {
1497
1576
  } | {
1498
1577
  readonly _tag: 'OverlayLifted';
1499
1578
  readonly id: string;
1579
+ } |
1580
+ /** `Data.more`: a read's window, by `windowGrowthKey`, now shows `size` rows. */
1581
+ {
1582
+ readonly _tag: 'WindowGrown';
1583
+ readonly window: string;
1584
+ readonly size: number;
1500
1585
  } | {
1501
1586
  readonly _tag: 'LiveReceived';
1502
1587
  readonly stream: string;
@@ -1512,6 +1597,26 @@ export type RemoteMessage = {
1512
1597
  readonly page: Segment;
1513
1598
  /** The page answers the connection's refresh, so the merge also clears `stale`. */
1514
1599
  readonly refreshes?: boolean | undefined;
1600
+ /**
1601
+ * The slice the query asked for, so the payload's entities write like a
1602
+ * read: one request per matching edge. Absent for a page from a server
1603
+ * that predates payload planning, which carries edges only.
1604
+ */
1605
+ readonly select?: RelationRequirement | undefined;
1606
+ /** The selected fields of the page's items, as a read batch returns them. */
1607
+ readonly entities?: ReadonlyArray<{
1608
+ readonly entity: string;
1609
+ readonly id: string;
1610
+ readonly values: Record<string, unknown>;
1611
+ }> | undefined;
1612
+ /** Fields asked for that the server settled without a value. */
1613
+ readonly settled?: ReadonlyArray<{
1614
+ readonly entity: string;
1615
+ readonly id: string;
1616
+ readonly fields: ReadonlyArray<string>;
1617
+ }> | undefined;
1618
+ /** Injected clock reading of the answer, dating what the payload writes. */
1619
+ readonly now?: number | undefined;
1515
1620
  } | {
1516
1621
  readonly _tag: 'ConnectionInvalidated';
1517
1622
  readonly connection: string;
@@ -1605,6 +1710,7 @@ export declare const remoteMessageCases: {
1605
1710
  readonly entity: Schema.String;
1606
1711
  readonly id: Schema.String;
1607
1712
  }>>>;
1713
+ now: Schema.Number;
1608
1714
  };
1609
1715
  MutationFailed: {
1610
1716
  requestId: Schema.String;
@@ -1620,6 +1726,10 @@ export declare const remoteMessageCases: {
1620
1726
  OverlayLifted: {
1621
1727
  id: Schema.String;
1622
1728
  };
1729
+ WindowGrown: {
1730
+ window: Schema.String;
1731
+ size: Schema.Number;
1732
+ };
1623
1733
  LiveReceived: {
1624
1734
  stream: Schema.String;
1625
1735
  event: Schema.Unknown;
@@ -1633,6 +1743,18 @@ export declare const remoteMessageCases: {
1633
1743
  connection: Schema.String;
1634
1744
  page: Schema.Unknown;
1635
1745
  refreshes: Schema.optional<Schema.Boolean>;
1746
+ select: Schema.optional<Schema.Codec<RelationRequirement, RelationRequirement, never, never>>;
1747
+ entities: Schema.optional<Schema.$Array<Schema.Struct<{
1748
+ readonly entity: Schema.String;
1749
+ readonly id: Schema.String;
1750
+ readonly values: Schema.$Record<Schema.String, Schema.Unknown>;
1751
+ }>>>;
1752
+ settled: Schema.optional<Schema.$Array<Schema.Struct<{
1753
+ readonly entity: Schema.String;
1754
+ readonly id: Schema.String;
1755
+ readonly fields: Schema.$Array<Schema.String>;
1756
+ }>>>;
1757
+ now: Schema.optional<Schema.Number>;
1636
1758
  };
1637
1759
  ConnectionInvalidated: {
1638
1760
  connection: Schema.String;
@@ -1667,6 +1789,14 @@ export declare const isRemoteMessage: <M extends {
1667
1789
  readonly _tag: RemoteMessageTag;
1668
1790
  }>;
1669
1791
  export declare const remoteMessageSchema: Schema.Schema<RemoteMessage>;
1792
+ /** The key a grown window is kept under: its connection and the window first asked for. */
1793
+ export declare const windowGrowthKey: (identity: string, window: string) => string;
1794
+ /**
1795
+ * Whether only overlays show this entity: the server has told the store
1796
+ * nothing of it and no request's layer writes it, so nothing will fetch what an
1797
+ * overlay leaves out.
1798
+ */
1799
+ export declare const onlyOverlaid: (model: RemoteModel, entity: string, id: string) => boolean;
1670
1800
  /** Whether a query for this connection is in flight. */
1671
1801
  export declare const isQueryLoading: (model: RemoteModel, identity: string) => boolean;
1672
1802
  /**
@@ -1697,7 +1827,12 @@ export declare const isLoading: (model: RemoteModel, entity: string, id: string,
1697
1827
  /**
1698
1828
  * The pure reducer all four producers share. A live event that arrives ahead of
1699
1829
  * its cursor is a gap: it is not applied, and the stream is recorded so the host
1700
- * can resubscribe rather than silently miss facts.
1830
+ * can resubscribe rather than silently miss facts. When every field a Message
1831
+ * touches keeps its identity, `model` itself is returned, so the application's
1832
+ * root keeps its identity and Foldkit does not render. The common no-ops do (a
1833
+ * duplicate live event, a repeated `ReadStarted`); a repeated failure, or a
1834
+ * `preserve-existing` restore of entities already held, still makes an equal
1835
+ * copy.
1701
1836
  */
1702
1837
  export declare const updateRemote: (model: RemoteModel, message: RemoteMessage) => RemoteModel;
1703
1838
  /** Whether this field's last read failed and nothing has settled it since. */
@@ -2307,10 +2442,13 @@ export interface Deadline {
2307
2442
  /**
2308
2443
  * The earliest moment a value `requirements` read, following the relations the
2309
2444
  * 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.
2445
+ * requirements of the entities that go stale then. A field the server settled
2446
+ * without a value ages with its entity too, since `plan` asks for it again once
2447
+ * the entity expires. Nothing when no such field is still fresh: what is
2448
+ * already expired or stale is planned, not awaited, which is what keeps a
2449
+ * timer from firing on what it already fired on. It is the next moment the
2450
+ * plan can change with the clock alone, which the read entry relies on to
2451
+ * reuse its plan until then.
2314
2452
  */
2315
2453
  export declare const deadlineOf: (store: EntityStore, requirements: readonly Requirement[], freshness: PlanFreshness) => Deadline | undefined;
2316
2454
  export declare const plan: (store: EntityStore, requirements: readonly Requirement[], options?: PlanOptions) => ReadonlyArray<Requirement>;
@@ -2421,6 +2559,8 @@ export interface DomainMutateOptions {
2421
2559
  readonly requestId: string;
2422
2560
  readonly tempId: string;
2423
2561
  }) => ReadonlyArray<OptimisticOperation>) | undefined;
2562
+ /** The clock `MutationSucceeded` stamps the answer with; default `Date.now`, read at each use. */
2563
+ readonly now?: (() => number) | undefined;
2424
2564
  }
2425
2565
  /** A Foldkit Subscription entry of the Remote domain, emitting its Messages through `RemoteClient`. */
2426
2566
  export type RemoteEntry<AppModel, Dependencies> = EntryWithoutKeepAlive<AppModel, RemoteMessage, Dependencies, RemoteClient>;
@@ -2429,9 +2569,15 @@ export type RemoteEntry<AppModel, Dependencies> = EntryWithoutKeepAlive<AppModel
2429
2569
  * and a live entry per key (the live one idles when nothing is read live), and
2430
2570
  * `retain`.
2431
2571
  */
2432
- export type SubscriptionEntries<AppModel, Active> = { readonly [K in keyof Active & (string | number) as `${K}.read` | `${K}.live`]: RemoteEntry<AppModel, any>; } & {
2433
- readonly retain: RemoteEntry<AppModel, any>;
2572
+ export type SubscriptionEntries<AppModel, Active, Message = RemoteMessage> = { readonly [K in keyof Active & (string | number) as `${K}.read`]: EntryWithoutKeepAlive<AppModel, Message, ReadDependencies, RemoteClient>; } & { readonly [K in keyof Active & (string | number) as `${K}.live`]: EntryWithoutKeepAlive<AppModel, Message, LiveDependencies, RemoteClient>; } & {
2573
+ readonly retain: EntryWithoutKeepAlive<AppModel, Message, RetentionRoots, never>;
2434
2574
  };
2575
+ /** The dependencies of a live entry: what it subscribes to, and where a restart resumes. */
2576
+ export interface LiveDependencies {
2577
+ readonly requirements: ReadonlyArray<Requirement>;
2578
+ readonly cursor: LiveCursor;
2579
+ readonly floor: number;
2580
+ }
2435
2581
  export interface SubscriptionsOptions extends ObserveOptions, LiveOptions, RetainOptions {}
2436
2582
  /** A Remote domain's wiring for an assembly: it routes Remote's Messages into `reduce`, brings its Subscriptions and contract, and requires `RemoteClient` from the runtime's resources Layer. */
2437
2583
  export type RemoteWiring<AppModel> = Wiring<AppModel, RemoteMessage | RemoteMessageInput, RemoteClient>;
@@ -2466,10 +2612,13 @@ export interface RemoteDomain<AppModel, Store extends RemoteModel, Entities exte
2466
2612
  * onto the connection read through the same projection.
2467
2613
  */
2468
2614
  query<Q extends QueryDescriptor<any, any, any>, Value, Entity extends string>(query: Q & Registered<Q['name'], QueryName<Queries[number]>, 'Query'>, input: QueryInput<Q>, options: QueryOptions<Value, Entity, QueryEntity<Q>>): QueryProjection<AppModel, Value, Q['name'], QueryInput<Q>>;
2469
- /** The `QueryRef` for the page after the loaded end (same page size), or `undefined` when there is none or its cursor is unknown. */
2470
- next<Name extends string, Input>(model: AppModel, projection: QueryProjection<AppModel, any, Name, Input>): QueryRef<Name, Input> | undefined;
2471
- /** The `QueryRef` for the page before the loaded start, or `undefined`. */
2472
- previous<Name extends string, Input>(model: AppModel, projection: QueryProjection<AppModel, any, Name, Input>): QueryRef<Name, Input> | undefined;
2615
+ /**
2616
+ * "Load more": the Model with the read's window grown by one page, so it
2617
+ * shows that many more rows; the read entry fetches what the connection
2618
+ * lacks. None when the read shows everything there is (or is not a sized
2619
+ * window). Called from `update`.
2620
+ */
2621
+ more<Name extends string, Input>(model: AppModel, projection: QueryProjection<AppModel, any, Name, Input>): Option.Option<AppModel>;
2473
2622
  /**
2474
2623
  * What a query read is and what it currently is, as one serializable value —
2475
2624
  * data-query-DESIGN §29.1: the domain, the definition and its input, the
@@ -2520,13 +2669,13 @@ export interface RemoteDomain<AppModel, Store extends RemoteModel, Entities exte
2520
2669
  * fetch. The server stays authoritative for which rows exist.
2521
2670
  */
2522
2671
  filtered<Value, Name extends string, Input, Q extends QueryDescriptor<any, any, any>>(model: AppModel, over: QueryProjection<AppModel, Value, Name, Input>, by: Q & Registered<Q['name'], QueryName<Queries[number]>, 'Query'>, input: QueryInput<Q>): Matched<Value>;
2523
- /** A Command that runs the query and yields the `ConnectionMerged` (or `QueryFailed`) that reduces it: "load more". */
2524
- fetch(ref: QueryRef<string, unknown>): Command<RemoteMessage, never, RemoteClient>;
2525
2672
  /**
2526
2673
  * Shows operations over the store with no request behind them, as a mutation's
2527
2674
  * `optimistic` ones show while it is in flight: every Selection and view draws
2528
2675
  * them. For a preview of a change nobody has made. They stay until `lift`;
2529
- * showing an id again replaces what it showed. Called from `update`.
2676
+ * showing an id again replaces what it showed. Called from `update`. A read
2677
+ * of an entity only overlays show, lacking a field it selects, is `Failed`
2678
+ * (`Overlaid`): nothing will fetch that field.
2530
2679
  */
2531
2680
  overlay(model: AppModel, id: string, optimistic: ReadonlyArray<OptimisticOperation>): AppModel;
2532
2681
  /** Lifts what `overlay` showed under this id. Lifting nothing returns the same Model. */
@@ -2535,17 +2684,29 @@ export interface RemoteDomain<AppModel, Store extends RemoteModel, Entities exte
2535
2684
  refresh(model: AppModel, target: Projection<AppModel, unknown> | Surface<AppModel, any, any, void>): AppModel;
2536
2685
  /** `Remote.forget`: the Model with every server-derived fact gone, for a change of principal. */
2537
2686
  forget(model: AppModel): AppModel;
2687
+ /**
2688
+ * A read of this domain as an active Surface, for `subscriptions` and
2689
+ * `wiring`: `projectionOf` is what it reads for a Model, none while it
2690
+ * reads nothing. It belongs to the domain's application, and lists no
2691
+ * Messages: it is a requirement, not a sender. A domain made from a raw
2692
+ * optic names no application, so this throws for one.
2693
+ */
2694
+ active<Value>(name: string, projectionOf: (model: AppModel) => Option.Option<Projection<AppModel, Value>>): ActiveSurface<AppModel>;
2538
2695
  /**
2539
2696
  * The Foldkit Subscription entries for the active Surfaces, keyed for
2540
2697
  * `Subscription.make`: a read entry per Surface (`Remote.observe`), a live
2541
2698
  * entry per Surface that reads through `live` (`Remote.live`), and one
2542
- * retain entry with every active Surface as a root (`Remote.retain`). A
2699
+ * retain entry (`Remote.retain`). Retention is the domain's: the retain
2700
+ * entry of every call roots the active Surfaces of every call, so reads
2701
+ * split over calls (one per policy) do not collect each other's data. A
2543
2702
  * Surface's params are a function of the Model (`Surface.at`), so what is
2544
2703
  * fetched, subscribed, and retained follows the Model.
2545
2704
  */
2546
2705
  subscriptions<const Active extends Readonly<Record<string, ActiveSurface<AppModel> | Surface<AppModel, any, any, void>>>>(active: Active, options?: SubscriptionsOptions): SubscriptionEntries<AppModel, Active>;
2547
2706
  /** `Remote.plan`: the requirements the store does not satisfy. */
2548
2707
  plan<Value>(model: AppModel, projection: Projection<AppModel, Value>, options?: PlanOptions): ReadonlyArray<Requirement>;
2708
+ /** `Remote.meta`: when what a projection shows was last received, and whether it is stale or loading. */
2709
+ meta<Value>(model: AppModel, projection: Projection<AppModel, Value>): ReadMeta;
2549
2710
  /** `Remote.storeOf`: the visible store, base under the pending optimistic layers. */
2550
2711
  storeOf(model: AppModel): EntityStore;
2551
2712
  /**
@@ -2578,6 +2739,17 @@ export interface RemoteDomain<AppModel, Store extends RemoteModel, Entities exte
2578
2739
  * items included). For SSR, route or hover prefetch, and tests.
2579
2740
  */
2580
2741
  prefetch<Value>(model: AppModel, projection: Projection<AppModel, Value>, options?: ObserveOptions): Effect.Effect<AppModel, RemoteReadError | RemoteProtocolError | RemoteQueryError, RemoteClient>;
2742
+ /**
2743
+ * The Model with everything the active Surfaces read, for a render that
2744
+ * fetches nothing (SSR, a prerender). Each pass prefetches every Surface
2745
+ * that plans a read, over the Model the one before it left, so a Surface
2746
+ * active only once another's read has arrived is read in the next pass (or
2747
+ * the same one, when it comes later in `active`). It stops at the first
2748
+ * pass that plans nothing, and fails with `RemoteUnsatisfied` when
2749
+ * `options.passes` run out first. Reads are cache-first: a field already
2750
+ * held is not asked again.
2751
+ */
2752
+ satisfy(model: AppModel, active: Readonly<Record<string, ActiveSurface<AppModel> | Surface<AppModel, any, any, void>>>, options?: SatisfyOptions): Effect.Effect<AppModel, RemoteReadError | RemoteProtocolError | RemoteQueryError | RemoteUnsatisfied, RemoteClient>;
2581
2753
  /**
2582
2754
  * Starts a registered mutation from `update`: applies `MutationStarted` (with
2583
2755
  * the optimistic operations) to the Model and returns the Command that runs
@@ -2618,12 +2790,10 @@ export type FoldedMutationStarted<AppModel, ParentMessage> = Omit<MutationStarte
2618
2790
  */
2619
2791
  export interface RemoteFold<AppModel, ParentMessage, Domain extends RemoteDomain<AppModel, any, any, any, any>> {
2620
2792
  (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
2793
  /** `Data.mutate`, its Command lifted. */
2624
2794
  readonly mutate: (...args: Parameters<Domain['mutate']>) => FoldedMutationStarted<AppModel, ParentMessage>;
2625
2795
  /** `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>; };
2796
+ readonly subscriptions: <const Active extends Readonly<Record<string, ActiveSurface<AppModel> | Surface<AppModel, any, any, void>>>>(active: Active, options?: SubscriptionsOptions) => SubscriptionEntries<AppModel, Active, ParentMessage>;
2627
2797
  }
2628
2798
  /** The application Model a bound domain is over. */
2629
2799
  export type ModelOfDomain<Domain> = Domain extends RemoteDomain<infer AppModel, any, any, any, any> ? AppModel : never;
@@ -2657,9 +2827,23 @@ export interface QueryProjection<AppModel, Value, Name extends string, Input> ex
2657
2827
  export type QueryInput<Q> = Q extends QueryDescriptor<any, infer Input, any> ? Input : never;
2658
2828
  /** The entity a query's connection is over. */
2659
2829
  export type QueryEntity<Q> = Q extends QueryDescriptor<any, any, ConnectionSpec<infer Entity>> ? Entity : string;
2830
+ /**
2831
+ * When what a projection shows was last received, and whether it is stale
2832
+ * or loading. See `Remote.meta`.
2833
+ */
2834
+ export interface ReadMeta {
2835
+ /** The newest server write among what is shown; none received reads `undefined`. */
2836
+ readonly updatedAt: number | undefined;
2837
+ /** Any shown field or connection is marked stale. */
2838
+ readonly stale: boolean;
2839
+ /** Any shown field or connection is in flight. */
2840
+ readonly loading: boolean;
2841
+ }
2660
2842
  export interface MutateOptions {
2661
2843
  /** What the request changes before the server answers; released when it settles. */
2662
2844
  readonly optimistic?: ReadonlyArray<OptimisticOperation> | undefined;
2845
+ /** The clock `MutationSucceeded` stamps the answer with; default `Date.now`, read at each use. */
2846
+ readonly now?: (() => number) | undefined;
2663
2847
  }
2664
2848
  export interface RetainOptions {
2665
2849
  /** The query connections the application shows, by `QueryRef` or identity. */
@@ -2671,11 +2855,29 @@ export interface RetainOptions {
2671
2855
  export interface ObserveOptions {
2672
2856
  /** Default `RemotePolicy.cacheFirst`. */
2673
2857
  readonly policy?: RemotePolicy | undefined;
2674
- /** The clock a refreshing policy reads; default `Date.now`. */
2858
+ /** The clock a refreshing policy reads; default `Date.now`, read at each use. */
2675
2859
  readonly now?: (() => number) | undefined;
2676
2860
  }
2861
+ /** How `Data.satisfy` reads: how many passes it may take, and the clock. */
2862
+ export interface SatisfyOptions {
2863
+ /** At most this many passes over the Surfaces; default 8. */
2864
+ readonly passes?: number | undefined;
2865
+ /** The clock the reads are stamped with; default `Date.now`, read at each use. */
2866
+ readonly now?: (() => number) | undefined;
2867
+ }
2868
+ declare const RemoteUnsatisfied_base: Schema.Class<RemoteUnsatisfied, Schema.TaggedStruct<"RemoteUnsatisfied", {
2869
+ readonly surfaces: Schema.$Array<Schema.String>;
2870
+ readonly passes: Schema.Number;
2871
+ }>, import("effect/Cause").YieldableError>;
2872
+ /**
2873
+ * `Data.satisfy` reached its bound with Surfaces still reading, such as a
2874
+ * chain whose every read reveals one more. It names them, so a page that
2875
+ * would have rendered them loading fails instead. (A read the server leaves
2876
+ * unanswered settles as missing and ends the loop.)
2877
+ */
2878
+ export declare class RemoteUnsatisfied extends RemoteUnsatisfied_base {}
2677
2879
  export interface LiveOptions {
2678
- /** The clock `LiveReceived` stamps events with; default `Date.now`. */
2880
+ /** The clock `LiveReceived` stamps events with; default `Date.now`, read at each use. */
2679
2881
  readonly now?: (() => number) | undefined;
2680
2882
  }
2681
2883
  /** A query the read entry runs, as its dependencies carry it: plain data Foldkit compares. */
@@ -2783,6 +2985,7 @@ export declare const Remote: {
2783
2985
  readonly entity: Schema.String;
2784
2986
  readonly id: Schema.String;
2785
2987
  }>>>;
2988
+ now: Schema.Number;
2786
2989
  };
2787
2990
  MutationFailed: {
2788
2991
  requestId: Schema.String;
@@ -2798,6 +3001,10 @@ export declare const Remote: {
2798
3001
  OverlayLifted: {
2799
3002
  id: Schema.String;
2800
3003
  };
3004
+ WindowGrown: {
3005
+ window: Schema.String;
3006
+ size: Schema.Number;
3007
+ };
2801
3008
  LiveReceived: {
2802
3009
  stream: Schema.String;
2803
3010
  event: Schema.Unknown;
@@ -2811,6 +3018,18 @@ export declare const Remote: {
2811
3018
  connection: Schema.String;
2812
3019
  page: Schema.Unknown;
2813
3020
  refreshes: Schema.optional<Schema.Boolean>;
3021
+ select: Schema.optional<Schema.Codec<RelationRequirement, RelationRequirement, never, never>>;
3022
+ entities: Schema.optional<Schema.$Array<Schema.Struct<{
3023
+ readonly entity: Schema.String;
3024
+ readonly id: Schema.String;
3025
+ readonly values: Schema.$Record<Schema.String, Schema.Unknown>;
3026
+ }>>>;
3027
+ settled: Schema.optional<Schema.$Array<Schema.Struct<{
3028
+ readonly entity: Schema.String;
3029
+ readonly id: Schema.String;
3030
+ readonly fields: Schema.$Array<Schema.String>;
3031
+ }>>>;
3032
+ now: Schema.optional<Schema.Number>;
2814
3033
  };
2815
3034
  ConnectionInvalidated: {
2816
3035
  connection: Schema.String;
@@ -2903,6 +3122,7 @@ export declare const Remote: {
2903
3122
  readonly entity: Schema.String;
2904
3123
  readonly id: Schema.String;
2905
3124
  }>>>;
3125
+ now: Schema.Number;
2906
3126
  };
2907
3127
  MutationFailed: {
2908
3128
  requestId: Schema.String;
@@ -2918,6 +3138,10 @@ export declare const Remote: {
2918
3138
  OverlayLifted: {
2919
3139
  id: Schema.String;
2920
3140
  };
3141
+ WindowGrown: {
3142
+ window: Schema.String;
3143
+ size: Schema.Number;
3144
+ };
2921
3145
  LiveReceived: {
2922
3146
  stream: Schema.String;
2923
3147
  event: Schema.Unknown;
@@ -2931,6 +3155,18 @@ export declare const Remote: {
2931
3155
  connection: Schema.String;
2932
3156
  page: Schema.Unknown;
2933
3157
  refreshes: Schema.optional<Schema.Boolean>;
3158
+ select: Schema.optional<Schema.Codec<RelationRequirement, RelationRequirement, never, never>>;
3159
+ entities: Schema.optional<Schema.$Array<Schema.Struct<{
3160
+ readonly entity: Schema.String;
3161
+ readonly id: Schema.String;
3162
+ readonly values: Schema.$Record<Schema.String, Schema.Unknown>;
3163
+ }>>>;
3164
+ settled: Schema.optional<Schema.$Array<Schema.Struct<{
3165
+ readonly entity: Schema.String;
3166
+ readonly id: Schema.String;
3167
+ readonly fields: Schema.$Array<Schema.String>;
3168
+ }>>>;
3169
+ now: Schema.optional<Schema.Number>;
2934
3170
  };
2935
3171
  ConnectionInvalidated: {
2936
3172
  connection: Schema.String;
@@ -3030,6 +3266,17 @@ export declare const Remote: {
3030
3266
  * stale field reads as `Refreshing`: an observer is refetching it.
3031
3267
  */
3032
3268
  select: <AppModel, Store extends RemoteModel, Names extends string, Value, Name extends string>(bound: BoundRemote<AppModel, Store, Names>, given: EntitySelection<Value, Name> & Registered<Name, Names, "Entity">) => (id: string) => Projection<AppModel, RemoteData<Value>>;
3269
+ /**
3270
+ * When what a projection shows was last received, and whether it is stale
3271
+ * or loading. Pure, so a view can show "updated 5s ago" without I/O.
3272
+ *
3273
+ * `updatedAt` is the newest server write among what is shown, as the
3274
+ * store dates it; `undefined` when nothing shown was received (nothing
3275
+ * loaded, or only an optimistic preview). `stale` is any shown field or
3276
+ * connection marked stale; `loading` is any of them in flight. Optimistic
3277
+ * previews do not date: showing only one reads `updatedAt: undefined`.
3278
+ */
3279
+ meta: <AppModel, Store extends RemoteModel, Value>(bound: BoundRemote<AppModel, Store>, model: AppModel, projection: Projection<AppModel, Value>) => ReadMeta;
3033
3280
  /**
3034
3281
  * The pure plan for a projection against a Model: the requirements its
3035
3282
  * remote store does not satisfy, under `options` (freshness, force). A
@@ -3121,8 +3368,10 @@ export declare const Remote: {
3121
3368
  /**
3122
3369
  * Runs a `Query` through `RemoteClient`, encoding its input from the ref.
3123
3370
  * Pair the result with `Remote.queryMessage` to merge the page into the Model.
3371
+ * With `select`, the server also returns the selected fields of the page's
3372
+ * items, so one response populates both the connection and the store.
3124
3373
  */
3125
- query: <Name extends string, Input>(ref: QueryRef<Name, Input>) => Effect.Effect<{
3374
+ query: <Name extends string, Input>(ref: QueryRef<Name, Input>, select?: RelationRequirement | undefined) => Effect.Effect<{
3126
3375
  readonly edges: readonly {
3127
3376
  readonly entity: string;
3128
3377
  readonly id: string;
@@ -3144,9 +3393,21 @@ export declare const Remote: {
3144
3393
  } | {
3145
3394
  readonly _tag: "Unknown";
3146
3395
  };
3396
+ readonly entities?: readonly {
3397
+ readonly entity: string;
3398
+ readonly id: string;
3399
+ readonly values: {
3400
+ readonly [x: string]: unknown;
3401
+ };
3402
+ }[] | undefined;
3403
+ readonly settled?: readonly {
3404
+ readonly entity: string;
3405
+ readonly id: string;
3406
+ readonly fields: readonly string[];
3407
+ }[] | undefined;
3147
3408
  }, RemoteQueryError, RemoteClient>;
3148
3409
  /** The `RemoteMessage` that merges a query page into its connection. */
3149
- queryMessage: <Name extends string, Input>(ref: QueryRef<Name, Input>, result: Schema.Schema.Type<typeof QueryResult>) => RemoteMessage;
3410
+ queryMessage: <Name extends string, Input>(ref: QueryRef<Name, Input>, result: Schema.Schema.Type<typeof QueryResult>, select?: RelationRequirement | undefined, now?: number | undefined) => RemoteMessage;
3150
3411
  /**
3151
3412
  * The edges a connection shows: its server-known region with pending and
3152
3413
  * confirmed overlays placed around it, minus removed edges and edges whose
@@ -3205,11 +3466,7 @@ export declare const Remote: {
3205
3466
  * read, since nothing was being read. The resume cursor is read
3206
3467
  * from `RemoteModel.live`, so the application tracks no cursor of its own.
3207
3468
  */
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, {
3209
- readonly requirements: ReadonlyArray<Requirement>;
3210
- readonly cursor: LiveCursor;
3211
- readonly floor: number;
3212
- }, RemoteClient>;
3469
+ live: <AppModel, Store extends RemoteModel, Names extends string, Model, SurfaceMessage, Params, Message = RemoteMessage>(bound: BoundRemote<AppModel, Store, Names>, surface: Surface<AppModel, Model, SurfaceMessage, Params>, params: Params, toMessage?: (message: RemoteMessage) => Message, options?: LiveOptions) => EntryWithoutKeepAlive<AppModel, Message, LiveDependencies, RemoteClient>;
3213
3470
  };
3214
3471
  //#endregion
3215
3472
  export { Entity$1 as Entity, type RemoteResumePart };