@powersync/common 1.29.0 → 1.30.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.mjs +3 -3
- package/lib/client/sync/bucket/CrudEntry.d.ts +13 -1
- package/lib/client/sync/bucket/CrudEntry.js +16 -2
- package/lib/client/sync/bucket/SqliteBucketStorage.js +2 -2
- package/lib/db/crud/SyncProgress.d.ts +1 -1
- package/lib/db/crud/SyncProgress.js +1 -1
- package/lib/db/schema/Schema.d.ts +4 -0
- package/lib/db/schema/Schema.js +1 -10
- package/lib/db/schema/Table.d.ts +34 -8
- package/lib/db/schema/Table.js +48 -9
- package/package.json +1 -1
|
@@ -51,6 +51,11 @@ export declare class CrudEntry {
|
|
|
51
51
|
* Data associated with the change.
|
|
52
52
|
*/
|
|
53
53
|
opData?: Record<string, any>;
|
|
54
|
+
/**
|
|
55
|
+
* For tables where the `trackPreviousValues` option has been enabled, this tracks previous values for
|
|
56
|
+
* `UPDATE` and `DELETE` statements.
|
|
57
|
+
*/
|
|
58
|
+
previousValues?: Record<string, any>;
|
|
54
59
|
/**
|
|
55
60
|
* Table that contained the change.
|
|
56
61
|
*/
|
|
@@ -59,8 +64,15 @@ export declare class CrudEntry {
|
|
|
59
64
|
* Auto-incrementing transaction id. This is the same for all operations within the same transaction.
|
|
60
65
|
*/
|
|
61
66
|
transactionId?: number;
|
|
67
|
+
/**
|
|
68
|
+
* Client-side metadata attached with this write.
|
|
69
|
+
*
|
|
70
|
+
* This field is only available when the `trackMetadata` option was set to `true` when creating a table
|
|
71
|
+
* and the insert or update statement set the `_metadata` column.
|
|
72
|
+
*/
|
|
73
|
+
metadata?: string;
|
|
62
74
|
static fromRow(dbRow: CrudEntryJSON): CrudEntry;
|
|
63
|
-
constructor(clientId: number, op: UpdateType, table: string, id: string, transactionId?: number, opData?: Record<string, any
|
|
75
|
+
constructor(clientId: number, op: UpdateType, table: string, id: string, transactionId?: number, opData?: Record<string, any>, previousValues?: Record<string, any>, metadata?: string);
|
|
64
76
|
/**
|
|
65
77
|
* Converts the change to JSON format.
|
|
66
78
|
*/
|
|
@@ -30,6 +30,11 @@ export class CrudEntry {
|
|
|
30
30
|
* Data associated with the change.
|
|
31
31
|
*/
|
|
32
32
|
opData;
|
|
33
|
+
/**
|
|
34
|
+
* For tables where the `trackPreviousValues` option has been enabled, this tracks previous values for
|
|
35
|
+
* `UPDATE` and `DELETE` statements.
|
|
36
|
+
*/
|
|
37
|
+
previousValues;
|
|
33
38
|
/**
|
|
34
39
|
* Table that contained the change.
|
|
35
40
|
*/
|
|
@@ -38,17 +43,26 @@ export class CrudEntry {
|
|
|
38
43
|
* Auto-incrementing transaction id. This is the same for all operations within the same transaction.
|
|
39
44
|
*/
|
|
40
45
|
transactionId;
|
|
46
|
+
/**
|
|
47
|
+
* Client-side metadata attached with this write.
|
|
48
|
+
*
|
|
49
|
+
* This field is only available when the `trackMetadata` option was set to `true` when creating a table
|
|
50
|
+
* and the insert or update statement set the `_metadata` column.
|
|
51
|
+
*/
|
|
52
|
+
metadata;
|
|
41
53
|
static fromRow(dbRow) {
|
|
42
54
|
const data = JSON.parse(dbRow.data);
|
|
43
|
-
return new CrudEntry(parseInt(dbRow.id), data.op, data.type, data.id, dbRow.tx_id, data.data);
|
|
55
|
+
return new CrudEntry(parseInt(dbRow.id), data.op, data.type, data.id, dbRow.tx_id, data.data, data.old, data.metadata);
|
|
44
56
|
}
|
|
45
|
-
constructor(clientId, op, table, id, transactionId, opData) {
|
|
57
|
+
constructor(clientId, op, table, id, transactionId, opData, previousValues, metadata) {
|
|
46
58
|
this.clientId = clientId;
|
|
47
59
|
this.id = id;
|
|
48
60
|
this.op = op;
|
|
49
61
|
this.opData = opData;
|
|
50
62
|
this.table = table;
|
|
51
63
|
this.transactionId = transactionId;
|
|
64
|
+
this.previousValues = previousValues;
|
|
65
|
+
this.metadata = metadata;
|
|
52
66
|
}
|
|
53
67
|
/**
|
|
54
68
|
* Converts the change to JSON format.
|
|
@@ -119,9 +119,9 @@ export class SqliteBucketStorage extends BaseObserver {
|
|
|
119
119
|
}
|
|
120
120
|
return { ready: false, checkpointValid: false, checkpointFailures: r.checkpointFailures };
|
|
121
121
|
}
|
|
122
|
-
|
|
122
|
+
let buckets = checkpoint.buckets;
|
|
123
123
|
if (priority !== undefined) {
|
|
124
|
-
buckets.filter((b) => hasMatchingPriority(priority, b));
|
|
124
|
+
buckets = buckets.filter((b) => hasMatchingPriority(priority, b));
|
|
125
125
|
}
|
|
126
126
|
const bucketNames = buckets.map((b) => b.bucket);
|
|
127
127
|
await this.writeTransaction(async (tx) => {
|
|
@@ -44,7 +44,7 @@ export interface ProgressWithOperations {
|
|
|
44
44
|
* Additionally, the {@link SyncProgress.untilPriority} method can be used to otbain progress towards
|
|
45
45
|
* a specific priority (instead of the progress for the entire download).
|
|
46
46
|
*
|
|
47
|
-
* The reported progress always reflects the status towards
|
|
47
|
+
* The reported progress always reflects the status towards the end of a sync iteration (after
|
|
48
48
|
* which a consistent snapshot of all buckets is available locally).
|
|
49
49
|
*
|
|
50
50
|
* In rare cases (in particular, when a [compacting](https://docs.powersync.com/usage/lifecycle-maintenance/compacting-buckets)
|
|
@@ -10,7 +10,7 @@ export const FULL_SYNC_PRIORITY = 2147483647;
|
|
|
10
10
|
* Additionally, the {@link SyncProgress.untilPriority} method can be used to otbain progress towards
|
|
11
11
|
* a specific priority (instead of the progress for the entire download).
|
|
12
12
|
*
|
|
13
|
-
* The reported progress always reflects the status towards
|
|
13
|
+
* The reported progress always reflects the status towards the end of a sync iteration (after
|
|
14
14
|
* which a consistent snapshot of all buckets is available locally).
|
|
15
15
|
*
|
|
16
16
|
* In rare cases (in particular, when a [compacting](https://docs.powersync.com/usage/lifecycle-maintenance/compacting-buckets)
|
|
@@ -18,6 +18,10 @@ export declare class Schema<S extends SchemaType = SchemaType> {
|
|
|
18
18
|
view_name: string;
|
|
19
19
|
local_only: boolean;
|
|
20
20
|
insert_only: boolean;
|
|
21
|
+
include_old: any;
|
|
22
|
+
include_old_only_when_changed: boolean;
|
|
23
|
+
include_metadata: boolean;
|
|
24
|
+
ignore_empty_update: boolean;
|
|
21
25
|
columns: {
|
|
22
26
|
name: string;
|
|
23
27
|
type: import("./Column.js").ColumnType | undefined;
|
package/lib/db/schema/Schema.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { Table } from './Table.js';
|
|
2
1
|
/**
|
|
3
2
|
* A schema is a collection of tables. It is used to define the structure of a database.
|
|
4
3
|
*/
|
|
@@ -41,15 +40,7 @@ export class Schema {
|
|
|
41
40
|
}
|
|
42
41
|
convertToClassicTables(props) {
|
|
43
42
|
return Object.entries(props).map(([name, table]) => {
|
|
44
|
-
|
|
45
|
-
name,
|
|
46
|
-
columns: table.columns,
|
|
47
|
-
indexes: table.indexes,
|
|
48
|
-
localOnly: table.localOnly,
|
|
49
|
-
insertOnly: table.insertOnly,
|
|
50
|
-
viewName: table.viewNameOverride || name
|
|
51
|
-
});
|
|
52
|
-
return convertedTable;
|
|
43
|
+
return table.copyWithName(name);
|
|
53
44
|
});
|
|
54
45
|
}
|
|
55
46
|
}
|
package/lib/db/schema/Table.d.ts
CHANGED
|
@@ -1,16 +1,32 @@
|
|
|
1
1
|
import { Column, ColumnsType, ColumnType, ExtractColumnValueType } from './Column.js';
|
|
2
2
|
import { Index } from './Index.js';
|
|
3
3
|
import { TableV2 } from './TableV2.js';
|
|
4
|
-
|
|
4
|
+
interface SharedTableOptions {
|
|
5
|
+
localOnly?: boolean;
|
|
6
|
+
insertOnly?: boolean;
|
|
7
|
+
viewName?: string;
|
|
8
|
+
trackPrevious?: boolean | TrackPreviousOptions;
|
|
9
|
+
trackMetadata?: boolean;
|
|
10
|
+
ignoreEmptyUpdates?: boolean;
|
|
11
|
+
}
|
|
12
|
+
/** Whether to include previous column values when PowerSync tracks local changes.
|
|
13
|
+
*
|
|
14
|
+
* Including old values may be helpful for some backend connector implementations, which is
|
|
15
|
+
* why it can be enabled on per-table or per-columm basis.
|
|
16
|
+
*/
|
|
17
|
+
export interface TrackPreviousOptions {
|
|
18
|
+
/** When defined, a list of column names for which old values should be tracked. */
|
|
19
|
+
columns?: string[];
|
|
20
|
+
/** When enabled, only include values that have actually been changed by an update. */
|
|
21
|
+
onlyWhenChanged?: boolean;
|
|
22
|
+
}
|
|
23
|
+
export interface TableOptions extends SharedTableOptions {
|
|
5
24
|
/**
|
|
6
25
|
* The synced table name, matching sync rules
|
|
7
26
|
*/
|
|
8
27
|
name: string;
|
|
9
28
|
columns: Column[];
|
|
10
29
|
indexes?: Index[];
|
|
11
|
-
localOnly?: boolean;
|
|
12
|
-
insertOnly?: boolean;
|
|
13
|
-
viewName?: string;
|
|
14
30
|
}
|
|
15
31
|
export type RowType<T extends TableV2<any>> = {
|
|
16
32
|
[K in keyof T['columnMap']]: ExtractColumnValueType<T['columnMap'][K]>;
|
|
@@ -18,16 +34,16 @@ export type RowType<T extends TableV2<any>> = {
|
|
|
18
34
|
id: string;
|
|
19
35
|
};
|
|
20
36
|
export type IndexShorthand = Record<string, string[]>;
|
|
21
|
-
export interface TableV2Options {
|
|
37
|
+
export interface TableV2Options extends SharedTableOptions {
|
|
22
38
|
indexes?: IndexShorthand;
|
|
23
|
-
localOnly?: boolean;
|
|
24
|
-
insertOnly?: boolean;
|
|
25
|
-
viewName?: string;
|
|
26
39
|
}
|
|
27
40
|
export declare const DEFAULT_TABLE_OPTIONS: {
|
|
28
41
|
indexes: never[];
|
|
29
42
|
insertOnly: boolean;
|
|
30
43
|
localOnly: boolean;
|
|
44
|
+
trackPrevious: boolean;
|
|
45
|
+
trackMetadata: boolean;
|
|
46
|
+
ignoreEmptyUpdates: boolean;
|
|
31
47
|
};
|
|
32
48
|
export declare const InvalidSQLCharacters: RegExp;
|
|
33
49
|
export declare class Table<Columns extends ColumnsType = ColumnsType> {
|
|
@@ -96,9 +112,11 @@ export declare class Table<Columns extends ColumnsType = ColumnsType> {
|
|
|
96
112
|
*```
|
|
97
113
|
*/
|
|
98
114
|
constructor(options: TableOptions);
|
|
115
|
+
copyWithName(name: string): Table;
|
|
99
116
|
private isTableV1;
|
|
100
117
|
private initTableV1;
|
|
101
118
|
private initTableV2;
|
|
119
|
+
private applyDefaultOptions;
|
|
102
120
|
get name(): string;
|
|
103
121
|
get viewNameOverride(): string | undefined;
|
|
104
122
|
get viewName(): string;
|
|
@@ -107,6 +125,9 @@ export declare class Table<Columns extends ColumnsType = ColumnsType> {
|
|
|
107
125
|
get indexes(): Index[];
|
|
108
126
|
get localOnly(): boolean;
|
|
109
127
|
get insertOnly(): boolean;
|
|
128
|
+
get trackPrevious(): boolean | TrackPreviousOptions;
|
|
129
|
+
get trackMetadata(): boolean;
|
|
130
|
+
get ignoreEmptyUpdates(): boolean;
|
|
110
131
|
get internalName(): string;
|
|
111
132
|
get validName(): boolean;
|
|
112
133
|
validate(): void;
|
|
@@ -115,6 +136,10 @@ export declare class Table<Columns extends ColumnsType = ColumnsType> {
|
|
|
115
136
|
view_name: string;
|
|
116
137
|
local_only: boolean;
|
|
117
138
|
insert_only: boolean;
|
|
139
|
+
include_old: any;
|
|
140
|
+
include_old_only_when_changed: boolean;
|
|
141
|
+
include_metadata: boolean;
|
|
142
|
+
ignore_empty_update: boolean;
|
|
118
143
|
columns: {
|
|
119
144
|
name: string;
|
|
120
145
|
type: ColumnType | undefined;
|
|
@@ -129,3 +154,4 @@ export declare class Table<Columns extends ColumnsType = ColumnsType> {
|
|
|
129
154
|
}[];
|
|
130
155
|
};
|
|
131
156
|
}
|
|
157
|
+
export {};
|
package/lib/db/schema/Table.js
CHANGED
|
@@ -4,7 +4,10 @@ import { IndexedColumn } from './IndexedColumn.js';
|
|
|
4
4
|
export const DEFAULT_TABLE_OPTIONS = {
|
|
5
5
|
indexes: [],
|
|
6
6
|
insertOnly: false,
|
|
7
|
-
localOnly: false
|
|
7
|
+
localOnly: false,
|
|
8
|
+
trackPrevious: false,
|
|
9
|
+
trackMetadata: false,
|
|
10
|
+
ignoreEmptyUpdates: false
|
|
8
11
|
};
|
|
9
12
|
export const InvalidSQLCharacters = /["'%,.#\s[\]]/;
|
|
10
13
|
export class Table {
|
|
@@ -41,16 +44,21 @@ export class Table {
|
|
|
41
44
|
this.initTableV2(optionsOrColumns, v2Options);
|
|
42
45
|
}
|
|
43
46
|
}
|
|
47
|
+
copyWithName(name) {
|
|
48
|
+
return new Table({
|
|
49
|
+
...this.options,
|
|
50
|
+
name
|
|
51
|
+
});
|
|
52
|
+
}
|
|
44
53
|
isTableV1(arg) {
|
|
45
54
|
return 'columns' in arg && Array.isArray(arg.columns);
|
|
46
55
|
}
|
|
47
56
|
initTableV1(options) {
|
|
48
57
|
this.options = {
|
|
49
58
|
...options,
|
|
50
|
-
indexes: options.indexes || []
|
|
51
|
-
insertOnly: options.insertOnly ?? DEFAULT_TABLE_OPTIONS.insertOnly,
|
|
52
|
-
localOnly: options.localOnly ?? DEFAULT_TABLE_OPTIONS.localOnly
|
|
59
|
+
indexes: options.indexes || []
|
|
53
60
|
};
|
|
61
|
+
this.applyDefaultOptions();
|
|
54
62
|
}
|
|
55
63
|
initTableV2(columns, options) {
|
|
56
64
|
const convertedColumns = Object.entries(columns).map(([name, columnInfo]) => new Column({ name, type: columnInfo.type }));
|
|
@@ -65,12 +73,23 @@ export class Table {
|
|
|
65
73
|
name: '',
|
|
66
74
|
columns: convertedColumns,
|
|
67
75
|
indexes: convertedIndexes,
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
76
|
+
viewName: options?.viewName,
|
|
77
|
+
insertOnly: options?.insertOnly,
|
|
78
|
+
localOnly: options?.localOnly,
|
|
79
|
+
trackPrevious: options?.trackPrevious,
|
|
80
|
+
trackMetadata: options?.trackMetadata,
|
|
81
|
+
ignoreEmptyUpdates: options?.ignoreEmptyUpdates
|
|
71
82
|
};
|
|
83
|
+
this.applyDefaultOptions();
|
|
72
84
|
this._mappedColumns = columns;
|
|
73
85
|
}
|
|
86
|
+
applyDefaultOptions() {
|
|
87
|
+
this.options.insertOnly ??= DEFAULT_TABLE_OPTIONS.insertOnly;
|
|
88
|
+
this.options.localOnly ??= DEFAULT_TABLE_OPTIONS.localOnly;
|
|
89
|
+
this.options.trackPrevious ??= DEFAULT_TABLE_OPTIONS.trackPrevious;
|
|
90
|
+
this.options.trackMetadata ??= DEFAULT_TABLE_OPTIONS.trackMetadata;
|
|
91
|
+
this.options.ignoreEmptyUpdates ??= DEFAULT_TABLE_OPTIONS.ignoreEmptyUpdates;
|
|
92
|
+
}
|
|
74
93
|
get name() {
|
|
75
94
|
return this.options.name;
|
|
76
95
|
}
|
|
@@ -94,10 +113,19 @@ export class Table {
|
|
|
94
113
|
return this.options.indexes ?? [];
|
|
95
114
|
}
|
|
96
115
|
get localOnly() {
|
|
97
|
-
return this.options.localOnly
|
|
116
|
+
return this.options.localOnly;
|
|
98
117
|
}
|
|
99
118
|
get insertOnly() {
|
|
100
|
-
return this.options.insertOnly
|
|
119
|
+
return this.options.insertOnly;
|
|
120
|
+
}
|
|
121
|
+
get trackPrevious() {
|
|
122
|
+
return this.options.trackPrevious;
|
|
123
|
+
}
|
|
124
|
+
get trackMetadata() {
|
|
125
|
+
return this.options.trackMetadata;
|
|
126
|
+
}
|
|
127
|
+
get ignoreEmptyUpdates() {
|
|
128
|
+
return this.options.ignoreEmptyUpdates;
|
|
101
129
|
}
|
|
102
130
|
get internalName() {
|
|
103
131
|
if (this.options.localOnly) {
|
|
@@ -124,6 +152,12 @@ export class Table {
|
|
|
124
152
|
if (this.columns.length > MAX_AMOUNT_OF_COLUMNS) {
|
|
125
153
|
throw new Error(`Table has too many columns. The maximum number of columns is ${MAX_AMOUNT_OF_COLUMNS}.`);
|
|
126
154
|
}
|
|
155
|
+
if (this.trackMetadata && this.localOnly) {
|
|
156
|
+
throw new Error(`Can't include metadata for local-only tables.`);
|
|
157
|
+
}
|
|
158
|
+
if (this.trackPrevious != false && this.localOnly) {
|
|
159
|
+
throw new Error(`Can't include old values for local-only tables.`);
|
|
160
|
+
}
|
|
127
161
|
const columnNames = new Set();
|
|
128
162
|
columnNames.add('id');
|
|
129
163
|
for (const column of this.columns) {
|
|
@@ -156,11 +190,16 @@ export class Table {
|
|
|
156
190
|
}
|
|
157
191
|
}
|
|
158
192
|
toJSON() {
|
|
193
|
+
const trackPrevious = this.trackPrevious;
|
|
159
194
|
return {
|
|
160
195
|
name: this.name,
|
|
161
196
|
view_name: this.viewName,
|
|
162
197
|
local_only: this.localOnly,
|
|
163
198
|
insert_only: this.insertOnly,
|
|
199
|
+
include_old: trackPrevious && (trackPrevious.columns ?? true),
|
|
200
|
+
include_old_only_when_changed: typeof trackPrevious == 'object' && trackPrevious.onlyWhenChanged == true,
|
|
201
|
+
include_metadata: this.trackMetadata,
|
|
202
|
+
ignore_empty_update: this.ignoreEmptyUpdates,
|
|
164
203
|
columns: this.columns.map((c) => c.toJSON()),
|
|
165
204
|
indexes: this.indexes.map((e) => e.toJSON(this))
|
|
166
205
|
};
|