@peers-app/peers-sdk 0.8.2 → 0.8.3
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/data/change-tracking.d.ts +34 -0
- package/dist/data/change-tracking.js +86 -0
- package/dist/data/orm/sql-db.d.ts +15 -0
- package/dist/data/orm/sql.data-source.d.ts +38 -0
- package/dist/data/orm/sql.data-source.js +146 -0
- package/dist/device/binary-peer-connection.js +1 -1
- package/dist/types/peer-device.js +1 -0
- package/package.json +1 -1
|
@@ -106,6 +106,40 @@ export declare class ChangeTrackingTable extends SQLDataSource<IChangeRecord> {
|
|
|
106
106
|
* Override to serialize value field to JSON string
|
|
107
107
|
*/
|
|
108
108
|
update(record: IChangeRecord): Promise<IChangeRecord>;
|
|
109
|
+
/**
|
|
110
|
+
* Synchronous insert for use within transactions.
|
|
111
|
+
* Table must be initialized before calling this method.
|
|
112
|
+
*/
|
|
113
|
+
insertSync(record: IChangeRecord): IChangeRecord;
|
|
114
|
+
/**
|
|
115
|
+
* Synchronous update for use within transactions.
|
|
116
|
+
* Table must be initialized before calling this method.
|
|
117
|
+
*/
|
|
118
|
+
updateSync(record: IChangeRecord): IChangeRecord;
|
|
119
|
+
/**
|
|
120
|
+
* Bulk insert multiple change records in a single transaction.
|
|
121
|
+
* Much faster than inserting records one at a time.
|
|
122
|
+
* All inserts succeed or all fail together.
|
|
123
|
+
*/
|
|
124
|
+
bulkInsert(records: IChangeRecord[]): Promise<IChangeRecord[]>;
|
|
125
|
+
/**
|
|
126
|
+
* Synchronous batch mark superseded for use within transactions.
|
|
127
|
+
* Table must be initialized before calling this method.
|
|
128
|
+
*/
|
|
129
|
+
batchMarkSupersededSync(updates: Array<{
|
|
130
|
+
changeId: string;
|
|
131
|
+
supersededAt: number;
|
|
132
|
+
}>): void;
|
|
133
|
+
/**
|
|
134
|
+
* Synchronous delete changes for use within transactions.
|
|
135
|
+
* Table must be initialized before calling this method.
|
|
136
|
+
*/
|
|
137
|
+
deleteChangesSync(changeIds: string[]): void;
|
|
138
|
+
/**
|
|
139
|
+
* Synchronous delete superseded changes for use within transactions.
|
|
140
|
+
* Table must be initialized before calling this method.
|
|
141
|
+
*/
|
|
142
|
+
deleteSupersededChangesOlderThanSync(tableName: string, beforeTimestamp: number): void;
|
|
109
143
|
/**
|
|
110
144
|
* Deserialize the value field if it's a JSON string
|
|
111
145
|
* For path "/" (full object changes), use fromJSONString to handle special types
|
|
@@ -206,6 +206,92 @@ class ChangeTrackingTable extends orm_1.SQLDataSource {
|
|
|
206
206
|
}
|
|
207
207
|
return super.update(record);
|
|
208
208
|
}
|
|
209
|
+
//================================================================================================
|
|
210
|
+
// Synchronous methods for use within transactions
|
|
211
|
+
//================================================================================================
|
|
212
|
+
/**
|
|
213
|
+
* Synchronous insert for use within transactions.
|
|
214
|
+
* Table must be initialized before calling this method.
|
|
215
|
+
*/
|
|
216
|
+
insertSync(record) {
|
|
217
|
+
if (record.value !== undefined) {
|
|
218
|
+
record = { ...record, value: (0, serial_json_1.toJSONString)(record.value) };
|
|
219
|
+
}
|
|
220
|
+
return super.insertSync(record);
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Synchronous update for use within transactions.
|
|
224
|
+
* Table must be initialized before calling this method.
|
|
225
|
+
*/
|
|
226
|
+
updateSync(record) {
|
|
227
|
+
if (record.value !== undefined) {
|
|
228
|
+
record = { ...record, value: (0, serial_json_1.toJSONString)(record.value) };
|
|
229
|
+
}
|
|
230
|
+
return super.updateSync(record);
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Bulk insert multiple change records in a single transaction.
|
|
234
|
+
* Much faster than inserting records one at a time.
|
|
235
|
+
* All inserts succeed or all fail together.
|
|
236
|
+
*/
|
|
237
|
+
async bulkInsert(records) {
|
|
238
|
+
if (records.length === 0)
|
|
239
|
+
return [];
|
|
240
|
+
await this.initTable();
|
|
241
|
+
if (!this.db.runInTransaction) {
|
|
242
|
+
// Fallback for databases that don't support transactions
|
|
243
|
+
const results = [];
|
|
244
|
+
for (const record of records) {
|
|
245
|
+
results.push(await this.insert(record));
|
|
246
|
+
}
|
|
247
|
+
return results;
|
|
248
|
+
}
|
|
249
|
+
return this.db.runInTransaction(() => {
|
|
250
|
+
return records.map(record => this.insertSync(record));
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Synchronous batch mark superseded for use within transactions.
|
|
255
|
+
* Table must be initialized before calling this method.
|
|
256
|
+
*/
|
|
257
|
+
batchMarkSupersededSync(updates) {
|
|
258
|
+
if (updates.length === 0)
|
|
259
|
+
return;
|
|
260
|
+
// Build a CASE statement for bulk update
|
|
261
|
+
const changeIds = updates.map(u => u.changeId);
|
|
262
|
+
const caseStatement = updates.map(u => `WHEN '${u.changeId}' THEN ${u.supersededAt}`).join('\n ');
|
|
263
|
+
this.db.execSync(`
|
|
264
|
+
UPDATE "${this.tableName}"
|
|
265
|
+
SET supersededAt = CASE changeId
|
|
266
|
+
${caseStatement}
|
|
267
|
+
END
|
|
268
|
+
WHERE changeId IN (${changeIds.map(() => '?').join(',')})
|
|
269
|
+
`, changeIds);
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Synchronous delete changes for use within transactions.
|
|
273
|
+
* Table must be initialized before calling this method.
|
|
274
|
+
*/
|
|
275
|
+
deleteChangesSync(changeIds) {
|
|
276
|
+
if (changeIds.length === 0)
|
|
277
|
+
return;
|
|
278
|
+
this.db.execSync(`
|
|
279
|
+
DELETE FROM "${this.tableName}"
|
|
280
|
+
WHERE changeId IN (${changeIds.map(c => "?").join()})
|
|
281
|
+
`, changeIds);
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Synchronous delete superseded changes for use within transactions.
|
|
285
|
+
* Table must be initialized before calling this method.
|
|
286
|
+
*/
|
|
287
|
+
deleteSupersededChangesOlderThanSync(tableName, beforeTimestamp) {
|
|
288
|
+
this.db.execSync(`
|
|
289
|
+
DELETE FROM "${this.tableName}"
|
|
290
|
+
WHERE tableName = ?
|
|
291
|
+
AND supersededAt IS NOT NULL
|
|
292
|
+
AND supersededAt < ?
|
|
293
|
+
`, [tableName, beforeTimestamp]);
|
|
294
|
+
}
|
|
209
295
|
/**
|
|
210
296
|
* Deserialize the value field if it's a JSON string
|
|
211
297
|
* For path "/" (full object changes), use fromJSONString to handle special types
|
|
@@ -3,4 +3,19 @@ export interface ISqlDb {
|
|
|
3
3
|
all: (sql: string, params?: any) => Promise<any[]>;
|
|
4
4
|
exec: (sql: string, params?: any) => Promise<void>;
|
|
5
5
|
close: () => Promise<void>;
|
|
6
|
+
execSync?: (sql: string, params?: any) => void;
|
|
7
|
+
getSync?: (sql: string, params?: any) => any;
|
|
8
|
+
allSync?: (sql: string, params?: any) => any[];
|
|
9
|
+
/**
|
|
10
|
+
* Execute multiple operations in a single SQLite transaction.
|
|
11
|
+
* All operations within the callback are committed atomically.
|
|
12
|
+
* If any operation throws, the entire transaction is rolled back.
|
|
13
|
+
*
|
|
14
|
+
* Note: The callback must be synchronous since better-sqlite3 transactions are synchronous.
|
|
15
|
+
* Use execSync/getSync/allSync within the transaction callback.
|
|
16
|
+
*
|
|
17
|
+
* @param fn - Synchronous function containing database operations
|
|
18
|
+
* @returns The return value of the callback function
|
|
19
|
+
*/
|
|
20
|
+
runInTransaction?: <R>(fn: () => R) => R;
|
|
6
21
|
}
|
|
@@ -32,6 +32,44 @@ export declare class SQLDataSource<T extends {
|
|
|
32
32
|
dropTableIfExists(): Promise<void>;
|
|
33
33
|
clearTable(): Promise<void>;
|
|
34
34
|
initRecord(data?: Partial<T>): T;
|
|
35
|
+
/**
|
|
36
|
+
* Synchronous insert for use within transactions.
|
|
37
|
+
* Table must be initialized before calling this method.
|
|
38
|
+
*/
|
|
39
|
+
insertSync(record: T): T;
|
|
40
|
+
/**
|
|
41
|
+
* Synchronous update for use within transactions.
|
|
42
|
+
* Table must be initialized before calling this method.
|
|
43
|
+
*/
|
|
44
|
+
updateSync(record: T): T;
|
|
45
|
+
/**
|
|
46
|
+
* Synchronous delete for use within transactions.
|
|
47
|
+
* Table must be initialized before calling this method.
|
|
48
|
+
*/
|
|
49
|
+
deleteSync(idOrRecord: string | T): void;
|
|
50
|
+
/**
|
|
51
|
+
* Synchronous save (insert or update) for use within transactions.
|
|
52
|
+
* Table must be initialized before calling this method.
|
|
53
|
+
*/
|
|
54
|
+
saveSync(record: T): T;
|
|
55
|
+
/**
|
|
56
|
+
* Insert multiple records in a single transaction.
|
|
57
|
+
* Much faster than inserting records one at a time.
|
|
58
|
+
* All inserts succeed or all fail together.
|
|
59
|
+
*/
|
|
60
|
+
bulkInsert(records: T[]): Promise<T[]>;
|
|
61
|
+
/**
|
|
62
|
+
* Delete multiple records by ID in a single transaction.
|
|
63
|
+
* Much faster than deleting records one at a time.
|
|
64
|
+
* All deletes succeed or all fail together.
|
|
65
|
+
*/
|
|
66
|
+
bulkDelete(ids: string[]): Promise<void>;
|
|
67
|
+
/**
|
|
68
|
+
* Save (insert or update) multiple records in a single transaction.
|
|
69
|
+
* Much faster than saving records one at a time.
|
|
70
|
+
* All saves succeed or all fail together.
|
|
71
|
+
*/
|
|
72
|
+
bulkSave(records: T[]): Promise<T[]>;
|
|
35
73
|
}
|
|
36
74
|
export declare function jsValueToSqlValue(value: any, escapeQuote?: boolean): any;
|
|
37
75
|
export declare function sqlValueToJsValue(value: any, fieldType: FieldType, isArray: boolean): any;
|
|
@@ -312,6 +312,152 @@ class SQLDataSource {
|
|
|
312
312
|
}
|
|
313
313
|
return { ...defaults, ...data };
|
|
314
314
|
}
|
|
315
|
+
//================================================================================================
|
|
316
|
+
// Synchronous methods for use within transactions
|
|
317
|
+
//================================================================================================
|
|
318
|
+
/**
|
|
319
|
+
* Synchronous insert for use within transactions.
|
|
320
|
+
* Table must be initialized before calling this method.
|
|
321
|
+
*/
|
|
322
|
+
insertSync(record) {
|
|
323
|
+
if (!record[this.metaData.primaryKeyName]) {
|
|
324
|
+
// @ts-expect-error - need to figure out how to get typescript to understand what's going on here
|
|
325
|
+
record[this.metaData.primaryKeyName] = (0, utils_1.newid)();
|
|
326
|
+
}
|
|
327
|
+
// validate data
|
|
328
|
+
try {
|
|
329
|
+
this.schema.parse(record);
|
|
330
|
+
}
|
|
331
|
+
catch (e) {
|
|
332
|
+
throw new Error(`Validation on insert failed for ${this.tableName}: ${e}`);
|
|
333
|
+
}
|
|
334
|
+
const fields = this.metaData.fields.map(field => `"${field.name}"`).join(', ');
|
|
335
|
+
const values = this.metaData.fields.map(field => jsValueToSqlValue(record[field.name]));
|
|
336
|
+
const placeholders = values.map(v => '?').join(', ');
|
|
337
|
+
const sql = `INSERT INTO "${this.tableName}"(${fields}) VALUES(${placeholders})`;
|
|
338
|
+
this.db.execSync(sql, values);
|
|
339
|
+
return record;
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* Synchronous update for use within transactions.
|
|
343
|
+
* Table must be initialized before calling this method.
|
|
344
|
+
*/
|
|
345
|
+
updateSync(record) {
|
|
346
|
+
if (!record[this.metaData.primaryKeyName]) {
|
|
347
|
+
// @ts-expect-error - need to figure out how to get typescript to understand what's going on here
|
|
348
|
+
record[this.metaData.primaryKeyName] = (0, utils_1.newid)();
|
|
349
|
+
}
|
|
350
|
+
// validate data
|
|
351
|
+
this.schema.parse(record);
|
|
352
|
+
const primaryKey = record[this.metaData.primaryKeyName];
|
|
353
|
+
const fields = this.metaData.fields.map(field => {
|
|
354
|
+
if (field.name === this.metaData.primaryKeyName)
|
|
355
|
+
return;
|
|
356
|
+
return `"${field.name}" = ?`;
|
|
357
|
+
}).filter(v => v !== undefined).join(',\n');
|
|
358
|
+
const values = this.metaData.fields.map(field => {
|
|
359
|
+
if (field.name === this.metaData.primaryKeyName)
|
|
360
|
+
return;
|
|
361
|
+
return jsValueToSqlValue(record[field.name]);
|
|
362
|
+
}).filter(v => v !== undefined);
|
|
363
|
+
const sql = [
|
|
364
|
+
`UPDATE "${this.tableName}"`,
|
|
365
|
+
`SET ${fields}`,
|
|
366
|
+
`WHERE "${this.metaData.primaryKeyName}" = ?`,
|
|
367
|
+
].join('\n');
|
|
368
|
+
this.db.execSync(sql, [...values, primaryKey]);
|
|
369
|
+
return record;
|
|
370
|
+
}
|
|
371
|
+
/**
|
|
372
|
+
* Synchronous delete for use within transactions.
|
|
373
|
+
* Table must be initialized before calling this method.
|
|
374
|
+
*/
|
|
375
|
+
deleteSync(idOrRecord) {
|
|
376
|
+
const primaryKey = typeof idOrRecord === 'string' ? idOrRecord : idOrRecord[this.metaData.primaryKeyName];
|
|
377
|
+
const sql = `DELETE FROM "${this.tableName}" WHERE "${this.metaData.primaryKeyName}" = ?`;
|
|
378
|
+
this.db.execSync(sql, [primaryKey]);
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* Synchronous save (insert or update) for use within transactions.
|
|
382
|
+
* Table must be initialized before calling this method.
|
|
383
|
+
*/
|
|
384
|
+
saveSync(record) {
|
|
385
|
+
const primaryKey = record[this.metaData.primaryKeyName];
|
|
386
|
+
if (primaryKey) {
|
|
387
|
+
const existingRecord = this.db.getSync(`SELECT "${this.metaData.primaryKeyName}" FROM "${this.tableName}" WHERE ${this.metaData.primaryKeyName} = ?`, [primaryKey]);
|
|
388
|
+
if (existingRecord) {
|
|
389
|
+
return this.updateSync(record);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
return this.insertSync(record);
|
|
393
|
+
}
|
|
394
|
+
//================================================================================================
|
|
395
|
+
// Bulk operations (use transactions for atomicity and performance)
|
|
396
|
+
//================================================================================================
|
|
397
|
+
/**
|
|
398
|
+
* Insert multiple records in a single transaction.
|
|
399
|
+
* Much faster than inserting records one at a time.
|
|
400
|
+
* All inserts succeed or all fail together.
|
|
401
|
+
*/
|
|
402
|
+
async bulkInsert(records) {
|
|
403
|
+
if (records.length === 0)
|
|
404
|
+
return [];
|
|
405
|
+
await this.initTable();
|
|
406
|
+
if (!this.db.runInTransaction) {
|
|
407
|
+
// Fallback for databases that don't support transactions
|
|
408
|
+
const results = [];
|
|
409
|
+
for (const record of records) {
|
|
410
|
+
results.push(await this.insert(record));
|
|
411
|
+
}
|
|
412
|
+
return results;
|
|
413
|
+
}
|
|
414
|
+
return this.db.runInTransaction(() => {
|
|
415
|
+
return records.map(record => this.insertSync(record));
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
/**
|
|
419
|
+
* Delete multiple records by ID in a single transaction.
|
|
420
|
+
* Much faster than deleting records one at a time.
|
|
421
|
+
* All deletes succeed or all fail together.
|
|
422
|
+
*/
|
|
423
|
+
async bulkDelete(ids) {
|
|
424
|
+
if (ids.length === 0)
|
|
425
|
+
return;
|
|
426
|
+
await this.initTable();
|
|
427
|
+
if (!this.db.runInTransaction) {
|
|
428
|
+
// Fallback for databases that don't support transactions
|
|
429
|
+
for (const id of ids) {
|
|
430
|
+
await this.delete(id);
|
|
431
|
+
}
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
this.db.runInTransaction(() => {
|
|
435
|
+
for (const id of ids) {
|
|
436
|
+
this.deleteSync(id);
|
|
437
|
+
}
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
/**
|
|
441
|
+
* Save (insert or update) multiple records in a single transaction.
|
|
442
|
+
* Much faster than saving records one at a time.
|
|
443
|
+
* All saves succeed or all fail together.
|
|
444
|
+
*/
|
|
445
|
+
async bulkSave(records) {
|
|
446
|
+
if (records.length === 0)
|
|
447
|
+
return [];
|
|
448
|
+
await this.initTable();
|
|
449
|
+
if (!this.db.runInTransaction) {
|
|
450
|
+
// Fallback for databases that don't support transactions
|
|
451
|
+
const results = [];
|
|
452
|
+
for (const record of records) {
|
|
453
|
+
results.push(await this.save(record));
|
|
454
|
+
}
|
|
455
|
+
return results;
|
|
456
|
+
}
|
|
457
|
+
return this.db.runInTransaction(() => {
|
|
458
|
+
return records.map(record => this.saveSync(record));
|
|
459
|
+
});
|
|
460
|
+
}
|
|
315
461
|
}
|
|
316
462
|
exports.SQLDataSource = SQLDataSource;
|
|
317
463
|
function jsValueToSqlValue(value, escapeQuote = false) {
|
|
@@ -280,7 +280,7 @@ function handleRPCMessage(bytes, handlers, callbacks, sendRPCData) {
|
|
|
280
280
|
if (message.type === 'call') {
|
|
281
281
|
const handler = handlers[message.eventName];
|
|
282
282
|
if (!handler) {
|
|
283
|
-
console.
|
|
283
|
+
console.warn(`No handler for event: ${message.eventName}`);
|
|
284
284
|
return;
|
|
285
285
|
}
|
|
286
286
|
try {
|