@doync/client 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/LICENSE +21 -0
- package/README.md +233 -0
- package/dist/adapter.cjs +1 -0
- package/dist/adapter.d.cts +86 -0
- package/dist/adapter.d.cts.map +1 -0
- package/dist/adapter.d.ts +86 -0
- package/dist/adapter.d.ts.map +1 -0
- package/dist/adapter.js +2 -0
- package/dist/adapter.js.map +1 -0
- package/dist/client-C6jAdhbe.cjs +15 -0
- package/dist/client-CNyLMCw0.d.ts +812 -0
- package/dist/client-CNyLMCw0.d.ts.map +1 -0
- package/dist/client-ClV8ce6X.js +16 -0
- package/dist/client-ClV8ce6X.js.map +1 -0
- package/dist/client-DHXO0dbf.d.cts +812 -0
- package/dist/client-DHXO0dbf.d.cts.map +1 -0
- package/dist/index.cjs +0 -0
- package/dist/index.d.cts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +0 -0
- package/dist/internal.cjs +1 -0
- package/dist/internal.d.cts +69 -0
- package/dist/internal.d.cts.map +1 -0
- package/dist/internal.d.ts +69 -0
- package/dist/internal.d.ts.map +1 -0
- package/dist/internal.js +2 -0
- package/dist/internal.js.map +1 -0
- package/package.json +79 -0
- package/src/adapter.ts +25 -0
- package/src/client-mutation-registry.ts +31 -0
- package/src/client.ts +100 -0
- package/src/engine.ts +3322 -0
- package/src/identity.ts +41 -0
- package/src/index.ts +36 -0
- package/src/internal.ts +37 -0
- package/src/migrate.ts +156 -0
- package/src/mutations.ts +96 -0
- package/src/port.ts +74 -0
- package/src/raw-read.ts +135 -0
- package/src/replica/db/0000_replica_engine_v0.sql +20 -0
- package/src/replica/db/0001_release_stamps.sql +6 -0
- package/src/replica/db/meta/0000_snapshot.json +129 -0
- package/src/replica/db/meta/0001_snapshot.json +167 -0
- package/src/replica/db/meta/_journal.json +20 -0
- package/src/replica/db/schema.ts +67 -0
- package/src/replica/index.ts +185 -0
- package/src/replica/meta.ts +26 -0
- package/src/replica/stamps.ts +102 -0
- package/src/replica/track.ts +190 -0
- package/src/socket-reconnect.ts +242 -0
- package/src/socket.ts +44 -0
- package/src/sql-raw.d.ts +4 -0
|
@@ -0,0 +1,812 @@
|
|
|
1
|
+
import { BoundQuery, DoyncSchema, MutationDefinition, MutationTree, SqlValue } from "@doync/core";
|
|
2
|
+
import { ClientMessage, ServerMessage } from "@doync/core/internal";
|
|
3
|
+
|
|
4
|
+
//#region ../../node_modules/.pnpm/@standard-schema+spec@1.1.0/node_modules/@standard-schema/spec/dist/index.d.ts
|
|
5
|
+
/** The Standard Typed interface. This is a base type extended by other specs. */
|
|
6
|
+
interface StandardTypedV1<Input = unknown, Output = Input> {
|
|
7
|
+
/** The Standard properties. */
|
|
8
|
+
readonly "~standard": StandardTypedV1.Props<Input, Output>;
|
|
9
|
+
}
|
|
10
|
+
declare namespace StandardTypedV1 {
|
|
11
|
+
/** The Standard Typed properties interface. */
|
|
12
|
+
interface Props<Input = unknown, Output = Input> {
|
|
13
|
+
/** The version number of the standard. */
|
|
14
|
+
readonly version: 1;
|
|
15
|
+
/** The vendor name of the schema library. */
|
|
16
|
+
readonly vendor: string;
|
|
17
|
+
/** Inferred types associated with the schema. */
|
|
18
|
+
readonly types?: Types<Input, Output> | undefined;
|
|
19
|
+
}
|
|
20
|
+
/** The Standard Typed types interface. */
|
|
21
|
+
interface Types<Input = unknown, Output = Input> {
|
|
22
|
+
/** The input type of the schema. */
|
|
23
|
+
readonly input: Input;
|
|
24
|
+
/** The output type of the schema. */
|
|
25
|
+
readonly output: Output;
|
|
26
|
+
}
|
|
27
|
+
/** Infers the input type of a Standard Typed. */
|
|
28
|
+
type InferInput<Schema extends StandardTypedV1> = NonNullable<Schema["~standard"]["types"]>["input"];
|
|
29
|
+
/** Infers the output type of a Standard Typed. */
|
|
30
|
+
type InferOutput<Schema extends StandardTypedV1> = NonNullable<Schema["~standard"]["types"]>["output"];
|
|
31
|
+
}
|
|
32
|
+
/** The Standard Schema interface. */
|
|
33
|
+
interface StandardSchemaV1<Input = unknown, Output = Input> {
|
|
34
|
+
/** The Standard Schema properties. */
|
|
35
|
+
readonly "~standard": StandardSchemaV1.Props<Input, Output>;
|
|
36
|
+
}
|
|
37
|
+
declare namespace StandardSchemaV1 {
|
|
38
|
+
/** The Standard Schema properties interface. */
|
|
39
|
+
interface Props<Input = unknown, Output = Input> extends StandardTypedV1.Props<Input, Output> {
|
|
40
|
+
/** Validates unknown input values. */
|
|
41
|
+
readonly validate: (value: unknown, options?: StandardSchemaV1.Options | undefined) => Result<Output> | Promise<Result<Output>>;
|
|
42
|
+
}
|
|
43
|
+
/** The result interface of the validate function. */
|
|
44
|
+
type Result<Output> = SuccessResult<Output> | FailureResult;
|
|
45
|
+
/** The result interface if validation succeeds. */
|
|
46
|
+
interface SuccessResult<Output> {
|
|
47
|
+
/** The typed output value. */
|
|
48
|
+
readonly value: Output;
|
|
49
|
+
/** A falsy value for `issues` indicates success. */
|
|
50
|
+
readonly issues?: undefined;
|
|
51
|
+
}
|
|
52
|
+
interface Options {
|
|
53
|
+
/** Explicit support for additional vendor-specific parameters, if needed. */
|
|
54
|
+
readonly libraryOptions?: Record<string, unknown> | undefined;
|
|
55
|
+
}
|
|
56
|
+
/** The result interface if validation fails. */
|
|
57
|
+
interface FailureResult {
|
|
58
|
+
/** The issues of failed validation. */
|
|
59
|
+
readonly issues: ReadonlyArray<Issue>;
|
|
60
|
+
}
|
|
61
|
+
/** The issue interface of the failure output. */
|
|
62
|
+
interface Issue {
|
|
63
|
+
/** The error message of the issue. */
|
|
64
|
+
readonly message: string;
|
|
65
|
+
/** The path of the issue, if any. */
|
|
66
|
+
readonly path?: ReadonlyArray<PropertyKey | PathSegment> | undefined;
|
|
67
|
+
}
|
|
68
|
+
/** The path segment interface of the issue. */
|
|
69
|
+
interface PathSegment {
|
|
70
|
+
/** The key representing a path segment. */
|
|
71
|
+
readonly key: PropertyKey;
|
|
72
|
+
}
|
|
73
|
+
/** The Standard types interface. */
|
|
74
|
+
interface Types<Input = unknown, Output = Input> extends StandardTypedV1.Types<Input, Output> {}
|
|
75
|
+
/** Infers the input type of a Standard. */
|
|
76
|
+
type InferInput<Schema extends StandardTypedV1> = StandardTypedV1.InferInput<Schema>;
|
|
77
|
+
/** Infers the output type of a Standard. */
|
|
78
|
+
type InferOutput<Schema extends StandardTypedV1> = StandardTypedV1.InferOutput<Schema>;
|
|
79
|
+
}
|
|
80
|
+
/** The Standard JSON Schema interface. */
|
|
81
|
+
//#endregion
|
|
82
|
+
//#region src/port.d.ts
|
|
83
|
+
/** One local-replica row: column name → {@link SqlValue}. */
|
|
84
|
+
interface LocalRow {
|
|
85
|
+
[column: string]: SqlValue;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Synchronous local SQLite port the client engine drives. Platform adapters
|
|
89
|
+
* (wa-sqlite, node:sqlite, op-sqlite) implement this; app code does not.
|
|
90
|
+
* Transactions and savepoints are ordinary SQL via {@link LocalDb.exec}.
|
|
91
|
+
*/
|
|
92
|
+
interface LocalDb {
|
|
93
|
+
/**
|
|
94
|
+
* Run one parameterized statement; return its rows (empty for writes/DDL/
|
|
95
|
+
* transaction control).
|
|
96
|
+
*/
|
|
97
|
+
exec<T extends LocalRow = LocalRow>(sql: string, ...params: SqlValue[]): T[];
|
|
98
|
+
/**
|
|
99
|
+
* Run a multi-statement DDL script (no parameters). Used to replay bundled
|
|
100
|
+
* migrations when creating a fresh replica. Trusted input only.
|
|
101
|
+
*/
|
|
102
|
+
execBatch(script: string): void;
|
|
103
|
+
/**
|
|
104
|
+
* Consumer tables written since the last call, then clear the set. Used so
|
|
105
|
+
* only affected subscriptions re-project after a mutation or rebase.
|
|
106
|
+
*/
|
|
107
|
+
drainWrittenTables(): Set<string>;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Extract the target table of one WRITE statement from its SQL text — the
|
|
111
|
+
* adapter-side stand-in where no synchronous native change feed exists
|
|
112
|
+
* (ADR-0019): node:sqlite has no update hook, and op-sqlite's hook delivers
|
|
113
|
+
* callbacks via `invokeAsync` — a later event-loop turn, unusable for the
|
|
114
|
+
* engine's drain-after-apply contract (closeio/doync#202 device pass).
|
|
115
|
+
*
|
|
116
|
+
* A real parse over `@doync/sqlite-parser`, not a keyword recognizer: the AST
|
|
117
|
+
* attaches a `WITH …` CTE prefix to the DML node itself, so CTE-topped writes
|
|
118
|
+
* (`WITH src AS (…) INSERT INTO t …`) extract their target with no string
|
|
119
|
+
* games. Returns the unquoted table name for INSERT / REPLACE / UPDATE /
|
|
120
|
+
* DELETE, or `null` for a non-write (SELECT, SAVEPOINT, PRAGMA, DDL) or
|
|
121
|
+
* unparseable input.
|
|
122
|
+
*
|
|
123
|
+
* TRIGGER cascades remain invisible to statement text — and stay out of scope
|
|
124
|
+
* by design: client replicas carry no triggers (the bundled-track replay strips
|
|
125
|
+
* CREATE TRIGGER; ADR-0009 — clients apply state, never enforce).
|
|
126
|
+
*/
|
|
127
|
+
declare function extractWriteTable(sql: string): string | null;
|
|
128
|
+
//#endregion
|
|
129
|
+
//#region src/mutations.d.ts
|
|
130
|
+
/**
|
|
131
|
+
* Write surface a client mutation body runs against: one parameterized
|
|
132
|
+
* statement at a time inside the mutation's savepoint, returning its rows. Same
|
|
133
|
+
* shape the Origin uses, so a shared body runs identically on device and
|
|
134
|
+
* server.
|
|
135
|
+
*/
|
|
136
|
+
interface ClientMutationTx {
|
|
137
|
+
exec<T extends LocalRow = LocalRow>(query: string, ...params: SqlValue[]): T[];
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Client mutation body `(args, ctx, sql)`. Must be deterministic and DB-only
|
|
141
|
+
* (may still be async for local work). Generate ids on the client and pass them
|
|
142
|
+
* in `args` — the body is replayed on rebase. External I/O belongs in a server
|
|
143
|
+
* override, not the client bundle.
|
|
144
|
+
*/
|
|
145
|
+
type ClientMutationHandler = (args: unknown, ctx: Record<string, unknown>, sql: ClientMutationTx) => void | Promise<void>;
|
|
146
|
+
/**
|
|
147
|
+
* A registered client mutation: body plus optional standard-schema args
|
|
148
|
+
* validator applied at `mutate()` time. A bare handler is also accepted.
|
|
149
|
+
*/
|
|
150
|
+
interface ClientMutation {
|
|
151
|
+
readonly args?: StandardSchemaV1;
|
|
152
|
+
readonly handler: ClientMutationHandler;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Name-keyed client mutation registry. Keys match the server's dotted names so
|
|
156
|
+
* a push reaches the matching authoritative body.
|
|
157
|
+
*/
|
|
158
|
+
type ClientMutationRegistry = Readonly<Record<string, ClientMutation | ClientMutationHandler>>;
|
|
159
|
+
/**
|
|
160
|
+
* Normalize a registry entry (bare handler or `{args, handler}`) to the object
|
|
161
|
+
* form.
|
|
162
|
+
*/
|
|
163
|
+
declare function asClientMutation(entry: ClientMutation | ClientMutationHandler): ClientMutation;
|
|
164
|
+
/**
|
|
165
|
+
* Validate `args` through a standard-schema synchronously (mirrors
|
|
166
|
+
* `@doync/core` query-args). Async validators refuse loudly — the body is
|
|
167
|
+
* deterministic on both ends. Returns validated value or throws.
|
|
168
|
+
*/
|
|
169
|
+
declare function validateMutationArgs(schema: StandardSchemaV1, args: unknown): unknown;
|
|
170
|
+
//#endregion
|
|
171
|
+
//#region src/socket.d.ts
|
|
172
|
+
/**
|
|
173
|
+
* Minimal transport the client engine uses to talk to its Mirror. Platform
|
|
174
|
+
* adapters supply WebSocket / reconnect policy behind this shape.
|
|
175
|
+
*/
|
|
176
|
+
interface SyncSocket {
|
|
177
|
+
/** Send one client→server frame (`connect` / `subscribe` / `push`). */
|
|
178
|
+
send(message: ClientMessage): void;
|
|
179
|
+
/**
|
|
180
|
+
* Register frame and lifecycle handlers (once at construction). `open` fires
|
|
181
|
+
* on every (re)connect so the engine can re-handshake; `close` pauses
|
|
182
|
+
* outbound traffic until the next `open`.
|
|
183
|
+
*/
|
|
184
|
+
setHandlers(handlers: SyncSocketHandlers): void;
|
|
185
|
+
/**
|
|
186
|
+
* Drop and re-establish under the adapter's backoff. Used after a
|
|
187
|
+
* client-ahead schema skew; the next successful open triggers re-handshake.
|
|
188
|
+
*/
|
|
189
|
+
reconnect(): void;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Transient transport states a reconnecting adapter may report: `connecting`
|
|
193
|
+
* (dial/backoff) and `error` (abnormal drop). Connected / disconnected ride
|
|
194
|
+
* `open`/`close`; auth failure is inferred from frames.
|
|
195
|
+
*/
|
|
196
|
+
type SeamStatus = "connecting" | "error";
|
|
197
|
+
/** Handlers the engine registers on a {@link SyncSocket}. */
|
|
198
|
+
interface SyncSocketHandlers {
|
|
199
|
+
/** A server→client frame arrived. */
|
|
200
|
+
message(message: ServerMessage): void;
|
|
201
|
+
/** Socket (re)connected — engine handshakes and re-sends pending. */
|
|
202
|
+
open(): void;
|
|
203
|
+
/** Socket disconnected — engine queues writes until the next open. */
|
|
204
|
+
close(): void;
|
|
205
|
+
/**
|
|
206
|
+
* Optional transient status (`connecting` / `error`). Adapters without
|
|
207
|
+
* reconnect simply never call this.
|
|
208
|
+
*/
|
|
209
|
+
status?(status: SeamStatus): void;
|
|
210
|
+
}
|
|
211
|
+
//#endregion
|
|
212
|
+
//#region src/replica/meta.d.ts
|
|
213
|
+
/**
|
|
214
|
+
* Read one `__doync_meta` value, or `null` if unset. Columns are `k`/`v`
|
|
215
|
+
* (ADR-0024).
|
|
216
|
+
*/
|
|
217
|
+
declare function readMeta(db: LocalDb, key: string): string | null;
|
|
218
|
+
/** Upsert one `__doync_meta` value. Columns are `k`/`v` (ADR-0024). */
|
|
219
|
+
declare function writeMeta(db: LocalDb, key: string, value: string): void;
|
|
220
|
+
//#endregion
|
|
221
|
+
//#region src/replica/stamps.d.ts
|
|
222
|
+
/**
|
|
223
|
+
* Durable release stamp for one parked Query instance (ADR-0014 client half /
|
|
224
|
+
* closeio/doync#224). Shared infrastructure for `heldAt` (#225) and membership
|
|
225
|
+
* GC (#226) — package-internal, no public consumer surface.
|
|
226
|
+
*
|
|
227
|
+
* Lifecycle: written at last-release; deleted on re-desire and on an ADR-0028
|
|
228
|
+
* warmth-break; wiped with memberships on resync/forget (a stamp must never
|
|
229
|
+
* outlive the memberships it vouches for).
|
|
230
|
+
*/
|
|
231
|
+
type ReleaseStamp = {
|
|
232
|
+
readonly instance: string; /** Client scalar cookie at last-release — the future `heldAt` claim. */
|
|
233
|
+
readonly cookie: number; /** Release moment in connected-clock units (ms). */
|
|
234
|
+
readonly releasedAt: number;
|
|
235
|
+
/**
|
|
236
|
+
* Resolved grace in ms — the Subscription's declared ttl, or the internal
|
|
237
|
+
* default (mirrors the server's DEFAULT_QUERY_TTL_MS; no public knob).
|
|
238
|
+
*/
|
|
239
|
+
readonly ttlMs: number;
|
|
240
|
+
};
|
|
241
|
+
//#endregion
|
|
242
|
+
//#region src/replica/index.d.ts
|
|
243
|
+
/**
|
|
244
|
+
* Apply the Replica engine track via `__doync_engine` ledger (ADR-0024 / #159).
|
|
245
|
+
* LocalDb twin of server `runEngineTrack`. Returns whether rollback tripwire
|
|
246
|
+
* fired (caller finishes consumer wipe-and-resync). Named `createEngineTables`
|
|
247
|
+
* for stable call sites.
|
|
248
|
+
*/
|
|
249
|
+
declare function createEngineTables(db: LocalDb): {
|
|
250
|
+
rolledBack: boolean;
|
|
251
|
+
};
|
|
252
|
+
/**
|
|
253
|
+
* Boot replica shape via DDL-only bundled track (ADR-0020). Fresh client is a
|
|
254
|
+
* partial Mirror of the same migrations (triggers skipped, FKs off ADR-0009;
|
|
255
|
+
* backfill skipped ADR-0006).
|
|
256
|
+
*
|
|
257
|
+
* - Fresh (no schema_version): full bundle track.
|
|
258
|
+
* - Persisted: leave recorded shape; new migrations arrive mid-session as
|
|
259
|
+
* `schema` directives ({@link applyBundledMigrations}), not at boot.
|
|
260
|
+
*/
|
|
261
|
+
declare function replayMigrations(db: LocalDb, schema: DoyncSchema): void;
|
|
262
|
+
/**
|
|
263
|
+
* Apply the bundled track's non-trigger DDL for the migrations in
|
|
264
|
+
* `(fromVersion, toVersion]` to the replica and record `toVersion` as the
|
|
265
|
+
* applied `schema_version`. The single primitive behind both fresh boot replay
|
|
266
|
+
* ({@link replayMigrations} with `fromVersion = 0`) and a mid-session catch-up
|
|
267
|
+
* (`fromVersion =` the currently applied version). The DDL text is the client's
|
|
268
|
+
* OWN bundle (never wire-carried — ADR-0020); a `CREATE TABLE` from a fresh
|
|
269
|
+
* base and an `ALTER TABLE … ADD COLUMN` catch-up both keep any rows already
|
|
270
|
+
* present (SQLite ADD COLUMN preserves rows). Throws if a statement fails — the
|
|
271
|
+
* caller turns a failed local migration into wipe-and-resync (ADR-0020).
|
|
272
|
+
*/
|
|
273
|
+
declare function applyBundledMigrations(db: LocalDb, schema: DoyncSchema, fromVersion: number, toVersion: number): void;
|
|
274
|
+
/**
|
|
275
|
+
* Drop every consumer (synced) table (ADR-0020's wipe fallback): the first step
|
|
276
|
+
* of wipe-replica-and-resync, run with foreign keys already off so no cascade
|
|
277
|
+
* fires and drop order is irrelevant (ADR-0009). `__doync_`-prefixed engine
|
|
278
|
+
* tables — the pending queue, clientId, and `__doync_engine` ledger that
|
|
279
|
+
* SURVIVE the wipe — are left untouched.
|
|
280
|
+
*/
|
|
281
|
+
declare function dropConsumerTables(db: LocalDb, schema: DoyncSchema): void;
|
|
282
|
+
//#endregion
|
|
283
|
+
//#region src/engine.d.ts
|
|
284
|
+
/**
|
|
285
|
+
* What `mutate()` returns: `client` settles when the optimistic body applies
|
|
286
|
+
* locally (rejects if the body throws or args fail validation); `server`
|
|
287
|
+
* settles when the Origin confirms or rejects the mutation. Render off
|
|
288
|
+
* `client`; await `server` when you need authoritative confirmation.
|
|
289
|
+
*/
|
|
290
|
+
interface MutationResult {
|
|
291
|
+
readonly client: Promise<void>;
|
|
292
|
+
readonly server: Promise<void>;
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* What happens to this identity's local store on logout: `keep` leaves it so
|
|
296
|
+
* unsynced writes await the next login (default); `forget` erases it
|
|
297
|
+
* (shared-computer / privacy). Set at construction or via
|
|
298
|
+
* {@link DoyncClient.setLogoutBehavior}.
|
|
299
|
+
*/
|
|
300
|
+
type LogoutBehavior = "keep" | "forget";
|
|
301
|
+
/**
|
|
302
|
+
* The `__doync_meta` key the durable {@link LogoutBehavior} lives under
|
|
303
|
+
* (closeio/doync#135) — shared by the direct engine's
|
|
304
|
+
* {@link ClientEngine.setLogoutBehavior} / `createClient` write and the web DB
|
|
305
|
+
* worker's per-identity write + boot read, so the ends can never drift.
|
|
306
|
+
*/
|
|
307
|
+
declare const __LOGOUT_BEHAVIOR_META_KEY = "logout_behavior";
|
|
308
|
+
/**
|
|
309
|
+
* The `__doync_meta` key the Client's durable connected-time counter lives
|
|
310
|
+
* under (ADR-0014 client-half addendum / closeio/doync#223). Blob-typed
|
|
311
|
+
* store-as-bound: no engine-track migration. Package-internal — release stamps
|
|
312
|
+
* (#224) and membership GC read it through the engine, not this key.
|
|
313
|
+
*/
|
|
314
|
+
declare const __CONNECTED_CLOCK_META_KEY = "connected_clock";
|
|
315
|
+
/**
|
|
316
|
+
* Schema / recovery states the UI can show as a banner:
|
|
317
|
+
*
|
|
318
|
+
* - `reload` — client bundle cannot understand the server's shape; reload the app
|
|
319
|
+
* after deploying a matching client. Terminal until reload.
|
|
320
|
+
* - `server-behind` — client is ahead of a mid-deploy Mirror; the client backs
|
|
321
|
+
* off and re-handshakes automatically.
|
|
322
|
+
* - `resync` — local replica/memberships wiped and rebuilding; pending writes and
|
|
323
|
+
* clientId survive. Transient; clears when sync resumes.
|
|
324
|
+
* - `forget` — local store erased (including identity); boots as a fresh client.
|
|
325
|
+
* Transient; clears when sync resumes.
|
|
326
|
+
*/
|
|
327
|
+
type SchemaEventKind = "reload" | "server-behind" | "resync" | "forget";
|
|
328
|
+
/**
|
|
329
|
+
* One schema-status transition: `kind` plus a human-readable `message` for
|
|
330
|
+
* diagnostics (not a control signal). `null` on the client means nominal.
|
|
331
|
+
*/
|
|
332
|
+
interface SchemaEvent {
|
|
333
|
+
readonly kind: SchemaEventKind;
|
|
334
|
+
/** Human-readable reason (diagnostics; never a control signal). */
|
|
335
|
+
readonly message: string;
|
|
336
|
+
}
|
|
337
|
+
/** Options for one `mutate()` call. */
|
|
338
|
+
interface MutationOptions {
|
|
339
|
+
/**
|
|
340
|
+
* Idempotency key: a second `mutate()` with a key already in flight (or
|
|
341
|
+
* already settled) returns the same `{client, server}` pair and enqueues
|
|
342
|
+
* nothing, so a retried submit never double-writes.
|
|
343
|
+
*/
|
|
344
|
+
readonly key?: string;
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* Whether a view's rows have been server-confirmed:
|
|
348
|
+
*
|
|
349
|
+
* - `unknown` — local answer only (fresh subscribe, skip, or reconnect). Rows may
|
|
350
|
+
* already be present; this speaks to confirmation, not emptiness.
|
|
351
|
+
* - `complete` — server has confirmed this subscription's rows up to the current
|
|
352
|
+
* sync point. Empty results can still be `complete`.
|
|
353
|
+
* - `error` — the Mirror could not honor the subscribe; detail on
|
|
354
|
+
* {@link ViewStatus.error}.
|
|
355
|
+
*/
|
|
356
|
+
type QueryStatus = "unknown" | "complete" | "error";
|
|
357
|
+
/**
|
|
358
|
+
* A view's status snapshot. The object reference is stable until the visible
|
|
359
|
+
* status changes, so it is safe for `useSyncExternalStore`.
|
|
360
|
+
*/
|
|
361
|
+
interface ViewStatus {
|
|
362
|
+
readonly status: QueryStatus;
|
|
363
|
+
/** Subscribe-failure detail; present only when `status` is `error`. */
|
|
364
|
+
readonly error?: Error;
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* Client ↔ Mirror connection state (backing for `useConnectionStatus`):
|
|
368
|
+
*
|
|
369
|
+
* - `connecting` — a (re)connect attempt is in flight
|
|
370
|
+
* - `connected` — socket open, sync live
|
|
371
|
+
* - `disconnected` — dropped and backing off
|
|
372
|
+
* - `error` — transport error
|
|
373
|
+
* - `needs-auth` — Mirror rejected auth; refresh credentials before sync resumes
|
|
374
|
+
*/
|
|
375
|
+
type ConnectionStatus = "connecting" | "connected" | "disconnected" | "error" | "needs-auth";
|
|
376
|
+
/**
|
|
377
|
+
* A live, shared handle on a query's rows — what `subscribe()` and `local()`
|
|
378
|
+
* return, and what `useQuery` renders from.
|
|
379
|
+
*
|
|
380
|
+
* `current()` returns the latest rows; the array reference only changes when
|
|
381
|
+
* the rows do, so it is safe for `useSyncExternalStore`. `onChange` fires when
|
|
382
|
+
* the rows or the visible status move; re-read both `current()` and `status()`
|
|
383
|
+
* in the listener. A view keeps serving its last rows through disconnects and
|
|
384
|
+
* errors — check `status()` to tell fresh from stale.
|
|
385
|
+
*
|
|
386
|
+
* Lifecycle: creating a view is free and owns nothing. Call `retain()` when
|
|
387
|
+
* your component mounts, `release()` when it unmounts (both idempotent). Views
|
|
388
|
+
* for the same query share one subscription automatically.
|
|
389
|
+
*/
|
|
390
|
+
interface View<Row extends Record<string, unknown> = Record<string, SqlValue>> {
|
|
391
|
+
current(): readonly Row[];
|
|
392
|
+
onChange(listener: () => void): () => void;
|
|
393
|
+
/**
|
|
394
|
+
* Take ownership of this handle (call from a mount effect, never during
|
|
395
|
+
* render). Idempotent per handle.
|
|
396
|
+
*/
|
|
397
|
+
retain(): void;
|
|
398
|
+
/**
|
|
399
|
+
* Drop ownership (call from unmount cleanup). The last release unsubscribes
|
|
400
|
+
* upstream; this handle keeps serving its last snapshot. Idempotent.
|
|
401
|
+
*/
|
|
402
|
+
release(): void;
|
|
403
|
+
/** Latest status snapshot. Moves ride the same `onChange` as row changes. */
|
|
404
|
+
status(): ViewStatus;
|
|
405
|
+
/**
|
|
406
|
+
* `true` when the query is one-row (`` sql.one`…` `` / Drizzle `findFirst`),
|
|
407
|
+
* so `useQuery` unwraps to `Row | undefined`. `false` for multi-row;
|
|
408
|
+
* `undefined` when one-ness is not yet known.
|
|
409
|
+
*/
|
|
410
|
+
readonly one?: boolean;
|
|
411
|
+
}
|
|
412
|
+
/**
|
|
413
|
+
* A one-shot cache-and-network read — what `once()` returns and `useQueryOnce`
|
|
414
|
+
* renders from. `current()` is the local cache immediately; `server` resolves
|
|
415
|
+
* with the Mirror's answer (and updates the snapshot). Not reactive to later
|
|
416
|
+
* local writes. Call `dispose()` when done; a quick remount within a short
|
|
417
|
+
* grace keeps the same promise and in-flight request (StrictMode-safe).
|
|
418
|
+
*/
|
|
419
|
+
interface OnceView<Row extends Record<string, unknown> = Record<string, SqlValue>> {
|
|
420
|
+
current(): readonly Row[];
|
|
421
|
+
onChange(listener: () => void): () => void;
|
|
422
|
+
dispose(): void;
|
|
423
|
+
/** Resolves with the server's answer (network half of cache-and-network). */
|
|
424
|
+
readonly server: Promise<readonly Row[]>;
|
|
425
|
+
}
|
|
426
|
+
/**
|
|
427
|
+
* The client call surface: `subscribe` / `once` / `local` / `mutate`, plus
|
|
428
|
+
* connection and schema status. Platform adapters (`@doync/web`,
|
|
429
|
+
* `@doync/mobile`) implement this; `@doync/react` hooks adapt over it.
|
|
430
|
+
*
|
|
431
|
+
* Query-taking methods accept a {@link BoundQuery} (from a registered query
|
|
432
|
+
* call) or {@link FalsyQuery} ("no query"). `options.skip` is also supported.
|
|
433
|
+
*/
|
|
434
|
+
interface DoyncClient {
|
|
435
|
+
/**
|
|
436
|
+
* Live subscription for a bound query (`queries.foo(args)`). Falsy or
|
|
437
|
+
* `options.skip` yields an inert view (empty rows, status `unknown`).
|
|
438
|
+
*/
|
|
439
|
+
subscribe<Row extends Record<string, unknown> = Record<string, SqlValue>>(query: BoundQuery<Row, boolean> | FalsyQuery, options?: SubscribeOptions): View<Row>;
|
|
440
|
+
/**
|
|
441
|
+
* One-shot cache-and-network read for a bound query. Falsy never starts a
|
|
442
|
+
* network half.
|
|
443
|
+
*/
|
|
444
|
+
once<Row extends Record<string, unknown> = Record<string, SqlValue>>(query: BoundQuery<Row, boolean> | FalsyQuery): OnceView<Row>;
|
|
445
|
+
/**
|
|
446
|
+
* Live local-only read of raw SQL against the replica (no server
|
|
447
|
+
* subscription). Same retain/release lifecycle as {@link subscribe}.
|
|
448
|
+
*/
|
|
449
|
+
local<Row extends Record<string, unknown> = Record<string, SqlValue>>(sql: string, ...params: SqlValue[]): View<Row>;
|
|
450
|
+
/**
|
|
451
|
+
* Apply a registered mutation optimistically and push it to the Origin. Pass
|
|
452
|
+
* the {@link MutationDefinition} from your mutations tree; args are
|
|
453
|
+
* type-checked from the definition. Returns {@link MutationResult}.
|
|
454
|
+
*/
|
|
455
|
+
mutate<Args = unknown>(mutation: MutationDefinition<Args>, args: Args, options?: MutationOptions): MutationResult;
|
|
456
|
+
/** Current schema/recovery state, or `null` when nominal. */
|
|
457
|
+
readonly schemaStatus: SchemaEvent | null;
|
|
458
|
+
/**
|
|
459
|
+
* Subscribe to schema-status transitions (including a clear back to nominal).
|
|
460
|
+
* Returns the unsubscribe function.
|
|
461
|
+
*/
|
|
462
|
+
onSchemaChange(listener: () => void): () => void;
|
|
463
|
+
/**
|
|
464
|
+
* Wipe the replica and resubscribe while keeping clientId and pending writes.
|
|
465
|
+
* Safe "my local data looks wrong" refresh. Optional on the interface —
|
|
466
|
+
* platform clients implement it.
|
|
467
|
+
*/
|
|
468
|
+
resync?(): void;
|
|
469
|
+
/**
|
|
470
|
+
* Erase this identity's local data (replica, pendings, identity). Privacy /
|
|
471
|
+
* logout-forget path. Optional on the interface — platform clients implement
|
|
472
|
+
* it.
|
|
473
|
+
*/
|
|
474
|
+
forget?(): void;
|
|
475
|
+
/**
|
|
476
|
+
* Durably set {@link LogoutBehavior} at runtime (e.g. a "remember me"
|
|
477
|
+
* checkbox). Survives restart. Optional — platform clients implement it.
|
|
478
|
+
*/
|
|
479
|
+
setLogoutBehavior?(behavior: LogoutBehavior): void;
|
|
480
|
+
/**
|
|
481
|
+
* Authenticated identity, or `null` when anonymous. Updated by auth refresh;
|
|
482
|
+
* never derived from a bearer token by the library.
|
|
483
|
+
*/
|
|
484
|
+
readonly userId: string | null;
|
|
485
|
+
/**
|
|
486
|
+
* Warm the replica for a query without creating a local view — rows flow in
|
|
487
|
+
* for other queries that read the same tables. Falsy yields a no-op handle.
|
|
488
|
+
* Call `cleanup()` to release (often never, for a session-long preload).
|
|
489
|
+
*/
|
|
490
|
+
preload(query: BoundQuery | FalsyQuery, options?: PreloadOptions): PreloadHandle;
|
|
491
|
+
/** Current {@link ConnectionStatus} to the Mirror. */
|
|
492
|
+
readonly connectionStatus: ConnectionStatus;
|
|
493
|
+
/** Subscribe to connection-status transitions. Returns the unsubscribe. */
|
|
494
|
+
onConnectionChange(listener: () => void): () => void;
|
|
495
|
+
}
|
|
496
|
+
/**
|
|
497
|
+
* Falsy "no query" on subscribe / once / preload: `false | null | undefined`.
|
|
498
|
+
* Lets `cond && query(args)` and optional-prop patterns type-check.
|
|
499
|
+
*/
|
|
500
|
+
type FalsyQuery = false | null | undefined;
|
|
501
|
+
/** Per-subscribe options. */
|
|
502
|
+
interface SubscribeOptions {
|
|
503
|
+
/**
|
|
504
|
+
* How long after unmount the server keeps this subscription warm, in ms of
|
|
505
|
+
* connected time. Absent ⇒ server default (clamped to its ceiling).
|
|
506
|
+
*/
|
|
507
|
+
readonly ttl?: number;
|
|
508
|
+
/**
|
|
509
|
+
* Skip the subscribe: empty rows, status `unknown`, no network. Use for
|
|
510
|
+
* conditional queries under React's unconditional-hooks rule.
|
|
511
|
+
*/
|
|
512
|
+
readonly skip?: boolean;
|
|
513
|
+
}
|
|
514
|
+
/** Options for {@link DoyncClient.preload}. */
|
|
515
|
+
interface PreloadOptions {
|
|
516
|
+
/**
|
|
517
|
+
* Connected-time grace after cleanup before the server drops the warm
|
|
518
|
+
* subscription, in ms. Absent ⇒ server default.
|
|
519
|
+
*/
|
|
520
|
+
readonly ttl?: number;
|
|
521
|
+
}
|
|
522
|
+
/** Handle returned by {@link DoyncClient.preload}. */
|
|
523
|
+
interface PreloadHandle {
|
|
524
|
+
/**
|
|
525
|
+
* Release the preload. Idempotent; typically unused for a session-long
|
|
526
|
+
* preload.
|
|
527
|
+
*/
|
|
528
|
+
cleanup(): void;
|
|
529
|
+
}
|
|
530
|
+
interface ClientEngineConfig {
|
|
531
|
+
/**
|
|
532
|
+
* The synchronous local-DB port (ADR-0019): node:sqlite in tests, wa-sqlite
|
|
533
|
+
* on the web.
|
|
534
|
+
*/
|
|
535
|
+
readonly db: LocalDb;
|
|
536
|
+
/**
|
|
537
|
+
* The consumer's synced schema — the shape source replayed for the replica
|
|
538
|
+
* (ADR-0020).
|
|
539
|
+
*/
|
|
540
|
+
readonly schema: DoyncSchema;
|
|
541
|
+
/**
|
|
542
|
+
* Named mutation bodies resolved on `push` — deterministic, DB-only
|
|
543
|
+
* (ADR-0017/0021).
|
|
544
|
+
*/
|
|
545
|
+
readonly mutations: ClientMutationRegistry;
|
|
546
|
+
/** The Mirror socket seam (ADR-0016). */
|
|
547
|
+
readonly socket: SyncSocket;
|
|
548
|
+
/**
|
|
549
|
+
* The asserted projected auth context (ADR-0018 addendum / closeio/doync#167)
|
|
550
|
+
* queries and mutation bodies read. Consumer-owned; the library ships no
|
|
551
|
+
* decode. Anonymous `{}`.
|
|
552
|
+
*/
|
|
553
|
+
readonly ctx?: Record<string, unknown>;
|
|
554
|
+
/**
|
|
555
|
+
* Transport credential PRESENTED as `connect{jwt}` (ADR-0016/0018) — the
|
|
556
|
+
* Mirror resolves token-first. Auth must ride the HANDSHAKE, not only the
|
|
557
|
+
* WebSocket upgrade's Cookie header: the socket lives in the SharedWorker and
|
|
558
|
+
* can OUTLIVE a login. The engine NEVER derives identity from this token;
|
|
559
|
+
* {@link userId} and {@link ctx} are the asserted siblings.
|
|
560
|
+
*/
|
|
561
|
+
readonly token?: string;
|
|
562
|
+
/**
|
|
563
|
+
* The ASSERTED authenticated identity (closeio/doync#167) — drives
|
|
564
|
+
* {@link ClientEngine.userId}. `null` / omitted = anonymous. Never derived
|
|
565
|
+
* from {@link token}.
|
|
566
|
+
*/
|
|
567
|
+
readonly userId?: string | null;
|
|
568
|
+
/**
|
|
569
|
+
* The durable clientId (ADR-0019's respawn-double-apply trap): reused from
|
|
570
|
+
* `__doync_meta` when present, else this value, else generated. Provide it to
|
|
571
|
+
* pin identity across engine restarts over the same DB.
|
|
572
|
+
*/
|
|
573
|
+
readonly clientId?: string;
|
|
574
|
+
/**
|
|
575
|
+
* ClientId generator when none is stored/provided (default:
|
|
576
|
+
* `crypto.randomUUID`). Test-only pin — production paths always provide
|
|
577
|
+
* {@link clientId} or accept a random UUID.
|
|
578
|
+
*/
|
|
579
|
+
readonly generateId?: () => string;
|
|
580
|
+
/**
|
|
581
|
+
* Consume schema-state transitions (ADR-0020): a stale-client or above-bundle
|
|
582
|
+
* skew (`reload`), a client-ahead skew (`server-behind`), or a
|
|
583
|
+
* post-failed-migration wipe (`resync`). The UI layer reloads the app on
|
|
584
|
+
* `reload` and can surface a retry banner on `server-behind`; the engine
|
|
585
|
+
* drives the recovery itself (re-handshake / wipe). The current state is also
|
|
586
|
+
* readable synchronously via {@link ClientEngine.schemaStatus}.
|
|
587
|
+
*/
|
|
588
|
+
readonly onSchemaEvent?: (event: SchemaEvent) => void;
|
|
589
|
+
/**
|
|
590
|
+
* Wall-clock source for the connected-time counter (ADR-0014 client half /
|
|
591
|
+
* closeio/doync#223). Defaults to `Date.now`. Injectable so Seam A tests pin
|
|
592
|
+
* pong deltas without real timers; package-internal — never a public
|
|
593
|
+
* surface.
|
|
594
|
+
*/
|
|
595
|
+
readonly now?: () => number;
|
|
596
|
+
}
|
|
597
|
+
declare class ClientEngine implements DoyncClient {
|
|
598
|
+
#private;
|
|
599
|
+
constructor(config: ClientEngineConfig);
|
|
600
|
+
/**
|
|
601
|
+
* In-memory connected-time ms (ADR-0014 / #223). Package-internal for release
|
|
602
|
+
* stamps (#224) and membership GC — not on {@link DoyncClient}.
|
|
603
|
+
*/
|
|
604
|
+
get connectedClock(): number;
|
|
605
|
+
/**
|
|
606
|
+
* Durable release stamp for one instance, or `null` (ADR-0014 / #224).
|
|
607
|
+
* Package-internal for `heldAt` (#225) and GC (#226).
|
|
608
|
+
*/
|
|
609
|
+
releaseStamp(instance: string): ReleaseStamp | null;
|
|
610
|
+
/**
|
|
611
|
+
* Every parked release stamp (#226 hygiene GC). Package-internal; order
|
|
612
|
+
* unspecified.
|
|
613
|
+
*/
|
|
614
|
+
releaseStamps(): ReleaseStamp[];
|
|
615
|
+
/** Durable clientId (stable across restarts on the same DB). */
|
|
616
|
+
get clientId(): string;
|
|
617
|
+
/**
|
|
618
|
+
* Asserted `userId` from construction / {@link updateAuth}, or `null` (#104 /
|
|
619
|
+
* #167). Never derived from a bearer (ADR-0018) — cookie sessions assert
|
|
620
|
+
* `userId` with no token.
|
|
621
|
+
*/
|
|
622
|
+
get userId(): string | null;
|
|
623
|
+
/** Scalar cookie held now (`null` on first sight). */
|
|
624
|
+
get cookie(): number | null;
|
|
625
|
+
/** Framed protocol errors (`error` frames, stray poke parts). */
|
|
626
|
+
get errors(): readonly string[];
|
|
627
|
+
/**
|
|
628
|
+
* Idempotency keys recovered at boot (ADR-0019). A DB_FAILOVER retry on a
|
|
629
|
+
* respawned engine gets the recovered pair whose orphaned `client` never
|
|
630
|
+
* settles for it — treat client phase as already applied.
|
|
631
|
+
*/
|
|
632
|
+
get recoveredKeys(): ReadonlySet<string>;
|
|
633
|
+
/** Bundled schema version (ADR-0020) — declared on every handshake for skew. */
|
|
634
|
+
get schemaVersion(): number;
|
|
635
|
+
/**
|
|
636
|
+
* Schema-handling state, or `null` when nominal (ADR-0020). Sync counterpart
|
|
637
|
+
* to {@link ClientEngineConfig.onSchemaEvent} for reload/retry UI.
|
|
638
|
+
*/
|
|
639
|
+
get schemaStatus(): SchemaEvent | null;
|
|
640
|
+
mutate<Args = unknown>(mutation: MutationDefinition<Args>, args: Args, options?: MutationOptions): MutationResult;
|
|
641
|
+
/**
|
|
642
|
+
* Adopt a refreshed SAME-USER identity triple (#167). When connected with a
|
|
643
|
+
* bearer, send in-band `updateAuth` (ADR-0018 / #85) so the Mirror extends
|
|
644
|
+
* auth without reconnect when userId matches and `issuedAt` is newer.
|
|
645
|
+
* Identity CHANGE (different `userId`, incl. anon↔user) is topology-level
|
|
646
|
+
* replica swap — SharedWorker routes only same-user refreshes here.
|
|
647
|
+
*
|
|
648
|
+
* Token is stored for the next reconnect (#92: surviving SharedWorker must
|
|
649
|
+
* not reuse a stale token). `ctx`/`userId` asserted, never derived. Halted
|
|
650
|
+
* engines never emit.
|
|
651
|
+
*/
|
|
652
|
+
updateAuth(token: string | null | undefined, ctx?: Record<string, unknown>, userId?: string | null): void;
|
|
653
|
+
/**
|
|
654
|
+
* Subscribe to a registered query (ADR-0021): resolve under args+ctx,
|
|
655
|
+
* register upstream, return a reactive {@link View}. Local snapshots run
|
|
656
|
+
* resolved SQL over base+overlay (RYOW). Re-project only when a local change
|
|
657
|
+
* touches the instance's server Read-set.
|
|
658
|
+
*
|
|
659
|
+
* Bound form only (ADR-0027 / #200): {@link BoundQuery} or falsy →
|
|
660
|
+
* SkippedView. `options.skip` is also an inert View with no desire.
|
|
661
|
+
*/
|
|
662
|
+
subscribe<Row extends Record<string, unknown> = Record<string, SqlValue>>(query: BoundQuery<Row, boolean> | FalsyQuery, options?: SubscribeOptions): View<Row>;
|
|
663
|
+
/**
|
|
664
|
+
* Warm the replica without materializing a View (#104, ADR-0019/0021):
|
|
665
|
+
* register upstream so pokes hydrate rows other queries read, at zero local
|
|
666
|
+
* recompute of the preload statement. Refcounts the same desired instance as
|
|
667
|
+
* a live subscribe (byte-identical statement). `{cleanup}` releases into TTL
|
|
668
|
+
* grace (ADR-0014). Bound form only (ADR-0027 / #200); falsy → no-op handle.
|
|
669
|
+
*/
|
|
670
|
+
preload(query: BoundQuery | FalsyQuery, options?: PreloadOptions): PreloadHandle;
|
|
671
|
+
/**
|
|
672
|
+
* Once (ADR-0012 / ADR-0021): cache-and-network. Local replica first (real
|
|
673
|
+
* SQL `[]` when empty); Mirror executes once under its ctx. Local
|
|
674
|
+
* exec/decode/shape errors throw at call (#141). Leaves no
|
|
675
|
+
* Subscription/CVR/Membership. Bound form only (ADR-0027 / #200); falsy never
|
|
676
|
+
* starts the network half.
|
|
677
|
+
*/
|
|
678
|
+
once<Row extends Record<string, unknown> = Record<string, SqlValue>>(query: BoundQuery<Row, boolean> | FalsyQuery): OnceView<Row>;
|
|
679
|
+
/**
|
|
680
|
+
* Local read (ADR-0019/0021): arbitrary SQL over the replica, never upstream;
|
|
681
|
+
* re-run on any local commit (unhinted).
|
|
682
|
+
*/
|
|
683
|
+
local<Row extends Record<string, unknown> = Record<string, SqlValue>>(sql: string, ...params: SqlValue[]): View<Row>;
|
|
684
|
+
/**
|
|
685
|
+
* Rebuild sync state (ADR-0022 resync): wipe replica/memberships/cookie, KEEP
|
|
686
|
+
* clientId + pending, rebootstrap. Same core as failed-migration and
|
|
687
|
+
* cookie-above-head heals ({@link #wipeAndResync}). Exclusive chain; offline
|
|
688
|
+
* rebootstrap rides next reconnect.
|
|
689
|
+
*/
|
|
690
|
+
resync(): void;
|
|
691
|
+
/**
|
|
692
|
+
* Erase this identity's local data (ADR-0022 forget): wipe replica,
|
|
693
|
+
* memberships, pending, and all meta (clientId), then fresh first-sight under
|
|
694
|
+
* a new clientId. Erases WHO, not just WHAT. Awaiters of forgotten writes are
|
|
695
|
+
* rejected. Topology can swap OPFS on active forget (#134); this resets the
|
|
696
|
+
* current store. Exclusive chain.
|
|
697
|
+
*/
|
|
698
|
+
forget(): void;
|
|
699
|
+
/**
|
|
700
|
+
* Durably set {@link LogoutBehavior} at runtime ({@link
|
|
701
|
+
* DoyncClient.setLogoutBehavior}). Via {@link #sideWriteMeta} so it commits
|
|
702
|
+
* outside the optimistic overlay (#135 / ADR-0019). Direct engine stores for
|
|
703
|
+
* RN; web DB worker reports to hub.
|
|
704
|
+
*/
|
|
705
|
+
setLogoutBehavior(behavior: LogoutBehavior): void;
|
|
706
|
+
/**
|
|
707
|
+
* Schema-state transitions including silent clear (#89) so hooks re-read
|
|
708
|
+
* {@link schemaStatus} and banners don't stick after recovery.
|
|
709
|
+
*/
|
|
710
|
+
onSchemaChange(listener: () => void): () => void;
|
|
711
|
+
/**
|
|
712
|
+
* {@link ConnectionStatus} to the Mirror (#105/#136). Precedence: 1.
|
|
713
|
+
* `needs-auth` — engine inference off framed `unauthorized` (ADR-0018),
|
|
714
|
+
* sticky until a real frame proves the refreshed handshake; outranks seam and
|
|
715
|
+
* even a reopened socket. 2. `connected` — live open outranks a stale seam
|
|
716
|
+
* report. 3. Last {@link SeamStatus}, else `disconnected`.
|
|
717
|
+
*
|
|
718
|
+
* Full five states when the seam reports; degraded adapters (no seam) get
|
|
719
|
+
* connected/disconnected (+ needs-auth). Parity with shared-hub.ts.
|
|
720
|
+
*/
|
|
721
|
+
get connectionStatus(): ConnectionStatus;
|
|
722
|
+
/** Subscribe to connection-status transitions (#105). */
|
|
723
|
+
onConnectionChange(listener: () => void): () => void;
|
|
724
|
+
}
|
|
725
|
+
/**
|
|
726
|
+
* `false | null | undefined` — the "no query" sentinel (ADR-0027 /
|
|
727
|
+
* closeio/doync#195/#200). Exported so adapters reuse the same predicate the
|
|
728
|
+
* direct engine uses rather than shadowing a three-literal check.
|
|
729
|
+
*/
|
|
730
|
+
declare function isFalsyQuery(value: unknown): value is FalsyQuery;
|
|
731
|
+
/**
|
|
732
|
+
* Bound-form surface normalize (ADR-0027 / closeio/doync#200): peel a
|
|
733
|
+
* {@link BoundQuery} into `{leaf, args, options?}`, or reject a truthy non-bound
|
|
734
|
+
* impostor with a surface-named error. An uncalled RegisteredQuery (a function)
|
|
735
|
+
* hits "did you forget to call it?" — registration is a type-level fact, so the
|
|
736
|
+
* legacy definition arm is gone.
|
|
737
|
+
*
|
|
738
|
+
* Exported from `@doync/client` so the web topology and mobile wrapper share
|
|
739
|
+
* one surface check with the direct engine — no per-adapter copy of the
|
|
740
|
+
* impostor wording.
|
|
741
|
+
*/
|
|
742
|
+
declare function normalizeQuerySurface<Options = never>(surface: "subscribe" | "once" | "preload", queryOrBound: unknown, options?: Options): {
|
|
743
|
+
query: BoundQuery["query"];
|
|
744
|
+
args: unknown;
|
|
745
|
+
options: Options | undefined;
|
|
746
|
+
};
|
|
747
|
+
/**
|
|
748
|
+
* A `mutate()` whose failure is known synchronously (unknown name, bad args).
|
|
749
|
+
* Exported for the web topology's tab-side `mutate`, which shares the
|
|
750
|
+
* never-throw contract (#161).
|
|
751
|
+
*/
|
|
752
|
+
declare function settledRejection(error: unknown): MutationResult;
|
|
753
|
+
//#endregion
|
|
754
|
+
//#region src/client.d.ts
|
|
755
|
+
/**
|
|
756
|
+
* Options for {@link createClient}. Platform adapters (`@doync/web`,
|
|
757
|
+
* `@doync/mobile`) fill these; app code rarely calls `createClient` directly.
|
|
758
|
+
*/
|
|
759
|
+
interface CreateClientOptions<TAuthData = unknown> {
|
|
760
|
+
/** Synchronous local SQLite port (wa-sqlite / node:sqlite / op-sqlite). */
|
|
761
|
+
readonly db: LocalDb;
|
|
762
|
+
/** Synced schema shared with Origin and Mirror. */
|
|
763
|
+
readonly schema: DoyncSchema;
|
|
764
|
+
/**
|
|
765
|
+
* Shared mutation tree from `defineMutations(shared)` — no server overrides
|
|
766
|
+
* in the client bundle.
|
|
767
|
+
*/
|
|
768
|
+
readonly mutations: MutationTree;
|
|
769
|
+
/** Transport to the Mirror. */
|
|
770
|
+
readonly socket: SyncSocket;
|
|
771
|
+
/**
|
|
772
|
+
* Bearer credential for the handshake. Does not drive `userId` or `ctx`. Omit
|
|
773
|
+
* for cookie-session auth.
|
|
774
|
+
*/
|
|
775
|
+
readonly token?: string;
|
|
776
|
+
/**
|
|
777
|
+
* Projected auth context for optimistic query/mutation resolution (mirror
|
|
778
|
+
* your server `toContext`). Defaults to `{}`.
|
|
779
|
+
*/
|
|
780
|
+
readonly ctx?: TAuthData;
|
|
781
|
+
/**
|
|
782
|
+
* Authenticated identity, or `null`/omit for anonymous. Never derived from
|
|
783
|
+
* `token`.
|
|
784
|
+
*/
|
|
785
|
+
readonly userId?: string | null;
|
|
786
|
+
/** Pin a durable client id across restarts. */
|
|
787
|
+
readonly clientId?: string;
|
|
788
|
+
/** Observe schema/recovery transitions. */
|
|
789
|
+
readonly onSchemaEvent?: (event: SchemaEvent) => void;
|
|
790
|
+
/**
|
|
791
|
+
* Initial {@link LogoutBehavior}, written durably when provided. Omit to keep
|
|
792
|
+
* a previously stored choice (a restart will not revert `forget` to `keep`).
|
|
793
|
+
* Change later via {@link DoyncClient.setLogoutBehavior}.
|
|
794
|
+
*/
|
|
795
|
+
readonly logoutBehavior?: LogoutBehavior;
|
|
796
|
+
}
|
|
797
|
+
/**
|
|
798
|
+
* Construct a {@link DoyncClient} from local DB, schema, shared mutations, and a
|
|
799
|
+
* Mirror socket. App code normally goes through `@doync/web` / `@doync/mobile`
|
|
800
|
+
* instead.
|
|
801
|
+
*/
|
|
802
|
+
declare function createClient<TAuthData = unknown>(options: CreateClientOptions<TAuthData>): DoyncClient;
|
|
803
|
+
/**
|
|
804
|
+
* Concrete-engine factory (ADR-0033 / closeio/doync#241). Same construction as
|
|
805
|
+
* {@link createClient}, but typed as {@link ClientEngine} so platform adapters
|
|
806
|
+
* (web DB-worker, mobile wrapper) can reach engine-only fields like `clientId`
|
|
807
|
+
* without an unchecked downcast. Exported on `@doync/client/internal` only.
|
|
808
|
+
*/
|
|
809
|
+
declare function createClientEngine<TAuthData = unknown>(options: CreateClientOptions<TAuthData>): ClientEngine;
|
|
810
|
+
//#endregion
|
|
811
|
+
export { readMeta as A, validateMutationArgs as B, isFalsyQuery as C, createEngineTables as D, applyBundledMigrations as E, ClientMutation as F, LocalRow as H, ClientMutationHandler as I, ClientMutationRegistry as L, SeamStatus as M, SyncSocket as N, dropConsumerTables as O, SyncSocketHandlers as P, ClientMutationTx as R, __LOGOUT_BEHAVIOR_META_KEY as S, settledRejection as T, extractWriteTable as U, LocalDb as V, SchemaEventKind as _, ClientEngineConfig as a, ViewStatus as b, FalsyQuery as c, MutationResult as d, OnceView as f, SchemaEvent as g, QueryStatus as h, ClientEngine as i, writeMeta as j, replayMigrations as k, LogoutBehavior as l, PreloadOptions as m, createClient as n, ConnectionStatus as o, PreloadHandle as p, createClientEngine as r, DoyncClient as s, CreateClientOptions as t, MutationOptions as u, SubscribeOptions as v, normalizeQuerySurface as w, __CONNECTED_CLOCK_META_KEY as x, View as y, asClientMutation as z };
|
|
812
|
+
//# sourceMappingURL=client-DHXO0dbf.d.cts.map
|