@syncular/tauri 0.30.5 → 0.30.7
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 +2 -1
- package/dist/index.js +24 -10
- package/package.json +3 -3
- package/src/index.ts +57 -14
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
|
@@ -184,7 +184,8 @@ export declare class TauriSyncClient implements PromiseMethods<ClientSnapshotMet
|
|
|
184
184
|
/** The error a `{error}` reply surfaces (mirrors the web-client `ClientSyncError`). */
|
|
185
185
|
export declare class TauriSyncError extends Error {
|
|
186
186
|
readonly code: string;
|
|
187
|
-
|
|
187
|
+
readonly details: Readonly<Record<string, unknown>> | undefined;
|
|
188
|
+
constructor(code: string, message: string, details?: Readonly<Record<string, unknown>>);
|
|
188
189
|
}
|
|
189
190
|
/**
|
|
190
191
|
* Construct the bridge and issue the native `create` (opening/attaching the
|
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',
|
|
@@ -178,14 +176,20 @@ export class TauriSyncClient {
|
|
|
178
176
|
}
|
|
179
177
|
const reply = await this.#tauri.invoke(`${PLUGIN}syncular_command`, { command: { method, params } });
|
|
180
178
|
if (reply.error !== undefined) {
|
|
181
|
-
throw new TauriSyncError(reply.error.code, reply.error.message);
|
|
179
|
+
throw new TauriSyncError(reply.error.code, reply.error.message, reply.error.details ?? undefined);
|
|
182
180
|
}
|
|
183
181
|
return reply.result;
|
|
184
182
|
}
|
|
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
|
}
|
|
@@ -346,7 +358,7 @@ export class TauriSyncClient {
|
|
|
346
358
|
this.#requireActive();
|
|
347
359
|
const reply = await this.#tauri.invoke(`${PLUGIN}syncular_query`, { sql, params: (params ?? []).map(encodeParam) });
|
|
348
360
|
if (reply.error !== undefined) {
|
|
349
|
-
throw new TauriSyncError(reply.error.code, reply.error.message);
|
|
361
|
+
throw new TauriSyncError(reply.error.code, reply.error.message, reply.error.details ?? undefined);
|
|
350
362
|
}
|
|
351
363
|
const rows = reply.result.rows ?? [];
|
|
352
364
|
return rows.map((r) => decodeRow(r));
|
|
@@ -360,7 +372,7 @@ export class TauriSyncClient {
|
|
|
360
372
|
...(spec.owner !== undefined ? { owner: spec.owner } : {}),
|
|
361
373
|
});
|
|
362
374
|
if (reply.error !== undefined) {
|
|
363
|
-
throw new TauriSyncError(reply.error.code, reply.error.message);
|
|
375
|
+
throw new TauriSyncError(reply.error.code, reply.error.message, reply.error.details ?? undefined);
|
|
364
376
|
}
|
|
365
377
|
const result = reply.result;
|
|
366
378
|
return {
|
|
@@ -430,7 +442,7 @@ export class TauriSyncClient {
|
|
|
430
442
|
this.#requireActive();
|
|
431
443
|
const reply = await this.#tauri.invoke(`${PLUGIN}syncular_set_headers`, { headers });
|
|
432
444
|
if (reply.error !== undefined) {
|
|
433
|
-
throw new TauriSyncError(reply.error.code, reply.error.message);
|
|
445
|
+
throw new TauriSyncError(reply.error.code, reply.error.message, reply.error.details ?? undefined);
|
|
434
446
|
}
|
|
435
447
|
}
|
|
436
448
|
// -- Native CRDT (SPEC.md §5.10.5; needs the plugin `crdt-yjs` feature) ------
|
|
@@ -587,10 +599,12 @@ function decodeDiagnosticsSnapshot(value, kind) {
|
|
|
587
599
|
/** The error a `{error}` reply surfaces (mirrors the web-client `ClientSyncError`). */
|
|
588
600
|
export class TauriSyncError extends Error {
|
|
589
601
|
code;
|
|
590
|
-
|
|
602
|
+
details;
|
|
603
|
+
constructor(code, message, details) {
|
|
591
604
|
super(message);
|
|
592
605
|
this.name = 'TauriSyncError';
|
|
593
606
|
this.code = code;
|
|
607
|
+
this.details = details;
|
|
594
608
|
}
|
|
595
609
|
}
|
|
596
610
|
function toStringSet(value) {
|
|
@@ -725,7 +739,7 @@ export async function createTauriSyncClient(config) {
|
|
|
725
739
|
});
|
|
726
740
|
if (reply.error !== undefined) {
|
|
727
741
|
unlisten();
|
|
728
|
-
throw new TauriSyncError(reply.error.code, reply.error.message);
|
|
742
|
+
throw new TauriSyncError(reply.error.code, reply.error.message, reply.error.details ?? undefined);
|
|
729
743
|
}
|
|
730
744
|
return client;
|
|
731
745
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@syncular/tauri",
|
|
3
|
-
"version": "0.30.
|
|
3
|
+
"version": "0.30.7",
|
|
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.7"
|
|
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.7"
|
|
58
58
|
}
|
|
59
59
|
}
|
package/src/index.ts
CHANGED
|
@@ -81,7 +81,11 @@ import {
|
|
|
81
81
|
/** A driver-protocol reply: `{result}` on success or `{error}` on failure. */
|
|
82
82
|
interface CommandReply {
|
|
83
83
|
readonly result?: unknown;
|
|
84
|
-
readonly error?: {
|
|
84
|
+
readonly error?: {
|
|
85
|
+
readonly code: string;
|
|
86
|
+
readonly message: string;
|
|
87
|
+
readonly details?: Readonly<Record<string, unknown>> | null;
|
|
88
|
+
};
|
|
85
89
|
}
|
|
86
90
|
|
|
87
91
|
/** One event pushed on `syncular://event` (the derived client-observable set). */
|
|
@@ -335,12 +339,7 @@ export class TauriSyncClient implements PromiseMethods<ClientSnapshotMethods> {
|
|
|
335
339
|
method: string,
|
|
336
340
|
params: Record<string, unknown>,
|
|
337
341
|
): Promise<unknown> {
|
|
338
|
-
|
|
339
|
-
throw new TauriSyncError(
|
|
340
|
-
'client.closed',
|
|
341
|
-
'the Tauri sync client is closed',
|
|
342
|
-
);
|
|
343
|
-
}
|
|
342
|
+
this.#requireOpen();
|
|
344
343
|
if (
|
|
345
344
|
this.#securityLifecycle === 'preflight' &&
|
|
346
345
|
![
|
|
@@ -361,7 +360,11 @@ export class TauriSyncClient implements PromiseMethods<ClientSnapshotMethods> {
|
|
|
361
360
|
{ command: { method, params } },
|
|
362
361
|
);
|
|
363
362
|
if (reply.error !== undefined) {
|
|
364
|
-
throw new TauriSyncError(
|
|
363
|
+
throw new TauriSyncError(
|
|
364
|
+
reply.error.code,
|
|
365
|
+
reply.error.message,
|
|
366
|
+
reply.error.details ?? undefined,
|
|
367
|
+
);
|
|
365
368
|
}
|
|
366
369
|
return reply.result;
|
|
367
370
|
}
|
|
@@ -373,7 +376,17 @@ export class TauriSyncClient implements PromiseMethods<ClientSnapshotMethods> {
|
|
|
373
376
|
);
|
|
374
377
|
}
|
|
375
378
|
|
|
379
|
+
#requireOpen(): void {
|
|
380
|
+
if (this.#closed) {
|
|
381
|
+
throw new TauriSyncError(
|
|
382
|
+
'client.closed',
|
|
383
|
+
'the Tauri sync client is closed',
|
|
384
|
+
);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
376
388
|
#requireActive(): void {
|
|
389
|
+
this.#requireOpen();
|
|
377
390
|
if (this.#securityLifecycle === 'preflight') this.#throwSecurityPreflight();
|
|
378
391
|
}
|
|
379
392
|
|
|
@@ -438,7 +451,8 @@ export class TauriSyncClient implements PromiseMethods<ClientSnapshotMethods> {
|
|
|
438
451
|
|
|
439
452
|
// -- SyncClientLike --------------------------------------------------------
|
|
440
453
|
|
|
441
|
-
securityLifecycle(): Promise<SecurityLifecycle> {
|
|
454
|
+
async securityLifecycle(): Promise<SecurityLifecycle> {
|
|
455
|
+
this.#requireOpen();
|
|
442
456
|
return Promise.resolve(this.#securityLifecycle);
|
|
443
457
|
}
|
|
444
458
|
|
|
@@ -479,6 +493,7 @@ export class TauriSyncClient implements PromiseMethods<ClientSnapshotMethods> {
|
|
|
479
493
|
readonly headers?: Readonly<Record<string, string>>;
|
|
480
494
|
} = {},
|
|
481
495
|
): Promise<void> {
|
|
496
|
+
this.#requireOpen();
|
|
482
497
|
if (this.#securityLifecycle === 'active') {
|
|
483
498
|
throw new TauriSyncError(
|
|
484
499
|
'sync.invalid_request',
|
|
@@ -496,23 +511,28 @@ export class TauriSyncClient implements PromiseMethods<ClientSnapshotMethods> {
|
|
|
496
511
|
}
|
|
497
512
|
|
|
498
513
|
onInvalidate(listener: InvalidationListener): () => void {
|
|
514
|
+
this.#requireOpen();
|
|
499
515
|
this.#invalidationListeners.add(listener);
|
|
500
516
|
return () => this.#invalidationListeners.delete(listener);
|
|
501
517
|
}
|
|
502
518
|
|
|
503
519
|
onProgress(listener: SyncProgressListener): () => void {
|
|
520
|
+
this.#requireOpen();
|
|
504
521
|
return this.#progress.on(listener);
|
|
505
522
|
}
|
|
506
523
|
progressSnapshot(): SyncProgress | undefined {
|
|
524
|
+
this.#requireOpen();
|
|
507
525
|
return this.#progress.snapshot();
|
|
508
526
|
}
|
|
509
527
|
|
|
510
528
|
onChange(listener: ClientChangeListener): () => void {
|
|
529
|
+
this.#requireOpen();
|
|
511
530
|
this.#changeListeners.add(listener);
|
|
512
531
|
return () => this.#changeListeners.delete(listener);
|
|
513
532
|
}
|
|
514
533
|
|
|
515
534
|
onDiagnostics(listener: ClientDiagnosticsListener): () => void {
|
|
535
|
+
this.#requireOpen();
|
|
516
536
|
this.#diagnosticsListeners.add(listener);
|
|
517
537
|
this.#enableDiagnosticsPush();
|
|
518
538
|
return () => this.#diagnosticsListeners.delete(listener);
|
|
@@ -542,6 +562,7 @@ export class TauriSyncClient implements PromiseMethods<ClientSnapshotMethods> {
|
|
|
542
562
|
}
|
|
543
563
|
|
|
544
564
|
onPresence(listener: (scopeKey: string) => void): () => void {
|
|
565
|
+
this.#requireOpen();
|
|
545
566
|
this.#presenceListeners.add(listener);
|
|
546
567
|
return () => this.#presenceListeners.delete(listener);
|
|
547
568
|
}
|
|
@@ -553,7 +574,11 @@ export class TauriSyncClient implements PromiseMethods<ClientSnapshotMethods> {
|
|
|
553
574
|
{ sql, params: (params ?? []).map(encodeParam) },
|
|
554
575
|
);
|
|
555
576
|
if (reply.error !== undefined) {
|
|
556
|
-
throw new TauriSyncError(
|
|
577
|
+
throw new TauriSyncError(
|
|
578
|
+
reply.error.code,
|
|
579
|
+
reply.error.message,
|
|
580
|
+
reply.error.details ?? undefined,
|
|
581
|
+
);
|
|
557
582
|
}
|
|
558
583
|
const rows = (reply.result as { rows?: unknown[] }).rows ?? [];
|
|
559
584
|
return rows.map((r) => decodeRow(r as Record<string, unknown>));
|
|
@@ -573,7 +598,11 @@ export class TauriSyncClient implements PromiseMethods<ClientSnapshotMethods> {
|
|
|
573
598
|
},
|
|
574
599
|
);
|
|
575
600
|
if (reply.error !== undefined) {
|
|
576
|
-
throw new TauriSyncError(
|
|
601
|
+
throw new TauriSyncError(
|
|
602
|
+
reply.error.code,
|
|
603
|
+
reply.error.message,
|
|
604
|
+
reply.error.details ?? undefined,
|
|
605
|
+
);
|
|
577
606
|
}
|
|
578
607
|
const result = reply.result as {
|
|
579
608
|
revision: string;
|
|
@@ -689,7 +718,11 @@ export class TauriSyncClient implements PromiseMethods<ClientSnapshotMethods> {
|
|
|
689
718
|
{ headers },
|
|
690
719
|
);
|
|
691
720
|
if (reply.error !== undefined) {
|
|
692
|
-
throw new TauriSyncError(
|
|
721
|
+
throw new TauriSyncError(
|
|
722
|
+
reply.error.code,
|
|
723
|
+
reply.error.message,
|
|
724
|
+
reply.error.details ?? undefined,
|
|
725
|
+
);
|
|
693
726
|
}
|
|
694
727
|
}
|
|
695
728
|
|
|
@@ -917,10 +950,16 @@ function decodeDiagnosticsSnapshot(
|
|
|
917
950
|
/** The error a `{error}` reply surfaces (mirrors the web-client `ClientSyncError`). */
|
|
918
951
|
export class TauriSyncError extends Error {
|
|
919
952
|
readonly code: string;
|
|
920
|
-
|
|
953
|
+
readonly details: Readonly<Record<string, unknown>> | undefined;
|
|
954
|
+
constructor(
|
|
955
|
+
code: string,
|
|
956
|
+
message: string,
|
|
957
|
+
details?: Readonly<Record<string, unknown>>,
|
|
958
|
+
) {
|
|
921
959
|
super(message);
|
|
922
960
|
this.name = 'TauriSyncError';
|
|
923
961
|
this.code = code;
|
|
962
|
+
this.details = details;
|
|
924
963
|
}
|
|
925
964
|
}
|
|
926
965
|
|
|
@@ -1071,7 +1110,11 @@ export async function createTauriSyncClient(
|
|
|
1071
1110
|
});
|
|
1072
1111
|
if (reply.error !== undefined) {
|
|
1073
1112
|
unlisten();
|
|
1074
|
-
throw new TauriSyncError(
|
|
1113
|
+
throw new TauriSyncError(
|
|
1114
|
+
reply.error.code,
|
|
1115
|
+
reply.error.message,
|
|
1116
|
+
reply.error.details ?? undefined,
|
|
1117
|
+
);
|
|
1075
1118
|
}
|
|
1076
1119
|
|
|
1077
1120
|
return client;
|