@syncular/client 0.15.31 → 0.15.33
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/schema.js +12 -3
- package/package.json +3 -3
- package/src/schema.ts +15 -3
package/dist/schema.js
CHANGED
|
@@ -230,9 +230,18 @@ function createFtsProjection(db, table, index) {
|
|
|
230
230
|
...index.columns.map((column) => `new.${quoteIdent(column)}`),
|
|
231
231
|
].join(', ');
|
|
232
232
|
const deleteFor = (value) => `DELETE FROM ${quoteIdent(index.name)} WHERE ${quoteIdent(FTS_SOURCE_ID_COLUMN)} = ${value}`;
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
233
|
+
// A clean insert cannot already have a projection row because the source
|
|
234
|
+
// primary key is unique. Keep clean inserts linear by moving replacement
|
|
235
|
+
// cleanup behind an indexed source-table existence check. SQLite does not
|
|
236
|
+
// reliably invoke DELETE triggers for the row displaced by REPLACE.
|
|
237
|
+
for (const suffix of ['bi', 'ai', 'ad', 'au']) {
|
|
238
|
+
db.exec(`DROP TRIGGER IF EXISTS ${quoteIdent(`${index.name}_${suffix}`)}`);
|
|
239
|
+
}
|
|
240
|
+
const replacementExists = `EXISTS (SELECT 1 FROM ${quoteIdent(table.name)} WHERE ${quoteIdent(table.primaryKey)} = new.${quoteIdent(table.primaryKey)})`;
|
|
241
|
+
db.exec(`CREATE TRIGGER ${quoteIdent(`${index.name}_bi`)} BEFORE INSERT ON ${quoteIdent(table.name)} WHEN ${replacementExists} BEGIN ${deleteFor(newSourceId)}; END`);
|
|
242
|
+
db.exec(`CREATE TRIGGER ${quoteIdent(`${index.name}_ai`)} AFTER INSERT ON ${quoteIdent(table.name)} BEGIN INSERT INTO ${quoteIdent(index.name)} (${projectionColumns}) VALUES (${newValues}); END`);
|
|
243
|
+
db.exec(`CREATE TRIGGER ${quoteIdent(`${index.name}_ad`)} AFTER DELETE ON ${quoteIdent(table.name)} BEGIN ${deleteFor(oldSourceId)}; END`);
|
|
244
|
+
db.exec(`CREATE TRIGGER ${quoteIdent(`${index.name}_au`)} AFTER UPDATE ON ${quoteIdent(table.name)} BEGIN ${deleteFor(oldSourceId)}; ${deleteFor(newSourceId)}; INSERT INTO ${quoteIdent(index.name)} (${projectionColumns}) VALUES (${newValues}); END`);
|
|
236
245
|
if (!existed) {
|
|
237
246
|
db.exec(`INSERT INTO ${quoteIdent(index.name)} (${projectionColumns}) SELECT ${sourceId}, ${indexedColumns.join(', ')} FROM ${quoteIdent(table.name)}`);
|
|
238
247
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@syncular/client",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.33",
|
|
4
4
|
"description": "Syncular TypeScript client core — offline-first sync over SQLite (WASM/OPFS, Bun, Node)",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Benjamin Kniffler",
|
|
@@ -81,7 +81,7 @@
|
|
|
81
81
|
},
|
|
82
82
|
"dependencies": {
|
|
83
83
|
"@sqlite.org/sqlite-wasm": "^3.53.0-build1",
|
|
84
|
-
"@syncular/core": "0.15.
|
|
84
|
+
"@syncular/core": "0.15.33"
|
|
85
85
|
},
|
|
86
86
|
"peerDependencies": {
|
|
87
87
|
"better-sqlite3": ">=11"
|
|
@@ -92,7 +92,7 @@
|
|
|
92
92
|
}
|
|
93
93
|
},
|
|
94
94
|
"devDependencies": {
|
|
95
|
-
"@syncular/server": "0.15.
|
|
95
|
+
"@syncular/server": "0.15.33",
|
|
96
96
|
"@types/better-sqlite3": "^7.6.13",
|
|
97
97
|
"better-sqlite3": "^12.11.1"
|
|
98
98
|
}
|
package/src/schema.ts
CHANGED
|
@@ -372,14 +372,26 @@ function createFtsProjection(
|
|
|
372
372
|
const deleteFor = (value: string) =>
|
|
373
373
|
`DELETE FROM ${quoteIdent(index.name)} WHERE ${quoteIdent(FTS_SOURCE_ID_COLUMN)} = ${value}`;
|
|
374
374
|
|
|
375
|
+
// A clean insert cannot already have a projection row because the source
|
|
376
|
+
// primary key is unique. Keep clean inserts linear by moving replacement
|
|
377
|
+
// cleanup behind an indexed source-table existence check. SQLite does not
|
|
378
|
+
// reliably invoke DELETE triggers for the row displaced by REPLACE.
|
|
379
|
+
for (const suffix of ['bi', 'ai', 'ad', 'au']) {
|
|
380
|
+
db.exec(`DROP TRIGGER IF EXISTS ${quoteIdent(`${index.name}_${suffix}`)}`);
|
|
381
|
+
}
|
|
382
|
+
const replacementExists = `EXISTS (SELECT 1 FROM ${quoteIdent(table.name)} WHERE ${quoteIdent(table.primaryKey)} = new.${quoteIdent(table.primaryKey)})`;
|
|
383
|
+
|
|
384
|
+
db.exec(
|
|
385
|
+
`CREATE TRIGGER ${quoteIdent(`${index.name}_bi`)} BEFORE INSERT ON ${quoteIdent(table.name)} WHEN ${replacementExists} BEGIN ${deleteFor(newSourceId)}; END`,
|
|
386
|
+
);
|
|
375
387
|
db.exec(
|
|
376
|
-
`CREATE TRIGGER
|
|
388
|
+
`CREATE TRIGGER ${quoteIdent(`${index.name}_ai`)} AFTER INSERT ON ${quoteIdent(table.name)} BEGIN INSERT INTO ${quoteIdent(index.name)} (${projectionColumns}) VALUES (${newValues}); END`,
|
|
377
389
|
);
|
|
378
390
|
db.exec(
|
|
379
|
-
`CREATE TRIGGER
|
|
391
|
+
`CREATE TRIGGER ${quoteIdent(`${index.name}_ad`)} AFTER DELETE ON ${quoteIdent(table.name)} BEGIN ${deleteFor(oldSourceId)}; END`,
|
|
380
392
|
);
|
|
381
393
|
db.exec(
|
|
382
|
-
`CREATE TRIGGER
|
|
394
|
+
`CREATE TRIGGER ${quoteIdent(`${index.name}_au`)} AFTER UPDATE ON ${quoteIdent(table.name)} BEGIN ${deleteFor(oldSourceId)}; ${deleteFor(newSourceId)}; INSERT INTO ${quoteIdent(index.name)} (${projectionColumns}) VALUES (${newValues}); END`,
|
|
383
395
|
);
|
|
384
396
|
|
|
385
397
|
if (!existed) {
|