@powersync/service-core 0.0.0-dev-20250122110924 → 0.0.0-dev-20250227082606

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 (193) hide show
  1. package/CHANGELOG.md +84 -9
  2. package/dist/api/RouteAPI.d.ts +9 -1
  3. package/dist/api/diagnostics.js +107 -169
  4. package/dist/api/diagnostics.js.map +1 -1
  5. package/dist/auth/CachedKeyCollector.js +26 -25
  6. package/dist/auth/CachedKeyCollector.js.map +1 -1
  7. package/dist/auth/CompoundKeyCollector.js +1 -0
  8. package/dist/auth/CompoundKeyCollector.js.map +1 -1
  9. package/dist/auth/KeySpec.js +3 -0
  10. package/dist/auth/KeySpec.js.map +1 -1
  11. package/dist/auth/KeyStore.js +4 -0
  12. package/dist/auth/KeyStore.js.map +1 -1
  13. package/dist/auth/LeakyBucket.js +5 -0
  14. package/dist/auth/LeakyBucket.js.map +1 -1
  15. package/dist/auth/RemoteJWKSCollector.js +4 -1
  16. package/dist/auth/RemoteJWKSCollector.js.map +1 -1
  17. package/dist/auth/StaticKeyCollector.js +1 -0
  18. package/dist/auth/StaticKeyCollector.js.map +1 -1
  19. package/dist/auth/StaticSupabaseKeyCollector.js +1 -0
  20. package/dist/auth/StaticSupabaseKeyCollector.js.map +1 -1
  21. package/dist/entry/commands/compact-action.js +10 -73
  22. package/dist/entry/commands/compact-action.js.map +1 -1
  23. package/dist/metrics/Metrics.js +35 -1
  24. package/dist/metrics/Metrics.js.map +1 -1
  25. package/dist/modules/AbstractModule.d.ts +1 -1
  26. package/dist/modules/AbstractModule.js +2 -0
  27. package/dist/modules/AbstractModule.js.map +1 -1
  28. package/dist/modules/ModuleManager.js +1 -3
  29. package/dist/modules/ModuleManager.js.map +1 -1
  30. package/dist/replication/AbstractReplicationJob.js +4 -2
  31. package/dist/replication/AbstractReplicationJob.js.map +1 -1
  32. package/dist/replication/AbstractReplicator.js +26 -88
  33. package/dist/replication/AbstractReplicator.js.map +1 -1
  34. package/dist/replication/ReplicationEngine.js +1 -3
  35. package/dist/replication/ReplicationEngine.js.map +1 -1
  36. package/dist/replication/ReplicationModule.js +3 -0
  37. package/dist/replication/ReplicationModule.js.map +1 -1
  38. package/dist/routes/RouterEngine.js +8 -0
  39. package/dist/routes/RouterEngine.js.map +1 -1
  40. package/dist/routes/configure-fastify.d.ts +3 -3
  41. package/dist/routes/endpoints/admin.d.ts +6 -6
  42. package/dist/routes/endpoints/admin.js +4 -1
  43. package/dist/routes/endpoints/admin.js.map +1 -1
  44. package/dist/routes/endpoints/checkpointing.js +17 -86
  45. package/dist/routes/endpoints/checkpointing.js.map +1 -1
  46. package/dist/routes/endpoints/socket-route.js +7 -6
  47. package/dist/routes/endpoints/socket-route.js.map +1 -1
  48. package/dist/routes/endpoints/sync-rules.js +7 -2
  49. package/dist/routes/endpoints/sync-rules.js.map +1 -1
  50. package/dist/routes/endpoints/sync-stream.js +7 -6
  51. package/dist/routes/endpoints/sync-stream.js.map +1 -1
  52. package/dist/runner/teardown.js +5 -67
  53. package/dist/runner/teardown.js.map +1 -1
  54. package/dist/storage/BucketStorage.d.ts +8 -414
  55. package/dist/storage/BucketStorage.js +9 -7
  56. package/dist/storage/BucketStorage.js.map +1 -1
  57. package/dist/storage/BucketStorageBatch.d.ts +130 -0
  58. package/dist/storage/BucketStorageBatch.js +10 -0
  59. package/dist/storage/BucketStorageBatch.js.map +1 -0
  60. package/dist/storage/BucketStorageFactory.d.ts +145 -0
  61. package/dist/storage/BucketStorageFactory.js +2 -0
  62. package/dist/storage/BucketStorageFactory.js.map +1 -0
  63. package/dist/storage/ChecksumCache.js +12 -7
  64. package/dist/storage/ChecksumCache.js.map +1 -1
  65. package/dist/storage/PersistedSyncRulesContent.d.ts +20 -0
  66. package/dist/storage/PersistedSyncRulesContent.js +2 -0
  67. package/dist/storage/PersistedSyncRulesContent.js.map +1 -0
  68. package/dist/storage/ReplicationEventPayload.d.ts +1 -1
  69. package/dist/storage/ReplicationLock.d.ts +4 -0
  70. package/dist/storage/ReplicationLock.js +2 -0
  71. package/dist/storage/ReplicationLock.js.map +1 -0
  72. package/dist/storage/SourceEntity.d.ts +6 -2
  73. package/dist/storage/SourceTable.d.ts +2 -2
  74. package/dist/storage/SourceTable.js +32 -25
  75. package/dist/storage/SourceTable.js.map +1 -1
  76. package/dist/storage/StorageEngine.d.ts +4 -4
  77. package/dist/storage/StorageEngine.js +6 -5
  78. package/dist/storage/StorageEngine.js.map +1 -1
  79. package/dist/storage/StorageProvider.d.ts +4 -1
  80. package/dist/storage/SyncRulesBucketStorage.d.ts +207 -0
  81. package/dist/storage/SyncRulesBucketStorage.js +7 -0
  82. package/dist/storage/SyncRulesBucketStorage.js.map +1 -0
  83. package/dist/storage/bson.d.ts +19 -6
  84. package/dist/storage/bson.js +18 -2
  85. package/dist/storage/bson.js.map +1 -1
  86. package/dist/storage/storage-index.d.ts +5 -0
  87. package/dist/storage/storage-index.js +5 -0
  88. package/dist/storage/storage-index.js.map +1 -1
  89. package/dist/sync/BroadcastIterable.js +4 -3
  90. package/dist/sync/BroadcastIterable.js.map +1 -1
  91. package/dist/sync/BucketChecksumState.d.ts +95 -0
  92. package/dist/sync/BucketChecksumState.js +321 -0
  93. package/dist/sync/BucketChecksumState.js.map +1 -0
  94. package/dist/sync/LastValueSink.js +2 -0
  95. package/dist/sync/LastValueSink.js.map +1 -1
  96. package/dist/sync/RequestTracker.js +2 -4
  97. package/dist/sync/RequestTracker.js.map +1 -1
  98. package/dist/sync/SyncContext.d.ts +17 -0
  99. package/dist/sync/SyncContext.js +23 -0
  100. package/dist/sync/SyncContext.js.map +1 -0
  101. package/dist/sync/merge.js +4 -0
  102. package/dist/sync/merge.js.map +1 -1
  103. package/dist/sync/sync-index.d.ts +2 -0
  104. package/dist/sync/sync-index.js +2 -0
  105. package/dist/sync/sync-index.js.map +1 -1
  106. package/dist/sync/sync.d.ts +10 -4
  107. package/dist/sync/sync.js +143 -149
  108. package/dist/sync/sync.js.map +1 -1
  109. package/dist/sync/util.d.ts +9 -0
  110. package/dist/sync/util.js +46 -2
  111. package/dist/sync/util.js.map +1 -1
  112. package/dist/system/ServiceContext.d.ts +3 -0
  113. package/dist/system/ServiceContext.js +10 -0
  114. package/dist/system/ServiceContext.js.map +1 -1
  115. package/dist/util/Mutex.js +5 -0
  116. package/dist/util/Mutex.js.map +1 -1
  117. package/dist/util/checkpointing.d.ts +13 -0
  118. package/dist/util/checkpointing.js +29 -0
  119. package/dist/util/checkpointing.js.map +1 -0
  120. package/dist/util/config/compound-config-collector.js +16 -2
  121. package/dist/util/config/compound-config-collector.js.map +1 -1
  122. package/dist/util/config/defaults.d.ts +5 -0
  123. package/dist/util/config/defaults.js +6 -0
  124. package/dist/util/config/defaults.js.map +1 -0
  125. package/dist/util/config/sync-rules/impl/base64-sync-rules-collector.js +1 -0
  126. package/dist/util/config/sync-rules/impl/base64-sync-rules-collector.js.map +1 -1
  127. package/dist/util/config/sync-rules/impl/filesystem-sync-rules-collector.js +1 -0
  128. package/dist/util/config/sync-rules/impl/filesystem-sync-rules-collector.js.map +1 -1
  129. package/dist/util/config/sync-rules/impl/inline-sync-rules-collector.js +1 -0
  130. package/dist/util/config/sync-rules/impl/inline-sync-rules-collector.js.map +1 -1
  131. package/dist/util/config/sync-rules/sync-rules-provider.d.ts +2 -0
  132. package/dist/util/config/sync-rules/sync-rules-provider.js +4 -0
  133. package/dist/util/config/sync-rules/sync-rules-provider.js.map +1 -1
  134. package/dist/util/config/types.d.ts +8 -2
  135. package/dist/util/config/types.js.map +1 -1
  136. package/dist/util/memory-tracking.js +1 -1
  137. package/dist/util/memory-tracking.js.map +1 -1
  138. package/dist/util/protocol-types.d.ts +13 -4
  139. package/dist/util/util-index.d.ts +1 -0
  140. package/dist/util/util-index.js +1 -0
  141. package/dist/util/util-index.js.map +1 -1
  142. package/dist/util/utils.d.ts +0 -1
  143. package/dist/util/utils.js +0 -9
  144. package/dist/util/utils.js.map +1 -1
  145. package/package.json +6 -6
  146. package/src/api/RouteAPI.ts +11 -1
  147. package/src/api/diagnostics.ts +1 -1
  148. package/src/entry/commands/compact-action.ts +2 -3
  149. package/src/modules/AbstractModule.ts +1 -1
  150. package/src/replication/AbstractReplicator.ts +16 -15
  151. package/src/routes/RouterEngine.ts +1 -0
  152. package/src/routes/endpoints/admin.ts +4 -1
  153. package/src/routes/endpoints/checkpointing.ts +11 -22
  154. package/src/routes/endpoints/socket-route.ts +9 -6
  155. package/src/routes/endpoints/sync-rules.ts +7 -2
  156. package/src/routes/endpoints/sync-stream.ts +10 -6
  157. package/src/runner/teardown.ts +1 -1
  158. package/src/storage/BucketStorage.ts +8 -515
  159. package/src/storage/BucketStorageBatch.ts +158 -0
  160. package/src/storage/BucketStorageFactory.ts +166 -0
  161. package/src/storage/ChecksumCache.ts +1 -0
  162. package/src/storage/PersistedSyncRulesContent.ts +26 -0
  163. package/src/storage/ReplicationEventPayload.ts +1 -1
  164. package/src/storage/ReplicationLock.ts +5 -0
  165. package/src/storage/SourceEntity.ts +6 -2
  166. package/src/storage/SourceTable.ts +1 -1
  167. package/src/storage/StorageEngine.ts +4 -4
  168. package/src/storage/StorageProvider.ts +4 -1
  169. package/src/storage/SyncRulesBucketStorage.ts +265 -0
  170. package/src/storage/bson.ts +31 -11
  171. package/src/storage/storage-index.ts +5 -0
  172. package/src/sync/BucketChecksumState.ts +418 -0
  173. package/src/sync/SyncContext.ts +36 -0
  174. package/src/sync/sync-index.ts +2 -0
  175. package/src/sync/sync.ts +199 -177
  176. package/src/sync/util.ts +54 -0
  177. package/src/system/ServiceContext.ts +9 -0
  178. package/src/util/checkpointing.ts +41 -0
  179. package/src/util/config/compound-config-collector.ts +26 -2
  180. package/src/util/config/defaults.ts +5 -0
  181. package/src/util/config/sync-rules/impl/base64-sync-rules-collector.ts +1 -0
  182. package/src/util/config/sync-rules/impl/filesystem-sync-rules-collector.ts +1 -0
  183. package/src/util/config/sync-rules/impl/inline-sync-rules-collector.ts +1 -0
  184. package/src/util/config/sync-rules/sync-rules-provider.ts +6 -0
  185. package/src/util/config/types.ts +9 -2
  186. package/src/util/memory-tracking.ts +2 -2
  187. package/src/util/protocol-types.ts +16 -4
  188. package/src/util/util-index.ts +1 -0
  189. package/src/util/utils.ts +0 -10
  190. package/test/src/auth.test.ts +5 -5
  191. package/test/src/sync/BucketChecksumState.test.ts +580 -0
  192. package/test/src/sync/util.test.ts +34 -0
  193. package/tsconfig.tsbuildinfo +1 -1
