foldkit-remote 0.4.0 → 0.6.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,6 @@
1
1
  import { Context, Duration, Effect, Layer, Option, Schema, Stream } from "effect";
2
2
  import * as Domain from "foldkit-entity";
3
+ import { AnyEntity, AnyQuery, Dependencies, InputExpr, Query as Query$1 } from "foldkit-entity";
3
4
  import { EntryWithoutKeepAlive } from "foldkit/subscription";
4
5
  import { ActiveSurface, Contract, Invalid, Metadata, ModelRef, Projection, Surface, Wiring } from "foldkit-surface";
5
6
  import { Rpc, RpcGroup } from "effect/unstable/rpc";
@@ -126,6 +127,21 @@ export type RemoteData<A> = {
126
127
  } | {
127
128
  readonly _tag: 'NotFound';
128
129
  };
130
+ /**
131
+ * What `RemoteData.render` tells the `data` branch about the value it is
132
+ * given: whether a newer answer is on its way, and whether this one is what
133
+ * was last known good before a read failed. It is one tag rather than two
134
+ * booleans because a value cannot be both at once, and a type that can say so
135
+ * invites a view to handle a state that never arrives.
136
+ */
137
+ export type Freshness = {
138
+ readonly _tag: 'Fresh';
139
+ } | {
140
+ readonly _tag: 'Refreshing';
141
+ } | {
142
+ readonly _tag: 'Stale';
143
+ readonly error: RemoteError;
144
+ };
129
145
  export declare const remoteErrorSchema: Schema.Struct<{
130
146
  readonly _tag: Schema.String;
131
147
  readonly message: Schema.String;
@@ -146,6 +162,36 @@ export declare const RemoteData: {
146
162
  readonly Failed: (error: RemoteError, previous: Option.Option<A>) => R;
147
163
  readonly NotFound: () => R;
148
164
  }) => R;
165
+ /**
166
+ * The view-oriented fold: the six states as the three things a view actually
167
+ * draws, under the policy that useful data stays on screen. A value that is
168
+ * being refetched, or that a later read failed to replace, still reaches
169
+ * `data` — with `freshness` saying which — instead of being replaced by a
170
+ * spinner or an error the reader cannot act on.
171
+ *
172
+ * ```ts
173
+ * RemoteData.render(model.user, {
174
+ * loading: () => UserSkeleton(),
175
+ * notFound: () => NoSuchUser(),
176
+ * failed: error => ErrorView(error),
177
+ * data: (user, freshness) => UserView({ user, dimmed: freshness._tag !== 'Fresh' }),
178
+ * })
179
+ * ```
180
+ *
181
+ * `notFound` is its own branch and not optional: a row the server answered
182
+ * for and does not have is neither loading nor a failure, and drawing it as
183
+ * either is a spinner that never ends or an error nobody can fix.
184
+ *
185
+ * This does not replace `match`, which is the exhaustive fold over the states
186
+ * themselves. Reach for `match` when the six states really do draw
187
+ * differently, and for this when they draw the usual three ways.
188
+ */
189
+ render: <A, R>(data: RemoteData<A>, cases: {
190
+ readonly loading: () => R;
191
+ readonly notFound: () => R;
192
+ readonly failed: (error: RemoteError) => R;
193
+ readonly data: (value: A, freshness: Freshness) => R;
194
+ }) => R;
149
195
  map: <A, B>(data: RemoteData<A>, f: (value: A) => B) => RemoteData<B>;
150
196
  };
151
197
  //#endregion
@@ -321,7 +367,30 @@ export interface QueryDescriptor<Name extends string, Input, Result> {
321
367
  readonly Input: Schema.Codec<Input>;
322
368
  readonly Result: Result;
323
369
  readonly ref: (input: Input) => QueryRef<Name, Input>;
370
+ /**
371
+ * What the query means, when it was declared with `Query.define`: the rows it
372
+ * is about, as a value an interpreter can compile. A descriptor from
373
+ * `Query.make` has none — its meaning lives in whatever the server registered
374
+ * to answer it — and a source is free to answer either.
375
+ */
376
+ readonly body?: AnyQuery | undefined;
324
377
  }
378
+ /**
379
+ * The inputs of a definition's body: one placeholder per key of `Input`, typed
380
+ * as that key's value. A body is built once, so these stand for what the query
381
+ * will be given rather than being it.
382
+ */
383
+ export type QueryInputs<Input> = { readonly [K in keyof Input & string]: InputExpr<Input[K]>; };
384
+ /**
385
+ * What separates a query's name from its canonical encoded input in a
386
+ * connection identity.
387
+ *
388
+ * NUL, because `JSON.stringify` escapes it inside a string (`\u0000`) and a
389
+ * query name is a TypeScript identifier — so the first occurrence is always the
390
+ * boundary, and a name or an input value cannot forge one. Anything recovering
391
+ * the query from an identity splits here.
392
+ */
393
+ export declare const IDENTITY_SEPARATOR = "\0";
325
394
  /** Stable stringify: object keys sorted, undefined-valued keys dropped, so equal inputs encode equally. */
326
395
  export declare const stableStringify: (value: unknown) => string;
