@powersync/common 1.27.1 → 1.29.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.
@@ -183,6 +183,7 @@ export declare abstract class AbstractPowerSyncDatabase extends BaseObserver<Pow
183
183
  * Cannot be used while connected - this should only be called before {@link AbstractPowerSyncDatabase.connect}.
184
184
  */
185
185
  updateSchema(schema: Schema): Promise<void>;
186
+ get logger(): Logger.ILogger;
186
187
  /**
187
188
  * Wait for initialization to complete.
188
189
  * While initializing is automatic, this helps to catch and report initialization errors.
@@ -15,6 +15,7 @@ import { CrudBatch } from './sync/bucket/CrudBatch.js';
15
15
  import { CrudEntry } from './sync/bucket/CrudEntry.js';
16
16
  import { CrudTransaction } from './sync/bucket/CrudTransaction.js';
17
17
  import { DEFAULT_CRUD_UPLOAD_THROTTLE_MS, DEFAULT_RETRY_DELAY_MS } from './sync/stream/AbstractStreamingSyncImplementation.js';
18
+ import { FULL_SYNC_PRIORITY } from '../db/crud/SyncProgress.js';
18
19
  const POWERSYNC_TABLE_MATCH = /(^ps_data__|^ps_data_local__)/;
19
20
  const DEFAULT_DISCONNECT_CLEAR_OPTIONS = {
20
21
  clearLocal: true
@@ -42,10 +43,6 @@ export const DEFAULT_LOCK_TIMEOUT_MS = 120_000; // 2 mins
42
43
  export const isPowerSyncDatabaseOptionsWithSettings = (test) => {
43
44
  return typeof test == 'object' && isSQLOpenOptions(test.database);
44
45
  };
45
- /**
46
- * The priority used by the core extension to indicate that a full sync was completed.
47
- */
48
- const FULL_SYNC_PRIORITY = 2147483647;
49
46
  export class AbstractPowerSyncDatabase extends BaseObserver {
50
47
  options;
51
48
  /**
@@ -250,6 +247,9 @@ export class AbstractPowerSyncDatabase extends BaseObserver {
250
247
  await this.database.refreshSchema();
251
248
  this.iterateListeners(async (cb) => cb.schemaChanged?.(schema));
252
249
  }
250
+ get logger() {
251
+ return this.options.logger;
252
+ }
253
253
  /**
254
254
  * Wait for initialization to complete.
255
255
  * While initializing is automatic, this helps to catch and report initialization errors.
@@ -260,6 +260,7 @@ export class AbstractPowerSyncDatabase extends BaseObserver {
260
260
  // Use the options passed in during connect, or fallback to the options set during database creation or fallback to the default options
261
261
  resolvedConnectionOptions(options) {
262
262
  return {
263
+ ...options,
263
264
  retryDelayMs: options?.retryDelayMs ?? this.options.retryDelayMs ?? this.options.retryDelay ?? DEFAULT_RETRY_DELAY_MS,
264
265
  crudUploadThrottleMs: options?.crudUploadThrottleMs ?? this.options.crudUploadThrottleMs ?? DEFAULT_CRUD_UPLOAD_THROTTLE_MS
265
266
  };
@@ -274,11 +275,8 @@ export class AbstractPowerSyncDatabase extends BaseObserver {
274
275
  if (this.closed) {
275
276
  throw new Error('Cannot connect using a closed client');
276
277
  }
277
- const { retryDelayMs, crudUploadThrottleMs } = this.resolvedConnectionOptions(options);
278
- this.syncStreamImplementation = this.generateSyncStreamImplementation(connector, {
279
- retryDelayMs,
280
- crudUploadThrottleMs
281
- });
278
+ const resolvedConnectOptions = this.resolvedConnectionOptions(options);
279
+ this.syncStreamImplementation = this.generateSyncStreamImplementation(connector, resolvedConnectOptions);
282
280
  this.syncStatusListenerDisposer = this.syncStreamImplementation.registerListener({
283
281
  statusChanged: (status) => {
284
282
  this.currentStatus = new SyncStatus({
@@ -27,6 +27,11 @@ export interface SyncLocalDatabaseResult {
27
27
  checkpointValid: boolean;
28
28
  checkpointFailures?: string[];
29
29
  }
30
+ export type SavedProgress = {
31
+ atLast: number;
32
+ sinceLast: number;
33
+ };
34
+ export type BucketOperationProgress = Record<string, SavedProgress>;
30
35
  export interface BucketChecksum {
31
36
  bucket: string;
32
37
  priority?: number;
@@ -56,6 +61,7 @@ export interface BucketStorageAdapter extends BaseObserver<BucketStorageListener
56
61
  setTargetCheckpoint(checkpoint: Checkpoint): Promise<void>;
57
62
  startSession(): void;
58
63
  getBucketStates(): Promise<BucketState[]>;
64
+ getBucketOperationProgress(): Promise<BucketOperationProgress>;
59
65
  syncLocalDatabase(checkpoint: Checkpoint, priority?: number): Promise<{
60
66
  checkpointValid: boolean;
61
67
  ready: boolean;
@@ -2,7 +2,7 @@ import { Mutex } from 'async-mutex';
2
2
  import { ILogger } from 'js-logger';
3
3
  import { DBAdapter, Transaction } from '../../../db/DBAdapter.js';
4
4
  import { BaseObserver } from '../../../utils/BaseObserver.js';
5
- import { BucketState, BucketStorageAdapter, BucketStorageListener, Checkpoint, SyncLocalDatabaseResult } from './BucketStorageAdapter.js';
5
+ import { BucketOperationProgress, BucketState, BucketStorageAdapter, BucketStorageListener, Checkpoint, SyncLocalDatabaseResult } from './BucketStorageAdapter.js';
6
6
  import { CrudBatch } from './CrudBatch.js';
7
7
  import { CrudEntry } from './CrudEntry.js';
8
8
  import { SyncDataBatch } from './SyncDataBatch.js';
@@ -30,6 +30,7 @@ export declare class SqliteBucketStorage extends BaseObserver<BucketStorageListe
30
30
  */
31
31
  startSession(): void;
32
32
  getBucketStates(): Promise<BucketState[]>;
33
+ getBucketOperationProgress(): Promise<BucketOperationProgress>;
33
34
  saveSyncData(batch: SyncDataBatch): Promise<void>;
34
35
  removeBuckets(buckets: string[]): Promise<void>;
35
36
  /**
@@ -66,6 +66,10 @@ export class SqliteBucketStorage extends BaseObserver {
66
66
  const result = await this.db.getAll("SELECT name as bucket, cast(last_op as TEXT) as op_id FROM ps_buckets WHERE pending_delete = 0 AND name != '$local'");
67
67
  return result;
68
68
  }
69
+ async getBucketOperationProgress() {
70
+ const rows = await this.db.getAll('SELECT name, count_at_last, count_since_last FROM ps_buckets');
71
+ return Object.fromEntries(rows.map((r) => [r.name, { atLast: r.count_at_last, sinceLast: r.count_since_last }]));
72
+ }
69
73
  async saveSyncData(batch) {
70
74
  await this.writeTransaction(async (tx) => {
71
75
  let count = 0;
@@ -161,7 +165,18 @@ export class SqliteBucketStorage extends BaseObserver {
161
165
  'sync_local',
162
166
  arg
163
167
  ]);
164
- return result == 1;
168
+ if (result == 1) {
169
+ if (priority == null) {
170
+ const bucketToCount = Object.fromEntries(checkpoint.buckets.map((b) => [b.bucket, b.count]));
171
+ // The two parameters could be replaced with one, but: https://github.com/powersync-ja/better-sqlite3/pull/6
172
+ const jsonBucketCount = JSON.stringify(bucketToCount);
173
+ await tx.execute("UPDATE ps_buckets SET count_since_last = 0, count_at_last = ?->name WHERE name != '$local' AND ?->name IS NOT NULL", [jsonBucketCount, jsonBucketCount]);
174
+ }
175
+ return true;
176
+ }
177
+ else {
178
+ return false;
179
+ }
165
180
  });
166
181
  }
167
182
  async validateChecksums(checkpoint, priority) {
@@ -54,6 +54,13 @@ export type AbstractRemoteOptions = {
54
54
  * Binding should be done before passing here.
55
55
  */
56
56
  fetchImplementation: FetchImplementation | FetchImplementationProvider;
57
+ /**
58
+ * Optional options to pass directly to all `fetch` calls.
59
+ *
60
+ * This can include fields such as `dispatcher` (e.g. for proxy support),
61
+ * `cache`, or any other fetch-compatible options.
62
+ */
63
+ fetchOptions?: {};
57
64
  };
