@powersync/service-core 1.20.3 → 1.20.5

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 (165) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/dist/api/RouteAPI.d.ts +14 -0
  3. package/dist/api/api-index.d.ts +1 -1
  4. package/dist/api/api-index.js +1 -1
  5. package/dist/api/api-index.js.map +1 -1
  6. package/dist/api/api-metrics.js.map +1 -1
  7. package/dist/api/diagnostics.js +16 -14
  8. package/dist/api/diagnostics.js.map +1 -1
  9. package/dist/auth/CachedKeyCollector.js +1 -1
  10. package/dist/auth/CachedKeyCollector.js.map +1 -1
  11. package/dist/auth/CompoundKeyCollector.js.map +1 -1
  12. package/dist/auth/KeyStore.js.map +1 -1
  13. package/dist/auth/RemoteJWKSCollector.js.map +1 -1
  14. package/dist/auth/StaticKeyCollector.d.ts +1 -1
  15. package/dist/auth/StaticKeyCollector.js.map +1 -1
  16. package/dist/auth/StaticSupabaseKeyCollector.d.ts +1 -1
  17. package/dist/auth/StaticSupabaseKeyCollector.js.map +1 -1
  18. package/dist/entry/commands/teardown-action.js +1 -1
  19. package/dist/entry/commands/teardown-action.js.map +1 -1
  20. package/dist/entry/entry-index.d.ts +1 -1
  21. package/dist/entry/entry-index.js +1 -1
  22. package/dist/entry/entry-index.js.map +1 -1
  23. package/dist/events/EventsEngine.js +1 -1
  24. package/dist/events/EventsEngine.js.map +1 -1
  25. package/dist/metrics/MetricsEngine.d.ts +1 -1
  26. package/dist/metrics/RollingBucketMax.d.ts +28 -0
  27. package/dist/metrics/RollingBucketMax.js +80 -0
  28. package/dist/metrics/RollingBucketMax.js.map +1 -0
  29. package/dist/metrics/metrics-index.d.ts +3 -2
  30. package/dist/metrics/metrics-index.js +3 -2
  31. package/dist/metrics/metrics-index.js.map +1 -1
  32. package/dist/metrics/open-telemetry/util.js +1 -1
  33. package/dist/metrics/open-telemetry/util.js.map +1 -1
  34. package/dist/metrics/register-metrics.js +2 -2
  35. package/dist/metrics/register-metrics.js.map +1 -1
  36. package/dist/modules/AbstractModule.d.ts +1 -1
  37. package/dist/modules/AbstractModule.js.map +1 -1
  38. package/dist/modules/modules-index.d.ts +1 -1
  39. package/dist/modules/modules-index.js +1 -1
  40. package/dist/modules/modules-index.js.map +1 -1
  41. package/dist/replication/AbstractReplicationJob.d.ts +2 -2
  42. package/dist/replication/AbstractReplicator.d.ts +1 -1
  43. package/dist/replication/AbstractReplicator.js +10 -7
  44. package/dist/replication/AbstractReplicator.js.map +1 -1
  45. package/dist/replication/ReplicationLagTracker.d.ts +50 -0
  46. package/dist/replication/ReplicationLagTracker.js +78 -0
  47. package/dist/replication/ReplicationLagTracker.js.map +1 -0
  48. package/dist/replication/replication-index.d.ts +3 -2
  49. package/dist/replication/replication-index.js +3 -2
  50. package/dist/replication/replication-index.js.map +1 -1
  51. package/dist/replication/replication-metrics.js.map +1 -1
  52. package/dist/routes/configure-fastify.d.ts +59 -32
  53. package/dist/routes/endpoints/admin.d.ts +108 -54
  54. package/dist/routes/endpoints/sync-rules.js +3 -3
  55. package/dist/routes/endpoints/sync-rules.js.map +1 -1
  56. package/dist/routes/endpoints/sync-stream.d.ts +10 -10
  57. package/dist/routes/hooks.js +1 -1
  58. package/dist/routes/hooks.js.map +1 -1
  59. package/dist/routes/route-register.js.map +1 -1
  60. package/dist/storage/BucketStorageBatch.d.ts +1 -1
  61. package/dist/storage/BucketStorageFactory.d.ts +5 -3
  62. package/dist/storage/BucketStorageFactory.js +10 -8
  63. package/dist/storage/BucketStorageFactory.js.map +1 -1
  64. package/dist/storage/ChecksumCache.js.map +1 -1
  65. package/dist/storage/PersistedSyncRulesContent.js +14 -2
  66. package/dist/storage/PersistedSyncRulesContent.js.map +1 -1
  67. package/dist/storage/ReplicationEventPayload.d.ts +1 -1
  68. package/dist/storage/SourceTable.d.ts +1 -1
  69. package/dist/storage/SourceTable.js.map +1 -1
  70. package/dist/storage/storage-index.d.ts +8 -8
  71. package/dist/storage/storage-index.js +8 -8
  72. package/dist/storage/storage-index.js.map +1 -1
  73. package/dist/storage/storage-metrics.js.map +1 -1
  74. package/dist/streams/streams-index.d.ts +2 -2
  75. package/dist/streams/streams-index.js +2 -2
  76. package/dist/streams/streams-index.js.map +1 -1
  77. package/dist/sync/BucketChecksumState.js +4 -19
  78. package/dist/sync/BucketChecksumState.js.map +1 -1
  79. package/dist/sync/RequestTracker.js +1 -1
  80. package/dist/sync/RequestTracker.js.map +1 -1
  81. package/dist/sync/sync-index.d.ts +2 -2
  82. package/dist/sync/sync-index.js +2 -2
  83. package/dist/sync/sync-index.js.map +1 -1
  84. package/dist/sync/sync.js.map +1 -1
  85. package/dist/sync/util.js.map +1 -1
  86. package/dist/system/ServiceContext.d.ts +1 -1
  87. package/dist/system/ServiceContext.js +1 -1
  88. package/dist/system/ServiceContext.js.map +1 -1
  89. package/dist/util/config/collectors/impl/base64-config-collector.d.ts +1 -1
  90. package/dist/util/config/collectors/impl/base64-config-collector.js.map +1 -1
  91. package/dist/util/config/collectors/impl/filesystem-config-collector.d.ts +1 -1
  92. package/dist/util/config/collectors/impl/filesystem-config-collector.js +1 -1
  93. package/dist/util/config/collectors/impl/filesystem-config-collector.js.map +1 -1
  94. package/dist/util/config/sync-rules/sync-rules-provider.js.map +1 -1
  95. package/dist/util/config.js +1 -1
  96. package/dist/util/config.js.map +1 -1
  97. package/dist/util/errors.d.ts +3 -0
  98. package/dist/util/errors.js +15 -0
  99. package/dist/util/errors.js.map +1 -0
  100. package/dist/util/protocol-types.d.ts +2 -2
  101. package/dist/util/util-index.d.ts +1 -1
  102. package/dist/util/util-index.js +1 -1
  103. package/dist/util/util-index.js.map +1 -1
  104. package/dist/util/utils.d.ts +1 -1
  105. package/package.json +6 -6
  106. package/src/api/RouteAPI.ts +17 -0
  107. package/src/api/api-index.ts +1 -1
  108. package/src/api/api-metrics.ts +1 -1
  109. package/src/api/diagnostics.ts +18 -19
  110. package/src/auth/CachedKeyCollector.ts +2 -3
  111. package/src/auth/CompoundKeyCollector.ts +2 -3
  112. package/src/auth/KeyStore.ts +1 -1
  113. package/src/auth/RemoteJWKSCollector.ts +0 -1
  114. package/src/auth/StaticKeyCollector.ts +1 -1
  115. package/src/auth/StaticSupabaseKeyCollector.ts +1 -1
  116. package/src/entry/commands/teardown-action.ts +1 -1
  117. package/src/entry/entry-index.ts +1 -1
  118. package/src/events/EventsEngine.ts +1 -1
  119. package/src/metrics/MetricsEngine.ts +1 -1
  120. package/src/metrics/RollingBucketMax.ts +109 -0
  121. package/src/metrics/metrics-index.ts +3 -2
  122. package/src/metrics/open-telemetry/util.ts +1 -1
  123. package/src/metrics/register-metrics.ts +3 -3
  124. package/src/modules/AbstractModule.ts +1 -1
  125. package/src/modules/modules-index.ts +1 -1
  126. package/src/replication/AbstractReplicationJob.ts +2 -2
  127. package/src/replication/AbstractReplicator.ts +9 -7
  128. package/src/replication/ReplicationLagTracker.ts +86 -0
  129. package/src/replication/replication-index.ts +3 -2
  130. package/src/replication/replication-metrics.ts +1 -1
  131. package/src/routes/endpoints/sync-rules.ts +3 -5
  132. package/src/routes/hooks.ts +2 -2
  133. package/src/routes/route-register.ts +2 -10
  134. package/src/storage/BucketStorageBatch.ts +1 -1
  135. package/src/storage/BucketStorageFactory.ts +18 -14
  136. package/src/storage/ChecksumCache.ts +1 -1
  137. package/src/storage/PersistedSyncRulesContent.ts +18 -2
  138. package/src/storage/ReplicationEventPayload.ts +1 -1
  139. package/src/storage/SourceTable.ts +1 -1
  140. package/src/storage/storage-index.ts +8 -8
  141. package/src/storage/storage-metrics.ts +2 -2
  142. package/src/streams/streams-index.ts +2 -2
  143. package/src/sync/BucketChecksumState.ts +5 -21
  144. package/src/sync/RequestTracker.ts +1 -1
  145. package/src/sync/sync-index.ts +2 -2
  146. package/src/sync/sync.ts +1 -7
  147. package/src/sync/util.ts +1 -1
  148. package/src/system/ServiceContext.ts +1 -1
  149. package/src/util/config/collectors/impl/base64-config-collector.ts +1 -1
  150. package/src/util/config/collectors/impl/filesystem-config-collector.ts +2 -2
  151. package/src/util/config/sync-rules/sync-rules-provider.ts +1 -1
  152. package/src/util/config.ts +1 -1
  153. package/src/util/errors.ts +21 -0
  154. package/src/util/util-index.ts +1 -1
  155. package/src/util/utils.ts +1 -1
  156. package/test/src/ReplicationLagTracker.test.ts +53 -0
  157. package/test/src/RollingBucketMax.test.ts +106 -0
  158. package/test/src/auth.test.ts +7 -7
  159. package/test/src/module-loader.test.ts +1 -1
  160. package/test/src/routes/mocks.ts +1 -1
  161. package/test/src/routes/stream.test.ts +1 -2
  162. package/test/src/sync/BucketChecksumState.test.ts +2 -2
  163. package/test/src/util/protocol_types.test.ts +1 -1
  164. package/tsconfig.tsbuildinfo +1 -1
  165. package/vitest.config.ts +1 -1
