@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 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
- constructor(code: string, message: string);
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
- constructor(code, message) {
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.6",
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.6"
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.6"
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?: { readonly code: string; readonly message: string };
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(reply.error.code, reply.error.message);
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(reply.error.code, reply.error.message);
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(reply.error.code, reply.error.message);
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(reply.error.code, reply.error.message);
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
- constructor(code: string, message: string) {
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(reply.error.code, reply.error.message);
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;