@powersync/common 1.41.0 → 1.42.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.
Files changed (183) hide show
  1. package/dist/bundle.cjs +10820 -22
  2. package/dist/bundle.cjs.map +1 -0
  3. package/dist/bundle.mjs +10741 -22
  4. package/dist/bundle.mjs.map +1 -0
  5. package/dist/bundle.node.cjs +10820 -0
  6. package/dist/bundle.node.cjs.map +1 -0
  7. package/dist/bundle.node.mjs +10741 -0
  8. package/dist/bundle.node.mjs.map +1 -0
  9. package/dist/index.d.cts +77 -13
  10. package/lib/client/AbstractPowerSyncDatabase.js +1 -0
  11. package/lib/client/AbstractPowerSyncDatabase.js.map +1 -0
  12. package/lib/client/AbstractPowerSyncOpenFactory.js +1 -0
  13. package/lib/client/AbstractPowerSyncOpenFactory.js.map +1 -0
  14. package/lib/client/ConnectionManager.js +1 -0
  15. package/lib/client/ConnectionManager.js.map +1 -0
  16. package/lib/client/CustomQuery.js +1 -0
  17. package/lib/client/CustomQuery.js.map +1 -0
  18. package/lib/client/Query.js +1 -0
  19. package/lib/client/Query.js.map +1 -0
  20. package/lib/client/SQLOpenFactory.js +1 -0
  21. package/lib/client/SQLOpenFactory.js.map +1 -0
  22. package/lib/client/compilableQueryWatch.js +1 -0
  23. package/lib/client/compilableQueryWatch.js.map +1 -0
  24. package/lib/client/connection/PowerSyncBackendConnector.js +1 -0
  25. package/lib/client/connection/PowerSyncBackendConnector.js.map +1 -0
  26. package/lib/client/connection/PowerSyncCredentials.js +1 -0
  27. package/lib/client/connection/PowerSyncCredentials.js.map +1 -0
  28. package/lib/client/constants.js +1 -0
  29. package/lib/client/constants.js.map +1 -0
  30. package/lib/client/runOnSchemaChange.js +1 -0
  31. package/lib/client/runOnSchemaChange.js.map +1 -0
  32. package/lib/client/sync/bucket/BucketStorageAdapter.js +1 -0
  33. package/lib/client/sync/bucket/BucketStorageAdapter.js.map +1 -0
  34. package/lib/client/sync/bucket/CrudBatch.js +1 -0
  35. package/lib/client/sync/bucket/CrudBatch.js.map +1 -0
  36. package/lib/client/sync/bucket/CrudEntry.js +1 -0
  37. package/lib/client/sync/bucket/CrudEntry.js.map +1 -0
  38. package/lib/client/sync/bucket/CrudTransaction.js +1 -0
  39. package/lib/client/sync/bucket/CrudTransaction.js.map +1 -0
  40. package/lib/client/sync/bucket/OpType.js +1 -0
  41. package/lib/client/sync/bucket/OpType.js.map +1 -0
  42. package/lib/client/sync/bucket/OplogEntry.js +1 -0
  43. package/lib/client/sync/bucket/OplogEntry.js.map +1 -0
  44. package/lib/client/sync/bucket/SqliteBucketStorage.js +1 -0
  45. package/lib/client/sync/bucket/SqliteBucketStorage.js.map +1 -0
  46. package/lib/client/sync/bucket/SyncDataBatch.js +1 -0
  47. package/lib/client/sync/bucket/SyncDataBatch.js.map +1 -0
  48. package/lib/client/sync/bucket/SyncDataBucket.js +1 -0
  49. package/lib/client/sync/bucket/SyncDataBucket.js.map +1 -0
  50. package/lib/client/sync/stream/AbstractRemote.d.ts +5 -0
  51. package/lib/client/sync/stream/AbstractRemote.js +19 -6
  52. package/lib/client/sync/stream/AbstractRemote.js.map +1 -0
  53. package/lib/client/sync/stream/AbstractStreamingSyncImplementation.js +1 -0
  54. package/lib/client/sync/stream/AbstractStreamingSyncImplementation.js.map +1 -0
  55. package/lib/client/sync/stream/WebsocketClientTransport.js +1 -0
  56. package/lib/client/sync/stream/WebsocketClientTransport.js.map +1 -0
  57. package/lib/client/sync/stream/core-instruction.js +1 -0
  58. package/lib/client/sync/stream/core-instruction.js.map +1 -0
  59. package/lib/client/sync/stream/streaming-sync-types.js +1 -0
  60. package/lib/client/sync/stream/streaming-sync-types.js.map +1 -0
  61. package/lib/client/sync/sync-streams.js +1 -0
  62. package/lib/client/sync/sync-streams.js.map +1 -0
  63. package/lib/client/triggers/TriggerManager.d.ts +71 -12
  64. package/lib/client/triggers/TriggerManager.js +1 -0
  65. package/lib/client/triggers/TriggerManager.js.map +1 -0
  66. package/lib/client/triggers/TriggerManagerImpl.js +11 -5
  67. package/lib/client/triggers/TriggerManagerImpl.js.map +1 -0
  68. package/lib/client/triggers/sanitizeSQL.js +1 -0
  69. package/lib/client/triggers/sanitizeSQL.js.map +1 -0
  70. package/lib/client/watched/GetAllQuery.js +1 -0
  71. package/lib/client/watched/GetAllQuery.js.map +1 -0
  72. package/lib/client/watched/WatchedQuery.js +1 -0
  73. package/lib/client/watched/WatchedQuery.js.map +1 -0
  74. package/lib/client/watched/processors/AbstractQueryProcessor.js +1 -0
  75. package/lib/client/watched/processors/AbstractQueryProcessor.js.map +1 -0
  76. package/lib/client/watched/processors/DifferentialQueryProcessor.js +1 -0
  77. package/lib/client/watched/processors/DifferentialQueryProcessor.js.map +1 -0
  78. package/lib/client/watched/processors/OnChangeQueryProcessor.js +1 -0
  79. package/lib/client/watched/processors/OnChangeQueryProcessor.js.map +1 -0
  80. package/lib/client/watched/processors/comparators.js +1 -0
  81. package/lib/client/watched/processors/comparators.js.map +1 -0
  82. package/lib/db/DBAdapter.js +1 -0
  83. package/lib/db/DBAdapter.js.map +1 -0
  84. package/lib/db/crud/SyncProgress.js +1 -0
  85. package/lib/db/crud/SyncProgress.js.map +1 -0
  86. package/lib/db/crud/SyncStatus.js +1 -0
  87. package/lib/db/crud/SyncStatus.js.map +1 -0
  88. package/lib/db/crud/UploadQueueStatus.js +1 -0
  89. package/lib/db/crud/UploadQueueStatus.js.map +1 -0
  90. package/lib/db/schema/Column.js +1 -0
  91. package/lib/db/schema/Column.js.map +1 -0
  92. package/lib/db/schema/Index.js +1 -0
  93. package/lib/db/schema/Index.js.map +1 -0
  94. package/lib/db/schema/IndexedColumn.js +1 -0
  95. package/lib/db/schema/IndexedColumn.js.map +1 -0
  96. package/lib/db/schema/RawTable.js +1 -0
  97. package/lib/db/schema/RawTable.js.map +1 -0
  98. package/lib/db/schema/Schema.js +1 -0
  99. package/lib/db/schema/Schema.js.map +1 -0
  100. package/lib/db/schema/Table.js +1 -0
  101. package/lib/db/schema/Table.js.map +1 -0
  102. package/lib/db/schema/TableV2.js +1 -0
  103. package/lib/db/schema/TableV2.js.map +1 -0
  104. package/lib/index.js +1 -0
  105. package/lib/index.js.map +1 -0
  106. package/lib/types/types.js +1 -0
  107. package/lib/types/types.js.map +1 -0
  108. package/lib/utils/AbortOperation.js +1 -0
  109. package/lib/utils/AbortOperation.js.map +1 -0
  110. package/lib/utils/BaseObserver.js +1 -0
  111. package/lib/utils/BaseObserver.js.map +1 -0
  112. package/lib/utils/ControlledExecutor.js +1 -0
  113. package/lib/utils/ControlledExecutor.js.map +1 -0
  114. package/lib/utils/DataStream.js +1 -0
  115. package/lib/utils/DataStream.js.map +1 -0
  116. package/lib/utils/Logger.js +1 -0
  117. package/lib/utils/Logger.js.map +1 -0
  118. package/lib/utils/MetaBaseObserver.js +1 -0
  119. package/lib/utils/MetaBaseObserver.js.map +1 -0
  120. package/lib/utils/async.js +1 -0
  121. package/lib/utils/async.js.map +1 -0
  122. package/lib/utils/mutex.js +1 -0
  123. package/lib/utils/mutex.js.map +1 -0
  124. package/lib/utils/parseQuery.js +1 -0
  125. package/lib/utils/parseQuery.js.map +1 -0
  126. package/package.json +23 -15
  127. package/src/client/AbstractPowerSyncDatabase.ts +1343 -0
  128. package/src/client/AbstractPowerSyncOpenFactory.ts +39 -0
  129. package/src/client/ConnectionManager.ts +402 -0
  130. package/src/client/CustomQuery.ts +56 -0
  131. package/src/client/Query.ts +106 -0
  132. package/src/client/SQLOpenFactory.ts +55 -0
  133. package/src/client/compilableQueryWatch.ts +55 -0
  134. package/src/client/connection/PowerSyncBackendConnector.ts +25 -0
  135. package/src/client/connection/PowerSyncCredentials.ts +5 -0
  136. package/src/client/constants.ts +1 -0
  137. package/src/client/runOnSchemaChange.ts +31 -0
  138. package/src/client/sync/bucket/BucketStorageAdapter.ts +118 -0
  139. package/src/client/sync/bucket/CrudBatch.ts +21 -0
  140. package/src/client/sync/bucket/CrudEntry.ts +172 -0
  141. package/src/client/sync/bucket/CrudTransaction.ts +21 -0
  142. package/src/client/sync/bucket/OpType.ts +23 -0
  143. package/src/client/sync/bucket/OplogEntry.ts +50 -0
  144. package/src/client/sync/bucket/SqliteBucketStorage.ts +395 -0
  145. package/src/client/sync/bucket/SyncDataBatch.ts +11 -0
  146. package/src/client/sync/bucket/SyncDataBucket.ts +49 -0
  147. package/src/client/sync/stream/AbstractRemote.ts +636 -0
  148. package/src/client/sync/stream/AbstractStreamingSyncImplementation.ts +1258 -0
  149. package/src/client/sync/stream/WebsocketClientTransport.ts +80 -0
  150. package/src/client/sync/stream/core-instruction.ts +99 -0
  151. package/src/client/sync/stream/streaming-sync-types.ts +205 -0
  152. package/src/client/sync/sync-streams.ts +107 -0
  153. package/src/client/triggers/TriggerManager.ts +451 -0
  154. package/src/client/triggers/TriggerManagerImpl.ts +320 -0
  155. package/src/client/triggers/sanitizeSQL.ts +66 -0
  156. package/src/client/watched/GetAllQuery.ts +46 -0
  157. package/src/client/watched/WatchedQuery.ts +121 -0
  158. package/src/client/watched/processors/AbstractQueryProcessor.ts +226 -0
  159. package/src/client/watched/processors/DifferentialQueryProcessor.ts +305 -0
  160. package/src/client/watched/processors/OnChangeQueryProcessor.ts +122 -0
  161. package/src/client/watched/processors/comparators.ts +57 -0
  162. package/src/db/DBAdapter.ts +134 -0
  163. package/src/db/crud/SyncProgress.ts +100 -0
  164. package/src/db/crud/SyncStatus.ts +308 -0
  165. package/src/db/crud/UploadQueueStatus.ts +20 -0
  166. package/src/db/schema/Column.ts +60 -0
  167. package/src/db/schema/Index.ts +39 -0
  168. package/src/db/schema/IndexedColumn.ts +42 -0
  169. package/src/db/schema/RawTable.ts +67 -0
  170. package/src/db/schema/Schema.ts +76 -0
  171. package/src/db/schema/Table.ts +359 -0
  172. package/src/db/schema/TableV2.ts +9 -0
  173. package/src/index.ts +52 -0
  174. package/src/types/types.ts +9 -0
  175. package/src/utils/AbortOperation.ts +17 -0
  176. package/src/utils/BaseObserver.ts +41 -0
  177. package/src/utils/ControlledExecutor.ts +72 -0
  178. package/src/utils/DataStream.ts +211 -0
  179. package/src/utils/Logger.ts +47 -0
  180. package/src/utils/MetaBaseObserver.ts +81 -0
  181. package/src/utils/async.ts +61 -0
  182. package/src/utils/mutex.ts +34 -0
  183. package/src/utils/parseQuery.ts +25 -0
