@syncular/client 0.15.29 → 0.15.31
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 +10 -4
- package/dist/client.js +2 -1
- package/dist/worker-host.d.ts +3 -0
- package/dist/worker-host.js +10 -2
- package/dist/worker-protocol.d.ts +2 -0
- package/dist/worker-protocol.js +2 -0
- package/package.json +3 -3
- package/src/client.ts +2 -1
- package/src/worker-host.ts +16 -4
- package/src/worker-protocol.ts +2 -0
package/README.md
CHANGED
|
@@ -144,11 +144,12 @@ deterministic tuple for diagnostics and host integration. Replica IDs are
|
|
|
144
144
|
stable code-like values (`A-Z`, `a-z`, `0-9`, dot, underscore, dash).
|
|
145
145
|
|
|
146
146
|
During Vite development, retain the React client resource only while its
|
|
147
|
-
captured generated schema version
|
|
148
|
-
|
|
147
|
+
captured generated schema version and published Syncular runtime identity both
|
|
148
|
+
match. The
|
|
149
|
+
[schema-and-runtime-aware Vite guide](https://syncular.dev/guide-vite/) uses
|
|
149
150
|
`retainViteSyncClientResource` to close the old worker before constructing a
|
|
150
|
-
schema-bump replacement; hot-reloading query code alone does
|
|
151
|
-
worker-owned database.
|
|
151
|
+
schema-bump or package-upgrade replacement; hot-reloading query code alone does
|
|
152
|
+
not migrate the worker-owned database.
|
|
152
153
|
|
|
153
154
|
Set `multiTab: false` to opt out. A losing tab then becomes an
|
|
154
155
|
`isLeader === false` handle whose calls reject with `client.not_leader`. This
|
|
@@ -360,6 +361,11 @@ in-memory one**; the local database and pending outbox may be perfectly healthy.
|
|
|
360
361
|
Missing or obsolete OPFS APIs instead use the non-retryable
|
|
361
362
|
`client.storage_unavailable` code.
|
|
362
363
|
|
|
364
|
+
A worker graph that still names a retired Vite optimizer chunk instead uses
|
|
365
|
+
the non-retryable `client.worker_restart_required` code. Restart the dev server
|
|
366
|
+
and reload the page; do not clear OPFS. The original bundler message and chunk
|
|
367
|
+
URL are deliberately not copied into the public error.
|
|
368
|
+
|
|
363
369
|
When using `@syncular/react`, `createSyncClientResource()` exposes `retry()` and
|
|
364
370
|
passes the same action as the second argument to `SyncProvider.renderError`.
|
|
365
371
|
Applications may use a small bounded backoff for errors whose `retryable` flag
|
package/dist/client.js
CHANGED
|
@@ -1745,7 +1745,8 @@ export class SyncClient {
|
|
|
1745
1745
|
return (code === 'transport.failed' ||
|
|
1746
1746
|
code === 'transport.unavailable' ||
|
|
1747
1747
|
code === 'sync.transport_failed' ||
|
|
1748
|
-
code === 'client.worker_failed'
|
|
1748
|
+
code === 'client.worker_failed' ||
|
|
1749
|
+
code === 'client.worker_restart_required');
|
|
1749
1750
|
}
|
|
1750
1751
|
#diagnosticCode(code) {
|
|
1751
1752
|
return code.length <= 96 && /^[a-z][a-z0-9]*(?:[._-][a-z0-9]+)+$/.test(code)
|
package/dist/worker-host.d.ts
CHANGED
|
@@ -27,6 +27,7 @@ import type { ConflictRecord, LeaseState, MutationInput, PresencePeer, QueryRead
|
|
|
27
27
|
import type { SqlRow, SqlValue } from './database.js';
|
|
28
28
|
import { ClientDiagnosticsEmitter, type ClientDiagnosticsListener, type ClientDiagnosticsRequest, type ClientDiagnosticsSnapshot } from './diagnostics.js';
|
|
29
29
|
import type { EncryptionKeyringConfig } from './encryption.js';
|
|
30
|
+
import { ClientSyncError } from './errors.js';
|
|
30
31
|
import { ChangeEmitter, type ClientChangeListener, InvalidationEmitter, type InvalidationListener, type LocalRevision, type SyncStatusSnapshot } from './invalidation.js';
|
|
31
32
|
import { type LeaderLease, type LeaderLock } from './leader-lock.js';
|
|
32
33
|
import type { LocalDataPurgeInput, LocalDataPurgeResult } from './local-purge.js';
|
|
@@ -38,6 +39,8 @@ import type { ClientSchema } from './schema.js';
|
|
|
38
39
|
import type { SubscriptionRecord } from './state.js';
|
|
39
40
|
import type { WindowBase } from './window.js';
|
|
40
41
|
import { type SyncWorkerEvent, type WorkerDatabaseInit, type WorkerEndpoints, type WorkerErrorShape, type WorkerSecurityActivation } from './worker-protocol.js';
|
|
42
|
+
/** Classify startup without echoing a chunk URL or bundler text to UI/logs. */
|
|
43
|
+
export declare function workerStartupError(message: unknown): ClientSyncError;
|
|
41
44
|
export type HandleRole = 'leader' | 'follower';
|
|
42
45
|
export type BrowserReplicaMode = {
|
|
43
46
|
readonly mode: 'shared';
|
package/dist/worker-host.js
CHANGED
|
@@ -4,7 +4,15 @@ import { ClientSyncError } from './errors.js';
|
|
|
4
4
|
import { ChangeEmitter, InvalidationEmitter, invalidationFromChange, } from './invalidation.js';
|
|
5
5
|
import { singleOwnerLock, webLocksLeaderLock, } from './leader-lock.js';
|
|
6
6
|
import { broadcastChannelFactory, FollowerLink, LeaderBridge, multiTabChannelName, newTabId, } from './multi-tab.js';
|
|
7
|
-
import { NOT_LEADER_CODE, WORKER_FAILED_CODE, } from './worker-protocol.js';
|
|
7
|
+
import { NOT_LEADER_CODE, WORKER_FAILED_CODE, WORKER_RESTART_REQUIRED_CODE, } from './worker-protocol.js';
|
|
8
|
+
const WORKER_BUNDLE_LOAD_FAILURE = /(?:failed to fetch dynamically imported module|error loading dynamically imported module|importing a module script failed|failed to load module script)/iu;
|
|
9
|
+
/** Classify startup without echoing a chunk URL or bundler text to UI/logs. */
|
|
10
|
+
export function workerStartupError(message) {
|
|
11
|
+
if (typeof message === 'string' && WORKER_BUNDLE_LOAD_FAILURE.test(message)) {
|
|
12
|
+
return new ClientSyncError(WORKER_RESTART_REQUIRED_CODE, 'the sync worker bundle changed; fully reload this application');
|
|
13
|
+
}
|
|
14
|
+
return new ClientSyncError(WORKER_FAILED_CODE, 'the sync worker failed');
|
|
15
|
+
}
|
|
8
16
|
/** Derive the complete ownership tuple for an independently owned replica. */
|
|
9
17
|
export function isolatedReplicaNames(options) {
|
|
10
18
|
if (!/^[A-Za-z0-9._-]+$/.test(options.replicaId)) {
|
|
@@ -403,7 +411,7 @@ async function startWorkerCore(options) {
|
|
|
403
411
|
}
|
|
404
412
|
};
|
|
405
413
|
const onError = (event) => {
|
|
406
|
-
const error =
|
|
414
|
+
const error = workerStartupError(event.message);
|
|
407
415
|
reject(error);
|
|
408
416
|
for (const entry of pending.values())
|
|
409
417
|
entry.reject(error);
|
|
@@ -34,6 +34,8 @@ import type { WindowBase } from './window.js';
|
|
|
34
34
|
export declare const NOT_LEADER_CODE = "client.not_leader";
|
|
35
35
|
/** The worker (or its RPC channel) failed outside protocol semantics. */
|
|
36
36
|
export declare const WORKER_FAILED_CODE = "client.worker_failed";
|
|
37
|
+
/** A page/worker bundle identity changed and a full host reload is required. */
|
|
38
|
+
export declare const WORKER_RESTART_REQUIRED_CODE = "client.worker_restart_required";
|
|
37
39
|
export type WorkerDatabaseInit = {
|
|
38
40
|
/** THE persistent mode: opfs-sahpool, named database. */
|
|
39
41
|
readonly mode: 'persistent';
|
package/dist/worker-protocol.js
CHANGED
|
@@ -5,3 +5,5 @@
|
|
|
5
5
|
export const NOT_LEADER_CODE = 'client.not_leader';
|
|
6
6
|
/** The worker (or its RPC channel) failed outside protocol semantics. */
|
|
7
7
|
export const WORKER_FAILED_CODE = 'client.worker_failed';
|
|
8
|
+
/** A page/worker bundle identity changed and a full host reload is required. */
|
|
9
|
+
export const WORKER_RESTART_REQUIRED_CODE = 'client.worker_restart_required';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@syncular/client",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.31",
|
|
4
4
|
"description": "Syncular TypeScript client core — offline-first sync over SQLite (WASM/OPFS, Bun, Node)",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Benjamin Kniffler",
|
|
@@ -81,7 +81,7 @@
|
|
|
81
81
|
},
|
|
82
82
|
"dependencies": {
|
|
83
83
|
"@sqlite.org/sqlite-wasm": "^3.53.0-build1",
|
|
84
|
-
"@syncular/core": "0.15.
|
|
84
|
+
"@syncular/core": "0.15.31"
|
|
85
85
|
},
|
|
86
86
|
"peerDependencies": {
|
|
87
87
|
"better-sqlite3": ">=11"
|
|
@@ -92,7 +92,7 @@
|
|
|
92
92
|
}
|
|
93
93
|
},
|
|
94
94
|
"devDependencies": {
|
|
95
|
-
"@syncular/server": "0.15.
|
|
95
|
+
"@syncular/server": "0.15.31",
|
|
96
96
|
"@types/better-sqlite3": "^7.6.13",
|
|
97
97
|
"better-sqlite3": "^12.11.1"
|
|
98
98
|
}
|
package/src/client.ts
CHANGED
|
@@ -2524,7 +2524,8 @@ export class SyncClient {
|
|
|
2524
2524
|
code === 'transport.failed' ||
|
|
2525
2525
|
code === 'transport.unavailable' ||
|
|
2526
2526
|
code === 'sync.transport_failed' ||
|
|
2527
|
-
code === 'client.worker_failed'
|
|
2527
|
+
code === 'client.worker_failed' ||
|
|
2528
|
+
code === 'client.worker_restart_required'
|
|
2528
2529
|
);
|
|
2529
2530
|
}
|
|
2530
2531
|
|
package/src/worker-host.ts
CHANGED
|
@@ -92,6 +92,7 @@ import {
|
|
|
92
92
|
NOT_LEADER_CODE,
|
|
93
93
|
type SyncWorkerEvent,
|
|
94
94
|
WORKER_FAILED_CODE,
|
|
95
|
+
WORKER_RESTART_REQUIRED_CODE,
|
|
95
96
|
type WorkerApi,
|
|
96
97
|
type WorkerDatabaseInit,
|
|
97
98
|
type WorkerEndpoints,
|
|
@@ -103,6 +104,20 @@ import {
|
|
|
103
104
|
type WorkerToMainMessage,
|
|
104
105
|
} from './worker-protocol';
|
|
105
106
|
|
|
107
|
+
const WORKER_BUNDLE_LOAD_FAILURE =
|
|
108
|
+
/(?:failed to fetch dynamically imported module|error loading dynamically imported module|importing a module script failed|failed to load module script)/iu;
|
|
109
|
+
|
|
110
|
+
/** Classify startup without echoing a chunk URL or bundler text to UI/logs. */
|
|
111
|
+
export function workerStartupError(message: unknown): ClientSyncError {
|
|
112
|
+
if (typeof message === 'string' && WORKER_BUNDLE_LOAD_FAILURE.test(message)) {
|
|
113
|
+
return new ClientSyncError(
|
|
114
|
+
WORKER_RESTART_REQUIRED_CODE,
|
|
115
|
+
'the sync worker bundle changed; fully reload this application',
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
return new ClientSyncError(WORKER_FAILED_CODE, 'the sync worker failed');
|
|
119
|
+
}
|
|
120
|
+
|
|
106
121
|
export type HandleRole = 'leader' | 'follower';
|
|
107
122
|
|
|
108
123
|
export type BrowserReplicaMode =
|
|
@@ -717,10 +732,7 @@ async function startWorkerCore(options: {
|
|
|
717
732
|
}
|
|
718
733
|
};
|
|
719
734
|
const onError = (event: ErrorEvent) => {
|
|
720
|
-
const error =
|
|
721
|
-
WORKER_FAILED_CODE,
|
|
722
|
-
`the sync worker failed: ${event.message ?? 'unknown error'}`,
|
|
723
|
-
);
|
|
735
|
+
const error = workerStartupError(event.message);
|
|
724
736
|
reject(error);
|
|
725
737
|
for (const entry of pending.values()) entry.reject(error);
|
|
726
738
|
pending.clear();
|
package/src/worker-protocol.ts
CHANGED
|
@@ -67,6 +67,8 @@ import type { WindowBase } from './window';
|
|
|
67
67
|
export const NOT_LEADER_CODE = 'client.not_leader';
|
|
68
68
|
/** The worker (or its RPC channel) failed outside protocol semantics. */
|
|
69
69
|
export const WORKER_FAILED_CODE = 'client.worker_failed';
|
|
70
|
+
/** A page/worker bundle identity changed and a full host reload is required. */
|
|
71
|
+
export const WORKER_RESTART_REQUIRED_CODE = 'client.worker_restart_required';
|
|
70
72
|
|
|
71
73
|
// ---------------------------------------------------------------------------
|
|
72
74
|
// Init configuration (structured-clone safe)
|