@absolutejs/sync 1.15.0 → 1.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/tanstack-db/index.js +45 -5
- package/dist/adapters/tanstack-db/index.js.map +5 -4
- package/dist/angular/index.js +45 -5
- package/dist/angular/index.js.map +5 -4
- package/dist/client/index.js +75 -17
- package/dist/client/index.js.map +8 -7
- package/dist/client/presence.d.ts +6 -0
- package/dist/client/syncClient.d.ts +8 -0
- package/dist/client/syncCollection.d.ts +6 -0
- package/dist/client/syncStore.d.ts +6 -0
- package/dist/engine/connection.d.ts +46 -3
- package/dist/engine/index.js +103 -21
- package/dist/engine/index.js.map +6 -5
- package/dist/engine/socket.d.ts +61 -7
- package/dist/engine/syncEngine.d.ts +30 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +165 -33
- package/dist/index.js.map +7 -6
- package/dist/react/index.js +45 -5
- package/dist/react/index.js.map +5 -4
- package/dist/serializer.d.ts +36 -0
- package/dist/svelte/index.js +45 -5
- package/dist/svelte/index.js.map +5 -4
- package/dist/testing.js +47 -11
- package/dist/testing.js.map +3 -3
- package/dist/vue/index.js +45 -5
- package/dist/vue/index.js.map +5 -4
- package/package.json +1 -1
package/dist/engine/socket.d.ts
CHANGED
|
@@ -1,6 +1,23 @@
|
|
|
1
1
|
import { Elysia } from 'elysia';
|
|
2
|
+
import type { SyncConnectionStats } from './connection';
|
|
2
3
|
import type { PresenceHub } from './presence';
|
|
3
4
|
import type { SyncEngine } from './syncEngine';
|
|
5
|
+
import type { FrameSerializer } from '../serializer';
|
|
6
|
+
/**
|
|
7
|
+
* Diagnostic surfaced via {@link SyncSocketOptions.onSlow} when a connection
|
|
8
|
+
* trips the WS backpressure threshold. The host can log, kick, or charge the
|
|
9
|
+
* tenant extra via the meter.
|
|
10
|
+
*/
|
|
11
|
+
export type SlowConnectionEvent = {
|
|
12
|
+
/** Stable per-connection id from Elysia's `ws.id`. */
|
|
13
|
+
wsId: string;
|
|
14
|
+
/** Bytes the WS currently has queued waiting to send. */
|
|
15
|
+
bufferedAmount: number;
|
|
16
|
+
/** Per-connection counters at the moment of detection. */
|
|
17
|
+
stats: SyncConnectionStats;
|
|
18
|
+
/** Why the event fired. */
|
|
19
|
+
reason: 'buffer-threshold' | 'send-backpressure';
|
|
20
|
+
};
|
|
4
21
|
export type SyncSocketOptions = {
|
|
5
22
|
/** The sync engine whose collections this socket serves. */
|
|
6
23
|
engine: SyncEngine;
|
|
@@ -15,19 +32,56 @@ export type SyncSocketOptions = {
|
|
|
15
32
|
* collection's `authorize`/`hydrate`/`match`. Defaults to an empty object.
|
|
16
33
|
*/
|
|
17
34
|
resolveContext?: (data: Record<string, unknown>) => unknown | Promise<unknown>;
|
|
35
|
+
/**
|
|
36
|
+
* Bytes threshold for the per-connection WS send buffer. When
|
|
37
|
+
* `ws.getBufferedAmount()` exceeds this, `onSlow` fires once per
|
|
38
|
+
* crossing. Default `Infinity` (disabled).
|
|
39
|
+
*
|
|
40
|
+
* Added in 1.14.0.
|
|
41
|
+
*/
|
|
42
|
+
maxBufferedBytes?: number;
|
|
43
|
+
/**
|
|
44
|
+
* Fired when the per-connection WS buffer crosses `maxBufferedBytes`, OR
|
|
45
|
+
* when `ws.send()` returns `-1` (Bun's backpressure signal). The signal
|
|
46
|
+
* re-arms once the WS reports `drain`. Pair with `closeOnSlow: true` to
|
|
47
|
+
* kick slow clients automatically, or use this hook to charge the
|
|
48
|
+
* tenant extra via `@absolutejs/metering`.
|
|
49
|
+
*
|
|
50
|
+
* Added in 1.14.0.
|
|
51
|
+
*/
|
|
52
|
+
onSlow?: (event: SlowConnectionEvent) => void | Promise<void>;
|
|
53
|
+
/**
|
|
54
|
+
* Close the WS the first time a connection crosses `maxBufferedBytes`
|
|
55
|
+
* (or the `-1` send threshold). Default `false`. Client will reconnect
|
|
56
|
+
* and re-hydrate.
|
|
57
|
+
*
|
|
58
|
+
* Added in 1.14.0.
|
|
59
|
+
*/
|
|
60
|
+
closeOnSlow?: boolean;
|
|
61
|
+
/**
|
|
62
|
+
* Wire-format serializer (1.16.0). Default `jsonSerializer` —
|
|
63
|
+
* preserves the pre-1.16 behavior. Both ends of the connection MUST
|
|
64
|
+
* use the same serializer; opt into a binary one (msgpack, cbor, or
|
|
65
|
+
* a custom layout) on BOTH this plugin AND the client to cut the
|
|
66
|
+
* bandwidth + parse CPU.
|
|
67
|
+
*/
|
|
68
|
+
serializer?: FrameSerializer;
|
|
18
69
|
};
|
|
19
70
|
/**
|
|
20
|
-
* Elysia WebSocket plugin for the
|
|
21
|
-
* number of collection subscriptions: the client sends `subscribe
|
|
22
|
-
* frames and receives `snapshot
|
|
23
|
-
* {@link createSyncConnection}). Mount
|
|
24
|
-
* from your mutations.
|
|
71
|
+
* Elysia WebSocket plugin for the sync engine. One socket multiplexes any
|
|
72
|
+
* number of collection subscriptions: the client sends `subscribe` /
|
|
73
|
+
* `unsubscribe` frames and receives `snapshot` / `diff` / `error` frames
|
|
74
|
+
* (see {@link createSyncConnection}). Mount once and drive
|
|
75
|
+
* `engine.applyChange` from your mutations.
|
|
25
76
|
*
|
|
26
77
|
* Uses Elysia's first-class `.ws()` rather than a hand-rolled stream — the
|
|
27
|
-
* bidirectional channel carries both subscriptions and
|
|
78
|
+
* bidirectional channel carries both subscriptions and mutations, and
|
|
28
79
|
* `ws.send` serializes frames for us.
|
|
80
|
+
*
|
|
81
|
+
* 1.14.0 adds WS-layer slow-client detection — see `maxBufferedBytes` /
|
|
82
|
+
* `onSlow` / `closeOnSlow`.
|
|
29
83
|
*/
|
|
30
|
-
export declare const syncSocket: ({ engine, path, resolveContext, presence }: SyncSocketOptions) => Elysia<"", {
|
|
84
|
+
export declare const syncSocket: ({ engine, path, resolveContext, presence, maxBufferedBytes, onSlow, closeOnSlow, serializer }: SyncSocketOptions) => Elysia<"", {
|
|
31
85
|
decorator: {};
|
|
32
86
|
store: {};
|
|
33
87
|
derive: {};
|
|
@@ -27,6 +27,16 @@ export type CrdtFields<Row> = {
|
|
|
27
27
|
export declare class UnauthorizedError extends Error {
|
|
28
28
|
constructor(subject: string);
|
|
29
29
|
}
|
|
30
|
+
/**
|
|
31
|
+
* Thrown by `engine.subscribe` / `engine.hydrate` (1.15.0+) when the caller's
|
|
32
|
+
* `AbortSignal` fires before the operation reaches a `Subscription` /
|
|
33
|
+
* resolved value. The `name` is `'AbortError'` to match the DOM-standard
|
|
34
|
+
* spelling so existing `catch (error) { if (error.name === 'AbortError') ... }`
|
|
35
|
+
* patterns work unchanged.
|
|
36
|
+
*/
|
|
37
|
+
export declare class AbortError extends Error {
|
|
38
|
+
constructor(reason?: string);
|
|
39
|
+
}
|
|
30
40
|
/**
|
|
31
41
|
* Thrown when a mutation's write fails its table's schema (see
|
|
32
42
|
* {@link defineSchema}). The message names the offending field.
|
|
@@ -50,6 +60,19 @@ export type SubscribeArgs<T, P, Ctx> = {
|
|
|
50
60
|
* snapshot.
|
|
51
61
|
*/
|
|
52
62
|
since?: number;
|
|
63
|
+
/**
|
|
64
|
+
* Cancellation handle (1.15.0). Two effects:
|
|
65
|
+
* 1. If the signal is already aborted when `subscribe` is called, the
|
|
66
|
+
* engine throws {@link AbortError} immediately — no authorize, no
|
|
67
|
+
* hydrate, no subscription.
|
|
68
|
+
* 2. If the signal fires AFTER the subscription is live, the engine
|
|
69
|
+
* auto-calls `unsubscribe()`. The consumer never has to thread two
|
|
70
|
+
* handles for the same lifetime.
|
|
71
|
+
*
|
|
72
|
+
* Backwards-compatible — omit `signal` and the engine behaves exactly
|
|
73
|
+
* as in pre-1.15.0.
|
|
74
|
+
*/
|
|
75
|
+
signal?: AbortSignal;
|
|
53
76
|
};
|
|
54
77
|
export type Subscription<T> = {
|
|
55
78
|
/** The result set at subscribe time — a snapshot (empty when resuming). */
|
|
@@ -97,8 +120,14 @@ export type SyncEngine = {
|
|
|
97
120
|
* One-shot read: authorize and return a collection's current rows without
|
|
98
121
|
* subscribing. Powers an Eden-typed HTTP hydrate route (and SSR). Rejects
|
|
99
122
|
* with {@link UnauthorizedError} on deny.
|
|
123
|
+
*
|
|
124
|
+
* Pass `options.signal` (1.15.0+) to cancel the operation mid-flight —
|
|
125
|
+
* the engine throws {@link AbortError} after the next await point if
|
|
126
|
+
* the signal has fired.
|
|
100
127
|
*/
|
|
101
|
-
hydrate: (collection: string, params: unknown, ctx: unknown
|
|
128
|
+
hydrate: (collection: string, params: unknown, ctx: unknown, options?: {
|
|
129
|
+
signal?: AbortSignal;
|
|
130
|
+
}) => Promise<unknown[]>;
|
|
102
131
|
/**
|
|
103
132
|
* Feed a committed change to `table` into the engine, fanning the resulting
|
|
104
133
|
* diff to every live subscription of every collection that reads that table.
|
package/dist/index.d.ts
CHANGED
|
@@ -5,7 +5,9 @@ export type { ReactiveEvent, ReactiveHub, ReactiveListener } from './reactiveHub
|
|
|
5
5
|
export { sync } from './plugin';
|
|
6
6
|
export type { SyncPluginOptions, SyncRequestContext } from './plugin';
|
|
7
7
|
export { syncSocket } from './engine/socket';
|
|
8
|
-
export type { SyncSocketOptions } from './engine/socket';
|
|
8
|
+
export type { SlowConnectionEvent, SyncSocketOptions } from './engine/socket';
|
|
9
|
+
export { jsonSerializer } from './serializer';
|
|
10
|
+
export type { FrameSerializer } from './serializer';
|
|
9
11
|
export { syncCdc } from './engine/cdc';
|
|
10
12
|
export type { SyncCdcOptions } from './engine/cdc';
|
|
11
13
|
export { syncDevtools } from './devtools';
|
package/dist/index.js
CHANGED
|
@@ -187,15 +187,43 @@ var sync = ({
|
|
|
187
187
|
// src/engine/socket.ts
|
|
188
188
|
import { Elysia as Elysia2 } from "elysia";
|
|
189
189
|
|
|
190
|
+
// src/serializer.ts
|
|
191
|
+
var jsonSerializer = {
|
|
192
|
+
decode: (raw) => {
|
|
193
|
+
if (typeof raw === "string") {
|
|
194
|
+
try {
|
|
195
|
+
return JSON.parse(raw);
|
|
196
|
+
} catch {
|
|
197
|
+
return null;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
if (raw instanceof Uint8Array) {
|
|
201
|
+
try {
|
|
202
|
+
return JSON.parse(new TextDecoder().decode(raw));
|
|
203
|
+
} catch {
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
if (raw instanceof ArrayBuffer) {
|
|
208
|
+
try {
|
|
209
|
+
return JSON.parse(new TextDecoder().decode(new Uint8Array(raw)));
|
|
210
|
+
} catch {
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
return raw;
|
|
215
|
+
},
|
|
216
|
+
encodeClient: (frame) => JSON.stringify(frame),
|
|
217
|
+
encodeServer: (frame) => JSON.stringify(frame)
|
|
218
|
+
};
|
|
219
|
+
|
|
190
220
|
// src/engine/connection.ts
|
|
191
|
-
var parseFrame = (raw) => {
|
|
221
|
+
var parseFrame = (raw, serializer) => {
|
|
192
222
|
let value = raw;
|
|
193
|
-
if (typeof value === "string") {
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
} catch {
|
|
223
|
+
if (typeof value === "string" || value instanceof Uint8Array || value instanceof ArrayBuffer) {
|
|
224
|
+
value = serializer.decode(raw);
|
|
225
|
+
if (value === null)
|
|
197
226
|
return;
|
|
198
|
-
}
|
|
199
227
|
}
|
|
200
228
|
if (typeof value !== "object" || value === null) {
|
|
201
229
|
return;
|
|
@@ -240,11 +268,23 @@ var parseFrame = (raw) => {
|
|
|
240
268
|
var createSyncConnection = ({
|
|
241
269
|
engine,
|
|
242
270
|
ctx,
|
|
243
|
-
send,
|
|
244
|
-
presence
|
|
271
|
+
send: rawSend,
|
|
272
|
+
presence,
|
|
273
|
+
serializer = jsonSerializer
|
|
245
274
|
}) => {
|
|
246
275
|
const subscriptions = new Map;
|
|
247
276
|
const presenceRooms = new Map;
|
|
277
|
+
let framesSent = 0;
|
|
278
|
+
let slowSendsRecent = 0;
|
|
279
|
+
const send = (frame) => {
|
|
280
|
+
const ret = rawSend(frame);
|
|
281
|
+
if (ret === -1) {
|
|
282
|
+
slowSendsRecent += 1;
|
|
283
|
+
} else {
|
|
284
|
+
framesSent += 1;
|
|
285
|
+
slowSendsRecent = 0;
|
|
286
|
+
}
|
|
287
|
+
};
|
|
248
288
|
let pending = [];
|
|
249
289
|
let pendingVersion;
|
|
250
290
|
let flushScheduled = false;
|
|
@@ -289,7 +329,7 @@ var createSyncConnection = ({
|
|
|
289
329
|
scheduleFlush();
|
|
290
330
|
};
|
|
291
331
|
const handle = async (raw) => {
|
|
292
|
-
const frame = parseFrame(raw);
|
|
332
|
+
const frame = parseFrame(raw, serializer);
|
|
293
333
|
if (frame === undefined) {
|
|
294
334
|
send({ type: "error", message: "Malformed sync frame" });
|
|
295
335
|
return;
|
|
@@ -406,7 +446,13 @@ var createSyncConnection = ({
|
|
|
406
446
|
}
|
|
407
447
|
presenceRooms.clear();
|
|
408
448
|
};
|
|
409
|
-
|
|
449
|
+
const stats = () => ({
|
|
450
|
+
framesSent,
|
|
451
|
+
presenceRoomCount: presenceRooms.size,
|
|
452
|
+
slowSendsRecent,
|
|
453
|
+
subscriptionCount: subscriptions.size
|
|
454
|
+
});
|
|
455
|
+
return { close, handle, stats };
|
|
410
456
|
};
|
|
411
457
|
|
|
412
458
|
// src/engine/socket.ts
|
|
@@ -414,27 +460,76 @@ var syncSocket = ({
|
|
|
414
460
|
engine,
|
|
415
461
|
path = "/sync/ws",
|
|
416
462
|
resolveContext,
|
|
417
|
-
presence
|
|
463
|
+
presence,
|
|
464
|
+
maxBufferedBytes,
|
|
465
|
+
onSlow,
|
|
466
|
+
closeOnSlow = false,
|
|
467
|
+
serializer = jsonSerializer
|
|
418
468
|
}) => {
|
|
419
469
|
const connections = new Map;
|
|
470
|
+
const threshold = maxBufferedBytes ?? Infinity;
|
|
471
|
+
const fireSlow = (event) => {
|
|
472
|
+
if (!onSlow)
|
|
473
|
+
return;
|
|
474
|
+
try {
|
|
475
|
+
const ret = onSlow(event);
|
|
476
|
+
if (ret && typeof ret.then === "function") {
|
|
477
|
+
ret.catch((error) => {
|
|
478
|
+
console.error("[sync/socket] onSlow rejected:", error);
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
} catch (error) {
|
|
482
|
+
console.error("[sync/socket] onSlow threw:", error);
|
|
483
|
+
}
|
|
484
|
+
};
|
|
420
485
|
return new Elysia2({ name: "@absolutejs/sync/socket" }).ws(path, {
|
|
421
486
|
async open(ws) {
|
|
422
487
|
const ctx = resolveContext ? await resolveContext(ws.data) : {};
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
488
|
+
const bunWs = ws;
|
|
489
|
+
const tracked = {
|
|
490
|
+
connection: createSyncConnection({
|
|
491
|
+
engine,
|
|
492
|
+
ctx,
|
|
493
|
+
presence,
|
|
494
|
+
serializer,
|
|
495
|
+
send: (frame) => {
|
|
496
|
+
const payload = serializer.encodeServer(frame);
|
|
497
|
+
const ret = bunWs.send(typeof payload === "string" ? payload : payload);
|
|
498
|
+
const buffered = bunWs.getBufferedAmount?.() ?? 0;
|
|
499
|
+
const overBuffer = buffered > threshold;
|
|
500
|
+
const backpressure = ret === -1;
|
|
501
|
+
if ((overBuffer || backpressure) && !tracked.slowSignaled) {
|
|
502
|
+
tracked.slowSignaled = true;
|
|
503
|
+
fireSlow({
|
|
504
|
+
bufferedAmount: buffered,
|
|
505
|
+
reason: backpressure ? "send-backpressure" : "buffer-threshold",
|
|
506
|
+
stats: tracked.connection.stats(),
|
|
507
|
+
wsId: bunWs.id
|
|
508
|
+
});
|
|
509
|
+
if (closeOnSlow)
|
|
510
|
+
bunWs.close?.();
|
|
511
|
+
}
|
|
512
|
+
return ret;
|
|
513
|
+
}
|
|
514
|
+
}),
|
|
515
|
+
slowSignaled: false
|
|
516
|
+
};
|
|
517
|
+
connections.set(bunWs.id, tracked);
|
|
431
518
|
},
|
|
432
519
|
async message(ws, message) {
|
|
433
|
-
await connections.get(ws.id)?.handle(message);
|
|
520
|
+
await connections.get(ws.id)?.connection.handle(message);
|
|
521
|
+
},
|
|
522
|
+
drain(ws) {
|
|
523
|
+
const tracked = connections.get(ws.id);
|
|
524
|
+
if (tracked)
|
|
525
|
+
tracked.slowSignaled = false;
|
|
434
526
|
},
|
|
435
527
|
close(ws) {
|
|
436
|
-
connections.get(ws.id)
|
|
437
|
-
|
|
528
|
+
const tracked = connections.get(ws.id);
|
|
529
|
+
if (tracked) {
|
|
530
|
+
tracked.connection.close();
|
|
531
|
+
connections.delete(ws.id);
|
|
532
|
+
}
|
|
438
533
|
}
|
|
439
534
|
});
|
|
440
535
|
};
|
|
@@ -994,6 +1089,32 @@ class UnauthorizedError extends Error {
|
|
|
994
1089
|
}
|
|
995
1090
|
}
|
|
996
1091
|
|
|
1092
|
+
class AbortError extends Error {
|
|
1093
|
+
constructor(reason) {
|
|
1094
|
+
super(reason ?? "Aborted");
|
|
1095
|
+
this.name = "AbortError";
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
var checkAborted = (signal) => {
|
|
1099
|
+
if (signal?.aborted) {
|
|
1100
|
+
throw new AbortError(signal.reason instanceof Error ? signal.reason.message : typeof signal.reason === "string" ? signal.reason : "Aborted");
|
|
1101
|
+
}
|
|
1102
|
+
};
|
|
1103
|
+
var linkAbortToUnsubscribe = (signal, unsubscribe) => {
|
|
1104
|
+
if (signal === undefined)
|
|
1105
|
+
return;
|
|
1106
|
+
if (signal.aborted) {
|
|
1107
|
+
unsubscribe();
|
|
1108
|
+
return;
|
|
1109
|
+
}
|
|
1110
|
+
const handler = () => {
|
|
1111
|
+
try {
|
|
1112
|
+
unsubscribe();
|
|
1113
|
+
} catch {}
|
|
1114
|
+
};
|
|
1115
|
+
signal.addEventListener("abort", handler, { once: true });
|
|
1116
|
+
};
|
|
1117
|
+
|
|
997
1118
|
class SchemaError extends Error {
|
|
998
1119
|
constructor(table, fieldName) {
|
|
999
1120
|
super(`Schema violation on "${table}": invalid field "${fieldName}"`);
|
|
@@ -1856,29 +1977,35 @@ var createSyncEngine = (options = {}) => {
|
|
|
1856
1977
|
registerSearch: (collection) => {
|
|
1857
1978
|
registry.set(collection.name, collection);
|
|
1858
1979
|
},
|
|
1859
|
-
subscribe: async ({ collection, params, ctx, onDiff, since }) => {
|
|
1980
|
+
subscribe: async ({ collection, params, ctx, onDiff, since, signal }) => {
|
|
1981
|
+
checkAborted(signal);
|
|
1860
1982
|
const registered = registry.get(collection);
|
|
1861
1983
|
if (registered === undefined) {
|
|
1862
1984
|
throw new Error(`Unknown collection "${collection}"`);
|
|
1863
1985
|
}
|
|
1864
1986
|
const typedOnDiff = onDiff;
|
|
1865
1987
|
const subscribeSet = subsFor(collection);
|
|
1988
|
+
const wrapReturn = (sub) => {
|
|
1989
|
+
checkAborted(signal);
|
|
1990
|
+
linkAbortToUnsubscribe(signal, sub.unsubscribe);
|
|
1991
|
+
return sub;
|
|
1992
|
+
};
|
|
1866
1993
|
const registeredKind = registered.kind;
|
|
1867
1994
|
if (registeredKind === "join") {
|
|
1868
1995
|
const joined = await subscribeJoin(collection, registered, params, ctx, typedOnDiff, subscribeSet);
|
|
1869
|
-
return joined;
|
|
1996
|
+
return wrapReturn(joined);
|
|
1870
1997
|
}
|
|
1871
1998
|
if (registeredKind === "graph") {
|
|
1872
1999
|
const graphed = await subscribeGraph(collection, registered, params, ctx, typedOnDiff, subscribeSet);
|
|
1873
|
-
return graphed;
|
|
2000
|
+
return wrapReturn(graphed);
|
|
1874
2001
|
}
|
|
1875
2002
|
if (registeredKind === "reactive") {
|
|
1876
2003
|
const reactived = await subscribeReactive(collection, registered, params, ctx, typedOnDiff, subscribeSet);
|
|
1877
|
-
return reactived;
|
|
2004
|
+
return wrapReturn(reactived);
|
|
1878
2005
|
}
|
|
1879
2006
|
if (registeredKind === "search") {
|
|
1880
2007
|
const searched = await subscribeSearch(collection, registered, params, ctx, typedOnDiff, subscribeSet);
|
|
1881
|
-
return searched;
|
|
2008
|
+
return wrapReturn(searched);
|
|
1882
2009
|
}
|
|
1883
2010
|
const definition = registered;
|
|
1884
2011
|
if (definition.authorize !== undefined) {
|
|
@@ -1920,31 +2047,35 @@ var createSyncEngine = (options = {}) => {
|
|
|
1920
2047
|
subscribeSet.delete(subscription);
|
|
1921
2048
|
};
|
|
1922
2049
|
if (resuming) {
|
|
1923
|
-
return {
|
|
2050
|
+
return wrapReturn({
|
|
1924
2051
|
initial: [],
|
|
1925
2052
|
catchup: buildCatchup(since, tables, key, boundMatch),
|
|
1926
2053
|
version: atVersion,
|
|
1927
2054
|
unsubscribe
|
|
1928
|
-
};
|
|
2055
|
+
});
|
|
1929
2056
|
}
|
|
1930
|
-
return {
|
|
2057
|
+
return wrapReturn({
|
|
1931
2058
|
initial: view.rows(),
|
|
1932
2059
|
version: atVersion,
|
|
1933
2060
|
unsubscribe
|
|
1934
|
-
};
|
|
2061
|
+
});
|
|
1935
2062
|
},
|
|
1936
|
-
hydrate: async (collection, params, ctx) => {
|
|
2063
|
+
hydrate: async (collection, params, ctx, options2) => {
|
|
2064
|
+
const signal = options2?.signal;
|
|
2065
|
+
checkAborted(signal);
|
|
1937
2066
|
const definition = registry.get(collection);
|
|
1938
2067
|
if (definition === undefined) {
|
|
1939
2068
|
throw new Error(`Unknown collection "${collection}"`);
|
|
1940
2069
|
}
|
|
1941
2070
|
if (definition.authorize !== undefined) {
|
|
1942
2071
|
const allowed = await definition.authorize(params, ctx);
|
|
2072
|
+
checkAborted(signal);
|
|
1943
2073
|
if (!allowed) {
|
|
1944
2074
|
throw new UnauthorizedError(`hydrate collection "${collection}"`);
|
|
1945
2075
|
}
|
|
1946
2076
|
}
|
|
1947
2077
|
const raw = [...await definition.hydrate(params, ctx)];
|
|
2078
|
+
checkAborted(signal);
|
|
1948
2079
|
const tables = definition.tables ?? [collection];
|
|
1949
2080
|
const scopedTable = tables.length === 1 ? tables[0] : undefined;
|
|
1950
2081
|
const rows = scopedTable !== undefined ? raw.map((row) => migrateRow(scopedTable, row)) : raw;
|
|
@@ -2754,10 +2885,11 @@ export {
|
|
|
2754
2885
|
syncDevtools,
|
|
2755
2886
|
syncCdc,
|
|
2756
2887
|
sync,
|
|
2888
|
+
jsonSerializer,
|
|
2757
2889
|
createWriteBehindCache,
|
|
2758
2890
|
createReactiveHub,
|
|
2759
2891
|
createPresenceHub
|
|
2760
2892
|
};
|
|
2761
2893
|
|
|
2762
|
-
//# debugId=
|
|
2894
|
+
//# debugId=B96A4C67BAC0D9FB64756E2164756E21
|
|
2763
2895
|
//# sourceMappingURL=index.js.map
|