@@ -107,15 +107,17 @@ export abstract class AbstractReplicator<T extends AbstractReplicationJob = Abst
107
107
  }, 1000);
108
108
  });
109
109
  this.metrics.getObservableGauge(ReplicationMetric.REPLICATION_LAG_SECONDS).setValueProvider(async () => {
110
- const lag = await this.getReplicationLagMillis().catch((e) => {
110
+ try {
111
+ const lag = this.getReplicationLagMillis();
112
+ if (lag == null) {
113
+ return undefined;
114
+ }
115
+ // ms to seconds
116
+ return Math.round(lag / 1000);
117
+ } catch (e) {
111
118
  this.logger.error('Failed to get replication lag', e);
112
119
  return undefined;
113
- });
114
- if (lag == null) {
115
- return undefined;
116
120
  }
117
- // ms to seconds
118
- return Math.round(lag / 1000);
119
121
  });
120
122
  }
121
123
 
@@ -312,7 +314,7 @@ export abstract class AbstractReplicator<T extends AbstractReplicationJob = Abst
312
314
  *
313
315
  * "processing" replication streams are not taken into account for this metric.
314
316
  */
315
- async getReplicationLagMillis(): Promise<number | undefined> {
317
+ getReplicationLagMillis(): number | undefined {
316
318
  return this.activeReplicationJob?.getReplicationLagMillis();
317
319
  }
318
320
  }