@@ -1,7 +1,6 @@
1
1
  import { logger, router, schema } from '@powersync/lib-services-framework';
2
2
  import * as t from 'ts-codec';
3
3
 
4
- import * as framework from '@powersync/lib-services-framework';
5
4
  import * as util from '../../util/util-index.js';
6
5
  import { authUser } from '../auth.js';
7
6
  import { routeDefinition } from '../router.js';
@@ -25,14 +24,15 @@ export const writeCheckpoint = routeDefinition({
25
24
  // Since we don't use LSNs anymore, the only way to get that is to wait.
26
25
  const start = Date.now();
27
26
 
28
- const head = await apiHandler.getReplicationHead();
27
+ const head = await apiHandler.createReplicationHead(async (head) => head);
29
28
 
30
29
  const timeout = 50_000;
31
30
 
32
31
  logger.info(`Waiting for LSN checkpoint: ${head}`);
33
32
  while (Date.now() - start < timeout) {
34
- const cp = await service_context.storageEngine.activeBucketStorage.getActiveCheckpoint();
35
- if (!cp.hasSyncRules()) {
33
+ const bucketStorage = await service_context.storageEngine.activeBucketStorage.getActiveStorage();
34
+ const cp = await bucketStorage?.getCheckpoint();
35
+ if (cp == null) {
36
36
  throw new Error('No sync rules available');
37
37
  }
38
38
  if (cp.lsn && cp.lsn >= head) {
@@ -56,25 +56,14 @@ export const writeCheckpoint2 = routeDefinition({
56
56
 
57
57
  const apiHandler = service_context.routerEngine!.getAPI();
58
58
 
59
- const client_id = payload.params.client_id;
60
- const full_user_id = util.checkpointUserId(user_id, client_id);
61
-
62
- const currentCheckpoint = await apiHandler.getReplicationHead();
63
- const {
64
- storageEngine: { activeBucketStorage }
65
- } = service_context;
66
-
67
- const activeSyncRules = await activeBucketStorage.getActiveSyncRulesContent();
68
- if (!activeSyncRules) {
69
- throw new framework.errors.ValidationError(`Cannot create Write Checkpoint since no sync rules are active.`);
70
- }
71
-
72
- using syncBucketStorage = activeBucketStorage.getInstance(activeSyncRules);
73
- const writeCheckpoint = await syncBucketStorage.createManagedWriteCheckpoint({
74
- user_id: full_user_id,
75
- heads: { '1': currentCheckpoint }
59
+ const { replicationHead, writeCheckpoint } = await util.createWriteCheckpoint({
60
+ userId: user_id,
61
+ clientId: payload.params.client_id,
62
+ api: apiHandler,
63
+ storage: service_context.storageEngine.activeBucketStorage
76
64
  });
77
- logger.info(`Write checkpoint 2: ${JSON.stringify({ currentCheckpoint, id: String(full_user_id) })}`);
65
+
66
+ logger.info(`Write checkpoint for ${user_id}/${payload.params.client_id}: ${writeCheckpoint} | ${replicationHead}`);
78
67
 
79
68
  return {
80
69
  write_checkpoint: String(writeCheckpoint)
@@ -13,7 +13,7 @@ export const syncStreamReactive: SocketRouteGenerator = (router) =>
13
13
  validator: schema.createTsCodecValidator(util.StreamingSyncRequest, { allowAdditional: true }),
14
14
  handler: async ({ context, params, responder, observer, initialN, signal: upstreamSignal }) => {
15
15
  const { service_context } = context;
16
- const { routerEngine } = service_context;
16
+ const { routerEngine, syncContext } = service_context;
17
17
 
18
18
  // Create our own controller that we can abort directly
19
19
  const controller = new AbortController();
@@ -49,9 +49,9 @@ export const syncStreamReactive: SocketRouteGenerator = (router) =>
49
49
  const {
50
50
  storageEngine: { activeBucketStorage }
51
51
  } = service_context;
52
- // Sanity check before we start the stream
53
- const cp = await activeBucketStorage.getActiveCheckpoint();
54
- if (!cp.hasSyncRules()) {
52
+
53
+ const bucketStorage = await activeBucketStorage.getActiveStorage();
54
+ if (bucketStorage == null) {
55
55
  responder.onError(
56
56
  new errors.ServiceError({
57
57
  status: 500,
@@ -63,6 +63,8 @@ export const syncStreamReactive: SocketRouteGenerator = (router) =>
63
63
  return;
64
64
  }
65
65
 
66
+ const syncRules = bucketStorage.getParsedSyncRules(routerEngine!.getAPI().getParseSyncRulesOptions());
67
+
66
68
  const removeStopHandler = routerEngine!.addStopHandler(() => {
67
69
  controller.abort();
68
70
  });
@@ -71,8 +73,9 @@ export const syncStreamReactive: SocketRouteGenerator = (router) =>
71
73
  const tracker = new sync.RequestTracker();
72
74
  try {
73
75
  for await (const data of sync.streamResponse({
74
- storage: activeBucketStorage,
75
- parseOptions: routerEngine!.getAPI().getParseSyncRulesOptions(),
76
+ syncContext: syncContext,
77
+ bucketStorage: bucketStorage,
78
+ syncRules: syncRules,
76
79
  params: {
77
80
  ...params,
78
81
  binary_data: true // always true for web sockets
@@ -69,7 +69,9 @@ export const deploySyncRules = routeDefinition({
69
69
  }
70
70
 
71
71
  const sync_rules = await storageEngine.activeBucketStorage.updateSyncRules({
72
- content: content
72
+ content: content,
73
+ // Aready validated above
74
+ validate: false
73
75
  });
74
76
 
75
77
  return {
@@ -167,7 +169,10 @@ export const reprocessSyncRules = routeDefinition({
167
169
  }
168
170
 
169
171
  const new_rules = await activeBucketStorage.updateSyncRules({
170
- content: sync_rules.sync_rules.content
172
+ content: sync_rules.sync_rules.content,
173
+ // These sync rules already passed validation. But if the rules are not valid anymore due
174
+ // to a service change, we do want to report the error here.
175
+ validate: true
171
176
  });
172
177
  return {
173
178
  slot_name: new_rules.slot_name
@@ -20,7 +20,7 @@ export const syncStreamed = routeDefinition({
20
20
  validator: schema.createTsCodecValidator(util.StreamingSyncRequest, { allowAdditional: true }),
21
21
  handler: async (payload) => {
22
22
  const { service_context } = payload.context;
23
- const { routerEngine, storageEngine } = service_context;
23
+ const { routerEngine, storageEngine, syncContext } = service_context;
24
24
  const headers = payload.request.headers;
25
25
  const userAgent = headers['x-user-agent'] ?? headers['user-agent'];
26
26
  const clientId = payload.params.client_id;
@@ -36,15 +36,18 @@ export const syncStreamed = routeDefinition({
36
36
  const params: util.StreamingSyncRequest = payload.params;
37
37
  const syncParams = new RequestParameters(payload.context.token_payload!, payload.params.parameters ?? {});
38
38
 
39
- // Sanity check before we start the stream
40
- const cp = await storageEngine.activeBucketStorage.getActiveCheckpoint();
41
- if (!cp.hasSyncRules()) {
39
+ const bucketStorage = await storageEngine.activeBucketStorage.getActiveStorage();
40
+
41
+ if (bucketStorage == null) {
42
42
  throw new errors.ServiceError({
43
43
  status: 500,
44
44
  code: ErrorCode.PSYNC_S2302,
45
45
  description: 'No sync rules available'
46
46
  });
47
47
  }
48
+
49
+ const syncRules = bucketStorage.getParsedSyncRules(routerEngine!.getAPI().getParseSyncRulesOptions());
50
+
48
51
  const controller = new AbortController();
49
52
  const tracker = new sync.RequestTracker();
50
53
  try {
@@ -53,8 +56,9 @@ export const syncStreamed = routeDefinition({
53
56
  sync.transformToBytesTracked(
54
57
  sync.ndjson(
55
58
  sync.streamResponse({
56
- storage: storageEngine.activeBucketStorage,
57
- parseOptions: routerEngine!.getAPI().getParseSyncRulesOptions(),
59
+ syncContext: syncContext,
60
+ bucketStorage,
61
+ syncRules: syncRules,
58
62
  params,
59
63
  syncParams,
60
64
  token: payload.context.token_payload!,
@@ -51,7 +51,7 @@ async function terminateSyncRules(storageFactory: storage.BucketStorageFactory,
51
51
 
52
52
  // Mark the sync rules as terminated
53
53
  for (let syncRules of combinedSyncRules) {
54
- using syncRulesStorage = storageFactory.getInstance(syncRules);
54
+ const syncRulesStorage = storageFactory.getInstance(syncRules);
55
55
  // The storage will be dropped at the end of the teardown, so we don't need to clear it here
56
56
  await syncRulesStorage.terminate({ clearStorage: false });
57
57
  }