327
396
  /**
@@ -332,6 +401,8 @@ export declare const stableStringify: (value: unknown) => string;
332
401
  export type ResultOf<Result> = Result extends {
333
402
  readonly name: infer Entity extends string;
334
403
  } ? ConnectionSpec<Entity> : Result;
404
+ /** A relational `Query` over one Entity, as `Query.define`'s body returns. */
405
+ type RelationalQueryOf<E extends AnyEntity> = ReturnType<typeof Query$1.from<E>>;
335
406
  export declare const Query: {
336
407
  /** Declares the entity and options a query's result is a connection over. */
337
408
  connection: <Entity extends string>(entity: {
@@ -349,10 +420,46 @@ export declare const Query: {
349
420
  readonly Input: Input;
350
421
  readonly Result: Result;
351
422
  }) => QueryDescriptor<Name, TypeOf<Input>, ResultOf<Result>>;
423
+ /**
424
+ * Declares a query by what it means. The body is built once, here, and the
425
+ * `input` it is given holds placeholders rather than values — there is
426
+ * nothing yet to branch on, so a condition that depends on what was passed is
427
+ * a comparison over the placeholder:
428
+ *
429
+ * ```ts
430
+ * const PostsBySlug = Query.define('PostsBySlug', { slug: Schema.String }, ({ input }) =>
431
+ * Query.from(Post).pipe(
432
+ * Query.where(Expr.eq(Post.fields.slug, input.slug)),
433
+ * Query.orderBy(Order.asc(Post.fields.id)),
434
+ * ),
435
+ * )
436
+ * ```
437
+ *
438
+ * What comes back is an ordinary `QueryDescriptor` — the same name, `Input`,
439
+ * `ref` and connection identity `Query.make` gives — carrying its `body`
440
+ * besides. A server can compile that body instead of being told the same
441
+ * thing again in its own dialect, and one that would rather answer the query
442
+ * its own way still can.
443
+ *
444
+ * The result is a connection over the Entity the body reads, so it is not
445
+ * named twice.
446
+ */
447
+ define: <const Name extends string, Input extends SchemaOrFields, E extends AnyEntity>(name: Name, Input: Input, body: (context: {
448
+ readonly input: QueryInputs<TypeOf<Input>>;
449
+ }) => RelationalQueryOf<E>, options?: {
450
+ readonly edgeKey?: Schema.Schema<unknown>;
451
+ readonly live?: LivePolicy;
452
+ }) => QueryDescriptor<Name, TypeOf<Input>, ConnectionSpec<E["name"]>>;
352
453
  first: (count: number) => <R extends QueryRef<string, unknown>>(ref: R) => R;
353
454
  last: (count: number) => <R extends QueryRef<string, unknown>>(ref: R) => R;
354
455
  after: (cursor: Cursor) => <R extends QueryRef<string, unknown>>(ref: R) => R;
355
456
  before: (cursor: Cursor) => <R extends QueryRef<string, unknown>>(ref: R) => R;
457
+ from: <E extends AnyEntity>(entity: E) => Query$1<E>;
458
+ where: (...predicates: ReadonlyArray<import("foldkit-entity").Predicate>) => <E extends AnyEntity>(self: Query$1<E>) => Query$1<E>;
459
+ orderBy: (...terms: ReadonlyArray<import("foldkit-entity").OrderTerm>) => <E extends AnyEntity>(self: Query$1<E>) => Query$1<E>;
460
+ dependencies: (self: AnyQuery) => import("foldkit-entity").Dependencies;
461
+ unsupported: (self: AnyQuery, supported: ReadonlyArray<import("foldkit-entity").Operation>) => ReadonlyArray<import("foldkit-entity").Operation>;
462
+ show: (self: AnyQuery) => string;
356
463
  };
357
464
  //#endregion
358
465
  //#region src/requirement.d.ts
@@ -556,8 +663,6 @@ export interface BoundaryCounts {
556
663
  export interface LiveState {
557
664
  /** Last applied cursor for this stream. */
558
665
  readonly cursor: LiveCursor;
559
- /** Connections marked stale by an invalidating event. */
560
- readonly stale: ReadonlySet<string>;
561
666
  /** Edges recorded outside the loaded boundary, not visible in `items`. */
562
667
  readonly boundary: Readonly<Record<string, BoundaryCounts>>;
563
668
  }
@@ -570,7 +675,6 @@ export type LiveOutcome = 'applied' | 'duplicate' | 'gap';
570
675
  * spurious gap.
571
676
  */
572
677
  export declare const classifyLive: (state: LiveState, cursor: LiveCursor) => LiveOutcome;
573
- export declare const invalidateConnection: (state: LiveState, connection: string) => LiveState;
574
678
  /**
575
679
  * A subscriber is woken only if the event changed a field it selects. A
576
680
  * change to an unselected field is skipped entirely.
@@ -588,17 +692,17 @@ export interface ConnectionApplied {
588
692
  readonly state: LiveState;
589
693
  readonly optimistic: OptimisticState;
590
694
  readonly outcome: LiveOutcome;
695
+ /**
696
+ * A connection the event says to refetch. Reported rather than recorded here,
697
+ * because staleness lives on the connection in the Model — which is what the
698
+ * planner and every read consult. It used to be written into a set on this
699
+ * live state that nothing read, so an invalidating event did nothing at all.
700
+ */
701
+ readonly invalidated?: string | undefined;
591
702
  }
592
703
  export declare const applyConnectionEvent: (state: LiveState, optimistic: OptimisticState, event: Extract<LiveEvent, {
593
704
  _tag: "ConnectionInsert" | "ConnectionRemove" | "ConnectionInvalidate";
594
705
  }>, policy?: LivePolicy) => ConnectionApplied;
595
- export declare const isStale: (state: LiveState, connection: string) => boolean;
596
- /**
597
- * Clears a connection's stale mark once a fresh page has been adopted. An
598
- * invalidating event only records that a refetch is due; whoever merges the
599
- * refetched page calls this so `isStale` stops reporting.
600
- */
601
- export declare const refreshConnection: (state: LiveState, connection: string) => LiveState;
602
706
  /** `hasPrevious` accounting for edges recorded outside the loaded boundary. */
603
707
  export declare const liveHasPrevious: (connection: Connection, state: LiveState, connectionId: string) => boolean;
604
708
  /** `hasNext` accounting for edges recorded outside the loaded boundary. */
@@ -636,7 +740,33 @@ export declare const gc: (state: Retained & {
636
740
  //#endregion
637
741
  //#region src/persistence.d.ts
638
742
  /** Bump when the serialized shape changes; a mismatch discards the cache. */
639
- export declare const REMOTE_CACHE_VERSION = 3;
743
+ export declare const REMOTE_CACHE_VERSION = 4;
744
+ /**
745
+ * What survives a reload: the entity cache, and the connections an application
746
+ * **declared** should come back.
747
+ *
748
+ * Connections are session state by default and stay that way — `snapshotOf`
749
+ * takes none unless asked. What a declared one keeps is its **edges and
750
+ * nothing else**, which is not a simplification but the rule: a connection's
751
+ * boundaries are cursors the server minted, and a cursor may name server state
752
+ * that no longer exists. Restoring one would be claiming to know where the
753
+ * page ends. The shape has nowhere to put a cursor, so that cannot be got
754
+ * wrong later.
755
+ */
756
+ export interface Snapshot {
757
+ readonly entities: EntityStore;
758
+ /**
759
+ * By connection identity: its **segments**, each a run of edges. No
760
+ * boundaries.
761
+ *
762
+ * Segments rather than one flat list, because the gaps between them are
763
+ * knowledge too. A connection paged from both ends holds a head and a tail
764
+ * with an unfetched middle; flattening those into one run would claim the
765
+ * tail's first row follows the head's last, which is a thing the client was
766
+ * never told.
767
+ */
768
+ readonly connections: Readonly<Record<string, ReadonlyArray<ReadonlyArray<Edge>>>>;
769
+ }
640
770
  export interface SnapshotOptions {
641
771
  /**
642
772
  * Who or what the snapshot is for (a user id, a tenant, a build). A snapshot
@@ -650,36 +780,50 @@ export interface SnapshotOptions {
650
780
  /** How a snapshot meets a store that already has entries. */
651
781
  export type MergePolicy = 'replace' | 'preserve-existing';
652
782
  /**
653
- * The snapshot text, or `undefined` when it would exceed `maxBytes`. Only the
654
- * entity store goes in; pass `model.entities`, never the whole `RemoteModel`.
783
+ * The snapshot text, or `undefined` when it would exceed `maxBytes`. Build the
784
+ * snapshot with `snapshotOf`, which is where the connections that survive are
785
+ * named; runtime state — optimistic layers, live cursors, gaps, the mutation
786
+ * ledger — is never in one.
655
787
  */
656
- declare function dehydrate(store: EntityStore, options?: SnapshotOptions & {
788
+ declare function dehydrate(snapshot: Snapshot, options?: SnapshotOptions & {
657
789
  readonly maxBytes?: undefined;
658
790
  }): string;
659
- declare function dehydrate(store: EntityStore, options?: SnapshotOptions): string | undefined;
791
+ declare function dehydrate(snapshot: Snapshot, options?: SnapshotOptions): string | undefined;
660
792
  export declare const RemotePersistence: {
661
793
  /** The snapshot text, or `undefined` when it would exceed `maxBytes`. */
662
794
  dehydrate: typeof dehydrate;
663
- /** The store a snapshot text holds, or `undefined` when it is refused. */
664
- hydrate: (raw: string | undefined | null, options?: SnapshotOptions) => EntityStore | undefined;
665
- /** A snapshot brought into a store by policy. */
795
+ /** What a snapshot text holds, or `undefined` when it is refused. */
796
+ hydrate: (raw: string | undefined | null, options?: SnapshotOptions) => Snapshot | undefined;
797
+ /**
798
+ * What to keep of a Model, naming the connections that should survive a
799
+ * reload. Naming none is the default and is what Remote always did.
800
+ */
801
+ snapshotOf: (model: {
802
+ readonly entities: EntityStore;
803
+ readonly connections: Readonly<Record<string, Connection>>;
804
+ }, options?: {
805
+ readonly connections?: ReadonlyArray<ConnectionIdentity> | undefined;
806
+ }) => Snapshot;
807
+ /** A snapshot's entities brought into a store by policy. */
666
808
  mergeStores: (current: EntityStore, snapshot: EntityStore, policy: MergePolicy) => EntityStore;
809
+ /** The cache as a fresh session has it; what a refused `restore` yields. */
810
+ emptySnapshot: Snapshot;
667
811
  /**
668
812
  * Writes the snapshot under `key`, or removes the key when the store would
669
813
  * exceed `maxBytes`, so a stale smaller snapshot cannot outlive a larger
670
814
  * store it no longer describes.
671
815
  */
672
- save: (store: EntityStore, options: {
816
+ save: (snapshot: Snapshot, options: {
673
817
  readonly key: string;
674
818
  } & SnapshotOptions) => Effect.Effect<void, KeyValueStore.KeyValueStoreError, KeyValueStore.KeyValueStore>;
675
819
  /**
676
- * Reads the cache. A missing key yields `emptyStore`; anything `hydrate`
677
- * refuses yields `emptyStore` and removes the key, so the next `restore` is
678
- * clean and the planner refetches.
820
+ * Reads the cache. A missing key yields `emptySnapshot`; anything `hydrate`
821
+ * refuses yields `emptySnapshot` and removes the key, so the next `restore`
822
+ * is clean and the planner refetches.
679
823
  */
680
824
  restore: (options: {
681
825
  readonly key: string;
682
- } & SnapshotOptions) => Effect.Effect<Readonly<Record<string, EntityEntry>>, KeyValueStore.KeyValueStoreError, KeyValueStore.KeyValueStore>;
826
+ } & SnapshotOptions) => Effect.Effect<Snapshot, KeyValueStore.KeyValueStoreError, KeyValueStore.KeyValueStore>;
683
827
  };
684
828
  //#endregion
685
829
  //#region src/wire.d.ts
@@ -1160,15 +1304,52 @@ export interface RemoteModel {
1160
1304
  */
1161
1305
  readonly loading: ReadonlySet<string>;
1162
1306
  /**
1163
- * `Remote.refresh` generations. The read entries restart when `requested`
1164
- * moves, so a read sent before a refresh cannot land after it; `started` is
1165
- * the latest generation a read began under.
1307
+ * `Remote.refresh` generations, held per field rather than for the store as a
1308
+ * whole. A read entry restarts when the generation over the fields it plans
1309
+ * moves, so a read sent before a refresh of its own fields cannot land after
1310
+ * it, and a refresh of one field leaves every other entry's read alone.
1166
1311
  */
1167
- readonly refresh: {
1168
- readonly requested: number;
1169
- readonly started: number;
1170
- };
1312
+ readonly refresh: RefreshState;
1313
+ /**
1314
+ * The reads that failed and have not been settled since, so a read can say
1315
+ * `Failed` rather than `Initial`.
1316
+ *
1317
+ * Without them a read that failed before anything loaded read exactly like
1318
+ * one nobody had asked for, and nothing asked again: a read entry restarts
1319
+ * only when what it plans changes, and a failure changed nothing. What failed
1320
+ * is not planned again on its own either, so a persistent error is not
1321
+ * retried every time an unrelated read restarts the entry. `Remote.refresh`
1322
+ * is how a view retries.
1323
+ */
1324
+ readonly failures: Failures;
1325
+ }
1326
+ /**
1327
+ * Failed reads, as the errors they failed with.
1328
+ *
1329
+ * `connections` is by connection identity, and is settled by a page arriving,
1330
+ * an invalidation, or retention dropping the connection. `fields` is by field
1331
+ * mark (`entity\0id\0field`, the marks `loading` uses), and is settled by the
1332
+ * field being written again, a refresh asking for it, or retention dropping the
1333
+ * entity. A read of it in flight reads `Loading` over the failure without
1334
+ * settling it: if that read fails too, the failure is simply recorded again.
1335
+ */
1336
+ export interface Failures {
1337
+ readonly connections: Readonly<Record<string, RemoteError>>;
1338
+ readonly fields: Readonly<Record<string, RemoteError>>;
1171
1339
  }
1340
+ export declare const noFailures: Failures;
1341
+ /**
1342
+ * `Remote.refresh` generations, per field mark. `generation` is the last one
1343
+ * handed out; `requested` is when each field was last refreshed, and `started`
1344
+ * is the generation each field's read last began under. Holding them per field
1345
+ * is what keeps one Projection's refresh from restarting every read entry.
1346
+ */
1347
+ export interface RefreshState {
1348
+ readonly generation: number;
1349
+ readonly requested: ReadonlyMap<string, number>;
1350
+ readonly started: ReadonlyMap<string, number>;
1351
+ }
1352
+ export declare const emptyRefresh: RefreshState;
1172
1353
  export declare const initialRemoteModel: RemoteModel;
1173
1354
  export declare const remoteModelSchema: () => Schema.Codec<RemoteModel, unknown>;
1174
1355
  /** The submodel's Messages; each reduces to `RemoteModel` through `updateRemote`. */
@@ -1181,6 +1362,13 @@ export type RemoteMessage = {
1181
1362
  readonly _tag: 'ReadFailed';
1182
1363
  readonly requests: readonly Requirement[];
1183
1364
  readonly error: RemoteError;
1365
+ /**
1366
+ * The live stream whose subscription broke, when this is that rather
1367
+ * than a read. Nothing was being read, so no field failed: the stream is
1368
+ * recorded as a gap, since changes after it are missed until the host
1369
+ * resubscribes.
1370
+ */
1371
+ readonly stream?: string | undefined;
1184
1372
  } |
1185
1373
  /** A refetch of present fields began; they read as `Refreshing` until it lands. */
1186
1374
  {
@@ -1194,6 +1382,15 @@ export type RemoteMessage = {
1194
1382
  /** The refresh generation the read was planned under. */
1195
1383
  readonly refresh?: number | undefined;
1196
1384
  } |
1385
+ /**
1386
+ * Queries for these connections were sent. One with nothing to show reads
1387
+ * `Loading` until its page or its failure arrives, which is what separates
1388
+ * "being fetched" from `Initial`, "nothing is fetching this".
1389
+ */
1390
+ {
1391
+ readonly _tag: 'QueryStarted';
1392
+ readonly connections: readonly string[];
1393
+ } |
1197
1394
  /** The active Surfaces' roots changed; everything they do not reach is collected. */
1198
1395
  {
1199
1396
  readonly _tag: 'RetentionChanged';
@@ -1203,6 +1400,13 @@ export type RemoteMessage = {
1203
1400
  {
1204
1401
  readonly _tag: 'Hydrated';
1205
1402
  readonly entities: EntityStore;
1403
+ /**
1404
+ * The connections a snapshot declared should survive, as their edges.
1405
+ * Restored with **unknown** boundaries and marked stale: the rows show
1406
+ * at once and the connection is refetched, because a persisted cursor
1407
+ * may name server state that is gone.
1408
+ */
1409
+ readonly connections?: Readonly<Record<string, ReadonlyArray<ReadonlyArray<Edge>>>> | undefined;
1206
1410
  readonly merge: MergePolicy;
1207
1411
  } |
1208
1412
  /** A mutation began; its optimistic operations show until it settles. */
@@ -1225,6 +1429,19 @@ export type RemoteMessage = {
1225
1429
  readonly _tag: 'MutationFailed';
1226
1430
  readonly requestId: string;
1227
1431
  readonly error: RemoteError;
1432
+ } |
1433
+ /**
1434
+ * Operations shown over the store with no request behind them, until they are
1435
+ * lifted: a preview of a change nobody has made. Showing an id again replaces
1436
+ * what it showed.
1437
+ */
1438
+ {
1439
+ readonly _tag: 'OverlayShown';
1440
+ readonly id: string;
1441
+ readonly optimistic: ReadonlyArray<OptimisticOperation>;
1442
+ } | {
1443
+ readonly _tag: 'OverlayLifted';
1444
+ readonly id: string;
1228
1445
  } | {
1229
1446
  readonly _tag: 'LiveReceived';
1230
1447
  readonly stream: string;
@@ -1247,7 +1464,11 @@ export type RemoteMessage = {
1247
1464
  readonly _tag: 'ConnectionRefreshed';
1248
1465
  readonly connection: string;
1249
1466
  } |
1250
- /** A query for the connection failed; it reads as it did before the request. */
1467
+ /**
1468
+ * A query for the connection failed. Its pages stay as they were, and it
1469
+ * reads `Failed` — with them as `previous`, when it has any — until a page
1470
+ * arrives, it is invalidated, or retention drops it.
1471
+ */
1251
1472
  {
1252
1473
  readonly _tag: 'QueryFailed';
1253
1474
  readonly connection: string;
@@ -1282,6 +1503,7 @@ export declare const remoteMessageCases: {
1282
1503
  readonly _tag: Schema.String;
1283
1504
  readonly message: Schema.String;
1284
1505
  }>;
1506
+ stream: Schema.optional<Schema.String>;
1285
1507
  };
1286
1508
  RefreshStarted: {
1287
1509
  requests: Schema.$Array<Schema.Codec<Requirement, Requirement, never, never>>;
@@ -1290,6 +1512,9 @@ export declare const remoteMessageCases: {
1290
1512
  requests: Schema.$Array<Schema.Codec<Requirement, Requirement, never, never>>;
1291
1513
  refresh: Schema.optional<Schema.Number>;
1292
1514
  };
1515
+ QueryStarted: {
1516
+ connections: Schema.$Array<Schema.String>;
1517
+ };
1293
1518
  RetentionChanged: {
1294
1519
  roots: Schema.Struct<{
1295
1520
  readonly requirements: Schema.$Array<Schema.Codec<Requirement, Requirement, never, never>>;
@@ -1300,6 +1525,7 @@ export declare const remoteMessageCases: {
1300
1525
  }>;
1301
1526
  };
1302
1527
  Hydrated: {
1528
+ connections: Schema.optional<Schema.Unknown>;
1303
1529
  entities: Schema.Unknown;
1304
1530
  merge: Schema.Union<readonly [Schema.Literal<"replace">, Schema.Literal<"preserve-existing">]>;
1305
1531
  };
@@ -1327,6 +1553,13 @@ export declare const remoteMessageCases: {
1327
1553
  readonly message: Schema.String;
1328
1554
  }>;
1329
1555
  };
1556
+ OverlayShown: {
1557
+ id: Schema.String;
1558
+ optimistic: Schema.$Array<Schema.Unknown>;
1559
+ };
1560
+ OverlayLifted: {
1561
+ id: Schema.String;
1562
+ };
1330
1563
  LiveReceived: {
1331
1564
  stream: Schema.String;
1332
1565
  event: Schema.Unknown;
@@ -1374,6 +1607,27 @@ export declare const isRemoteMessage: <M extends {
1374
1607
  readonly _tag: RemoteMessageTag;
1375
1608
  }>;
1376
1609
  export declare const remoteMessageSchema: Schema.Schema<RemoteMessage>;
1610
+ /** Whether a query for this connection is in flight. */
1611
+ export declare const isQueryLoading: (model: RemoteModel, identity: string) => boolean;
1612
+ /**
1613
+ * The generation a read is planned under: the highest any of the fields it
1614
+ * reads or connections it runs was refreshed at. A read entry carries this as a
1615
+ * dependency, so it restarts when what it observes is refreshed and not when
1616
+ * anything else is.
1617
+ */
1618
+ export declare const refreshedAt: (refresh: RefreshState, requests: ReadonlyArray<Requirement>, connections?: ReadonlyArray<string>) => number;
1619
+ /**
1620
+ * Marks every field of `requests` and every connection in `connections`
1621
+ * refreshed at the next generation.
1622
+ */
1623
+ export declare const withRefreshRequested: (refresh: RefreshState, requests: ReadonlyArray<Requirement>, connections?: ReadonlyArray<string>) => RefreshState;
1624
+ /**
1625
+ * Whether one field's read already began under the generation that field was
1626
+ * last refreshed at. Such a read is in flight and would otherwise outlive the
1627
+ * refresh, so the refresh takes a new generation to restart it even though the
1628
+ * field is already stale.
1629
+ */
1630
+ export declare const refreshIsInFlight: (refresh: RefreshState, request: Requirement, field: string) => boolean;
1377
1631
  /**
1378
1632
  * Whether a read is fetching any of `fields` for this entity. Consulted only
1379
1633
  * when the store lacks the value, which is what separates `Loading` from
@@ -1386,6 +1640,15 @@ export declare const isLoading: (model: RemoteModel, entity: string, id: string,
1386
1640
  * can resubscribe rather than silently miss facts.
1387
1641
  */
1388
1642
  export declare const updateRemote: (model: RemoteModel, message: RemoteMessage) => RemoteModel;
1643
+ /** Whether this field's last read failed and nothing has settled it since. */
1644
+ export declare const isFieldFailed: (model: RemoteModel, entity: string, id: string, field: string) => boolean;
1645
+ /**
1646
+ * The first failure among the fields `relation` reads of an entity, following
1647
+ * each relation the store holds into its targets, as `assemble` does. A read
1648
+ * that consults it shows `Failed` for a related entity's failure too, rather
1649
+ * than waiting on a field nothing will fetch.
1650
+ */
1651
+ export declare const failureOf: (model: RemoteModel, entity: string, id: string, relation: RelationRequirement) => RemoteError | undefined;
1389
1652
  /**
1390
1653
  * Writes a read result into the store, recording each field's applied window so
1391
1654
  * a later request with a different window refetches. A relation page requested
@@ -1502,6 +1765,25 @@ export interface RelationShape {
1502
1765
  readonly nullable: boolean;
1503
1766
  readonly entity: string;
1504
1767
  }
1768
+ /**
1769
+ * A page of a relation that is a whole list is read under a name of its own, the
1770
+ * field's name and the page's size: `comments@first=10`. It is a field like any
1771
+ * other to the store, the planner, and the wire, so the whole list and a page of
1772
+ * it are held side by side, merged, marked stale, and persisted each on its own.
1773
+ * The server reads the name apart, reads the relation with the window, and
1774
+ * answers under the alias. A cursor is not part of the name: a page read from a
1775
+ * cursor merges onto the page it continues.
1776
+ */
1777
+ export declare const RELATION_ALIAS = "@";
1778
+ /** The alias a relation field is read under, for a window of this size. */
1779
+ export declare const relationAlias: (field: string, window: {
1780
+ readonly first?: number | undefined;
1781
+ readonly last?: number | undefined;
1782
+ readonly after?: string | undefined;
1783
+ readonly before?: string | undefined;
1784
+ }) => string;
1785
+ /** The field an alias reads; a name that is no alias is its own field. */
1786
+ export declare const aliasedField: (name: string) => string;
1505
1787
  /** Splits a ref key (`"Entity:id"`, the store key too) back into its parts. */
1506
1788
  export declare const refParts: (encoded: string) => RefParts;
1507
1789
  /**
@@ -1685,6 +1967,92 @@ declare const Entity$1: {
1685
1967
  patch: <Name extends string, F extends Schema.Struct.Fields>(ref: EntityRef<Name, F>, patch: Partial<Schema.Struct.Encoded<F>>) => EntityPatch<Name, F>;
1686
1968
  };
1687
1969
  //#endregion
1970
+ //#region src/matching.d.ts
1971
+ /**
1972
+ * An answer over the rows the client holds — never over the rows that exist.
1973
+ *
1974
+ * `complete` is the whole reason this is its own type rather than a `Page`. A
1975
+ * `Page` carries `hasNext`/`hasPrevious`, which are facts the *server* stated
1976
+ * about rows beyond the ones it delivered; a local answer has no such facts and
1977
+ * cannot invent them. What it can say is whether the population it judged was
1978
+ * all of it, which is decidable: a connection terminal at both ends is wholly
1979
+ * held, and nothing was skipped for a missing field.
1980
+ *
1981
+ * An incomplete answer is not wrong — it is an answer about less than the
1982
+ * caller may have meant, and the flag is what lets a view say "3 so far" rather
1983
+ * than "3", or a caller decide to ask the server.
1984
+ */
1985
+ export interface Matched<A> {
1986
+ readonly items: ReadonlyArray<A>;
1987
+ /** Whether every row the answer is about was judged. */
1988
+ readonly complete: boolean;
1989
+ }
1990
+ /**
1991
+ * What the rows the client holds had to say about a body.
1992
+ *
1993
+ * `skipped` is the honest half. A row missing a field the body reads cannot be
1994
+ * judged — it is neither a match nor a non-match — and dropping it silently
1995
+ * would turn "I could not tell" into "no". Naming it lets a caller decide:
1996
+ * fetch the field, ask the server, or say the answer is partial.
1997
+ */
1998
+ export interface Judged {
1999
+ /** Keys of the rows that satisfy the body, in the order the body asks for. */
2000
+ readonly matched: ReadonlyArray<EntityKey>;
2001
+ /** Keys of rows held but missing a field the body reads, so not judged either way. */
2002
+ readonly skipped: ReadonlyArray<EntityKey>;
2003
+ }
2004
+ export interface MatchingOptions {
2005
+ /**
2006
+ * Judge only these keys. Without it every row of the body's Entity in the
2007
+ * store is a candidate, which is what a filter over a loaded list wants; with
2008
+ * it, a caller narrows to a connection's own edges.
2009
+ */
2010
+ readonly among?: ReadonlyArray<EntityKey> | undefined;
2011
+ }
2012
+ /**
2013
+ * The rows of `descriptor`'s Entity that the store holds and the body matches.
2014
+ *
2015
+ * The input is given **decoded**, as an application writes it, and encoded here
2016
+ * through the descriptor's own `Input` codec. That is deliberate and is the
2017
+ * whole reason this takes a descriptor rather than a bare body: a store holds
2018
+ * wire values, so an input has to be in that space to be compared against them,
2019
+ * and handing an interpreter a decoded value is a mistake **no runtime error
2020
+ * catches** — the reference interpreter answers "no rows", which reads exactly
2021
+ * like a correct answer. Doing it here means a caller cannot get it wrong.
2022
+ *
2023
+ * Literals in the body are deliberately *not* encoded. A literal written in
2024
+ * domain terms is already broken against the server, which binds it to a column
2025
+ * the same way; accepting it here would make the client quietly more permissive
2026
+ * than the thing it has to agree with.
2027
+ */
2028
+ export declare const matching: <Name extends string, Input>(store: EntityStore, descriptor: QueryDescriptor<Name, Input, unknown>, input: Input, options?: MatchingOptions) => Judged;
2029
+ /**
2030
+ * Whether one row belongs to a query, as far as the client can tell.
2031
+ *
2032
+ * Three answers, and the third is the one that makes this usable. `'unknown'`
2033
+ * is a row the store never fetched, or holds without a field the body reads —
2034
+ * a caller that collapses it into `'no'` has turned "I could not tell" into an
2035
+ * answer, which is the mistake this whole module is shaped against.
2036
+ *
2037
+ * **Membership is decidable where position is not.** Whether a row satisfies
2038
+ * `eq`, `isNull` or `contains` needs no collation: equality on text is exact
2039
+ * and `contains` folds ASCII, both stated in §6.0.1. *Where* a row sorts needs
2040
+ * text collation, which is the backend's and deliberately outside the
2041
+ * conformant subset — so this answers the half that is always answerable, and
2042
+ * ordering stays the server's until a query can declare its collation.
2043
+ */
2044
+ export type Belongs = 'yes' | 'no' | 'unknown';
2045
+ /**
2046
+ * Takes the input **encoded**, because its one caller holds it that way: a
2047
+ * connection identity carries the canonical encoded input, and making it decode
2048
+ * only for this to encode again would be a round trip that can fail.
2049
+ *
2050
+ * A decoded-input sibling was written first and deleted: nothing called it. The
2051
+ * asymmetry with `matching`, which takes a decoded input, is the shape of the
2052
+ * two real callers rather than an oversight.
2053
+ */
2054
+ export declare const belongsEncoded: <Name extends string, Input>(store: EntityStore, descriptor: QueryDescriptor<Name, Input, unknown>, encoded: Readonly<Record<string, unknown>>, key: EntityKey) => Belongs;
2055
+ //#endregion
1688
2056
  //#region src/inspect.d.ts
1689
2057
  /** A serializable summary of a RemoteModel for DevTools and diagnostics. */
1690
2058
  export interface RemoteInspection {
@@ -1699,6 +2067,19 @@ export interface RemoteInspection {
1699
2067
  readonly connections: ReadonlyArray<string>;
1700
2068
  readonly live: ReadonlyArray<string>;
1701
2069
  readonly gaps: ReadonlyArray<string>;
2070
+ /**
2071
+ * The `entity\0id\0field` marks of reads in flight, and a
2072
+ * `\0connection\0identity` mark for each query in flight. This is what "Remote is
2073
+ * working" means here, and it is read from the Model rather than from the
2074
+ * fibers doing the work: a tool that shows it shows something the Model can
2075
+ * be replayed to, and nothing that needs the runtime to be asked.
2076
+ */
2077
+ readonly loading: ReadonlyArray<string>;
2078
+ /**
2079
+ * The reads that failed and are not yet settled: queries by connection
2080
+ * identity, entity fields by `entity\0id\0field` mark, each with its error.
2081
+ */
2082
+ readonly failures: Failures;
1702
2083
  readonly mutations: {
1703
2084
  readonly pending: ReadonlyArray<string>;
1704
2085
  readonly failed: ReadonlyArray<string>;
@@ -1709,6 +2090,75 @@ export interface RemoteInspection {
1709
2090
  export declare const inspectRemote: (model: RemoteModel) => RemoteInspection;
1710
2091
  /** A pure, serializable view of one entity, or `undefined` if unknown. */
1711
2092
  export declare const inspectEntity: (model: RemoteModel, key: string) => RemoteInspection["entities"][number] | undefined;
2093
+ /**
2094
+ * What one query read is, and what it currently is — data-query-DESIGN §29.1.
2095
+ *
2096
+ * Every member is gathered from something that already existed: the read was
2097
+ * always this many pieces, and nothing had ever been asked to put them in one
2098
+ * place. It is plain serializable data, so a DevTools panel, a log line and a
2099
+ * test assert on the same value.
2100
+ *
2101
+ * Two members of §29.1's sketch are **not** here, and their absence is the
2102
+ * finding rather than an omission:
2103
+ *
2104
+ * - **Surface.** A Projection does not know which Surface reads it, and often
2105
+ * several do — so it is not a property of the read, and `explain` cannot
2106
+ * recover it alone. Given the active Surfaces (the same record
2107
+ * `subscriptions` takes), it reports **every** Surface reading the connection
2108
+ * rather than guessing one, and omits the member entirely when it was not
2109
+ * given them.
2110
+ * - **Executor.** The thing that answers a query is a `RemoteClient` Layer in
2111
+ * the runtime, not a value in the Model, and a pure read of the Model cannot
2112
+ * see it. That is the same boundary that makes this function pure and
2113
+ * replayable, so it is worth more than the line of text.
2114
+ *
2115
+ * And one that the design proposed and this does not want: *expectation*
2116
+ * (required versus optional). §11 left it unbuilt for want of a consumer, and
2117
+ * the explanation was the likeliest consumer. It turns out not to need it — a
2118
+ * query's result is a connection, so the shape is a `Page`, decided by the
2119
+ * definition rather than by the read. Nothing here has an opinion about whether
2120
+ * an empty one is an error, because nothing here has to have one.
2121
+ */
2122
+ export interface QueryExplanation {
2123
+ /** The bound Remote domain that answers it. */
2124
+ readonly domain: string;
2125
+ /** The definition's name. */
2126
+ readonly query: string;
2127
+ /** The input as encoded, which is what the identity is built from. */
2128
+ readonly input: unknown;
2129
+ /** The connection identity: definition plus canonical input, excluding the window. */
2130
+ readonly identity: string;
2131
+ /** How much of the connection this read asks for. */
2132
+ readonly window: QueryWindow;
2133
+ /** What it reads of each item, as the slice asked of the server. */
2134
+ readonly select: RelationRequirement;
2135
+ /**
2136
+ * What the query means, as readable text — `Query.show`, and so neither SQL
2137
+ * nor whatever the backend compiled. Absent for a definition made with
2138
+ * `Query.make`, whose meaning lives on the server that answers it.
2139
+ */
2140
+ readonly body?: string | undefined;
2141
+ /** The fields, inputs and operations the body reads; absent with the body. */
2142
+ readonly dependencies?: Dependencies | undefined;
2143
+ /** What the read answers from this Model right now. */
2144
+ readonly state: RemoteData<unknown>['_tag'];
2145
+ /**
2146
+ * The active Surfaces reading this connection at this Model, if the active
2147
+ * record was supplied. Plural because several may, which is why a Projection
2148
+ * cannot carry the answer itself.
2149
+ */
2150
+ readonly surfaces?: ReadonlyArray<string> | undefined;
2151
+ /**
2152
+ * Why each of those Surfaces is active, where it was placed with
2153
+ * `Surface.when` — the Model path and the tag, without running anything. A
2154
+ * Surface placed with `Surface.at` has no entry: a callback cannot be read.
2155
+ */
2156
+ readonly activation?: ReadonlyArray<{
2157
+ readonly surface: string;
2158
+ readonly path: readonly string[];
2159
+ readonly tag: string;
2160
+ }> | undefined;
2161
+ }
1712
2162
  //#endregion
1713
2163
  //#region src/plan.d.ts
1714
2164
  type Window = NonNullable<Requirement['windows']>[string];
@@ -1774,9 +2224,9 @@ export type EntityLike = EntityDescriptor<any, any> | Domain.AnyEntity;
1774
2224
  * What a read selects: a Remote Selection, or a `foldkit-entity` Selection
1775
2225
  * (read through `Selection.from`).
1776
2226
  */
1777
- export type EntitySelection<Value, Name extends string> = Selection<Value, Name, 'entity'> | Domain.Selection<Name, unknown, Schema.Constraint & {
2227
+ export type EntitySelection<Value, Name extends string, Id extends string = string> = Selection<Value, Name, 'entity'> | Domain.Selection<Name, unknown, Schema.Constraint & {
1778
2228
  readonly Type: Value;
1779
- }>;
2229
+ }, Id>;
1780
2230
  type EntityName<D> = D extends {
1781
2231
  readonly name: infer Name extends string;
1782
2232
  } ? Name : never;
@@ -1858,13 +2308,13 @@ export interface MutationStarted<AppModel> {
1858
2308
  */
1859
2309
  export interface RemoteDomain<AppModel, Store extends RemoteModel, Entities extends readonly EntityLike[], Queries extends readonly QueryDescriptor<any, any, any>[], Mutations extends readonly MutationDescriptor<any, any, any>[]> extends BoundRemote<AppModel, Store, EntityName<Entities[number]>>, RemoteDescriptor<Entities, Queries, Mutations> {
1860
2310
  /** `Remote.select`: a Projection reading one entity through a selection of a registered entity. */
1861
- get<Value, Name extends string>(selection: EntitySelection<Value, Name> & Registered<Name, EntityName<Entities[number]>, 'Entity'>, id: string): Projection<AppModel, RemoteData<Value>>;
2311
+ get<Value, Name extends string, Id extends string = string>(selection: EntitySelection<Value, Name, Id> & Registered<Name, EntityName<Entities[number]>, 'Entity'>, id: NoInfer<Id>): Projection<AppModel, RemoteData<Value>>;
1862
2312
  /**
1863
2313
  * `get`, and the projection also subscribes to the entity's changes: its
1864
2314
  * requirements are marked `live`, so `subscriptions` derives a live entry for
1865
2315
  * the Surfaces that read it.
1866
2316
  */
1867
- live<Value, Name extends string>(selection: EntitySelection<Value, Name> & Registered<Name, EntityName<Entities[number]>, 'Entity'>, id: string): Projection<AppModel, RemoteData<Value>>;
2317
+ live<Value, Name extends string, Id extends string = string>(selection: EntitySelection<Value, Name, Id> & Registered<Name, EntityName<Entities[number]>, 'Entity'>, id: NoInfer<Id>): Projection<AppModel, RemoteData<Value>>;
1868
2318
  /**
1869
2319
  * A query connection read as a `Page` of items selected of the query's
1870
2320
  * entity: `Initial` until the page and every item's selected fields are
@@ -1877,8 +2327,55 @@ export interface RemoteDomain<AppModel, Store extends RemoteModel, Entities exte
1877
2327
  next<Name extends string, Input>(model: AppModel, projection: QueryProjection<AppModel, any, Name, Input>): QueryRef<Name, Input> | undefined;
1878
2328
  /** The `QueryRef` for the page before the loaded start, or `undefined`. */
1879
2329
  previous<Name extends string, Input>(model: AppModel, projection: QueryProjection<AppModel, any, Name, Input>): QueryRef<Name, Input> | undefined;
2330
+ /**
2331
+ * What a query read is and what it currently is, as one serializable value —
2332
+ * data-query-DESIGN §29.1: the domain, the definition and its input, the
2333
+ * connection identity, the window, the Selection, the body as readable text
2334
+ * with what it depends on, and what the read answers from this Model.
2335
+ *
2336
+ * Pure, so a DevTools panel showing it shows something the Model can be
2337
+ * replayed to.
2338
+ *
2339
+ * Given `surfaces` — the same active record `subscriptions` takes — it also
2340
+ * reports which of them read this connection, and why each is active where
2341
+ * that is a readable fact (`Surface.when`) rather than a callback
2342
+ * (`Surface.at`). A Projection cannot carry this itself: several Surfaces may
2343
+ * read one connection, so it is not a property of the read.
2344
+ */
2345
+ explain<Name extends string, Input>(model: AppModel, projection: QueryProjection<AppModel, any, Name, Input>, options?: {
2346
+ readonly surfaces?: Readonly<Record<string, ActiveSurface<AppModel>>> | undefined;
2347
+ }): QueryExplanation;
2348
+ /**
2349
+ * The rows of a loaded list that a body matches, decoded as the list decodes
2350
+ * them — a filter that asks the server nothing.
2351
+ *
2352
+ * **It filters a list; it does not run a query.** That distinction is what
2353
+ * keeps it honest. "Which rows match" would need to know that the list holds
2354
+ * every row the body could match, which is predicate containment and is
2355
+ * deliberately not built. "Which rows *of this list* match" is decidable from
2356
+ * what is already here, and is what a search box over a loaded page actually
2357
+ * wants.
2358
+ *
2359
+ * `complete` says whether the answer is about the whole list: every edge
2360
+ * judged, and the connection terminal at both ends. An incomplete answer is
2361
+ * not wrong — it is about less than the caller may have meant, which is why
2362
+ * it is said rather than left for a view to assume.
2363
+ *
2364
+ * Creates no connection, so there is nothing new to retain and nothing new to
2365
+ * fetch. The server stays authoritative for which rows exist.
2366
+ */
2367
+ 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>;
1880
2368
  /** A Command that runs the query and yields the `ConnectionMerged` (or `QueryFailed`) that reduces it: "load more". */
1881
2369
  fetch(ref: QueryRef<string, unknown>): Command<RemoteMessage, never, RemoteClient>;
2370
+ /**
2371
+ * Shows operations over the store with no request behind them, as a mutation's
2372
+ * `optimistic` ones show while it is in flight: every Selection and view draws
2373
+ * them. For a preview of a change nobody has made. They stay until `lift`;
2374
+ * showing an id again replaces what it showed. Called from `update`.
2375
+ */
2376
+ overlay(model: AppModel, id: string, optimistic: ReadonlyArray<OptimisticOperation>): AppModel;
2377
+ /** Lifts what `overlay` showed under this id. Lifting nothing returns the same Model. */
2378
+ lift(model: AppModel, id: string): AppModel;
1882
2379
  /** `Remote.refresh`: marks what a projection or a Surface requires as due, for its read entry to refetch. */
1883
2380
  refresh(model: AppModel, target: Projection<AppModel, unknown> | Surface<AppModel, any, any, void>): AppModel;
1884
2381
  /**
@@ -1894,6 +2391,30 @@ export interface RemoteDomain<AppModel, Store extends RemoteModel, Entities exte
1894
2391
  plan<Value>(model: AppModel, projection: Projection<AppModel, Value>, options?: PlanOptions): ReadonlyArray<Requirement>;
1895
2392
  /** `Remote.storeOf`: the visible store, base under the pending optimistic layers. */
1896
2393
  storeOf(model: AppModel): EntityStore;
2394
+ /**
2395
+ * `Remote.confirmed`: the same projection read against the server-derived
2396
+ * store alone, with every pending optimistic layer and connection overlay
2397
+ * left off. What it plans is unchanged — the requirements are the
2398
+ * projection's own — so observing it reads exactly what observing the
2399
+ * projection reads; only what it *shows* differs.
2400
+ *
2401
+ * A view usually wants the projection itself, which is the visible read:
2402
+ * the optimistic layers are there so a change shows before the server has
2403
+ * agreed to it. This is for the reader that must not believe a change until
2404
+ * the server has confirmed it, which in practice is an Agent capability
2405
+ * reporting that what it was asked to do is done:
2406
+ *
2407
+ * ```ts
2408
+ * Agent.when({
2409
+ * projection: Data.confirmed(ProjectPage.model.project),
2410
+ * predicate: (project, request) => project.name === request.name,
2411
+ * })
2412
+ * ```
2413
+ *
2414
+ * There is deliberately no `visible`: a projection is already the visible
2415
+ * read, and a wrapper that only forwards would be a second name for it.
2416
+ */
2417
+ confirmed<P extends Projection<AppModel, any>>(projection: P): P;
1897
2418
  /**
1898
2419
  * The plan run through `RemoteClient` and reduced into the Model: the
1899
2420
  * projection's pending queries first, then the fields it lacks (the pages'
@@ -1948,6 +2469,8 @@ export type QueryOptions<Value, Entity extends string, Of extends string = Entit
1948
2469
  /** A query connection read as a `Page` of selected items; `ref` is the connection with its first window. */
1949
2470
  export interface QueryProjection<AppModel, Value, Name extends string, Input> extends Projection<AppModel, RemoteData<Page<Value>>> {
1950
2471
  readonly ref: QueryRef<Name, Input>;
2472
+ /** What it reads of each item, so a filter over the same list decodes identically. */
2473
+ readonly selection: Selection<Value, string>;
1951
2474
  }
1952
2475
  /** The input of a query descriptor. */
1953
2476
  export type QueryInput<Q> = Q extends QueryDescriptor<any, infer Input, any> ? Input : never;
@@ -1989,7 +2512,11 @@ declare const PlannedQuery: Schema.Struct<{
1989
2512
  export interface ReadDependencies {
1990
2513
  readonly requirements: ReadonlyArray<Requirement>;
1991
2514
  readonly queries: ReadonlyArray<Schema.Schema.Type<typeof PlannedQuery>>;
1992
- /** The Model's requested refresh generation: a refresh restarts the entry. */
2515
+ /**
2516
+ * The highest generation the fields this entry reads were refreshed at: a
2517
+ * refresh of any of them restarts the entry, and a refresh of anything else
2518
+ * leaves it running.
2519
+ */
1993
2520
  readonly refresh: number;
1994
2521
  }
1995
2522
  export declare const Remote: {
@@ -2020,6 +2547,7 @@ export declare const Remote: {
2020
2547
  readonly _tag: Schema.String;
2021
2548
  readonly message: Schema.String;
2022
2549
  }>;
2550
+ stream: Schema.optional<Schema.String>;
2023
2551
  };
2024
2552
  RefreshStarted: {
2025
2553
  requests: Schema.$Array<Schema.Codec<Requirement, Requirement, never, never>>;
@@ -2028,6 +2556,9 @@ export declare const Remote: {
2028
2556
  requests: Schema.$Array<Schema.Codec<Requirement, Requirement, never, never>>;
2029
2557
  refresh: Schema.optional<Schema.Number>;
2030
2558
  };
2559
+ QueryStarted: {
2560
+ connections: Schema.$Array<Schema.String>;
2561
+ };
2031
2562
  RetentionChanged: {
2032
2563
  roots: Schema.Struct<{
2033
2564
  readonly requirements: Schema.$Array<Schema.Codec<Requirement, Requirement, never, never>>;
@@ -2038,6 +2569,7 @@ export declare const Remote: {
2038
2569
  }>;
2039
2570
  };
2040
2571
  Hydrated: {
2572
+ connections: Schema.optional<Schema.Unknown>;
2041
2573
  entities: Schema.Unknown;
2042
2574
  merge: Schema.Union<readonly [Schema.Literal<"replace">, Schema.Literal<"preserve-existing">]>;
2043
2575
  };
@@ -2065,6 +2597,13 @@ export declare const Remote: {
2065
2597
  readonly message: Schema.String;
2066
2598
  }>;
2067
2599
  };
2600
+ OverlayShown: {
2601
+ id: Schema.String;
2602
+ optimistic: Schema.$Array<Schema.Unknown>;
2603
+ };
2604
+ OverlayLifted: {
2605
+ id: Schema.String;
2606
+ };
2068
2607
  LiveReceived: {
2069
2608
  stream: Schema.String;
2070
2609
  event: Schema.Unknown;
@@ -2151,6 +2690,12 @@ export declare const Remote: {
2151
2690
  * has not changed.
2152
2691
  */
2153
2692
  storeOf: <AppModel, Store extends RemoteModel>(bound: BoundRemote<AppModel, Store>, model: AppModel) => EntityStore;
2693
+ /**
2694
+ * The projection read against the server-derived store alone: the same
2695
+ * requirements, planned the same way, with the pending optimistic layers and
2696
+ * connection overlays left off. See the bound `Remote.confirmed`.
2697
+ */
2698
+ confirmed: <AppModel, Store extends RemoteModel, P extends Projection<AppModel, any>>(bound: BoundRemote<AppModel, Store>, projection: P) => P;
2154
2699
  /**
2155
2700
  * Executes the plan against the `RemoteClient` and returns a new store. Used
2156
2701
  * for SSR route prefetch, hover prefetch, and tests. Never called during render.
@@ -2171,6 +2716,9 @@ export declare const Remote: {
2171
2716
  * again with `next`/`previous`. The read entries restart, so a read or query
2172
2717
  * sent before the refresh is not applied after it.
2173
2718
  *
2719
+ * It is also how a failed query is retried: a connection whose query failed
2720
+ * is not run again on its own, so its read stays `Failed` until this asks.
2721
+ *
2174
2722
  * The projection must be observed, which it is while it is on screen. Live
2175
2723
  * subscriptions are left as they are. For data nothing observes (SSR, tests),
2176
2724
  * use `Remote.prefetch` with `RemotePolicy.networkOnly`. Refreshing what is
@@ -2283,8 +2831,10 @@ export declare const Remote: {
2283
2831
  observe: <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?: ObserveOptions) => EntryWithoutKeepAlive<AppModel, Message, ReadDependencies, RemoteClient>;
2284
2832
  /**
2285
2833
  * A Foldkit Subscription entry that consumes the live stream for a Surface's
2286
- * requirements, emitting a `LiveReceived` per event and a `ReadFailed` when
2287
- * the stream breaks (including `ResumeUnavailable`). The resume cursor is read
2834
+ * requirements, emitting a `LiveReceived` per event and a `ReadFailed`
2835
+ * carrying its `stream` when the stream breaks (including
2836
+ * `ResumeUnavailable`). That records a gap on the stream rather than a failed
2837
+ * read, since nothing was being read. The resume cursor is read
2288
2838
  * from `RemoteModel.live`, so the application tracks no cursor of its own.
2289
2839
  */
2290
2840
  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, {