@syncular/client 0.30.12 → 0.30.14
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/apply.d.ts +3 -2
- package/dist/apply.js +63 -15
- package/dist/blob.d.ts +1 -0
- package/dist/blob.js +4 -3
- package/dist/client.d.ts +3 -0
- package/dist/client.js +180 -159
- package/dist/failed-overlay.d.ts +6 -3
- package/dist/failed-overlay.js +50 -48
- package/dist/outbox.d.ts +4 -1
- package/dist/outbox.js +18 -12
- package/dist/outcomes.d.ts +1 -0
- package/dist/outcomes.js +12 -7
- package/dist/previous-version.js +3 -3
- package/dist/schema.d.ts +1 -0
- package/dist/schema.js +23 -14
- package/dist/state.d.ts +2 -0
- package/dist/state.js +15 -15
- package/dist/window.d.ts +2 -0
- package/dist/window.js +13 -10
- package/dist/worker-entry.js +14 -35
- package/dist/worker-host.d.ts +1 -0
- package/dist/worker-host.js +3 -0
- package/dist/worker-protocol.d.ts +1 -0
- package/package.json +3 -3
- package/src/apply.ts +106 -37
- package/src/blob.ts +4 -3
- package/src/client.ts +224 -197
- package/src/failed-overlay.ts +96 -78
- package/src/outbox.ts +19 -16
- package/src/outcomes.ts +12 -12
- package/src/previous-version.ts +3 -3
- package/src/schema.ts +24 -18
- package/src/state.ts +15 -15
- package/src/window.ts +13 -15
- package/src/worker-entry.ts +13 -50
- package/src/worker-host.ts +4 -0
- package/src/worker-protocol.ts +1 -0
package/src/window.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
export const PENDING_EVICTIONS = '_syncular_window_pending_evict';
|
|
2
|
+
export const WINDOWS_TABLE = '_syncular_windows';
|
|
1
3
|
/**
|
|
2
4
|
* Windowed subscriptions (SPEC.md §4.8).
|
|
3
5
|
*
|
|
@@ -151,7 +153,7 @@ export function loadWindowUnits(
|
|
|
151
153
|
): WindowUnit[] {
|
|
152
154
|
return db
|
|
153
155
|
.query(
|
|
154
|
-
|
|
156
|
+
`SELECT unit,sub_id FROM ${WINDOWS_TABLE} WHERE base=? ORDER BY unit`,
|
|
155
157
|
[baseKey],
|
|
156
158
|
)
|
|
157
159
|
.map((row) => ({ unit: row.unit as string, subId: row.sub_id as string }));
|
|
@@ -163,7 +165,7 @@ export function getWindowUnitBySubId(
|
|
|
163
165
|
subId: string,
|
|
164
166
|
): RegisteredWindowUnit | undefined {
|
|
165
167
|
const row = db.query(
|
|
166
|
-
`SELECT base, unit, sub_id FROM
|
|
168
|
+
`SELECT base, unit, sub_id FROM ${WINDOWS_TABLE}
|
|
167
169
|
WHERE sub_id = ? LIMIT 1`,
|
|
168
170
|
[subId],
|
|
169
171
|
)[0];
|
|
@@ -183,7 +185,7 @@ export function insertWindowUnit(
|
|
|
183
185
|
subId: string,
|
|
184
186
|
): void {
|
|
185
187
|
db.exec(
|
|
186
|
-
|
|
188
|
+
`INSERT OR REPLACE INTO ${WINDOWS_TABLE}(base,unit,sub_id)VALUES(?,?,?)`,
|
|
187
189
|
[baseKey, unit, subId],
|
|
188
190
|
);
|
|
189
191
|
}
|
|
@@ -193,7 +195,7 @@ export function deleteWindowUnit(
|
|
|
193
195
|
baseKey: string,
|
|
194
196
|
unit: string,
|
|
195
197
|
): void {
|
|
196
|
-
db.exec(
|
|
198
|
+
db.exec(`DELETE FROM ${WINDOWS_TABLE} WHERE base=? AND unit=?`, [
|
|
197
199
|
baseKey,
|
|
198
200
|
unit,
|
|
199
201
|
]);
|
|
@@ -206,10 +208,10 @@ export function isUnitLive(
|
|
|
206
208
|
unit: string,
|
|
207
209
|
): boolean {
|
|
208
210
|
return (
|
|
209
|
-
db.query(
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
).length > 0
|
|
211
|
+
db.query(`SELECT 1 FROM ${WINDOWS_TABLE} WHERE base=? AND unit=? LIMIT 1`, [
|
|
212
|
+
baseKey,
|
|
213
|
+
unit,
|
|
214
|
+
]).length > 0
|
|
213
215
|
);
|
|
214
216
|
}
|
|
215
217
|
|
|
@@ -230,23 +232,19 @@ export function savePendingEviction(
|
|
|
230
232
|
effective: ScopeMap,
|
|
231
233
|
): void {
|
|
232
234
|
db.exec(
|
|
233
|
-
`INSERT OR REPLACE INTO
|
|
235
|
+
`INSERT OR REPLACE INTO ${PENDING_EVICTIONS}(sub_id, tbl, effective_scopes)
|
|
234
236
|
VALUES (?, ?, ?)`,
|
|
235
237
|
[subId, table, JSON.stringify(effective)],
|
|
236
238
|
);
|
|
237
239
|
}
|
|
238
240
|
|
|
239
241
|
export function deletePendingEviction(db: ClientDatabase, subId: string): void {
|
|
240
|
-
db.exec(
|
|
241
|
-
subId,
|
|
242
|
-
]);
|
|
242
|
+
db.exec(`DELETE FROM ${PENDING_EVICTIONS} WHERE sub_id = ?`, [subId]);
|
|
243
243
|
}
|
|
244
244
|
|
|
245
245
|
export function loadPendingEvictions(db: ClientDatabase): PendingEviction[] {
|
|
246
246
|
return db
|
|
247
|
-
.query(
|
|
248
|
-
'SELECT sub_id, tbl, effective_scopes FROM _syncular_window_pending_evict',
|
|
249
|
-
)
|
|
247
|
+
.query(`SELECT sub_id, tbl, effective_scopes FROM ${PENDING_EVICTIONS}`)
|
|
250
248
|
.map((row) => ({
|
|
251
249
|
subId: row.sub_id as string,
|
|
252
250
|
table: row.tbl as string,
|
package/src/worker-entry.ts
CHANGED
|
@@ -165,6 +165,7 @@ export function startSyncWorker(overrides: SyncWorkerOverrides = {}): void {
|
|
|
165
165
|
if (
|
|
166
166
|
closed ||
|
|
167
167
|
client === undefined ||
|
|
168
|
+
offline ||
|
|
168
169
|
client.securityLifecycle() === 'preflight'
|
|
169
170
|
) {
|
|
170
171
|
return;
|
|
@@ -189,6 +190,7 @@ export function startSyncWorker(overrides: SyncWorkerOverrides = {}): void {
|
|
|
189
190
|
!autoSync ||
|
|
190
191
|
closed ||
|
|
191
192
|
client === undefined ||
|
|
193
|
+
offline ||
|
|
192
194
|
client.securityLifecycle() === 'preflight' ||
|
|
193
195
|
intent.kind === 'none'
|
|
194
196
|
) {
|
|
@@ -235,41 +237,6 @@ export function startSyncWorker(overrides: SyncWorkerOverrides = {}): void {
|
|
|
235
237
|
return client;
|
|
236
238
|
}
|
|
237
239
|
|
|
238
|
-
function assertOnline(): void {
|
|
239
|
-
if (offline) {
|
|
240
|
-
throw new ClientSyncError(
|
|
241
|
-
'sync.transport_failed',
|
|
242
|
-
'the worker transport is offline (setOffline(true))',
|
|
243
|
-
true,
|
|
244
|
-
);
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
function gateOffline<T>(inner: (request: T) => Promise<Uint8Array>) {
|
|
249
|
-
return (request: T): Promise<Uint8Array> => {
|
|
250
|
-
assertOnline();
|
|
251
|
-
return inner(request);
|
|
252
|
-
};
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
/** Offline gate that preserves the §5.4 `fetchUrl` capability marker. */
|
|
256
|
-
function gateSegmentsOffline(inner: SegmentDownloader): SegmentDownloader {
|
|
257
|
-
const fetchUrl = inner.fetchUrl;
|
|
258
|
-
return Object.assign(gateOffline(inner), {
|
|
259
|
-
...(fetchUrl !== undefined
|
|
260
|
-
? {
|
|
261
|
-
fetchUrl: (
|
|
262
|
-
url: string,
|
|
263
|
-
onProgress?: (bytesReceived: number) => void,
|
|
264
|
-
) => {
|
|
265
|
-
assertOnline();
|
|
266
|
-
return fetchUrl(url, onProgress);
|
|
267
|
-
},
|
|
268
|
-
}
|
|
269
|
-
: {}),
|
|
270
|
-
});
|
|
271
|
-
}
|
|
272
|
-
|
|
273
240
|
async function init(config: WorkerInitConfig): Promise<WorkerInitResult> {
|
|
274
241
|
if (client !== undefined) {
|
|
275
242
|
throw new ClientSyncError(
|
|
@@ -308,11 +275,10 @@ export function startSyncWorker(overrides: SyncWorkerOverrides = {}): void {
|
|
|
308
275
|
}
|
|
309
276
|
}
|
|
310
277
|
}
|
|
311
|
-
const transport =
|
|
278
|
+
const transport =
|
|
312
279
|
overrides.createTransport !== undefined
|
|
313
280
|
? overrides.createTransport(config)
|
|
314
|
-
: httpSyncTransport(config.endpoints.syncUrl, http)
|
|
315
|
-
);
|
|
281
|
+
: httpSyncTransport(config.endpoints.syncUrl, http);
|
|
316
282
|
const segments =
|
|
317
283
|
overrides.createSegments !== undefined
|
|
318
284
|
? overrides.createSegments(config)
|
|
@@ -329,22 +295,15 @@ export function startSyncWorker(overrides: SyncWorkerOverrides = {}): void {
|
|
|
329
295
|
// `{clientId}` URL placeholder — start the client first, then attach
|
|
330
296
|
// the connector through a mutable slot read at connect time.
|
|
331
297
|
let realtimeConnector: RealtimeConnector | undefined;
|
|
298
|
+
offline = config.transportEnabled === false;
|
|
332
299
|
const started = new SyncClient({
|
|
300
|
+
transportEnabled: !offline,
|
|
333
301
|
database,
|
|
334
302
|
schema: config.schema,
|
|
335
303
|
transport,
|
|
336
|
-
...(segments !== undefined
|
|
337
|
-
? { segments: gateSegmentsOffline(segments) }
|
|
338
|
-
: {}),
|
|
304
|
+
...(segments !== undefined ? { segments } : {}),
|
|
339
305
|
...(blobs !== undefined ? { blobs } : {}),
|
|
340
306
|
realtime: (handlers) => {
|
|
341
|
-
if (offline) {
|
|
342
|
-
throw new ClientSyncError(
|
|
343
|
-
'sync.transport_failed',
|
|
344
|
-
'the worker realtime channel is offline (setOffline(true))',
|
|
345
|
-
true,
|
|
346
|
-
);
|
|
347
|
-
}
|
|
348
307
|
if (realtimeConnector === undefined) {
|
|
349
308
|
throw new ClientSyncError(
|
|
350
309
|
'sync.invalid_request',
|
|
@@ -515,8 +474,12 @@ export function startSyncWorker(overrides: SyncWorkerOverrides = {}): void {
|
|
|
515
474
|
},
|
|
516
475
|
setOffline: (value) => {
|
|
517
476
|
offline = value;
|
|
518
|
-
|
|
519
|
-
|
|
477
|
+
requireClient().setTransportEnabled(!value);
|
|
478
|
+
if (offline) {
|
|
479
|
+
if (backgroundTimer !== undefined) clearTimeout(backgroundTimer);
|
|
480
|
+
backgroundTimer = undefined;
|
|
481
|
+
backgroundDue = Number.POSITIVE_INFINITY;
|
|
482
|
+
} else if (client !== undefined) {
|
|
520
483
|
const snapshot = client.diagnosticsSnapshot();
|
|
521
484
|
post({
|
|
522
485
|
t: 'event',
|
package/src/worker-host.ts
CHANGED
|
@@ -194,6 +194,7 @@ export interface SyncClientHandleConfig {
|
|
|
194
194
|
readonly encryption?: EncryptionKeyringConfig;
|
|
195
195
|
/** Open the worker-owned replica behind the fail-closed security gate. */
|
|
196
196
|
readonly securityPreflight?: boolean;
|
|
197
|
+
readonly transportEnabled?: boolean;
|
|
197
198
|
readonly clientId?: string;
|
|
198
199
|
readonly limits?: SyncClientLimits;
|
|
199
200
|
readonly retainFailedCommits?: boolean;
|
|
@@ -947,6 +948,9 @@ function buildInitConfig(
|
|
|
947
948
|
...(config.encryption !== undefined
|
|
948
949
|
? { encryption: config.encryption }
|
|
949
950
|
: {}),
|
|
951
|
+
...(config.transportEnabled !== undefined
|
|
952
|
+
? { transportEnabled: config.transportEnabled }
|
|
953
|
+
: {}),
|
|
950
954
|
...(config.securityPreflight !== undefined
|
|
951
955
|
? { securityPreflight: config.securityPreflight }
|
|
952
956
|
: {}),
|
package/src/worker-protocol.ts
CHANGED
|
@@ -112,6 +112,7 @@ export interface WorkerInitConfig {
|
|
|
112
112
|
readonly encryption?: EncryptionKeyringConfig;
|
|
113
113
|
/** Open the worker-owned replica behind the fail-closed security gate. */
|
|
114
114
|
readonly securityPreflight?: boolean;
|
|
115
|
+
readonly transportEnabled?: boolean;
|
|
115
116
|
readonly clientId?: string;
|
|
116
117
|
readonly limits?: SyncClientLimits;
|
|
117
118
|
readonly retainFailedCommits?: boolean;
|