@syncular/client 0.1.3 → 0.2.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/README.md +227 -709
- package/dist/apply.d.ts +80 -0
- package/dist/apply.js +301 -0
- package/dist/blob.d.ts +127 -0
- package/dist/blob.js +201 -0
- package/dist/bun-database.d.ts +22 -0
- package/dist/bun-database.js +61 -0
- package/dist/client.d.ts +320 -69
- package/dist/client.js +1627 -163
- package/dist/content-type.d.ts +2 -0
- package/dist/content-type.js +2 -0
- package/dist/database.d.ts +35 -80
- package/dist/database.js +32 -947
- package/dist/encryption.d.ts +47 -0
- package/dist/encryption.js +75 -0
- package/dist/errors.d.ts +8 -22
- package/dist/errors.js +10 -207
- package/dist/http.d.ts +32 -0
- package/dist/http.js +196 -0
- package/dist/index.d.ts +28 -16
- package/dist/index.js +25 -12
- package/dist/invalidation.d.ts +69 -0
- package/dist/invalidation.js +84 -0
- package/dist/leader-lock.d.ts +28 -0
- package/dist/leader-lock.js +38 -0
- package/dist/multi-tab.d.ts +134 -0
- package/dist/multi-tab.js +399 -0
- package/dist/node-database.d.ts +41 -0
- package/dist/node-database.js +133 -0
- package/dist/outbox.d.ts +56 -0
- package/dist/outbox.js +140 -0
- package/dist/schema.d.ts +107 -0
- package/dist/schema.js +267 -0
- package/dist/state.d.ts +40 -0
- package/dist/state.js +77 -0
- package/dist/transport.d.ts +44 -0
- package/dist/transport.js +7 -0
- package/dist/wasm-database.d.ts +30 -0
- package/dist/wasm-database.js +170 -0
- package/dist/window.d.ts +64 -0
- package/dist/window.js +0 -0
- package/dist/worker-entry.d.ts +16 -2
- package/dist/worker-entry.js +300 -456
- package/dist/worker-host.d.ts +174 -0
- package/dist/worker-host.js +536 -0
- package/dist/worker-protocol.d.ts +173 -61
- package/dist/worker-protocol.js +7 -16
- package/package.json +45 -112
- package/src/apply.ts +435 -0
- package/src/blob.ts +333 -0
- package/src/bun-database.ts +75 -0
- package/src/client.ts +2247 -277
- package/src/content-type.ts +2 -0
- package/src/database.ts +63 -1389
- package/src/encryption.ts +123 -0
- package/src/errors.ts +11 -265
- package/src/http.ts +254 -0
- package/src/index.ts +25 -20
- package/src/invalidation.ts +128 -0
- package/src/leader-lock.ts +68 -0
- package/src/multi-tab.ts +550 -0
- package/src/node-database.ts +180 -0
- package/src/outbox.ts +206 -0
- package/src/schema.ts +397 -0
- package/src/state.ts +126 -0
- package/src/transport.ts +52 -0
- package/src/wasm-database.ts +279 -0
- package/src/window.ts +0 -0
- package/src/worker-entry.ts +391 -545
- package/src/worker-host.ts +800 -0
- package/src/worker-protocol.ts +204 -99
- package/dist/auth-leases.d.ts +0 -11
- package/dist/auth-leases.d.ts.map +0 -1
- package/dist/auth-leases.js +0 -140
- package/dist/auth-leases.js.map +0 -1
- package/dist/blob-limits.d.ts +0 -15
- package/dist/blob-limits.d.ts.map +0 -1
- package/dist/blob-limits.js +0 -66
- package/dist/blob-limits.js.map +0 -1
- package/dist/bridge-client.d.ts +0 -69
- package/dist/bridge-client.d.ts.map +0 -1
- package/dist/bridge-client.js +0 -282
- package/dist/bridge-client.js.map +0 -1
- package/dist/client-config.d.ts +0 -6
- package/dist/client-config.d.ts.map +0 -1
- package/dist/client-config.js +0 -18
- package/dist/client-config.js.map +0 -1
- package/dist/client.d.ts.map +0 -1
- package/dist/client.js.map +0 -1
- package/dist/command-history.d.ts +0 -35
- package/dist/command-history.d.ts.map +0 -1
- package/dist/command-history.js +0 -378
- package/dist/command-history.js.map +0 -1
- package/dist/console-diagnostics.d.ts +0 -33
- package/dist/console-diagnostics.d.ts.map +0 -1
- package/dist/console-diagnostics.js +0 -496
- package/dist/console-diagnostics.js.map +0 -1
- package/dist/crdt-yjs/index.d.ts +0 -4
- package/dist/crdt-yjs/index.d.ts.map +0 -1
- package/dist/crdt-yjs/index.js +0 -4
- package/dist/crdt-yjs/index.js.map +0 -1
- package/dist/crdt-yjs/webview-host-facade.d.ts +0 -126
- package/dist/crdt-yjs/webview-host-facade.d.ts.map +0 -1
- package/dist/crdt-yjs/webview-host-facade.js +0 -284
- package/dist/crdt-yjs/webview-host-facade.js.map +0 -1
- package/dist/crdt-yjs/yjs-document-field-adapter.d.ts +0 -153
- package/dist/crdt-yjs/yjs-document-field-adapter.d.ts.map +0 -1
- package/dist/crdt-yjs/yjs-document-field-adapter.js +0 -406
- package/dist/crdt-yjs/yjs-document-field-adapter.js.map +0 -1
- package/dist/crdt-yjs/yjs-prosemirror-bridge.d.ts +0 -73
- package/dist/crdt-yjs/yjs-prosemirror-bridge.d.ts.map +0 -1
- package/dist/crdt-yjs/yjs-prosemirror-bridge.js +0 -169
- package/dist/crdt-yjs/yjs-prosemirror-bridge.js.map +0 -1
- package/dist/database.d.ts.map +0 -1
- package/dist/database.js.map +0 -1
- package/dist/diagnostics.d.ts +0 -10
- package/dist/diagnostics.d.ts.map +0 -1
- package/dist/diagnostics.js +0 -83
- package/dist/diagnostics.js.map +0 -1
- package/dist/errors.d.ts.map +0 -1
- package/dist/errors.js.map +0 -1
- package/dist/generated-bridge.d.ts +0 -365
- package/dist/generated-bridge.d.ts.map +0 -1
- package/dist/generated-bridge.js +0 -250
- package/dist/generated-bridge.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/mutations.d.ts +0 -72
- package/dist/mutations.d.ts.map +0 -1
- package/dist/mutations.js +0 -63
- package/dist/mutations.js.map +0 -1
- package/dist/network.d.ts +0 -3
- package/dist/network.d.ts.map +0 -1
- package/dist/network.js +0 -17
- package/dist/network.js.map +0 -1
- package/dist/react/index.d.ts +0 -169
- package/dist/react/index.d.ts.map +0 -1
- package/dist/react/index.js +0 -628
- package/dist/react/index.js.map +0 -1
- package/dist/react-native/index.d.ts +0 -35
- package/dist/react-native/index.d.ts.map +0 -1
- package/dist/react-native/index.js +0 -49
- package/dist/react-native/index.js.map +0 -1
- package/dist/runtime-contract.d.ts +0 -13
- package/dist/runtime-contract.d.ts.map +0 -1
- package/dist/runtime-contract.js +0 -24
- package/dist/runtime-contract.js.map +0 -1
- package/dist/rust-client.d.ts +0 -106
- package/dist/rust-client.d.ts.map +0 -1
- package/dist/rust-client.js +0 -938
- package/dist/rust-client.js.map +0 -1
- package/dist/sentry.d.ts +0 -35
- package/dist/sentry.d.ts.map +0 -1
- package/dist/sentry.js +0 -155
- package/dist/sentry.js.map +0 -1
- package/dist/sql-safety.d.ts +0 -3
- package/dist/sql-safety.d.ts.map +0 -1
- package/dist/sql-safety.js +0 -62
- package/dist/sql-safety.js.map +0 -1
- package/dist/syncular-runtime-artifacts.json +0 -61
- package/dist/tauri/index.d.ts +0 -35
- package/dist/tauri/index.d.ts.map +0 -1
- package/dist/tauri/index.js +0 -114
- package/dist/tauri/index.js.map +0 -1
- package/dist/types.d.ts +0 -1096
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -2
- package/dist/types.js.map +0 -1
- package/dist/wasm/.syncular-wasm-profile +0 -1
- package/dist/wasm/syncular-runtime-artifact.json +0 -21
- package/dist/wasm/syncular.d.ts +0 -207
- package/dist/wasm/syncular.js +0 -2341
- package/dist/wasm/syncular_bg.wasm +0 -0
- package/dist/wasm/syncular_bg.wasm.d.ts +0 -97
- package/dist/wasm-bindings/runtime-contract.d.ts +0 -22
- package/dist/wasm-bindings/runtime-contract.d.ts.map +0 -1
- package/dist/wasm-bindings/runtime-contract.js +0 -112
- package/dist/wasm-bindings/runtime-contract.js.map +0 -1
- package/dist/wasm-core/.syncular-wasm-profile +0 -1
- package/dist/wasm-core/syncular-runtime-artifact.json +0 -17
- package/dist/wasm-core/syncular.d.ts +0 -162
- package/dist/wasm-core/syncular.js +0 -1847
- package/dist/wasm-core/syncular_bg.wasm +0 -0
- package/dist/wasm-core/syncular_bg.wasm.d.ts +0 -77
- package/dist/wasm-perf/.syncular-wasm-profile +0 -1
- package/dist/wasm-perf/syncular-runtime-artifact.json +0 -21
- package/dist/wasm-perf/syncular.d.ts +0 -207
- package/dist/wasm-perf/syncular.js +0 -2341
- package/dist/wasm-perf/syncular_bg.wasm +0 -0
- package/dist/wasm-perf/syncular_bg.wasm.d.ts +0 -97
- package/dist/wasm-runtime.d.ts +0 -23
- package/dist/wasm-runtime.d.ts.map +0 -1
- package/dist/wasm-runtime.js +0 -69
- package/dist/wasm-runtime.js.map +0 -1
- package/dist/worker-client.d.ts +0 -123
- package/dist/worker-client.d.ts.map +0 -1
- package/dist/worker-client.js +0 -1735
- package/dist/worker-client.js.map +0 -1
- package/dist/worker-entry.d.ts.map +0 -1
- package/dist/worker-entry.js.map +0 -1
- package/dist/worker-protocol.d.ts.map +0 -1
- package/dist/worker-protocol.js.map +0 -1
- package/dist/worker-realtime.d.ts +0 -39
- package/dist/worker-realtime.d.ts.map +0 -1
- package/dist/worker-realtime.js +0 -677
- package/dist/worker-realtime.js.map +0 -1
- package/src/auth-leases.ts +0 -251
- package/src/blob-limits.ts +0 -98
- package/src/bridge-client.ts +0 -512
- package/src/client-config.ts +0 -29
- package/src/command-history.ts +0 -623
- package/src/console-diagnostics.ts +0 -617
- package/src/crdt-yjs/index.ts +0 -3
- package/src/crdt-yjs/webview-host-facade.ts +0 -477
- package/src/crdt-yjs/yjs-document-field-adapter.ts +0 -733
- package/src/crdt-yjs/yjs-prosemirror-bridge.ts +0 -272
- package/src/diagnostics.ts +0 -116
- package/src/generated-bridge.ts +0 -741
- package/src/mutations.ts +0 -168
- package/src/network.ts +0 -32
- package/src/react/index.ts +0 -1036
- package/src/react-native/index.ts +0 -152
- package/src/runtime-contract.ts +0 -48
- package/src/rust-client.ts +0 -1491
- package/src/sentry.ts +0 -215
- package/src/sql-safety.ts +0 -61
- package/src/tauri/index.ts +0 -211
- package/src/types.ts +0 -1397
- package/src/wasm-bindings/generated-wasm-bindings.d.ts +0 -70
- package/src/wasm-bindings/runtime-contract.ts +0 -158
- package/src/wasm-runtime.ts +0 -145
- package/src/worker-client.ts +0 -2289
- package/src/worker-realtime.ts +0 -843
package/dist/worker-entry.js
CHANGED
|
@@ -1,479 +1,323 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
};
|
|
29
|
-
async function handleRequest(request) {
|
|
30
|
-
if (request.protocolVersion !== SYNCULAR_WORKER_PROTOCOL_VERSION) {
|
|
31
|
-
post({
|
|
32
|
-
id: request.id,
|
|
33
|
-
protocolVersion: SYNCULAR_WORKER_PROTOCOL_VERSION,
|
|
34
|
-
ok: false,
|
|
35
|
-
error: createSyncularWorkerErrorPayload('worker.protocol_mismatch', `Unsupported Syncular worker protocol ${request.protocolVersion}`, { details: { supported: SYNCULAR_WORKER_PROTOCOL_VERSION } }),
|
|
36
|
-
});
|
|
37
|
-
return;
|
|
1
|
+
/**
|
|
2
|
+
* Worker-side bootstrap (Direction decision 2): constructs the WHOLE
|
|
3
|
+
* client core — SyncClient + transports + realtime + SQLite — inside the
|
|
4
|
+
* worker and serves the `worker-protocol` RPC over postMessage.
|
|
5
|
+
*
|
|
6
|
+
* Browsers get the defaults: opfs-sahpool persistence and the fetch/WS
|
|
7
|
+
* transports from `./http`, wired from `WorkerInitConfig.endpoints`.
|
|
8
|
+
* Tests inject a database factory (bun:sqlite) through `overrides` — the
|
|
9
|
+
* same bootstrap, the same RPC, a different SQLite.
|
|
10
|
+
*
|
|
11
|
+
* The §8.4 host loop lives HERE: wake-ups (hello/`sync` events, delta
|
|
12
|
+
* drops) coalesce into one jittered `syncUntilIdle` round in the worker;
|
|
13
|
+
* RPC-driven and auto-driven sync rounds serialize on one queue because
|
|
14
|
+
* the core owns exactly one loop.
|
|
15
|
+
*/
|
|
16
|
+
import { SyncClient } from './client.js';
|
|
17
|
+
import { ClientSyncError } from './errors.js';
|
|
18
|
+
import { httpBlobTransport, httpSegmentDownloader, httpSyncTransport, webSocketRealtimeConnector, } from './http.js';
|
|
19
|
+
import { openPersistentWasmDatabase } from './wasm-database.js';
|
|
20
|
+
import { WORKER_FAILED_CODE, } from './worker-protocol.js';
|
|
21
|
+
function toErrorShape(error) {
|
|
22
|
+
if (error instanceof ClientSyncError) {
|
|
23
|
+
return {
|
|
24
|
+
code: error.code,
|
|
25
|
+
message: error.message,
|
|
26
|
+
retryable: error.retryable,
|
|
27
|
+
};
|
|
38
28
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
29
|
+
return {
|
|
30
|
+
code: WORKER_FAILED_CODE,
|
|
31
|
+
message: error instanceof Error ? error.message : String(error),
|
|
32
|
+
retryable: false,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* ArrayBuffers safe to transfer out of a query result: whole-buffer
|
|
37
|
+
* views only (a view into a larger/pooled buffer is cloned instead —
|
|
38
|
+
* transferring would detach unrelated data), deduplicated because a
|
|
39
|
+
* buffer may only appear once in a transfer list.
|
|
40
|
+
*/
|
|
41
|
+
function queryTransferables(rows) {
|
|
42
|
+
const buffers = new Set();
|
|
43
|
+
for (const row of rows) {
|
|
44
|
+
if (row === null || typeof row !== 'object')
|
|
45
|
+
continue;
|
|
46
|
+
for (const value of Object.values(row)) {
|
|
47
|
+
if (value instanceof Uint8Array &&
|
|
48
|
+
value.buffer instanceof ArrayBuffer &&
|
|
49
|
+
value.byteOffset === 0 &&
|
|
50
|
+
value.byteLength === value.buffer.byteLength) {
|
|
51
|
+
buffers.add(value.buffer);
|
|
52
|
+
}
|
|
51
53
|
}
|
|
52
|
-
post({
|
|
53
|
-
id: request.id,
|
|
54
|
-
protocolVersion: request.protocolVersion,
|
|
55
|
-
ok: true,
|
|
56
|
-
});
|
|
57
|
-
return;
|
|
58
54
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
55
|
+
return [...buffers];
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Register the RPC server on the current worker scope. Call this once
|
|
59
|
+
* from a worker entry script; the main-thread handle drives everything
|
|
60
|
+
* else through `init`/`call` messages.
|
|
61
|
+
*/
|
|
62
|
+
export function startSyncWorker(overrides = {}) {
|
|
63
|
+
const scope = globalThis;
|
|
64
|
+
const post = (message, transfer) => {
|
|
65
|
+
if (transfer !== undefined && transfer.length > 0) {
|
|
66
|
+
scope.postMessage(message, transfer);
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
scope.postMessage(message);
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
let client;
|
|
73
|
+
let database;
|
|
74
|
+
let offline = false;
|
|
75
|
+
let closed = false;
|
|
76
|
+
// -- one sync loop: RPC-driven and auto-driven rounds serialize ----------
|
|
77
|
+
let syncChain = Promise.resolve();
|
|
78
|
+
function serializedSync(fn) {
|
|
79
|
+
const next = syncChain.then(fn, fn);
|
|
80
|
+
syncChain = next.then(() => undefined, () => undefined);
|
|
81
|
+
return next;
|
|
63
82
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
83
|
+
// -- §8.4 host loop: coalesced, jittered, inside the worker --------------
|
|
84
|
+
let autoSync = true;
|
|
85
|
+
let wakeJitterMs = 200;
|
|
86
|
+
let autoSyncScheduled = false;
|
|
87
|
+
function scheduleAutoSync() {
|
|
88
|
+
if (!autoSync || autoSyncScheduled || closed || client === undefined) {
|
|
69
89
|
return;
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
90
|
+
}
|
|
91
|
+
autoSyncScheduled = true;
|
|
92
|
+
setTimeout(() => {
|
|
93
|
+
autoSyncScheduled = false;
|
|
94
|
+
if (closed || client === undefined)
|
|
95
|
+
return;
|
|
96
|
+
const running = client;
|
|
97
|
+
void serializedSync(() => running.syncUntilIdle())
|
|
98
|
+
.then((summary) => {
|
|
99
|
+
if (!closed)
|
|
100
|
+
post({ t: 'event', event: { kind: 'synced', summary } });
|
|
101
|
+
})
|
|
102
|
+
.catch((error) => {
|
|
103
|
+
if (!closed) {
|
|
104
|
+
post({
|
|
105
|
+
t: 'event',
|
|
106
|
+
event: { kind: 'synced', error: toErrorShape(error) },
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
}, Math.floor(Math.random() * wakeJitterMs));
|
|
111
|
+
}
|
|
112
|
+
function requireClient() {
|
|
113
|
+
if (client === undefined) {
|
|
114
|
+
throw new ClientSyncError(WORKER_FAILED_CODE, 'the worker received a call before init completed');
|
|
115
|
+
}
|
|
116
|
+
return client;
|
|
117
|
+
}
|
|
118
|
+
function assertOnline() {
|
|
119
|
+
if (offline) {
|
|
120
|
+
throw new ClientSyncError('sync.transport_failed', 'the worker transport is offline (setOffline(true))', true);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
function gateOffline(inner) {
|
|
124
|
+
return (request) => {
|
|
125
|
+
assertOnline();
|
|
126
|
+
return inner(request);
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
/** Offline gate that preserves the §5.4 `fetchUrl` capability marker. */
|
|
130
|
+
function gateSegmentsOffline(inner) {
|
|
131
|
+
const fetchUrl = inner.fetchUrl;
|
|
132
|
+
return Object.assign(gateOffline(inner), {
|
|
133
|
+
...(fetchUrl !== undefined
|
|
134
|
+
? {
|
|
135
|
+
fetchUrl: (url) => {
|
|
136
|
+
assertOnline();
|
|
137
|
+
return fetchUrl(url);
|
|
138
|
+
},
|
|
139
|
+
}
|
|
140
|
+
: {}),
|
|
75
141
|
});
|
|
76
|
-
postRequestSuccessDiagnostic(request, value, Date.now() - startedAt);
|
|
77
142
|
}
|
|
78
|
-
|
|
79
|
-
if (
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
?
|
|
95
|
-
:
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
143
|
+
async function init(config) {
|
|
144
|
+
if (client !== undefined) {
|
|
145
|
+
throw new ClientSyncError(WORKER_FAILED_CODE, 'the worker is already initialized');
|
|
146
|
+
}
|
|
147
|
+
autoSync = config.autoSync ?? true;
|
|
148
|
+
wakeJitterMs = config.wakeJitterMs ?? 200;
|
|
149
|
+
database =
|
|
150
|
+
overrides.openDatabase !== undefined
|
|
151
|
+
? await overrides.openDatabase(config)
|
|
152
|
+
: await defaultOpenDatabase(config);
|
|
153
|
+
const transport = gateOffline(overrides.createTransport !== undefined
|
|
154
|
+
? overrides.createTransport(config)
|
|
155
|
+
: httpSyncTransport(config.endpoints.syncUrl));
|
|
156
|
+
const segments = overrides.createSegments !== undefined
|
|
157
|
+
? overrides.createSegments(config)
|
|
158
|
+
: config.endpoints.segmentsUrl !== undefined
|
|
159
|
+
? httpSegmentDownloader(config.endpoints.segmentsUrl)
|
|
160
|
+
: undefined;
|
|
161
|
+
// §5.9 blob transport: only when a blobs URL is configured.
|
|
162
|
+
const blobs = config.endpoints.blobsUrl !== undefined
|
|
163
|
+
? httpBlobTransport(config.endpoints.blobsUrl)
|
|
164
|
+
: undefined;
|
|
165
|
+
// Realtime needs the (possibly persisted) clientId for the
|
|
166
|
+
// `{clientId}` URL placeholder — start the client first, then attach
|
|
167
|
+
// the connector through a mutable slot read at connect time.
|
|
168
|
+
let realtimeConnector;
|
|
169
|
+
const started = new SyncClient({
|
|
170
|
+
database,
|
|
171
|
+
schema: config.schema,
|
|
172
|
+
transport,
|
|
173
|
+
...(segments !== undefined
|
|
174
|
+
? { segments: gateSegmentsOffline(segments) }
|
|
175
|
+
: {}),
|
|
176
|
+
...(blobs !== undefined ? { blobs } : {}),
|
|
177
|
+
realtime: (handlers) => {
|
|
178
|
+
if (offline) {
|
|
179
|
+
throw new ClientSyncError('sync.transport_failed', 'the worker realtime channel is offline (setOffline(true))', true);
|
|
180
|
+
}
|
|
181
|
+
if (realtimeConnector === undefined) {
|
|
182
|
+
throw new ClientSyncError('sync.invalid_request', 'no realtime connector configured');
|
|
183
|
+
}
|
|
184
|
+
return realtimeConnector(handlers);
|
|
185
|
+
},
|
|
186
|
+
...(config.clientId !== undefined ? { clientId: config.clientId } : {}),
|
|
187
|
+
...(config.limits !== undefined ? { limits: config.limits } : {}),
|
|
188
|
+
onSyncNeeded: (reason) => {
|
|
189
|
+
post({ t: 'event', event: { kind: 'sync-needed', reason } });
|
|
190
|
+
scheduleAutoSync();
|
|
191
|
+
},
|
|
192
|
+
onConflict: (conflict) => {
|
|
193
|
+
post({ t: 'event', event: { kind: 'conflict', conflict } });
|
|
194
|
+
},
|
|
195
|
+
onUpgrading: (upgrading) => {
|
|
196
|
+
// §7.4.5: surface the schema-bump reset + completion to the UI thread.
|
|
197
|
+
post({ t: 'event', event: { kind: 'upgrading', upgrading } });
|
|
198
|
+
},
|
|
199
|
+
onPresence: (scopeKey) => {
|
|
200
|
+
// §8.6: surface a presence change to the UI thread.
|
|
201
|
+
post({ t: 'event', event: { kind: 'presence', scopeKey } });
|
|
110
202
|
},
|
|
111
|
-
...requestSyncAttemptDiagnosticFields(request),
|
|
112
203
|
});
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
204
|
+
// TODO 3.1 / I1: forward every coalesced apply-batch invalidation to the
|
|
205
|
+
// UI thread so `SyncClientHandle.onInvalidate` fires with worker parity.
|
|
206
|
+
started.onInvalidate((event) => {
|
|
207
|
+
if (!closed)
|
|
208
|
+
post({ t: 'event', event: { kind: 'invalidate', event } });
|
|
118
209
|
});
|
|
210
|
+
await started.start();
|
|
211
|
+
realtimeConnector =
|
|
212
|
+
overrides.createRealtime !== undefined
|
|
213
|
+
? overrides.createRealtime(config, started.clientId)
|
|
214
|
+
: config.endpoints.realtimeUrl !== undefined
|
|
215
|
+
? webSocketRealtimeConnector(config.endpoints.realtimeUrl.replace('{clientId}', encodeURIComponent(started.clientId)))
|
|
216
|
+
: undefined;
|
|
217
|
+
client = started;
|
|
218
|
+
return { clientId: started.clientId };
|
|
119
219
|
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
realtime.start(options);
|
|
132
|
-
return true;
|
|
220
|
+
const api = {
|
|
221
|
+
subscribe: (input) => requireClient().subscribe(input),
|
|
222
|
+
unsubscribe: (id) => requireClient().unsubscribe(id),
|
|
223
|
+
setWindow: async (base, units) => {
|
|
224
|
+
await requireClient().setWindow(base, units);
|
|
225
|
+
// A window change adds/removes value-sharded subscriptions; a widened
|
|
226
|
+
// unit needs a bootstrap pull to become visible. In autoSync mode the
|
|
227
|
+
// host loop owns rounds, so schedule one now — otherwise the new unit
|
|
228
|
+
// waits for an unrelated server wake-up (§8.4). A no-op change still
|
|
229
|
+
// schedules a harmless idempotent round.
|
|
230
|
+
scheduleAutoSync();
|
|
133
231
|
},
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
232
|
+
windowState: (base) => requireClient().windowState(base),
|
|
233
|
+
mutate: (mutations) => {
|
|
234
|
+
const id = requireClient().mutate(mutations);
|
|
235
|
+
// In autoSync mode the host loop owns rounds (§8.4): a local write must
|
|
236
|
+
// push without the app orchestrating sync, so schedule a jittered round
|
|
237
|
+
// to drain the outbox promptly. `mutate` is synchronous — its outbox
|
|
238
|
+
// write has committed before this line — and the round itself serializes
|
|
239
|
+
// on SyncClient's operation chain, so no ordering hack is needed here;
|
|
240
|
+
// the §8.4 jitter in `scheduleAutoSync` is the only deferral, and it
|
|
241
|
+
// coalesces bursts. (Historically this used setTimeout to dodge a
|
|
242
|
+
// nested-transaction flake; that race is fixed at the source now — the
|
|
243
|
+
// real cause was cross-operation interleaving, serialized in the core.)
|
|
244
|
+
scheduleAutoSync();
|
|
245
|
+
return id;
|
|
137
246
|
},
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
return
|
|
247
|
+
sync: () => {
|
|
248
|
+
const running = requireClient();
|
|
249
|
+
return serializedSync(() => running.sync());
|
|
141
250
|
},
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
}
|
|
146
|
-
async function openClient(request) {
|
|
147
|
-
const config = resolveSyncularClientConfig(request.config);
|
|
148
|
-
realtime.stop();
|
|
149
|
-
detachRowsChangedListener();
|
|
150
|
-
client?.close();
|
|
151
|
-
client = undefined;
|
|
152
|
-
openedConfig = undefined;
|
|
153
|
-
openedRuntime = undefined;
|
|
154
|
-
const runtime = request.runtime;
|
|
155
|
-
client = await openSyncularRustClient({
|
|
156
|
-
config,
|
|
157
|
-
module: runtime?.wasmGlueUrl
|
|
158
|
-
? loadWorkerWasmGlue(runtime.wasmGlueUrl)
|
|
159
|
-
: undefined,
|
|
160
|
-
wasmGlueUrl: runtime?.wasmGlueUrl,
|
|
161
|
-
wasmUrl: runtime?.wasmUrl,
|
|
162
|
-
});
|
|
163
|
-
attachRowsChangedListener(client);
|
|
164
|
-
openedConfig = config;
|
|
165
|
-
openedRuntime = runtime;
|
|
166
|
-
return true;
|
|
167
|
-
}
|
|
168
|
-
function closeClient() {
|
|
169
|
-
realtime.stop();
|
|
170
|
-
detachRowsChangedListener();
|
|
171
|
-
client?.close();
|
|
172
|
-
client = undefined;
|
|
173
|
-
openedConfig = undefined;
|
|
174
|
-
openedRuntime = undefined;
|
|
175
|
-
return true;
|
|
176
|
-
}
|
|
177
|
-
async function runtimeInfo() {
|
|
178
|
-
const wasmUrl = getSyncularWasmUrl();
|
|
179
|
-
const selectedWasmUrl = openedRuntime?.wasmUrl ?? wasmUrl;
|
|
180
|
-
const selectedWasmGlueUrl = openedRuntime?.wasmGlueUrl ?? getSyncularWasmGlueUrl();
|
|
181
|
-
return createSyncularRuntimeInfo({
|
|
182
|
-
storage: openedConfig?.storage,
|
|
183
|
-
workerUrl: typeof self.location?.href === 'string' ? self.location.href : '',
|
|
184
|
-
wasmGlueUrl: selectedWasmGlueUrl,
|
|
185
|
-
wasmUrl: selectedWasmUrl,
|
|
186
|
-
rust: await getSyncularRustRuntimeInfo(openedRuntime?.wasmGlueUrl
|
|
187
|
-
? loadWorkerWasmGlue(openedRuntime.wasmGlueUrl)
|
|
188
|
-
: undefined, selectedWasmUrl),
|
|
189
|
-
});
|
|
190
|
-
}
|
|
191
|
-
function loadWorkerWasmGlue(wasmGlueUrl) {
|
|
192
|
-
return import(/* @vite-ignore */ wasmGlueUrl);
|
|
193
|
-
}
|
|
194
|
-
function requireClient() {
|
|
195
|
-
if (!client) {
|
|
196
|
-
throw createSyncularWorkerErrorPayload('worker.not_open', 'Syncular worker client is not open');
|
|
197
|
-
}
|
|
198
|
-
return client;
|
|
199
|
-
}
|
|
200
|
-
function attachRowsChangedListener(nextClient) {
|
|
201
|
-
removeRowsChangedListener = nextClient.addRowsChangedListener((event) => {
|
|
202
|
-
post({
|
|
203
|
-
protocolVersion: SYNCULAR_WORKER_PROTOCOL_VERSION,
|
|
204
|
-
type: 'rowsChanged',
|
|
205
|
-
source: event.source,
|
|
206
|
-
changedTables: event.changedTables,
|
|
207
|
-
changedRows: event.changedRows,
|
|
208
|
-
changedRowsTruncated: event.changedRowsTruncated,
|
|
209
|
-
});
|
|
210
|
-
});
|
|
211
|
-
}
|
|
212
|
-
function detachRowsChangedListener() {
|
|
213
|
-
removeRowsChangedListener?.();
|
|
214
|
-
removeRowsChangedListener = undefined;
|
|
215
|
-
}
|
|
216
|
-
function post(message) {
|
|
217
|
-
self.postMessage(message);
|
|
218
|
-
}
|
|
219
|
-
function createAbortController(type) {
|
|
220
|
-
if (!isAbortableRequest(type) || typeof AbortController === 'undefined') {
|
|
221
|
-
return undefined;
|
|
222
|
-
}
|
|
223
|
-
return new AbortController();
|
|
224
|
-
}
|
|
225
|
-
function isAbortableRequest(type) {
|
|
226
|
-
return isGeneratedSyncularAbortableWorkerRequestType(type);
|
|
227
|
-
}
|
|
228
|
-
function clearClientAbortSignal() {
|
|
229
|
-
try {
|
|
230
|
-
client?.setAbortSignal(null);
|
|
231
|
-
}
|
|
232
|
-
catch {
|
|
233
|
-
// Best effort after a request completed, failed, or closed the client.
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
function postDiagnostic(event) {
|
|
237
|
-
self.postMessage({
|
|
238
|
-
protocolVersion: SYNCULAR_WORKER_PROTOCOL_VERSION,
|
|
239
|
-
type: 'diagnostic',
|
|
240
|
-
event: {
|
|
241
|
-
at: event.at ?? Date.now(),
|
|
242
|
-
level: event.level,
|
|
243
|
-
source: event.source,
|
|
244
|
-
code: event.code,
|
|
245
|
-
message: event.message,
|
|
246
|
-
...(event.syncAttemptId ? { syncAttemptId: event.syncAttemptId } : {}),
|
|
247
|
-
...(event.traceId ? { traceId: event.traceId } : {}),
|
|
248
|
-
...(event.spanId ? { spanId: event.spanId } : {}),
|
|
249
|
-
...(event.clientId ? { clientId: event.clientId } : {}),
|
|
250
|
-
...(event.subscriptionId ? { subscriptionId: event.subscriptionId } : {}),
|
|
251
|
-
...(event.table ? { table: event.table } : {}),
|
|
252
|
-
...(event.rowId ? { rowId: event.rowId } : {}),
|
|
253
|
-
...(event.cursor !== undefined ? { cursor: event.cursor } : {}),
|
|
254
|
-
...(event.details ? { details: event.details } : {}),
|
|
251
|
+
syncUntilIdle: (maxRounds) => {
|
|
252
|
+
const running = requireClient();
|
|
253
|
+
return serializedSync(() => running.syncUntilIdle(maxRounds));
|
|
255
254
|
},
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
255
|
+
query: (sql, params) => requireClient().query(sql, params),
|
|
256
|
+
conflicts: () => requireClient().conflicts,
|
|
257
|
+
rejections: () => requireClient().rejections,
|
|
258
|
+
schemaFloor: () => requireClient().schemaFloor,
|
|
259
|
+
leaseState: () => requireClient().leaseState,
|
|
260
|
+
upgrading: () => requireClient().upgrading,
|
|
261
|
+
syncNeeded: () => requireClient().syncNeeded,
|
|
262
|
+
pendingCommits: () => requireClient().pendingCommits(),
|
|
263
|
+
subscriptions: () => requireClient().subscriptions(),
|
|
264
|
+
subscription: (id) => requireClient().subscription(id),
|
|
265
|
+
connectRealtime: () => requireClient().connectRealtime(),
|
|
266
|
+
disconnectRealtime: () => requireClient().disconnectRealtime(),
|
|
267
|
+
setPresence: (scopeKey, doc) => requireClient().setPresence(scopeKey, doc),
|
|
268
|
+
presence: (scopeKey) => requireClient().presence(scopeKey),
|
|
269
|
+
uploadBlob: (bytes, options) => requireClient().uploadBlob(bytes, options),
|
|
270
|
+
fetchBlob: (blobIdOrRef) => requireClient().fetchBlob(blobIdOrRef),
|
|
271
|
+
setOffline: (value) => {
|
|
272
|
+
offline = value;
|
|
273
|
+
if (offline)
|
|
274
|
+
client?.disconnectRealtime();
|
|
272
275
|
},
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
details: {
|
|
285
|
-
requestType: request.type,
|
|
286
|
-
...syncRevocation,
|
|
287
|
-
},
|
|
288
|
-
});
|
|
276
|
+
close: async () => {
|
|
277
|
+
closed = true;
|
|
278
|
+
await client?.close();
|
|
279
|
+
database?.close();
|
|
280
|
+
client = undefined;
|
|
281
|
+
database = undefined;
|
|
282
|
+
},
|
|
283
|
+
};
|
|
284
|
+
async function dispatch(message) {
|
|
285
|
+
const method = api[message.method];
|
|
286
|
+
return await method.apply(api, message.args);
|
|
289
287
|
}
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
288
|
+
function run(id, fn, transfer) {
|
|
289
|
+
void (async () => {
|
|
290
|
+
try {
|
|
291
|
+
const value = await fn();
|
|
292
|
+
post({ t: 'result', id, value }, transfer !== undefined ? transfer(value) : undefined);
|
|
293
|
+
}
|
|
294
|
+
catch (error) {
|
|
295
|
+
post({ t: 'error', id, error: toErrorShape(error) });
|
|
296
|
+
}
|
|
297
|
+
})();
|
|
296
298
|
}
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
function requestDiagnosticSource(type) {
|
|
303
|
-
return generatedSyncularWorkerRequestDiagnosticSource(type);
|
|
304
|
-
}
|
|
305
|
-
function requestDiagnosticLevel(type, error) {
|
|
306
|
-
if (type === 'setAuthHeaders' || syncularErrorStatus(error))
|
|
307
|
-
return 'warn';
|
|
308
|
-
return 'error';
|
|
309
|
-
}
|
|
310
|
-
function requestSuccessDetails(request, value) {
|
|
311
|
-
switch (request.type) {
|
|
312
|
-
case 'open':
|
|
313
|
-
return {
|
|
314
|
-
storage: openedConfig?.storage,
|
|
315
|
-
clientId: openedConfig?.clientId,
|
|
316
|
-
};
|
|
317
|
-
case 'setAuthHeaders':
|
|
318
|
-
return { headerCount: Object.keys(request.headers).length };
|
|
319
|
-
case 'setFieldEncryption':
|
|
320
|
-
return {
|
|
321
|
-
enabled: request.config != null,
|
|
322
|
-
ruleCount: request.config?.rules.length ?? 0,
|
|
323
|
-
};
|
|
324
|
-
case 'setEncryptedCrdt':
|
|
325
|
-
return {
|
|
326
|
-
enabled: request.config != null,
|
|
327
|
-
keyCount: request.config ? Object.keys(request.config.keys).length : 0,
|
|
328
|
-
};
|
|
329
|
-
case 'setBlobEncryption':
|
|
330
|
-
return {
|
|
331
|
-
enabled: request.config != null,
|
|
332
|
-
keyCount: request.config ? Object.keys(request.config.keys).length : 0,
|
|
333
|
-
};
|
|
334
|
-
case 'syncPull':
|
|
335
|
-
case 'syncPush':
|
|
336
|
-
case 'syncOnce':
|
|
337
|
-
return syncResultDetails(value);
|
|
338
|
-
case 'storeBlob':
|
|
339
|
-
return { immediate: request.options?.immediate === true };
|
|
340
|
-
case 'pruneBlobCache':
|
|
341
|
-
return { maxBytes: request.maxBytes ?? null, prunedBytes: value };
|
|
342
|
-
case 'compactStorage':
|
|
343
|
-
return objectRecord(value);
|
|
344
|
-
case 'localHealthCheck': {
|
|
345
|
-
const report = objectRecord(value);
|
|
346
|
-
const findings = Array.isArray(report.findings) ? report.findings : [];
|
|
347
|
-
return {
|
|
348
|
-
ok: report.ok === true,
|
|
349
|
-
findingCount: findings.length,
|
|
350
|
-
};
|
|
351
|
-
}
|
|
352
|
-
case 'exportLocalSupportBundle': {
|
|
353
|
-
const bundle = objectRecord(value);
|
|
354
|
-
const health = objectRecord(bundle.health);
|
|
355
|
-
const findings = Array.isArray(health.findings) ? health.findings : [];
|
|
356
|
-
return {
|
|
357
|
-
redacted: bundle.redacted === true,
|
|
358
|
-
source: bundle.source,
|
|
359
|
-
findingCount: findings.length,
|
|
360
|
-
};
|
|
299
|
+
scope.addEventListener('message', (event) => {
|
|
300
|
+
const message = event.data;
|
|
301
|
+
if (message.t === 'init') {
|
|
302
|
+
run(message.id, () => init(message.config));
|
|
303
|
+
return;
|
|
361
304
|
}
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
case 'resetLocalSyncState':
|
|
367
|
-
return objectRecord(value);
|
|
368
|
-
case 'processBlobUploadQueue':
|
|
369
|
-
return objectRecord(value);
|
|
370
|
-
default:
|
|
371
|
-
return {};
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
function syncResultDetails(value) {
|
|
375
|
-
const result = objectRecord(value);
|
|
376
|
-
const changedTables = Array.isArray(result.changedTables)
|
|
377
|
-
? result.changedTables
|
|
378
|
-
: [];
|
|
379
|
-
const changedRows = Array.isArray(result.changedRows)
|
|
380
|
-
? result.changedRows
|
|
381
|
-
: [];
|
|
382
|
-
return {
|
|
383
|
-
changedTables,
|
|
384
|
-
changedRows,
|
|
385
|
-
changedRowsTruncated: result.changedRowsTruncated === true,
|
|
386
|
-
changedTableCount: changedTables.length,
|
|
387
|
-
changedRowCount: changedRows.length,
|
|
388
|
-
bootstrap: objectRecord(result.bootstrap),
|
|
389
|
-
pushedCommits: typeof result.pushedCommits === 'number' ? result.pushedCommits : 0,
|
|
390
|
-
};
|
|
391
|
-
}
|
|
392
|
-
function syncScopeRevocationDetails(value) {
|
|
393
|
-
const result = objectRecord(value);
|
|
394
|
-
const subscriptions = Array.isArray(result.subscriptions)
|
|
395
|
-
? result.subscriptions
|
|
396
|
-
: [];
|
|
397
|
-
const revokedSubscriptionIds = subscriptions
|
|
398
|
-
.filter((subscription) => {
|
|
399
|
-
return (subscription !== null &&
|
|
400
|
-
typeof subscription === 'object' &&
|
|
401
|
-
subscription.status === 'revoked' &&
|
|
402
|
-
typeof subscription.id === 'string');
|
|
403
|
-
})
|
|
404
|
-
.map((subscription) => subscription.id);
|
|
405
|
-
return revokedSubscriptionIds.length > 0
|
|
406
|
-
? {
|
|
407
|
-
revokedSubscriptionIds,
|
|
408
|
-
revokedSubscriptionCount: revokedSubscriptionIds.length,
|
|
305
|
+
if (message.t === 'call') {
|
|
306
|
+
run(message.id, () => dispatch(message), message.method === 'query'
|
|
307
|
+
? (value) => queryTransferables(value)
|
|
308
|
+
: undefined);
|
|
409
309
|
}
|
|
410
|
-
|
|
411
|
-
}
|
|
412
|
-
function diagnosticStatus(error) {
|
|
413
|
-
const status = syncularErrorStatus(error);
|
|
414
|
-
return status ? { status } : {};
|
|
415
|
-
}
|
|
416
|
-
function objectRecord(value) {
|
|
417
|
-
return value && typeof value === 'object'
|
|
418
|
-
? value
|
|
419
|
-
: {};
|
|
420
|
-
}
|
|
421
|
-
function errorMessage(error) {
|
|
422
|
-
return syncularErrorMessage(error);
|
|
423
|
-
}
|
|
424
|
-
function errorRequiresFullRefresh(error) {
|
|
425
|
-
return errorMessage(error).includes('full snapshot resync required');
|
|
310
|
+
});
|
|
311
|
+
post({ t: 'ready' });
|
|
426
312
|
}
|
|
427
|
-
function
|
|
428
|
-
if (
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
return {
|
|
432
|
-
code: error.code,
|
|
433
|
-
message: error.message,
|
|
434
|
-
category: error.category,
|
|
435
|
-
retryable: error.retryable,
|
|
436
|
-
recommendedAction: error.recommendedAction,
|
|
437
|
-
name: error.name,
|
|
438
|
-
stack: error.stack,
|
|
439
|
-
details: error.details,
|
|
440
|
-
};
|
|
313
|
+
async function defaultOpenDatabase(config) {
|
|
314
|
+
if (config.database.mode !== 'persistent') {
|
|
315
|
+
throw new ClientSyncError(WORKER_FAILED_CODE, "database mode 'custom' requires an openDatabase override in the " +
|
|
316
|
+
'worker bootstrap (startSyncWorker({ openDatabase }))');
|
|
441
317
|
}
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
name: error.name,
|
|
448
|
-
stack: error.stack,
|
|
449
|
-
details,
|
|
450
|
-
});
|
|
451
|
-
}
|
|
452
|
-
return {
|
|
453
|
-
code: classification.code,
|
|
454
|
-
message: error.message,
|
|
455
|
-
category: classification.category,
|
|
456
|
-
retryable: classification.retryable,
|
|
457
|
-
recommendedAction: classification.recommendedAction,
|
|
458
|
-
name: error.name,
|
|
459
|
-
stack: error.stack,
|
|
460
|
-
details,
|
|
461
|
-
};
|
|
462
|
-
}
|
|
463
|
-
const message = String(error);
|
|
464
|
-
const classification = classifySyncularError(error, message);
|
|
465
|
-
if (!classification) {
|
|
466
|
-
return createSyncularWorkerErrorPayload('worker.failed', message);
|
|
467
|
-
}
|
|
468
|
-
return {
|
|
469
|
-
code: classification.code,
|
|
470
|
-
message,
|
|
471
|
-
category: classification.category,
|
|
472
|
-
retryable: classification.retryable,
|
|
473
|
-
recommendedAction: classification.recommendedAction,
|
|
474
|
-
};
|
|
475
|
-
}
|
|
476
|
-
function isWorkerErrorPayload(value) {
|
|
477
|
-
return Boolean(value && typeof value === 'object' && 'code' in value && 'message' in value);
|
|
318
|
+
return openPersistentWasmDatabase(config.database.name, {
|
|
319
|
+
...(config.database.directory !== undefined
|
|
320
|
+
? { directory: config.database.directory }
|
|
321
|
+
: {}),
|
|
322
|
+
});
|
|
478
323
|
}
|
|
479
|
-
//# sourceMappingURL=worker-entry.js.map
|