@thomasfosterau/legation 0.0.0 → 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.
- package/CHANGELOG.md +2054 -0
- package/LICENSE +21 -0
- package/README.md +349 -1
- package/dist/MemoryBackend-BziXwfcs.d.ts +4884 -0
- package/dist/MemoryBackend-BziXwfcs.d.ts.map +1 -0
- package/dist/MemoryBackend-DxP8t4ge.js +9477 -0
- package/dist/MemoryBackend-DxP8t4ge.js.map +1 -0
- package/dist/index-CPPfp0F4.d.ts +399 -0
- package/dist/index-CPPfp0F4.d.ts.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/internal-CxXRlFK9.d.ts +2362 -0
- package/dist/internal-CxXRlFK9.d.ts.map +1 -0
- package/dist/internal-JyoQMlfK.js +3758 -0
- package/dist/internal-JyoQMlfK.js.map +1 -0
- package/dist/internal.d.ts +3 -0
- package/dist/internal.js +3 -0
- package/dist/rolldown-runtime-D8jQpDG7.js +12 -0
- package/dist/sql/index.d.ts +294 -0
- package/dist/sql/index.d.ts.map +1 -0
- package/dist/sql/index.js +969 -0
- package/dist/sql/index.js.map +1 -0
- package/dist/src-FWXpu_vP.js +1082 -0
- package/dist/src-FWXpu_vP.js.map +1 -0
- package/package.json +61 -3
|
@@ -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
|