@syncular/client 0.1.0 → 0.1.2
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/blob-limits.js +1 -1
- package/dist/bridge-client.js +2 -2
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +48 -12
- package/dist/client.js.map +1 -1
- package/dist/command-history.js +1 -1
- package/dist/console-diagnostics.js +1 -1
- package/dist/crdt-yjs/index.js +3 -3
- package/dist/database.js +47 -9
- package/dist/database.js.map +1 -1
- package/dist/index.js +14 -14
- package/dist/runtime-contract.d.ts +1 -1
- package/dist/runtime-contract.js +4 -4
- package/dist/rust-client.d.ts.map +1 -1
- package/dist/rust-client.js +11 -9
- package/dist/rust-client.js.map +1 -1
- package/dist/syncular-runtime-artifacts.json +8 -8
- package/dist/types.d.ts +38 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/wasm/syncular-runtime-artifact.json +2 -2
- package/dist/wasm/syncular.d.ts +5 -5
- package/dist/wasm/syncular.js +17 -17
- package/dist/wasm/syncular_bg.wasm +0 -0
- package/dist/wasm/syncular_bg.wasm.d.ts +5 -5
- package/dist/wasm-bindings/runtime-contract.d.ts +1 -1
- package/dist/wasm-bindings/runtime-contract.js +1 -1
- package/dist/wasm-core/syncular-runtime-artifact.json +2 -2
- package/dist/wasm-core/syncular.d.ts +5 -5
- package/dist/wasm-core/syncular.js +18 -18
- package/dist/wasm-core/syncular_bg.wasm +0 -0
- package/dist/wasm-core/syncular_bg.wasm.d.ts +5 -5
- package/dist/wasm-perf/syncular-runtime-artifact.json +2 -2
- package/dist/wasm-perf/syncular.d.ts +5 -5
- package/dist/wasm-perf/syncular.js +16 -16
- package/dist/wasm-perf/syncular_bg.wasm +0 -0
- package/dist/wasm-perf/syncular_bg.wasm.d.ts +5 -5
- package/dist/wasm-runtime.d.ts +6 -1
- package/dist/wasm-runtime.d.ts.map +1 -1
- package/dist/wasm-runtime.js +44 -3
- package/dist/wasm-runtime.js.map +1 -1
- package/dist/worker-client.d.ts +1 -1
- package/dist/worker-client.d.ts.map +1 -1
- package/dist/worker-client.js +65 -16
- package/dist/worker-client.js.map +1 -1
- package/dist/worker-entry.js +9 -9
- package/dist/worker-realtime.js +2 -2
- package/package.json +3 -3
- package/src/client.ts +54 -10
- package/src/database.ts +55 -1
- package/src/rust-client.ts +4 -1
- package/src/types.ts +42 -1
- package/src/wasm-bindings/runtime-contract.ts +1 -1
- package/src/wasm-runtime.ts +71 -2
- package/src/worker-client.ts +82 -8
package/src/wasm-runtime.ts
CHANGED
|
@@ -23,7 +23,7 @@ export {
|
|
|
23
23
|
} from './wasm-bindings/runtime-contract';
|
|
24
24
|
|
|
25
25
|
export interface SyncularWasmGlue {
|
|
26
|
-
default(moduleOrPath?:
|
|
26
|
+
default(moduleOrPath?: SyncularWasmInitInput): Promise<unknown>;
|
|
27
27
|
syncularRuntimeInfoJson(): string;
|
|
28
28
|
syncularBuildYjsTextUpdateJson(argsJson: string): string;
|
|
29
29
|
syncularApplyYjsTextUpdatesJson(argsJson: string): string;
|
|
@@ -35,6 +35,25 @@ export interface SyncularWasmGlue {
|
|
|
35
35
|
): Promise<SyncularRustOwnedSqliteClient>;
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
+
export type SyncularWasmModuleInput =
|
|
39
|
+
| string
|
|
40
|
+
| URL
|
|
41
|
+
| Request
|
|
42
|
+
| Response
|
|
43
|
+
| BufferSource
|
|
44
|
+
| WebAssembly.Module;
|
|
45
|
+
|
|
46
|
+
export type SyncularWasmInitInput =
|
|
47
|
+
| SyncularWasmModuleInput
|
|
48
|
+
| Promise<SyncularWasmModuleInput>
|
|
49
|
+
| {
|
|
50
|
+
module_or_path:
|
|
51
|
+
| SyncularWasmModuleInput
|
|
52
|
+
| Promise<SyncularWasmModuleInput>;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
type SyncularWasmFetchInput = string | URL | Request;
|
|
56
|
+
|
|
38
57
|
let modulePromise: Promise<SyncularWasmGlue> | undefined;
|
|
39
58
|
|
|
40
59
|
export function loadSyncularWasmGlue(): Promise<SyncularWasmGlue> {
|
|
@@ -65,12 +84,62 @@ export async function getSyncularRustRuntimeInfo(
|
|
|
65
84
|
wasmUrl: string | URL | Request = getSyncularWasmUrl()
|
|
66
85
|
): Promise<SyncularRustRuntimeInfo> {
|
|
67
86
|
const resolved = await (mod ?? loadSyncularWasmGlue());
|
|
68
|
-
await resolved.default(
|
|
87
|
+
await resolved.default({
|
|
88
|
+
module_or_path: await prepareSyncularWasmModuleInput(wasmUrl),
|
|
89
|
+
});
|
|
69
90
|
return readSyncularRustRuntimeInfo(resolved);
|
|
70
91
|
}
|
|
71
92
|
|
|
93
|
+
export async function prepareSyncularWasmModuleInput(
|
|
94
|
+
wasmUrl: SyncularWasmModuleInput = getSyncularWasmUrl()
|
|
95
|
+
): Promise<SyncularWasmModuleInput> {
|
|
96
|
+
if (!shouldReadWasmUrlAsBytes(wasmUrl)) return wasmUrl;
|
|
97
|
+
const response = await fetch(wasmUrl);
|
|
98
|
+
if (!response.ok) {
|
|
99
|
+
throw new Error(
|
|
100
|
+
`Syncular WASM runtime artifact could not be loaded from ${describeSyncularWasmInput(
|
|
101
|
+
wasmUrl
|
|
102
|
+
)} (${response.status} ${response.statusText})`
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
return response.arrayBuffer();
|
|
106
|
+
}
|
|
107
|
+
|
|
72
108
|
export function readSyncularRustRuntimeInfo(
|
|
73
109
|
mod: SyncularWasmGlue
|
|
74
110
|
): SyncularRustRuntimeInfo {
|
|
75
111
|
return JSON.parse(mod.syncularRuntimeInfoJson()) as SyncularRustRuntimeInfo;
|
|
76
112
|
}
|
|
113
|
+
|
|
114
|
+
function shouldReadWasmUrlAsBytes(
|
|
115
|
+
input: SyncularWasmModuleInput
|
|
116
|
+
): input is SyncularWasmFetchInput {
|
|
117
|
+
return isBunRuntime() && isFileUrlInput(input);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function isBunRuntime(): boolean {
|
|
121
|
+
return (globalThis as typeof globalThis & { Bun?: unknown }).Bun != null;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function isFileUrlInput(
|
|
125
|
+
input: SyncularWasmModuleInput
|
|
126
|
+
): input is SyncularWasmFetchInput {
|
|
127
|
+
if (input instanceof URL) return input.protocol === 'file:';
|
|
128
|
+
if (input instanceof Request) return isFileUrlString(input.url);
|
|
129
|
+
if (typeof input === 'string') return isFileUrlString(input);
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function isFileUrlString(input: string): boolean {
|
|
134
|
+
try {
|
|
135
|
+
return new URL(input).protocol === 'file:';
|
|
136
|
+
} catch {
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function describeSyncularWasmInput(input: string | URL | Request): string {
|
|
142
|
+
if (input instanceof URL) return input.href;
|
|
143
|
+
if (input instanceof Request) return input.url;
|
|
144
|
+
return input;
|
|
145
|
+
}
|
package/src/worker-client.ts
CHANGED
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
import {
|
|
21
21
|
isGeneratedSyncularOperationalStateWorkerRequestType,
|
|
22
22
|
type SyncularGeneratedWorkerRequestInput,
|
|
23
|
+
type SyncularGeneratedWorkerRequestType,
|
|
23
24
|
} from './generated-bridge';
|
|
24
25
|
import { browserSyncularNetworkStatusSource } from './network';
|
|
25
26
|
import { assertSyncularReadonlySql } from './sql-safety';
|
|
@@ -94,6 +95,8 @@ import type {
|
|
|
94
95
|
SyncularSyncResult,
|
|
95
96
|
SyncularSyncTimings,
|
|
96
97
|
SyncularTransportStats,
|
|
98
|
+
SyncularWorkerRequestTimeoutMs,
|
|
99
|
+
SyncularWorkerRequestTimeouts,
|
|
97
100
|
} from './types';
|
|
98
101
|
import {
|
|
99
102
|
getSyncularRuntimeArtifact,
|
|
@@ -132,6 +135,70 @@ type BlobOutboxRow = {
|
|
|
132
135
|
type SyncularWorkerRequestInput = SyncularGeneratedWorkerRequestInput;
|
|
133
136
|
|
|
134
137
|
const DEFAULT_SYNCULAR_WORKER_REQUEST_TIMEOUT_MS = 30_000;
|
|
138
|
+
const NO_SYNCULAR_WORKER_REQUEST_TIMEOUT = 0;
|
|
139
|
+
|
|
140
|
+
const SYNCULAR_SYNC_WORKER_REQUEST_TYPES = new Set<string>([
|
|
141
|
+
'forceSubscriptionsBootstrap',
|
|
142
|
+
'syncPull',
|
|
143
|
+
'syncPush',
|
|
144
|
+
'syncOnce',
|
|
145
|
+
]);
|
|
146
|
+
|
|
147
|
+
const SYNCULAR_BLOB_WORKER_REQUEST_TYPES = new Set<string>([
|
|
148
|
+
'storeBlob',
|
|
149
|
+
'retrieveBlob',
|
|
150
|
+
'processBlobUploadQueue',
|
|
151
|
+
]);
|
|
152
|
+
|
|
153
|
+
const SYNCULAR_STORAGE_MAINTENANCE_WORKER_REQUEST_TYPES = new Set<string>([
|
|
154
|
+
'open',
|
|
155
|
+
'compactStorage',
|
|
156
|
+
'exportLocalSupportBundle',
|
|
157
|
+
'importLocalSupportBundle',
|
|
158
|
+
'repairLocalHealth',
|
|
159
|
+
'resetLocalSyncState',
|
|
160
|
+
]);
|
|
161
|
+
|
|
162
|
+
function syncularWorkerRequestTimeoutMs(
|
|
163
|
+
config:
|
|
164
|
+
| SyncularWorkerRequestTimeoutMs
|
|
165
|
+
| SyncularWorkerRequestTimeouts
|
|
166
|
+
| undefined,
|
|
167
|
+
type: SyncularGeneratedWorkerRequestType
|
|
168
|
+
): number {
|
|
169
|
+
if (typeof config === 'number' || config === false) {
|
|
170
|
+
return normalizeSyncularWorkerTimeoutMs(config);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const byType = config?.byType?.[type];
|
|
174
|
+
if (byType !== undefined) return normalizeSyncularWorkerTimeoutMs(byType);
|
|
175
|
+
|
|
176
|
+
if (SYNCULAR_SYNC_WORKER_REQUEST_TYPES.has(type)) {
|
|
177
|
+
return normalizeSyncularWorkerTimeoutMs(config?.syncMs ?? false);
|
|
178
|
+
}
|
|
179
|
+
if (SYNCULAR_BLOB_WORKER_REQUEST_TYPES.has(type)) {
|
|
180
|
+
return normalizeSyncularWorkerTimeoutMs(config?.blobMs ?? false);
|
|
181
|
+
}
|
|
182
|
+
if (SYNCULAR_STORAGE_MAINTENANCE_WORKER_REQUEST_TYPES.has(type)) {
|
|
183
|
+
return normalizeSyncularWorkerTimeoutMs(
|
|
184
|
+
config?.storageMaintenanceMs ?? false
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return normalizeSyncularWorkerTimeoutMs(
|
|
189
|
+
config?.defaultMs ?? DEFAULT_SYNCULAR_WORKER_REQUEST_TIMEOUT_MS
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function normalizeSyncularWorkerTimeoutMs(
|
|
194
|
+
value: SyncularWorkerRequestTimeoutMs
|
|
195
|
+
): number {
|
|
196
|
+
if (value === false) return NO_SYNCULAR_WORKER_REQUEST_TIMEOUT;
|
|
197
|
+
if (!Number.isFinite(value) || value <= 0) {
|
|
198
|
+
return NO_SYNCULAR_WORKER_REQUEST_TIMEOUT;
|
|
199
|
+
}
|
|
200
|
+
return Math.trunc(value);
|
|
201
|
+
}
|
|
135
202
|
|
|
136
203
|
export async function createSyncularWorkerClient(
|
|
137
204
|
options: CreateSyncularDatabaseOptions
|
|
@@ -223,7 +290,10 @@ export class SyncularWorkerClient implements SyncularRuntimeClient {
|
|
|
223
290
|
#nextId = 1;
|
|
224
291
|
#pending = new Map<number, PendingRequest>();
|
|
225
292
|
#closed = false;
|
|
226
|
-
#
|
|
293
|
+
#requestTimeouts:
|
|
294
|
+
| SyncularWorkerRequestTimeoutMs
|
|
295
|
+
| SyncularWorkerRequestTimeouts
|
|
296
|
+
| undefined;
|
|
227
297
|
#getHeaders: CreateSyncularDatabaseOptions['getHeaders'] | undefined;
|
|
228
298
|
#authHeaders: SyncularAuthHeaders = {};
|
|
229
299
|
#authLifecycle: CreateSyncularDatabaseOptions['authLifecycle'] | undefined;
|
|
@@ -275,7 +345,7 @@ export class SyncularWorkerClient implements SyncularRuntimeClient {
|
|
|
275
345
|
private readonly worker: Worker,
|
|
276
346
|
options: {
|
|
277
347
|
ownsWorker: boolean;
|
|
278
|
-
requestTimeoutMs?:
|
|
348
|
+
requestTimeoutMs?: CreateSyncularDatabaseOptions['requestTimeoutMs'];
|
|
279
349
|
getHeaders?: CreateSyncularDatabaseOptions['getHeaders'];
|
|
280
350
|
authLifecycle?: CreateSyncularDatabaseOptions['authLifecycle'];
|
|
281
351
|
diagnostics?: SyncularDiagnosticSink;
|
|
@@ -284,8 +354,7 @@ export class SyncularWorkerClient implements SyncularRuntimeClient {
|
|
|
284
354
|
blobLimits?: CreateSyncularDatabaseOptions['blobLimits'];
|
|
285
355
|
}
|
|
286
356
|
) {
|
|
287
|
-
this.#
|
|
288
|
-
options.requestTimeoutMs ?? DEFAULT_SYNCULAR_WORKER_REQUEST_TIMEOUT_MS;
|
|
357
|
+
this.#requestTimeouts = options.requestTimeoutMs;
|
|
289
358
|
this.#getHeaders = options.getHeaders;
|
|
290
359
|
this.#authLifecycle = options.authLifecycle;
|
|
291
360
|
this.#blobLimits = options.blobLimits;
|
|
@@ -1465,8 +1534,9 @@ export class SyncularWorkerClient implements SyncularRuntimeClient {
|
|
|
1465
1534
|
protocolVersion: SYNCULAR_WORKER_PROTOCOL_VERSION,
|
|
1466
1535
|
} as SyncularWorkerRequest;
|
|
1467
1536
|
return new Promise<T>((resolve, reject) => {
|
|
1537
|
+
const requestTimeoutMs = this.#timeoutMsForRequest(request.type);
|
|
1468
1538
|
const timeout =
|
|
1469
|
-
|
|
1539
|
+
requestTimeoutMs > 0
|
|
1470
1540
|
? setTimeout(() => {
|
|
1471
1541
|
this.#pending.delete(id);
|
|
1472
1542
|
this.#sendCancel(id);
|
|
@@ -1478,13 +1548,13 @@ export class SyncularWorkerClient implements SyncularRuntimeClient {
|
|
|
1478
1548
|
details: {
|
|
1479
1549
|
requestId: id,
|
|
1480
1550
|
requestType: request.type,
|
|
1481
|
-
timeoutMs:
|
|
1551
|
+
timeoutMs: requestTimeoutMs,
|
|
1482
1552
|
},
|
|
1483
1553
|
});
|
|
1484
1554
|
const error = new SyncularWorkerError(
|
|
1485
1555
|
createSyncularWorkerErrorPayload(
|
|
1486
1556
|
'worker.request_timeout',
|
|
1487
|
-
`Syncular worker request ${request.type} timed out after ${
|
|
1557
|
+
`Syncular worker request ${request.type} timed out after ${requestTimeoutMs}ms`,
|
|
1488
1558
|
{ details: { requestId: id, requestType: request.type } }
|
|
1489
1559
|
)
|
|
1490
1560
|
);
|
|
@@ -1494,7 +1564,7 @@ export class SyncularWorkerClient implements SyncularRuntimeClient {
|
|
|
1494
1564
|
};
|
|
1495
1565
|
this.#emitLifecycleChanged();
|
|
1496
1566
|
reject(error);
|
|
1497
|
-
},
|
|
1567
|
+
}, requestTimeoutMs)
|
|
1498
1568
|
: undefined;
|
|
1499
1569
|
this.#pending.set(id, {
|
|
1500
1570
|
type: request.type,
|
|
@@ -1507,6 +1577,10 @@ export class SyncularWorkerClient implements SyncularRuntimeClient {
|
|
|
1507
1577
|
});
|
|
1508
1578
|
}
|
|
1509
1579
|
|
|
1580
|
+
#timeoutMsForRequest(type: SyncularGeneratedWorkerRequestType): number {
|
|
1581
|
+
return syncularWorkerRequestTimeoutMs(this.#requestTimeouts, type);
|
|
1582
|
+
}
|
|
1583
|
+
|
|
1510
1584
|
#handleWorkerMessage(message: SyncularWorkerOutboundMessage): void {
|
|
1511
1585
|
if (message.protocolVersion !== SYNCULAR_WORKER_PROTOCOL_VERSION) {
|
|
1512
1586
|
this.#rejectAll(
|