@thomasfosterau/legation 0.0.0-stage → 0.1.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.
@@ -0,0 +1,2362 @@
1
+ import { $ as MutatorRegistry, A as RelatedRow, B as WindowObservation, Bt as StoreError, C as ClientViewError, D as Entry, Dt as Literal, F as FormRuling$1, G as RowChange, H as FallbackOptions, I as LocalRow, J as MutationStateMap, L as MutationOutcome, Lt as MutatorError, M as ClientEngine, Mt as BackendError, O as Pulled, Ot as LiteralKind, P as EngineEvent, Q as Mutator, R as ReadRow, S as ClientView, St as Subset, T as Reader, Tt as CollectionIndex, U as ServerHandler, V as ClientTransport, Vt as TransportError, X as AnyMutator, _t as CollectionRegistry, a as BusBroker, at as AggregateValue, bt as OrderBy, dt as Mutation, g as MirrorChannel, gt as AnyCollection, h as UrlReads, it as Aggregate, k as EdgeSelect$1, kt as Predicate, lt as ChangeHint, o as InvalidationBus, pt as NodeRegistry, q as MutationState, st as Backend, t as BackendHandler, tt as ClientStore, u as AuthPolicy, v as AnyQuery, wt as TypedSubset, xt as Row$3, z as TypedRow, zt as QueryError } from "./MemoryBackend-BziXwfcs.js";
2
+ import * as Schema from "effect/Schema";
3
+ import * as Effect from "effect/Effect";
4
+ import * as Context from "effect/Context";
5
+ import * as Stream from "effect/Stream";
6
+ import * as Exit from "effect/Exit";
7
+ import * as Scope from "effect/Scope";
8
+ import * as Duration from "effect/Duration";
9
+ import * as Layer from "effect/Layer";
10
+ import * as Metric from "effect/Metric";
11
+ import { Redis } from "effect/persistence";
12
+ import { Rpc, RpcClient, RpcGroup } from "effect/rpc";
13
+ import * as JsonSchema from "effect/JsonSchema";
14
+ import { AsyncResult, Atom, AtomRegistry, Reactivity } from "effect/reactivity";
15
+ import * as Cause from "effect/Cause";
16
+ import * as ManagedRuntime from "effect/ManagedRuntime";
17
+ declare namespace Hash_d_exports {
18
+ export { canonicalJson, contentHash };
19
+ }
20
+ /**
21
+ * Hash
22
+ *
23
+ * A stable, order-independent content hash, used as a row version when a
24
+ * backend exposes no native version token (CONTEXT.md: row version). The hash
25
+ * changes iff the canonical value changes — exactly what CVR diffing needs —
26
+ * so adapters that sync plain values (the JSON:API and SQL reference backends)
27
+ * share one implementation here rather than each rolling their own.
28
+ */
29
+ /**
30
+ * A canonical, key-order-insensitive JSON serialisation: two structurally
31
+ * equal values always produce the same string, whatever the order their keys
32
+ * were written in. The correct basis for cache/subscription keys derived from
33
+ * args objects (where `JSON.stringify` would key on insertion order and split
34
+ * one logical subscription across two entries).
35
+ */
36
+ declare const canonicalJson: (value: unknown) => string;
37
+ /** A content-hash row version (FNV-1a over the canonical value). */
38
+ declare const contentHash: (value: unknown) => string;
39
+ declare namespace UnknownMutator_d_exports {
40
+ export { Cause$1 as Cause, isError, make$9 as make };
41
+ }
42
+ /** The `cause` of a {@link BackendError} raised for a mutator with no registered handler. */
43
+ interface Cause$1 {
44
+ readonly _tag: "UnknownMutator";
45
+ readonly mutator: string;
46
+ }
47
+ /** Build the transient `BackendError` for a mutator with no registered handler. */
48
+ declare const make$9: (mutator: string) => BackendError;
49
+ /**
50
+ * True when `error` is a `BackendError` built by {@link make}, so its `cause` is
51
+ * the typed unknown-mutator cause, whatever its message says.
52
+ */
53
+ declare const isError: (error: unknown) => boolean;
54
+ declare namespace RpcTransport_d_exports {
55
+ export { FromOriginOptions, RpcTransportOptions, SyncRpc, SyncRpcError, SyncRpcs, fromOrigin, fromRpcClient, layer$2 as layer, layerHandlers, make$8 as make };
56
+ }
57
+ declare const SyncRpcError_base: Schema.Class<SyncRpcError, Schema.TaggedStruct<"SyncRpcError", {
58
+ readonly message: Schema.String;
59
+ }>, import("effect/Cause").YieldableError & {
60
+ readonly brand: unique symbol;
61
+ }>;
62
+ /** The wire-serializable failure carried on sync RPCs. */
63
+ declare class SyncRpcError extends SyncRpcError_base {}
64
+ /**
65
+ * The sync protocol as a typed RPC group. Apps embedding it next to their
66
+ * own RPCs can namespace it with `SyncRpcs.prefix("sync.")`.
67
+ */
68
+ declare const SyncRpcs: RpcGroup.RpcGroup<Rpc.Rpc<"push", Schema.Struct<{
69
+ readonly clientGroupId: Schema.String;
70
+ readonly mutations: Schema.$Array<Schema.Struct<{
71
+ readonly clientId: Schema.String;
72
+ readonly mutationId: Schema.Number;
73
+ readonly mutator: Schema.String;
74
+ readonly args: Schema.Unknown;
75
+ readonly createdAt: Schema.Number;
76
+ }>>;
77
+ readonly schemaVersion: Schema.optional<Schema.String>;
78
+ }>, Schema.Struct<{
79
+ readonly lastMutationIds: Schema.$Record<Schema.String, Schema.Number>;
80
+ readonly rejected: Schema.optional<Schema.$Array<Schema.Struct<{
81
+ readonly clientId: Schema.String;
82
+ readonly mutationId: Schema.Number;
83
+ readonly reason: Schema.String;
84
+ }>>>;
85
+ }>, typeof SyncRpcError, never, never> | Rpc.Rpc<"pull", Schema.Struct<{
86
+ readonly clientGroupId: Schema.String;
87
+ readonly cookie: Schema.optional<Schema.String>;
88
+ readonly subscriptions: Schema.$Array<Schema.Struct<{
89
+ readonly collection: Schema.String;
90
+ readonly where: Schema.Codec<Predicate, Predicate, never, never>;
91
+ readonly orderBy: Schema.$Array<Schema.Struct<{
92
+ readonly field: Schema.String;
93
+ readonly direction: Schema.Literals<readonly ["asc", "desc"]>;
94
+ }>>;
95
+ readonly limit: Schema.optional<Schema.Number>;
96
+ readonly cursor: Schema.optional<Schema.String>;
97
+ }>>;
98
+ readonly namedSubscriptions: Schema.optional<Schema.$Array<Schema.Struct<{
99
+ readonly name: Schema.String;
100
+ readonly args: Schema.Unknown;
101
+ }>>>;
102
+ readonly schemaVersion: Schema.optional<Schema.String>;
103
+ }>, Schema.Struct<{
104
+ readonly cookie: Schema.String;
105
+ readonly patch: Schema.$Array<Schema.Union<readonly [Schema.Struct<{
106
+ readonly _tag: Schema.tag<"Put">;
107
+ readonly collection: Schema.String;
108
+ readonly id: Schema.String;
109
+ readonly version: Schema.Union<readonly [Schema.String, Schema.Number]>;
110
+ readonly value: Schema.Unknown;
111
+ }>, Schema.Struct<{
112
+ readonly _tag: Schema.tag<"Del">;
113
+ readonly collection: Schema.String;
114
+ readonly id: Schema.String;
115
+ }>, Schema.Struct<{
116
+ readonly _tag: Schema.tag<"Clear">;
117
+ }>]>>;
118
+ readonly lastMutationIds: Schema.$Record<Schema.String, Schema.Number>;
119
+ readonly rejected: Schema.optional<Schema.$Array<Schema.Struct<{
120
+ readonly clientId: Schema.String;
121
+ readonly mutationId: Schema.Number;
122
+ readonly reason: Schema.String;
123
+ }>>>;
124
+ }>, typeof SyncRpcError, never, never> | Rpc.Rpc<"read", Schema.Struct<{
125
+ readonly clientGroupId: Schema.String;
126
+ readonly subsets: Schema.$Array<Schema.Struct<{
127
+ readonly collection: Schema.String;
128
+ readonly where: Schema.Codec<Predicate, Predicate, never, never>;
129
+ readonly orderBy: Schema.$Array<Schema.Struct<{
130
+ readonly field: Schema.String;
131
+ readonly direction: Schema.Literals<readonly ["asc", "desc"]>;
132
+ }>>;
133
+ readonly limit: Schema.optional<Schema.Number>;
134
+ readonly cursor: Schema.optional<Schema.String>;
135
+ }>>;
136
+ readonly schemaVersion: Schema.optional<Schema.String>;
137
+ }>, Schema.Struct<{
138
+ readonly rows: Schema.$Array<Schema.Struct<{
139
+ readonly collection: Schema.String;
140
+ readonly id: Schema.String;
141
+ readonly version: Schema.Union<readonly [Schema.String, Schema.Number]>;
142
+ readonly value: Schema.Unknown;
143
+ }>>;
144
+ }>, typeof SyncRpcError, never, never> | Rpc.Rpc<"pokes", Schema.Struct<{
145
+ clientGroupId: Schema.String;
146
+ }>, import("effect/rpc/RpcSchema").Stream<Schema.Struct<{
147
+ readonly reason: Schema.optional<Schema.String>;
148
+ }>, Schema.Never>, Schema.Never, never, never>>;
149
+ /** The sync RPCs ({@link SyncRpcs}) as a union of their definitions. */
150
+ type SyncRpc = RpcGroup.Rpcs<typeof SyncRpcs>;
151
+ /** Server side: implement the sync RPCs from an in-context ServerHandler. */
152
+ declare const layerHandlers: Layer.Layer<Rpc.ToHandler<SyncRpc>, never, ServerHandler>;
153
+ /** Options for the RPC client transport. */
154
+ interface RpcTransportOptions {
155
+ readonly clientGroupId: string;
156
+ }
157
+ /**
158
+ * Adapt a generated `SyncRpcs` client into a ClientTransport. Connectivity
159
+ * is reported as permanently "connected": RPC protocols reconnect (or fail
160
+ * per call) internally, and sync stays correct over pull alone. Wrap the
161
+ * result with `Transport.withInferredStatus` for behaviour-derived status
162
+ * (and `Transport.withFallback` to degrade to a second carrier); in browsers
163
+ * add `BrowserConnectivity.withBrowserConnectivity` on top.
164
+ */
165
+ declare const fromRpcClient: <E extends {
166
+ readonly message: string;
167
+ }>(client: RpcClient.RpcClient<SyncRpc, E>, options: RpcTransportOptions) => ClientTransport;
168
+ /** Build a ClientTransport over a real RPC protocol (HTTP, WebSocket, …). */
169
+ declare const make$8: (options: RpcTransportOptions) => Effect.Effect<ClientTransport, never, RpcClient.Protocol | Scope.Scope>;
170
+ /** Layer building the ClientTransport from an in-context RPC protocol. */
171
+ declare const layer$2: (options: RpcTransportOptions) => Layer.Layer<ClientTransport, never, RpcClient.Protocol>;
172
+ /** Options for {@link fromOrigin}. */
173
+ interface FromOriginOptions {
174
+ readonly clientGroupId: string;
175
+ /** The sync server's origin, e.g. `https://sync.example.com`. */
176
+ readonly origin: string;
177
+ /** The HTTP POST route (the fallback carrier). Default `/sync`. */
178
+ readonly httpPath?: string;
179
+ /** The WebSocket route (the live carrier). Default `/sync-ws`. */
180
+ readonly wsPath?: string;
181
+ /**
182
+ * Fallback tuning ({@link Transport.withFallback}). Defaults: sidelined
183
+ * primaries retry after 30 seconds; each WebSocket call gets a 10-second
184
+ * budget before the call falls to HTTP.
185
+ */
186
+ readonly fallback?: FallbackOptions;
187
+ }
188
+ /**
189
+ * The standard remote transport: sync RPCs over a WebSocket (the live
190
+ * path, pokes included) with plain HTTP POST as the fallback carrier —
191
+ * `Client.make`'s `transport: { url }` shorthand builds exactly this.
192
+ * Wrap with `BrowserConnectivity.withBrowserConnectivity`
193
+ * (`@thomasfosterau/legation-browser`) for navigator-aware status in browsers.
194
+ */
195
+ declare const fromOrigin: (options: FromOriginOptions) => Effect.Effect<ClientTransport, never, Scope.Scope>;
196
+ declare namespace SpecExport_d_exports {
197
+ export { Document, NamedSchema, SpecInfo, SyncApi, SyncSpec, asyncApi, build, openApi, toAsyncApi, toOpenApi };
198
+ }
199
+ /** Document metadata shared by every emitted format. */
200
+ interface SpecInfo {
201
+ readonly title: string;
202
+ readonly version: string;
203
+ readonly description?: string;
204
+ }
205
+ /**
206
+ * The app's sync surface to describe: its collections, mutators and queries.
207
+ * All are optional — a read-only or write-only app still yields a valid spec —
208
+ * and the protocol messages are always present.
209
+ */
210
+ interface SyncApi {
211
+ readonly info: SpecInfo;
212
+ readonly collections?: ReadonlyArray<AnyCollection>;
213
+ readonly mutators?: ReadonlyArray<AnyMutator>;
214
+ readonly queries?: ReadonlyArray<AnyQuery>;
215
+ /** Base path the sync RPCs are served on. Defaults to `"/sync"`. */
216
+ readonly path?: string;
217
+ }
218
+ /** The component name an app type resolves to in the shared schema pool. */
219
+ interface NamedSchema {
220
+ readonly name: string;
221
+ readonly schema: string;
222
+ }
223
+ /**
224
+ * A format-neutral description of a sync surface: a shared component pool of
225
+ * JSON Schemas (OpenAPI-style `#/components/schemas/...` refs) plus the
226
+ * component name of each protocol message and app type. Emitters project this
227
+ * into a concrete document; adding a format means adding a `toX(spec)`.
228
+ */
229
+ interface SyncSpec {
230
+ readonly info: SpecInfo;
231
+ readonly path: string;
232
+ /** Shared JSON-Schema component pool (draft-2020-12, OpenAPI-style refs). */
233
+ readonly components: JsonSchema.Definitions;
234
+ /** Component name of each protocol message. */
235
+ readonly messages: {
236
+ readonly pushRequest: string;
237
+ readonly pushResponse: string;
238
+ readonly pullRequest: string;
239
+ readonly pullResponse: string;
240
+ readonly poke: string;
241
+ readonly error: string;
242
+ };
243
+ /** App types, each with the component name of its specialized schema. */
244
+ readonly catalog: {
245
+ readonly collections: ReadonlyArray<NamedSchema>;
246
+ readonly mutators: ReadonlyArray<NamedSchema>;
247
+ readonly queries: ReadonlyArray<NamedSchema>;
248
+ };
249
+ }
250
+ /**
251
+ * Build the format-neutral {@link SyncSpec} from an app's sync surface. The
252
+ * shared component pool is assembled once (so every message reuses the same
253
+ * row/args definitions) in OpenAPI-style draft-2020-12.
254
+ */
255
+ declare const build: (api: SyncApi) => SyncSpec;
256
+ /** A JSON-serializable document (object form), as emitted by the projectors. */
257
+ type Document = {
258
+ readonly [key: string]: unknown;
259
+ };
260
+ /**
261
+ * Project a {@link SyncSpec} into an OpenAPI 3.1 document: push and pull as the
262
+ * protocol's logical `POST {path}/push` and `POST {path}/pull` operations, with
263
+ * every app type in `components.schemas`. Streamed pokes live in AsyncAPI.
264
+ */
265
+ declare const toOpenApi: (spec: SyncSpec) => Document;
266
+ /**
267
+ * Project a {@link SyncSpec} into an AsyncAPI 3.0 document: push and pull as
268
+ * request/reply send operations and the contentless poke hint as a receive
269
+ * channel. Component schemas are shared with the OpenAPI projection.
270
+ */
271
+ declare const toAsyncApi: (spec: SyncSpec) => Document;
272
+ /** Build and project to OpenAPI 3.1 in one step. */
273
+ declare const openApi: (api: SyncApi) => Document;
274
+ /** Build and project to AsyncAPI 3.0 in one step. */
275
+ declare const asyncApi: (api: SyncApi) => Document;
276
+ declare namespace AtomicKeyValue_d_exports {
277
+ export { AtomicKeyValue, KeyValueEntry, KeyValueOp };
278
+ }
279
+ /**
280
+ * One operation of an atomic batch. `key` is the full key the backing store
281
+ * holds, namespace prefix included, exactly as the `KeyValueStore` underneath
282
+ * stores it. `value` is the string the store would hold for a `set`.
283
+ */
284
+ type KeyValueOp = {
285
+ readonly _tag: "Set";
286
+ readonly key: string;
287
+ readonly value: string;
288
+ } | {
289
+ readonly _tag: "Remove";
290
+ readonly key: string;
291
+ };
292
+ /** One string entry the backing store holds: its full key and its value. */
293
+ interface KeyValueEntry {
294
+ readonly key: string;
295
+ readonly value: string;
296
+ }
297
+ /** The {@link AtomicKeyValue} service: an atomic batch over the store's keys. */
298
+ interface AtomicKeyValue {
299
+ /**
300
+ * Commit `ops` in order as one all-or-nothing step. Up to the commit point, a
301
+ * failure or an interruption leaves none of the operations durable. A failure
302
+ * reported after the commit point may still have committed, so a caller must
303
+ * treat that outcome as unknown and re-read the store before relying on it. On
304
+ * success every operation is durable. A later operation on a key replaces an
305
+ * earlier one in the same batch.
306
+ */
307
+ readonly apply: (ops: ReadonlyArray<KeyValueOp>) => Effect.Effect<void, StoreError>;
308
+ /**
309
+ * Every string entry the backing store holds, read in one read transaction, so the
310
+ * result is one state of the store: a batch committed by another connection is
311
+ * either wholly in it or wholly absent (#392). Optional: a store without it reads
312
+ * its keys one at a time, which a concurrent connection can straddle.
313
+ */
314
+ readonly readAll?: Effect.Effect<ReadonlyArray<KeyValueEntry>, StoreError> | undefined;
315
+ }
316
+ /**
317
+ * The atomic batch seam (#393), optional: a `ClientStore` reads it with
318
+ * `Effect.serviceOption` and falls back to its release-record path when absent.
319
+ */
320
+ declare const AtomicKeyValue: Context.Service<AtomicKeyValue, AtomicKeyValue>;
321
+ declare namespace Undo_d_exports {
322
+ export { MutateHandle, MutationIntent, UndoEntry, UndoManager, make$7 as make };
323
+ }
324
+ /** A mutator invocation: the name and (decoded) args handed to `mutate`. */
325
+ interface MutationIntent {
326
+ readonly mutator: string;
327
+ readonly args: unknown;
328
+ }
329
+ /**
330
+ * A reversible action as a pair of intents: `execute` applies it, `undo`
331
+ * reverses it. Both replay through `ClientEngine.mutate`, so both are
332
+ * optimistic, journaled, and rebased.
333
+ */
334
+ interface UndoEntry {
335
+ readonly execute: MutationIntent;
336
+ readonly undo: MutationIntent;
337
+ }
338
+ /**
339
+ * The structural shape {@link make} needs: anything that can run a named
340
+ * mutation. A `ClientEngine` and a `Client` both satisfy this — with
341
+ * different error channels, hence the type parameter (issue #223).
342
+ */
343
+ interface MutateHandle<E> {
344
+ readonly mutate: (mutator: string, args: unknown) => Effect.Effect<void, E>;
345
+ }
346
+ /**
347
+ * A per-client undo/redo manager over a {@link MutateHandle}. Every operation
348
+ * runs through the handle's `mutate` pipeline, so a failed mutation surfaces
349
+ * as `E` — the handle's own error type; the manager itself only owns the
350
+ * in-memory history and the grouping flag.
351
+ */
352
+ interface UndoManager<E> {
353
+ /**
354
+ * Run an entry's `execute` through `mutate`, push it onto the undo stack, and
355
+ * clear the redo stack (a new action invalidates any redo). Inside a
356
+ * {@link group}, the entry is instead accumulated into the group's step.
357
+ */
358
+ readonly run: (entry: UndoEntry) => Effect.Effect<void, E>;
359
+ /**
360
+ * Pop the top undo step, replay its members' `undo` intents (in reverse order
361
+ * for a group), and push it onto the redo stack. A no-op when the undo stack
362
+ * is empty.
363
+ */
364
+ readonly undo: Effect.Effect<void, E>;
365
+ /**
366
+ * Pop the top redo step, replay its members' `execute` intents (in order for a
367
+ * group), and push it back onto the undo stack. A no-op when the redo stack is
368
+ * empty.
369
+ */
370
+ readonly redo: Effect.Effect<void, E>;
371
+ /** Whether an undo is currently possible. */
372
+ readonly canUndo: Effect.Effect<boolean>;
373
+ /** Whether a redo is currently possible. */
374
+ readonly canRedo: Effect.Effect<boolean>;
375
+ /** Observable {@link canUndo} for bindings (emits now, then on every change). */
376
+ readonly canUndoChanges: Stream.Stream<boolean>;
377
+ /** Observable {@link canRedo} for bindings (emits now, then on every change). */
378
+ readonly canRedoChanges: Stream.Stream<boolean>;
379
+ /**
380
+ * Coalesce every `run` made while `effect` runs into a single undo step. The
381
+ * effect's own result, errors, and requirements pass through unchanged.
382
+ * Nested groups fold into the outermost one.
383
+ */
384
+ readonly group: <A, E2, R>(effect: Effect.Effect<A, E2, R>) => Effect.Effect<A, E2, R>;
385
+ }
386
+ /**
387
+ * Build an {@link UndoManager} over any {@link MutateHandle} — a
388
+ * `ClientEngine`, a `Client`, or a test double. History is in-memory, so
389
+ * nothing needs a scope; the manager lives as long as the reference is held.
390
+ * The manager's error type is inferred from the handle's `mutate`, so a
391
+ * `ClientEngine` yields the same mutator/store union it always has, and a
392
+ * `Client` yields its narrower view error.
393
+ */
394
+ declare const make$7: <E>(handle: MutateHandle<E>) => Effect.Effect<UndoManager<E>>;
395
+ declare namespace Paced_d_exports {
396
+ export { PacedMutation, coalesce, debounce, throttle };
397
+ }
398
+ /** A paced mutation bound to one mutator: `call(key, args)` supersedes any of
399
+ * this key's own prior calls per the strategy's rule. */
400
+ interface PacedMutation {
401
+ /**
402
+ * Journal `args` for `key`. `key` scopes compaction — calls with different
403
+ * keys (e.g. different row ids) never supersede one another, only
404
+ * consecutive calls sharing a key do.
405
+ */
406
+ readonly call: (key: string, args: unknown) => Effect.Effect<void, MutatorError | StoreError>;
407
+ }
408
+ /**
409
+ * Coalesce: consecutive unpushed invocations of `mutator` for the same `key`
410
+ * collapse to the last one. No timing — every call journals and compacts
411
+ * immediately, so nothing is ever buffered only in memory (fully durable),
412
+ * at the cost of a momentary extra journal entry between a call and its
413
+ * compaction.
414
+ */
415
+ declare const coalesce: (engine: ClientEngine, mutator: string) => Effect.Effect<PacedMutation>;
416
+ /**
417
+ * Debounce: the first call after a quiet period journals immediately (the
418
+ * overlay renders it right away — optimistic rendering never waits for the
419
+ * window); every call inside the window just buffers its args and restarts
420
+ * the trailing timer. When `duration` elapses with no further calls, the
421
+ * latest buffered args flush once, superseding the leading entry. A 50-call
422
+ * burst inside one window therefore produces exactly two journal writes
423
+ * (leading + trailing) no matter how many calls landed in between — see the
424
+ * module doc for the durability tradeoff this implies.
425
+ */
426
+ declare const debounce: (engine: ClientEngine, mutator: string, duration: Duration.Input) => Effect.Effect<PacedMutation, never, Scope.Scope>;
427
+ /**
428
+ * Throttle: like {@link debounce}'s leading edge, but the window is never
429
+ * reset by later calls — it flushes at most once per `duration` while calls
430
+ * keep arriving (a continuous drag emits periodic journal writes, not just
431
+ * one at the very end), then goes idle once a window elapses with nothing new
432
+ * buffered.
433
+ */
434
+ declare const throttle: (engine: ClientEngine, mutator: string, duration: Duration.Input) => Effect.Effect<PacedMutation, never, Scope.Scope>;
435
+ declare namespace Introspection_d_exports {
436
+ export { EngineIntrospection, fromServices, layer$1 as layer, make$6 as make };
437
+ }
438
+ /**
439
+ * A read-only introspection surface over an engine + store. `subscriptions`
440
+ * and `events` read the engine; `journal`, `cookie`, and `collections` read
441
+ * the store — all public reads, no private access.
442
+ */
443
+ interface EngineIntrospection {
444
+ /** Active subsets: raw subscriptions plus each named subscription resolved. */
445
+ readonly subscriptions: Effect.Effect<ReadonlyArray<Subset>>;
446
+ /**
447
+ * The journal: pending (unacknowledged) mutations in causal order. It holds
448
+ * pending entries only. A mutation the backend acknowledged but no pull has
449
+ * reported yet is in the engine's unpulled tail (#286), which this view does
450
+ * not show.
451
+ */
452
+ readonly journal: Effect.Effect<ReadonlyArray<Mutation>, StoreError>;
453
+ /** The current pull cookie, or `undefined` before the first pull. */
454
+ readonly cookie: Effect.Effect<string | undefined, StoreError>;
455
+ /** Every collection currently holding confirmed rows. */
456
+ readonly collections: Effect.Effect<ReadonlyArray<string>, StoreError>;
457
+ /** The unified sync-lifecycle event stream (see {@link EngineEvent}). */
458
+ readonly events: Stream.Stream<EngineEvent>;
459
+ }
460
+ /** The {@link EngineIntrospection} service devtools read through. */
461
+ declare const EngineIntrospection: Context.Service<EngineIntrospection, EngineIntrospection>;
462
+ /** Build the introspection view over a given engine and store. */
463
+ declare const make$6: (engine: ClientEngine, store: ClientStore) => EngineIntrospection;
464
+ /** Build the view from the {@link ClientEngine} + {@link ClientStore} in context. */
465
+ declare const fromServices: Effect.Effect<EngineIntrospection, never, ClientEngine | ClientStore>;
466
+ /** Layer providing {@link EngineIntrospection} from the engine + store services. */
467
+ declare const layer$1: Layer.Layer<EngineIntrospection, never, ClientEngine | ClientStore>;
468
+ declare namespace OrderedTree_d_exports {
469
+ export { Tree, at, findBy, fromSorted, insert, rankOf, remove, size, slice, toArray };
470
+ }
471
+ /**
472
+ * OrderedTree
473
+ *
474
+ * A persistent (path-copying) weight-balanced binary search tree keyed by a
475
+ * caller-supplied total order (bounded-balance / "BB[α]" trees, Adams 1992 —
476
+ * the algorithm behind Haskell's `Data.Set`/`Data.Map`). Every mutation
477
+ * returns a new tree sharing everything off the changed path, so an
478
+ * insert/remove is *O(log n)* node allocations rather than an *O(n)* array
479
+ * copy. Each node also carries its subtree size, so a value's 0-based rank
480
+ * (and the reverse: the value at a given rank) resolve in *O(log n)*, and a
481
+ * `count`-bounded slice starting at a rank costs *O(log n + count)* — this is
482
+ * what lets `LiveView` reslice its window without rescanning the whole
483
+ * maintained order (issue #112).
484
+ *
485
+ * Weight-balance (rather than a randomized treap) makes bulk construction
486
+ * from an already-sorted array linear and deterministic: `fromSorted`
487
+ * recursively roots each half at its middle element, which is already
488
+ * within the balance bound, so no rotation is needed on the way up.
489
+ *
490
+ * Generic over the element type; the caller's `compare` must be a strict
491
+ * total order (no ties) for `rankOf`/`remove` to behave — `LiveView` gets
492
+ * this from `Subset.comparator`'s id tie-break.
493
+ */
494
+ interface Node<T> {
495
+ readonly value: T;
496
+ readonly left: Node<T> | undefined;
497
+ readonly right: Node<T> | undefined;
498
+ readonly size: number;
499
+ }
500
+ /** An immutable ordered tree; `undefined` is the empty tree. */
501
+ type Tree<T> = Node<T> | undefined;
502
+ /** Number of elements in the tree, O(1). */
503
+ declare const size: <T>(tree: Tree<T>) => number;
504
+ /** Insert (or, if `compare` finds an exact match, replace) a value. O(log n). */
505
+ declare const insert: <T>(tree: Tree<T>, value: T, compare: (a: T, b: T) => number) => Tree<T>;
506
+ /** Remove the value matching `compare`, if present. O(log n); a no-op returns the same reference. */
507
+ declare const remove: <T>(tree: Tree<T>, value: T, compare: (a: T, b: T) => number) => Tree<T>;
508
+ /**
509
+ * The value whose key matches `key` per `compare` (negative when `key`
510
+ * belongs left of a node's value, positive when right, zero on a match), or
511
+ * `undefined` if absent. O(log n) — a plain BST point lookup, independent of
512
+ * rank, so a second tree can index the same elements by an unrelated key
513
+ * (`LiveView`'s `byId` alongside its value-ordered `order`) just as cheaply
514
+ * as a `Map`, but without a `Map`'s O(n) `new Map(bigMap)` copy on "update
515
+ * one entry, keep the rest" (path-copying only touches O(log n) nodes).
516
+ */
517
+ declare const findBy: <T, K>(tree: Tree<T>, key: K, compare: (key: K, value: T) => number) => T | undefined;
518
+ /**
519
+ * The 0-based rank of the value matching `compare`, or `-1` if absent.
520
+ * O(log n).
521
+ */
522
+ declare const rankOf: <T>(tree: Tree<T>, value: T, compare: (a: T, b: T) => number) => number;
523
+ /** The value at 0-based rank `index`, or `undefined` if out of bounds. O(log n). */
524
+ declare const at: <T>(tree: Tree<T>, index: number) => T | undefined;
525
+ /** Up to `count` in-order values starting at 0-based rank `from`. O(log n + count). */
526
+ declare const slice: <T>(tree: Tree<T>, from: number, count: number) => ReadonlyArray<T>;
527
+ /** Every value, in order. O(n) — the reference/full-materialization path. */
528
+ declare const toArray: <T>(tree: Tree<T>) => ReadonlyArray<T>;
529
+ /**
530
+ * Build a tree from an array already sorted per `compare`. O(n): each half
531
+ * roots at its middle element, which is already within the balance bound
532
+ * (`|leftSize - rightSize| <= 1`), so `fromSorted` never rotates.
533
+ */
534
+ declare const fromSorted: <T>(sorted: ReadonlyArray<T>) => Tree<T>;
535
+ declare namespace LiveView_d_exports {
536
+ export { LiveView, PositionedRow, Row$2 as Row, del, make$5 as make, makeIndexed, put, result, retarget };
537
+ }
538
+ /** A materialized `(id, value)` row, as a subset produces it. */
539
+ type Row$2 = Row$3;
540
+ /** A {@link Row} carrying its stable in-window position (see the module doc). */
541
+ interface PositionedRow extends Row$2 {
542
+ readonly position: string;
543
+ }
544
+ /**
545
+ * One subset's incrementally maintained window: the matching rows in subset order, plus an id
546
+ * index over the same rows.
547
+ */
548
+ interface LiveView {
549
+ readonly subset: Subset;
550
+ /** The full filtered + sorted matching set, maintained as an ordered tree. */
551
+ readonly order: Tree<Row$2>;
552
+ /**
553
+ * The same rows, maintained as a second tree ordered by id — an id index
554
+ * that supports an O(log n) presence check and O(log n) update, unlike a
555
+ * `Map` (see the module doc for why that distinction matters here).
556
+ */
557
+ readonly byId: Tree<Row$2>;
558
+ /** id → stable position, for rows currently inside the window only. */
559
+ readonly positions: ReadonlyMap<string, string>;
560
+ }
561
+ /** Build a view from an unordered collection snapshot. */
562
+ declare const make$5: (subset: Subset, rows: ReadonlyArray<Row$2>) => LiveView;
563
+ /**
564
+ * {@link make}, but reading rows from an id-keyed map with optional secondary-index
565
+ * acceleration (issue #113) — the collection-scale rebuild path (`Rebuild`
566
+ * view changes, a change-hub seq-gap collapse) shares the same narrowing as
567
+ * one-shot `query`.
568
+ */
569
+ declare const makeIndexed: (subset: Subset, rowsById: ReadonlyMap<string, Row$2>, index?: CollectionIndex) => LiveView;
570
+ /** The visible page: the subset's cursor + limit over the maintained order. */
571
+ declare const result: (view: LiveView) => ReadonlyArray<PositionedRow>;
572
+ /**
573
+ * Retarget the window — a new `limit` and/or `cursor`, e.g. "load more"
574
+ * widening the page — without touching the maintained order: no re-fetch, no
575
+ * re-sort, just a new O(log n + window) slice of the same tree.
576
+ * `collection`/`where`/`orderBy` stay the view's own; only `limit`/`cursor`
577
+ * move, matching `useConnection`'s "load more widens one window" (ADR-0024)
578
+ * — the pure, comparator-shaped half of "widen"; the engine-level API that
579
+ * reactively feeds a widened `limit` to an open subscription (so the client
580
+ * never re-registers it) is `ClientEngine.observeWindow` (#133).
581
+ * Same-reference-on-no-op: returns `view` unchanged if neither `limit` nor
582
+ * `cursor` moved.
583
+ */
584
+ declare const retarget: (view: LiveView, window: {
585
+ readonly limit?: number;
586
+ readonly cursor?: string;
587
+ }) => LiveView;
588
+ /**
589
+ * Apply a row upsert: place it at its sorted position if it matches the
590
+ * predicate, or remove it if it no longer does. Returns the same view
591
+ * reference when the row neither matched before nor matches now (an O(log n)
592
+ * `byId` check).
593
+ */
594
+ declare const put: (view: LiveView, row: Row$2) => LiveView;
595
+ /** Apply a row deletion. Returns the same view reference if the id is absent. */
596
+ declare const del: (view: LiveView, id: string) => LiveView;
597
+ declare namespace Incremental_d_exports {
598
+ export { Delta, Group, JoinCombine, JoinOperator, JoinOptions$1 as JoinOptions, Operator, OrderedWindow, Row$1 as Row, applyDelta, deltaFromChanges, diff, distinctFromScratch, emptyDelta, filterFromScratch, groupByFromScratch, indexById, joinFromScratch, makeDistinct, makeFilter, makeGroupBy, makeJoin, makeMap, makeOrderedWindow, mapFromScratch, orderedWindowFromScratch, pageOf };
599
+ }
600
+ /** A `(id, value)` row — structurally `Subset.Row` / `ClientEngine.LocalRow`. */
601
+ interface Row$1 {
602
+ readonly id: string;
603
+ readonly value: unknown;
604
+ }
605
+ /**
606
+ * A batch of row changes. An update is carried as a `removed` (old value)
607
+ * paired with an `added` (new value) for the same id — the shape
608
+ * {@link diff} produces from consecutive page snapshots, and the shape every
609
+ * operator's `step` accepts and returns.
610
+ */
611
+ interface Delta {
612
+ readonly added: ReadonlyArray<Row$1>;
613
+ readonly removed: ReadonlyArray<Row$1>;
614
+ }
615
+ /** The empty delta (a no-op step result). */
616
+ declare const emptyDelta: Delta;
617
+ /** Index a page by id, for diffing against the next snapshot. */
618
+ declare const indexById: (rows: ReadonlyArray<Row$1>) => Map<string, Row$1>;
619
+ /**
620
+ * Diff a new page against the previous snapshot (by id, value change detected
621
+ * by content hash): a new id is an `added`, a vanished id a `removed`, a
622
+ * changed value a `removed` (old) + `added` (new). Returns the delta and the
623
+ * new snapshot index to carry forward.
624
+ */
625
+ declare const diff: (previous: ReadonlyMap<string, Row$1>, next: ReadonlyArray<Row$1>) => {
626
+ readonly delta: Delta;
627
+ readonly index: Map<string, Row$1>;
628
+ };
629
+ /** Fold a delta into a maintained result map (remove ids, then set adds). */
630
+ declare const applyDelta: (result: Map<string, Row$1>, delta: Delta) => void;
631
+ /**
632
+ * Recover a {@link Delta} directly from an `observeChanges` batch (#115),
633
+ * instead of diffing two page snapshots (see {@link diff} above) — the
634
+ * optimization ADR-0027 §4 and this module's header flag as a follow-up:
635
+ * `LiveView` already decided enter/update/exit/move per row, so this just
636
+ * reads that decision instead of recomputing it from two `Map`s. `known`
637
+ * tracks the last row seen per id (across calls, mutated in place) so a
638
+ * `reset` can emit a `removed` for everything it cleared — the same
639
+ * information a page-diff's `previous` snapshot would have given for free.
640
+ * `move` carries no value change, so it contributes nothing to the delta.
641
+ */
642
+ declare const deltaFromChanges: (known: Map<string, Row$1>, batch: ReadonlyArray<RowChange<Row$1>>) => Delta;
643
+ /** Materialize a result map to a page in a deterministic (id-ascending, code-point) order. */
644
+ declare const pageOf: (result: ReadonlyMap<string, Row$1>) => ReadonlyArray<Row$1>;
645
+ /** A single-input delta→delta operator maintaining its own internal state. */
646
+ interface Operator {
647
+ readonly step: (delta: Delta) => Delta;
648
+ }
649
+ /**
650
+ * `filter` — keep rows whose value passes an arbitrary JS predicate. Stateless
651
+ * per row: the predicate is deterministic on the value, and a delta carries the
652
+ * value it concerns, so an added/removed row's membership is decided on the
653
+ * spot (an update that filters a row out is a `removed` with the old value that
654
+ * passed; one that filters it in is an `added` that now passes).
655
+ */
656
+ declare const makeFilter: (predicate: (value: unknown) => boolean) => Operator;
657
+ /** {@link makeFilter}'s naive recompute, for the property test. */
658
+ declare const filterFromScratch: (rows: ReadonlyArray<Row$1>, predicate: (value: unknown) => boolean) => Map<string, Row$1>;
659
+ /**
660
+ * `map`/select — project each value through an arbitrary JS function, keeping
661
+ * the row id (projection preserves identity). Stateless: `f` is deterministic,
662
+ * so an update's `removed` reconstructs the old projection and its `added` the
663
+ * new one.
664
+ */
665
+ declare const makeMap: (f: (value: unknown) => unknown) => Operator;
666
+ /** {@link makeMap}'s naive recompute. */
667
+ declare const mapFromScratch: (rows: ReadonlyArray<Row$1>, f: (value: unknown) => unknown) => Map<string, Row$1>;
668
+ /**
669
+ * `distinct` — collapse duplicate values to one row, keyed and identified by
670
+ * the value's canonical serialization (so value equality, not reference,
671
+ * decides duplication and there is no hash-collision risk). A multiplicity
672
+ * count per distinct value means removing one of several duplicates keeps the
673
+ * distinct row; the row appears when its count goes 0→1 and disappears at 1→0.
674
+ */
675
+ declare const makeDistinct: () => Operator;
676
+ /** {@link makeDistinct}'s naive recompute (dedupe by canonical value). */
677
+ declare const distinctFromScratch: (rows: ReadonlyArray<Row$1>) => Map<string, Row$1>;
678
+ /**
679
+ * `groupBy` returning grouped **rows** (not only scalar aggregates): one output
680
+ * row per group, `value` = `{ key, rows }` where `rows` are the group members'
681
+ * values ordered by member id (a deterministic within-group order, so a
682
+ * from-scratch recompute matches). Only the one or two groups a delta touches
683
+ * re-materialize — every other group's row is untouched (the same
684
+ * touch-only-the-affected-bucket discipline as `Aggregate`'s grouped state).
685
+ */
686
+ declare const makeGroupBy: (key: (value: unknown) => string) => Operator;
687
+ /** A group row's value shape (what {@link makeGroupBy} emits). */
688
+ interface Group {
689
+ readonly key: string;
690
+ readonly rows: ReadonlyArray<unknown>;
691
+ }
692
+ /** {@link makeGroupBy}'s naive recompute. */
693
+ declare const groupByFromScratch: (rows: ReadonlyArray<Row$1>, key: (value: unknown) => string) => Map<string, Row$1>;
694
+ /** How a joined row's value is assembled; default `{ left, right }`. */
695
+ type JoinCombine = (left: unknown, right: unknown | null) => unknown;
696
+ /** Options for {@link makeJoin} / {@link joinFromScratch}. */
697
+ interface JoinOptions$1 {
698
+ /** Extract the left row's join key (an arbitrary value; `null`/`undefined` never matches). */
699
+ readonly leftKey: (value: unknown) => unknown;
700
+ /** Extract the right row's join key. */
701
+ readonly rightKey: (value: unknown) => unknown;
702
+ /** `inner` drops unmatched left rows; `left` keeps them with a `null` right. */
703
+ readonly kind: "inner" | "left";
704
+ /** Assemble the joined value; defaults to `{ left, right }`. */
705
+ readonly combine?: JoinCombine;
706
+ }
707
+ /**
708
+ * A two-input join over arbitrary key equality (inner or left). Maintains,
709
+ * per side, an id→row map and a key→ids index; a change re-derives only the
710
+ * left rows whose key the change touches (a right delta re-hydrates only the
711
+ * lefts in the affected buckets; a left delta only itself) — never a full
712
+ * re-group, the same discipline as `Related.makeJoinEngine`. Left-join null
713
+ * rows fall out for free: an unmatched left's contribution is a single
714
+ * `combine(left, null)` row, replaced the moment a matching right arrives.
715
+ */
716
+ interface JoinOperator {
717
+ readonly stepLeft: (delta: Delta) => Delta;
718
+ readonly stepRight: (delta: Delta) => Delta;
719
+ }
720
+ /** Build a {@link JoinOperator} for the given join keys and kind (inner or left). */
721
+ declare const makeJoin: (options: JoinOptions$1) => JoinOperator;
722
+ /** {@link makeJoin}'s naive recompute (nested-loop join by key equality). */
723
+ declare const joinFromScratch: (left: ReadonlyArray<Row$1>, right: ReadonlyArray<Row$1>, options: JoinOptions$1) => Map<string, Row$1>;
724
+ /** An ordered-window operator maintaining a `LiveView` over derived rows. */
725
+ interface OrderedWindow {
726
+ /** Apply a delta; returns the current window, ordered and limited. */
727
+ readonly step: (delta: Delta) => ReadonlyArray<Row$1>;
728
+ }
729
+ /**
730
+ * `orderedWindow` — sort derived rows by an `orderBy` and keep the first
731
+ * `limit`. Reuses `LiveView` (the `OrderedTree` + fractional-index machinery of
732
+ * #112): a delta is folded in as O(log n) `put`/`del`s and the window resliced
733
+ * without a re-sort, so a change outside the page costs the same as one inside
734
+ * it. Order is `Subset.comparator` — the one ordering authority (ADR-0026), so
735
+ * "no orderBy" still means a stable id-ascending order.
736
+ */
737
+ declare const makeOrderedWindow: (orderBy: ReadonlyArray<OrderBy>, limit?: number) => OrderedWindow;
738
+ /** {@link makeOrderedWindow}'s naive recompute (`Subset.apply`: sort then window). */
739
+ declare const orderedWindowFromScratch: (rows: ReadonlyArray<Row$1>, orderBy: ReadonlyArray<OrderBy>, limit?: number) => ReadonlyArray<Row$1>;
740
+ declare namespace Subscriptions_d_exports {
741
+ export { SubscriptionHandle, Subscriptions, SubscriptionsOptions, WindowHandle, counted, make$4 as make };
742
+ }
743
+ /** A held subscription. `release` is idempotent; the grace timer starts when
744
+ * the last holder releases. */
745
+ interface SubscriptionHandle {
746
+ readonly release: Effect.Effect<void>;
747
+ }
748
+ /**
749
+ * A held, reactively-retargetable window (#133): `rows`/`retarget` are
750
+ * {@link WindowObservation}'s (see `ClientEngine.observeWindow`); `release`
751
+ * drops the subscription this window's *current* (possibly retargeted)
752
+ * subset holds — unlike {@link acquire}, not refcounted, since a window's
753
+ * subset identity (its `limit`/`cursor`) changes over its lifetime and so
754
+ * cannot be shared under one hash-keyed entry the way a plain subset
755
+ * subscription is. Each retargetable window should be its own observation
756
+ * (the common case: one user's one paginated view); two independent
757
+ * `acquireWindow` calls that happen to start from equivalently-hashed
758
+ * subsets are not coordinated with each other.
759
+ */
760
+ interface WindowHandle extends WindowObservation {
761
+ readonly release: Effect.Effect<void>;
762
+ }
763
+ /**
764
+ * Subscription lifecycle over a `ClientEngine`: refcounted acquire/release, a grace period
765
+ * before an unused subscription is dropped, and preload.
766
+ */
767
+ interface Subscriptions {
768
+ /** Acquire a raw-subset subscription (refcounted). */
769
+ readonly acquire: (subset: Subset) => Effect.Effect<SubscriptionHandle>;
770
+ /**
771
+ * Acquire a reactively-retargetable window (#133): composes `acquire`'s
772
+ * subscribe/release with {@link ClientEngine.observeWindow}, so `retarget`
773
+ * widens/advances the window in place (reusing `LiveView`'s maintained
774
+ * order) and keeps the server-synced subscription in step — the
775
+ * `useConnection` "load more" paging hook's engine-level seam.
776
+ */
777
+ readonly acquireWindow: (subset: Subset) => Effect.Effect<WindowHandle, StoreError | MutatorError, Scope.Scope>;
778
+ /** Acquire a named-query subscription (refcounted). */
779
+ readonly acquireQuery: (name: string, args: unknown) => Effect.Effect<SubscriptionHandle, QueryError>;
780
+ /** Pin a subset for the session (never released). */
781
+ readonly preload: (subset: Subset) => Effect.Effect<void>;
782
+ /** Pin a named query for the session (never released). */
783
+ readonly preloadQuery: (name: string, args: unknown) => Effect.Effect<void, QueryError>;
784
+ }
785
+ /**
786
+ * One counted voice over the engine's set-based subscription list. The
787
+ * engine's `subscribe` / `unsubscribe` register or drop a window outright,
788
+ * so two independent registrations of one window — an explicit pin beside
789
+ * a relational read's own root subscription, two relational reads pinning
790
+ * one foreign window — would drop it for both when the first released.
791
+ * Registrations made through the returned engine hold a count per window:
792
+ * the engine hears `subscribe` on the first hold and `unsubscribe` on the
793
+ * last. A window this facade never counted (registered on the engine
794
+ * directly) is released as before, so the engine's own semantics survive
795
+ * for callers that bypass it. Everything else passes through untouched.
796
+ *
797
+ * Idempotent: counting a counted engine returns it, so one engine handed
798
+ * to several fronts — an `EngineHost` and a `ClientView.fromEngine` in the
799
+ * same tab, ADR-0010's main-thread path — has one voice, not one per front.
800
+ */
801
+ declare const counted: (engine: ClientEngine) => ClientEngine;
802
+ /** Options for {@link make}. */
803
+ interface SubscriptionsOptions {
804
+ /** Grace period a released subscription is kept before being dropped. */
805
+ readonly ttl?: Duration.Duration;
806
+ }
807
+ /** Build a subscription manager bound to an engine. Scoped: pending grace
808
+ * timers are interrupted when the scope closes. */
809
+ declare const make$4: (engine: ClientEngine, options?: SubscriptionsOptions) => Effect.Effect<Subscriptions, never, Scope.Scope>;
810
+ declare namespace Derive_d_exports {
811
+ export { DerivedResult, JoinOptions, OrderedWindowOptions, Row, Source, atom, distinct, filter, groupBy, join, map, observed, orderedWindow, select };
812
+ }
813
+ /** A derived row — the `(id, value)` unit, structurally `ClientEngine.LocalRow`. */
814
+ type Row = Row$1;
815
+ /** A derived source: a stream of page snapshots (what `ClientView.observe` yields). */
816
+ type Source<E> = Stream.Stream<ReadonlyArray<Row>, E>;
817
+ /**
818
+ * Keep the rows whose value passes an arbitrary JS predicate. The predicate is
819
+ * never serialized and never narrows what syncs — it shapes only what is shown
820
+ * of the already-synced page (the boundary rule above).
821
+ */
822
+ declare const filter: <E>(source: Source<E>, predicate: (value: unknown) => boolean) => Source<E>;
823
+ /** Project each row's value through an arbitrary JS function, keeping its id. */
824
+ declare const map: <E>(source: Source<E>, f: (value: unknown) => unknown) => Source<E>;
825
+ /** {@inheritDoc map} — `select` reads better for a projection; same combinator. */
826
+ declare const select: <E>(source: Source<E>, f: (value: unknown) => unknown) => Source<E>;
827
+ /** Collapse duplicate values to one row, keyed by the canonical value. */
828
+ declare const distinct: <E>(source: Source<E>) => Source<E>;
829
+ /**
830
+ * Group rows by an arbitrary key function, emitting one row per group whose
831
+ * value is `{ key, rows }` (the group's member values). A having-clause is
832
+ * `filter` after `groupBy`; a scalar rollup is `map` over the group rows.
833
+ */
834
+ declare const groupBy: <E>(source: Source<E>, key: (value: unknown) => string) => Source<E>;
835
+ /** Options for {@link join} (re-exported from {@link Incremental}). */
836
+ type JoinOptions = JoinOptions$1;
837
+ /**
838
+ * Join two derived sources on arbitrary key equality (inner or left). Merges
839
+ * the two page streams, feeding each side's delta to the shared join operator,
840
+ * which re-derives only the left rows a change touches. A joined row's value is
841
+ * `{ left, right }` by default (`right` is `null` for an unmatched left row
842
+ * under `kind: "left"`); pass `combine` to shape it otherwise.
843
+ */
844
+ declare const join: <E, F>(left: Source<E>, right: Source<F>, options: JoinOptions) => Source<E | F>;
845
+ /** Options for {@link orderedWindow}: a sort order and an optional row limit. */
846
+ interface OrderedWindowOptions {
847
+ readonly orderBy: ReadonlyArray<OrderBy>;
848
+ readonly limit?: number;
849
+ }
850
+ /**
851
+ * Sort derived rows by an `orderBy` and keep the first `limit` — an ORDER
852
+ * BY … LIMIT over the derived result, maintained by `LiveView`'s `OrderedTree`
853
+ * so a change outside the window costs the same as one inside it (ADR-0026's
854
+ * one ordering authority applies, so "no orderBy" still means stable id order).
855
+ */
856
+ declare const orderedWindow: <E>(source: Source<E>, options: OrderedWindowOptions) => Source<E>;
857
+ /** A derived value's Atom: the current page as an `AsyncResult`, re-emitting on change. */
858
+ type DerivedResult<E> = AsyncResult.AsyncResult<ReadonlyArray<Row>, E | Cause.NoSuchElementError>;
859
+ /**
860
+ * Expose a derived pipeline as an Atom on the standard reactivity surface — the
861
+ * same shape `LiveQuery.make(...).atom(...)` produces for a raw subset, so a
862
+ * framework binding renders a derived value with no new machinery.
863
+ */
864
+ declare const atom: <E>(source: Source<E>) => Atom.Atom<DerivedResult<E>>;
865
+ /**
866
+ * A scoped leaf source: acquire a refcounted subscription for `subset` (so the
867
+ * rows actually sync) and observe it, releasing the subscription when the scope
868
+ * closes. A pipeline built from `observed` leaves holds all its sources'
869
+ * subscriptions for its lifetime and drops them together on teardown — the
870
+ * refcounted-lifetime discipline the #114 brief asks for, `Subscriptions`
871
+ * doing the reference counting so a shared leaf is synced once.
872
+ *
873
+ * When `view` is a full {@link ClientView.ClientView} (not just the narrower
874
+ * {@link ClientView.Reader}), the returned source is built from its
875
+ * `observeChanges` feed and tagged so every combinator composed on top reads
876
+ * that feed directly too (see the module doc). A bare `Reader` has no
877
+ * `observeChanges` to prefer, so this falls back to `observe`'s pages,
878
+ * unchanged.
879
+ */
880
+ declare const observed: (subscriptions: Subscriptions, view: Reader | ClientView, subset: Subset) => Effect.Effect<Source<ClientViewError>, never, Scope.Scope>;
881
+ declare namespace FractionalIndex_d_exports {
882
+ export { compare, generateBetween, generateNBetween };
883
+ }
884
+ /**
885
+ * FractionalIndex
886
+ *
887
+ * Stable position keys for in-window rows (issue #112): a string that sorts
888
+ * between any two others, so a row entering, leaving, or moving within a
889
+ * maintained window can be given a key without renumbering its neighbors —
890
+ * the primitive behind expressing a delta as `{ type: "move", key, position
891
+ * }` instead of diffing pages (a future row-level delta feed's concern;
892
+ * `LiveView` only assigns and maintains the keys today).
893
+ *
894
+ * Each key is a base-62 digit string (`0-9A-Za-z`, chosen so ASCII order
895
+ * already matches digit order) read as a fraction in `[0, 1)`:
896
+ * `digits[0] * 62⁻¹ + digits[1] * 62⁻² + …`. Keys compare by that *value*,
897
+ * not raw string order — `compare` zero-pads the shorter key first, so e.g.
898
+ * `"5"` and `"50"` (both 5/62) compare equal. This is what keeps
899
+ * `generateBetween` total: two real numbers always have a value strictly
900
+ * between them, so — unlike schemes that compare the raw strings
901
+ * lexicographically (where, say, nothing sorts between `"A"` and `"A0"`,
902
+ * since any extension of `"A"` is lexicographically ≥ `"A0"`) — there is no
903
+ * pair of distinct keys this can fail to split.
904
+ *
905
+ * Repeatedly inserting at the same boundary (e.g. always "before the
906
+ * current first row") grows key length by roughly one digit every ~5-6
907
+ * insertions at that boundary (`log_2(62)`) — the standard, accepted cost of
908
+ * any fractional-indexing scheme (TanStack DB's `generateKeyBetween`,
909
+ * Figma's original algorithm) and not specific to this implementation.
910
+ */
911
+ /**
912
+ * Compare two keys by the fraction they encode (zero-padding the shorter
913
+ * one), not by raw string order — see the module doc for why that matters.
914
+ */
915
+ declare const compare: (a: string, b: string) => number;
916
+ /**
917
+ * A key whose value lies strictly between `lo` and `hi` (each defaulting to
918
+ * the open bound: `lo` omitted means 0, `hi` omitted means unbounded above).
919
+ * Throws if `lo` is not strictly before `hi`.
920
+ */
921
+ declare const generateBetween: (lo?: string, hi?: string) => string;
922
+ /**
923
+ * `count` keys, all strictly between `lo` and `hi` and strictly increasing —
924
+ * built by recursively bisecting the interval so the keys stay close in
925
+ * length (O(log count) longer than a single `generateBetween`) rather than
926
+ * chaining `generateBetween(lo, hi)` calls, which would grow the last key
927
+ * by one digit per call.
928
+ */
929
+ declare const generateNBetween: (lo: string | undefined, hi: string | undefined, count: number) => ReadonlyArray<string>;
930
+ declare namespace LiveQuery_d_exports {
931
+ export { LiveQueries, LiveQueryResult$1 as LiveQueryResult, LiveQuerySource, make$3 as make };
932
+ }
933
+ /** Anything that observes a subset as a stream of materialized rows. */
934
+ interface LiveQuerySource<E> {
935
+ readonly observe: (subset: Subset) => Stream.Stream<ReadonlyArray<LocalRow>, E>;
936
+ }
937
+ /**
938
+ * The Atom value for a live query: `AsyncResult` of the current rows. A
939
+ * never-ending source means it stays `Success` (or `Failure`) after the
940
+ * first emission; `NoSuchElementError` only appears if a source completes
941
+ * without emitting.
942
+ */
943
+ type LiveQueryResult$1<E> = AsyncResult.AsyncResult<ReadonlyArray<LocalRow>, E | Cause.NoSuchElementError>;
944
+ /** Shared live-query Atoms over a {@link LiveQuerySource}, one per subset identity. */
945
+ interface LiveQueries<E> {
946
+ /**
947
+ * The Atom for a subset's live results. Stable per subset identity
948
+ * (`Subset.hash`): the same subset yields the same Atom, so mounting it
949
+ * from many components shares one subscription.
950
+ */
951
+ readonly atom: (subset: Subset) => Atom.Atom<LiveQueryResult$1<E>>;
952
+ }
953
+ /** Build the Atom factory for a source's live queries. */
954
+ declare const make$3: <E>(source: LiveQuerySource<E>) => LiveQueries<E>;
955
+ declare namespace EngineAtoms_d_exports {
956
+ export { AggregateResult, EngineAtoms, GroupedAggregateResult, LiveQueryResult, MutationArg, MutationStatesResult, MutationsForResult, NamedQueryResult, TypedLiveQueryResult, fromLayer };
957
+ }
958
+ type Rows = ReadonlyArray<LocalRow>;
959
+ /**
960
+ * A live query's Atom value: the current rows as an `AsyncResult`. A
961
+ * never-ending source stays `Success` after the first emission;
962
+ * `NoSuchElementError` only appears if a source completes without emitting.
963
+ */
964
+ type LiveQueryResult = AsyncResult.AsyncResult<Rows, StoreError | MutatorError | Cause.NoSuchElementError>;
965
+ /**
966
+ * A typed live query's Atom value: the current rows refined to the row type
967
+ * `A`, as an `AsyncResult`. `TypedLiveQueryResult<unknown>` is structurally
968
+ * {@link LiveQueryResult}, so plain-subset callers are unaffected.
969
+ */
970
+ type TypedLiveQueryResult<A> = AsyncResult.AsyncResult<ReadonlyArray<TypedRow<A>>, StoreError | MutatorError | Cause.NoSuchElementError>;
971
+ /** A named query's Atom value (adds `QueryError` to the failure channel). */
972
+ type NamedQueryResult = AsyncResult.AsyncResult<Rows, StoreError | MutatorError | QueryError | Cause.NoSuchElementError>;
973
+ /** A live aggregate's Atom value: the current aggregate value as an `AsyncResult`. */
974
+ type AggregateResult = AsyncResult.AsyncResult<AggregateValue, StoreError | MutatorError | Cause.NoSuchElementError>;
975
+ /** A live grouped-aggregate's Atom value: the per-group record as an `AsyncResult`. */
976
+ type GroupedAggregateResult = AsyncResult.AsyncResult<Record<string, AggregateValue>, StoreError | MutatorError | Cause.NoSuchElementError>;
977
+ /** What to write to the mutation atom to run a mutator optimistically. */
978
+ interface MutationArg {
979
+ readonly mutator: string;
980
+ readonly args: unknown;
981
+ }
982
+ /** The mutation-state map's Atom value (#116): an `AsyncResult` of the current map. */
983
+ type MutationStatesResult = AsyncResult.AsyncResult<MutationStateMap, Cause.NoSuchElementError>;
984
+ /** A `mutationsFor` selector's Atom value: the matching states, oldest first. */
985
+ type MutationsForResult = AsyncResult.AsyncResult<ReadonlyArray<MutationState>, Cause.NoSuchElementError>;
986
+ /** The engine's reactive surface, built from its layer. */
987
+ interface EngineAtoms {
988
+ /** The runtime backing every engine atom; the registry owns its scope. */
989
+ readonly runtime: Atom.AtomRuntime<ClientEngine, never>;
990
+ /**
991
+ * The Atom for a subset's live results. Stable per subset identity
992
+ * (`Subset.hash`): the same subset yields the same Atom, so mounting it
993
+ * from many components shares one subscription. A {@link Subset.TypedSubset}
994
+ * refines the Atom's rows to its row type.
995
+ */
996
+ readonly liveQuery: {
997
+ <A>(subset: TypedSubset<A>): Atom.Atom<TypedLiveQueryResult<A>>;
998
+ (subset: Subset): Atom.Atom<LiveQueryResult>;
999
+ };
1000
+ /** The Atom for a named query's live results, stable per (name, args). */
1001
+ readonly namedQuery: (name: string, args: unknown) => Atom.Atom<NamedQueryResult>;
1002
+ /**
1003
+ * The Atom for a subset's live aggregate value. Stable per
1004
+ * `(subset hash, aggregate)`, so mounting it from many components shares one
1005
+ * subscription. Re-emits only when the aggregate value changes.
1006
+ */
1007
+ readonly aggregate: (subset: Subset, agg: Aggregate) => Atom.Atom<AggregateResult>;
1008
+ /** The Atom for a subset's live grouped aggregate, stable per `(subset hash, aggregate, groupBy)`. */
1009
+ readonly aggregateGrouped: (subset: Subset, agg: Aggregate, groupBy: string) => Atom.Atom<GroupedAggregateResult>;
1010
+ /** Write a {@link MutationArg} to run a mutator; its result is an `AsyncResult`. */
1011
+ readonly mutation: Atom.AtomResultFn<MutationArg, void, MutatorError | StoreError>;
1012
+ /**
1013
+ * The Atom for the engine's whole keyed mutation-state map (#116) —
1014
+ * `ClientEngine.mutationStates` re-emits on every fold; a single shared
1015
+ * Atom, so mounting it from many components shares one subscription.
1016
+ */
1017
+ readonly mutationStates: Atom.Atom<MutationStatesResult>;
1018
+ /**
1019
+ * A derived selector over {@link mutationStates}, filtered to one mutator
1020
+ * and sorted oldest-journaled first — "N unsent changes" or "list failed
1021
+ * intents" for one mutator in one line. Stable per `mutator`.
1022
+ */
1023
+ readonly mutationsFor: (mutator: string) => Atom.Atom<MutationsForResult>;
1024
+ /**
1025
+ * Like {@link mutation}, but its result resolves with the mutation's
1026
+ * authoritative outcome (applied or permanently rejected) once the backend
1027
+ * settles it — the atom stays `Waiting` until then. Backs `useMutationWithAck`.
1028
+ */
1029
+ readonly mutationWithAck: Atom.AtomResultFn<MutationArg, MutationOutcome, MutatorError | StoreError>;
1030
+ }
1031
+ /**
1032
+ * Build the engine's reactive surface from its layer. The layer must provide
1033
+ * `ClientEngine` with all of its own dependencies already satisfied —
1034
+ * `Atom.runtime` supplies only `AtomRegistry`/`Reactivity` and manages the
1035
+ * engine's scope for the registry's lifetime.
1036
+ */
1037
+ declare const fromLayer: (engineLayer: Layer.Layer<ClientEngine, never, AtomRegistry.AtomRegistry | Reactivity.Reactivity>) => EngineAtoms;
1038
+ declare namespace PromiseFacade_d_exports {
1039
+ export { PromiseFacade, Subscription, make$2 as make };
1040
+ }
1041
+ /**
1042
+ * A callback subscription handle.
1043
+ */
1044
+ interface Subscription {
1045
+ /** Stop receiving values and release the underlying fiber. */
1046
+ readonly unsubscribe: () => void;
1047
+ }
1048
+ /**
1049
+ * A promise/callback facade over an Effect runtime.
1050
+ */
1051
+ interface PromiseFacade<R> {
1052
+ /** The underlying managed runtime (for advanced use). */
1053
+ readonly runtime: ManagedRuntime.ManagedRuntime<R, never>;
1054
+ /** Run an effect, rejecting the promise on any failure. */
1055
+ readonly run: <A, E>(effect: Effect.Effect<A, E, R>) => Promise<A>;
1056
+ /** Run an effect, resolving with its `Exit` (never rejects on typed failures). */
1057
+ readonly runExit: <A, E>(effect: Effect.Effect<A, E, R>) => Promise<Exit.Exit<A, E>>;
1058
+ /** Consume a stream with callbacks. */
1059
+ readonly subscribe: <A, E>(stream: Stream.Stream<A, E, R>, listener: {
1060
+ readonly onValue: (value: A) => void;
1061
+ readonly onError?: ((error: E) => void) | undefined;
1062
+ readonly onDone?: (() => void) | undefined;
1063
+ }) => Subscription;
1064
+ /** Tear down the runtime and every running subscription. */
1065
+ readonly dispose: () => Promise<void>;
1066
+ }
1067
+ /**
1068
+ * Build a promise/callback facade from a layer providing `R`.
1069
+ *
1070
+ * ```ts
1071
+ * const facade = makePromiseFacade(
1072
+ * SyncCache.layer.pipe(Layer.provideMerge(MemoryCacheStore.layer()))
1073
+ * );
1074
+ * const value = await facade.run(
1075
+ * Effect.flatMap(SyncCache.SyncCache.asEffect(), (cache) =>
1076
+ * cache.read({ key: ["users", "1"], loader: fetchUser("1") })
1077
+ * )
1078
+ * );
1079
+ * await facade.dispose();
1080
+ * ```
1081
+ */
1082
+ declare const make$2: <R, ER>(layer: Layer.Layer<R, ER, never>) => PromiseFacade<R>;
1083
+ declare namespace MirrorProtocol_d_exports {
1084
+ export { EdgeSelect, FORM_MUTATION_MUTATOR, FormMutationBegin, FormRuling, HostMessage, HostMessageSchema, MUTATION_STATES_KEY, MirrorMessage, MirrorMessageSchema, MirrorRelatedRow, MirrorRow, MutationStateEntry, PulledRegistration, decodeHostMessage, decodeMirrorMessage, mirrorUpdate, mutationStatesUpdate, namedKey, relatedKey, relatedUpdate };
1085
+ }
1086
+ /** Wire codec for one selected edge of a `related` registration. */
1087
+ declare const EdgeSelect: Schema.Struct<{
1088
+ readonly name: Schema.String;
1089
+ readonly first: Schema.optional<Schema.Number>;
1090
+ }>;
1091
+ /** The type {@link EdgeSelect} decodes to. */
1092
+ type EdgeSelect = typeof EdgeSelect.Type;
1093
+ /** Wire codec for one {@link MutationState.MutationState} entry (#116). */
1094
+ declare const MutationStateEntry: Schema.Struct<{
1095
+ readonly mutationId: Schema.Number;
1096
+ readonly mutator: Schema.String;
1097
+ readonly args: Schema.Unknown;
1098
+ readonly phase: Schema.Literals<readonly ["pending", "acknowledged", "rejected", "dropped"]>;
1099
+ readonly reason: Schema.optional<Schema.String>;
1100
+ readonly journaledAt: Schema.Number;
1101
+ readonly settledAt: Schema.optional<Schema.Number>;
1102
+ }>;
1103
+ /** The type {@link MutationStateEntry} decodes to. */
1104
+ type MutationStateEntry = typeof MutationStateEntry.Type;
1105
+ /** The broadcast key a named query's results are mirrored under. */
1106
+ declare const namedKey: (name: string, args: unknown) => string;
1107
+ /**
1108
+ * The broadcast key {@link ClientEngine.ClientEngine.mutationStates} is
1109
+ * mirrored under (#116) — a singleton, unparameterized registration (unlike
1110
+ * subsets/named queries, there is exactly one mutation-state map per engine).
1111
+ */
1112
+ declare const MUTATION_STATES_KEY = "mutationStates";
1113
+ /**
1114
+ * The broadcast key a `related` registration's resolved rows are mirrored
1115
+ * under. Each edge's window (`first`) is part of the key: reads differing
1116
+ * only in window are distinct registrations (ADR-0024).
1117
+ */
1118
+ declare const relatedKey: (collection: string, subset: Subset, edges: ReadonlyArray<string | EdgeSelect$1>) => string;
1119
+ /**
1120
+ * The reserved mutator name an enhanced form submit's begin travels under
1121
+ * (ADR-0030 #215 §4, #536). A mirror asks its host to begin a form mutation
1122
+ * with a `Mutate` naming this mutator, its args a {@link FormMutationBegin}.
1123
+ * A host that knows it answers `FormMutationBegun` or `FormMutationFailed`
1124
+ * and journals nothing. It travels as a `Mutate`, not a tag of its own, so a
1125
+ * host that predates it answers too: its engine has no such mutator, so it
1126
+ * replies `MutateError` and journals nothing, and the mirror reports that
1127
+ * the host is too old instead of waiting on a message the host dropped as
1128
+ * foreign traffic. No app mutator may take this name.
1129
+ */
1130
+ declare const FORM_MUTATION_MUTATOR = "@thomasfosterau/legation/beginFormMutation";
1131
+ /**
1132
+ * The args of a {@link FORM_MUTATION_MUTATOR} `Mutate`: the mirror the entry
1133
+ * belongs to (its `Disconnect`, or the liveness sweep, abandons it), the form
1134
+ * client, and the Mutation with its args in encoded (wire) form, which the
1135
+ * host decodes.
1136
+ */
1137
+ declare const FormMutationBegin: Schema.Struct<{
1138
+ readonly mirrorId: Schema.String;
1139
+ readonly form: Schema.Struct<{
1140
+ readonly clientId: Schema.String;
1141
+ readonly mutationId: Schema.Number;
1142
+ }>;
1143
+ readonly mutator: Schema.String;
1144
+ readonly args: Schema.Unknown;
1145
+ }>;
1146
+ /** The decoded {@link FormMutationBegin}. */
1147
+ type FormMutationBegin = typeof FormMutationBegin.Type;
1148
+ /** Wire codec for {@link ClientEngine.FormRuling}: the ruling an `EndFormMutation` settles on. */
1149
+ declare const FormRuling: Schema.Union<readonly [Schema.Struct<{
1150
+ readonly phase: Schema.Literal<"acknowledged">;
1151
+ }>, Schema.Struct<{
1152
+ readonly phase: Schema.Literal<"rejected">;
1153
+ readonly reason: Schema.String;
1154
+ }>]>;
1155
+ /** The decoded {@link FormRuling}; the same shape as {@link ClientEngine.FormRuling}. */
1156
+ type FormRuling = typeof FormRuling.Type;
1157
+ /** A message a mirror sends to the host. */
1158
+ type MirrorMessage = {
1159
+ readonly _tag: "Mutate";
1160
+ readonly requestId: number;
1161
+ /**
1162
+ * The requesting mirror (#546). Its replies echo it, so a mirror ignores a
1163
+ * reply to another tab's request of the same `requestId`. Absent from a
1164
+ * mirror that predates it.
1165
+ */
1166
+ readonly mirrorId?: string;
1167
+ readonly mutator: string;
1168
+ readonly args: unknown;
1169
+ } | {
1170
+ readonly _tag: "Subscribe";
1171
+ readonly mirrorId: string;
1172
+ readonly subset: Subset;
1173
+ } | {
1174
+ readonly _tag: "Unsubscribe";
1175
+ readonly mirrorId: string;
1176
+ readonly subset: Subset;
1177
+ } | {
1178
+ readonly _tag: "SubscribeNamed";
1179
+ readonly mirrorId: string;
1180
+ readonly name: string;
1181
+ readonly args: unknown;
1182
+ } | {
1183
+ readonly _tag: "UnsubscribeNamed";
1184
+ readonly mirrorId: string;
1185
+ readonly name: string;
1186
+ readonly args: unknown;
1187
+ } | {
1188
+ readonly _tag: "SubscribeRelated";
1189
+ readonly mirrorId: string;
1190
+ readonly collection: string;
1191
+ readonly subset: Subset;
1192
+ /** The selected edges, each optionally windowed (ADR-0024). */
1193
+ readonly edges: ReadonlyArray<EdgeSelect$1>;
1194
+ } | {
1195
+ readonly _tag: "UnsubscribeRelated";
1196
+ readonly mirrorId: string;
1197
+ readonly collection: string;
1198
+ readonly subset: Subset;
1199
+ readonly edges: ReadonlyArray<EdgeSelect$1>;
1200
+ } |
1201
+ /**
1202
+ * A URL-keyed subscription (ADR-0029 section 2, #159) from a party with
1203
+ * no `Client` of its own — a page posting on the channel. The host parses
1204
+ * the URL through its `UrlReads`, and registers the read's root window
1205
+ * and edge windows under `mirrorId` exactly as a mirror's `Subscribe` and
1206
+ * `SubscribeRelated` would: refcounted per party, replayed on `HostReady`
1207
+ * by the party, and reclaimed by the liveness sweep unless the party
1208
+ * heartbeats. A URL the host cannot parse registers nothing.
1209
+ */
1210
+ {
1211
+ readonly _tag: "SubscribeUrl";
1212
+ readonly mirrorId: string;
1213
+ readonly url: string;
1214
+ } | {
1215
+ readonly _tag: "UnsubscribeUrl";
1216
+ readonly mirrorId: string;
1217
+ readonly url: string;
1218
+ } |
1219
+ /** A server-fallthrough read (ADR-0025), answered by `ReadResult`. */
1220
+ {
1221
+ readonly _tag: "Read";
1222
+ readonly requestId: number;
1223
+ /**
1224
+ * The requesting mirror (#546). Its replies echo it, so a mirror ignores a
1225
+ * reply to another tab's request of the same `requestId`. Absent from a
1226
+ * mirror that predates it.
1227
+ */
1228
+ readonly mirrorId?: string;
1229
+ readonly subsets: ReadonlyArray<Subset>;
1230
+ } |
1231
+ /** Register interest in the mutation-state map (#116); see {@link MUTATION_STATES_KEY}. */
1232
+ {
1233
+ readonly _tag: "SubscribeMutationStates";
1234
+ readonly mirrorId: string;
1235
+ } | {
1236
+ readonly _tag: "UnsubscribeMutationStates";
1237
+ readonly mirrorId: string;
1238
+ } |
1239
+ /** A mirror leaving: release every subscription it held. */
1240
+ {
1241
+ readonly _tag: "Disconnect";
1242
+ readonly mirrorId: string;
1243
+ } |
1244
+ /** Liveness ping: lets the host reclaim subsets from mirrors that crash. */
1245
+ {
1246
+ readonly _tag: "Heartbeat";
1247
+ readonly mirrorId: string;
1248
+ } |
1249
+ /**
1250
+ * End an enhanced form submission begun with a {@link FORM_MUTATION_MUTATOR}
1251
+ * `Mutate` (#536): `ruling` settles it on the form action's receipt, and its
1252
+ * absence abandons it. `requestId` is the begin's. Answered by
1253
+ * `FormMutationEnded`, also when the host no longer holds the entry.
1254
+ */
1255
+ {
1256
+ readonly _tag: "EndFormMutation";
1257
+ readonly mirrorId: string;
1258
+ readonly requestId: number;
1259
+ readonly ruling?: FormRuling$1;
1260
+ };
1261
+ /** Wire codec for {@link MirrorMessage}. */
1262
+ declare const MirrorMessageSchema: Schema.Codec<MirrorMessage>;
1263
+ /** A row as the host sends it to a mirror. */
1264
+ interface MirrorRow {
1265
+ readonly id: string;
1266
+ readonly value: unknown;
1267
+ }
1268
+ /** A resolved relational row on the wire: base row + its resolved names. */
1269
+ interface MirrorRelatedRow {
1270
+ readonly id: string;
1271
+ readonly value: unknown;
1272
+ readonly related: Readonly<Record<string, unknown>>;
1273
+ }
1274
+ /** A message the host sends to its mirrors. */
1275
+ type HostMessage =
1276
+ /**
1277
+ * Broadcast once when a host starts serving. Mirrors answer by re-posting
1278
+ * every registration they hold, so subscriptions survive a host that
1279
+ * (re)starts after the mirror — a killed service worker, or a client context
1280
+ * that raced the worker's boot.
1281
+ */
1282
+ {
1283
+ readonly _tag: "HostReady";
1284
+ } | {
1285
+ readonly _tag: "MutateAck";
1286
+ readonly requestId: number;
1287
+ /**
1288
+ * The mirror the request came from (#546), echoed from the request. A
1289
+ * mirror ignores a reply addressed to another; a host that predates it
1290
+ * sends none, and the reply is taken by `requestId` alone.
1291
+ */
1292
+ readonly mirrorId?: string;
1293
+ } | {
1294
+ readonly _tag: "MutateError";
1295
+ readonly requestId: number;
1296
+ /**
1297
+ * The mirror the request came from (#546), echoed from the request. A
1298
+ * mirror ignores a reply addressed to another; a host that predates it
1299
+ * sends none, and the reply is taken by `requestId` alone.
1300
+ */
1301
+ readonly mirrorId?: string;
1302
+ readonly message: string;
1303
+ } |
1304
+ /**
1305
+ * The backend's authoritative outcome for a forwarded mutation, correlated by
1306
+ * `requestId`. Sent in addition to `MutateAck` (which still fires on journal),
1307
+ * so a mirror can await the backend verdict via `Mirror.mutateWithAck`.
1308
+ */
1309
+ {
1310
+ readonly _tag: "MutateSettled";
1311
+ readonly requestId: number;
1312
+ /**
1313
+ * The mirror the request came from (#546), echoed from the request. A
1314
+ * mirror ignores a reply addressed to another; a host that predates it
1315
+ * sends none, and the reply is taken by `requestId` alone.
1316
+ */
1317
+ readonly mirrorId?: string;
1318
+ readonly outcome: "applied" | "rejected";
1319
+ readonly reason?: string;
1320
+ } | {
1321
+ readonly _tag: "MirrorUpdate";
1322
+ /** `Subset.hash` of the subset (or {@link namedKey}) these rows materialize. */
1323
+ readonly key: string;
1324
+ readonly rows: ReadonlyArray<MirrorRow>;
1325
+ } | {
1326
+ readonly _tag: "RelatedUpdate";
1327
+ /** {@link relatedKey} of the registration these resolved rows materialize. */
1328
+ readonly key: string;
1329
+ readonly rows: ReadonlyArray<MirrorRelatedRow>;
1330
+ } |
1331
+ /** The answer to a `Read` request (ADR-0025), correlated by `requestId`. */
1332
+ {
1333
+ readonly _tag: "ReadResult";
1334
+ readonly requestId: number;
1335
+ /**
1336
+ * The mirror the request came from (#546), echoed from the request. A
1337
+ * mirror ignores a reply addressed to another; a host that predates it
1338
+ * sends none, and the reply is taken by `requestId` alone.
1339
+ */
1340
+ readonly mirrorId?: string;
1341
+ readonly rows: ReadonlyArray<{
1342
+ readonly collection: string;
1343
+ readonly id: string;
1344
+ readonly value: unknown;
1345
+ }>;
1346
+ /** Set instead of rows when the host could not serve the read. */
1347
+ readonly error?: string;
1348
+ } |
1349
+ /** The whole mutation-state map, broadcast on every fold (#116). */
1350
+ {
1351
+ readonly _tag: "MutationStatesUpdate";
1352
+ readonly states: ReadonlyArray<MutationState>;
1353
+ } |
1354
+ /**
1355
+ * The pulled summaries of the host's registrations (#426): per key (a
1356
+ * `Subscribe`'s subset hash, or a `SubscribeRelated`'s {@link relatedKey}),
1357
+ * each window the registration holds with whether its rows are in the
1358
+ * replica. A mirror answers a covered read locally only when the windows
1359
+ * the read's rows link to are pulled (`Coverage.pulledFor`). Sent whenever it
1360
+ * changes and on `HostReady`. A mirror that predates it ignores the message
1361
+ * (foreign traffic) and judges coverage from its registrations alone, as it
1362
+ * did before #426: it answers covered reads locally, so the #422 race (a
1363
+ * covered answer before its linked windows are pulled) persists for it until
1364
+ * it is upgraded.
1365
+ */
1366
+ {
1367
+ readonly _tag: "PulledRegistrations";
1368
+ readonly registrations: ReadonlyArray<PulledRegistration>;
1369
+ } |
1370
+ /**
1371
+ * A {@link FORM_MUTATION_MUTATOR} begin overlaid under its form client
1372
+ * (#536). The form replies carry the begin's `mirrorId` as well as its
1373
+ * `requestId`: host messages are broadcast to every mirror, and each
1374
+ * mirror numbers its requests from zero, so a `requestId` alone could
1375
+ * complete another tab's request.
1376
+ */
1377
+ {
1378
+ readonly _tag: "FormMutationBegun";
1379
+ readonly mirrorId: string;
1380
+ readonly requestId: number;
1381
+ } |
1382
+ /**
1383
+ * A {@link FORM_MUTATION_MUTATOR} begin the host refused (unknown Mutation,
1384
+ * args that do not decode, a store failure): nothing was overlaid. A
1385
+ * dedicated tag, so that `MutateError` for a begin means only "this host
1386
+ * predates enhanced form submits".
1387
+ */
1388
+ {
1389
+ readonly _tag: "FormMutationFailed";
1390
+ readonly mirrorId: string;
1391
+ readonly requestId: number;
1392
+ readonly message: string;
1393
+ } |
1394
+ /**
1395
+ * The answer to `EndFormMutation`, correlated by the begin's `requestId`.
1396
+ * `error` is set when settling failed in the host's store (the entry could
1397
+ * not be parked in the unpulled tail); the host rebuilt its overlay and
1398
+ * pulled regardless.
1399
+ */
1400
+ {
1401
+ readonly _tag: "FormMutationEnded";
1402
+ readonly mirrorId: string;
1403
+ readonly requestId: number;
1404
+ readonly error?: string;
1405
+ };
1406
+ /** One registration's pulled summary, keyed as the host keys it. */
1407
+ interface PulledRegistration {
1408
+ readonly key: string;
1409
+ readonly pulled: Pulled;
1410
+ }
1411
+ /** Wire codec for {@link HostMessage}. */
1412
+ declare const HostMessageSchema: Schema.Codec<HostMessage>;
1413
+ /** Build a MirrorUpdate from materialized engine rows. */
1414
+ declare const mirrorUpdate: (key: string, rows: ReadonlyArray<LocalRow>) => HostMessage;
1415
+ /** Build a RelatedUpdate from resolved relational rows. */
1416
+ declare const relatedUpdate: (key: string, rows: ReadonlyArray<RelatedRow>) => HostMessage;
1417
+ /** Build a MutationStatesUpdate from the engine's current mutation-state map (#116). */
1418
+ declare const mutationStatesUpdate: (states: MutationStateMap) => HostMessage;
1419
+ /** Decode an inbound mirror message; `undefined` for foreign traffic. */
1420
+ declare const decodeMirrorMessage: (input: unknown) => Effect.Effect<MirrorMessage | undefined>;
1421
+ /** Decode an inbound host message; `undefined` for foreign traffic. */
1422
+ declare const decodeHostMessage: (input: unknown) => Effect.Effect<HostMessage | undefined>;
1423
+ declare namespace EngineHost_d_exports {
1424
+ export { EngineHostOptions, Host, run$2 as run };
1425
+ }
1426
+ /** Options for {@link run}: the channel mirrors connect over and the engine they share. */
1427
+ interface EngineHostOptions {
1428
+ readonly channel: MirrorChannel;
1429
+ readonly engine: ClientEngine;
1430
+ /**
1431
+ * The collection registry `related` registrations resolve against.
1432
+ * Defaults to `nodes.collections` when `nodes` is given; without either,
1433
+ * `SubscribeRelated` messages are answered with empty results and a
1434
+ * logged warning — pass one whenever mirrors use relational reads.
1435
+ */
1436
+ readonly registry?: CollectionRegistry;
1437
+ /**
1438
+ * The node graph and URL-keyed reads `SubscribeUrl` registrations are
1439
+ * parsed and planned against (#159). Without both, a `SubscribeUrl` is
1440
+ * answered with a logged warning and registers nothing.
1441
+ */
1442
+ readonly nodes?: NodeRegistry;
1443
+ readonly urls?: UrlReads;
1444
+ /**
1445
+ * Liveness sweep: reclaim registrations from mirrors not heard from within
1446
+ * `mirrorTimeout`, checked every `sweepInterval`. Defaults: 30s timeout,
1447
+ * 10s sweep — pair with a Mirror heartbeat shorter than the timeout.
1448
+ */
1449
+ readonly liveness?: {
1450
+ readonly mirrorTimeout?: Duration.Input;
1451
+ readonly sweepInterval?: Duration.Input;
1452
+ };
1453
+ }
1454
+ /** The running host: what a worker can consult about the mirrors it serves. */
1455
+ interface Host {
1456
+ /**
1457
+ * The registrations the host holds on mirrors' behalf at this moment, each
1458
+ * with its pulled summary (#426): root windows (`Subscribe`), relational
1459
+ * registrations with their edge selects (`SubscribeRelated`) and both
1460
+ * halves of a `SubscribeUrl`, the latter carrying its canonical URL for as
1461
+ * long as some party holds it by that URL. A registration's windows are
1462
+ * pulled once a pull started after each was made has applied; a coverage
1463
+ * check counts only the windows a read depends on. Named-query and
1464
+ * mutation-state registrations cover no read and are not listed. The ledger
1465
+ * a coverage check scans: `Coverage.coverageOf(nodes, view.query, host.registrations)`.
1466
+ */
1467
+ readonly registrations: Effect.Effect<ReadonlyArray<Entry>>;
1468
+ }
1469
+ /**
1470
+ * Serve the engine over the channel. The inbound subscription is
1471
+ * established before this effect completes; message handling runs on a
1472
+ * scoped background fiber.
1473
+ */
1474
+ declare const run$2: (options: EngineHostOptions) => Effect.Effect<Host, never, Scope.Scope>;
1475
+ declare namespace Mirror_d_exports {
1476
+ export { Mirror, MirrorOptions, make$1 as make };
1477
+ }
1478
+ /** Options for {@link make}: the channel to the host and the shared mutator registry. */
1479
+ interface MirrorOptions {
1480
+ readonly channel: MirrorChannel;
1481
+ /** The shared mutator registry (same definitions the host engine runs). */
1482
+ readonly mutators: MutatorRegistry;
1483
+ /**
1484
+ * How often to send a liveness heartbeat to the host. Keep it shorter
1485
+ * than the host's `mirrorTimeout` so a live mirror is never reclaimed. Defaults
1486
+ * to 10 seconds.
1487
+ */
1488
+ readonly heartbeatInterval?: Duration.Input;
1489
+ /**
1490
+ * Capacity of the bounded change hub (#44). A stalled observer cannot
1491
+ * make the hub grow without limit: at capacity the hub drops the newest
1492
+ * subset keys, and the observer collapses the resulting seq gap into a full
1493
+ * re-read of its subset — lossless because the newest rows live in the
1494
+ * mirror's `Ref`. Defaults to {@link DEFAULT_CHANGE_HUB_CAPACITY}.
1495
+ */
1496
+ readonly changeHubCapacity?: number;
1497
+ }
1498
+ /**
1499
+ * A client context's in-memory copy of a host engine's subscribed subsets: queries are answered
1500
+ * synchronously from it, and writes are forwarded to the host's journal.
1501
+ */
1502
+ interface Mirror {
1503
+ /** Validate args locally, forward to the host journal, await the ack. */
1504
+ readonly mutate: (mutator: string, args: unknown) => Effect.Effect<void, MutatorError | TransportError>;
1505
+ /**
1506
+ * Like {@link mutate}, but resolves with the mutation's authoritative backend
1507
+ * outcome (applied or permanently rejected) once the host forwards it, rather
1508
+ * than on the host journal ack.
1509
+ */
1510
+ readonly mutateWithAck: (mutator: string, args: unknown) => Effect.Effect<MutationOutcome, MutatorError | TransportError>;
1511
+ /**
1512
+ * The client half of an enhanced form submit (ADR-0030 #215 §4), carried to
1513
+ * the host (#536): the host's engine overlays the Mutation under the form
1514
+ * client's id without journaling it, and holds the in-flight entry, so its
1515
+ * overlay and unpulled tail serve every mirror. Settling or abandoning the
1516
+ * returned submission tells the host; as on an engine, an acknowledgement
1517
+ * parks the effect until the pull that covers the form client releases it.
1518
+ * Fails with a `TransportError` when the host predates this, and with a
1519
+ * `MutatorError` when the args do not fit the Mutation or the host refuses
1520
+ * the begin. The signature is `ClientView`'s. A submission whose host is gone (restarted, or this mirror's
1521
+ * scope closed) settles as a no-op: its entry went with the host.
1522
+ */
1523
+ readonly beginFormMutation: NonNullable<ClientView["beginFormMutation"]>;
1524
+ /** Register a subset with the host; rows arrive as mirror updates. */
1525
+ readonly subscribe: (subset: Subset) => Effect.Effect<void, TransportError>;
1526
+ readonly unsubscribe: (subset: Subset) => Effect.Effect<void, TransportError>;
1527
+ /**
1528
+ * Register a named query with the host (which owns the server-authoritative
1529
+ * resolution); results arrive as mirror updates under the named key.
1530
+ */
1531
+ readonly subscribeQuery: (name: string, args: unknown) => Effect.Effect<void, TransportError>;
1532
+ readonly unsubscribeQuery: (name: string, args: unknown) => Effect.Effect<void, TransportError>;
1533
+ /** The mirror's current rows for a subset (empty until hydrated). */
1534
+ readonly query: (subset: Subset) => Effect.Effect<ReadonlyArray<LocalRow>>;
1535
+ /**
1536
+ * Server-fallthrough read (ADR-0025), forwarded to the host — which owns
1537
+ * the transport — and answered point-in-time, never entering the mirror.
1538
+ */
1539
+ readonly read: (subsets: ReadonlyArray<Subset>) => Effect.Effect<ReadonlyArray<ReadRow>, MutatorError | TransportError>;
1540
+ /** The mirror's current rows for a named query (empty until hydrated). */
1541
+ readonly queryNamed: (name: string, args: unknown) => Effect.Effect<ReadonlyArray<LocalRow>>;
1542
+ /** Emits the current rows now and on every mirror update for the subset. */
1543
+ readonly observe: (subset: Subset) => Stream.Stream<ReadonlyArray<LocalRow>>;
1544
+ /** The host's pulled summary of the registration under `key` (#426); see {@link ClientView.ClientView.pulled}. */
1545
+ readonly pulled: (key: string) => Effect.Effect<Pulled>;
1546
+ /**
1547
+ * Row-level deltas of a subset's live window (#115) — see
1548
+ * {@link ClientEngine.ClientEngine.observeChanges}. The mirror has no
1549
+ * incremental `LiveView` of its own: it diffs consecutive whole-page
1550
+ * `MirrorUpdate`s (`RowChange.diff`), which is exact because the host
1551
+ * already delivers the full current page on every change. A gap on the
1552
+ * mirror's change hub (a dropped key — CONTEXT.md's hub table) re-reads
1553
+ * the always-current `rows` Ref, same as `observe`, and is folded into a
1554
+ * `reset` batch here rather than a diff, since the skipped updates in
1555
+ * between are unknown.
1556
+ */
1557
+ readonly observeChanges: (subset: Subset, options?: {
1558
+ readonly includeInitial?: boolean;
1559
+ }) => Stream.Stream<ReadonlyArray<RowChange<LocalRow>>>;
1560
+ /**
1561
+ * The host engine's keyed mutation-state map (#116), mirrored so every tab
1562
+ * sees the same states the host holds. Registers interest with the host for
1563
+ * the stream's lifetime (refcounted like `observe`); emits the current map
1564
+ * now and on every host-side fold.
1565
+ */
1566
+ readonly mutationStates: Stream.Stream<MutationStateMap>;
1567
+ /** Emits the named query's current rows now and on every mirror update. */
1568
+ readonly observeNamed: (name: string, args: unknown) => Stream.Stream<ReadonlyArray<LocalRow>>;
1569
+ /**
1570
+ * Auto-subscribing relational read: the HOST syncs the base and narrowed
1571
+ * foreign subsets and computes the join — the mirror receives resolved rows
1572
+ * (main-thread work stays minimal). The registration is released when the
1573
+ * scope closes. The `registry` parameter exists for {@link ClientView}
1574
+ * signature parity; the host resolves against its own registry.
1575
+ */
1576
+ readonly related: (registry: CollectionRegistry, collectionName: string, subset: Subset, edges: ReadonlyArray<string | EdgeSelect$1>) => Effect.Effect<Stream.Stream<ReadonlyArray<RelatedRow>, never>, never, Scope.Scope>;
1577
+ /**
1578
+ * Hold a relational registration with the host for the scope's lifetime
1579
+ * without reading its resolved rows — an explicit pin's edge windows
1580
+ * (ADR-0029 section 2). The host runs the same reconcile loop it runs for
1581
+ * {@link related}; the signature is `ClientView`'s (the `registry`
1582
+ * parameter exists for that parity alone).
1583
+ */
1584
+ readonly pinEdges: ClientView["pinEdges"];
1585
+ }
1586
+ /**
1587
+ * Connect a {@link Mirror} to the host on `options.channel`; its subscriptions are released when
1588
+ * the scope closes.
1589
+ */
1590
+ declare const make$1: (options: MirrorOptions) => Effect.Effect<Mirror, never, Scope.Scope>;
1591
+ declare namespace BackendContract_d_exports {
1592
+ export { ContractViolation, PeerSettle, VerifyOptions, countUp, delRow, failAlways, memoryHandlers, park, putRow, rejectAlways, verifyBackendContract, writeThenFail };
1593
+ }
1594
+ declare const ContractViolation_base: Schema.Class<ContractViolation, Schema.TaggedStruct<"ContractViolation", {
1595
+ readonly rule: Schema.String;
1596
+ readonly message: Schema.String;
1597
+ }>, import("effect/Cause").YieldableError & {
1598
+ readonly brand: unique symbol;
1599
+ }>;
1600
+ /** A backend broke one of the contract's guarantees. */
1601
+ declare class ContractViolation extends ContractViolation_base {}
1602
+ /**
1603
+ * The park a `contract/count-up` handler takes between its read and its write.
1604
+ * When the harness has registered `gate`, it signals that the handler is parked
1605
+ * and waits until the harness releases it; for any other name it does nothing.
1606
+ * Only the contract's `countUp` handler calls it, so no production handler should.
1607
+ */
1608
+ declare const park: (gate: string) => Effect.Effect<void>;
1609
+ /** Write one row. */
1610
+ declare const putRow: Mutator<"contract/put", {
1611
+ readonly collection: string;
1612
+ readonly id: string;
1613
+ readonly value: unknown;
1614
+ }, {
1615
+ readonly collection: string;
1616
+ readonly id: string;
1617
+ readonly value: unknown;
1618
+ }>;
1619
+ /** Delete one row. */
1620
+ declare const delRow: Mutator<"contract/del", {
1621
+ readonly collection: string;
1622
+ readonly id: string;
1623
+ }, {
1624
+ readonly collection: string;
1625
+ readonly id: string;
1626
+ }>;
1627
+ /**
1628
+ * Fails transiently — probes that a `BackendError` aborts the batch and the
1629
+ * mutation-id bump is atomic with effects (no bump on abort).
1630
+ */
1631
+ declare const failAlways: Mutator<"contract/fail", {}, {}>;
1632
+ /**
1633
+ * Writes a row and then rejects permanently — probes that a `MutationRejected`
1634
+ * bumps the id *without* leaving effects (the staged write must roll back).
1635
+ */
1636
+ declare const rejectAlways: Mutator<"contract/reject", {
1637
+ readonly collection: string;
1638
+ readonly id: string;
1639
+ }, {
1640
+ readonly collection: string;
1641
+ readonly id: string;
1642
+ }>;
1643
+ /**
1644
+ * Writes a row and then fails transiently — probes that a `BackendError` leaves
1645
+ * no trace of that write (the row keeps its value and version) and no bump
1646
+ * (the client's retry applies the mutation again). `failAlways` writes nothing
1647
+ * first, so it does not probe this. A backend that cannot discard a handler's
1648
+ * writes on its own failure fails this rule by design: D1 commits each
1649
+ * statement separately (#342), so it is not run through this contract. A
1650
+ * registered handler that never writes passes this rule vacuously, and the
1651
+ * contract cannot detect that: it sees only the failure.
1652
+ */
1653
+ declare const writeThenFail: Mutator<"contract/write-then-fail", {
1654
+ readonly collection: string;
1655
+ readonly id: string;
1656
+ }, {
1657
+ readonly collection: string;
1658
+ readonly id: string;
1659
+ }>;
1660
+ /**
1661
+ * Reads a counter row, parks after the read (see {@link park}), then writes the
1662
+ * counter plus one. Two client groups running it on one row must both count
1663
+ * (rule 11). `gate` names a park gate the harness registered, or matches none.
1664
+ *
1665
+ * A third-party backend registers its handler for this mutator under
1666
+ * `BackendContract.countUp.name` (`contract/count-up`), passes `gate` unchanged
1667
+ * to `park` between its read and its write, and writes the counter plus one. A
1668
+ * handler that omits `park` fails rule 11 with "finished without reaching park()".
1669
+ */
1670
+ declare const countUp: Mutator<"contract/count-up", {
1671
+ readonly collection: string;
1672
+ readonly id: string;
1673
+ readonly gate: string;
1674
+ }, {
1675
+ readonly collection: string;
1676
+ readonly id: string;
1677
+ readonly gate: string;
1678
+ }>;
1679
+ /** Ready-made authoritative handlers for MemoryBackend-style registration. */
1680
+ declare const memoryHandlers: Record<string, BackendHandler>;
1681
+ /**
1682
+ * Waits, while rule 11 holds a parked call, for the peer call the harness started. `peerSettled`
1683
+ * reads true once the peer's call has finished. The returned effect must complete once the peer has
1684
+ * settled, or once the backend observes the peer blocked on a lock the parked call holds (the peer
1685
+ * can then finish only after the parked call is released). It decides on that observed state, never
1686
+ * on elapsed time.
1687
+ */
1688
+ type PeerSettle = (peerSettled: Effect.Effect<boolean>) => Effect.Effect<void>;
1689
+ /** Options for {@link verifyBackendContract}. */
1690
+ interface VerifyOptions {
1691
+ /** Base name for the collections written during verification. */
1692
+ readonly collection?: string;
1693
+ readonly clientGroupId?: string;
1694
+ /**
1695
+ * How rule 11 waits for the peer while the parked call is held. Absent, the
1696
+ * harness gives the peer a bounded number of scheduler turns, which suits a backend
1697
+ * whose peer runs at once or waits on a lock. A backend whose peer does real I/O
1698
+ * (PostgreSQL) cannot reach the overlap that way, so it observes the peer instead
1699
+ * (#416).
1700
+ */
1701
+ readonly awaitPeer?: PeerSettle | undefined;
1702
+ /**
1703
+ * The contract client's ledger a restarted run expects: the id the backend reported for it before the
1704
+ * restart. Absent, the run is a fresh group and expects 0. The contract fails first when the backend
1705
+ * reports another id, naming a forgotten or differing ledger, then numbers its mutations from it (#483).
1706
+ */
1707
+ readonly restoredLedger?: number | undefined;
1708
+ /**
1709
+ * The ledger rule 11's peer group expects on a restarted run, as `restoredLedger` is for the
1710
+ * contract client. Absent, the peer group is fresh and expects 0 (#483).
1711
+ */
1712
+ readonly restoredPeerLedger?: number | undefined;
1713
+ }
1714
+ /**
1715
+ * Verify a backend against the contract. The backend must have the contract
1716
+ * mutators registered and the contract collections empty. On a fresh client group
1717
+ * the contract expects the backend to report a ledger of 0 for its client. A run
1718
+ * over a restarted backend passes the ledger it expects as `restoredLedger`: the
1719
+ * contract first checks the backend reports exactly that, failing a backend that
1720
+ * forgot its ledger, and then numbers its mutations from it (#483).
1721
+ */
1722
+ declare const verifyBackendContract: (backend: Backend, options?: VerifyOptions) => Effect.Effect<void, ContractViolation>;
1723
+ declare namespace RejectionMemory_d_exports {
1724
+ export { floorsOf, forgetBelow };
1725
+ }
1726
+ /**
1727
+ * The forgetting bound for a backend's rejection memory (#355, #366, #468): the
1728
+ * per-client rule that drops a remembered rejection once the client's next push
1729
+ * has moved past it. MemoryBackend and JsonApiBackend bound their memory with
1730
+ * `forgetBelow`; SqlBackend shares only `floorsOf` and keeps its own forgetting
1731
+ * loop over its ledger rows.
1732
+ */
1733
+ /**
1734
+ * The lowest mutation id a push carries, per client: the push's floors (#355).
1735
+ * A client resends only ids it has not had acknowledged, so a push whose lowest
1736
+ * id for a client is `f` proves the client has seen the outcome of every id below `f`.
1737
+ */
1738
+ declare const floorsOf: (mutations: ReadonlyArray<{
1739
+ readonly clientId: string;
1740
+ readonly mutationId: number;
1741
+ }>) => Map<string, number>;
1742
+ /**
1743
+ * Forget the rejections a client's push has moved past (#355): drop each client's
1744
+ * remembered rejections whose mutation id is below its floor, and keep the rest.
1745
+ * `clients` maps each client in one client group to its remembered rejections
1746
+ * (mutation id → reason); `floors` maps each client in a push to the lowest
1747
+ * mutation id that push carries (see {@link floorsOf}). A client with no
1748
+ * rejections left is removed from the result, and a pull never forgets, so only
1749
+ * a push calls this. Returns `clients` itself when nothing is forgotten, so a
1750
+ * caller can detect the no-op by reference.
1751
+ */
1752
+ declare const forgetBelow: (clients: ReadonlyMap<string, ReadonlyMap<number, string>>, floors: ReadonlyMap<string, number>) => ReadonlyMap<string, ReadonlyMap<number, string>>;
1753
+ declare namespace FilterCorpus_d_exports {
1754
+ export { Case, Column, Corpus, CorpusOptions, CorpusRow, Coverage, DEFAULT_SEED, Disagreement, GenerateOptions, OperatorName, corpusColumns, emptyCoverage, formatDisagreement, generateCorpus, generateOrderBy, generatePredicate, generateRows, leafOperators, minimise, minimumRows, resolveSeed, shrinkPredicate };
1755
+ }
1756
+ /** The default corpus seed; `LEGATION_CORPUS_SEED` overrides it. */
1757
+ declare const DEFAULT_SEED = 20260902;
1758
+ /**
1759
+ * Resolve the corpus seed from an optional environment override (the value
1760
+ * of `LEGATION_CORPUS_SEED`). Callers pass the variable in so this module
1761
+ * stays free of `process` (it ships in the engine core, which also runs in
1762
+ * browsers).
1763
+ */
1764
+ declare const resolveSeed: (override: string | undefined) => number;
1765
+ /** One column of the fixture schema: a row field with a declared literal kind (`Predicate.LiteralKind`). */
1766
+ interface Column {
1767
+ readonly name: string;
1768
+ readonly kind: LiteralKind;
1769
+ }
1770
+ /**
1771
+ * The harness's fixture table `corpus`: every non-id column is nullable.
1772
+ * Postgres declares `COLLATE "C"` on `id` and `s` and stores `t` as
1773
+ * `timestamptz`; SQLite uses `BINARY` text throughout.
1774
+ */
1775
+ declare const corpusColumns: ReadonlyArray<Column>;
1776
+ /** Generator knobs shared by rows and predicates. */
1777
+ interface CorpusOptions {
1778
+ /**
1779
+ * Generate NULL and absent (JS-only: the key is missing) field values.
1780
+ * Default `true`. `false` keeps every field present and non-null (the
1781
+ * regression floor every evaluator agreed on before ADR-0028's rules
1782
+ * (a)/(b) were implemented) and also drops the `isnull` operator, which is
1783
+ * a constant predicate over such rows: a corpus only covers `isnull` with
1784
+ * `nulls` on.
1785
+ */
1786
+ readonly nulls?: boolean;
1787
+ /**
1788
+ * Generate astral (non-BMP) text and text mixing BMP and astral
1789
+ * characters. Default `true`. This is where UTF-16 code-unit order (JS)
1790
+ * diverges from code-point order (Postgres `COLLATE "C"`, SQLite
1791
+ * `BINARY`) — ADR-0028 rule (c).
1792
+ */
1793
+ readonly astral?: boolean;
1794
+ /**
1795
+ * Apply the ordered operators (`gt gte lt lte`) to the boolean column too.
1796
+ * Default `true`. Postgres orders booleans `false < true` (ADR-0028
1797
+ * section 3), and so does `Predicate.compareLiterals`; `false` is the
1798
+ * regression floor from before that rule was implemented.
1799
+ */
1800
+ readonly booleanOrder?: boolean;
1801
+ }
1802
+ /**
1803
+ * A generated row in its JS object form: an absent field is a missing key,
1804
+ * distinct from an explicit `null`. The SQL legs insert both as SQL NULL
1805
+ * (ADR-0028 rule (b)).
1806
+ */
1807
+ interface CorpusRow {
1808
+ readonly id: string;
1809
+ readonly value: Readonly<Record<string, Literal>>;
1810
+ }
1811
+ /**
1812
+ * The fewest rows {@link generateRows} accepts for `columns` under
1813
+ * `options`: the pinned NULL/absent rows plus the sweep that puts every pool
1814
+ * value into every column. Fewer would silently drop guaranteed edge values.
1815
+ */
1816
+ declare const minimumRows: (columns?: ReadonlyArray<Column>, options?: CorpusOptions) => number;
1817
+ /**
1818
+ * Generate `count` rows over `columns`. Coverage of the edge values is
1819
+ * guaranteed, not left to chance: with `nulls` on, row 0 is all-NULL and row
1820
+ * 1 all-absent; then one row per pool position sweeps every pool value into
1821
+ * every column (int4 bounds, `-0`, `2^53±1`, the empty string, U+FFFF, the
1822
+ * astral characters, …); the remaining rows are random with NULL/absent
1823
+ * rolls. `count` below {@link minimumRows} is an error rather than a
1824
+ * truncated sweep. Ids are zero-padded ASCII (`r000`, `r001`, …) so their
1825
+ * order is the same in JS, SQLite `BINARY`, and Postgres `COLLATE "C"`,
1826
+ * keeping the id tie-break comparable across legs.
1827
+ */
1828
+ declare const generateRows: (count: number, columns?: ReadonlyArray<Column>, options?: CorpusOptions) => Effect.Effect<ReadonlyArray<CorpusRow>>;
1829
+ /** Every operator name the generator can emit; coverage is counted by these. */
1830
+ type OperatorName = "eq" | "ne" | "gt" | "gte" | "lt" | "lte" | "in" | "nin" | "isnull" | "and" | "or" | "not" | "true";
1831
+ /** Per-operator emission counts for one generated corpus. */
1832
+ type Coverage = Record<OperatorName, number>;
1833
+ /** A fresh all-zero coverage table. */
1834
+ declare const emptyCoverage: () => Coverage;
1835
+ interface LeafOperator {
1836
+ readonly name: OperatorName;
1837
+ /** An ordered comparison: applied to a boolean column only with `booleanOrder`. */
1838
+ readonly ordered: boolean;
1839
+ /** Build the leaf from a literal draw and a list draw; each runs only if used. */
1840
+ readonly build: (field: string, literal: Effect.Effect<Literal>, list: Effect.Effect<ReadonlyArray<Literal>>) => Effect.Effect<Predicate>;
1841
+ }
1842
+ /** The leaf operator table: one constructor per core operator. */
1843
+ declare const leafOperators: ReadonlyArray<LeafOperator>;
1844
+ /**
1845
+ * Generate one depth-bounded predicate tree: leaves from the operator table
1846
+ * on a random column with a literal of that column's kind; `And`/`Or` with
1847
+ * 2–3 children and `Not` with one, down to `depth`. Emitted operators are
1848
+ * tallied into `coverage`.
1849
+ */
1850
+ declare const generatePredicate: (columns: ReadonlyArray<Column>, options: CorpusOptions | undefined, coverage: Coverage, depth?: number) => Effect.Effect<Predicate>;
1851
+ /** Generate 1–2 `orderBy` terms over distinct columns with random directions. */
1852
+ declare const generateOrderBy: (columns?: ReadonlyArray<Column>) => Effect.Effect<ReadonlyArray<OrderBy>>;
1853
+ /** Options for {@link generateCorpus}. */
1854
+ interface GenerateOptions extends CorpusOptions {
1855
+ readonly seed: number;
1856
+ readonly columns?: ReadonlyArray<Column>;
1857
+ /** Rows in the fixture set (default 64: the pinned sweep plus random rows). */
1858
+ readonly rows?: number;
1859
+ /** Predicates to generate (default 200). */
1860
+ readonly predicates?: number;
1861
+ /** `orderBy` term lists to generate (default 60). */
1862
+ readonly orderBys?: number;
1863
+ /** Predicate tree depth bound (default 2). */
1864
+ readonly depth?: number;
1865
+ }
1866
+ /** A generated corpus: one row set, many predicates and order terms, and coverage. */
1867
+ interface Corpus {
1868
+ readonly seed: number;
1869
+ readonly columns: ReadonlyArray<Column>;
1870
+ readonly rows: ReadonlyArray<CorpusRow>;
1871
+ readonly predicates: ReadonlyArray<Predicate>;
1872
+ readonly orderBys: ReadonlyArray<ReadonlyArray<OrderBy>>;
1873
+ readonly coverage: Coverage;
1874
+ }
1875
+ /**
1876
+ * Generate a whole corpus deterministically from a seed. The generators run
1877
+ * under `Random.withSeed(options.seed)`; the seeding is suspended so every
1878
+ * run of the returned effect starts the sequence afresh and yields the same
1879
+ * corpus.
1880
+ */
1881
+ declare const generateCorpus: (options: GenerateOptions) => Effect.Effect<Corpus>;
1882
+ /** A predicate over a row set — the unit the harness compares and shrinks. */
1883
+ interface Case {
1884
+ readonly predicate: Predicate;
1885
+ readonly rows: ReadonlyArray<CorpusRow>;
1886
+ }
1887
+ /**
1888
+ * One-step shrinks of a predicate, larger jumps first: replace a junction by
1889
+ * one child, drop one child, unwrap `Not`, drop one `In` value, then the
1890
+ * same recursively inside each child.
1891
+ */
1892
+ declare const shrinkPredicate: (predicate: Predicate) => ReadonlyArray<Predicate>;
1893
+ /**
1894
+ * Greedily shrink a disagreeing case while `disagrees` keeps holding:
1895
+ * predicate shrinks first, then row removals, restarting from the smallest
1896
+ * accepted candidate until no candidate still disagrees. `disagrees` runs the
1897
+ * subjects, so it is an Effect (the SQL legs are asynchronous).
1898
+ */
1899
+ declare const minimise: <E, R>(failing: Case, disagrees: (candidate: Case) => Effect.Effect<boolean, E, R>) => Effect.Effect<Case, E, R>;
1900
+ /** A disagreement between subjects, ready to print. */
1901
+ interface Disagreement {
1902
+ readonly seed: number;
1903
+ readonly subject: string;
1904
+ readonly predicate?: Predicate;
1905
+ readonly orderBy?: ReadonlyArray<OrderBy>;
1906
+ /** The (minimal) rows; any `(id, value)` row prints, not only corpus rows. */
1907
+ readonly rows: ReadonlyArray<Row$3>;
1908
+ /** Per-subject result (ids), keyed by subject name. */
1909
+ readonly results: Readonly<Record<string, ReadonlyArray<string>>>;
1910
+ /**
1911
+ * How to reproduce at this seed. Defaults to the harness's environment
1912
+ * override; a caller with a fixed seed (`BackendContract`) says so instead.
1913
+ */
1914
+ readonly reproduce?: string;
1915
+ }
1916
+ /**
1917
+ * Render a disagreement: the seed, the (minimal) predicate or order terms,
1918
+ * the (minimal) rows — absent keys stay absent, `null` prints as `null`,
1919
+ * `-0` prints as `"-0"` — and every subject's ids.
1920
+ */
1921
+ declare const formatDisagreement: (disagreement: Disagreement) => string;
1922
+ declare namespace HttpCache_d_exports {
1923
+ export { CachedResponseMeta, EvictionTagOptions, HttpCachePolicy, ResponseCacheStore, Touched, evictionTags, fineTagFor, tagFor };
1924
+ }
1925
+ /** Cache metadata kept alongside a stored Response. */
1926
+ interface CachedResponseMeta {
1927
+ readonly url: string;
1928
+ readonly storedAt: number;
1929
+ readonly staleAt: number;
1930
+ readonly expiresAt: number | undefined;
1931
+ readonly tags: ReadonlyArray<string>;
1932
+ }
1933
+ /** A pluggable store of Request/Response pairs (Cache API in browsers). */
1934
+ interface ResponseCacheStore {
1935
+ readonly match: (request: Request) => Effect.Effect<{
1936
+ readonly response: Response;
1937
+ readonly meta: CachedResponseMeta;
1938
+ } | undefined, StoreError>;
1939
+ readonly put: (request: Request, response: Response, meta: CachedResponseMeta) => Effect.Effect<void, StoreError>;
1940
+ readonly delete: (request: Request) => Effect.Effect<void, StoreError>;
1941
+ /** Remove every entry carrying any of the tags. */
1942
+ readonly invalidateTags: (tags: ReadonlyArray<string>) => Effect.Effect<void, StoreError>;
1943
+ }
1944
+ /**
1945
+ * The {@link ResponseCacheStore} service tag, for apps that provide a store by layer;
1946
+ * the fetch handlers take it as an option.
1947
+ */
1948
+ declare const ResponseCacheStore: Context.Service<ResponseCacheStore, ResponseCacheStore>;
1949
+ /** Per-route caching policy. */
1950
+ interface HttpCachePolicy {
1951
+ readonly staleTime: Duration.Input;
1952
+ readonly timeToLive?: Duration.Input | undefined;
1953
+ readonly tags?: ReadonlyArray<string> | undefined;
1954
+ }
1955
+ /** The coarse tag of a collection: `collection:{name}`. Every response enumerating (part of) it carries it. */
1956
+ declare const tagFor: (collection: string) => string;
1957
+ /** The fine tag of one row: `collection:{name}:{id}`. A response carries one per row it contains. */
1958
+ declare const fineTagFor: (collection: string, id: string) => string;
1959
+ /** The collections and individual rows a change touched. */
1960
+ interface Touched {
1961
+ readonly collections: ReadonlyArray<string>;
1962
+ readonly rows: ReadonlyArray<{
1963
+ readonly collection: string;
1964
+ readonly id: string;
1965
+ }>;
1966
+ }
1967
+ /** Options for {@link evictionTags}: the tag spelling, defaulting to {@link tagFor} and {@link fineTagFor}. */
1968
+ interface EvictionTagOptions {
1969
+ readonly tagFor?: (collection: string) => string;
1970
+ readonly fineTagFor?: (collection: string, id: string) => string;
1971
+ }
1972
+ /**
1973
+ * The tags to evict for a change, each once: the coarse tag of every touched
1974
+ * collection **and** the fine tag of every touched row. The coarse eviction is
1975
+ * not redundant with the fine one: a cached aggregate (a list, a feed, a
1976
+ * sitemap) can be staled by a row it does not yet contain, such as a newly
1977
+ * published one, and only the coarse tag reaches it.
1978
+ */
1979
+ declare const evictionTags: (touched: Touched, options?: EvictionTagOptions) => ReadonlyArray<string>;
1980
+ declare namespace InvalidationSink_d_exports {
1981
+ export { InvalidationSink, all, run$1 as run, touchedBy };
1982
+ }
1983
+ /**
1984
+ * A server-side cache that evicts what a change touched (#206). `invalidate`
1985
+ * never fails: an implementation logs its own failures, since a lost eviction
1986
+ * only leaves an entry until its TTL.
1987
+ */
1988
+ interface InvalidationSink {
1989
+ readonly invalidate: (touched: Touched) => Effect.Effect<void>;
1990
+ }
1991
+ /** What a hint batch touched: its collections, and every row id it names (puts and deletes). */
1992
+ declare const touchedBy: (hints: ReadonlyArray<ChangeHint>) => Touched;
1993
+ /**
1994
+ * One sink over several, run concurrently: a KV cache and the edge cache
1995
+ * evicted from the same hint. A defect in one is logged and does not stop the
1996
+ * others.
1997
+ */
1998
+ declare const all: (sinks: ReadonlyArray<InvalidationSink>) => InvalidationSink;
1999
+ /**
2000
+ * Subscribe `sink` to the bus for the scope's lifetime: each hint batch's
2001
+ * touched collections and rows are passed to it, on a fibre of its own. Run it
2002
+ * where the bus is, in the process that commits the writes. A sink that has to
2003
+ * reach a cache in another process forwards there (Cloudflare:
2004
+ * `EdgeInvalidation.forward` over a service binding).
2005
+ */
2006
+ declare const run$1: (sink: InvalidationSink) => Effect.Effect<void, never, InvalidationBus | Scope.Scope>;
2007
+ declare namespace PushPoke_d_exports {
2008
+ export { Budget, SinkOptions, isPoke, payload, sink };
2009
+ }
2010
+ /**
2011
+ * The payload of every push-poke: a fixed marker, so the worker can tell the
2012
+ * library's push from the app's own, and nothing about what changed.
2013
+ */
2014
+ declare const payload = "{\"legation\":\"poke\"}";
2015
+ /** Whether a push's text is the library's poke ({@link payload}). */
2016
+ declare const isPoke: (text: string) => boolean;
2017
+ /** How many push-pokes a target may receive per period. */
2018
+ interface Budget {
2019
+ readonly pushes: number;
2020
+ readonly per: Duration.Input;
2021
+ }
2022
+ /** Options for {@link sink}. `Target` is the app's own push subscription record. */
2023
+ interface SinkOptions<Target> {
2024
+ /**
2025
+ * The push subscriptions a change should wake: the app's endpoint registry,
2026
+ * scoped as the app scopes its users' data (a write to a user's rows wakes
2027
+ * that user's devices). Failing reads as no targets, logged.
2028
+ */
2029
+ readonly targetsFor: (touched: Touched) => Effect.Effect<ReadonlyArray<Target>, unknown>;
2030
+ /** A stable key per target, for coalescing and the budget: its endpoint URL, say. */
2031
+ readonly keyOf: (target: Target) => string;
2032
+ /**
2033
+ * Send `payload` to `target` through its push service, signed with the
2034
+ * app's VAPID keys. A failure is logged, never retried here: the next change
2035
+ * pokes again, and pull converges regardless.
2036
+ */
2037
+ readonly send: (target: Target, payload: string) => Effect.Effect<void, unknown>;
2038
+ /** **Required.** At most one push per target per window, plus one trailing push. */
2039
+ readonly coalesce: Duration.Input;
2040
+ /** **Required.** The most pushes a target receives per period; past it, none. */
2041
+ readonly budget: Budget;
2042
+ }
2043
+ /**
2044
+ * An `InvalidationSink` that pokes the devices a change should wake, over Web
2045
+ * Push, within the app's coalescing window and budget. Run it with
2046
+ * `InvalidationSink.run` where the bus is. Its trailing pushes run on fibres
2047
+ * of the scope it is built in.
2048
+ */
2049
+ declare const sink: <Target>(options: SinkOptions<Target>) => Effect.Effect<InvalidationSink, never, Scope.Scope>;
2050
+ declare namespace FanoutTopics_d_exports {
2051
+ export { Topic, coarse, collectionOf, derivableSets, fine, topicForSubset, topicsForWrite };
2052
+ }
2053
+ /** A fan-out topic: `collection` (coarse) or `collection/field=value` (fine). */
2054
+ type Topic = string;
2055
+ /** The coarse topic of a collection: every write to it publishes here. */
2056
+ declare const coarse: (collection: string) => Topic;
2057
+ /** A fine topic: a graph location (`reviews/productId=p-1` IS that edge). */
2058
+ declare const fine: (collection: string, field: string, value: string | number | boolean) => Topic;
2059
+ /** The collection a topic belongs to (both coarse and fine). */
2060
+ declare const collectionOf: (topic: Topic) => string;
2061
+ /** The derivable set of every node in a registry, keyed by collection. */
2062
+ declare const derivableSets: (nodes: NodeRegistry) => ReadonlyMap<string, ReadonlySet<string>>;
2063
+ /**
2064
+ * The finest topic a window's predicate legally allows: an equality
2065
+ * conjunct on a derivable field → that fine topic (every row in the
2066
+ * window carries the value, so every affecting write derives it);
2067
+ * anything else → the coarse topic.
2068
+ */
2069
+ declare const topicForSubset: (subset: Subset, derivable: ReadonlySet<string> | undefined) => Topic;
2070
+ /**
2071
+ * The topics one written row derives: the coarse topic plus a fine topic
2072
+ * per derivable field of its **old and new** values — an update that moves
2073
+ * a row between parents pokes both edge windows; a delete derives from the
2074
+ * old row; a value-less write degrades to coarse alone.
2075
+ */
2076
+ declare const topicsForWrite: (collection: string, derivable: ReadonlySet<string> | undefined, values: {
2077
+ readonly oldValue?: unknown;
2078
+ readonly newValue?: unknown;
2079
+ }) => ReadonlySet<Topic>;
2080
+ declare namespace FanoutBroker_d_exports {
2081
+ export { FanoutBroker, TopicPokes, makeInProcess };
2082
+ }
2083
+ /** A live subscription to a room's topic pokes. */
2084
+ interface TopicPokes {
2085
+ /** The next batch of matched topics; suspends until one arrives. */
2086
+ readonly take: Effect.Effect<ReadonlyArray<Topic>>;
2087
+ }
2088
+ /**
2089
+ * Topic pub/sub with leased registrations: rooms lease the topics their windows compile to, and
2090
+ * `publish` pokes every room whose live lease intersects a write's topics.
2091
+ */
2092
+ interface FanoutBroker {
2093
+ /**
2094
+ * Lease (or replace) a room's topic registrations. The lease expires
2095
+ * after `ttl` unless re-registered — registration is idempotent, so the
2096
+ * heartbeat IS re-registration.
2097
+ */
2098
+ readonly register: (roomId: string, topics: ReadonlySet<Topic>, ttl: Duration.Input) => Effect.Effect<void>;
2099
+ /** Route derived topics to every room whose live lease intersects them. */
2100
+ readonly publish: (topics: Iterable<Topic>) => Effect.Effect<void>;
2101
+ /** A room's poke feed. Established once the effect completes. */
2102
+ readonly pokes: (roomId: string) => Effect.Effect<TopicPokes, never, Scope.Scope>;
2103
+ }
2104
+ /**
2105
+ * The {@link FanoutBroker} service tag, for apps that provide a broker by layer;
2106
+ * `FanoutBus.make` takes it as an option.
2107
+ */
2108
+ declare const FanoutBroker: Context.Service<FanoutBroker, FanoutBroker>;
2109
+ /** The in-process broker: leases in a Ref, one dropping hub per room. */
2110
+ declare const makeInProcess: Effect.Effect<FanoutBroker, never, Scope.Scope>;
2111
+ declare namespace FanoutBus_d_exports {
2112
+ export { MakeOptions, make };
2113
+ }
2114
+ /**
2115
+ * Options for {@link make}: this room's id at the broker, the broker, and the node graph topics
2116
+ * are derived from.
2117
+ */
2118
+ interface MakeOptions {
2119
+ /** This room's identity at the broker (typically the user id). */
2120
+ readonly roomId: string;
2121
+ readonly broker: FanoutBroker;
2122
+ /** The node graph — the derivable sets both derivation sides agree on. */
2123
+ readonly nodes: NodeRegistry;
2124
+ /**
2125
+ * The room's current windows (e.g. the authorized subsets of its client
2126
+ * groups, from the sync server's introspection). Re-evaluated on every
2127
+ * heartbeat, so new subscriptions register within one beat.
2128
+ */
2129
+ readonly subsets: Effect.Effect<ReadonlyArray<Subset>>;
2130
+ /** Lease heartbeat. Default 10 seconds. */
2131
+ readonly heartbeat?: Duration.Input;
2132
+ /** Lease time-to-live. Default 3× the heartbeat. */
2133
+ readonly ttl?: Duration.Input;
2134
+ /**
2135
+ * Bound on each broker call the bus makes (a publish, a lease registration). A call that
2136
+ * exceeds it is logged and dropped, since a lost poke only delays the next pull (ADR-0009).
2137
+ * `publish` makes two such calls in turn (the broker publish, then a lease registration), each
2138
+ * with its own bound, so a hung broker can hold one `publish` for up to two bounds. `publish`
2139
+ * runs inside the sync server's uninterruptible commit-and-publish region, so these bounds are
2140
+ * what keep a hung broker from holding that region open indefinitely. With Durable Objects one
2141
+ * broker publish is many shard calls under its single bound, so a multi-topic publish can be cut
2142
+ * short and its unsent pokes dropped. That is acceptable per ADR-0009: a lost poke only delays
2143
+ * the next pull, which `syncLoop.pullInterval` bounds when it is configured. Default 5 seconds.
2144
+ */
2145
+ readonly timeout?: Duration.Input | undefined;
2146
+ }
2147
+ /** Build the bus; the lease heartbeat runs until the scope closes. */
2148
+ declare const make: (options: MakeOptions) => Effect.Effect<InvalidationBus, never, Scope.Scope>;
2149
+ declare namespace BusBrokers_d_exports {
2150
+ export { BroadcastChannelLike, RedisBrokerOptions, WebSocketLike, makeBroadcastChannelBroker, makeRedisBroker, makeWebSocketBroker };
2151
+ }
2152
+ /** Options for {@link makeRedisBroker}. */
2153
+ interface RedisBrokerOptions {
2154
+ /** Stream key all instances share. Defaults to `"legation:hints"`. */
2155
+ readonly stream?: string;
2156
+ /**
2157
+ * XREAD BLOCK timeout before re-issuing. Defaults to 5 seconds. A reader XREAD with no reply
2158
+ * within this plus a fixed 2-second margin is a failed step. `0` disables that hang detection:
2159
+ * the reader parks until an entry arrives, so a dead connection parks it silently, as before.
2160
+ */
2161
+ readonly block?: Duration.Input;
2162
+ /**
2163
+ * Where a new subscriber starts reading. `"$"` (default) delivers only
2164
+ * messages published after it connects — pub/sub semantics; `"0"` replays
2165
+ * the whole stream (harmless for a bus, but unbounded without trimming).
2166
+ */
2167
+ readonly from?: "$" | "0";
2168
+ /**
2169
+ * Bound on one XADD. A publish that exceeds it is logged and dropped: the bus publishes inside
2170
+ * the sync server's uninterruptible commit-and-publish region, so a hung Redis must not hold
2171
+ * that region open. Defaults to 5 seconds.
2172
+ */
2173
+ readonly timeout?: Duration.Input;
2174
+ }
2175
+ /**
2176
+ * A BusBroker over Redis Streams. Captures the `Redis` service; each
2177
+ * `subscribe` forks a scoped reader looping `XREAD BLOCK`.
2178
+ *
2179
+ * `subscribe` makes one attempt at the start position (`XREVRANGE`, or `0-0`
2180
+ * for an empty stream), waiting at most 500 ms, and returns either way. If the
2181
+ * attempt answers in time, a hint published once `subscribe` returns is never
2182
+ * missed. If it fails or times out, the forked reader resolves the start
2183
+ * position on its first step and retries with back-off (200 ms, doubling,
2184
+ * capped at 5 s) until Redis answers. While Redis is unreachable, a publish that
2185
+ * races the start can be missed: it lands after `subscribe` returns but before
2186
+ * the reader resolves its start position. Delivery is best-effort (ADR-0009).
2187
+ * Each reader XREAD is bounded at its `block` time plus the margin described on
2188
+ * `block`. A call with no reply by then is a failed step: it is logged, backed off
2189
+ * and retried from the last ID seen, as a failed start lookup is, so no entry is
2190
+ * skipped. A reply of null (a normal BLOCK timeout) is a success.
2191
+ *
2192
+ * The `Redis` service's `send` must meet two properties that this bound relies on.
2193
+ * It must be interruptible: the bound interrupts a read that never replies. And
2194
+ * after an aborted `send`, it must keep replies in order on its connection, so a
2195
+ * late reply to an abandoned XREAD is never read as the answer to the next command.
2196
+ *
2197
+ * Known limit: after a `DEL` and recreate of the stream, or a Redis clock
2198
+ * stepping backwards, new IDs can fall at or below the sticky cursor and be
2199
+ * skipped until the subscriber resubscribes.
2200
+ */
2201
+ declare const makeRedisBroker: (options?: RedisBrokerOptions) => Effect.Effect<BusBroker, never, Redis.Redis>;
2202
+ /** The minimal WebSocket surface this broker uses (browser/DO/ws-compatible). */
2203
+ interface WebSocketLike {
2204
+ readonly send: (data: string) => void;
2205
+ readonly addEventListener: (type: "message", listener: (event: {
2206
+ readonly data: unknown;
2207
+ }) => void) => void;
2208
+ readonly removeEventListener: (type: "message", listener: (event: {
2209
+ readonly data: unknown;
2210
+ }) => void) => void;
2211
+ }
2212
+ /**
2213
+ * A BusBroker over a `WebSocketLike` whose far end rebroadcasts every frame
2214
+ * to all connected peers — the Durable Object relay pattern. `publish` sends
2215
+ * a frame; `subscribe` collects inbound string frames.
2216
+ */
2217
+ declare const makeWebSocketBroker: (socket: WebSocketLike) => BusBroker;
2218
+ /** The minimal `BroadcastChannel` surface this broker uses. */
2219
+ interface BroadcastChannelLike {
2220
+ readonly postMessage: (data: string) => void;
2221
+ readonly addEventListener: (type: "message", listener: (event: {
2222
+ readonly data: unknown;
2223
+ }) => void) => void;
2224
+ readonly removeEventListener: (type: "message", listener: (event: {
2225
+ readonly data: unknown;
2226
+ }) => void) => void;
2227
+ }
2228
+ /**
2229
+ * A BusBroker over a `BroadcastChannel` — cross-context fan-out on one machine
2230
+ * (browser tabs, Node worker threads). A `BroadcastChannel` delivers to every
2231
+ * *other* channel of the same name but not the sender, so `publish` also offers
2232
+ * to this broker's own subscribers, honoring the contract's self-delivery (each
2233
+ * instance fans the hint out to its local sessions). Point every context's
2234
+ * broker at the same channel name.
2235
+ */
2236
+ declare const makeBroadcastChannelBroker: (channel: BroadcastChannelLike) => Effect.Effect<BusBroker, never, Scope.Scope>;
2237
+ declare namespace HintSource_d_exports {
2238
+ export { HintSource, fromStream, run };
2239
+ }
2240
+ /**
2241
+ * A producer of change hints for writes that bypass mutations; fork its `run` beside the server.
2242
+ */
2243
+ interface HintSource {
2244
+ /** Run until the scope closes; publishes to the in-context InvalidationBus. */
2245
+ readonly run: Effect.Effect<void, never, InvalidationBus | Scope.Scope>;
2246
+ }
2247
+ /**
2248
+ * Build a HintSource from a stream of hint batches (used by the LISTEN/NOTIFY
2249
+ * recipe and tests): each emitted batch is published to the in-context bus, and
2250
+ * the consuming fibre is tied to the scope so it stops when the scope closes.
2251
+ */
2252
+ declare const fromStream: (hints: Stream.Stream<ReadonlyArray<ChangeHint>>) => HintSource;
2253
+ /**
2254
+ * Fork a HintSource for the caller's scope, resolving the bus from context: the
2255
+ * source's worker runs on a scoped fibre alongside the SyncServer and is
2256
+ * interrupted when the scope closes.
2257
+ */
2258
+ declare const run: (source: HintSource) => Effect.Effect<void, never, InvalidationBus | Scope.Scope>;
2259
+ declare namespace SyncRoom_d_exports {
2260
+ export { layer };
2261
+ }
2262
+ /**
2263
+ * A self-contained room: provide a Backend and an AuthPolicy, get a
2264
+ * ServerHandler. CVRs and the bus are in-memory/in-process.
2265
+ */
2266
+ declare const layer: Layer.Layer<ServerHandler, never, Backend | AuthPolicy>;
2267
+ declare namespace Telemetry_d_exports {
2268
+ export { cacheTagBudgetExceededTotal, changeCollapsesTotal, count, fanoutLatencyMs, hintsDroppedTotal, journalDepth, mirrorRefreshesTotal, mutationsPushedTotal, patchOps, pokesSuppressedTotal, pokesTotal, pullPatchOps, pullsTotal, pushMutations, pushesTotal, rebaseDurationMs, serverPullsTotal, span, syncFailuresTotal };
2269
+ }
2270
+ /** Pulls completed by a client (a patch applied + rebase). */
2271
+ declare const pullsTotal: Metric.Counter<number>;
2272
+ /** Pushes completed by a client. */
2273
+ declare const pushesTotal: Metric.Counter<number>;
2274
+ /** Mutations delivered upstream across all pushes. */
2275
+ declare const mutationsPushedTotal: Metric.Counter<number>;
2276
+ /** Pull requests served by the sync server. */
2277
+ declare const serverPullsTotal: Metric.Counter<number>;
2278
+ /** Pokes published by server fan-out. */
2279
+ declare const pokesTotal: Metric.Counter<number>;
2280
+ /**
2281
+ * Pokes suppressed by predicate-level fan-out matching: a group that a
2282
+ * collection-only match would have poked, but whose subscriptions no hinted
2283
+ * change plausibly affects. Over-matching is safe (a redundant poke costs an
2284
+ * empty pull), so this counts avoided-but-safe work, not correctness.
2285
+ */
2286
+ declare const pokesSuppressedTotal: Metric.Counter<number>;
2287
+ /** Patch sizes (ops per pull) computed by the server. */
2288
+ declare const patchOps: Metric.Histogram<number>;
2289
+ /**
2290
+ * Sync operations (push/pull/poke stream) that failed terminally after the
2291
+ * client's retry policy was exhausted. A rising count means the transport is
2292
+ * degraded — bindings can surface a "sync degraded" state from it.
2293
+ */
2294
+ declare const syncFailuresTotal: Metric.Counter<number>;
2295
+ /**
2296
+ * Invalidation-bus hints dropped because a bounded bus (in-process PubSub or
2297
+ * memory broker) overflowed while a consumer was behind. A dropped hint only
2298
+ * delays convergence — the next pull repairs it — so this counts safely-shed
2299
+ * work under backpressure, never lost correctness (ADR-0009).
2300
+ */
2301
+ declare const hintsDroppedTotal: Metric.Counter<number>;
2302
+ /**
2303
+ * Times a stalled engine observer's bounded change hub dropped a view-change,
2304
+ * forcing the consumer to collapse the gap into a full `Rebuild`. A Rebuild
2305
+ * subsumes any dropped `Patch`es (ADR-0008), so the view stays correct; this
2306
+ * counts backpressure-induced recomputes, not lost updates.
2307
+ */
2308
+ declare const changeCollapsesTotal: Metric.Counter<number>;
2309
+ /**
2310
+ * Times a stalled mirror observer's bounded change hub dropped a subset
2311
+ * key, forcing a full re-read of the observed subset. The newest rows live in
2312
+ * the mirror's `Ref`, so the re-read is lossless; this counts refreshes forced
2313
+ * by backpressure.
2314
+ */
2315
+ declare const mirrorRefreshesTotal: Metric.Counter<number>;
2316
+ /**
2317
+ * Depth of the client's journal — the number of pending (unacknowledged)
2318
+ * mutations. Sampled as an absolute gauge on every append (a new pending
2319
+ * mutation) and every acknowledgement (a trim), so it tracks the outbox
2320
+ * backlog: a persistently high value means pushes are not draining.
2321
+ */
2322
+ declare const journalDepth: Metric.Gauge<number>;
2323
+ /**
2324
+ * Time (ms) to rebuild the optimistic overlay — the rebase (ADR-0008): replay
2325
+ * every pending mutation over confirmed rows. Sampled around the actual
2326
+ * materialisation (cache misses only), so it measures replay cost, not cache
2327
+ * hits.
2328
+ */
2329
+ declare const rebaseDurationMs: Metric.Histogram<number>;
2330
+ /**
2331
+ * Time (ms) the server fan-out loop spends processing one hint batch — from
2332
+ * taking the batch off the invalidation bus to poking every matching group.
2333
+ * A proxy for hint-published → poke-sent latency (the hints carry no publish
2334
+ * timestamp), so it measures the fan-out leg the server controls.
2335
+ */
2336
+ declare const fanoutLatencyMs: Metric.Histogram<number>;
2337
+ /** Patch size (ops) a client applies per pull — the downstream volume per rebase. */
2338
+ declare const pullPatchOps: Metric.Histogram<number>;
2339
+ /** Mutations delivered per push — the upstream batch size the journal coalesced. */
2340
+ declare const pushMutations: Metric.Histogram<number>;
2341
+ /**
2342
+ * Times a cached response's row count exceeded the HTTP cache's fine-tag
2343
+ * budget (`HttpCacheInvalidation.FINE_TAG_ROW_BUDGET` in `@thomasfosterau/legation-browser`)
2344
+ * and fell back to tagging that response with its coarse
2345
+ * `collection:{name}` tag alone, skipping the per-row `collection:{name}:{id}`
2346
+ * tags. Correctness is unaffected (the coarse tag still evicts it on any row
2347
+ * change in the collection) — this counts responses that lost fine-grained
2348
+ * eviction to the budget, so the fallback is observable rather than silent.
2349
+ */
2350
+ declare const cacheTagBudgetExceededTotal: Metric.Counter<number>;
2351
+ /** Record an integer count against a counter (no-op-safe in any context). */
2352
+ declare const count: <Input extends number>(metric: Metric.Metric<Input, unknown>, amount: Input) => Effect.Effect<void>;
2353
+ /** Names used for `Effect.withSpan` on the engine's hot paths. */
2354
+ declare const span: {
2355
+ readonly clientPull: "legation.client.pull";
2356
+ readonly clientPush: "legation.client.push";
2357
+ readonly serverPull: "legation.server.pull";
2358
+ readonly serverPush: "legation.server.push";
2359
+ readonly serverRead: "legation.server.read";
2360
+ };
2361
+ export { AtomicKeyValue_d_exports as A, Subscriptions_d_exports as C, Introspection_d_exports as D, OrderedTree_d_exports as E, RpcTransport_d_exports as M, UnknownMutator_d_exports as N, Paced_d_exports as O, Hash_d_exports as P, Derive_d_exports as S, LiveView_d_exports as T, MirrorProtocol_d_exports as _, BusBrokers_d_exports as a, LiveQuery_d_exports as b, FanoutTopics_d_exports as c, HttpCache_d_exports as d, FilterCorpus_d_exports as f, EngineHost_d_exports as g, Mirror_d_exports as h, HintSource_d_exports as i, SpecExport_d_exports as j, Undo_d_exports as k, PushPoke_d_exports as l, BackendContract_d_exports as m, SyncRoom_d_exports as n, FanoutBus_d_exports as o, RejectionMemory_d_exports as p, HintSource as r, FanoutBroker_d_exports as s, Telemetry_d_exports as t, InvalidationSink_d_exports as u, PromiseFacade_d_exports as v, Incremental_d_exports as w, FractionalIndex_d_exports as x, EngineAtoms_d_exports as y };
2362
+ //# sourceMappingURL=internal-CxXRlFK9.d.ts.map