@powersync/service-core 1.20.4 → 1.21.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 (220) hide show
  1. package/CHANGELOG.md +53 -0
  2. package/dist/api/RouteAPI.d.ts +17 -3
  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.d.ts +1 -1
  8. package/dist/api/diagnostics.js +32 -14
  9. package/dist/api/diagnostics.js.map +1 -1
  10. package/dist/auth/CachedKeyCollector.js +1 -1
  11. package/dist/auth/CachedKeyCollector.js.map +1 -1
  12. package/dist/auth/CompoundKeyCollector.js.map +1 -1
  13. package/dist/auth/KeyStore.js.map +1 -1
  14. package/dist/auth/RemoteJWKSCollector.js.map +1 -1
  15. package/dist/auth/StaticKeyCollector.d.ts +1 -1
  16. package/dist/auth/StaticKeyCollector.js.map +1 -1
  17. package/dist/auth/StaticSupabaseKeyCollector.d.ts +1 -1
  18. package/dist/auth/StaticSupabaseKeyCollector.js.map +1 -1
  19. package/dist/entry/commands/teardown-action.js +2 -2
  20. package/dist/entry/commands/teardown-action.js.map +1 -1
  21. package/dist/entry/entry-index.d.ts +1 -1
  22. package/dist/entry/entry-index.js +1 -1
  23. package/dist/entry/entry-index.js.map +1 -1
  24. package/dist/events/EventsEngine.js +1 -1
  25. package/dist/events/EventsEngine.js.map +1 -1
  26. package/dist/index.d.ts +1 -0
  27. package/dist/index.js +1 -0
  28. package/dist/index.js.map +1 -1
  29. package/dist/metrics/MetricsEngine.d.ts +1 -1
  30. package/dist/metrics/metrics-index.d.ts +3 -3
  31. package/dist/metrics/metrics-index.js +3 -3
  32. package/dist/metrics/metrics-index.js.map +1 -1
  33. package/dist/metrics/open-telemetry/util.js +1 -1
  34. package/dist/metrics/open-telemetry/util.js.map +1 -1
  35. package/dist/metrics/register-metrics.js +2 -2
  36. package/dist/metrics/register-metrics.js.map +1 -1
  37. package/dist/modules/AbstractModule.d.ts +2 -2
  38. package/dist/modules/AbstractModule.js.map +1 -1
  39. package/dist/modules/modules-index.d.ts +1 -1
  40. package/dist/modules/modules-index.js +1 -1
  41. package/dist/modules/modules-index.js.map +1 -1
  42. package/dist/replication/AbstractReplicationJob.d.ts +1 -1
  43. package/dist/replication/AbstractReplicationJob.js +1 -1
  44. package/dist/replication/AbstractReplicationJob.js.map +1 -1
  45. package/dist/replication/AbstractReplicator.d.ts +6 -6
  46. package/dist/replication/AbstractReplicator.js +21 -21
  47. package/dist/replication/AbstractReplicator.js.map +1 -1
  48. package/dist/replication/replication-index.d.ts +3 -3
  49. package/dist/replication/replication-index.js +3 -3
  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/admin.js +7 -3
  55. package/dist/routes/endpoints/admin.js.map +1 -1
  56. package/dist/routes/endpoints/checkpointing.js +1 -1
  57. package/dist/routes/endpoints/checkpointing.js.map +1 -1
  58. package/dist/routes/endpoints/socket-route.js +1 -1
  59. package/dist/routes/endpoints/socket-route.js.map +1 -1
  60. package/dist/routes/endpoints/sync-rules.js +10 -10
  61. package/dist/routes/endpoints/sync-rules.js.map +1 -1
  62. package/dist/routes/endpoints/sync-stream.d.ts +10 -10
  63. package/dist/routes/endpoints/sync-stream.js +2 -2
  64. package/dist/routes/endpoints/sync-stream.js.map +1 -1
  65. package/dist/routes/hooks.js +1 -1
  66. package/dist/routes/hooks.js.map +1 -1
  67. package/dist/routes/route-register.js.map +1 -1
  68. package/dist/runner/teardown.js +4 -4
  69. package/dist/runner/teardown.js.map +1 -1
  70. package/dist/storage/BucketStorage.d.ts +9 -9
  71. package/dist/storage/BucketStorage.js +9 -9
  72. package/dist/storage/BucketStorageBatch.d.ts +1 -1
  73. package/dist/storage/BucketStorageFactory.d.ts +27 -20
  74. package/dist/storage/BucketStorageFactory.js +19 -16
  75. package/dist/storage/BucketStorageFactory.js.map +1 -1
  76. package/dist/storage/ChecksumCache.js.map +1 -1
  77. package/dist/storage/PersistedSyncRulesContent.d.ts +3 -1
  78. package/dist/storage/PersistedSyncRulesContent.js +24 -5
  79. package/dist/storage/PersistedSyncRulesContent.js.map +1 -1
  80. package/dist/storage/ReplicationEventPayload.d.ts +1 -1
  81. package/dist/storage/SourceTable.d.ts +4 -4
  82. package/dist/storage/SourceTable.js +3 -3
  83. package/dist/storage/SourceTable.js.map +1 -1
  84. package/dist/storage/StorageVersionConfig.d.ts +1 -1
  85. package/dist/storage/StorageVersionConfig.js +1 -1
  86. package/dist/storage/SyncRulesBucketStorage.d.ts +38 -6
  87. package/dist/storage/SyncRulesBucketStorage.js +14 -0
  88. package/dist/storage/SyncRulesBucketStorage.js.map +1 -1
  89. package/dist/storage/WriteCheckpointAPI.d.ts +6 -6
  90. package/dist/storage/WriteCheckpointAPI.js +1 -1
  91. package/dist/storage/bson.d.ts +0 -1
  92. package/dist/storage/bson.js +0 -4
  93. package/dist/storage/bson.js.map +1 -1
  94. package/dist/storage/storage-index.d.ts +8 -8
  95. package/dist/storage/storage-index.js +8 -8
  96. package/dist/storage/storage-index.js.map +1 -1
  97. package/dist/storage/storage-metrics.js.map +1 -1
  98. package/dist/streams/streams-index.d.ts +2 -2
  99. package/dist/streams/streams-index.js +2 -2
  100. package/dist/streams/streams-index.js.map +1 -1
  101. package/dist/sync/BucketChecksumState.d.ts +2 -5
  102. package/dist/sync/BucketChecksumState.js +119 -75
  103. package/dist/sync/BucketChecksumState.js.map +1 -1
  104. package/dist/sync/RequestTracker.js +1 -1
  105. package/dist/sync/RequestTracker.js.map +1 -1
  106. package/dist/sync/sync-index.d.ts +2 -2
  107. package/dist/sync/sync-index.js +2 -2
  108. package/dist/sync/sync-index.js.map +1 -1
  109. package/dist/sync/sync.js.map +1 -1
  110. package/dist/sync/util.js.map +1 -1
  111. package/dist/system/ServiceContext.d.ts +1 -1
  112. package/dist/system/ServiceContext.js +1 -1
  113. package/dist/system/ServiceContext.js.map +1 -1
  114. package/dist/tracing/PerformanceTracer.d.ts +44 -0
  115. package/dist/tracing/PerformanceTracer.js +102 -0
  116. package/dist/tracing/PerformanceTracer.js.map +1 -0
  117. package/dist/tracing/TraceWriter.d.ts +22 -0
  118. package/dist/tracing/TraceWriter.js +63 -0
  119. package/dist/tracing/TraceWriter.js.map +1 -0
  120. package/dist/util/checkpointing.js +1 -1
  121. package/dist/util/config/collectors/impl/base64-config-collector.d.ts +1 -1
  122. package/dist/util/config/collectors/impl/base64-config-collector.js.map +1 -1
  123. package/dist/util/config/collectors/impl/filesystem-config-collector.d.ts +1 -1
  124. package/dist/util/config/collectors/impl/filesystem-config-collector.js +1 -1
  125. package/dist/util/config/collectors/impl/filesystem-config-collector.js.map +1 -1
  126. package/dist/util/config/compound-config-collector.d.ts +1 -1
  127. package/dist/util/config/compound-config-collector.js +2 -2
  128. package/dist/util/config/compound-config-collector.js.map +1 -1
  129. package/dist/util/config/sync-rules/impl/filesystem-sync-rules-collector.js +1 -1
  130. package/dist/util/config/sync-rules/impl/filesystem-sync-rules-collector.js.map +1 -1
  131. package/dist/util/config/sync-rules/sync-rules-provider.js.map +1 -1
  132. package/dist/util/config.js +1 -1
  133. package/dist/util/config.js.map +1 -1
  134. package/dist/util/env.js +1 -1
  135. package/dist/util/errors.d.ts +3 -0
  136. package/dist/util/errors.js +15 -0
  137. package/dist/util/errors.js.map +1 -0
  138. package/dist/util/protocol-types.d.ts +3 -3
  139. package/dist/util/protocol-types.js +1 -1
  140. package/dist/util/util-index.d.ts +1 -1
  141. package/dist/util/util-index.js +1 -1
  142. package/dist/util/util-index.js.map +1 -1
  143. package/dist/util/utils.d.ts +1 -1
  144. package/package.json +11 -11
  145. package/src/api/RouteAPI.ts +20 -3
  146. package/src/api/api-index.ts +1 -1
  147. package/src/api/api-metrics.ts +1 -1
  148. package/src/api/diagnostics.ts +42 -20
  149. package/src/auth/CachedKeyCollector.ts +2 -3
  150. package/src/auth/CompoundKeyCollector.ts +2 -3
  151. package/src/auth/KeyStore.ts +1 -1
  152. package/src/auth/RemoteJWKSCollector.ts +0 -1
  153. package/src/auth/StaticKeyCollector.ts +1 -1
  154. package/src/auth/StaticSupabaseKeyCollector.ts +1 -1
  155. package/src/entry/commands/teardown-action.ts +2 -2
  156. package/src/entry/entry-index.ts +1 -1
  157. package/src/events/EventsEngine.ts +1 -1
  158. package/src/index.ts +2 -0
  159. package/src/metrics/MetricsEngine.ts +1 -1
  160. package/src/metrics/metrics-index.ts +3 -3
  161. package/src/metrics/open-telemetry/util.ts +1 -1
  162. package/src/metrics/register-metrics.ts +3 -3
  163. package/src/modules/AbstractModule.ts +2 -2
  164. package/src/modules/modules-index.ts +1 -1
  165. package/src/replication/AbstractReplicationJob.ts +2 -2
  166. package/src/replication/AbstractReplicator.ts +23 -23
  167. package/src/replication/replication-index.ts +3 -3
  168. package/src/replication/replication-metrics.ts +1 -1
  169. package/src/routes/endpoints/admin.ts +7 -3
  170. package/src/routes/endpoints/checkpointing.ts +1 -1
  171. package/src/routes/endpoints/socket-route.ts +1 -1
  172. package/src/routes/endpoints/sync-rules.ts +10 -12
  173. package/src/routes/endpoints/sync-stream.ts +2 -2
  174. package/src/routes/hooks.ts +2 -2
  175. package/src/routes/route-register.ts +2 -10
  176. package/src/runner/teardown.ts +4 -4
  177. package/src/storage/BucketStorage.ts +9 -9
  178. package/src/storage/BucketStorageBatch.ts +1 -1
  179. package/src/storage/BucketStorageFactory.ts +45 -34
  180. package/src/storage/ChecksumCache.ts +1 -1
  181. package/src/storage/PersistedSyncRulesContent.ts +30 -6
  182. package/src/storage/ReplicationEventPayload.ts +1 -1
  183. package/src/storage/SourceTable.ts +4 -4
  184. package/src/storage/StorageVersionConfig.ts +1 -1
  185. package/src/storage/SyncRulesBucketStorage.ts +46 -7
  186. package/src/storage/WriteCheckpointAPI.ts +6 -6
  187. package/src/storage/bson.ts +0 -5
  188. package/src/storage/storage-index.ts +8 -8
  189. package/src/storage/storage-metrics.ts +2 -2
  190. package/src/streams/streams-index.ts +2 -2
  191. package/src/sync/BucketChecksumState.ts +141 -93
  192. package/src/sync/RequestTracker.ts +1 -1
  193. package/src/sync/sync-index.ts +2 -2
  194. package/src/sync/sync.ts +2 -8
  195. package/src/sync/util.ts +1 -1
  196. package/src/system/ServiceContext.ts +1 -1
  197. package/src/tracing/PerformanceTracer.ts +126 -0
  198. package/src/tracing/TraceWriter.ts +67 -0
  199. package/src/util/checkpointing.ts +1 -1
  200. package/src/util/config/collectors/impl/base64-config-collector.ts +1 -1
  201. package/src/util/config/collectors/impl/filesystem-config-collector.ts +2 -2
  202. package/src/util/config/compound-config-collector.ts +3 -3
  203. package/src/util/config/sync-rules/impl/filesystem-sync-rules-collector.ts +1 -1
  204. package/src/util/config/sync-rules/sync-rules-provider.ts +1 -1
  205. package/src/util/config.ts +1 -1
  206. package/src/util/env.ts +1 -1
  207. package/src/util/errors.ts +21 -0
  208. package/src/util/protocol-types.ts +1 -1
  209. package/src/util/util-index.ts +1 -1
  210. package/src/util/utils.ts +1 -1
  211. package/test/src/auth.test.ts +115 -7
  212. package/test/src/diagnostics.test.ts +151 -0
  213. package/test/src/module-loader.test.ts +1 -1
  214. package/test/src/routes/mocks.ts +1 -1
  215. package/test/src/routes/stream.test.ts +1 -2
  216. package/test/src/sync/BucketChecksumState.test.ts +223 -67
  217. package/test/src/util/protocol_types.test.ts +1 -1
  218. package/test/tsconfig.json +0 -1
  219. package/tsconfig.tsbuildinfo +1 -1
  220. package/vitest.config.ts +1 -1