@@ -0,0 +1,134 @@
1
+ /**
2
+ * Set of generic interfaces to allow PowerSync compatibility with
3
+ * different SQLite DB implementations.
4
+ */
5
+
6
+ import { BaseListener, BaseObserverInterface } from '../utils/BaseObserver.js';
7
+
8
+ /**
9
+ * TODO most of these types could be exported to a common `types` package
10
+ * which is used by the DB adapter libraries as well.
11
+ */
12
+
13
+ /**
14
+ * Object returned by SQL Query executions.
15
+ */
16
+ export type QueryResult = {
17
+ /** Represents the auto-generated row id if applicable. */
18
+ insertId?: number;
19
+ /** Number of affected rows if result of a update query. */
20
+ rowsAffected: number;
21
+ /** if status is undefined or 0 this object will contain the query results */
22
+ rows?: {
23
+ /** Raw array with all dataset */
24
+ _array: any[];
25
+ /** The length of the dataset */
26
+ length: number;
27
+ /** A convenience function to acess the index based the row object
28
+ * @param idx the row index
29
+ * @returns the row structure identified by column names
30
+ */
31
+ item: (idx: number) => any;
32
+ };
33
+ };
34
+
35
+ export interface DBGetUtils {
36
+ /** Execute a read-only query and return results. */
37
+ getAll<T>(sql: string, parameters?: any[]): Promise<T[]>;
38
+ /** Execute a read-only query and return the first result, or null if the ResultSet is empty. */
39
+ getOptional<T>(sql: string, parameters?: any[]): Promise<T | null>;
40
+ /** Execute a read-only query and return the first result, error if the ResultSet is empty. */
41
+ get<T>(sql: string, parameters?: any[]): Promise<T>;
42
+ }
43
+
44
+ export interface LockContext extends DBGetUtils {
45
+ /** Execute a single write statement. */
46
+ execute: (query: string, params?: any[] | undefined) => Promise<QueryResult>;
47
+ /**
48
+ * Execute a single write statement and return raw results.
49
+ * Unlike `execute`, which returns an object with structured key-value pairs,
50
+ * `executeRaw` returns a nested array of raw values, where each row is
51
+ * represented as an array of column values without field names.
52
+ *
53
+ * Example result:
54
+ *
55
+ * ```[ [ '1', 'list 1', '33', 'Post content', '1' ] ]```
56
+ *
57
+ * Where as `execute`'s `rows._array` would have been:
58
+ *
59
+ * ```[ { id: '33', name: 'list 1', content: 'Post content', list_id: '1' } ]```
60
+ */
61
+ executeRaw: (query: string, params?: any[] | undefined) => Promise<any[][]>;
62
+ }
63
+
64
+ export interface Transaction extends LockContext {
65
+ /** Commit multiple changes to the local DB using the Transaction context. */
66
+ commit: () => Promise<QueryResult>;
67
+ /** Roll back multiple attempted changes using the Transaction context. */
68
+ rollback: () => Promise<QueryResult>;
69
+ }
70
+
71
+ /**
72
+ * Update table operation numbers from SQLite
73
+ */
74
+ export enum RowUpdateType {
75
+ SQLITE_INSERT = 18,
76
+ SQLITE_DELETE = 9,
77
+ SQLITE_UPDATE = 23
78
+ }
79
+ export interface TableUpdateOperation {
80
+ opType: RowUpdateType;
81
+ rowId: number;
82
+ }
83
+ /**
84
+ * Notification of an update to one or more tables, for the purpose of realtime change notifications.
85
+ */
86
+ export interface UpdateNotification extends TableUpdateOperation {
87
+ table: string;
88
+ }
89
+
90
+ export interface BatchedUpdateNotification {
91
+ rawUpdates: UpdateNotification[];
92
+ tables: string[];
93
+ groupedUpdates: Record<string, TableUpdateOperation[]>;
94
+ }
95
+
96
+ export interface DBAdapterListener extends BaseListener {
97
+ /**
98
+ * Listener for table updates.
99
+ * Allows for single table updates in order to maintain API compatibility
100
+ * without the need for a major version bump
101
+ * The DB adapter can also batch update notifications if supported.
102
+ */
103
+ tablesUpdated: (updateNotification: BatchedUpdateNotification | UpdateNotification) => void;
104
+ }
105
+
106
+ export interface DBLockOptions {
107
+ timeoutMs?: number;
108
+ }
109
+
110
+ export interface DBAdapter extends BaseObserverInterface<DBAdapterListener>, DBGetUtils {
111
+ close: () => void | Promise<void>;
112
+ execute: (query: string, params?: any[]) => Promise<QueryResult>;
113
+ executeRaw: (query: string, params?: any[]) => Promise<any[][]>;
114
+ executeBatch: (query: string, params?: any[][]) => Promise<QueryResult>;
115
+ name: string;
116
+ readLock: <T>(fn: (tx: LockContext) => Promise<T>, options?: DBLockOptions) => Promise<T>;
117
+ readTransaction: <T>(fn: (tx: Transaction) => Promise<T>, options?: DBLockOptions) => Promise<T>;
118
+ writeLock: <T>(fn: (tx: LockContext) => Promise<T>, options?: DBLockOptions) => Promise<T>;
119
+ writeTransaction: <T>(fn: (tx: Transaction) => Promise<T>, options?: DBLockOptions) => Promise<T>;
120
+ /**
121
+ * This method refreshes the schema information across all connections. This is for advanced use cases, and should generally not be needed.
122
+ */
123
+ refreshSchema: () => Promise<void>;
124
+ }
125
+
126
+ export function isBatchedUpdateNotification(
127
+ update: BatchedUpdateNotification | UpdateNotification
128
+ ): update is BatchedUpdateNotification {
129
+ return 'tables' in update;
130
+ }
131
+
132
+ export function extractTableUpdates(update: BatchedUpdateNotification | UpdateNotification) {
133
+ return isBatchedUpdateNotification(update) ? update.tables : [update.table];
134
+ }
@@ -0,0 +1,100 @@
1
+ import type { BucketProgress } from '../../client/sync/stream/core-instruction.js';
2
+ import type { SyncStatus } from './SyncStatus.js';
3
+
4
+ // (bucket, progress) pairs
5
+ /** @internal */
6
+ export type InternalProgressInformation = Record<string, BucketProgress>;
7
+
8
+ /**
9
+ * @internal The priority used by the core extension to indicate that a full sync was completed.
10
+ */
11
+ export const FULL_SYNC_PRIORITY = 2147483647;
12
+
13
+ /**
14
+ * Information about a progressing download made by the PowerSync SDK.
15
+ *
16
+ * To obtain these values, use {@link SyncProgress}, available through
17
+ * {@link SyncStatus#downloadProgress}.
18
+ */
19
+ export interface ProgressWithOperations {
20
+ /**
21
+ * The total amount of operations to download for the current sync iteration
22
+ * to complete.
23
+ */
24
+ totalOperations: number;
25
+ /**
26
+ * The amount of operations that have already been downloaded.
27
+ */
28
+ downloadedOperations: number;
29
+
30
+ /**
31
+ * Relative progress, as {@link downloadedOperations} of {@link totalOperations}.
32
+ *
33
+ * This will be a number between `0.0` and `1.0` (inclusive).
34
+ *
35
+ * When this number reaches `1.0`, all changes have been received from the sync service.
36
+ * Actually applying these changes happens before the `downloadProgress` field is cleared from
37
+ * {@link SyncStatus}, so progress can stay at `1.0` for a short while before completing.
38
+ */
39
+ downloadedFraction: number;
40
+ }
41
+
42
+ /**
43
+ * Provides realtime progress on how PowerSync is downloading rows.
44
+ *
45
+ * The progress until the next complete sync is available through the fields on {@link ProgressWithOperations},
46
+ * which this class implements.
47
+ * Additionally, the {@link SyncProgress.untilPriority} method can be used to otbain progress towards
48
+ * a specific priority (instead of the progress for the entire download).
49
+ *
50
+ * The reported progress always reflects the status towards the end of a sync iteration (after
51
+ * which a consistent snapshot of all buckets is available locally).
52
+ *
53
+ * In rare cases (in particular, when a [compacting](https://docs.powersync.com/usage/lifecycle-maintenance/compacting-buckets)
54
+ * operation takes place between syncs), it's possible for the returned numbers to be slightly
55
+ * inaccurate. For this reason, {@link SyncProgress} should be seen as an approximation of progress.
56
+ * The information returned is good enough to build progress bars, but not exact enough to track
57
+ * individual download counts.
58
+ *
59
+ * Also note that data is downloaded in bulk, which means that individual counters are unlikely
60
+ * to be updated one-by-one.
61
+ */
62
+ export class SyncProgress implements ProgressWithOperations {
63
+ totalOperations: number;
64
+ downloadedOperations: number;
65
+ downloadedFraction: number;
66
+
67
+ constructor(protected internal: InternalProgressInformation) {
68
+ const untilCompletion = this.untilPriority(FULL_SYNC_PRIORITY);
69
+
70
+ this.totalOperations = untilCompletion.totalOperations;
71
+ this.downloadedOperations = untilCompletion.downloadedOperations;
72
+ this.downloadedFraction = untilCompletion.downloadedFraction;
73
+ }
74
+
75
+ /**
76
+ * Returns download progress towards all data up until the specified priority being received.
77
+ *
78
+ * The returned {@link ProgressWithOperations} tracks the target amount of operations that need
79
+ * to be downloaded in total and how many of them have already been received.
80
+ */
81
+ untilPriority(priority: number): ProgressWithOperations {
82
+ let total = 0;
83
+ let downloaded = 0;
84
+
85
+ for (const progress of Object.values(this.internal)) {
86
+ // Include higher-priority buckets, which are represented by lower numbers.
87
+ if (progress.priority <= priority) {
88
+ downloaded += progress.since_last;
89
+ total += progress.target_count - progress.at_last;
90
+ }
91
+ }
92
+
93
+ let progress = total == 0 ? 0.0 : downloaded / total;
94
+ return {
95
+ totalOperations: total,
96
+ downloadedOperations: downloaded,
97
+ downloadedFraction: progress
98
+ };
99
+ }
100
+ }
@@ -0,0 +1,308 @@
1
+ import { CoreStreamSubscription } from '../../client/sync/stream/core-instruction.js';
2
+ import { SyncClientImplementation } from '../../client/sync/stream/AbstractStreamingSyncImplementation.js';
3
+ import { InternalProgressInformation, ProgressWithOperations, SyncProgress } from './SyncProgress.js';
4
+ import { SyncStreamDescription, SyncSubscriptionDescription } from '../../client/sync/sync-streams.js';
5
+
6
+ export type SyncDataFlowStatus = Partial<{
7
+ downloading: boolean;
8
+ uploading: boolean;
9
+ /**
10
+ * Error during downloading (including connecting).
11
+ *
12
+ * Cleared on the next successful data download.
13
+ */
14
+ downloadError?: Error;
15
+ /**
16
+ * Error during uploading.
17
+ * Cleared on the next successful upload.
18
+ */
19
+ uploadError?: Error;
20
+ /**
21
+ * Internal information about how far we are downloading operations in buckets.
22
+ *
23
+ * Please use the {@link SyncStatus#downloadProgress} property to track sync progress.
24
+ */
25
+ downloadProgress: InternalProgressInformation | null;
26
+ internalStreamSubscriptions: CoreStreamSubscription[] | null;
27
+ }>;
28
+
29
+ export interface SyncPriorityStatus {
30
+ priority: number;
31
+ lastSyncedAt?: Date;
32
+ hasSynced?: boolean;
33
+ }
34
+
35
+ export type SyncStatusOptions = {
36
+ connected?: boolean;
37
+ connecting?: boolean;
38
+ dataFlow?: SyncDataFlowStatus;
39
+ lastSyncedAt?: Date;
40
+ hasSynced?: boolean;
41
+ priorityStatusEntries?: SyncPriorityStatus[];
42
+ clientImplementation?: SyncClientImplementation;
43
+ };
44
+
45
+ export class SyncStatus {
46
+ constructor(protected options: SyncStatusOptions) {}
47
+
48
+ /**
49
+ * Returns the used sync client implementation (either the one implemented in JavaScript or the newer Rust-based
50
+ * implementation).
51
+ *
52
+ * This information is only available after a connection has been requested.
53
+ */
54
+ get clientImplementation() {
55
+ return this.options.clientImplementation;
56
+ }
57
+
58
+ /**
59
+ * Indicates if the client is currently connected to the PowerSync service.
60
+ *
61
+ * @returns {boolean} True if connected, false otherwise. Defaults to false if not specified.
62
+ */
63
+ get connected() {
64
+ return this.options.connected ?? false;
65
+ }
66
+
67
+ /**
68
+ * Indicates if the client is in the process of establishing a connection to the PowerSync service.
69
+ *
70
+ * @returns {boolean} True if connecting, false otherwise. Defaults to false if not specified.
71
+ */
72
+ get connecting() {
73
+ return this.options.connecting ?? false;
74
+ }
75
+
76
+ /**
77
+ * Time that a last sync has fully completed, if any.
78
+ * This timestamp is reset to null after a restart of the PowerSync service.
79
+ *
80
+ * @returns {Date | undefined} The timestamp of the last successful sync, or undefined if no sync has completed.
81
+ */
82
+ get lastSyncedAt() {
83
+ return this.options.lastSyncedAt;
84
+ }
85
+
86
+ /**
87
+ * Indicates whether there has been at least one full sync completed since initialization.
88
+ *
89
+ * @returns {boolean | undefined} True if at least one sync has completed, false if no sync has completed,
90
+ * or undefined when the state is still being loaded from the database.
91
+ */
92
+ get hasSynced() {
93
+ return this.options.hasSynced;
94
+ }
95
+
96
+ /**
97
+ * Provides the current data flow status regarding uploads and downloads.
98
+ *
99
+ * @returns {SyncDataFlowStatus} An object containing:
100
+ * - downloading: True if actively downloading changes (only when connected is also true)
101
+ * - uploading: True if actively uploading changes
102
+ * Defaults to {downloading: false, uploading: false} if not specified.
103
+ */
104
+ get dataFlowStatus() {
105
+ return (
106
+ this.options.dataFlow ?? {
107
+ /**
108
+ * true if actively downloading changes.
109
+ * This is only true when {@link connected} is also true.
110
+ */
111
+ downloading: false,
112
+ /**
113
+ * true if uploading changes.
114
+ */
115
+ uploading: false
116
+ }
117
+ );
118
+ }
119
+
120
+ /**
121
+ * All sync streams currently being tracked in teh database.
122
+ *
123
+ * This returns null when the database is currently being opened and we don't have reliable information about all
124
+ * included streams yet.
125
+ *
126
+ * @experimental Sync streams are currently in alpha.
127
+ */
128
+ get syncStreams(): SyncStreamStatus[] | undefined {
129
+ return this.options.dataFlow?.internalStreamSubscriptions?.map((core) => new SyncStreamStatusView(this, core));
130
+ }
131
+
132
+ /**
133
+ * If the `stream` appears in {@link syncStreams}, returns the current status for that stream.
134
+ *
135
+ * @experimental Sync streams are currently in alpha.
136
+ */
137
+ forStream(stream: SyncStreamDescription): SyncStreamStatus | undefined {
138
+ const asJson = JSON.stringify(stream.parameters);
139
+ const raw = this.options.dataFlow?.internalStreamSubscriptions?.find(
140
+ (r) => r.name == stream.name && asJson == JSON.stringify(r.parameters)
141
+ );
142
+
143
+ return raw && new SyncStreamStatusView(this, raw);
144
+ }
145
+
146
+ /**
147
+ * Provides sync status information for all bucket priorities, sorted by priority (highest first).
148
+ *
149
+ * @returns {SyncPriorityStatus[]} An array of status entries for different sync priority levels,
150
+ * sorted with highest priorities (lower numbers) first.
151
+ */
152
+ get priorityStatusEntries() {
153
+ return (this.options.priorityStatusEntries ?? []).slice().sort(SyncStatus.comparePriorities);
154
+ }
155
+
156
+ /**
157
+ * A realtime progress report on how many operations have been downloaded and
158
+ * how many are necessary in total to complete the next sync iteration.
159
+ *
160
+ * This field is only set when {@link SyncDataFlowStatus#downloading} is also true.
161
+ */
162
+ get downloadProgress(): SyncProgress | null {
163
+ const internalProgress = this.options.dataFlow?.downloadProgress;
164
+ if (internalProgress == null) {
165
+ return null;
166
+ }
167
+
168
+ return new SyncProgress(internalProgress);
169
+ }
170
+
171
+ /**
172
+ * Reports the sync status (a pair of {@link SyncStatus#hasSynced} and {@link SyncStatus#lastSyncedAt} fields)
173
+ * for a specific bucket priority level.
174
+ *
175
+ * When buckets with different priorities are declared, PowerSync may choose to synchronize higher-priority
176
+ * buckets first. When a consistent view over all buckets for all priorities up until the given priority is
177
+ * reached, PowerSync makes data from those buckets available before lower-priority buckets have finished
178
+ * syncing.
179
+ *
180
+ * This method returns the status for the requested priority or the next higher priority level that has
181
+ * status information available. This is because when PowerSync makes data for a given priority available,
182
+ * all buckets in higher-priorities are guaranteed to be consistent with that checkpoint.
183
+ *
184
+ * For example, if PowerSync just finished synchronizing buckets in priority level 3, calling this method
185
+ * with a priority of 1 may return information for priority level 3.
186
+ *
187
+ * @param {number} priority The bucket priority for which the status should be reported
188
+ * @returns {SyncPriorityStatus} Status information for the requested priority level or the next higher level with available status
189
+ */
190
+ statusForPriority(priority: number): SyncPriorityStatus {
191
+ // priorityStatusEntries are sorted by ascending priorities (so higher numbers to lower numbers).
192
+ for (const known of this.priorityStatusEntries) {
193
+ // We look for the first entry that doesn't have a higher priority.
194
+ if (known.priority >= priority) {
195
+ return known;
196
+ }
197
+ }
198
+
199
+ // If we have a complete sync, that necessarily includes all priorities.
200
+ return {
201
+ priority,
202
+ lastSyncedAt: this.lastSyncedAt,
203
+ hasSynced: this.hasSynced
204
+ };
205
+ }
206
+
207
+ /**
208
+ * Compares this SyncStatus instance with another to determine if they are equal.
209
+ * Equality is determined by comparing the serialized JSON representation of both instances.
210
+ *
211
+ * @param {SyncStatus} status The SyncStatus instance to compare against
212
+ * @returns {boolean} True if the instances are considered equal, false otherwise
213
+ */
214
+ isEqual(status: SyncStatus) {
215
+ /**
216
+ * By default Error object are serialized to an empty object.
217
+ * This replaces Errors with more useful information before serialization.
218
+ */
219
+ const replacer = (_: string, value: any) => {
220
+ if (value instanceof Error) {
221
+ return {
222
+ name: value.name,
223
+ message: value.message,
224
+ stack: value.stack
225
+ };
226
+ }
227
+ return value;
228
+ };
229
+
230
+ return JSON.stringify(this.options, replacer) == JSON.stringify(status.options, replacer);
231
+ }
232
+
233
+ /**
234
+ * Creates a human-readable string representation of the current sync status.
235
+ * Includes information about connection state, sync completion, and data flow.
236
+ *
237
+ * @returns {string} A string representation of the sync status
238
+ */
239
+ getMessage() {
240
+ const dataFlow = this.dataFlowStatus;
241
+ return `SyncStatus<connected: ${this.connected} connecting: ${this.connecting} lastSyncedAt: ${this.lastSyncedAt} hasSynced: ${this.hasSynced}. Downloading: ${dataFlow.downloading}. Uploading: ${dataFlow.uploading}. UploadError: ${dataFlow.uploadError}, DownloadError?: ${dataFlow.downloadError}>`;
242
+ }
243
+
244
+ /**
245
+ * Serializes the SyncStatus instance to a plain object.
246
+ *
247
+ * @returns {SyncStatusOptions} A plain object representation of the sync status
248
+ */
249
+ toJSON(): SyncStatusOptions {
250
+ return {
251
+ connected: this.connected,
252
+ connecting: this.connecting,
253
+ dataFlow: this.dataFlowStatus,
254
+ lastSyncedAt: this.lastSyncedAt,
255
+ hasSynced: this.hasSynced,
256
+ priorityStatusEntries: this.priorityStatusEntries
257
+ };
258
+ }
259
+
260
+ private static comparePriorities(a: SyncPriorityStatus, b: SyncPriorityStatus) {
261
+ return b.priority - a.priority; // Reverse because higher priorities have lower numbers
262
+ }
263
+ }
264
+
265
+ /**
266
+ * Information about a sync stream subscription.
267
+ */
268
+ export interface SyncStreamStatus {
269
+ progress: ProgressWithOperations | null;
270
+ subscription: SyncSubscriptionDescription;
271
+ priority: number | null;
272
+ }
273
+
274
+ class SyncStreamStatusView implements SyncStreamStatus {
275
+ subscription: SyncSubscriptionDescription;
276
+
277
+ constructor(
278
+ private status: SyncStatus,
279
+ private core: CoreStreamSubscription
280
+ ) {
281
+ this.subscription = {
282
+ name: core.name,
283
+ parameters: core.parameters,
284
+ active: core.active,
285
+ isDefault: core.is_default,
286
+ hasExplicitSubscription: core.has_explicit_subscription,
287
+ expiresAt: core.expires_at != null ? new Date(core.expires_at * 1000) : null,
288
+ hasSynced: core.last_synced_at != null,
289
+ lastSyncedAt: core.last_synced_at != null ? new Date(core.last_synced_at * 1000) : null
290
+ };
291
+ }
292
+
293
+ get progress() {
294
+ if (this.status.dataFlowStatus.downloadProgress == null) {
295
+ // Don't make download progress public if we're not currently downloading.
296
+ return null;
297
+ }
298
+
299
+ const { total, downloaded } = this.core.progress;
300
+ const progress = total == 0 ? 0.0 : downloaded / total;
301
+
302
+ return { totalOperations: total, downloadedOperations: downloaded, downloadedFraction: progress };
303
+ }
304
+
305
+ get priority() {
306
+ return this.core.priority;
307
+ }
308
+ }
@@ -0,0 +1,20 @@
1
+ export class UploadQueueStats {
2
+ constructor(
3
+ /**
4
+ * Number of records in the upload queue.
5
+ */
6
+ public count: number,
7
+ /**
8
+ * Size of the upload queue in bytes.
9
+ */
10
+ public size: number | null = null
11
+ ) {}
12
+
13
+ toString() {
14
+ if (this.size == null) {
15
+ return `UploadQueueStats<count:${this.count}>`;
16
+ } else {
17
+ return `UploadQueueStats<count: $count size: ${this.size / 1024}kB>`;
18
+ }
19
+ }
20
+ }
@@ -0,0 +1,60 @@
1
+ // https://www.sqlite.org/lang_expr.html#castexpr
2
+ export enum ColumnType {
3
+ TEXT = 'TEXT',
4
+ INTEGER = 'INTEGER',
5
+ REAL = 'REAL'
6
+ }
7
+
8
+ export interface ColumnOptions {
9
+ name: string;
10
+ type?: ColumnType;
11
+ }
12
+
13
+ export type BaseColumnType<T extends number | string | null> = {
14
+ type: ColumnType;
15
+ };
16
+
17
+ export type ColumnsType = Record<string, BaseColumnType<any>>;
18
+
19
+ export type ExtractColumnValueType<T extends BaseColumnType<any>> = T extends BaseColumnType<infer R> ? R : unknown;
20
+
21
+ const text: BaseColumnType<string | null> = {
22
+ type: ColumnType.TEXT
23
+ };
24
+
25
+ const integer: BaseColumnType<number | null> = {
26
+ type: ColumnType.INTEGER
27
+ };
28
+
29
+ const real: BaseColumnType<number | null> = {
30
+ type: ColumnType.REAL
31
+ };
32
+
33
+ // powersync-sqlite-core limits the number of column per table to 1999, due to internal SQLite limits.
34
+ // In earlier versions this was limited to 63.
35
+ export const MAX_AMOUNT_OF_COLUMNS = 1999;
36
+
37
+ export const column = {
38
+ text,
39
+ integer,
40
+ real
41
+ };
42
+
43
+ export class Column {
44
+ constructor(protected options: ColumnOptions) {}
45
+
46
+ get name() {
47
+ return this.options.name;
48
+ }
49
+
50
+ get type() {
51
+ return this.options.type;
52
+ }
53
+
54
+ toJSON() {
55
+ return {
56
+ name: this.name,
57
+ type: this.type
58
+ };
59
+ }
60
+ }
@@ -0,0 +1,39 @@
1
+ import { IndexedColumn } from './IndexedColumn.js';
2
+ import { Table } from './Table.js';
3
+
4
+ export interface IndexOptions {
5
+ name: string;
6
+ columns?: IndexedColumn[];
7
+ }
8
+
9
+ export const DEFAULT_INDEX_OPTIONS: Partial<IndexOptions> = {
10
+ columns: []
11
+ };
12
+
13
+ export class Index {
14
+ static createAscending(options: IndexOptions, columnNames: string[]) {
15
+ return new Index({
16
+ ...options,
17
+ columns: columnNames.map((name) => IndexedColumn.createAscending(name))
18
+ });
19
+ }
20
+
21
+ constructor(protected options: IndexOptions) {
22
+ this.options = { ...DEFAULT_INDEX_OPTIONS, ...options };
23
+ }
24
+
25
+ get name() {
26
+ return this.options.name;
27
+ }
28
+
29
+ get columns() {
30
+ return this.options.columns ?? [];
31
+ }
32
+
33
+ toJSON(table: Table) {
34
+ return {
35
+ name: this.name,
36
+ columns: this.columns.map((c) => c.toJSON(table))
37
+ };
38
+ }
39
+ }