@@ -0,0 +1,86 @@
1
+ import { RollingBucketMax } from '../metrics/RollingBucketMax.js';
2
+
3
+ /**
4
+ * Tracks replication lag across the current in-flight transaction and a rolling
5
+ * max of recently observed lag values.
6
+ */
7
+ export class ReplicationLagTracker {
8
+ private readonly rollingReplicationLag = new RollingBucketMax();
9
+ private _oldestUncommittedChange: Date | null = null;
10
+ private _isStartingReplication = true;
11
+
12
+ /**
13
+ * The oldest source timestamp still part of the current in-flight work.
14
+ */
15
+ get oldestUncommittedChange(): Date | null {
16
+ return this._oldestUncommittedChange;
17
+ }
18
+
19
+ /**
20
+ * True until replication has seen its first completed commit or equivalent keepalive.
21
+ */
22
+ get isStartingReplication(): boolean {
23
+ return this._isStartingReplication;
24
+ }
25
+
26
+ /**
27
+ * Registers the first source timestamp for the current in-flight work,
28
+ * for example the start of a transaction
29
+ */
30
+ trackUncommittedChange(timestamp: Date | null | undefined): void {
31
+ if (this._oldestUncommittedChange == null && timestamp != null) {
32
+ this._oldestUncommittedChange = timestamp;
33
+ }
34
+ }
35
+
36
+ /**
37
+ * Clears the current in-flight timestamp without changing startup state.
38
+ */
39
+ clearUncommittedChange(): void {
40
+ this._oldestUncommittedChange = null;
41
+ }
42
+
43
+ /**
44
+ * Marks replication as started even if no committed transaction lag was recorded.
45
+ */
46
+ markStarted(): void {
47
+ this._isStartingReplication = false;
48
+ }
49
+
50
+ /**
51
+ * Mark the current pending changes as "committed".
52
+ *
53
+ * Records the current in-flight lag into the rolling window and clears it.
54
+ * The current lag is calculated as the differnence between current time and the oldest change,
55
+ * as marked by trackUncommittedChange.
56
+ */
57
+ markCommitted(timestampMs = Date.now()): void {
58
+ if (this._oldestUncommittedChange != null) {
59
+ this.rollingReplicationLag.report(timestampMs - this._oldestUncommittedChange.getTime(), timestampMs);
60
+ }
61
+ this.clearUncommittedChange();
62
+ this.markStarted();
63
+ }
64
+
65
+ /**
66
+ * Returns the lag for the current in-flight work.
67
+ *
68
+ * 0 if idle (no pending changes to replicate).
69
+ *
70
+ * undefined when replication is still starting up.
71
+ */
72
+ getCurrentLagMillis(timestampMs = Date.now()): number | undefined {
73
+ if (this._oldestUncommittedChange == null) {
74
+ return this._isStartingReplication ? undefined : 0;
75
+ }
76
+ return timestampMs - this._oldestUncommittedChange.getTime();
77
+ }
78
+
79
+ /**
80
+ * Returns the rolling lag metric value, including the current in-flight lag when present.
81
+ */
82
+ getLagMillis(timestampMs = Date.now()): number | undefined {
83
+ this.rollingReplicationLag.report(this.getCurrentLagMillis(timestampMs), timestampMs);
84
+ return this.rollingReplicationLag.getRollingMax(timestampMs);
85
+ }
86
+ }
@@ -1,7 +1,8 @@
1
1
  export * from './AbstractReplicationJob.js';