@@ -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
@@ -43,12 +43,12 @@ export const deploySyncRules = routeDefinition({
43
43
  const { storageEngine } = service_context;
44
44
 
45
45
  if (service_context.configuration.sync_rules.present) {
46
- // If sync rules are configured via the config, disable deploy via the API.
46
+ // If sync config is configured via the service config, disable deploy via the API.
47
47
  throw new errors.ServiceError({
48
48
  status: 422,
49
49
  code: ErrorCode.PSYNC_S4105,
50
- description: 'Sync rules API disabled',
51
- details: 'Use the management API to deploy sync rules'
50
+ description: 'Sync config API disabled',
51
+ details: 'Update sync config in the service configuration'
52
52
  });
53
53
  }
54
54
  const content = payload.params.content;
@@ -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
@@ -65,14 +65,12 @@ export const deploySyncRules = routeDefinition({
65
65
  throw new errors.ServiceError({
66
66
  status: 422,
67
67
  code: ErrorCode.PSYNC_R0001,
68
- description: 'Sync rules parsing failed',
68
+ description: 'Sync config parsing failed',
69
69
  details: e.message
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
@@ -117,7 +115,7 @@ export const currentSyncRules = routeDefinition({
117
115
  throw new errors.ServiceError({
118
116
  status: 422,
119
117
  code: ErrorCode.PSYNC_S4104,
120
- description: 'No active sync rules'
118
+ description: 'No active sync config'
121
119
  });
122
120
  }
123
121
 
@@ -164,13 +162,13 @@ export const reprocessSyncRules = routeDefinition({
164
162
  throw new errors.ServiceError({
165
163
  status: 422,
166
164
  code: ErrorCode.PSYNC_S4104,
167
- description: 'No active sync rules'
165
+ description: 'No active sync config'
168
166
  });
169
167
  }
170
168
 
171
169
  const new_rules = await activeBucketStorage.updateSyncRules(
172
170
  updateSyncRulesFromYaml(sync_rules.sync_rules.config.content, {
173
- // These sync rules already passed validation. But if the rules are not valid anymore due
171
+ // This sync config already passed validation. But if the rules are not valid anymore due
174
172
  // to a service change, we do want to report the error here.
175
173
  validate: true
176
174
  })
@@ -68,7 +68,7 @@ export const syncStreamed = routeDefinition({
68
68
  throw new errors.ServiceError({
69
69
  status: 500,
70
70
  code: ErrorCode.PSYNC_S2302,
71
- description: 'No sync rules available'
71
+ description: 'No sync config available'
72
72
  });
73
73
  }
74
74
 
@@ -121,7 +121,7 @@ export const syncStreamed = routeDefinition({
121
121
  });
122
122
 
123
123
  stream.on('end', () => {
124
- // Auth failure or switch to new sync rules
124
+ // Auth failure or switch to new sync config
125
125
  closeReason ??= 'service closing stream';
126
126
  });
127
127
 
@@ -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;
@@ -35,13 +35,13 @@ export async function teardown(runnerConfig: utils.RunnerConfig) {
35
35
  }
36
36
 
37
37
  async function terminateSyncRules(storageFactory: storage.BucketStorageFactory, moduleManager: modules.ModuleManager) {
38
- logger.info(`Terminating sync rules...`);
38
+ logger.info(`Terminating replication stream...`);
39
39
  const start = Date.now();
40
40
  const locks: storage.ReplicationLock[] = [];
41
41
  while (Date.now() - start < 120_000) {
42
42
  let retry = false;
43
43
  const replicatingSyncRules = await storageFactory.getReplicatingSyncRules();
44
- // Lock all the replicating sync rules
44
+ // Lock all the replicating replication streams
45
45
  for (const replicatingSyncRule of replicatingSyncRules) {
46
46
  const lock = await replicatingSyncRule.lock();
47
47
  locks.push(lock);
@@ -50,10 +50,10 @@ async function terminateSyncRules(storageFactory: storage.BucketStorageFactory,
50
50
  const stoppedSyncRules = await storageFactory.getStoppedSyncRules();
51
51
  const combinedSyncRules = [...replicatingSyncRules, ...stoppedSyncRules];
52
52
  try {
53
- // Clean up any module specific configuration for the sync rules
53
+ // Clean up any module specific configuration for the replication stream
54
54
  await moduleManager.tearDown({ syncRules: combinedSyncRules });
55
55
 
56
- // Mark the sync rules as terminated
56
+ // Mark the replication stream as terminated
57
57
  for (let syncRules of combinedSyncRules) {
58
58
  const syncRulesStorage = storageFactory.getInstance(syncRules);
59
59
  // The storage will be dropped at the end of the teardown, so we don't need to clear it here
@@ -2,36 +2,36 @@ import { ToastableSqliteRow } from '@powersync/service-sync-rules';
2
2
 
3
3
  export enum SyncRuleState {
4
4
  /**
5
- * New sync rules - needs to be processed (initial replication).
5
+ * New replication stream - needs to be processed (initial replication).
6
6
  *
7
- * While multiple sets of sync rules _can_ be in PROCESSING,
7
+ * While multiple replication streams _can_ be in PROCESSING,
8
8
  * it's generally pointless, so we only keep one in that state.
9
9
  */
10
10
  PROCESSING = 'PROCESSING',
11
11
 
12
12
  /**
13
- * Sync rule processing is done, and can be used for sync.
13
+ * Intial processing is done, and can be used for sync.
14
14
  *
15
- * Only one set of sync rules should be in ACTIVE or ERRORED state.
15
+ * Only one replication stream should be in ACTIVE or ERRORED state.
16
16
  */
17
17
  ACTIVE = 'ACTIVE',
18
18
  /**
19
- * This state is used when the sync rules has been replaced,
19
+ * This state is used when the replication stream has been replaced,
20
20
  * and replication is or should be stopped.
21
21
  */
22
22
  STOP = 'STOP',
23
23
  /**
24
- * After sync rules have been stopped, the data needs to be
24
+ * After replication stream has been stopped, the data needs to be
25
25
  * deleted. Once deleted, the state is TERMINATED.
26
26
  */
27
27
  TERMINATED = 'TERMINATED',
28
28
 
29
29
  /**
30
- * Sync rules has run into a permanent replication error. It
31
- * is still the "active" sync rules for syncing to users,
30
+ * Replication stream has run into a permanent replication error. It
31
+ * is still the "active" replication stram for syncing to users,
32
32
  * but should not replicate anymore.
33
33
  *
34
- * It will transition to STOP when a new sync rules is activated.
34
+ * It will transition to STOP when a new replication stream is activated.
35
35
  */
36
36
  ERRORED = 'ERRORED'
37
37
  }
@@ -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,31 +1,33 @@
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.
17
19
  *
18
- * The provider can handle multiple copies of sync rules concurrently, each with their own storage.
19
- * This is to handle replication of a new version of sync rules, while the old version is still active.
20
+ * The provider can handle multiple replication streams concurrently, each with their own storage.
21
+ * This is to handle replication of a new version of sync config, while the old replication stream is still active.
20
22
  *
21
- * Storage APIs for a specific copy of sync rules are provided by the `SyncRulesBucketStorage` instances.
23
+ * Storage APIs for a specific replication stream are provided by the `SyncRulesBucketStorage` instances.
22
24
  */
23
25
  export abstract class BucketStorageFactory
24
26
  extends BaseObserver<BucketStorageFactoryListener>
25
27
  implements AsyncDisposable
26
28
  {
27
29
  /**
28
- * Update sync rules from configuration, if changed.
30
+ * Update sync config from configuration, if changed.
29
31
  */
30
32
  async configureSyncRules(
31
33
  options: UpdateSyncRulesOptions
@@ -34,42 +36,42 @@ export abstract class BucketStorageFactory
34
36
  const active = await this.getActiveSyncRulesContent();
35
37
 
36
38
  if (next?.sync_rules_content == options.config.yaml) {
37
- logger.info('Sync rules from configuration unchanged');
39
+ logger.info('Sync config unchanged');
38
40
  return { updated: false };
39
41
  } else if (next == null && active?.sync_rules_content == options.config.yaml) {
40
- logger.info('Sync rules from configuration unchanged');
42
+ logger.info('Sync config unchanged');
41
43
  return { updated: false };
42
44
  } else {
43
- logger.info('Sync rules updated from configuration');
45
+ logger.info('Sync config updated');
44
46
  const persisted_sync_rules = await this.updateSyncRules(options);
45
47
  return { updated: true, persisted_sync_rules, lock: persisted_sync_rules.current_lock ?? undefined };
46
48
  }
47
49
  }
48
50
 
49
51
  /**
50
- * Get a storage instance to query sync data for specific sync rules.
52
+ * Get a storage instance to query sync data for specific sync config.
51
53
  */
52
54
  abstract getInstance(syncRules: PersistedSyncRulesContent, options?: GetIntanceOptions): SyncRulesBucketStorage;
53
55
 
54
56
  /**
55
- * Deploy new sync rules.
57
+ * Deploy new sync config.
56
58
  */
57
59
  abstract updateSyncRules(options: UpdateSyncRulesOptions): Promise<PersistedSyncRulesContent>;
58
60
 
59
61
  /**
60
62
  * Indicate that a slot was removed, and we should re-sync by creating
61
- * a new sync rules instance.
63
+ * a new replication stream.
62
64
  *
63
65
  * This is roughly the same as deploying a new version of the current sync
64
- * rules, but also accounts for cases where the current sync rules are not
65
- * the latest ones.
66
+ * config, but also accounts for cases where the current sync config is not
67
+ * the latest one.
66
68
  *
67
69
  * Replication should be restarted after this.
68
70
  */
69
71
  abstract restartReplication(sync_rules_group_id: number): Promise<void>;
70
72
 
71
73
  /**
72
- * Get the sync rules used for querying.
74
+ * Get the sync config used for querying.
73
75
  */
74
76
  async getActiveSyncRules(options: ParseSyncRulesOptions): Promise<PersistedSyncRules | null> {
75
77
  const content = await this.getActiveSyncRulesContent();
@@ -77,12 +79,12 @@ export abstract class BucketStorageFactory
77
79
  }
78
80
 
79
81
  /**
80
- * Get the sync rules used for querying.
82
+ * Get the sync config used for querying.
81
83
  */
82
84
  abstract getActiveSyncRulesContent(): Promise<PersistedSyncRulesContent | null>;
83
85
 
84
86
  /**
85
- * Get the sync rules that will be active next once done with initial replicatino.
87
+ * Get the sync config that will be active next once done with initial replicatino.
86
88
  */
87
89
  async getNextSyncRules(options: ParseSyncRulesOptions): Promise<PersistedSyncRules | null> {
88
90
  const content = await this.getNextSyncRulesContent();
@@ -90,17 +92,17 @@ export abstract class BucketStorageFactory
90
92
  }
91
93
 
92
94
  /**
93
- * Get the sync rules that will be active next once done with initial replicatino.
95
+ * Get the sync config that will be active next once done with initial replicatino.
94
96
  */
95
97
  abstract getNextSyncRulesContent(): Promise<PersistedSyncRulesContent | null>;
96
98
 
97
99
  /**
98
- * Get all sync rules currently replicating. Typically this is the "active" and "next" sync rules.
100
+ * Get all sync config currently replicating. Typically this is the "active" and "next" sync config.
99
101
  */
100
102
  abstract getReplicatingSyncRules(): Promise<PersistedSyncRulesContent[]>;
101
103
 
102
104
  /**
103
- * Get all sync rules stopped but not terminated yet.
105
+ * Get all sync config stopped but not terminated yet.
104
106
  */
105
107
  abstract getStoppedSyncRules(): Promise<PersistedSyncRulesContent[]>;
106
108
 
@@ -110,12 +112,12 @@ export abstract class BucketStorageFactory
110
112
  abstract getActiveStorage(): Promise<SyncRulesBucketStorage | null>;
111
113
 
112
114
  /**
113
- * Get storage size of active sync rules.
115
+ * Get storage size of active replication stream.
114
116
  */
115
117
  abstract getStorageMetrics(): Promise<StorageMetrics>;
116
118
 
117
119
  /**
118
- * Get the unique identifier for this instance of Powersync
120
+ * Get the unique identifier for this instance of Powersync.
119
121
  */
120
122
  abstract getPowerSyncInstanceId(): Promise<string>;
121
123
 
@@ -159,6 +161,12 @@ export interface UpdateSyncRulesOptions {
159
161
  * compiler.
160
162
  */
161
163
  plan: SerializedSyncPlan | null;
164
+
165
+ /**
166
+ * Parsed sync config, primarily to generate a definition mapping.
167
+ * Not persisted, and the defaultSchema used for parsing is not relevant.
168
+ */
169
+ parsed: SyncConfigWithErrors;
162
170
  };
163
171
  lock?: boolean;
164
172
  storageVersion?: number;
@@ -178,13 +186,14 @@ export interface SerializedSyncPlan {
178
186
  * them.
179
187
  */
180
188
  eventDescriptors: Record<string, string[]>;
189
+ errors?: ReplicationError[];
181
190
  }
182
191
 
183
192
  export function updateSyncRulesFromYaml(
184
193
  content: string,
185
194
  options?: Omit<UpdateSyncRulesOptions, 'config'> & { validate?: boolean }
186
195
  ): UpdateSyncRulesOptions {
187
- const { config } = SqlSyncRules.fromYaml(content, {
196
+ const config = SqlSyncRules.fromYaml(content, {
188
197
  // No schema-based validation at this point
189
198
  schema: undefined,
190
199
  defaultSchema: 'not_applicable', // Not needed for validation
@@ -195,24 +204,26 @@ export function updateSyncRulesFromYaml(
195
204
  }
196
205
 
197
206
  export function updateSyncRulesFromConfig(
198
- parsed: SyncConfig,
207
+ parsed: SyncConfigWithErrors,
199
208
  options?: Omit<UpdateSyncRulesOptions, 'config'>
200
209
  ): UpdateSyncRulesOptions {
201
210
  let plan: SerializedSyncPlan | null = null;
202
- if (parsed instanceof PrecompiledSyncConfig) {
211
+ const { config, errors } = parsed;
212
+ if (config instanceof PrecompiledSyncConfig) {
203
213
  const eventDescriptors: Record<string, string[]> = {};
204
- for (const event of parsed.eventDescriptors) {
214
+ for (const event of config.eventDescriptors) {
205
215
  eventDescriptors[event.name] = event.sourceQueries.map((q) => q.sql);
206
216
  }
207
217
 
208
218
  plan = {
209
- compatibility: parsed.compatibility.serialize(),
210
- plan: serializeSyncPlan(parsed.plan),
211
- eventDescriptors
219
+ compatibility: config.compatibility.serialize(),
220
+ plan: serializeSyncPlan(config.plan),
221
+ eventDescriptors,
222
+ errors: errors.map((e) => syncConfigYamlErrorToReplicationError(e))
212
223
  };
213
224
  }
214
225
 
215
- return { config: { yaml: parsed.content, plan }, ...options };
226
+ return { config: { yaml: config.content, plan, parsed }, ...options };
216
227
  }
217
228
 
218
229
  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';
@@ -1,9 +1,10 @@
1
- import { ErrorCode, ServiceError } from '@powersync/lib-services-framework';
1
+ import { logger as defaultLogger, ErrorCode, Logger, ServiceError } from '@powersync/lib-services-framework';
2
2
  import {
3
3
  CompatibilityContext,
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';
@@ -27,7 +29,7 @@ export interface PersistedSyncRulesContentData {
27
29
  readonly compiled_plan: SerializedSyncPlan | null;
28
30
  readonly slot_name: string;
29
31
  /**
30
- * True if this is the "active" copy of the sync rules.
32
+ * True if this is the "active" copy of the sync config.
31
33
  */
32
34
  readonly active: boolean;
33
35
  readonly storageVersion: number;
@@ -47,6 +49,7 @@ export abstract class PersistedSyncRulesContent implements PersistedSyncRulesCon
47
49
  readonly slot_name!: string;
48
50
  readonly active!: boolean;
49
51
  readonly storageVersion!: number;
52
+ readonly logger: Logger;
50
53
 
51
54
  readonly last_checkpoint_lsn!: string | null;
52
55
 
@@ -59,6 +62,7 @@ export abstract class PersistedSyncRulesContent implements PersistedSyncRulesCon
59
62
 
60
63
  constructor(data: PersistedSyncRulesContentData) {
61
64
  Object.assign(this, data);
65
+ this.logger = defaultLogger.child({ prefix: `[${this.slot_name}] ` });
62
66
  }
63
67
 
64
68
  /**
@@ -71,7 +75,7 @@ export abstract class PersistedSyncRulesContent implements PersistedSyncRulesCon
71
75
  if (storageConfig == null) {
72
76
  throw new ServiceError(
73
77
  ErrorCode.PSYNC_S1005,
74
- `Unsupported storage version ${this.storageVersion} for sync rules ${this.id}`
78
+ `Unsupported storage version ${this.storageVersion} for replication stream ${this.id}`
75
79
  );
76
80
  }
77
81
  return storageConfig;
@@ -101,7 +105,25 @@ export abstract class PersistedSyncRulesContent implements PersistedSyncRulesCon
101
105
  sourceText: this.sync_rules_content
102
106
  });
103
107
 
104
- config = { config: precompiled, errors: [] };
108
+ // Note: If the original content did not define a storage version, this will still set the storage version.
109
+ // This means asUpdateOptions will not change the storage version, even if the default changes.
110
+ precompiled.storageVersion = this.storageVersion;
111
+
112
+ const errors: YamlError[] = [];
113
+ if (this.compiled_plan.errors) {
114
+ for (const error of this.compiled_plan.errors) {
115
+ const location: ErrorLocation | undefined = error.location && {
116
+ start: error.location.start_offset,
117
+ end: error.location.end_offset
118
+ };
119
+ const asYamlError = new YamlError(new Error(error.message), location);
120
+ asYamlError.type = error.level;
121
+
122
+ errors.push(asYamlError);
123
+ }
124
+ }
125
+
126
+ config = { config: precompiled, errors };
105
127
  } else {
106
128
  config = SqlSyncRules.fromYaml(this.sync_rules_content, options);
107
129
  }
@@ -128,8 +150,10 @@ export abstract class PersistedSyncRulesContent implements PersistedSyncRulesCon
128
150
  }
129
151
 
130
152
  asUpdateOptions(options?: Omit<UpdateSyncRulesOptions, 'config'>): UpdateSyncRulesOptions {
153
+ // defaultSchema is not relevant for the parsed version here
154
+ const parsed = this.parsed({ defaultSchema: 'not_applicable' });
131
155
  return {
132
- config: { yaml: this.sync_rules_content, plan: this.compiled_plan },
156
+ config: { yaml: this.sync_rules_content, plan: this.compiled_plan, parsed: parsed.sync_rules },
133
157
  ...options
134
158
  };
135
159
  }
@@ -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.
@@ -28,7 +28,7 @@ export class SourceTable implements SourceEntityDescriptor {
28
28
  static readonly DEFAULT_TAG = DEFAULT_TAG;
29
29
 
30
30
  /**
31
- * True if the table is used in sync rules for data queries.
31
+ * True if the table is used in sync config for data queries.
32
32
  *
33
33
  * This value is resolved externally, and cached here.
34
34
  *
@@ -37,7 +37,7 @@ export class SourceTable implements SourceEntityDescriptor {
37
37
  public syncData = true;
38
38
 
39
39
  /**
40
- * True if the table is used in sync rules for data queries.
40
+ * True if the table is used in sync config for data queries.
41
41
  *
42
42
  * This value is resolved externally, and cached here.
43
43
  *
@@ -46,7 +46,7 @@ export class SourceTable implements SourceEntityDescriptor {
46
46
  public syncParameters = true;
47
47
 
48
48
  /**
49
- * True if the table is used in sync rules for events.
49
+ * True if the table is used in sync config for events.
50
50
  *
51
51
  * This value is resolved externally, and cached here.
52
52
  *
@@ -45,7 +45,7 @@ export const STORAGE_VERSION_3 = 3;
45
45
  export const LEGACY_STORAGE_VERSION = STORAGE_VERSION_1;
46
46
 
47
47
  /**
48
- * Default storage version for newly persisted sync rules.
48
+ * Default storage version for newly persisted replication streams.
49
49
  */
50
50
  export const CURRENT_STORAGE_VERSION = STORAGE_VERSION_2;
51
51