@powersync/common 1.41.1 → 1.43.0
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/bundle.cjs +4539 -177
- package/dist/bundle.cjs.map +1 -1
- package/dist/bundle.mjs +4539 -177
- package/dist/bundle.mjs.map +1 -1
- package/dist/bundle.node.cjs +21 -10
- package/dist/bundle.node.cjs.map +1 -1
- package/dist/bundle.node.mjs +21 -10
- package/dist/bundle.node.mjs.map +1 -1
- package/dist/index.d.cts +72 -13
- package/lib/client/sync/stream/AbstractRemote.js +10 -4
- package/lib/client/sync/stream/AbstractRemote.js.map +1 -1
- package/lib/client/triggers/TriggerManager.d.ts +71 -12
- package/lib/client/triggers/TriggerManagerImpl.js +10 -5
- package/lib/client/triggers/TriggerManagerImpl.js.map +1 -1
- package/package.json +2 -2
- package/src/client/sync/stream/AbstractRemote.ts +15 -5
- package/src/client/triggers/TriggerManager.ts +79 -12
- package/src/client/triggers/TriggerManagerImpl.ts +12 -6
|
@@ -14,8 +14,11 @@ export enum DiffTriggerOperation {
|
|
|
14
14
|
* @experimental
|
|
15
15
|
* Diffs created by {@link TriggerManager#createDiffTrigger} are stored in a temporary table.
|
|
16
16
|
* This is the base record structure for all diff records.
|
|
17
|
+
*
|
|
18
|
+
* @template TOperationId - The type for `operation_id`. Defaults to `number` as returned by default SQLite database queries.
|
|
19
|
+
* Use `string` for full 64-bit precision when using `{ castOperationIdAsText: true }` option.
|
|
17
20
|
*/
|
|
18
|
-
export interface BaseTriggerDiffRecord {
|
|
21
|
+
export interface BaseTriggerDiffRecord<TOperationId extends string | number = number> {
|
|
19
22
|
/**
|
|
20
23
|
* The modified row's `id` column value.
|
|
21
24
|
*/
|
|
@@ -24,6 +27,13 @@ export interface BaseTriggerDiffRecord {
|
|
|
24
27
|
* The operation performed which created this record.
|
|
25
28
|
*/
|
|
26
29
|
operation: DiffTriggerOperation;
|
|
30
|
+
/**
|
|
31
|
+
* Auto-incrementing primary key for the operation.
|
|
32
|
+
* Defaults to number as returned by database queries (wa-sqlite returns lower 32 bits).
|
|
33
|
+
* Can be string for full 64-bit precision when using `{ castOperationIdAsText: true }` option.
|
|
34
|
+
*/
|
|
35
|
+
operation_id: TOperationId;
|
|
36
|
+
|
|
27
37
|
/**
|
|
28
38
|
* Time the change operation was recorded.
|
|
29
39
|
* This is in ISO 8601 format, e.g. `2023-10-01T12:00:00.000Z`.
|
|
@@ -37,7 +47,8 @@ export interface BaseTriggerDiffRecord {
|
|
|
37
47
|
* This record contains the new value and optionally the previous value.
|
|
38
48
|
* Values are stored as JSON strings.
|
|
39
49
|
*/
|
|
40
|
-
export interface TriggerDiffUpdateRecord extends
|
|
50
|
+
export interface TriggerDiffUpdateRecord<TOperationId extends string | number = number>
|
|
51
|
+
extends BaseTriggerDiffRecord<TOperationId> {
|
|
41
52
|
operation: DiffTriggerOperation.UPDATE;
|
|
42
53
|
/**
|
|
43
54
|
* The updated state of the row in JSON string format.
|
|
@@ -54,7 +65,8 @@ export interface TriggerDiffUpdateRecord extends BaseTriggerDiffRecord {
|
|
|
54
65
|
* Represents a diff record for a SQLite INSERT operation.
|
|
55
66
|
* This record contains the new value represented as a JSON string.
|
|
56
67
|
*/
|
|
57
|
-
export interface TriggerDiffInsertRecord extends
|
|
68
|
+
export interface TriggerDiffInsertRecord<TOperationId extends string | number = number>
|
|
69
|
+
extends BaseTriggerDiffRecord<TOperationId> {
|
|
58
70
|
operation: DiffTriggerOperation.INSERT;
|
|
59
71
|
/**
|
|
60
72
|
* The value of the row, at the time of INSERT, in JSON string format.
|
|
@@ -67,7 +79,8 @@ export interface TriggerDiffInsertRecord extends BaseTriggerDiffRecord {
|
|
|
67
79
|
* Represents a diff record for a SQLite DELETE operation.
|
|
68
80
|
* This record contains the new value represented as a JSON string.
|
|
69
81
|
*/
|
|
70
|
-
export interface TriggerDiffDeleteRecord extends
|
|
82
|
+
export interface TriggerDiffDeleteRecord<TOperationId extends string | number = number>
|
|
83
|
+
extends BaseTriggerDiffRecord<TOperationId> {
|
|
71
84
|
operation: DiffTriggerOperation.DELETE;
|
|
72
85
|
/**
|
|
73
86
|
* The value of the row, before the DELETE operation, in JSON string format.
|
|
@@ -82,27 +95,53 @@ export interface TriggerDiffDeleteRecord extends BaseTriggerDiffRecord {
|
|
|
82
95
|
*
|
|
83
96
|
* Querying the DIFF table directly with {@link TriggerDiffHandlerContext#withDiff} will return records
|
|
84
97
|
* with the structure of this type.
|
|
98
|
+
*
|
|
99
|
+
* @template TOperationId - The type for `operation_id`. Defaults to `number` as returned by database queries.
|
|
100
|
+
* Use `string` for full 64-bit precision when using `{ castOperationIdAsText: true }` option.
|
|
101
|
+
*
|
|
85
102
|
* @example
|
|
86
103
|
* ```typescript
|
|
104
|
+
* // Default: operation_id is number
|
|
87
105
|
* const diffs = await context.withDiff<TriggerDiffRecord>('SELECT * FROM DIFF');
|
|
88
|
-
*
|
|
106
|
+
*
|
|
107
|
+
* // With string operation_id for full precision
|
|
108
|
+
* const diffsWithString = await context.withDiff<TriggerDiffRecord<string>>(
|
|
109
|
+
* 'SELECT * FROM DIFF',
|
|
110
|
+
* undefined,
|
|
111
|
+
* { castOperationIdAsText: true }
|
|
112
|
+
* );
|
|
89
113
|
* ```
|
|
90
114
|
*/
|
|
91
|
-
export type TriggerDiffRecord
|
|
115
|
+
export type TriggerDiffRecord<TOperationId extends string | number = number> =
|
|
116
|
+
| TriggerDiffUpdateRecord<TOperationId>
|
|
117
|
+
| TriggerDiffInsertRecord<TOperationId>
|
|
118
|
+
| TriggerDiffDeleteRecord<TOperationId>;
|
|
92
119
|
|
|
93
120
|
/**
|
|
94
121
|
* @experimental
|
|
95
122
|
* Querying the DIFF table directly with {@link TriggerDiffHandlerContext#withExtractedDiff} will return records
|
|
96
123
|
* with the tracked columns extracted from the JSON value.
|
|
97
124
|
* This type represents the structure of such records.
|
|
125
|
+
*
|
|
126
|
+
* @template T - The type for the extracted columns from the tracked JSON value.
|
|
127
|
+
* @template TOperationId - The type for `operation_id`. Defaults to `number` as returned by database queries.
|
|
128
|
+
* Use `string` for full 64-bit precision when using `{ castOperationIdAsText: true }` option.
|
|
129
|
+
*
|
|
98
130
|
* @example
|
|
99
131
|
* ```typescript
|
|
132
|
+
* // Default: operation_id is number
|
|
100
133
|
* const diffs = await context.withExtractedDiff<ExtractedTriggerDiffRecord<{id: string, name: string}>>('SELECT * FROM DIFF');
|
|
101
|
-
*
|
|
134
|
+
*
|
|
135
|
+
* // With string operation_id for full precision
|
|
136
|
+
* const diffsWithString = await context.withExtractedDiff<ExtractedTriggerDiffRecord<{id: string, name: string}, string>>(
|
|
137
|
+
* 'SELECT * FROM DIFF',
|
|
138
|
+
* undefined,
|
|
139
|
+
* { castOperationIdAsText: true }
|
|
140
|
+
* );
|
|
102
141
|
* ```
|
|
103
142
|
*/
|
|
104
|
-
export type ExtractedTriggerDiffRecord<T> = T & {
|
|
105
|
-
[K in keyof Omit<BaseTriggerDiffRecord
|
|
143
|
+
export type ExtractedTriggerDiffRecord<T, TOperationId extends string | number = number> = T & {
|
|
144
|
+
[K in keyof Omit<BaseTriggerDiffRecord<TOperationId>, 'id'> as `__${string & K}`]: TriggerDiffRecord<TOperationId>[K];
|
|
106
145
|
} & {
|
|
107
146
|
__previous_value?: string;
|
|
108
147
|
};
|
|
@@ -183,6 +222,21 @@ export interface CreateDiffTriggerOptions extends BaseCreateDiffTriggerOptions {
|
|
|
183
222
|
*/
|
|
184
223
|
export type TriggerRemoveCallback = () => Promise<void>;
|
|
185
224
|
|
|
225
|
+
/**
|
|
226
|
+
* @experimental
|
|
227
|
+
* Options for {@link TriggerDiffHandlerContext#withDiff}.
|
|
228
|
+
*/
|
|
229
|
+
export interface WithDiffOptions {
|
|
230
|
+
/**
|
|
231
|
+
* If true, casts `operation_id` as TEXT in the internal CTE to preserve full 64-bit precision.
|
|
232
|
+
* Use this when you need to ensure `operation_id` is treated as a string to avoid precision loss
|
|
233
|
+
* for values exceeding JavaScript's Number.MAX_SAFE_INTEGER.
|
|
234
|
+
*
|
|
235
|
+
* When enabled, use {@link TriggerDiffRecord}<string> to type the result correctly.
|
|
236
|
+
*/
|
|
237
|
+
castOperationIdAsText?: boolean;
|
|
238
|
+
}
|
|
239
|
+
|
|
186
240
|
/**
|
|
187
241
|
* @experimental
|
|
188
242
|
* Context for the `onChange` handler provided to {@link TriggerManager#trackTableDiff}.
|
|
@@ -200,9 +254,10 @@ export interface TriggerDiffHandlerContext extends LockContext {
|
|
|
200
254
|
* The `DIFF` table is of the form described in {@link TriggerManager#createDiffTrigger}
|
|
201
255
|
* ```sql
|
|
202
256
|
* CREATE TEMP DIFF (
|
|
257
|
+
* operation_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
203
258
|
* id TEXT,
|
|
204
259
|
* operation TEXT,
|
|
205
|
-
* timestamp TEXT
|
|
260
|
+
* timestamp TEXT,
|
|
206
261
|
* value TEXT,
|
|
207
262
|
* previous_value TEXT
|
|
208
263
|
* );
|
|
@@ -222,8 +277,19 @@ export interface TriggerDiffHandlerContext extends LockContext {
|
|
|
222
277
|
* JOIN todos ON DIFF.id = todos.id
|
|
223
278
|
* WHERE json_extract(DIFF.value, '$.status') = 'active'
|
|
224
279
|
* ```
|
|
280
|
+
*
|
|
281
|
+
* @example
|
|
282
|
+
* ```typescript
|
|
283
|
+
* // With operation_id cast as TEXT for full precision
|
|
284
|
+
* const diffs = await context.withDiff<TriggerDiffRecord<string>>(
|
|
285
|
+
* 'SELECT * FROM DIFF',
|
|
286
|
+
* undefined,
|
|
287
|
+
* { castOperationIdAsText: true }
|
|
288
|
+
* );
|
|
289
|
+
* // diffs[0].operation_id is now typed as string
|
|
290
|
+
* ```
|
|
225
291
|
*/
|
|
226
|
-
withDiff: <T = any>(query: string, params?: ReadonlyArray<Readonly<any
|
|
292
|
+
withDiff: <T = any>(query: string, params?: ReadonlyArray<Readonly<any>>, options?: WithDiffOptions) => Promise<T[]>;
|
|
227
293
|
|
|
228
294
|
/**
|
|
229
295
|
* Allows querying the database with access to the table containing diff records.
|
|
@@ -292,9 +358,10 @@ export interface TriggerManager {
|
|
|
292
358
|
*
|
|
293
359
|
* ```sql
|
|
294
360
|
* CREATE TEMP TABLE ${destination} (
|
|
361
|
+
* operation_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
295
362
|
* id TEXT,
|
|
296
363
|
* operation TEXT,
|
|
297
|
-
* timestamp TEXT
|
|
364
|
+
* timestamp TEXT,
|
|
298
365
|
* value TEXT,
|
|
299
366
|
* previous_value TEXT
|
|
300
367
|
* );
|
|
@@ -7,7 +7,8 @@ import {
|
|
|
7
7
|
DiffTriggerOperation,
|
|
8
8
|
TrackDiffOptions,
|
|
9
9
|
TriggerManager,
|
|
10
|
-
TriggerRemoveCallback
|
|
10
|
+
TriggerRemoveCallback,
|
|
11
|
+
WithDiffOptions
|
|
11
12
|
} from './TriggerManager.js';
|
|
12
13
|
|
|
13
14
|
export type TriggerManagerImplOptions = {
|
|
@@ -117,6 +118,7 @@ export class TriggerManagerImpl implements TriggerManager {
|
|
|
117
118
|
await hooks?.beforeCreate?.(tx);
|
|
118
119
|
await tx.execute(/* sql */ `
|
|
119
120
|
CREATE TEMP TABLE ${destination} (
|
|
121
|
+
operation_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
120
122
|
id TEXT,
|
|
121
123
|
operation TEXT,
|
|
122
124
|
timestamp TEXT,
|
|
@@ -243,17 +245,20 @@ export class TriggerManagerImpl implements TriggerManager {
|
|
|
243
245
|
const callbackResult = await options.onChange({
|
|
244
246
|
...tx,
|
|
245
247
|
destinationTable: destination,
|
|
246
|
-
withDiff: async <T>(query, params) => {
|
|
248
|
+
withDiff: async <T>(query, params, options?: WithDiffOptions) => {
|
|
247
249
|
// Wrap the query to expose the destination table
|
|
250
|
+
const operationIdSelect = options?.castOperationIdAsText
|
|
251
|
+
? 'id, operation, CAST(operation_id AS TEXT) as operation_id, timestamp, value, previous_value'
|
|
252
|
+
: '*';
|
|
248
253
|
const wrappedQuery = /* sql */ `
|
|
249
254
|
WITH
|
|
250
255
|
DIFF AS (
|
|
251
256
|
SELECT
|
|
252
|
-
|
|
257
|
+
${operationIdSelect}
|
|
253
258
|
FROM
|
|
254
259
|
${destination}
|
|
255
260
|
ORDER BY
|
|
256
|
-
|
|
261
|
+
operation_id ASC
|
|
257
262
|
) ${query}
|
|
258
263
|
`;
|
|
259
264
|
return tx.getAll<T>(wrappedQuery, params);
|
|
@@ -267,13 +272,14 @@ export class TriggerManagerImpl implements TriggerManager {
|
|
|
267
272
|
id,
|
|
268
273
|
${contextColumns.length > 0
|
|
269
274
|
? `${contextColumns.map((col) => `json_extract(value, '$.${col}') as ${col}`).join(', ')},`
|
|
270
|
-
: ''}
|
|
275
|
+
: ''} operation_id as __operation_id,
|
|
276
|
+
operation as __operation,
|
|
271
277
|
timestamp as __timestamp,
|
|
272
278
|
previous_value as __previous_value
|
|
273
279
|
FROM
|
|
274
280
|
${destination}
|
|
275
281
|
ORDER BY
|
|
276
|
-
|
|
282
|
+
__operation_id ASC
|
|
277
283
|
) ${query}
|
|
278
284
|
`;
|
|
279
285
|
return tx.getAll<T>(wrappedQuery, params);
|