58
65
  export declare const DEFAULT_REMOTE_OPTIONS: AbstractRemoteOptions;
59
66
  export declare abstract class AbstractRemote {
@@ -84,6 +91,7 @@ export declare abstract class AbstractRemote {
84
91
  * Provides a BSON implementation. The import nature of this varies depending on the platform
85
92
  */
86
93
  abstract getBSON(): Promise<BSONImplementation>;
94
+ protected createSocket(url: string): WebSocket;
87
95
  /**
88
96
  * Connects to the sync/stream websocket endpoint
89
97
  */
@@ -44,7 +44,8 @@ export const DEFAULT_REMOTE_OPTIONS = {
44
44
  socketUrlTransformer: (url) => url.replace(/^https?:\/\//, function (match) {
45
45
  return match === 'https://' ? 'wss://' : 'ws://';
46
46
  }),
47
- fetchImplementation: new FetchImplementationProvider()
47
+ fetchImplementation: new FetchImplementationProvider(),
48
+ fetchOptions: {}
48
49
  };
49
50
  export class AbstractRemote {
50
51
  connector;
@@ -153,6 +154,9 @@ export class AbstractRemote {
153
154
  }
154
155
  return res;
155
156
  }
157
+ createSocket(url) {
158
+ return new WebSocket(url);
159
+ }
156
160
  /**
157
161
  * Connects to the sync/stream websocket endpoint
158
162
  */
@@ -167,7 +171,8 @@ export class AbstractRemote {
167
171
  const userAgent = this.getUserAgent();
168
172
  const connector = new RSocketConnector({
169
173
  transport: new WebsocketClientTransport({
170
- url: this.options.socketUrlTransformer(request.url)
174
+ url: this.options.socketUrlTransformer(request.url),
175
+ wsCreator: (url) => this.createSocket(url)
171
176
  }),
172
177
  setup: {
173
178
  keepAlive: KEEP_ALIVE_MS,
@@ -318,6 +323,7 @@ export class AbstractRemote {
318
323
  body: JSON.stringify(data),
319
324
  signal: controller.signal,
320
325
  cache: 'no-store',
326
+ ...(this.options.fetchOptions ?? {}),
321
327
  ...options.fetchOptions
322
328
  }).catch((ex) => {
323
329
  if (ex.name == 'AbortError') {
@@ -139,6 +139,7 @@ export declare abstract class AbstractStreamingSyncImplementation extends BaseOb
139
139
  streamingSync(signal?: AbortSignal, options?: PowerSyncConnectionOptions): Promise<void>;
140
140
  private collectLocalBucketState;
141
141
  protected streamingSyncIteration(signal: AbortSignal, options?: PowerSyncConnectionOptions): Promise<void>;
142
+ private updateSyncStatusForStartingCheckpoint;
142
143
  private applyCheckpoint;
143
144
  protected updateSyncStatus(options: SyncStatusOptions): void;
144
145
  private delayRetry;
@@ -270,7 +270,8 @@ The next upload iteration will be delayed.`);
270
270
  connected: false,
271
271
  connecting: false,
272
272
  dataFlow: {
273
- downloading: false
273
+ downloading: false,
274
+ downloadProgress: null
274
275
  }
275
276
  });
276
277
  });
@@ -409,6 +410,7 @@ The next upload iteration will be delayed.`);
409
410
  bucketMap = newBuckets;
410
411
  await this.options.adapter.removeBuckets([...bucketsToDelete]);
411
412
  await this.options.adapter.setTargetCheckpoint(targetCheckpoint);
413
+ await this.updateSyncStatusForStartingCheckpoint(targetCheckpoint);
412
414
  }
413
415
  else if (isStreamingSyncCheckpointComplete(line)) {
414
416
  const result = await this.applyCheckpoint(targetCheckpoint, signal);
@@ -472,6 +474,7 @@ The next upload iteration will be delayed.`);
472
474
  write_checkpoint: diff.write_checkpoint
473
475
  };
474
476
  targetCheckpoint = newCheckpoint;
477
+ await this.updateSyncStatusForStartingCheckpoint(targetCheckpoint);
475
478
  bucketMap = new Map();
476
479
  newBuckets.forEach((checksum, name) => bucketMap.set(name, {
477
480
  name: checksum.bucket,
@@ -486,9 +489,22 @@ The next upload iteration will be delayed.`);
486
489
  }
487
490
  else if (isStreamingSyncData(line)) {
488
491
  const { data } = line;
492
+ const previousProgress = this.syncStatus.dataFlowStatus.downloadProgress;
493
+ let updatedProgress = null;
494
+ if (previousProgress) {
495
+ updatedProgress = { ...previousProgress };
496
+ const progressForBucket = updatedProgress[data.bucket];
497
+ if (progressForBucket) {
498
+ updatedProgress[data.bucket] = {
499
+ ...progressForBucket,
500
+ sinceLast: progressForBucket.sinceLast + data.data.length
501
+ };
502
+ }
503
+ }
489
504
  this.updateSyncStatus({
490
505
  dataFlow: {
491
- downloading: true
506
+ downloading: true,
507
+ downloadProgress: updatedProgress
492
508
  }
493
509
  });
494
510
  await this.options.adapter.saveSyncData({ buckets: [SyncDataBucket.fromRow(data)] });
@@ -536,6 +552,27 @@ The next upload iteration will be delayed.`);
536
552
  }
537
553
  });
538
554
  }
555
+ async updateSyncStatusForStartingCheckpoint(checkpoint) {
556
+ const localProgress = await this.options.adapter.getBucketOperationProgress();
557
+ const progress = {};
558
+ for (const bucket of checkpoint.buckets) {
559
+ const savedProgress = localProgress[bucket.bucket];
560
+ progress[bucket.bucket] = {
561
+ // The fallback priority doesn't matter here, but 3 is the one newer versions of the sync service
562
+ // will use by default.
563
+ priority: bucket.priority ?? 3,
564
+ atLast: savedProgress?.atLast ?? 0,
565
+ sinceLast: savedProgress?.sinceLast ?? 0,
566
+ targetCount: bucket.count ?? 0
567
+ };
568
+ }
569
+ this.updateSyncStatus({
570
+ dataFlow: {
571
+ downloading: true,
572
+ downloadProgress: progress
573
+ }
574
+ });
575
+ }
539
576
  async applyCheckpoint(checkpoint, abort) {
540
577
  let result = await this.options.adapter.syncLocalDatabase(checkpoint);
541
578
  const pending = this.pendingCrudUpload;
@@ -565,6 +602,7 @@ The next upload iteration will be delayed.`);
565
602
  lastSyncedAt: new Date(),
566
603
  dataFlow: {
567
604
  downloading: false,
605
+ downloadProgress: null,
568
606
  downloadError: undefined
569
607
  }
570
608
  });
@@ -79,7 +79,7 @@ export interface StreamingSyncCheckpointDiff {
79
79
  last_op_id: OpId;
80
80
  updated_buckets: BucketChecksum[];
81
81
  removed_buckets: string[];
82
- write_checkpoint: string;
82
+ write_checkpoint?: string;
83
83
  };
84
84
  }
85
85
  export interface StreamingSyncDataJSON {
@@ -0,0 +1,72 @@
1
+ /** @internal */
2
+ export type InternalProgressInformation = Record<string, {
3
+ priority: number;
4
+ atLast: number;
5
+ sinceLast: number;
6
+ targetCount: number;
7
+ }>;
8
+ /**
9
+ * @internal The priority used by the core extension to indicate that a full sync was completed.
10
+ */
11
+ export declare const FULL_SYNC_PRIORITY = 2147483647;
12
+ /**
13
+ * Information about a progressing download made by the PowerSync SDK.
14
+ *
15
+ * To obtain these values, use {@link SyncProgress}, available through
16
+ * {@link SyncStatus#downloadProgress}.
17
+ */
18
+ export interface ProgressWithOperations {
19
+ /**
20
+ * The total amount of operations to download for the current sync iteration
21
+ * to complete.
22
+ */
23
+ totalOperations: number;
24
+ /**
25
+ * The amount of operations that have already been downloaded.
26
+ */
27
+ downloadedOperations: number;
28
+ /**
29
+ * Relative progress, as {@link downloadedOperations} of {@link totalOperations}.
30
+ *
31
+ * This will be a number between `0.0` and `1.0` (inclusive).
32
+ *
33
+ * When this number reaches `1.0`, all changes have been received from the sync service.
34
+ * Actually applying these changes happens before the `downloadProgress` field is cleared from
35
+ * {@link SyncStatus}, so progress can stay at `1.0` for a short while before completing.
36
+ */
37
+ downloadedFraction: number;
38
+ }
39
+ /**
40
+ * Provides realtime progress on how PowerSync is downloading rows.
41
+ *
42
+ * The progress until the next complete sync is available through the fields on {@link ProgressWithOperations},
43
+ * which this class implements.
44
+ * Additionally, the {@link SyncProgress.untilPriority} method can be used to otbain progress towards
45
+ * a specific priority (instead of the progress for the entire download).
46
+ *
47
+ * The reported progress always reflects the status towards th end of a sync iteration (after
48
+ * which a consistent snapshot of all buckets is available locally).
49
+ *
50
+ * In rare cases (in particular, when a [compacting](https://docs.powersync.com/usage/lifecycle-maintenance/compacting-buckets)
51
+ * operation takes place between syncs), it's possible for the returned numbers to be slightly
52
+ * inaccurate. For this reason, {@link SyncProgress} should be seen as an approximation of progress.
53
+ * The information returned is good enough to build progress bars, but not exact enough to track
54
+ * individual download counts.
55
+ *
56
+ * Also note that data is downloaded in bulk, which means that individual counters are unlikely
57
+ * to be updated one-by-one.
58
+ */
59
+ export declare class SyncProgress implements ProgressWithOperations {
60
+ protected internal: InternalProgressInformation;
61
+ totalOperations: number;
62
+ downloadedOperations: number;
63
+ downloadedFraction: number;
64
+ constructor(internal: InternalProgressInformation);
65
+ /**
66
+ * Returns download progress towards all data up until the specified priority being received.
67
+ *
68
+ * The returned {@link ProgressWithOperations} tracks the target amount of operations that need
69
+ * to be downloaded in total and how many of them have already been received.
70
+ */
71
+ untilPriority(priority: number): ProgressWithOperations;
72
+ }
@@ -0,0 +1,60 @@
1
+ /**
2
+ * @internal The priority used by the core extension to indicate that a full sync was completed.
3
+ */
4
+ export const FULL_SYNC_PRIORITY = 2147483647;
5
+ /**
6
+ * Provides realtime progress on how PowerSync is downloading rows.
7
+ *
8
+ * The progress until the next complete sync is available through the fields on {@link ProgressWithOperations},
9
+ * which this class implements.
10
+ * Additionally, the {@link SyncProgress.untilPriority} method can be used to otbain progress towards
11
+ * a specific priority (instead of the progress for the entire download).
12
+ *
13
+ * The reported progress always reflects the status towards th end of a sync iteration (after
14
+ * which a consistent snapshot of all buckets is available locally).
15
+ *
16
+ * In rare cases (in particular, when a [compacting](https://docs.powersync.com/usage/lifecycle-maintenance/compacting-buckets)
17
+ * operation takes place between syncs), it's possible for the returned numbers to be slightly
18
+ * inaccurate. For this reason, {@link SyncProgress} should be seen as an approximation of progress.
19
+ * The information returned is good enough to build progress bars, but not exact enough to track
20
+ * individual download counts.
21
+ *
22
+ * Also note that data is downloaded in bulk, which means that individual counters are unlikely
23
+ * to be updated one-by-one.
24
+ */
25
+ export class SyncProgress {
26
+ internal;
27
+ totalOperations;
28
+ downloadedOperations;
29
+ downloadedFraction;
30
+ constructor(internal) {
31
+ this.internal = internal;
32
+ const untilCompletion = this.untilPriority(FULL_SYNC_PRIORITY);
33
+ this.totalOperations = untilCompletion.totalOperations;
34
+ this.downloadedOperations = untilCompletion.downloadedOperations;
35
+ this.downloadedFraction = untilCompletion.downloadedFraction;
36
+ }
37
+ /**
38
+ * Returns download progress towards all data up until the specified priority being received.
39
+ *
40
+ * The returned {@link ProgressWithOperations} tracks the target amount of operations that need
41
+ * to be downloaded in total and how many of them have already been received.
42
+ */
43
+ untilPriority(priority) {
44
+ let total = 0;
45
+ let downloaded = 0;
46
+ for (const progress of Object.values(this.internal)) {
47
+ // Include higher-priority buckets, which are represented by lower numbers.
48
+ if (progress.priority <= priority) {
49
+ downloaded += progress.sinceLast;
50
+ total += progress.targetCount - progress.atLast;
51
+ }
52
+ }
53
+ let progress = total == 0 ? 0.0 : downloaded / total;
54
+ return {
55
+ totalOperations: total,
56
+ downloadedOperations: downloaded,
57
+ downloadedFraction: progress
58
+ };
59
+ }
60
+ }
@@ -1,3 +1,4 @@
1
+ import { InternalProgressInformation, SyncProgress } from './SyncProgress.js';
1
2
  export type SyncDataFlowStatus = Partial<{
2
3
  downloading: boolean;
3
4
  uploading: boolean;
@@ -12,6 +13,12 @@ export type SyncDataFlowStatus = Partial<{
12
13
  * Cleared on the next successful upload.
13
14
  */
14
15
  uploadError?: Error;
16
+ /**
17
+ * Internal information about how far we are downloading operations in buckets.
18
+ *
19
+ * Please use the {@link SyncStatus#downloadProgress} property to track sync progress.
20
+ */
21
+ downloadProgress: InternalProgressInformation | null;
15
22
  }>;
16
23
  export interface SyncPriorityStatus {
17
24
  priority: number;
@@ -77,6 +84,12 @@ export declare class SyncStatus {
77
84
  * Cleared on the next successful upload.
78
85
  */
79
86
  uploadError?: Error;
87
+ /**
88
+ * Internal information about how far we are downloading operations in buckets.
89
+ *
90
+ * Please use the {@link SyncStatus#downloadProgress} property to track sync progress.
91
+ */
92
+ downloadProgress: InternalProgressInformation | null;
80
93
  }>;
81
94
  /**
82
95
  * Provides sync status information for all bucket priorities, sorted by priority (highest first).
@@ -85,6 +98,13 @@ export declare class SyncStatus {
85
98
  * sorted with highest priorities (lower numbers) first.
86
99
  */
87
100
  get priorityStatusEntries(): SyncPriorityStatus[];
101
+ /**
102
+ * A realtime progress report on how many operations have been downloaded and
103
+ * how many are necessary in total to complete the next sync iteration.
104
+ *
105
+ * This field is only set when {@link SyncDataFlowStatus#downloading} is also true.
106
+ */
107
+ get downloadProgress(): SyncProgress | null;
88
108
  /**
89
109
  * Reports the sync status (a pair of {@link SyncStatus#hasSynced} and {@link SyncStatus#lastSyncedAt} fields)
90
110
  * for a specific bucket priority level.
@@ -1,3 +1,4 @@
1
+ import { SyncProgress } from './SyncProgress.js';
1
2
  export class SyncStatus {
2
3
  options;
3
4
  constructor(options) {
@@ -67,6 +68,19 @@ export class SyncStatus {
67
68
  get priorityStatusEntries() {
68
69
  return (this.options.priorityStatusEntries ?? []).slice().sort(SyncStatus.comparePriorities);
69
70
  }
71
+ /**
72
+ * A realtime progress report on how many operations have been downloaded and
73
+ * how many are necessary in total to complete the next sync iteration.
74
+ *
75
+ * This field is only set when {@link SyncDataFlowStatus#downloading} is also true.
76
+ */
77
+ get downloadProgress() {
78
+ const internalProgress = this.options.dataFlow?.downloadProgress;
79
+ if (internalProgress == null) {
80
+ return null;
81
+ }
82
+ return new SyncProgress(internalProgress);
83
+ }
70
84
  /**
71
85
  * Reports the sync status (a pair of {@link SyncStatus#hasSynced} and {@link SyncStatus#lastSyncedAt} fields)
72
86
  * for a specific bucket priority level.
package/lib/index.d.ts CHANGED
@@ -18,6 +18,7 @@ export * from './client/sync/stream/AbstractRemote.js';
18
18
  export * from './client/sync/stream/AbstractStreamingSyncImplementation.js';
19
19
  export * from './client/sync/stream/streaming-sync-types.js';
20
20
  export { MAX_OP_ID } from './client/constants.js';
21
+ export { ProgressWithOperations, SyncProgress } from './db/crud/SyncProgress.js';
21
22
  export * from './db/crud/SyncStatus.js';
22
23
  export * from './db/crud/UploadQueueStatus.js';
23
24
  export * from './db/schema/Schema.js';
@@ -32,5 +33,6 @@ export * from './db/DBAdapter.js';
32
33
  export * from './utils/AbortOperation.js';
33
34
  export * from './utils/BaseObserver.js';
34
35
  export * from './utils/DataStream.js';
36
+ export * from './utils/Logger.js';
35
37
  export * from './utils/parseQuery.js';
36
38
  export * from './types/types.js';
package/lib/index.js CHANGED
@@ -18,6 +18,7 @@ export * from './client/sync/stream/AbstractRemote.js';
18
18
  export * from './client/sync/stream/AbstractStreamingSyncImplementation.js';
19
19
  export * from './client/sync/stream/streaming-sync-types.js';
20
20
  export { MAX_OP_ID } from './client/constants.js';
21
+ export { SyncProgress } from './db/crud/SyncProgress.js';
21
22
  export * from './db/crud/SyncStatus.js';
22
23
  export * from './db/crud/UploadQueueStatus.js';
23
24
  export * from './db/schema/Schema.js';
@@ -32,5 +33,6 @@ export * from './db/DBAdapter.js';
32
33
  export * from './utils/AbortOperation.js';
33
34
  export * from './utils/BaseObserver.js';
34
35
  export * from './utils/DataStream.js';
36
+ export * from './utils/Logger.js';
35
37
  export * from './utils/parseQuery.js';
36
38
  export * from './types/types.js';
@@ -0,0 +1,31 @@
1
+ import Logger, { type ILogger, type ILogLevel } from 'js-logger';
2
+ export { GlobalLogger, ILogger, ILoggerOpts, ILogHandler, ILogLevel } from 'js-logger';
3
+ export declare const LogLevel: {
4
+ TRACE: Logger.ILogLevel;
5
+ DEBUG: Logger.ILogLevel;
6
+ INFO: Logger.ILogLevel;
7
+ TIME: Logger.ILogLevel;
8
+ WARN: Logger.ILogLevel;
9
+ ERROR: Logger.ILogLevel;
10
+ OFF: Logger.ILogLevel;
11
+ };
12
+ export interface CreateLoggerOptions {
13
+ logLevel?: ILogLevel;
14
+ }
15
+ /**
16
+ * Retrieves the base (default) logger instance.
17
+ *
18
+ * This base logger controls the default logging configuration and is shared
19
+ * across all loggers created with `createLogger`. Adjusting settings on this
20
+ * base logger affects all loggers derived from it unless explicitly overridden.
21
+ *
22
+ */
23
+ export declare function createBaseLogger(): typeof Logger;
24
+ /**
25
+ * Creates and configures a new named logger based on the base logger.
26
+ *
27
+ * Named loggers allow specific modules or areas of your application to have
28
+ * their own logging levels and behaviors. These loggers inherit configuration
29
+ * from the base logger by default but can override settings independently.
30
+ */
31
+ export declare function createLogger(name: string, options?: CreateLoggerOptions): ILogger;
@@ -0,0 +1,36 @@
1
+ import Logger from 'js-logger';
2
+ const TypedLogger = Logger;
3
+ export const LogLevel = {
4
+ TRACE: TypedLogger.TRACE,
5
+ DEBUG: TypedLogger.DEBUG,
6
+ INFO: TypedLogger.INFO,
7
+ TIME: TypedLogger.TIME,
8
+ WARN: TypedLogger.WARN,
9
+ ERROR: TypedLogger.ERROR,
10
+ OFF: TypedLogger.OFF
11
+ };
12
+ /**
13
+ * Retrieves the base (default) logger instance.
14
+ *
15
+ * This base logger controls the default logging configuration and is shared
16
+ * across all loggers created with `createLogger`. Adjusting settings on this
17
+ * base logger affects all loggers derived from it unless explicitly overridden.
18
+ *
19
+ */
20
+ export function createBaseLogger() {
21
+ return Logger;
22
+ }
23
+ /**
24
+ * Creates and configures a new named logger based on the base logger.
25
+ *
26
+ * Named loggers allow specific modules or areas of your application to have
27
+ * their own logging levels and behaviors. These loggers inherit configuration
28
+ * from the base logger by default but can override settings independently.
29
+ */
30
+ export function createLogger(name, options = {}) {
31
+ const logger = Logger.get(name);
32
+ if (options.logLevel) {
33
+ logger.setLevel(options.logLevel);
34
+ }
35
+ return logger;
36
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@powersync/common",
3
- "version": "1.27.1",
3
+ "version": "1.29.0",
4
4
  "publishConfig": {
5
5
  "registry": "https://registry.npmjs.org/",
6
6
  "access": "public"