@syncular/tauri 0.30.4 → 0.30.6
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 +5 -2
- package/dist/index.d.ts +7 -0
- package/dist/index.js +19 -6
- package/package.json +3 -3
- package/src/index.ts +30 -9
package/README.md
CHANGED
|
@@ -117,8 +117,11 @@ still owns OS secure-store deletion and any key buffers it supplied.
|
|
|
117
117
|
|
|
118
118
|
Runtime `setHeaders()` is an active-session operation and is rejected during
|
|
119
119
|
preflight at both the JavaScript and native command boundaries. Supply bootstrap
|
|
120
|
-
headers through trusted plugin configuration
|
|
121
|
-
|
|
120
|
+
headers through trusted plugin configuration or `activateSecurity({ headers })`;
|
|
121
|
+
the latter installs the current bearer before the startup round. Rotate them
|
|
122
|
+
with `setHeaders()` only after successful activation. Closed clients reject data
|
|
123
|
+
and control calls with `client.closed` before testing preflight; listener
|
|
124
|
+
registration and local progress reads throw that code. `close()` is idempotent.
|
|
122
125
|
|
|
123
126
|
## Privacy-safe diagnostics
|
|
124
127
|
|
package/dist/index.d.ts
CHANGED
|
@@ -53,6 +53,13 @@ export interface TauriSyncClientConfig {
|
|
|
53
53
|
* an existing database fails with `client.identity_mismatch`.
|
|
54
54
|
*/
|
|
55
55
|
readonly clientId?: string;
|
|
56
|
+
/**
|
|
57
|
+
* Name of the native database to open, for example one replica per
|
|
58
|
+
* signed-in actor. The plugin opens `<database_dir>/<database>.db` from its
|
|
59
|
+
* `SyncularConfig`; a name is 1 to 128 ASCII letters, digits, `-`, `_` or
|
|
60
|
+
* `.` and starts with a letter or digit. Absent ⇒ the plugin's `db_path`.
|
|
61
|
+
*/
|
|
62
|
+
readonly database?: string;
|
|
56
63
|
/** §4.2 client limits, forwarded to the native `create`. */
|
|
57
64
|
readonly limits?: Record<string, unknown>;
|
|
58
65
|
/**
|
package/dist/index.js
CHANGED
|
@@ -160,9 +160,7 @@ export class TauriSyncClient {
|
|
|
160
160
|
}
|
|
161
161
|
/** Dispatch a `syncular_command` and unwrap `{result}` / throw on `{error}`. */
|
|
162
162
|
async #command(method, params) {
|
|
163
|
-
|
|
164
|
-
throw new TauriSyncError('client.closed', 'the Tauri sync client is closed');
|
|
165
|
-
}
|
|
163
|
+
this.#requireOpen();
|
|
166
164
|
if (this.#securityLifecycle === 'preflight' &&
|
|
167
165
|
![
|
|
168
166
|
'securityLifecycle',
|
|
@@ -185,7 +183,13 @@ export class TauriSyncClient {
|
|
|
185
183
|
#throwSecurityPreflight() {
|
|
186
184
|
throw new TauriSyncError(SECURITY_PREFLIGHT_REQUIRED_CODE, 'the local replica is in security preflight; complete quarantine checks and call activateSecurity before accessing protected data');
|
|
187
185
|
}
|
|
186
|
+
#requireOpen() {
|
|
187
|
+
if (this.#closed) {
|
|
188
|
+
throw new TauriSyncError('client.closed', 'the Tauri sync client is closed');
|
|
189
|
+
}
|
|
190
|
+
}
|
|
188
191
|
#requireActive() {
|
|
192
|
+
this.#requireOpen();
|
|
189
193
|
if (this.#securityLifecycle === 'preflight')
|
|
190
194
|
this.#throwSecurityPreflight();
|
|
191
195
|
}
|
|
@@ -254,7 +258,8 @@ export class TauriSyncClient {
|
|
|
254
258
|
}
|
|
255
259
|
}
|
|
256
260
|
// -- SyncClientLike --------------------------------------------------------
|
|
257
|
-
securityLifecycle() {
|
|
261
|
+
async securityLifecycle() {
|
|
262
|
+
this.#requireOpen();
|
|
258
263
|
return Promise.resolve(this.#securityLifecycle);
|
|
259
264
|
}
|
|
260
265
|
beginSecurityPreflight() {
|
|
@@ -284,6 +289,7 @@ export class TauriSyncClient {
|
|
|
284
289
|
* credentials (the direct `setHeaders` path stays refused while gated).
|
|
285
290
|
*/
|
|
286
291
|
async activateSecurity(options = {}) {
|
|
292
|
+
this.#requireOpen();
|
|
287
293
|
if (this.#securityLifecycle === 'active') {
|
|
288
294
|
throw new TauriSyncError('sync.invalid_request', 'activateSecurity requires the client to be in security preflight');
|
|
289
295
|
}
|
|
@@ -297,20 +303,25 @@ export class TauriSyncClient {
|
|
|
297
303
|
this.#securityLifecycle = 'active';
|
|
298
304
|
}
|
|
299
305
|
onInvalidate(listener) {
|
|
306
|
+
this.#requireOpen();
|
|
300
307
|
this.#invalidationListeners.add(listener);
|
|
301
308
|
return () => this.#invalidationListeners.delete(listener);
|
|
302
309
|
}
|
|
303
310
|
onProgress(listener) {
|
|
311
|
+
this.#requireOpen();
|
|
304
312
|
return this.#progress.on(listener);
|
|
305
313
|
}
|
|
306
314
|
progressSnapshot() {
|
|
315
|
+
this.#requireOpen();
|
|
307
316
|
return this.#progress.snapshot();
|
|
308
317
|
}
|
|
309
318
|
onChange(listener) {
|
|
319
|
+
this.#requireOpen();
|
|
310
320
|
this.#changeListeners.add(listener);
|
|
311
321
|
return () => this.#changeListeners.delete(listener);
|
|
312
322
|
}
|
|
313
323
|
onDiagnostics(listener) {
|
|
324
|
+
this.#requireOpen();
|
|
314
325
|
this.#diagnosticsListeners.add(listener);
|
|
315
326
|
this.#enableDiagnosticsPush();
|
|
316
327
|
return () => this.#diagnosticsListeners.delete(listener);
|
|
@@ -339,6 +350,7 @@ export class TauriSyncClient {
|
|
|
339
350
|
});
|
|
340
351
|
}
|
|
341
352
|
onPresence(listener) {
|
|
353
|
+
this.#requireOpen();
|
|
342
354
|
this.#presenceListeners.add(listener);
|
|
343
355
|
return () => this.#presenceListeners.delete(listener);
|
|
344
356
|
}
|
|
@@ -698,13 +710,14 @@ export async function createTauriSyncClient(config) {
|
|
|
698
710
|
}
|
|
699
711
|
const client = new TauriSyncClient(tauri, unlisten, config.securityPreflight === true ? 'preflight' : 'active');
|
|
700
712
|
clientRef.client = client;
|
|
701
|
-
// The native side owns
|
|
702
|
-
// schema, clientId, and limits.
|
|
713
|
+
// The native side owns every path (plugin config); the JS side names a
|
|
714
|
+
// database and supplies the schema, clientId, and limits.
|
|
703
715
|
const reply = await tauri.invoke(`${PLUGIN}syncular_command`, {
|
|
704
716
|
command: {
|
|
705
717
|
method: 'create',
|
|
706
718
|
params: {
|
|
707
719
|
...(config.clientId !== undefined ? { clientId: config.clientId } : {}),
|
|
720
|
+
...(config.database !== undefined ? { database: config.database } : {}),
|
|
708
721
|
schema: config.schema,
|
|
709
722
|
...(config.limits !== undefined ? { limits: config.limits } : {}),
|
|
710
723
|
...(config.realtimePolicy !== undefined
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@syncular/tauri",
|
|
3
|
-
"version": "0.30.
|
|
3
|
+
"version": "0.30.6",
|
|
4
4
|
"description": "Tauri integration for the Syncular client",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Benjamin Kniffler",
|
|
@@ -48,12 +48,12 @@
|
|
|
48
48
|
"test": "bun test"
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
|
-
"@syncular/client": "0.30.
|
|
51
|
+
"@syncular/client": "0.30.6"
|
|
52
52
|
},
|
|
53
53
|
"peerDependencies": {
|
|
54
54
|
"@tauri-apps/api": ">=2.0.0"
|
|
55
55
|
},
|
|
56
56
|
"devDependencies": {
|
|
57
|
-
"@syncular/react": "0.30.
|
|
57
|
+
"@syncular/react": "0.30.6"
|
|
58
58
|
}
|
|
59
59
|
}
|
package/src/index.ts
CHANGED
|
@@ -114,6 +114,13 @@ export interface TauriSyncClientConfig {
|
|
|
114
114
|
* an existing database fails with `client.identity_mismatch`.
|
|
115
115
|
*/
|
|
116
116
|
readonly clientId?: string;
|
|
117
|
+
/**
|
|
118
|
+
* Name of the native database to open, for example one replica per
|
|
119
|
+
* signed-in actor. The plugin opens `<database_dir>/<database>.db` from its
|
|
120
|
+
* `SyncularConfig`; a name is 1 to 128 ASCII letters, digits, `-`, `_` or
|
|
121
|
+
* `.` and starts with a letter or digit. Absent ⇒ the plugin's `db_path`.
|
|
122
|
+
*/
|
|
123
|
+
readonly database?: string;
|
|
117
124
|
/** §4.2 client limits, forwarded to the native `create`. */
|
|
118
125
|
readonly limits?: Record<string, unknown>;
|
|
119
126
|
/**
|
|
@@ -328,12 +335,7 @@ export class TauriSyncClient implements PromiseMethods<ClientSnapshotMethods> {
|
|
|
328
335
|
method: string,
|
|
329
336
|
params: Record<string, unknown>,
|
|
330
337
|
): Promise<unknown> {
|
|
331
|
-
|
|
332
|
-
throw new TauriSyncError(
|
|
333
|
-
'client.closed',
|
|
334
|
-
'the Tauri sync client is closed',
|
|
335
|
-
);
|
|
336
|
-
}
|
|
338
|
+
this.#requireOpen();
|
|
337
339
|
if (
|
|
338
340
|
this.#securityLifecycle === 'preflight' &&
|
|
339
341
|
![
|
|
@@ -366,7 +368,17 @@ export class TauriSyncClient implements PromiseMethods<ClientSnapshotMethods> {
|
|
|
366
368
|
);
|
|
367
369
|
}
|
|
368
370
|
|
|
371
|
+
#requireOpen(): void {
|
|
372
|
+
if (this.#closed) {
|
|
373
|
+
throw new TauriSyncError(
|
|
374
|
+
'client.closed',
|
|
375
|
+
'the Tauri sync client is closed',
|
|
376
|
+
);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
369
380
|
#requireActive(): void {
|
|
381
|
+
this.#requireOpen();
|
|
370
382
|
if (this.#securityLifecycle === 'preflight') this.#throwSecurityPreflight();
|
|
371
383
|
}
|
|
372
384
|
|
|
@@ -431,7 +443,8 @@ export class TauriSyncClient implements PromiseMethods<ClientSnapshotMethods> {
|
|
|
431
443
|
|
|
432
444
|
// -- SyncClientLike --------------------------------------------------------
|
|
433
445
|
|
|
434
|
-
securityLifecycle(): Promise<SecurityLifecycle> {
|
|
446
|
+
async securityLifecycle(): Promise<SecurityLifecycle> {
|
|
447
|
+
this.#requireOpen();
|
|
435
448
|
return Promise.resolve(this.#securityLifecycle);
|
|
436
449
|
}
|
|
437
450
|
|
|
@@ -472,6 +485,7 @@ export class TauriSyncClient implements PromiseMethods<ClientSnapshotMethods> {
|
|
|
472
485
|
readonly headers?: Readonly<Record<string, string>>;
|
|
473
486
|
} = {},
|
|
474
487
|
): Promise<void> {
|
|
488
|
+
this.#requireOpen();
|
|
475
489
|
if (this.#securityLifecycle === 'active') {
|
|
476
490
|
throw new TauriSyncError(
|
|
477
491
|
'sync.invalid_request',
|
|
@@ -489,23 +503,28 @@ export class TauriSyncClient implements PromiseMethods<ClientSnapshotMethods> {
|
|
|
489
503
|
}
|
|
490
504
|
|
|
491
505
|
onInvalidate(listener: InvalidationListener): () => void {
|
|
506
|
+
this.#requireOpen();
|
|
492
507
|
this.#invalidationListeners.add(listener);
|
|
493
508
|
return () => this.#invalidationListeners.delete(listener);
|
|
494
509
|
}
|
|
495
510
|
|
|
496
511
|
onProgress(listener: SyncProgressListener): () => void {
|
|
512
|
+
this.#requireOpen();
|
|
497
513
|
return this.#progress.on(listener);
|
|
498
514
|
}
|
|
499
515
|
progressSnapshot(): SyncProgress | undefined {
|
|
516
|
+
this.#requireOpen();
|
|
500
517
|
return this.#progress.snapshot();
|
|
501
518
|
}
|
|
502
519
|
|
|
503
520
|
onChange(listener: ClientChangeListener): () => void {
|
|
521
|
+
this.#requireOpen();
|
|
504
522
|
this.#changeListeners.add(listener);
|
|
505
523
|
return () => this.#changeListeners.delete(listener);
|
|
506
524
|
}
|
|
507
525
|
|
|
508
526
|
onDiagnostics(listener: ClientDiagnosticsListener): () => void {
|
|
527
|
+
this.#requireOpen();
|
|
509
528
|
this.#diagnosticsListeners.add(listener);
|
|
510
529
|
this.#enableDiagnosticsPush();
|
|
511
530
|
return () => this.#diagnosticsListeners.delete(listener);
|
|
@@ -535,6 +554,7 @@ export class TauriSyncClient implements PromiseMethods<ClientSnapshotMethods> {
|
|
|
535
554
|
}
|
|
536
555
|
|
|
537
556
|
onPresence(listener: (scopeKey: string) => void): () => void {
|
|
557
|
+
this.#requireOpen();
|
|
538
558
|
this.#presenceListeners.add(listener);
|
|
539
559
|
return () => this.#presenceListeners.delete(listener);
|
|
540
560
|
}
|
|
@@ -1037,13 +1057,14 @@ export async function createTauriSyncClient(
|
|
|
1037
1057
|
);
|
|
1038
1058
|
clientRef.client = client;
|
|
1039
1059
|
|
|
1040
|
-
// The native side owns
|
|
1041
|
-
// schema, clientId, and limits.
|
|
1060
|
+
// The native side owns every path (plugin config); the JS side names a
|
|
1061
|
+
// database and supplies the schema, clientId, and limits.
|
|
1042
1062
|
const reply = await tauri.invoke<CommandReply>(`${PLUGIN}syncular_command`, {
|
|
1043
1063
|
command: {
|
|
1044
1064
|
method: 'create',
|
|
1045
1065
|
params: {
|
|
1046
1066
|
...(config.clientId !== undefined ? { clientId: config.clientId } : {}),
|
|
1067
|
+
...(config.database !== undefined ? { database: config.database } : {}),
|
|
1047
1068
|
schema: config.schema,
|
|
1048
1069
|
...(config.limits !== undefined ? { limits: config.limits } : {}),
|
|
1049
1070
|
...(config.realtimePolicy !== undefined
|