@lunora/client 1.0.0-alpha.4 → 1.0.0-alpha.6
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/README.md +2 -0
- package/dist/auth/index.d.mts +1 -1
- package/dist/auth/index.d.ts +1 -1
- package/dist/index.d.mts +67 -4
- package/dist/index.d.ts +67 -4
- package/dist/index.mjs +3 -2
- package/dist/packem_shared/{LunoraClient-DHMV-94j.mjs → LunoraClient-DhwA_5Kj.mjs} +355 -51
- package/dist/packem_shared/{OfflineQueue-D5p_QgF_.mjs → OfflineQueue-D-ASeqL7.mjs} +5 -1
- package/dist/packem_shared/createMutatorRunner-BETvCd0p.mjs +31 -0
- package/dist/packem_shared/{createServerClient-DMh-2UDv.mjs → createServerClient-XahK8bPv.mjs} +1 -1
- package/dist/packem_shared/{lunora-client.d-CbnxmuqG.d.ts → lunora-client.d-DzEWBYeP.d.mts} +336 -11
- package/dist/packem_shared/{lunora-client.d-CbnxmuqG.d.mts → lunora-client.d-DzEWBYeP.d.ts} +336 -11
- package/dist/packem_shared/{preload.d-C73DT6bL.d.ts → preload.d-CmMMC-wm.d.ts} +1 -1
- package/dist/packem_shared/{preload.d-Bhq6DBSe.d.mts → preload.d-DuYqEQ5J.d.mts} +1 -1
- package/dist/query/index.d.mts +2 -2
- package/dist/query/index.d.ts +2 -2
- package/dist/ssr/index.d.mts +3 -3
- package/dist/ssr/index.d.ts +3 -3
- package/dist/ssr/index.mjs +1 -1
- package/package.json +1 -1
|
@@ -120,6 +120,40 @@ interface PersistenceAdapter {
|
|
|
120
120
|
remove: (id: string) => Promise<void>;
|
|
121
121
|
}
|
|
122
122
|
/**
|
|
123
|
+
* One write handed to an {@link OutboxSink}. Mirrors {@link PersistedMutation}
|
|
124
|
+
* plus the custom-mutator identity (`clientId`/`mutationId`/`idempotencyKey`)
|
|
125
|
+
* the durable outbox needs to dedupe and watermark replays.
|
|
126
|
+
*/
|
|
127
|
+
interface OutboxMutation {
|
|
128
|
+
args: Record<string, unknown>;
|
|
129
|
+
/** Stable per-client id; pairs with {@link OutboxMutation.mutationId} as `idempotencyKey`. */
|
|
130
|
+
clientId: string;
|
|
131
|
+
functionPath: string;
|
|
132
|
+
/** `${clientId}:${mutationId}` — sent as `x-lunora-mutation-id` so a replay is server-idempotent. */
|
|
133
|
+
idempotencyKey: string;
|
|
134
|
+
/** Issuing identity fingerprint (`null` = signed out); drives the sink's identity guard. */
|
|
135
|
+
identity: string | null;
|
|
136
|
+
/** Monotonic per-client mutation id, backing the server `__client_watermark`. */
|
|
137
|
+
mutationId: number;
|
|
138
|
+
shardKey?: string;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Pluggable durable outbox seam. When set on {@link LunoraClientOptions.outbox},
|
|
142
|
+
* the client delegates offline write durability + at-least-once replay to this
|
|
143
|
+
* sink instead of its built-in {@link PersistenceAdapter}-backed `OfflineQueue`.
|
|
144
|
+
* `@lunora/db` supplies the blessed implementation (`createExecutorOutboxSink`,
|
|
145
|
+
* backed by the TanStack `OfflineExecutor`); the interface itself is
|
|
146
|
+
* dependency-free so `@lunora/client` stays TanStack-free.
|
|
147
|
+
*/
|
|
148
|
+
interface OutboxSink {
|
|
149
|
+
/**
|
|
150
|
+
* Persist and schedule a write for replay. Rejects with an
|
|
151
|
+
* `OFFLINE_QUEUE_OVERFLOW`-coded error when the sink's cap is exceeded, so
|
|
152
|
+
* the caller can surface back-pressure to the issuing mutation.
|
|
153
|
+
*/
|
|
154
|
+
enqueue: (mutation: OutboxMutation) => Promise<void>;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
123
157
|
* One persisted query result in the durable read cache (Pillar 2). Keyed in the
|
|
124
158
|
* store by `shardKey + functionPath + argsKey`; the record carries everything
|
|
125
159
|
* needed to render offline on reload and to resume the live subscription.
|
|
@@ -177,6 +211,16 @@ interface LunoraClientOptions {
|
|
|
177
211
|
authBasePath?: string;
|
|
178
212
|
bookmarkStorage?: BookmarkStorage;
|
|
179
213
|
/**
|
|
214
|
+
* Stable per-client id backing the custom-mutator watermark. Sent on the
|
|
215
|
+
* `connect` envelope (so the server can scope this client's
|
|
216
|
+
* `__client_watermark`) and stamped onto every {@link OutboxMutation} the
|
|
217
|
+
* {@link LunoraClientOptions.outbox} sink persists, where it pairs with the
|
|
218
|
+
* monotonic mutation id to form the idempotency key. The `@lunora/db` path
|
|
219
|
+
* persists a stable id alongside the outbox and passes it here; omit for the
|
|
220
|
+
* standalone client, which generates an ephemeral per-session id.
|
|
221
|
+
*/
|
|
222
|
+
clientId?: string;
|
|
223
|
+
/**
|
|
180
224
|
* Default app context sent in the `connect` envelope right after each socket
|
|
181
225
|
* opens, forwarded to the server's `onConnect`/`onDisconnect` lifecycle hooks
|
|
182
226
|
* as `event.context`. A per-shard context registered via
|
|
@@ -204,6 +248,14 @@ interface LunoraClientOptions {
|
|
|
204
248
|
*/
|
|
205
249
|
heartbeatIntervalMs?: number;
|
|
206
250
|
offlineQueue?: OfflineQueueOptions;
|
|
251
|
+
/**
|
|
252
|
+
* Durable outbox seam for offline writes. When supplied (the `@lunora/db`
|
|
253
|
+
* path wires `createExecutorOutboxSink`), offline mutations are delegated to
|
|
254
|
+
* the sink and the built-in {@link PersistenceAdapter}-backed `OfflineQueue`
|
|
255
|
+
* is bypassed, so a db app has exactly one durable write path. Omit for the
|
|
256
|
+
* standalone client, which keeps using {@link LunoraClientOptions.persistence}.
|
|
257
|
+
*/
|
|
258
|
+
outbox?: OutboxSink;
|
|
207
259
|
/** Durable store for the offline mutation queue; omit to keep it in memory. */
|
|
208
260
|
persistence?: PersistenceAdapter;
|
|
209
261
|
/**
|
|
@@ -232,17 +284,43 @@ interface LunoraClientOptions {
|
|
|
232
284
|
/** Wire envelope sent on `POST /_lunora/rpc`. */
|
|
233
285
|
interface RpcEnvelope {
|
|
234
286
|
args?: Record<string, unknown>;
|
|
287
|
+
/**
|
|
288
|
+
* Stable per-client identifier (custom-mutator push path). Pairs with
|
|
289
|
+
* {@link RpcEnvelope.mutationId} to form `idempotencyKey` and scope the
|
|
290
|
+
* server `__client_watermark`. Absent on plain `client.mutation` calls.
|
|
291
|
+
*/
|
|
292
|
+
clientId?: string;
|
|
235
293
|
functionPath: string;
|
|
294
|
+
/**
|
|
295
|
+
* Idempotency key (`${clientId}:${mutationId}`) for the custom-mutator push
|
|
296
|
+
* path, mirrored into the `x-lunora-mutation-id` header. Absent on plain
|
|
297
|
+
* `client.mutation` calls.
|
|
298
|
+
*/
|
|
299
|
+
idempotencyKey?: string;
|
|
300
|
+
/**
|
|
301
|
+
* Monotonic per-client mutation id (custom-mutator push path), backing the
|
|
302
|
+
* server-side per-client watermark: `id <= watermark` is a replay (skipped),
|
|
303
|
+
* `id == watermark + 1` runs authoritatively, `id > watermark + 1` halts the
|
|
304
|
+
* batch so the client resends from `watermark + 1`. Absent on plain
|
|
305
|
+
* `client.mutation` calls.
|
|
306
|
+
*/
|
|
307
|
+
mutationId?: number;
|
|
236
308
|
shardKey?: string;
|
|
237
309
|
}
|
|
238
|
-
/**
|
|
310
|
+
/**
|
|
311
|
+
* Wire response from the shard's `/rpc` endpoint (forwarded by the runtime). A
|
|
312
|
+
* watermarked custom-mutator push additionally carries `lastMutationId` — the
|
|
313
|
+
* highest per-client sequence the DO has applied — which the client uses to keep
|
|
314
|
+
* its `clientSeq` generator monotonic across reloads (see `LunoraClient.callMutator`).
|
|
315
|
+
*/
|
|
239
316
|
type RpcResponseBody = {
|
|
240
|
-
result: unknown;
|
|
241
|
-
} | {
|
|
242
317
|
error: {
|
|
243
318
|
code: string;
|
|
244
319
|
message: string;
|
|
245
320
|
};
|
|
321
|
+
} | {
|
|
322
|
+
lastMutationId?: number;
|
|
323
|
+
result: unknown;
|
|
246
324
|
};
|
|
247
325
|
/** Subscription protocol — client → server. */
|
|
248
326
|
interface ClientSubscribeMessage {
|
|
@@ -275,10 +353,51 @@ interface ClientUnsubscribeMessage {
|
|
|
275
353
|
* `onDisconnect` when the socket drops.
|
|
276
354
|
*/
|
|
277
355
|
interface ClientConnectMessage {
|
|
356
|
+
/**
|
|
357
|
+
* Stable per-client id (persisted alongside the outbox). Lets the server
|
|
358
|
+
* scope this connection's `__client_watermark` so custom-mutator pokes can
|
|
359
|
+
* echo the right per-client `lastMutationId`. Omitted by clients that don't
|
|
360
|
+
* use custom mutators.
|
|
361
|
+
*/
|
|
362
|
+
clientId?: string;
|
|
278
363
|
context?: Record<string, unknown>;
|
|
279
364
|
id: string;
|
|
280
365
|
type: "connect";
|
|
281
366
|
}
|
|
367
|
+
/**
|
|
368
|
+
* Subscribe to a declarative **shape** — server-side partial replication scoped
|
|
369
|
+
* by `shardBy` + the shape's predicate + RLS. The client sends the shape *name*
|
|
370
|
+
* + validated `args`; the server resolves the trusted `where` (identity/RLS
|
|
371
|
+
* `baseWhere` the client can't forge) and streams the matching rowset, then live
|
|
372
|
+
* {@link ServerPokePartMessage} diffs. `id` namespaces the subscription and is
|
|
373
|
+
* echoed as `shapeId` on every poke part.
|
|
374
|
+
*/
|
|
375
|
+
interface ClientShapeSubscribeMessage {
|
|
376
|
+
id: string;
|
|
377
|
+
shape: {
|
|
378
|
+
args?: Record<string, unknown>;
|
|
379
|
+
name: string;
|
|
380
|
+
};
|
|
381
|
+
/**
|
|
382
|
+
* Resume from this checkpoint (the `__cdc_log` cursor the client last
|
|
383
|
+
* applied for this shape). When absent or below the server's retained floor
|
|
384
|
+
* (`minCdcSeq`), the server re-seeds with a full insert-poke instead of a
|
|
385
|
+
* delta.
|
|
386
|
+
*/
|
|
387
|
+
sinceCheckpoint?: number;
|
|
388
|
+
/**
|
|
389
|
+
* The CDC epoch {@link ClientShapeSubscribeMessage.sinceCheckpoint} belongs
|
|
390
|
+
* to. A mismatch (forked changelog timeline) forces a full re-seed even when
|
|
391
|
+
* the cursor is numerically in range.
|
|
392
|
+
*/
|
|
393
|
+
sinceEpoch?: string;
|
|
394
|
+
type: "shape_subscribe";
|
|
395
|
+
}
|
|
396
|
+
/** Cancel a shape subscription started with the same `id`. */
|
|
397
|
+
interface ClientShapeUnsubscribeMessage {
|
|
398
|
+
id: string;
|
|
399
|
+
type: "shape_unsubscribe";
|
|
400
|
+
}
|
|
282
401
|
interface ClientAckMessage {
|
|
283
402
|
id: string;
|
|
284
403
|
type: "ack";
|
|
@@ -319,7 +438,7 @@ interface ClientWhisperMessage {
|
|
|
319
438
|
topic: string;
|
|
320
439
|
type: "whisper";
|
|
321
440
|
}
|
|
322
|
-
type ClientMessage = ClientAckMessage | ClientConnectMessage | ClientStreamMessage | ClientSubscribeMessage | ClientUnsubscribeMessage | ClientWhisperMessage | ClientWhisperSubscribeMessage;
|
|
441
|
+
type ClientMessage = ClientAckMessage | ClientConnectMessage | ClientShapeSubscribeMessage | ClientShapeUnsubscribeMessage | ClientStreamMessage | ClientSubscribeMessage | ClientUnsubscribeMessage | ClientWhisperMessage | ClientWhisperSubscribeMessage;
|
|
323
442
|
/** Subscription protocol — server → client. */
|
|
324
443
|
interface ServerDataMessage {
|
|
325
444
|
/**
|
|
@@ -333,6 +452,13 @@ interface ServerDataMessage {
|
|
|
333
452
|
/** The CDC epoch this frame's cursor belongs to (see {@link CachedQuery.serverEpoch}). */
|
|
334
453
|
epoch?: string;
|
|
335
454
|
id: string;
|
|
455
|
+
/**
|
|
456
|
+
* The highest custom-mutator `mutationId` from this client the server has
|
|
457
|
+
* now applied (the per-client `__client_watermark`). Echoed so the client's
|
|
458
|
+
* outbox can drop confirmed pending mutations and let TanStack DB collapse
|
|
459
|
+
* the matching optimistic overlay. Absent on shards without custom mutators.
|
|
460
|
+
*/
|
|
461
|
+
lastMutationId?: number;
|
|
336
462
|
type: "data" | "delta";
|
|
337
463
|
}
|
|
338
464
|
/**
|
|
@@ -346,6 +472,8 @@ interface ServerResumeMessage {
|
|
|
346
472
|
/** The CDC epoch this resume's cursor belongs to (see {@link CachedQuery.serverEpoch}). */
|
|
347
473
|
epoch?: string;
|
|
348
474
|
id: string;
|
|
475
|
+
/** Per-client custom-mutator watermark (see {@link ServerDataMessage.lastMutationId}). */
|
|
476
|
+
lastMutationId?: number;
|
|
349
477
|
type: "resume";
|
|
350
478
|
}
|
|
351
479
|
interface ServerErrorMessage {
|
|
@@ -380,7 +508,65 @@ interface ServerWhisperMessage {
|
|
|
380
508
|
topic: string;
|
|
381
509
|
type: "whisper";
|
|
382
510
|
}
|
|
383
|
-
|
|
511
|
+
/**
|
|
512
|
+
* One row-level change in a shape's replication stream — the wire form of the
|
|
513
|
+
* DO's `__cdc_log` `CdcChange`. `insert`/`update` carry the post-image in
|
|
514
|
+
* `value` (projected to the shape's `columns`); `delete` omits it, identifying
|
|
515
|
+
* the removed row by `key` alone. The client applies these to its local
|
|
516
|
+
* collection; an unknown `key` on a `delete` is a safe no-op (a row the client
|
|
517
|
+
* never had in this shape).
|
|
518
|
+
*/
|
|
519
|
+
interface RowOp {
|
|
520
|
+
/** Row primary key (`_id`). */
|
|
521
|
+
key: string;
|
|
522
|
+
op: "delete" | "insert" | "update";
|
|
523
|
+
/** Logical table the row belongs to. */
|
|
524
|
+
table: string;
|
|
525
|
+
/** Post-image document for insert/update; absent on delete. */
|
|
526
|
+
value?: Record<string, unknown>;
|
|
527
|
+
}
|
|
528
|
+
/**
|
|
529
|
+
* Opens a **poke** — an atomically-applied batch of shape diffs (Zero's poke
|
|
530
|
+
* protocol). A `pokeStart` is followed by zero or more {@link ServerPokePartMessage}
|
|
531
|
+
* frames and closed by exactly one {@link ServerPokeEndMessage}; the client
|
|
532
|
+
* buffers every part and applies them in a single transaction at `pokeEnd`, so a
|
|
533
|
+
* socket that drops mid-poke simply re-seeds on reconnect (no torn view).
|
|
534
|
+
*/
|
|
535
|
+
interface ServerPokeStartMessage {
|
|
536
|
+
/** The checkpoint the client's view is expected to be at before this poke applies (for ordering/gap detection). */
|
|
537
|
+
baseCheckpoint?: number;
|
|
538
|
+
/** CDC epoch this poke belongs to; a mismatch forces the client to re-seed rather than apply. */
|
|
539
|
+
epoch?: string;
|
|
540
|
+
/** Correlates this poke's `pokeStart`/`pokePart`/`pokeEnd` frames. */
|
|
541
|
+
pokeId: string;
|
|
542
|
+
type: "pokeStart";
|
|
543
|
+
}
|
|
544
|
+
/** One shape's slice of an in-flight poke: the row-ops to apply for `shapeId`. */
|
|
545
|
+
interface ServerPokePartMessage {
|
|
546
|
+
/** Per-client custom-mutator watermark carried with this slice (see {@link ServerDataMessage.lastMutationId}). */
|
|
547
|
+
lastMutationId?: number;
|
|
548
|
+
pokeId: string;
|
|
549
|
+
/** Ordered row-level changes for this shape, applied in sequence at `pokeEnd`. */
|
|
550
|
+
rowsPatch: RowOp[];
|
|
551
|
+
/** The {@link ClientShapeSubscribeMessage.id} these row-ops belong to. */
|
|
552
|
+
shapeId: string;
|
|
553
|
+
type: "pokePart";
|
|
554
|
+
}
|
|
555
|
+
/**
|
|
556
|
+
* Closes a poke: the client commits the buffered parts atomically and advances
|
|
557
|
+
* its checkpoint to {@link ServerPokeEndMessage.checkpoint} (the `__cdc_log`
|
|
558
|
+
* cursor high-watermark the view now reflects), replayed as `sinceCheckpoint` on
|
|
559
|
+
* the next reconnect.
|
|
560
|
+
*/
|
|
561
|
+
interface ServerPokeEndMessage {
|
|
562
|
+
/** The `__cdc_log` cursor the view is at after applying this poke. */
|
|
563
|
+
checkpoint?: number;
|
|
564
|
+
/** CDC epoch the {@link ServerPokeEndMessage.checkpoint} belongs to. */
|
|
565
|
+
epoch?: string;
|
|
566
|
+
pokeId: string;
|
|
567
|
+
type: "pokeEnd";
|
|
568
|
+
}
|
|
569
|
+
type ServerMessage = ServerAckMessage | ServerChunkMessage | ServerCompleteMessage | ServerDataMessage | ServerErrorMessage | ServerPokeEndMessage | ServerPokePartMessage | ServerPokeStartMessage | ServerResumeMessage | ServerWhisperMessage;
|
|
384
570
|
/**
|
|
385
571
|
* The authenticated user as exposed client-side, mirroring better-auth's
|
|
386
572
|
* `user` row (the `user` field of the `get-session` response). Kept minimal
|
|
@@ -780,6 +966,13 @@ type ConnectionStatus = "connected" | "connecting" | "idle" | "offline";
|
|
|
780
966
|
* re-declaring it.
|
|
781
967
|
*/
|
|
782
968
|
interface MutationCallOptions<TCurrent = unknown, TValue = unknown, TArgs = unknown> {
|
|
969
|
+
/**
|
|
970
|
+
* Override the auto-generated idempotency key (`x-lunora-mutation-id`). Lets a
|
|
971
|
+
* durable outbox replay a committed-but-unacked write under its *original* key
|
|
972
|
+
* so the server dedups it instead of applying it twice. Omit for normal calls —
|
|
973
|
+
* each then gets a fresh key.
|
|
974
|
+
*/
|
|
975
|
+
mutationId?: string;
|
|
783
976
|
optimistic?: (current: TCurrent | undefined) => TValue;
|
|
784
977
|
/**
|
|
785
978
|
* Convex-parity multi-query optimistic update. Receives an
|
|
@@ -790,6 +983,19 @@ interface MutationCallOptions<TCurrent = unknown, TValue = unknown, TArgs = unkn
|
|
|
790
983
|
optimisticUpdate?: OptimisticUpdate<TArgs>;
|
|
791
984
|
shardKey?: string;
|
|
792
985
|
}
|
|
986
|
+
/** Callback a shape subscription invokes with its materialized rowset on every applied poke. */
|
|
987
|
+
type ShapeCallback = (rows: Record<string, unknown>[]) => void;
|
|
988
|
+
/**
|
|
989
|
+
* The high-water marks a shape poke has now synced to the client: `checkpoint`
|
|
990
|
+
* is the op-log cursor and `mutationId` the highest custom-mutator id the server
|
|
991
|
+
* echoed for this client. A `@lunora/db` collection feeds these into its
|
|
992
|
+
* checkpoint registry to drop optimistic overlays once the server's authoritative
|
|
993
|
+
* rows have landed.
|
|
994
|
+
*/
|
|
995
|
+
interface SyncWatermark {
|
|
996
|
+
checkpoint?: number;
|
|
997
|
+
mutationId?: number;
|
|
998
|
+
}
|
|
793
999
|
/**
|
|
794
1000
|
* Lunora browser/edge client. Talks RPC over HTTP and real-time deltas over
|
|
795
1001
|
* a single multiplexed WebSocket.
|
|
@@ -798,6 +1004,8 @@ interface MutationCallOptions<TCurrent = unknown, TValue = unknown, TArgs = unkn
|
|
|
798
1004
|
* see the package README for the wire protocol.
|
|
799
1005
|
*/
|
|
800
1006
|
declare class LunoraClient {
|
|
1007
|
+
/** Hard cap on concurrently-buffered pokes — a backstop that reclaims buffers abandoned by a mid-poke disconnect (no `pokeEnd`). Far above any real concurrent-in-flight count. */
|
|
1008
|
+
private static readonly MAX_POKE_BUFFERS;
|
|
801
1009
|
readonly url: string;
|
|
802
1010
|
readonly wsUrl: string;
|
|
803
1011
|
private wsToken;
|
|
@@ -812,6 +1020,25 @@ declare class LunoraClient {
|
|
|
812
1020
|
/** Keepalive cadence (ms); `0` disables the heartbeat. See {@link LunoraClientOptions.heartbeatIntervalMs}. */
|
|
813
1021
|
private readonly heartbeatIntervalMs;
|
|
814
1022
|
private readonly offlineQueue;
|
|
1023
|
+
/**
|
|
1024
|
+
* Durable outbox seam (the `@lunora/db` `createExecutorOutboxSink`). When
|
|
1025
|
+
* set, offline writes are delegated here and the built-in {@link OfflineQueue}
|
|
1026
|
+
* is bypassed, so a db app has exactly one durable write path.
|
|
1027
|
+
*/
|
|
1028
|
+
private readonly outbox;
|
|
1029
|
+
/** Stable per-client id stamped onto every `OutboxMutation` (custom-mutator watermark). */
|
|
1030
|
+
private readonly clientId;
|
|
1031
|
+
/**
|
|
1032
|
+
* Highest custom-mutator watermark the server has echoed for this client,
|
|
1033
|
+
* keyed by shard bucket (`shardKey ?? ""`) since the DO tracks one
|
|
1034
|
+
* `__client_watermark` per shard. `callMutator` bumps it from every
|
|
1035
|
+
* ack; the `@lunora/db` mutator runtime seeds its `clientSeq` generator from
|
|
1036
|
+
* it so a reload (which resets the in-memory counter) never reissues a stale
|
|
1037
|
+
* sequence the server would silently swallow as a replay.
|
|
1038
|
+
*/
|
|
1039
|
+
private readonly clientWatermarks;
|
|
1040
|
+
/** Monotonic per-client mutation counter backing the server `__client_watermark`. */
|
|
1041
|
+
private outboxMutationCounter;
|
|
815
1042
|
private readonly onPersistenceError;
|
|
816
1043
|
private readonly persistence;
|
|
817
1044
|
/** Durable read cache (Pillar 2); `undefined` when `queryCache` is omitted or `false`. */
|
|
@@ -888,6 +1115,11 @@ declare class LunoraClient {
|
|
|
888
1115
|
* calls `.cancel()` or the iterator is garbage-collected.
|
|
889
1116
|
*/
|
|
890
1117
|
private readonly streams;
|
|
1118
|
+
/** Live shape subscriptions (partial replication), keyed by their wire id. */
|
|
1119
|
+
private readonly shapeSubscriptions;
|
|
1120
|
+
/** In-flight pokes being assembled between `pokeStart` and `pokeEnd`, keyed by `pokeId`. */
|
|
1121
|
+
private readonly pokeBuffers;
|
|
1122
|
+
private nextShapeId;
|
|
891
1123
|
constructor(options: LunoraClientOptions);
|
|
892
1124
|
/**
|
|
893
1125
|
* Set (or clear) the bearer token sent on every HTTP RPC. Notifies any
|
|
@@ -902,6 +1134,47 @@ declare class LunoraClient {
|
|
|
902
1134
|
setAuthToken(token: string | null): void;
|
|
903
1135
|
getAuthToken(): string | null;
|
|
904
1136
|
/**
|
|
1137
|
+
* The current identity fingerprint (the same stamp queued offline writes
|
|
1138
|
+
* carry). Exposed so a durable {@link OutboxSink}'s replay handler — which
|
|
1139
|
+
* owns its own at-least-once replay outside the built-in `OfflineQueue` —
|
|
1140
|
+
* can drop a persisted write whose captured `identity` no longer matches the
|
|
1141
|
+
* signed-in user, the guard the queue path applies in `flushOfflineQueue`.
|
|
1142
|
+
*/
|
|
1143
|
+
currentIdentity(): string | null;
|
|
1144
|
+
/** This client's stable identifier — the watermark key the server's custom-mutator protocol advances per `clientSeq`. */
|
|
1145
|
+
clientIdentifier(): string;
|
|
1146
|
+
/**
|
|
1147
|
+
* The highest custom-mutator watermark the server has echoed for this client
|
|
1148
|
+
* on the given shard (0 if none yet). The `@lunora/db` mutator runtime seeds
|
|
1149
|
+
* its `clientSeq` generator from this so a reload never reissues a sequence
|
|
1150
|
+
* the server has already applied (which it would swallow as a replay, silently
|
|
1151
|
+
* dropping the write).
|
|
1152
|
+
*/
|
|
1153
|
+
confirmedMutationWatermark(shardKey?: string): number;
|
|
1154
|
+
/**
|
|
1155
|
+
* Push a custom mutator to its authoritative server impl over the watermark
|
|
1156
|
+
* protocol (Phase 4): the request carries `x-lunora-client-id` + a monotonic
|
|
1157
|
+
* `x-lunora-client-seq`, so the DO runs it exactly once and advances this
|
|
1158
|
+
* client's `__client_watermark`.
|
|
1159
|
+
*
|
|
1160
|
+
* Returns the server `result` plus `applied`: `true` when the DO ran this push
|
|
1161
|
+
* as the next-in-order mutation, `false` when it was a replay ack (`clientSeq`
|
|
1162
|
+
* was at or below the stored watermark — e.g. a stale sequence after a reload).
|
|
1163
|
+
* A `false` verdict tells the caller to reissue above the now-known watermark
|
|
1164
|
+
* (echoed into {@link confirmedMutationWatermark}) rather than treat the benign
|
|
1165
|
+
* ack as a confirmed write. Every ack — applied or not — bumps the watermark.
|
|
1166
|
+
*
|
|
1167
|
+
* This is the online transport for `@lunora/db`'s client-mutator runtime; the
|
|
1168
|
+
* optimistic overlay + durable-outbox concerns live in that runtime, not here.
|
|
1169
|
+
*/
|
|
1170
|
+
callMutator(functionPath: string, args: Record<string, unknown>, options?: {
|
|
1171
|
+
clientSeq?: number;
|
|
1172
|
+
shardKey?: string;
|
|
1173
|
+
}): Promise<{
|
|
1174
|
+
applied: boolean;
|
|
1175
|
+
result: unknown;
|
|
1176
|
+
}>;
|
|
1177
|
+
/**
|
|
905
1178
|
* Subscribe to auth-token changes. Returns an unsubscribe function. The
|
|
906
1179
|
* listener is NOT invoked on registration — use {@link getAuthToken} for
|
|
907
1180
|
* the current value.
|
|
@@ -1430,6 +1703,26 @@ declare class LunoraClient {
|
|
|
1430
1703
|
shardKey?: string;
|
|
1431
1704
|
}): Unsubscribe;
|
|
1432
1705
|
/**
|
|
1706
|
+
* Subscribe to a declarative **shape** — server-side partial replication
|
|
1707
|
+
* scoped by `shardBy` + the shape's predicate + RLS. The parallel to
|
|
1708
|
+
* {@link subscribe} for the poke protocol: the client sends the shape *name* +
|
|
1709
|
+
* validated `args` (never a `where` the client could forge), the server seeds
|
|
1710
|
+
* the current membership as an insert-poke and streams live membership diffs.
|
|
1711
|
+
* Each applied poke materializes the shape's rowset and invokes `callback`.
|
|
1712
|
+
*
|
|
1713
|
+
* Unlike {@link subscribe}, shape subscriptions are NOT deduped by
|
|
1714
|
+
* (name, args): the server resolves them under the socket's verified identity,
|
|
1715
|
+
* so every call gets its own id + view. The returned function unsubscribes.
|
|
1716
|
+
*/
|
|
1717
|
+
subscribeShape(shape: {
|
|
1718
|
+
args?: Record<string, unknown>;
|
|
1719
|
+
name: string;
|
|
1720
|
+
}, callback: ShapeCallback, options?: {
|
|
1721
|
+
onCheckpoint?: (watermark: SyncWatermark) => void;
|
|
1722
|
+
onError?: SubscriptionErrorCallback;
|
|
1723
|
+
shardKey?: string;
|
|
1724
|
+
}): Unsubscribe;
|
|
1725
|
+
/**
|
|
1433
1726
|
* Open a streaming query. The function reference must be a
|
|
1434
1727
|
* `kind:"stream"` registration (built with `c.query.input(...).stream(...)`);
|
|
1435
1728
|
* the type constraint catches accidental use of a query/mutation/action
|
|
@@ -1453,6 +1746,18 @@ declare class LunoraClient {
|
|
|
1453
1746
|
}): StreamIterable<ReturnOf<F>>;
|
|
1454
1747
|
close(): void;
|
|
1455
1748
|
/**
|
|
1749
|
+
* Persist a mutation that can't go out on the wire right now (offline, or
|
|
1750
|
+
* mid-reconnect after a prior connect). The optimistic update has already
|
|
1751
|
+
* been applied by `mutation`; this only chooses the durable write path and
|
|
1752
|
+
* rolls the optimistic write back if persistence is rejected.
|
|
1753
|
+
*
|
|
1754
|
+
* Two paths: when an `outbox` sink is wired (the `@lunora/db` executor) it
|
|
1755
|
+
* owns persistence + at-least-once replay, so we delegate and return
|
|
1756
|
+
* optimistically (confirmation rides the synced view). Otherwise the
|
|
1757
|
+
* built-in `OfflineQueue` resolves/rejects the returned promise on replay.
|
|
1758
|
+
*/
|
|
1759
|
+
private enqueueOfflineMutation;
|
|
1760
|
+
/**
|
|
1456
1761
|
* Restore offline mutations persisted in a prior session and open a socket
|
|
1457
1762
|
* for each shard they target so they flush once the WS reconnects. Failures
|
|
1458
1763
|
* are swallowed — a broken durable store must not stop the client booting.
|
|
@@ -1488,11 +1793,18 @@ declare class LunoraClient {
|
|
|
1488
1793
|
/** Recompute the aggregate status and notify listeners if it changed. */
|
|
1489
1794
|
private emitConnectionStatus;
|
|
1490
1795
|
/**
|
|
1491
|
-
* Apply an optimistic update to
|
|
1492
|
-
*
|
|
1493
|
-
*
|
|
1494
|
-
*
|
|
1495
|
-
*
|
|
1796
|
+
* Apply an optimistic update to the subscription that matches the mutation's
|
|
1797
|
+
* `(functionRef, args, shardKey)` triple, returning the rollback callbacks to
|
|
1798
|
+
* invoke if the mutation later fails.
|
|
1799
|
+
*
|
|
1800
|
+
* The registry is already indexed by exactly this triple via
|
|
1801
|
+
* `SubscriptionRegistry.key`, so at most one subscription can match. A direct
|
|
1802
|
+
* O(1) keyed lookup replaces the former O(N) linear scan over all subscriptions.
|
|
1803
|
+
*
|
|
1804
|
+
* `shardKey` normalization: both `undefined` and `""` map to the empty string
|
|
1805
|
+
* inside `SubscriptionRegistry.key` (via `?? ""`), so a mutation fired without
|
|
1806
|
+
* a shardKey correctly matches a subscription registered without one regardless
|
|
1807
|
+
* of whether the caller passed `undefined` or omitted the field.
|
|
1496
1808
|
*/
|
|
1497
1809
|
private applyOptimisticUpdates;
|
|
1498
1810
|
/**
|
|
@@ -1549,6 +1861,13 @@ declare class LunoraClient {
|
|
|
1549
1861
|
* to the lifecycle dispatch.
|
|
1550
1862
|
*/
|
|
1551
1863
|
private sendConnectEnvelope;
|
|
1864
|
+
/**
|
|
1865
|
+
* Re-send every shape subscription bound to `shardKey` over its (now open)
|
|
1866
|
+
* socket. Each frame carries the shape's last applied checkpoint, so the
|
|
1867
|
+
* server resumes from it — or re-seeds when the cursor fell below CDC
|
|
1868
|
+
* retention or the epoch forked.
|
|
1869
|
+
*/
|
|
1870
|
+
private resendShapeSubscriptions;
|
|
1552
1871
|
private ensureSocket;
|
|
1553
1872
|
private handleDisconnect;
|
|
1554
1873
|
/**
|
|
@@ -1564,8 +1883,14 @@ declare class LunoraClient {
|
|
|
1564
1883
|
/** Mark every subscription bound to `shardKey` as needing a fresh ack. */
|
|
1565
1884
|
private markShardPendingAck;
|
|
1566
1885
|
private sendSubscribeIfOpen;
|
|
1886
|
+
private sendShapeSubscribeIfOpen;
|
|
1567
1887
|
private handleServerMessage;
|
|
1568
1888
|
private handleErrorMessage;
|
|
1889
|
+
private handlePokeStart;
|
|
1890
|
+
private handlePokePart;
|
|
1891
|
+
private handlePokeEnd;
|
|
1892
|
+
/** Materialize a shape's keyed view to an array and invoke its callbacks. */
|
|
1893
|
+
private emitShapeRows;
|
|
1569
1894
|
private handleDataMessage;
|
|
1570
1895
|
/**
|
|
1571
1896
|
* Handle a `resume` frame (Pillar 1b): the server proved nothing the
|
|
@@ -1620,4 +1945,4 @@ declare class LunoraClient {
|
|
|
1620
1945
|
private clearQueryCacheForIdentityChange;
|
|
1621
1946
|
private flushOfflineQueue;
|
|
1622
1947
|
}
|
|
1623
|
-
export { ArgsOf as A, BookmarkStorage as B, CachedQuery as C, DEFAULT_MAX_BUFFER as D,
|
|
1948
|
+
export { SyncWatermark as $, ArgsOf as A, BookmarkStorage as B, CachedQuery as C, DEFAULT_MAX_BUFFER as D, ServerMessage as E, FunctionReference as F, GlobalFacetResult as G, ServerPokeEndMessage as H, ServerPokePartMessage as I, ServerPokeStartMessage as J, ShardTrafficEntry as K, LunoraClient as L, MutationCallOptions as M, ShardTrafficResult as N, OfflineQueueOptions as O, Preloaded as P, QueryCacheAdapter as Q, ReturnOf as R, SubscriptionError as S, StorageListPage as T, User as U, StorageObject as V, StreamHandle as W, StreamIterable as X, SubscriptionCallback as Y, SubscriptionRegistry as Z, SubscriptionState as _, Unsubscribe as a, WorkflowInstanceAction as a0, WorkflowInstanceDetail as a1, WorkflowInstancePage as a2, WorkflowInstanceStatus as a3, WorkflowInstanceSummary as a4, WorkflowStepDetail as a5, createLocalStore as a6, createStream as a7, SubscriptionErrorCallback as b, PersistenceAdapter as c, ReconnectOptions as d, ClientMessage as e, ClientShapeSubscribeMessage as f, ClientShapeUnsubscribeMessage as g, ConnectionStatus as h, FunctionArgumentDescriptor as i, FunctionDescriptor as j, GlobalFacetValue as k, GlobalFilterClause as l, GlobalTableInfo as m, GlobalTablePage as n, LunoraClientOptions as o, OptimisticLocalStore as p, OptimisticUpdate as q, OutboxMutation as r, OutboxSink as s, PersistedMutation as t, RowOp as u, RpcEnvelope as v, RpcResponseBody as w, ScheduleRecord as x, SchedulerPoolStatus as y, SchedulerStatus as z };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { F as FunctionReference, L as LunoraClient, A as ArgsOf, R as ReturnOf, P as Preloaded } from "./lunora-client.d-
|
|
1
|
+
import { F as FunctionReference, L as LunoraClient, A as ArgsOf, R as ReturnOf, P as Preloaded } from "./lunora-client.d-DzEWBYeP.js";
|
|
2
2
|
/**
|
|
3
3
|
* Run a query once on the server (during SSR) and capture its result in a
|
|
4
4
|
* serializable {@link Preloaded} token. Embed the token in the rendered HTML and
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { F as FunctionReference, L as LunoraClient, A as ArgsOf, R as ReturnOf, P as Preloaded } from "./lunora-client.d-
|
|
1
|
+
import { F as FunctionReference, L as LunoraClient, A as ArgsOf, R as ReturnOf, P as Preloaded } from "./lunora-client.d-DzEWBYeP.mjs";
|
|
2
2
|
/**
|
|
3
3
|
* Run a query once on the server (during SSR) and capture its result in a
|
|
4
4
|
* serializable {@link Preloaded} token. Embed the token in the rendered HTML and
|
package/dist/query/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { S as SubscriptionError, F as FunctionReference, A as ArgsOf, R as ReturnOf, L as LunoraClient, a as Unsubscribe } from "../packem_shared/lunora-client.d-
|
|
2
|
-
export type { b as SubscriptionErrorCallback } from "../packem_shared/lunora-client.d-
|
|
1
|
+
import { S as SubscriptionError, F as FunctionReference, A as ArgsOf, R as ReturnOf, L as LunoraClient, a as Unsubscribe } from "../packem_shared/lunora-client.d-DzEWBYeP.mjs";
|
|
2
|
+
export type { b as SubscriptionErrorCallback } from "../packem_shared/lunora-client.d-DzEWBYeP.mjs";
|
|
3
3
|
import '@lunora/runtime';
|
|
4
4
|
/**
|
|
5
5
|
* The sentinel a framework adapter resolves its reactive args to when it wants
|
package/dist/query/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { S as SubscriptionError, F as FunctionReference, A as ArgsOf, R as ReturnOf, L as LunoraClient, a as Unsubscribe } from "../packem_shared/lunora-client.d-
|
|
2
|
-
export type { b as SubscriptionErrorCallback } from "../packem_shared/lunora-client.d-
|
|
1
|
+
import { S as SubscriptionError, F as FunctionReference, A as ArgsOf, R as ReturnOf, L as LunoraClient, a as Unsubscribe } from "../packem_shared/lunora-client.d-DzEWBYeP.js";
|
|
2
|
+
export type { b as SubscriptionErrorCallback } from "../packem_shared/lunora-client.d-DzEWBYeP.js";
|
|
3
3
|
import '@lunora/runtime';
|
|
4
4
|
/**
|
|
5
5
|
* The sentinel a framework adapter resolves its reactive args to when it wants
|
package/dist/ssr/index.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { P as Preloaded, L as LunoraClient } from "../packem_shared/lunora-client.d-
|
|
2
|
-
export type { A as ArgsOf, F as FunctionReference, R as ReturnOf } from "../packem_shared/lunora-client.d-
|
|
3
|
-
export { p as preloadQuery, a as preloadedQueryResult } from "../packem_shared/preload.d-
|
|
1
|
+
import { P as Preloaded, L as LunoraClient } from "../packem_shared/lunora-client.d-DzEWBYeP.mjs";
|
|
2
|
+
export type { A as ArgsOf, F as FunctionReference, R as ReturnOf } from "../packem_shared/lunora-client.d-DzEWBYeP.mjs";
|
|
3
|
+
export { p as preloadQuery, a as preloadedQueryResult } from "../packem_shared/preload.d-DuYqEQ5J.mjs";
|
|
4
4
|
import '@lunora/runtime';
|
|
5
5
|
/**
|
|
6
6
|
* Structural shape of a better-auth `getSession` call's resolved value.
|
package/dist/ssr/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { P as Preloaded, L as LunoraClient } from "../packem_shared/lunora-client.d-
|
|
2
|
-
export type { A as ArgsOf, F as FunctionReference, R as ReturnOf } from "../packem_shared/lunora-client.d-
|
|
3
|
-
export { p as preloadQuery, a as preloadedQueryResult } from "../packem_shared/preload.d-
|
|
1
|
+
import { P as Preloaded, L as LunoraClient } from "../packem_shared/lunora-client.d-DzEWBYeP.js";
|
|
2
|
+
export type { A as ArgsOf, F as FunctionReference, R as ReturnOf } from "../packem_shared/lunora-client.d-DzEWBYeP.js";
|
|
3
|
+
export { p as preloadQuery, a as preloadedQueryResult } from "../packem_shared/preload.d-CmMMC-wm.js";
|
|
4
4
|
import '@lunora/runtime';
|
|
5
5
|
/**
|
|
6
6
|
* Structural shape of a better-auth `getSession` call's resolved value.
|
package/dist/ssr/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { getServerSession } from '../packem_shared/getServerSession-8jXewqxd.mjs';
|
|
2
2
|
export { deserializePreloaded, serializePreloaded } from '../packem_shared/deserializePreloaded-C0eJTY_W.mjs';
|
|
3
|
-
export { createServerClient } from '../packem_shared/createServerClient-
|
|
3
|
+
export { createServerClient } from '../packem_shared/createServerClient-XahK8bPv.mjs';
|
|
4
4
|
export { preloadQuery, preloadedQueryResult } from '../packem_shared/preloadQuery-lobFkD2Z.mjs';
|