2
2
  export * from './AbstractReplicator.js';
3
3
  export * from './ErrorRateLimiter.js';
4
+ export * from './RelationCache.js';
5
+ export * from './replication-metrics.js';
4
6
  export * from './ReplicationEngine.js';
7
+ export * from './ReplicationLagTracker.js';
5
8
  export * from './ReplicationModule.js';
6
- export * from './replication-metrics.js';
7
- export * from './RelationCache.js';
@@ -1,5 +1,5 @@
1
- import { MetricsEngine } from '../metrics/metrics-index.js';
2
1
  import { ReplicationMetric } from '@powersync/service-types';
2
+ import { MetricsEngine } from '../metrics/metrics-index.js';
3
3
 
4
4
  /**
5
5
  * Create and register the core replication metrics.
@@ -4,9 +4,9 @@ import type { FastifyPluginAsync } from 'fastify';
4
4
  import * as t from 'ts-codec';
5
5
 
6
6
  import { RouteAPI } from '../../api/RouteAPI.js';
7
+ import { updateSyncRulesFromConfig, updateSyncRulesFromYaml } from '../../storage/BucketStorageFactory.js';
7
8
  import { authApi } from '../auth.js';
8
9
  import { routeDefinition } from '../router.js';
9
- import { updateSyncRulesFromConfig, updateSyncRulesFromYaml } from '../../storage/BucketStorageFactory.js';
10
10
 
11
11
  const DeploySyncRulesRequest = t.object({
12
12
  content: t.string
@@ -56,7 +56,7 @@ export const deploySyncRules = routeDefinition({
56
56
 
57
57
  try {
58
58
  const apiHandler = service_context.routerEngine.getAPI();
59
- syncConfig = SqlSyncRules.fromYaml(payload.params.content, {
59
+ syncConfig = SqlSyncRules.fromYaml(content, {
60
60
  ...apiHandler.getParseSyncRulesOptions(),
61
61
  // We don't do any schema-level validation at this point
62
62
  schema: undefined
@@ -70,9 +70,7 @@ export const deploySyncRules = routeDefinition({
70
70
  });
71
71
  }
72
72
 
73
- const sync_rules = await storageEngine.activeBucketStorage.updateSyncRules(
74
- updateSyncRulesFromConfig(syncConfig.config)
75
- );
73
+ const sync_rules = await storageEngine.activeBucketStorage.updateSyncRules(updateSyncRulesFromConfig(syncConfig));
76
74
 
77
75
  return {
78
76
  slot_name: sync_rules.slot_name
@@ -1,6 +1,6 @@
1
- import type fastify from 'fastify';
2
- import a from 'async';
3
1
  import { logger } from '@powersync/lib-services-framework';
2
+ import a from 'async';
3
+ import type fastify from 'fastify';
4
4
 
5
5
  export type CreateRequestQueueParams = {
6
6
  max_queue_depth: number;
@@ -1,17 +1,9 @@
1
1
  import type fastify from 'fastify';
2
2
  import * as uuid from 'uuid';
3
3
 
4
- import {
5
- ErrorCode,
6
- errors,
7
- HTTPMethod,
8
- logger,
9
- RouteNotFound,
10
- router,
11
- ServiceError
12
- } from '@powersync/lib-services-framework';
13
- import { Context, ContextProvider, RequestEndpoint, RequestEndpointHandlerPayload } from './router.js';
4
+ import { errors, HTTPMethod, logger, RouteNotFound, router, ServiceError } from '@powersync/lib-services-framework';
14
5
  import { FastifyReply } from 'fastify';
6
+ import { Context, ContextProvider, RequestEndpoint, RequestEndpointHandlerPayload } from './router.js';
15
7
 
16
8
  export type FastifyEndpoint<I, O, C> = RequestEndpoint<I, O, C> & {
17
9
  parse?: boolean;
@@ -1,10 +1,10 @@
1
1
  import { ObserverClient } from '@powersync/lib-services-framework';
2
2
  import { EvaluatedParameters, EvaluatedRow, SqliteRow, ToastableSqliteRow } from '@powersync/service-sync-rules';
3
3
  import { BSON } from 'bson';
4
+ import { InternalOpId } from '../util/utils.js';
4
5
  import { ReplicationEventPayload } from './ReplicationEventPayload.js';
5
6
  import { SourceTable, TableSnapshotStatus } from './SourceTable.js';
6
7
  import { BatchedCustomWriteCheckpointOptions } from './storage-index.js';
7
- import { InternalOpId } from '../util/utils.js';
8
8
 
9
9
  export const DEFAULT_BUCKET_BATCH_COMMIT_OPTIONS: ResolvedBucketBatchCommitOptions = {
10
10
  createEmptyCheckpoints: true,
@@ -1,16 +1,18 @@
1
1
  import { BaseObserver, logger } from '@powersync/lib-services-framework';
2
- import { ParseSyncRulesOptions, PersistedSyncRules, PersistedSyncRulesContent } from './PersistedSyncRulesContent.js';
3
- import { ReplicationEventPayload } from './ReplicationEventPayload.js';
4
- import { ReplicationLock } from './ReplicationLock.js';
5
- import { SyncRulesBucketStorage } from './SyncRulesBucketStorage.js';
6
- import { ReportStorage } from './ReportStorage.js';
7
2
  import {
8
3
  PrecompiledSyncConfig,
9
4
  SerializedCompatibilityContext,
10
5
  serializeSyncPlan,
11
6
  SqlSyncRules,
12
- SyncConfig
7
+ SyncConfigWithErrors
13
8
  } from '@powersync/service-sync-rules';
9
+ import { ReplicationError } from '@powersync/service-types';
10
+ import { syncConfigYamlErrorToReplicationError } from '../util/errors.js';
11
+ import { ParseSyncRulesOptions, PersistedSyncRules, PersistedSyncRulesContent } from './PersistedSyncRulesContent.js';
12
+ import { ReplicationEventPayload } from './ReplicationEventPayload.js';
13
+ import { ReplicationLock } from './ReplicationLock.js';
14
+ import { ReportStorage } from './ReportStorage.js';
15
+ import { SyncRulesBucketStorage } from './SyncRulesBucketStorage.js';
14
16
 
15
17
  /**
16
18
  * Represents a configured storage provider.
@@ -178,13 +180,14 @@ export interface SerializedSyncPlan {
178
180
  * them.
179
181
  */
