@syncular/server 0.30.6 → 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/dist/pg-executor-pglite.js +7 -1
- package/dist/push.js +9 -4
- package/dist/sqlite-image.js +4 -2
- package/dist/sqlite-segment-store.js +4 -2
- package/dist/sqlite-storage.js +22 -16
- package/dist/storage-errors.d.ts +3 -0
- package/dist/storage-errors.js +15 -0
- package/package.json +2 -2
- package/src/pg-executor-pglite.ts +6 -1
- package/src/push.ts +8 -4
- package/src/sqlite-image.ts +4 -2
- package/src/sqlite-segment-store.ts +4 -2
- package/src/sqlite-storage.ts +21 -15
- package/src/storage-errors.ts +19 -0
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { retainRollbackFailure } from './storage-errors.js';
|
|
1
2
|
function queryable(db) {
|
|
2
3
|
return {
|
|
3
4
|
async query(text, params) {
|
|
@@ -32,7 +33,12 @@ export function pgliteExecutor(db) {
|
|
|
32
33
|
return result;
|
|
33
34
|
}
|
|
34
35
|
catch (error) {
|
|
35
|
-
|
|
36
|
+
try {
|
|
37
|
+
await like.exec('ROLLBACK');
|
|
38
|
+
}
|
|
39
|
+
catch (rollbackError) {
|
|
40
|
+
retainRollbackFailure(error, rollbackError);
|
|
41
|
+
}
|
|
36
42
|
throw error;
|
|
37
43
|
}
|
|
38
44
|
}
|
package/dist/push.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { retainRollbackFailure } from './storage-errors.js';
|
|
1
2
|
/**
|
|
2
3
|
* Push apply (SPEC.md §6) with §3.4 write-path authorization.
|
|
3
4
|
*
|
|
@@ -1102,14 +1103,18 @@ export async function processPushOperationsWithTrace(ctx, schema, resolved, clie
|
|
|
1102
1103
|
try {
|
|
1103
1104
|
await tx.rollback();
|
|
1104
1105
|
}
|
|
1105
|
-
catch {
|
|
1106
|
-
|
|
1107
|
-
// otherwise mask it.
|
|
1106
|
+
catch (rollbackError) {
|
|
1107
|
+
retainRollbackFailure(finalizationError, rollbackError);
|
|
1108
1108
|
}
|
|
1109
1109
|
throw finalizationError;
|
|
1110
1110
|
}
|
|
1111
1111
|
}
|
|
1112
|
-
|
|
1112
|
+
try {
|
|
1113
|
+
await tx.rollback();
|
|
1114
|
+
}
|
|
1115
|
+
catch (rollbackError) {
|
|
1116
|
+
retainRollbackFailure(error, rollbackError);
|
|
1117
|
+
}
|
|
1113
1118
|
throw error;
|
|
1114
1119
|
}
|
|
1115
1120
|
}
|
package/dist/sqlite-image.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { rollbackAfterError } from './storage-errors.js';
|
|
1
2
|
/**
|
|
2
3
|
* SQLite-image segment generation (SPEC.md §5.3): a complete SQLite
|
|
3
4
|
* database file carrying one table's whole effective-scope snapshot at
|
|
@@ -79,7 +80,8 @@ export async function writeSqliteImage(db, input) {
|
|
|
79
80
|
db.exec('COMMIT');
|
|
80
81
|
}
|
|
81
82
|
catch (error) {
|
|
82
|
-
|
|
83
|
-
|
|
83
|
+
rollbackAfterError(() => {
|
|
84
|
+
db.exec('ROLLBACK');
|
|
85
|
+
}, error);
|
|
84
86
|
}
|
|
85
87
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { rollbackAfterError } from './storage-errors.js';
|
|
1
2
|
/**
|
|
2
3
|
* SQLite-backed segment store over the shared synchronous driver.
|
|
3
4
|
*/
|
|
@@ -137,8 +138,9 @@ export class SqliteSegmentStore {
|
|
|
137
138
|
this.db.exec('COMMIT');
|
|
138
139
|
}
|
|
139
140
|
catch (error) {
|
|
140
|
-
|
|
141
|
-
|
|
141
|
+
rollbackAfterError(() => {
|
|
142
|
+
this.db.exec('ROLLBACK');
|
|
143
|
+
}, error);
|
|
142
144
|
}
|
|
143
145
|
return record;
|
|
144
146
|
}
|
package/dist/sqlite-storage.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { retainRollbackFailure, rollbackAfterError } from './storage-errors.js';
|
|
1
2
|
import { validateCommitPruneQuery } from './prune.js';
|
|
2
3
|
import { StorageQueryError } from './storage-errors.js';
|
|
3
4
|
import { serveGateRefusal } from './storage.js';
|
|
@@ -229,9 +230,8 @@ class SqliteTransaction {
|
|
|
229
230
|
try {
|
|
230
231
|
this.#storage.db.exec('ROLLBACK');
|
|
231
232
|
}
|
|
232
|
-
catch {
|
|
233
|
-
|
|
234
|
-
// mask it.
|
|
233
|
+
catch (rollbackError) {
|
|
234
|
+
retainRollbackFailure(error, rollbackError);
|
|
235
235
|
}
|
|
236
236
|
throw error;
|
|
237
237
|
}
|
|
@@ -433,8 +433,9 @@ END`);
|
|
|
433
433
|
this.db.exec('COMMIT');
|
|
434
434
|
}
|
|
435
435
|
catch (error) {
|
|
436
|
-
|
|
437
|
-
|
|
436
|
+
rollbackAfterError(() => {
|
|
437
|
+
this.db.exec('ROLLBACK');
|
|
438
|
+
}, error);
|
|
438
439
|
}
|
|
439
440
|
});
|
|
440
441
|
}
|
|
@@ -486,8 +487,9 @@ END`);
|
|
|
486
487
|
this.db.exec('COMMIT');
|
|
487
488
|
}
|
|
488
489
|
catch (error) {
|
|
489
|
-
|
|
490
|
-
|
|
490
|
+
rollbackAfterError(() => {
|
|
491
|
+
this.db.exec('ROLLBACK');
|
|
492
|
+
}, error);
|
|
491
493
|
}
|
|
492
494
|
return {
|
|
493
495
|
partition,
|
|
@@ -624,9 +626,10 @@ END`);
|
|
|
624
626
|
return result;
|
|
625
627
|
}
|
|
626
628
|
catch (error) {
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
629
|
+
rollbackAfterError(() => {
|
|
630
|
+
if (open)
|
|
631
|
+
this.db.exec('ROLLBACK');
|
|
632
|
+
}, error);
|
|
630
633
|
}
|
|
631
634
|
finally {
|
|
632
635
|
release();
|
|
@@ -690,8 +693,9 @@ END`);
|
|
|
690
693
|
};
|
|
691
694
|
}
|
|
692
695
|
catch (error) {
|
|
693
|
-
|
|
694
|
-
|
|
696
|
+
rollbackAfterError(() => {
|
|
697
|
+
this.db.exec('ROLLBACK');
|
|
698
|
+
}, error);
|
|
695
699
|
}
|
|
696
700
|
});
|
|
697
701
|
}
|
|
@@ -771,8 +775,9 @@ END`);
|
|
|
771
775
|
return toStoredCheckpoint(row);
|
|
772
776
|
}
|
|
773
777
|
catch (error) {
|
|
774
|
-
|
|
775
|
-
|
|
778
|
+
rollbackAfterError(() => {
|
|
779
|
+
this.db.exec('ROLLBACK');
|
|
780
|
+
}, error);
|
|
776
781
|
}
|
|
777
782
|
});
|
|
778
783
|
}
|
|
@@ -810,8 +815,9 @@ END`);
|
|
|
810
815
|
return Number(updated.changes) === 1 ? 'activated' : 'stale';
|
|
811
816
|
}
|
|
812
817
|
catch (error) {
|
|
813
|
-
|
|
814
|
-
|
|
818
|
+
rollbackAfterError(() => {
|
|
819
|
+
this.db.exec('ROLLBACK');
|
|
820
|
+
}, error);
|
|
815
821
|
}
|
|
816
822
|
});
|
|
817
823
|
}
|
package/dist/storage-errors.d.ts
CHANGED
|
@@ -31,3 +31,6 @@ export declare function isPostgresConstraintError(error: unknown): boolean;
|
|
|
31
31
|
* the original message is copied to the public StorageConstraintError.
|
|
32
32
|
*/
|
|
33
33
|
export declare function isD1ConstraintError(error: unknown): boolean;
|
|
34
|
+
/** Preserve the operation failure when transaction cleanup also fails. */
|
|
35
|
+
export declare function retainRollbackFailure(error: unknown, rollbackError: unknown): void;
|
|
36
|
+
export declare function rollbackAfterError(run: () => void, error: unknown): never;
|
package/dist/storage-errors.js
CHANGED
|
@@ -84,3 +84,18 @@ export function isD1ConstraintError(error) {
|
|
|
84
84
|
return (typeof message === 'string' &&
|
|
85
85
|
/^(?:D1(?:_EXEC)?_ERROR:\s*)?(?:UNIQUE|NOT NULL|CHECK|FOREIGN KEY) constraint failed\b/i.test(message));
|
|
86
86
|
}
|
|
87
|
+
/** Preserve the operation failure when transaction cleanup also fails. */
|
|
88
|
+
export function retainRollbackFailure(error, rollbackError) {
|
|
89
|
+
if (error instanceof Error && !('rollbackError' in error)) {
|
|
90
|
+
Object.defineProperty(error, 'rollbackError', { value: rollbackError });
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
export function rollbackAfterError(run, error) {
|
|
94
|
+
try {
|
|
95
|
+
run();
|
|
96
|
+
}
|
|
97
|
+
catch (rollbackError) {
|
|
98
|
+
retainRollbackFailure(error, rollbackError);
|
|
99
|
+
}
|
|
100
|
+
throw error;
|
|
101
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@syncular/server",
|
|
3
|
-
"version": "0.30.
|
|
3
|
+
"version": "0.30.7",
|
|
4
4
|
"description": "Syncular server: handleSyncRequest + storage/auth interfaces for the sync protocol",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Benjamin Kniffler",
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
"verify:node": "cd ../.. && bun build ./packages/server/test/sqlite-runtime/verify-node.mjs --target=node --conditions=bun --outfile=./packages/server/.verify-node.built.mjs && node ./packages/server/.verify-node.built.mjs"
|
|
69
69
|
},
|
|
70
70
|
"dependencies": {
|
|
71
|
-
"@syncular/core": "0.30.
|
|
71
|
+
"@syncular/core": "0.30.7"
|
|
72
72
|
},
|
|
73
73
|
"devDependencies": {
|
|
74
74
|
"@electric-sql/pglite": "^0.5.4"
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { retainRollbackFailure } from './storage-errors';
|
|
1
2
|
/**
|
|
2
3
|
* A `PgExecutor` over @electric-sql/pglite (embedded WASM Postgres).
|
|
3
4
|
*
|
|
@@ -64,7 +65,11 @@ export function pgliteExecutor(db: PGlite | PgliteLike): PgExecutor {
|
|
|
64
65
|
await like.exec('COMMIT');
|
|
65
66
|
return result;
|
|
66
67
|
} catch (error) {
|
|
67
|
-
|
|
68
|
+
try {
|
|
69
|
+
await like.exec('ROLLBACK');
|
|
70
|
+
} catch (rollbackError) {
|
|
71
|
+
retainRollbackFailure(error, rollbackError);
|
|
72
|
+
}
|
|
68
73
|
throw error;
|
|
69
74
|
}
|
|
70
75
|
} finally {
|
package/src/push.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { retainRollbackFailure } from './storage-errors';
|
|
1
2
|
/**
|
|
2
3
|
* Push apply (SPEC.md §6) with §3.4 write-path authorization.
|
|
3
4
|
*
|
|
@@ -1661,14 +1662,17 @@ export async function processPushOperationsWithTrace(
|
|
|
1661
1662
|
// a no-op when the finalization already committed.
|
|
1662
1663
|
try {
|
|
1663
1664
|
await tx.rollback();
|
|
1664
|
-
} catch {
|
|
1665
|
-
|
|
1666
|
-
// otherwise mask it.
|
|
1665
|
+
} catch (rollbackError) {
|
|
1666
|
+
retainRollbackFailure(finalizationError, rollbackError);
|
|
1667
1667
|
}
|
|
1668
1668
|
throw finalizationError;
|
|
1669
1669
|
}
|
|
1670
1670
|
}
|
|
1671
|
-
|
|
1671
|
+
try {
|
|
1672
|
+
await tx.rollback();
|
|
1673
|
+
} catch (rollbackError) {
|
|
1674
|
+
retainRollbackFailure(error, rollbackError);
|
|
1675
|
+
}
|
|
1672
1676
|
throw error;
|
|
1673
1677
|
}
|
|
1674
1678
|
}
|
package/src/sqlite-image.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { rollbackAfterError } from './storage-errors';
|
|
1
2
|
/**
|
|
2
3
|
* SQLite-image segment generation (SPEC.md §5.3): a complete SQLite
|
|
3
4
|
* database file carrying one table's whole effective-scope snapshot at
|
|
@@ -124,7 +125,8 @@ export async function writeSqliteImage(
|
|
|
124
125
|
|
|
125
126
|
db.exec('COMMIT');
|
|
126
127
|
} catch (error) {
|
|
127
|
-
|
|
128
|
-
|
|
128
|
+
rollbackAfterError(() => {
|
|
129
|
+
db.exec('ROLLBACK');
|
|
130
|
+
}, error);
|
|
129
131
|
}
|
|
130
132
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { rollbackAfterError } from './storage-errors';
|
|
1
2
|
/**
|
|
2
3
|
* SQLite-backed segment store over the shared synchronous driver.
|
|
3
4
|
*/
|
|
@@ -238,8 +239,9 @@ export class SqliteSegmentStore implements SegmentStore {
|
|
|
238
239
|
}
|
|
239
240
|
this.db.exec('COMMIT');
|
|
240
241
|
} catch (error) {
|
|
241
|
-
|
|
242
|
-
|
|
242
|
+
rollbackAfterError(() => {
|
|
243
|
+
this.db.exec('ROLLBACK');
|
|
244
|
+
}, error);
|
|
243
245
|
}
|
|
244
246
|
return record;
|
|
245
247
|
}
|
package/src/sqlite-storage.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { retainRollbackFailure, rollbackAfterError } from './storage-errors';
|
|
1
2
|
import { validateCommitPruneQuery } from './prune';
|
|
2
3
|
import { StorageQueryError } from './storage-errors';
|
|
3
4
|
import type { CommitPruneQuery, CommitPruneResult } from './storage';
|
|
@@ -469,9 +470,8 @@ class SqliteTransaction implements StorageTransaction {
|
|
|
469
470
|
// connection.
|
|
470
471
|
try {
|
|
471
472
|
this.#storage.db.exec('ROLLBACK');
|
|
472
|
-
} catch {
|
|
473
|
-
|
|
474
|
-
// mask it.
|
|
473
|
+
} catch (rollbackError) {
|
|
474
|
+
retainRollbackFailure(error, rollbackError);
|
|
475
475
|
}
|
|
476
476
|
throw error;
|
|
477
477
|
} finally {
|
|
@@ -725,8 +725,9 @@ END`);
|
|
|
725
725
|
}
|
|
726
726
|
this.db.exec('COMMIT');
|
|
727
727
|
} catch (error) {
|
|
728
|
-
|
|
729
|
-
|
|
728
|
+
rollbackAfterError(() => {
|
|
729
|
+
this.db.exec('ROLLBACK');
|
|
730
|
+
}, error);
|
|
730
731
|
}
|
|
731
732
|
});
|
|
732
733
|
}
|
|
@@ -799,8 +800,9 @@ END`);
|
|
|
799
800
|
.run(partition);
|
|
800
801
|
this.db.exec('COMMIT');
|
|
801
802
|
} catch (error) {
|
|
802
|
-
|
|
803
|
-
|
|
803
|
+
rollbackAfterError(() => {
|
|
804
|
+
this.db.exec('ROLLBACK');
|
|
805
|
+
}, error);
|
|
804
806
|
}
|
|
805
807
|
return {
|
|
806
808
|
partition,
|
|
@@ -1002,8 +1004,9 @@ END`);
|
|
|
1002
1004
|
open = false;
|
|
1003
1005
|
return result;
|
|
1004
1006
|
} catch (error) {
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
+
rollbackAfterError(() => {
|
|
1008
|
+
if (open) this.db.exec('ROLLBACK');
|
|
1009
|
+
}, error);
|
|
1007
1010
|
} finally {
|
|
1008
1011
|
release();
|
|
1009
1012
|
}
|
|
@@ -1085,8 +1088,9 @@ END`);
|
|
|
1085
1088
|
removedCommits: Number(removed.changes),
|
|
1086
1089
|
};
|
|
1087
1090
|
} catch (error) {
|
|
1088
|
-
|
|
1089
|
-
|
|
1091
|
+
rollbackAfterError(() => {
|
|
1092
|
+
this.db.exec('ROLLBACK');
|
|
1093
|
+
}, error);
|
|
1090
1094
|
}
|
|
1091
1095
|
});
|
|
1092
1096
|
}
|
|
@@ -1202,8 +1206,9 @@ END`);
|
|
|
1202
1206
|
this.db.exec('COMMIT');
|
|
1203
1207
|
return toStoredCheckpoint(row);
|
|
1204
1208
|
} catch (error) {
|
|
1205
|
-
|
|
1206
|
-
|
|
1209
|
+
rollbackAfterError(() => {
|
|
1210
|
+
this.db.exec('ROLLBACK');
|
|
1211
|
+
}, error);
|
|
1207
1212
|
}
|
|
1208
1213
|
});
|
|
1209
1214
|
}
|
|
@@ -1260,8 +1265,9 @@ END`);
|
|
|
1260
1265
|
this.db.exec('COMMIT');
|
|
1261
1266
|
return Number(updated.changes) === 1 ? 'activated' : 'stale';
|
|
1262
1267
|
} catch (error) {
|
|
1263
|
-
|
|
1264
|
-
|
|
1268
|
+
rollbackAfterError(() => {
|
|
1269
|
+
this.db.exec('ROLLBACK');
|
|
1270
|
+
}, error);
|
|
1265
1271
|
}
|
|
1266
1272
|
});
|
|
1267
1273
|
}
|
package/src/storage-errors.ts
CHANGED
|
@@ -150,3 +150,22 @@ export function isD1ConstraintError(error: unknown): boolean {
|
|
|
150
150
|
)
|
|
151
151
|
);
|
|
152
152
|
}
|
|
153
|
+
|
|
154
|
+
/** Preserve the operation failure when transaction cleanup also fails. */
|
|
155
|
+
export function retainRollbackFailure(
|
|
156
|
+
error: unknown,
|
|
157
|
+
rollbackError: unknown,
|
|
158
|
+
): void {
|
|
159
|
+
if (error instanceof Error && !('rollbackError' in error)) {
|
|
160
|
+
Object.defineProperty(error, 'rollbackError', { value: rollbackError });
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export function rollbackAfterError(run: () => void, error: unknown): never {
|
|
165
|
+
try {
|
|
166
|
+
run();
|
|
167
|
+
} catch (rollbackError) {
|
|
168
|
+
retainRollbackFailure(error, rollbackError);
|
|
169
|
+
}
|
|
170
|
+
throw error;
|
|
171
|
+
}
|