@powersync/common 1.40.0 → 1.41.1
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 +10809 -22
- package/dist/bundle.cjs.map +1 -0
- package/dist/bundle.mjs +10730 -22
- package/dist/bundle.mjs.map +1 -0
- package/dist/bundle.node.cjs +10809 -0
- package/dist/bundle.node.cjs.map +1 -0
- package/dist/bundle.node.mjs +10730 -0
- package/dist/bundle.node.mjs.map +1 -0
- package/dist/index.d.cts +5 -1
- package/lib/client/AbstractPowerSyncDatabase.js +1 -0
- package/lib/client/AbstractPowerSyncDatabase.js.map +1 -0
- package/lib/client/AbstractPowerSyncOpenFactory.js +1 -0
- package/lib/client/AbstractPowerSyncOpenFactory.js.map +1 -0
- package/lib/client/ConnectionManager.js +1 -0
- package/lib/client/ConnectionManager.js.map +1 -0
- package/lib/client/CustomQuery.js +1 -0
- package/lib/client/CustomQuery.js.map +1 -0
- package/lib/client/Query.js +1 -0
- package/lib/client/Query.js.map +1 -0
- package/lib/client/SQLOpenFactory.js +1 -0
- package/lib/client/SQLOpenFactory.js.map +1 -0
- package/lib/client/compilableQueryWatch.js +1 -0
- package/lib/client/compilableQueryWatch.js.map +1 -0
- package/lib/client/connection/PowerSyncBackendConnector.js +1 -0
- package/lib/client/connection/PowerSyncBackendConnector.js.map +1 -0
- package/lib/client/connection/PowerSyncCredentials.js +1 -0
- package/lib/client/connection/PowerSyncCredentials.js.map +1 -0
- package/lib/client/constants.js +1 -0
- package/lib/client/constants.js.map +1 -0
- package/lib/client/runOnSchemaChange.js +1 -0
- package/lib/client/runOnSchemaChange.js.map +1 -0
- package/lib/client/sync/bucket/BucketStorageAdapter.js +1 -0
- package/lib/client/sync/bucket/BucketStorageAdapter.js.map +1 -0
- package/lib/client/sync/bucket/CrudBatch.js +1 -0
- package/lib/client/sync/bucket/CrudBatch.js.map +1 -0
- package/lib/client/sync/bucket/CrudEntry.js +1 -0
- package/lib/client/sync/bucket/CrudEntry.js.map +1 -0
- package/lib/client/sync/bucket/CrudTransaction.js +1 -0
- package/lib/client/sync/bucket/CrudTransaction.js.map +1 -0
- package/lib/client/sync/bucket/OpType.js +1 -0
- package/lib/client/sync/bucket/OpType.js.map +1 -0
- package/lib/client/sync/bucket/OplogEntry.js +1 -0
- package/lib/client/sync/bucket/OplogEntry.js.map +1 -0
- package/lib/client/sync/bucket/SqliteBucketStorage.js +1 -0
- package/lib/client/sync/bucket/SqliteBucketStorage.js.map +1 -0
- package/lib/client/sync/bucket/SyncDataBatch.js +1 -0
- package/lib/client/sync/bucket/SyncDataBatch.js.map +1 -0
- package/lib/client/sync/bucket/SyncDataBucket.js +1 -0
- package/lib/client/sync/bucket/SyncDataBucket.js.map +1 -0
- package/lib/client/sync/stream/AbstractRemote.d.ts +5 -0
- package/lib/client/sync/stream/AbstractRemote.js +9 -2
- package/lib/client/sync/stream/AbstractRemote.js.map +1 -0
- package/lib/client/sync/stream/AbstractStreamingSyncImplementation.js +1 -0
- package/lib/client/sync/stream/AbstractStreamingSyncImplementation.js.map +1 -0
- package/lib/client/sync/stream/WebsocketClientTransport.js +1 -0
- package/lib/client/sync/stream/WebsocketClientTransport.js.map +1 -0
- package/lib/client/sync/stream/core-instruction.js +1 -0
- package/lib/client/sync/stream/core-instruction.js.map +1 -0
- package/lib/client/sync/stream/streaming-sync-types.js +1 -0
- package/lib/client/sync/stream/streaming-sync-types.js.map +1 -0
- package/lib/client/sync/sync-streams.js +1 -0
- package/lib/client/sync/sync-streams.js.map +1 -0
- package/lib/client/triggers/TriggerManager.js +1 -0
- package/lib/client/triggers/TriggerManager.js.map +1 -0
- package/lib/client/triggers/TriggerManagerImpl.js +1 -0
- package/lib/client/triggers/TriggerManagerImpl.js.map +1 -0
- package/lib/client/triggers/sanitizeSQL.js +1 -0
- package/lib/client/triggers/sanitizeSQL.js.map +1 -0
- package/lib/client/watched/GetAllQuery.js +1 -0
- package/lib/client/watched/GetAllQuery.js.map +1 -0
- package/lib/client/watched/WatchedQuery.js +1 -0
- package/lib/client/watched/WatchedQuery.js.map +1 -0
- package/lib/client/watched/processors/AbstractQueryProcessor.js +1 -0
- package/lib/client/watched/processors/AbstractQueryProcessor.js.map +1 -0
- package/lib/client/watched/processors/DifferentialQueryProcessor.js +1 -0
- package/lib/client/watched/processors/DifferentialQueryProcessor.js.map +1 -0
- package/lib/client/watched/processors/OnChangeQueryProcessor.js +1 -0
- package/lib/client/watched/processors/OnChangeQueryProcessor.js.map +1 -0
- package/lib/client/watched/processors/comparators.js +1 -0
- package/lib/client/watched/processors/comparators.js.map +1 -0
- package/lib/db/DBAdapter.js +1 -0
- package/lib/db/DBAdapter.js.map +1 -0
- package/lib/db/crud/SyncProgress.js +1 -0
- package/lib/db/crud/SyncProgress.js.map +1 -0
- package/lib/db/crud/SyncStatus.js +1 -0
- package/lib/db/crud/SyncStatus.js.map +1 -0
- package/lib/db/crud/UploadQueueStatus.js +1 -0
- package/lib/db/crud/UploadQueueStatus.js.map +1 -0
- package/lib/db/schema/Column.js +1 -0
- package/lib/db/schema/Column.js.map +1 -0
- package/lib/db/schema/Index.js +1 -0
- package/lib/db/schema/Index.js.map +1 -0
- package/lib/db/schema/IndexedColumn.js +1 -0
- package/lib/db/schema/IndexedColumn.js.map +1 -0
- package/lib/db/schema/RawTable.js +1 -0
- package/lib/db/schema/RawTable.js.map +1 -0
- package/lib/db/schema/Schema.d.ts +0 -1
- package/lib/db/schema/Schema.js +4 -8
- package/lib/db/schema/Schema.js.map +1 -0
- package/lib/db/schema/Table.js +1 -0
- package/lib/db/schema/Table.js.map +1 -0
- package/lib/db/schema/TableV2.js +1 -0
- package/lib/db/schema/TableV2.js.map +1 -0
- package/lib/index.js +1 -0
- package/lib/index.js.map +1 -0
- package/lib/types/types.js +1 -0
- package/lib/types/types.js.map +1 -0
- package/lib/utils/AbortOperation.js +1 -0
- package/lib/utils/AbortOperation.js.map +1 -0
- package/lib/utils/BaseObserver.js +1 -0
- package/lib/utils/BaseObserver.js.map +1 -0
- package/lib/utils/ControlledExecutor.js +1 -0
- package/lib/utils/ControlledExecutor.js.map +1 -0
- package/lib/utils/DataStream.js +1 -0
- package/lib/utils/DataStream.js.map +1 -0
- package/lib/utils/Logger.js +1 -0
- package/lib/utils/Logger.js.map +1 -0
- package/lib/utils/MetaBaseObserver.js +1 -0
- package/lib/utils/MetaBaseObserver.js.map +1 -0
- package/lib/utils/async.js +1 -0
- package/lib/utils/async.js.map +1 -0
- package/lib/utils/mutex.js +1 -0
- package/lib/utils/mutex.js.map +1 -0
- package/lib/utils/parseQuery.js +1 -0
- package/lib/utils/parseQuery.js.map +1 -0
- package/package.json +23 -15
- package/src/client/AbstractPowerSyncDatabase.ts +1343 -0
- package/src/client/AbstractPowerSyncOpenFactory.ts +39 -0
- package/src/client/ConnectionManager.ts +402 -0
- package/src/client/CustomQuery.ts +56 -0
- package/src/client/Query.ts +106 -0
- package/src/client/SQLOpenFactory.ts +55 -0
- package/src/client/compilableQueryWatch.ts +55 -0
- package/src/client/connection/PowerSyncBackendConnector.ts +25 -0
- package/src/client/connection/PowerSyncCredentials.ts +5 -0
- package/src/client/constants.ts +1 -0
- package/src/client/runOnSchemaChange.ts +31 -0
- package/src/client/sync/bucket/BucketStorageAdapter.ts +118 -0
- package/src/client/sync/bucket/CrudBatch.ts +21 -0
- package/src/client/sync/bucket/CrudEntry.ts +172 -0
- package/src/client/sync/bucket/CrudTransaction.ts +21 -0
- package/src/client/sync/bucket/OpType.ts +23 -0
- package/src/client/sync/bucket/OplogEntry.ts +50 -0
- package/src/client/sync/bucket/SqliteBucketStorage.ts +395 -0
- package/src/client/sync/bucket/SyncDataBatch.ts +11 -0
- package/src/client/sync/bucket/SyncDataBucket.ts +49 -0
- package/src/client/sync/stream/AbstractRemote.ts +626 -0
- package/src/client/sync/stream/AbstractStreamingSyncImplementation.ts +1258 -0
- package/src/client/sync/stream/WebsocketClientTransport.ts +80 -0
- package/src/client/sync/stream/core-instruction.ts +99 -0
- package/src/client/sync/stream/streaming-sync-types.ts +205 -0
- package/src/client/sync/sync-streams.ts +107 -0
- package/src/client/triggers/TriggerManager.ts +384 -0
- package/src/client/triggers/TriggerManagerImpl.ts +314 -0
- package/src/client/triggers/sanitizeSQL.ts +66 -0
- package/src/client/watched/GetAllQuery.ts +46 -0
- package/src/client/watched/WatchedQuery.ts +121 -0
- package/src/client/watched/processors/AbstractQueryProcessor.ts +226 -0
- package/src/client/watched/processors/DifferentialQueryProcessor.ts +305 -0
- package/src/client/watched/processors/OnChangeQueryProcessor.ts +122 -0
- package/src/client/watched/processors/comparators.ts +57 -0
- package/src/db/DBAdapter.ts +134 -0
- package/src/db/crud/SyncProgress.ts +100 -0
- package/src/db/crud/SyncStatus.ts +308 -0
- package/src/db/crud/UploadQueueStatus.ts +20 -0
- package/src/db/schema/Column.ts +60 -0
- package/src/db/schema/Index.ts +39 -0
- package/src/db/schema/IndexedColumn.ts +42 -0
- package/src/db/schema/RawTable.ts +67 -0
- package/src/db/schema/Schema.ts +76 -0
- package/src/db/schema/Table.ts +359 -0
- package/src/db/schema/TableV2.ts +9 -0
- package/src/index.ts +52 -0
- package/src/types/types.ts +9 -0
- package/src/utils/AbortOperation.ts +17 -0
- package/src/utils/BaseObserver.ts +41 -0
- package/src/utils/ControlledExecutor.ts +72 -0
- package/src/utils/DataStream.ts +211 -0
- package/src/utils/Logger.ts +47 -0
- package/src/utils/MetaBaseObserver.ts +81 -0
- package/src/utils/async.ts +61 -0
- package/src/utils/mutex.ts +34 -0
- package/src/utils/parseQuery.ts +25 -0
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { RawTable, RawTableType } from './RawTable.js';
|
|
2
|
+
import { RowType, Table } from './Table.js';
|
|
3
|
+
|
|
4
|
+
type SchemaType = Record<string, Table<any>>;
|
|
5
|
+
|
|
6
|
+
export type SchemaTableType<S extends SchemaType> = {
|
|
7
|
+
[K in keyof S]: RowType<S[K]>;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* A schema is a collection of tables. It is used to define the structure of a database.
|
|
12
|
+
*/
|
|
13
|
+
export class Schema<S extends SchemaType = SchemaType> {
|
|
14
|
+
/*
|
|
15
|
+
Only available when constructing with mapped typed definition columns
|
|
16
|
+
*/
|
|
17
|
+
readonly types: SchemaTableType<S>;
|
|
18
|
+
readonly props: S;
|
|
19
|
+
readonly tables: Table[];
|
|
20
|
+
readonly rawTables: RawTable[];
|
|
21
|
+
|
|
22
|
+
constructor(tables: Table[] | S) {
|
|
23
|
+
if (Array.isArray(tables)) {
|
|
24
|
+
/*
|
|
25
|
+
We need to validate that the tables have a name here because a user could pass in an array
|
|
26
|
+
of Tables that don't have a name because they are using the V2 syntax.
|
|
27
|
+
Therefore, 'convertToClassicTables' won't be called on the tables resulting in a runtime error.
|
|
28
|
+
*/
|
|
29
|
+
for (const table of tables) {
|
|
30
|
+
if (table.name === '') {
|
|
31
|
+
throw new Error(
|
|
32
|
+
"It appears you are trying to create a new Schema with an array instead of an object. Passing in an object instead of an array into 'new Schema()' may resolve your issue."
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
this.tables = tables;
|
|
37
|
+
} else {
|
|
38
|
+
// Update the table entries with the provided table name key
|
|
39
|
+
this.props = Object.fromEntries(
|
|
40
|
+
Object.entries(tables).map(([tableName, table]) => [tableName, table.copyWithName(tableName)])
|
|
41
|
+
) as S;
|
|
42
|
+
this.tables = Object.values(this.props);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
this.rawTables = [];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Adds raw tables to this schema. Raw tables are identified by their name, but entirely managed by the application
|
|
50
|
+
* developer instead of automatically by PowerSync.
|
|
51
|
+
* Since raw tables are not backed by JSON, running complex queries on them may be more efficient. Further, they allow
|
|
52
|
+
* using client-side table and column constraints.
|
|
53
|
+
* Note that raw tables are only supported when using the new `SyncClientImplementation.rust` sync client.
|
|
54
|
+
*
|
|
55
|
+
* @param tables An object of (table name, raw table definition) entries.
|
|
56
|
+
* @experimental Note that the raw tables API is still experimental and may change in the future.
|
|
57
|
+
*/
|
|
58
|
+
withRawTables(tables: Record<string, RawTableType>) {
|
|
59
|
+
for (const [name, rawTableDefinition] of Object.entries(tables)) {
|
|
60
|
+
this.rawTables.push(new RawTable(name, rawTableDefinition));
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
validate() {
|
|
65
|
+
for (const table of this.tables) {
|
|
66
|
+
table.validate();
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
toJSON() {
|
|
71
|
+
return {
|
|
72
|
+
tables: this.tables.map((t) => t.toJSON()),
|
|
73
|
+
raw_tables: this.rawTables
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BaseColumnType,
|
|
3
|
+
Column,
|
|
4
|
+
ColumnsType,
|
|
5
|
+
ColumnType,
|
|
6
|
+
ExtractColumnValueType,
|
|
7
|
+
MAX_AMOUNT_OF_COLUMNS
|
|
8
|
+
} from './Column.js';
|
|
9
|
+
import { Index } from './Index.js';
|
|
10
|
+
import { IndexedColumn } from './IndexedColumn.js';
|
|
11
|
+
import { TableV2 } from './TableV2.js';
|
|
12
|
+
|
|
13
|
+
interface SharedTableOptions {
|
|
14
|
+
localOnly?: boolean;
|
|
15
|
+
insertOnly?: boolean;
|
|
16
|
+
viewName?: string;
|
|
17
|
+
trackPrevious?: boolean | TrackPreviousOptions;
|
|
18
|
+
trackMetadata?: boolean;
|
|
19
|
+
ignoreEmptyUpdates?: boolean;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Whether to include previous column values when PowerSync tracks local changes.
|
|
23
|
+
*
|
|
24
|
+
* Including old values may be helpful for some backend connector implementations, which is
|
|
25
|
+
* why it can be enabled on per-table or per-columm basis.
|
|
26
|
+
*/
|
|
27
|
+
export interface TrackPreviousOptions {
|
|
28
|
+
/** When defined, a list of column names for which old values should be tracked. */
|
|
29
|
+
columns?: string[];
|
|
30
|
+
/** When enabled, only include values that have actually been changed by an update. */
|
|
31
|
+
onlyWhenChanged?: boolean;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface TableOptions extends SharedTableOptions {
|
|
35
|
+
/**
|
|
36
|
+
* The synced table name, matching sync rules
|
|
37
|
+
*/
|
|
38
|
+
name: string;
|
|
39
|
+
columns: Column[];
|
|
40
|
+
indexes?: Index[];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export type RowType<T extends TableV2<any>> = {
|
|
44
|
+
[K in keyof T['columnMap']]: ExtractColumnValueType<T['columnMap'][K]>;
|
|
45
|
+
} & {
|
|
46
|
+
id: string;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export type IndexShorthand = Record<string, string[]>;
|
|
50
|
+
|
|
51
|
+
export interface TableV2Options extends SharedTableOptions {
|
|
52
|
+
indexes?: IndexShorthand;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export const DEFAULT_TABLE_OPTIONS = {
|
|
56
|
+
indexes: [],
|
|
57
|
+
insertOnly: false,
|
|
58
|
+
localOnly: false,
|
|
59
|
+
trackPrevious: false,
|
|
60
|
+
trackMetadata: false,
|
|
61
|
+
ignoreEmptyUpdates: false
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
export const InvalidSQLCharacters = /["'%,.#\s[\]]/;
|
|
65
|
+
|
|
66
|
+
export class Table<Columns extends ColumnsType = ColumnsType> {
|
|
67
|
+
protected options: TableOptions;
|
|
68
|
+
|
|
69
|
+
protected _mappedColumns: Columns;
|
|
70
|
+
|
|
71
|
+
static createLocalOnly(options: TableOptions) {
|
|
72
|
+
return new Table({ ...options, localOnly: true, insertOnly: false });
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
static createInsertOnly(options: TableOptions) {
|
|
76
|
+
return new Table({ ...options, localOnly: false, insertOnly: true });
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Create a table.
|
|
81
|
+
* @deprecated This was only only included for TableV2 and is no longer necessary.
|
|
82
|
+
* Prefer to use new Table() directly.
|
|
83
|
+
*
|
|
84
|
+
* TODO remove in the next major release.
|
|
85
|
+
*/
|
|
86
|
+
static createTable(name: string, table: Table) {
|
|
87
|
+
return new Table({
|
|
88
|
+
name,
|
|
89
|
+
columns: table.columns,
|
|
90
|
+
indexes: table.indexes,
|
|
91
|
+
localOnly: table.options.localOnly,
|
|
92
|
+
insertOnly: table.options.insertOnly,
|
|
93
|
+
viewName: table.options.viewName
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Creates a new Table instance.
|
|
99
|
+
*
|
|
100
|
+
* This constructor supports two different versions:
|
|
101
|
+
* 1. New constructor: Using a Columns object and an optional TableV2Options object
|
|
102
|
+
* 2. Deprecated constructor: Using a TableOptions object (will be removed in the next major release)
|
|
103
|
+
*
|
|
104
|
+
* @constructor
|
|
105
|
+
* @param {Columns | TableOptions} optionsOrColumns - Either a Columns object (for V2 syntax) or a TableOptions object (for V1 syntax)
|
|
106
|
+
* @param {TableV2Options} [v2Options] - Optional configuration options for V2 syntax
|
|
107
|
+
*
|
|
108
|
+
* @example
|
|
109
|
+
* ```javascript
|
|
110
|
+
* // New Constructor
|
|
111
|
+
* const table = new Table(
|
|
112
|
+
* {
|
|
113
|
+
* name: column.text,
|
|
114
|
+
* age: column.integer
|
|
115
|
+
* },
|
|
116
|
+
* { indexes: { nameIndex: ['name'] } }
|
|
117
|
+
* );
|
|
118
|
+
*```
|
|
119
|
+
*
|
|
120
|
+
*
|
|
121
|
+
* @example
|
|
122
|
+
* ```javascript
|
|
123
|
+
* // Deprecated Constructor
|
|
124
|
+
* const table = new Table({
|
|
125
|
+
* name: 'users',
|
|
126
|
+
* columns: [
|
|
127
|
+
* new Column({ name: 'name', type: ColumnType.TEXT }),
|
|
128
|
+
* new Column({ name: 'age', type: ColumnType.INTEGER })
|
|
129
|
+
* ]
|
|
130
|
+
* });
|
|
131
|
+
*```
|
|
132
|
+
*/
|
|
133
|
+
constructor(columns: Columns, options?: TableV2Options);
|
|
134
|
+
/**
|
|
135
|
+
* @deprecated This constructor will be removed in the next major release.
|
|
136
|
+
* Use the new constructor shown below instead as this does not show types.
|
|
137
|
+
* @example
|
|
138
|
+
* <caption>Use this instead</caption>
|
|
139
|
+
* ```javascript
|
|
140
|
+
* const table = new Table(
|
|
141
|
+
* {
|
|
142
|
+
* name: column.text,
|
|
143
|
+
* age: column.integer
|
|
144
|
+
* },
|
|
145
|
+
* { indexes: { nameIndex: ['name'] } }
|
|
146
|
+
* );
|
|
147
|
+
*```
|
|
148
|
+
*/
|
|
149
|
+
constructor(options: TableOptions);
|
|
150
|
+
constructor(optionsOrColumns: Columns | TableOptions, v2Options?: TableV2Options) {
|
|
151
|
+
if (this.isTableV1(optionsOrColumns)) {
|
|
152
|
+
this.initTableV1(optionsOrColumns);
|
|
153
|
+
} else {
|
|
154
|
+
this.initTableV2(optionsOrColumns, v2Options);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
copyWithName(name: string): Table {
|
|
159
|
+
return new Table({
|
|
160
|
+
...this.options,
|
|
161
|
+
name
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
private isTableV1(arg: TableOptions | Columns): arg is TableOptions {
|
|
166
|
+
return 'columns' in arg && Array.isArray(arg.columns);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
private initTableV1(options: TableOptions) {
|
|
170
|
+
this.options = {
|
|
171
|
+
...options,
|
|
172
|
+
indexes: options.indexes || []
|
|
173
|
+
};
|
|
174
|
+
this.applyDefaultOptions();
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
private initTableV2(columns: Columns, options?: TableV2Options) {
|
|
178
|
+
const convertedColumns = Object.entries(columns).map(
|
|
179
|
+
([name, columnInfo]) => new Column({ name, type: columnInfo.type })
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
const convertedIndexes = Object.entries(options?.indexes ?? {}).map(
|
|
183
|
+
([name, columnNames]) =>
|
|
184
|
+
new Index({
|
|
185
|
+
name,
|
|
186
|
+
columns: columnNames.map(
|
|
187
|
+
(name) =>
|
|
188
|
+
new IndexedColumn({
|
|
189
|
+
name: name.replace(/^-/, ''),
|
|
190
|
+
ascending: !name.startsWith('-')
|
|
191
|
+
})
|
|
192
|
+
)
|
|
193
|
+
})
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
this.options = {
|
|
197
|
+
name: '',
|
|
198
|
+
columns: convertedColumns,
|
|
199
|
+
indexes: convertedIndexes,
|
|
200
|
+
viewName: options?.viewName,
|
|
201
|
+
insertOnly: options?.insertOnly,
|
|
202
|
+
localOnly: options?.localOnly,
|
|
203
|
+
trackPrevious: options?.trackPrevious,
|
|
204
|
+
trackMetadata: options?.trackMetadata,
|
|
205
|
+
ignoreEmptyUpdates: options?.ignoreEmptyUpdates
|
|
206
|
+
};
|
|
207
|
+
this.applyDefaultOptions();
|
|
208
|
+
|
|
209
|
+
this._mappedColumns = columns;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
private applyDefaultOptions() {
|
|
213
|
+
this.options.insertOnly ??= DEFAULT_TABLE_OPTIONS.insertOnly;
|
|
214
|
+
this.options.localOnly ??= DEFAULT_TABLE_OPTIONS.localOnly;
|
|
215
|
+
this.options.trackPrevious ??= DEFAULT_TABLE_OPTIONS.trackPrevious;
|
|
216
|
+
this.options.trackMetadata ??= DEFAULT_TABLE_OPTIONS.trackMetadata;
|
|
217
|
+
this.options.ignoreEmptyUpdates ??= DEFAULT_TABLE_OPTIONS.ignoreEmptyUpdates;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
get name() {
|
|
221
|
+
return this.options.name;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
get viewNameOverride() {
|
|
225
|
+
return this.options.viewName;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
get viewName() {
|
|
229
|
+
return this.viewNameOverride ?? this.name;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
get columns() {
|
|
233
|
+
return this.options.columns;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
get columnMap(): Columns {
|
|
237
|
+
return (
|
|
238
|
+
this._mappedColumns ??
|
|
239
|
+
this.columns.reduce((hash: Record<string, BaseColumnType<any>>, column) => {
|
|
240
|
+
hash[column.name] = { type: column.type ?? ColumnType.TEXT };
|
|
241
|
+
return hash;
|
|
242
|
+
}, {} as Columns)
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
get indexes() {
|
|
247
|
+
return this.options.indexes ?? [];
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
get localOnly() {
|
|
251
|
+
return this.options.localOnly!;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
get insertOnly() {
|
|
255
|
+
return this.options.insertOnly!;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
get trackPrevious() {
|
|
259
|
+
return this.options.trackPrevious!;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
get trackMetadata() {
|
|
263
|
+
return this.options.trackMetadata!;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
get ignoreEmptyUpdates() {
|
|
267
|
+
return this.options.ignoreEmptyUpdates!;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
get internalName() {
|
|
271
|
+
if (this.options.localOnly) {
|
|
272
|
+
return `ps_data_local__${this.name}`;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
return `ps_data__${this.name}`;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
get validName() {
|
|
279
|
+
if (InvalidSQLCharacters.test(this.name)) {
|
|
280
|
+
return false;
|
|
281
|
+
}
|
|
282
|
+
if (this.viewNameOverride != null && InvalidSQLCharacters.test(this.viewNameOverride)) {
|
|
283
|
+
return false;
|
|
284
|
+
}
|
|
285
|
+
return true;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
validate() {
|
|
289
|
+
if (InvalidSQLCharacters.test(this.name)) {
|
|
290
|
+
throw new Error(`Invalid characters in table name: ${this.name}`);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
if (this.viewNameOverride && InvalidSQLCharacters.test(this.viewNameOverride!)) {
|
|
294
|
+
throw new Error(`Invalid characters in view name: ${this.viewNameOverride}`);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
if (this.columns.length > MAX_AMOUNT_OF_COLUMNS) {
|
|
298
|
+
throw new Error(`Table has too many columns. The maximum number of columns is ${MAX_AMOUNT_OF_COLUMNS}.`);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
if (this.trackMetadata && this.localOnly) {
|
|
302
|
+
throw new Error(`Can't include metadata for local-only tables.`);
|
|
303
|
+
}
|
|
304
|
+
if (this.trackPrevious != false && this.localOnly) {
|
|
305
|
+
throw new Error(`Can't include old values for local-only tables.`);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
const columnNames = new Set<string>();
|
|
309
|
+
columnNames.add('id');
|
|
310
|
+
for (const column of this.columns) {
|
|
311
|
+
const { name: columnName } = column;
|
|
312
|
+
if (column.name === 'id') {
|
|
313
|
+
throw new Error(`An id column is automatically added, custom id columns are not supported`);
|
|
314
|
+
}
|
|
315
|
+
if (columnNames.has(columnName)) {
|
|
316
|
+
throw new Error(`Duplicate column ${columnName}`);
|
|
317
|
+
}
|
|
318
|
+
if (InvalidSQLCharacters.test(columnName)) {
|
|
319
|
+
throw new Error(`Invalid characters in column name: ${column.name}`);
|
|
320
|
+
}
|
|
321
|
+
columnNames.add(columnName);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
const indexNames = new Set<string>();
|
|
325
|
+
for (const index of this.indexes) {
|
|
326
|
+
if (indexNames.has(index.name)) {
|
|
327
|
+
throw new Error(`Duplicate index ${index.name}`);
|
|
328
|
+
}
|
|
329
|
+
if (InvalidSQLCharacters.test(index.name)) {
|
|
330
|
+
throw new Error(`Invalid characters in index name: ${index.name}`);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
for (const column of index.columns) {
|
|
334
|
+
if (!columnNames.has(column.name)) {
|
|
335
|
+
throw new Error(`Column ${column.name} not found for index ${index.name}`);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
indexNames.add(index.name);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
toJSON() {
|
|
344
|
+
const trackPrevious = this.trackPrevious;
|
|
345
|
+
|
|
346
|
+
return {
|
|
347
|
+
name: this.name,
|
|
348
|
+
view_name: this.viewName,
|
|
349
|
+
local_only: this.localOnly,
|
|
350
|
+
insert_only: this.insertOnly,
|
|
351
|
+
include_old: trackPrevious && ((trackPrevious as any).columns ?? true),
|
|
352
|
+
include_old_only_when_changed: typeof trackPrevious == 'object' && trackPrevious.onlyWhenChanged == true,
|
|
353
|
+
include_metadata: this.trackMetadata,
|
|
354
|
+
ignore_empty_update: this.ignoreEmptyUpdates,
|
|
355
|
+
columns: this.columns.map((c) => c.toJSON()),
|
|
356
|
+
indexes: this.indexes.map((e) => e.toJSON(this))
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { ColumnsType } from './Column.js';
|
|
2
|
+
import { Table } from './Table.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
Generate a new table from the columns and indexes
|
|
6
|
+
@deprecated You should use {@link Table} instead as it now allows TableV2 syntax.
|
|
7
|
+
This will be removed in the next major release.
|
|
8
|
+
*/
|
|
9
|
+
export class TableV2<Columns extends ColumnsType = ColumnsType> extends Table<Columns> {}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
export * from './client/AbstractPowerSyncDatabase.js';
|
|
2
|
+
export * from './client/AbstractPowerSyncOpenFactory.js';
|
|
3
|
+
export { compilableQueryWatch, CompilableQueryWatchHandler } from './client/compilableQueryWatch.js';
|
|
4
|
+
export * from './client/connection/PowerSyncBackendConnector.js';
|
|
5
|
+
export * from './client/connection/PowerSyncCredentials.js';
|
|
6
|
+
export { MAX_OP_ID } from './client/constants.js';
|
|
7
|
+
export { runOnSchemaChange } from './client/runOnSchemaChange.js';
|
|
8
|
+
export * from './client/SQLOpenFactory.js';
|
|
9
|
+
export * from './client/sync/bucket/BucketStorageAdapter.js';
|
|
10
|
+
export * from './client/sync/bucket/CrudBatch.js';
|
|
11
|
+
export { CrudEntry, OpId, UpdateType } from './client/sync/bucket/CrudEntry.js';
|
|
12
|
+
export * from './client/sync/bucket/CrudTransaction.js';
|
|
13
|
+
export * from './client/sync/bucket/OplogEntry.js';
|
|
14
|
+
export * from './client/sync/bucket/OpType.js';
|
|
15
|
+
export * from './client/sync/bucket/SqliteBucketStorage.js';
|
|
16
|
+
export * from './client/sync/bucket/SyncDataBatch.js';
|
|
17
|
+
export * from './client/sync/bucket/SyncDataBucket.js';
|
|
18
|
+
export * from './client/sync/stream/AbstractRemote.js';
|
|
19
|
+
export * from './client/sync/stream/AbstractStreamingSyncImplementation.js';
|
|
20
|
+
export * from './client/sync/stream/streaming-sync-types.js';
|
|
21
|
+
export * from './client/sync/sync-streams.js';
|
|
22
|
+
|
|
23
|
+
export * from './client/ConnectionManager.js';
|
|
24
|
+
export { ProgressWithOperations, SyncProgress } from './db/crud/SyncProgress.js';
|
|
25
|
+
export * from './db/crud/SyncStatus.js';
|
|
26
|
+
export * from './db/crud/UploadQueueStatus.js';
|
|
27
|
+
export * from './db/DBAdapter.js';
|
|
28
|
+
export * from './db/schema/Column.js';
|
|
29
|
+
export * from './db/schema/Index.js';
|
|
30
|
+
export * from './db/schema/IndexedColumn.js';
|
|
31
|
+
export * from './db/schema/Schema.js';
|
|
32
|
+
export * from './db/schema/Table.js';
|
|
33
|
+
export * from './db/schema/TableV2.js';
|
|
34
|
+
|
|
35
|
+
export * from './client/Query.js';
|
|
36
|
+
export * from './client/triggers/sanitizeSQL.js';
|
|
37
|
+
export * from './client/triggers/TriggerManager.js';
|
|
38
|
+
export * from './client/watched/GetAllQuery.js';
|
|
39
|
+
export * from './client/watched/processors/AbstractQueryProcessor.js';
|
|
40
|
+
export * from './client/watched/processors/comparators.js';
|
|
41
|
+
export * from './client/watched/processors/DifferentialQueryProcessor.js';
|
|
42
|
+
export * from './client/watched/processors/OnChangeQueryProcessor.js';
|
|
43
|
+
export * from './client/watched/WatchedQuery.js';
|
|
44
|
+
|
|
45
|
+
export * from './utils/AbortOperation.js';
|
|
46
|
+
export * from './utils/BaseObserver.js';
|
|
47
|
+
export * from './utils/ControlledExecutor.js';
|
|
48
|
+
export * from './utils/DataStream.js';
|
|
49
|
+
export * from './utils/Logger.js';
|
|
50
|
+
export * from './utils/parseQuery.js';
|
|
51
|
+
|
|
52
|
+
export * from './types/types.js';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Calls to Abortcontroller.abort(reason: any) will result in the
|
|
3
|
+
* `reason` being thrown. This is not necessarily an error,
|
|
4
|
+
* but extends error for better logging purposes.
|
|
5
|
+
*/
|
|
6
|
+
export class AbortOperation extends Error {
|
|
7
|
+
constructor(protected reason: string) {
|
|
8
|
+
super(reason);
|
|
9
|
+
// Set the prototype explicitly
|
|
10
|
+
Object.setPrototypeOf(this, AbortOperation.prototype);
|
|
11
|
+
|
|
12
|
+
// Capture stack trace
|
|
13
|
+
if (Error.captureStackTrace) {
|
|
14
|
+
Error.captureStackTrace(this, AbortOperation);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export interface Disposable {
|
|
2
|
+
dispose: () => Promise<void> | void;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export type BaseListener = Record<string, ((...event: any) => any) | undefined>;
|
|
6
|
+
|
|
7
|
+
export interface BaseObserverInterface<T extends BaseListener> {
|
|
8
|
+
registerListener(listener: Partial<T>): () => void;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export class BaseObserver<T extends BaseListener = BaseListener> implements BaseObserverInterface<T> {
|
|
12
|
+
protected listeners = new Set<Partial<T>>();
|
|
13
|
+
|
|
14
|
+
constructor() {}
|
|
15
|
+
|
|
16
|
+
dispose(): void {
|
|
17
|
+
this.listeners.clear();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Register a listener for updates to the PowerSync client.
|
|
22
|
+
*/
|
|
23
|
+
registerListener(listener: Partial<T>): () => void {
|
|
24
|
+
this.listeners.add(listener);
|
|
25
|
+
return () => {
|
|
26
|
+
this.listeners.delete(listener);
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
iterateListeners(cb: (listener: Partial<T>) => any) {
|
|
31
|
+
for (const listener of this.listeners) {
|
|
32
|
+
cb(listener);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async iterateAsyncListeners(cb: (listener: Partial<T>) => Promise<any>) {
|
|
37
|
+
for (let i of Array.from(this.listeners.values())) {
|
|
38
|
+
await cb(i);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
export interface ControlledExecutorOptions {
|
|
2
|
+
/**
|
|
3
|
+
* If throttling is enabled, it ensures only one task runs at a time,
|
|
4
|
+
* and only one additional task can be scheduled to run after the current task completes. The pending task will be overwritten by the latest task.
|
|
5
|
+
* Enabled by default.
|
|
6
|
+
*/
|
|
7
|
+
throttleEnabled?: boolean;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export class ControlledExecutor<T> {
|
|
11
|
+
private task: (param: T) => Promise<void> | void;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Represents the currently running task, which could be a Promise or undefined if no task is running.
|
|
15
|
+
*/
|
|
16
|
+
private runningTask: undefined | (Promise<void> | void);
|
|
17
|
+
|
|
18
|
+
private pendingTaskParam: T | undefined;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Flag to determine if throttling is enabled, which controls whether tasks are queued or run immediately.
|
|
22
|
+
*/
|
|
23
|
+
private isThrottling: boolean;
|
|
24
|
+
|
|
25
|
+
private closed: boolean;
|
|
26
|
+
|
|
27
|
+
constructor(task: (param: T) => Promise<void> | void, options?: ControlledExecutorOptions) {
|
|
28
|
+
this.task = task;
|
|
29
|
+
const { throttleEnabled = true } = options ?? {};
|
|
30
|
+
this.isThrottling = throttleEnabled;
|
|
31
|
+
this.closed = false;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
schedule(param: T) {
|
|
35
|
+
if (this.closed) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
if (!this.isThrottling) {
|
|
39
|
+
this.task(param);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (this.runningTask) {
|
|
44
|
+
// set or replace the pending task param with latest one
|
|
45
|
+
this.pendingTaskParam = param;
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
this.execute(param);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
dispose() {
|
|
53
|
+
this.closed = true;
|
|
54
|
+
|
|
55
|
+
if (this.runningTask) {
|
|
56
|
+
this.runningTask = undefined;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
private async execute(param: T) {
|
|
61
|
+
this.runningTask = this.task(param);
|
|
62
|
+
await this.runningTask;
|
|
63
|
+
this.runningTask = undefined;
|
|
64
|
+
|
|
65
|
+
if (this.pendingTaskParam) {
|
|
66
|
+
const pendingParam = this.pendingTaskParam;
|
|
67
|
+
this.pendingTaskParam = undefined;
|
|
68
|
+
|
|
69
|
+
this.execute(pendingParam);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|