180
182
  eventDescriptors: Record<string, string[]>;
183
+ errors?: ReplicationError[];
181
184
  }
182
185
 
183
186
  export function updateSyncRulesFromYaml(
184
187
  content: string,
185
188
  options?: Omit<UpdateSyncRulesOptions, 'config'> & { validate?: boolean }
186
189
  ): UpdateSyncRulesOptions {
187
- const { config } = SqlSyncRules.fromYaml(content, {
190
+ const config = SqlSyncRules.fromYaml(content, {
188
191
  // No schema-based validation at this point
189
192
  schema: undefined,
190
193
  defaultSchema: 'not_applicable', // Not needed for validation
@@ -195,24 +198,25 @@ export function updateSyncRulesFromYaml(
195
198
  }
196
199
 
197
200
  export function updateSyncRulesFromConfig(
198
- parsed: SyncConfig,
201
+ { config, errors }: SyncConfigWithErrors,
199
202
  options?: Omit<UpdateSyncRulesOptions, 'config'>
200
203
  ): UpdateSyncRulesOptions {
201
204
  let plan: SerializedSyncPlan | null = null;
202
- if (parsed instanceof PrecompiledSyncConfig) {
205
+ if (config instanceof PrecompiledSyncConfig) {
203
206
  const eventDescriptors: Record<string, string[]> = {};
204
- for (const event of parsed.eventDescriptors) {
207
+ for (const event of config.eventDescriptors) {
205
208
  eventDescriptors[event.name] = event.sourceQueries.map((q) => q.sql);
206
209
  }
207
210
 
208
211
  plan = {
209
- compatibility: parsed.compatibility.serialize(),
210
- plan: serializeSyncPlan(parsed.plan),
211
- eventDescriptors
212
+ compatibility: config.compatibility.serialize(),
213
+ plan: serializeSyncPlan(config.plan),
214
+ eventDescriptors,
215
+ errors: errors.map((e) => syncConfigYamlErrorToReplicationError(e))
212
216
  };
213
217
  }
214
218
 
215
- return { config: { yaml: parsed.content, plan }, ...options };
219
+ return { config: { yaml: config.content, plan }, ...options };
216
220
  }
217
221
 
218
222
  export interface GetIntanceOptions {
@@ -1,6 +1,6 @@
1
1
  import { OrderedSet } from '@js-sdsl/ordered-set';
2
- import { LRUCache } from 'lru-cache/min';
3
2
  import { BucketDataSource } from '@powersync/service-sync-rules';
3
+ import { LRUCache } from 'lru-cache/min';
4
4
  import { BucketChecksum } from '../util/protocol-types.js';
5
5
  import { addBucketChecksums, ChecksumMap, InternalOpId, PartialChecksum } from '../util/utils.js';
6
6
  import { BucketChecksumRequest } from './SyncRulesBucketStorage.js';
@@ -4,6 +4,7 @@ import {
4
4
  CompatibilityOption,
5
5
  DEFAULT_HYDRATION_STATE,
6
6
  deserializeSyncPlan,
7
+ ErrorLocation,
7
8
  HydratedSyncRules,
8
9
  HydrationState,
9
10
  javaScriptExpressionEngine,
@@ -11,7 +12,8 @@ import {
11
12
  SqlEventDescriptor,
12
13
  SqlSyncRules,
13
14
  SyncConfigWithErrors,
14
- versionedHydrationState
15
+ versionedHydrationState,
16
+ YamlError
15
17
  } from '@powersync/service-sync-rules';
16
18
  import { SerializedSyncPlan, UpdateSyncRulesOptions } from './BucketStorageFactory.js';
17
19
  import { ReplicationLock } from './ReplicationLock.js';
@@ -101,7 +103,21 @@ export abstract class PersistedSyncRulesContent implements PersistedSyncRulesCon
101
103
  sourceText: this.sync_rules_content
102
104
  });
103
105
 
104
- config = { config: precompiled, errors: [] };
106
+ const errors: YamlError[] = [];
107
+ if (this.compiled_plan.errors) {
108
+ for (const error of this.compiled_plan.errors) {
109
+ const location: ErrorLocation | undefined = error.location && {
110
+ start: error.location.start_offset,
111
+ end: error.location.end_offset
112
+ };
113
+ const asYamlError = new YamlError(new Error(error.message), location);
114
+ asYamlError.type = error.level;
115
+
116
+ errors.push(asYamlError);
117
+ }
118
+ }
119
+
120
+ config = { config: precompiled, errors };
105
121
  } else {
106
122
  config = SqlSyncRules.fromYaml(this.sync_rules_content, options);
107
123
  }
@@ -1,6 +1,6 @@
1
1
  import * as sync_rules from '@powersync/service-sync-rules';
2
- import { SourceTable } from './SourceTable.js';
3
2
  import { BucketStorageBatch, SaveOp } from './BucketStorageBatch.js';
3
+ import { SourceTable } from './SourceTable.js';
4
4
 
5
5
  export type EventData = {
6
6
  op: SaveOp;
@@ -1,7 +1,7 @@
1
1
  import { DEFAULT_TAG } from '@powersync/service-sync-rules';
2
+ import { bson } from '../index.js';
2
3
  import * as util from '../util/util-index.js';
3
4
  import { ColumnDescriptor, SourceEntityDescriptor } from './SourceEntity.js';
4
- import { bson } from '../index.js';
5
5
 
6
6
  /**
7
7
  * Format of the id depends on the bucket storage module. It should be consistent within the module.
@@ -1,17 +1,17 @@
1
1
  export * from './bson.js';
2
2
  export * from './BucketStorage.js';
3
+ export * from './BucketStorageBatch.js';
4
+ export * from './BucketStorageFactory.js';
3
5
  export * from './ChecksumCache.js';
6
+ export * from './PersistedSyncRulesContent.js';
4
7
  export * from './ReplicationEventPayload.js';
8
+ export * from './ReplicationLock.js';
9
+ export * from './ReportStorage.js';
5
10
  export * from './SourceEntity.js';
6
11
  export * from './SourceTable.js';
12
+ export * from './storage-metrics.js';
7
13
  export * from './StorageEngine.js';
8
14
  export * from './StorageProvider.js';
9
- export * from './storage-metrics.js';
10
- export * from './WriteCheckpointAPI.js';
11
- export * from './BucketStorageFactory.js';
12
- export * from './BucketStorageBatch.js';
13
- export * from './SyncRulesBucketStorage.js';
14
- export * from './PersistedSyncRulesContent.js';
15
- export * from './ReplicationLock.js';
16
- export * from './ReportStorage.js';
17
15
  export * from './StorageVersionConfig.js';
16
+ export * from './SyncRulesBucketStorage.js';
17
+ export * from './WriteCheckpointAPI.js';
@@ -1,7 +1,7 @@
1
- import { MetricsEngine } from '../metrics/MetricsEngine.js';
2
1
  import { logger } from '@powersync/lib-services-framework';
3
- import { BucketStorageFactory, StorageMetrics } from './BucketStorageFactory.js';
4
2
  import { StorageMetric } from '@powersync/service-types';
3
+ import { MetricsEngine } from '../metrics/MetricsEngine.js';
4
+ import { BucketStorageFactory, StorageMetrics } from './BucketStorageFactory.js';
5
5
 
6
6
  export function createCoreStorageMetrics(engine: MetricsEngine): void {
7
7
  engine.createObservableGauge({
@@ -1,3 +1,3 @@
1
- export * from './merge.js';
2
- export * from './LastValueSink.js';
3
1
  export * from './BroadcastIterable.js';
2
+ export * from './LastValueSink.js';
3
+ export * from './merge.js';
@@ -1,14 +1,13 @@
1
1
  import {
2
- BucketDescription,
3
2
  BucketParameterQuerier,
4
3
  BucketPriority,
5
4
  BucketSource,
6
5
  HydratedSyncRules,
6
+ mergeBuckets,
7
7
  QuerierError,
8
8
  RequestedStream,
9
9
  RequestParameters,
10
- ResolvedBucket,
11
- mergeBuckets
10
+ ResolvedBucket
12
11
  } from '@powersync/service-sync-rules';
13
12
 
14
13
  import * as storage from '../storage/storage-index.js';
@@ -445,6 +444,7 @@ export class BucketParameterState {
445
444
  const subscription = explicitStreamSubscriptions[i];
446
445
 
447
446
  const syncRuleStream: RequestedStream = {
447
+ priorityOverride: subscription.override_priority as BucketPriority | null,
448
448
  parameters: subscription.parameters ?? {},
449
449
  opaque_id: i
450
450
  };
@@ -480,25 +480,9 @@ export class BucketParameterState {
480
480
  * reference default buckets by their stream index instead of duplicating the name on wire.
481
481
  */
482
482
  translateResolvedBucket(description: ResolvedBucket, lookupIndex: Map<string, number>): util.ClientBucketDescription {
483
- // If the client is overriding the priority of any stream that yields this bucket, sync the bucket with that
484
- // priority.
485
- let priorityOverride: BucketPriority | null = null;
486
- for (const reason of description.inclusion_reasons) {
487
- if (reason != 'default') {
488
- const requestedPriority = this.explicitStreamSubscriptions[reason.subscription]?.override_priority;
489
- if (requestedPriority != null) {
490
- if (priorityOverride == null) {
491
- priorityOverride = requestedPriority as BucketPriority;
492
- } else {
493
- priorityOverride = Math.min(requestedPriority, priorityOverride) as BucketPriority;
494
- }
495
- }
496
- }
497
- }
498
-
499
483
  return {
500
484
  bucket: description.bucket,
501
- priority: priorityOverride ?? description.priority,
485
+ priority: description.priority,
502
486
  subscriptions: description.inclusion_reasons.map((reason) => {
503
487
  if (reason == 'default') {
504
488
  const stream = description.definition;
@@ -533,7 +517,7 @@ export class BucketParameterState {
533
517
 
534
518
  let errorMessage = error.message;
535
519
  const logData: any = {
536
- checkpoint: checkpoint,
520
+ checkpoint: checkpoint.base.checkpoint,
537
521
  user_id: this.syncParams.userId,
538
522
  parameter_query_results: update.buckets.length
539
523
  };
@@ -1,8 +1,8 @@
1
1
  import { MetricsEngine } from '../metrics/MetricsEngine.js';
2
2
 
3
+ import { ServiceAssertionError } from '@powersync/lib-services-framework';
3
4
  import { APIMetric } from '@powersync/service-types';
4
5
  import { SyncBucketData } from '../util/protocol-types.js';
5
- import { ServiceAssertionError } from '@powersync/lib-services-framework';
6
6
 
7
7
  /**
8
8
  * Record sync stats per request stream.
@@ -1,6 +1,6 @@
1
+ export * from './BucketChecksumState.js';
1
2
  export * from './RequestTracker.js';
2
3
  export * from './safeRace.js';
3
4
  export * from './sync.js';
4
- export * from './util.js';
5
- export * from './BucketChecksumState.js';
6
5
  export * from './SyncContext.js';
6
+ export * from './util.js';
package/src/sync/sync.ts CHANGED
@@ -1,11 +1,5 @@
1
1
  import { JSONBig, JsonContainer } from '@powersync/service-jsonbig';
2
- import {
3
- BucketDescription,
4
- BucketPriority,
5
- HydratedSyncRules,
6
- ResolvedBucket,
7
- SqliteJsonValue
8
- } from '@powersync/service-sync-rules';
2
+ import { BucketPriority, HydratedSyncRules, ResolvedBucket, SqliteJsonValue } from '@powersync/service-sync-rules';
9
3
 
10
4
  import { AbortError } from 'ix/aborterror.js';
11
5
 
package/src/sync/util.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  import * as timers from 'timers/promises';
2
2
 
3
3
  import { SemaphoreInterface } from 'async-mutex';
4
+ import { serialize } from 'bson';
4
5
  import * as util from '../util/util-index.js';
5
6
  import { RequestTracker } from './RequestTracker.js';
6
- import { serialize } from 'bson';
7
7
 
8
8
  export type TokenStreamOptions = {
9
9
  /**
@@ -1,5 +1,6 @@
1
1
  import { container, LifeCycledSystem, MigrationManager, ServiceIdentifier } from '@powersync/lib-services-framework';
2
2
 
3
+ import { EventsEngine } from '../events/EventsEngine.js';
3
4
  import { framework } from '../index.js';
4
5
  import * as metrics from '../metrics/MetricsEngine.js';
5
6
  import { PowerSyncMigrationManager } from '../migrations/PowerSyncMigrationManager.js';
@@ -8,7 +9,6 @@ import * as routes from '../routes/routes-index.js';
8
9
  import * as storage from '../storage/storage-index.js';
9
10
  import { SyncContext } from '../sync/SyncContext.js';
10
11
  import * as utils from '../util/util-index.js';
11
- import { EventsEngine } from '../events/EventsEngine.js';
12
12
 
13
13
  export interface ServiceContext {
14
14
  configuration: utils.ResolvedPowerSyncConfig;
@@ -1,5 +1,5 @@
1
- import { ConfigCollector } from '../config-collector.js';
2
1
  import { RunnerConfig } from '../../types.js';
2
+ import { ConfigCollector } from '../config-collector.js';
3
3
 
4
4
  export class Base64ConfigCollector extends ConfigCollector {
5
5
  get name(): string {
@@ -1,9 +1,9 @@
1
1
  import * as fs from 'fs/promises';
2
2
  import * as path from 'path';
3
3
 
4
- import { ConfigCollector, ConfigFileFormat } from '../config-collector.js';
5
- import { RunnerConfig } from '../../types.js';
6
4
  import { logger } from '@powersync/lib-services-framework';
5
+ import { RunnerConfig } from '../../types.js';
6
+ import { ConfigCollector, ConfigFileFormat } from '../config-collector.js';
7
7
 
8
8
  export class FileSystemConfigCollector extends ConfigCollector {
9
9
  get name(): string {
@@ -1,5 +1,5 @@
1
- import { SyncRulesConfig } from '../types.js';
2
1
  import fs from 'fs/promises';
2
+ import { SyncRulesConfig } from '../types.js';
3
3
 
4
4
  export interface SyncRulesProvider {
5
5
  get(): Promise<string | undefined>;
@@ -1,7 +1,7 @@
1
1
  import * as fs from 'fs/promises';
2
2
  import winston from 'winston';
3
3
 
4
- import { container, logger, LogFormat, DEFAULT_LOG_LEVEL, DEFAULT_LOG_FORMAT } from '@powersync/lib-services-framework';
4
+ import { container, DEFAULT_LOG_FORMAT, DEFAULT_LOG_LEVEL, LogFormat, logger } from '@powersync/lib-services-framework';
5
5
  import { configFile } from '@powersync/service-types';
6
6
  import { ResolvedPowerSyncConfig, RunnerConfig } from './config/types.js';
7
7
  import { CompoundConfigCollector } from './util-index.js';
@@ -0,0 +1,21 @@
1
+ import { YamlError } from '@powersync/service-sync-rules';
2
+ import { ReplicationError } from '@powersync/service-types';
3
+
4
+ export function syncConfigYamlErrorToReplicationError(
5
+ { type, message, location }: YamlError,
6
+ ts?: string
7
+ ): ReplicationError {
8
+ const error: ReplicationError = {
9
+ level: type,
10
+ message,
11
+ ts
12
+ };
13
+ if (location != null) {
14
+ error.location = {
15
+ start_offset: location.start,
16
+ end_offset: location.end
17
+ };
18
+ }
19
+
20
+ return error;
21
+ }
@@ -1,4 +1,5 @@
1
1
  export * from './alerting.js';
2
+ export * from './checkpointing.js';
2
3
  export * from './env.js';
3
4
  export * from './lsn.js';
4
5
  export * from './memory-tracking.js';
@@ -6,7 +7,6 @@ export * from './Mutex.js';
6
7
  export * from './protocol-types.js';
7
8
  export * from './secs.js';
8
9
  export * from './utils.js';
9
- export * from './checkpointing.js';
10
10
  export * from './version.js';
11
11
 
12
12
  export * from './config.js';
package/src/util/utils.ts CHANGED
@@ -2,7 +2,7 @@ import * as sync_rules from '@powersync/service-sync-rules';
2
2
  import * as bson from 'bson';
3
3
  import crypto from 'crypto';
4
4
  import * as uuid from 'uuid';
5
- import { BucketChecksum, ProtocolOpId, OplogEntry } from './protocol-types.js';
5
+ import { BucketChecksum, OplogEntry, ProtocolOpId } from './protocol-types.js';
6
6
 
7
7
  import * as storage from '../storage/storage-index.js';
8
8