@syncular/tauri 0.30.6 → 0.30.9
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 +13 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +11 -6
- package/package.json +3 -3
- package/src/index.ts +41 -7
package/README.md
CHANGED
|
@@ -173,3 +173,16 @@ The snapshot API revision removes the individual `schemaFloor`, `leaseState`,
|
|
|
173
173
|
`await client.statusSnapshot()`. Collection and outcome reads remain methods.
|
|
174
174
|
React accepts the bridge directly. See the
|
|
175
175
|
[client migration](https://syncular.dev/platform-web/#snapshot-api-migration).
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
## Atomic sparse writes and retained conflicts
|
|
179
|
+
|
|
180
|
+
The native client accepts mixed `patch`, `upsert` and `delete` operations in one
|
|
181
|
+
`mutate` call. Sparse patches include the primary key and supplied columns.
|
|
182
|
+
Omitted encrypted columns require no key and retain their ciphertext.
|
|
183
|
+
|
|
184
|
+
Set `retainFailedCommits: true` to preserve rejected aggregate intent across
|
|
185
|
+
pulls and restart. `commitOutcome(id).retainedRows` exposes intended rows and the
|
|
186
|
+
latest authorized server base. Resolve with `resolved_keep_server`, or create a
|
|
187
|
+
reviewed replacement and link it with `superseded`. Scope revocation and local
|
|
188
|
+
security purge remove whole retained aggregates.
|
package/dist/index.d.ts
CHANGED
|
@@ -68,6 +68,7 @@ export interface TauriSyncClientConfig {
|
|
|
68
68
|
* connected instead of falling back to HTTP.
|
|
69
69
|
*/
|
|
70
70
|
readonly realtimePolicy?: SyncClientConfig['realtimePolicy'];
|
|
71
|
+
readonly retainFailedCommits?: boolean;
|
|
71
72
|
/**
|
|
72
73
|
* Portable E2EE keys and declarative per-row key-id columns. Raw keys are
|
|
73
74
|
* encoded into the native command envelope and never sent to the server.
|
|
@@ -184,7 +185,8 @@ export declare class TauriSyncClient implements PromiseMethods<ClientSnapshotMet
|
|
|
184
185
|
/** The error a `{error}` reply surfaces (mirrors the web-client `ClientSyncError`). */
|
|
185
186
|
export declare class TauriSyncError extends Error {
|
|
186
187
|
readonly code: string;
|
|
187
|
-
|
|
188
|
+
readonly details: Readonly<Record<string, unknown>> | undefined;
|
|
189
|
+
constructor(code: string, message: string, details?: Readonly<Record<string, unknown>>);
|
|
188
190
|
}
|
|
189
191
|
/**
|
|
190
192
|
* Construct the bridge and issue the native `create` (opening/attaching the
|
package/dist/index.js
CHANGED
|
@@ -176,7 +176,7 @@ export class TauriSyncClient {
|
|
|
176
176
|
}
|
|
177
177
|
const reply = await this.#tauri.invoke(`${PLUGIN}syncular_command`, { command: { method, params } });
|
|
178
178
|
if (reply.error !== undefined) {
|
|
179
|
-
throw new TauriSyncError(reply.error.code, reply.error.message);
|
|
179
|
+
throw new TauriSyncError(reply.error.code, reply.error.message, reply.error.details ?? undefined);
|
|
180
180
|
}
|
|
181
181
|
return reply.result;
|
|
182
182
|
}
|
|
@@ -358,7 +358,7 @@ export class TauriSyncClient {
|
|
|
358
358
|
this.#requireActive();
|
|
359
359
|
const reply = await this.#tauri.invoke(`${PLUGIN}syncular_query`, { sql, params: (params ?? []).map(encodeParam) });
|
|
360
360
|
if (reply.error !== undefined) {
|
|
361
|
-
throw new TauriSyncError(reply.error.code, reply.error.message);
|
|
361
|
+
throw new TauriSyncError(reply.error.code, reply.error.message, reply.error.details ?? undefined);
|
|
362
362
|
}
|
|
363
363
|
const rows = reply.result.rows ?? [];
|
|
364
364
|
return rows.map((r) => decodeRow(r));
|
|
@@ -372,7 +372,7 @@ export class TauriSyncClient {
|
|
|
372
372
|
...(spec.owner !== undefined ? { owner: spec.owner } : {}),
|
|
373
373
|
});
|
|
374
374
|
if (reply.error !== undefined) {
|
|
375
|
-
throw new TauriSyncError(reply.error.code, reply.error.message);
|
|
375
|
+
throw new TauriSyncError(reply.error.code, reply.error.message, reply.error.details ?? undefined);
|
|
376
376
|
}
|
|
377
377
|
const result = reply.result;
|
|
378
378
|
return {
|
|
@@ -442,7 +442,7 @@ export class TauriSyncClient {
|
|
|
442
442
|
this.#requireActive();
|
|
443
443
|
const reply = await this.#tauri.invoke(`${PLUGIN}syncular_set_headers`, { headers });
|
|
444
444
|
if (reply.error !== undefined) {
|
|
445
|
-
throw new TauriSyncError(reply.error.code, reply.error.message);
|
|
445
|
+
throw new TauriSyncError(reply.error.code, reply.error.message, reply.error.details ?? undefined);
|
|
446
446
|
}
|
|
447
447
|
}
|
|
448
448
|
// -- Native CRDT (SPEC.md §5.10.5; needs the plugin `crdt-yjs` feature) ------
|
|
@@ -599,10 +599,12 @@ function decodeDiagnosticsSnapshot(value, kind) {
|
|
|
599
599
|
/** The error a `{error}` reply surfaces (mirrors the web-client `ClientSyncError`). */
|
|
600
600
|
export class TauriSyncError extends Error {
|
|
601
601
|
code;
|
|
602
|
-
|
|
602
|
+
details;
|
|
603
|
+
constructor(code, message, details) {
|
|
603
604
|
super(message);
|
|
604
605
|
this.name = 'TauriSyncError';
|
|
605
606
|
this.code = code;
|
|
607
|
+
this.details = details;
|
|
606
608
|
}
|
|
607
609
|
}
|
|
608
610
|
function toStringSet(value) {
|
|
@@ -720,6 +722,9 @@ export async function createTauriSyncClient(config) {
|
|
|
720
722
|
...(config.database !== undefined ? { database: config.database } : {}),
|
|
721
723
|
schema: config.schema,
|
|
722
724
|
...(config.limits !== undefined ? { limits: config.limits } : {}),
|
|
725
|
+
...(config.retainFailedCommits !== undefined
|
|
726
|
+
? { retainFailedCommits: config.retainFailedCommits }
|
|
727
|
+
: {}),
|
|
723
728
|
...(config.realtimePolicy !== undefined
|
|
724
729
|
? { realtimePolicy: config.realtimePolicy }
|
|
725
730
|
: {}),
|
|
@@ -737,7 +742,7 @@ export async function createTauriSyncClient(config) {
|
|
|
737
742
|
});
|
|
738
743
|
if (reply.error !== undefined) {
|
|
739
744
|
unlisten();
|
|
740
|
-
throw new TauriSyncError(reply.error.code, reply.error.message);
|
|
745
|
+
throw new TauriSyncError(reply.error.code, reply.error.message, reply.error.details ?? undefined);
|
|
741
746
|
}
|
|
742
747
|
return client;
|
|
743
748
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@syncular/tauri",
|
|
3
|
-
"version": "0.30.
|
|
3
|
+
"version": "0.30.9",
|
|
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.9"
|
|
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.9"
|
|
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). */
|
|
@@ -129,6 +133,7 @@ export interface TauriSyncClientConfig {
|
|
|
129
133
|
* connected instead of falling back to HTTP.
|
|
130
134
|
*/
|
|
131
135
|
readonly realtimePolicy?: SyncClientConfig['realtimePolicy'];
|
|
136
|
+
readonly retainFailedCommits?: boolean;
|
|
132
137
|
/**
|
|
133
138
|
* Portable E2EE keys and declarative per-row key-id columns. Raw keys are
|
|
134
139
|
* encoded into the native command envelope and never sent to the server.
|
|
@@ -356,7 +361,11 @@ export class TauriSyncClient implements PromiseMethods<ClientSnapshotMethods> {
|
|
|
356
361
|
{ command: { method, params } },
|
|
357
362
|
);
|
|
358
363
|
if (reply.error !== undefined) {
|
|
359
|
-
throw new TauriSyncError(
|
|
364
|
+
throw new TauriSyncError(
|
|
365
|
+
reply.error.code,
|
|
366
|
+
reply.error.message,
|
|
367
|
+
reply.error.details ?? undefined,
|
|
368
|
+
);
|
|
360
369
|
}
|
|
361
370
|
return reply.result;
|
|
362
371
|
}
|
|
@@ -566,7 +575,11 @@ export class TauriSyncClient implements PromiseMethods<ClientSnapshotMethods> {
|
|
|
566
575
|
{ sql, params: (params ?? []).map(encodeParam) },
|
|
567
576
|
);
|
|
568
577
|
if (reply.error !== undefined) {
|
|
569
|
-
throw new TauriSyncError(
|
|
578
|
+
throw new TauriSyncError(
|
|
579
|
+
reply.error.code,
|
|
580
|
+
reply.error.message,
|
|
581
|
+
reply.error.details ?? undefined,
|
|
582
|
+
);
|
|
570
583
|
}
|
|
571
584
|
const rows = (reply.result as { rows?: unknown[] }).rows ?? [];
|
|
572
585
|
return rows.map((r) => decodeRow(r as Record<string, unknown>));
|
|
@@ -586,7 +599,11 @@ export class TauriSyncClient implements PromiseMethods<ClientSnapshotMethods> {
|
|
|
586
599
|
},
|
|
587
600
|
);
|
|
588
601
|
if (reply.error !== undefined) {
|
|
589
|
-
throw new TauriSyncError(
|
|
602
|
+
throw new TauriSyncError(
|
|
603
|
+
reply.error.code,
|
|
604
|
+
reply.error.message,
|
|
605
|
+
reply.error.details ?? undefined,
|
|
606
|
+
);
|
|
590
607
|
}
|
|
591
608
|
const result = reply.result as {
|
|
592
609
|
revision: string;
|
|
@@ -702,7 +719,11 @@ export class TauriSyncClient implements PromiseMethods<ClientSnapshotMethods> {
|
|
|
702
719
|
{ headers },
|
|
703
720
|
);
|
|
704
721
|
if (reply.error !== undefined) {
|
|
705
|
-
throw new TauriSyncError(
|
|
722
|
+
throw new TauriSyncError(
|
|
723
|
+
reply.error.code,
|
|
724
|
+
reply.error.message,
|
|
725
|
+
reply.error.details ?? undefined,
|
|
726
|
+
);
|
|
706
727
|
}
|
|
707
728
|
}
|
|
708
729
|
|
|
@@ -930,10 +951,16 @@ function decodeDiagnosticsSnapshot(
|
|
|
930
951
|
/** The error a `{error}` reply surfaces (mirrors the web-client `ClientSyncError`). */
|
|
931
952
|
export class TauriSyncError extends Error {
|
|
932
953
|
readonly code: string;
|
|
933
|
-
|
|
954
|
+
readonly details: Readonly<Record<string, unknown>> | undefined;
|
|
955
|
+
constructor(
|
|
956
|
+
code: string,
|
|
957
|
+
message: string,
|
|
958
|
+
details?: Readonly<Record<string, unknown>>,
|
|
959
|
+
) {
|
|
934
960
|
super(message);
|
|
935
961
|
this.name = 'TauriSyncError';
|
|
936
962
|
this.code = code;
|
|
963
|
+
this.details = details;
|
|
937
964
|
}
|
|
938
965
|
}
|
|
939
966
|
|
|
@@ -1067,6 +1094,9 @@ export async function createTauriSyncClient(
|
|
|
1067
1094
|
...(config.database !== undefined ? { database: config.database } : {}),
|
|
1068
1095
|
schema: config.schema,
|
|
1069
1096
|
...(config.limits !== undefined ? { limits: config.limits } : {}),
|
|
1097
|
+
...(config.retainFailedCommits !== undefined
|
|
1098
|
+
? { retainFailedCommits: config.retainFailedCommits }
|
|
1099
|
+
: {}),
|
|
1070
1100
|
...(config.realtimePolicy !== undefined
|
|
1071
1101
|
? { realtimePolicy: config.realtimePolicy }
|
|
1072
1102
|
: {}),
|
|
@@ -1084,7 +1114,11 @@ export async function createTauriSyncClient(
|
|
|
1084
1114
|
});
|
|
1085
1115
|
if (reply.error !== undefined) {
|
|
1086
1116
|
unlisten();
|
|
1087
|
-
throw new TauriSyncError(
|
|
1117
|
+
throw new TauriSyncError(
|
|
1118
|
+
reply.error.code,
|
|
1119
|
+
reply.error.message,
|
|
1120
|
+
reply.error.details ?? undefined,
|
|
1121
|
+
);
|
|
1088
1122
|
}
|
|
1089
1123
|
|
|
1090
1124
|
return client;
|