@powersync/service-module-mongodb-storage 0.0.0-dev-20260225160713 → 0.0.0-dev-20260511080634

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 (213) hide show
  1. package/CHANGELOG.md +119 -6
  2. package/dist/migrations/db/migrations/1688556755264-initial-sync-rules.js +1 -1
  3. package/dist/migrations/db/migrations/1688556755264-initial-sync-rules.js.map +1 -1
  4. package/dist/migrations/db/migrations/1702295701188-sync-rule-state.js +3 -3
  5. package/dist/migrations/db/migrations/1702295701188-sync-rule-state.js.map +1 -1
  6. package/dist/migrations/db/migrations/1770213298299-storage-version.js.map +1 -1
  7. package/dist/storage/MongoBucketStorage.d.ts +5 -3
  8. package/dist/storage/MongoBucketStorage.js +53 -26
  9. package/dist/storage/MongoBucketStorage.js.map +1 -1
  10. package/dist/storage/MongoReportStorage.js.map +1 -1
  11. package/dist/storage/implementation/BucketDefinitionMapping.d.ts +17 -0
  12. package/dist/storage/implementation/BucketDefinitionMapping.js +58 -0
  13. package/dist/storage/implementation/BucketDefinitionMapping.js.map +1 -0
  14. package/dist/storage/implementation/MongoBucketBatch.d.ts +25 -21
  15. package/dist/storage/implementation/MongoBucketBatch.js +256 -210
  16. package/dist/storage/implementation/MongoBucketBatch.js.map +1 -1
  17. package/dist/storage/implementation/MongoBucketBatchShared.d.ts +5 -0
  18. package/dist/storage/implementation/MongoBucketBatchShared.js +8 -0
  19. package/dist/storage/implementation/MongoBucketBatchShared.js.map +1 -0
  20. package/dist/storage/implementation/MongoChecksums.d.ts +30 -19
  21. package/dist/storage/implementation/MongoChecksums.js +13 -71
  22. package/dist/storage/implementation/MongoChecksums.js.map +1 -1
  23. package/dist/storage/implementation/MongoCompactor.d.ts +99 -53
  24. package/dist/storage/implementation/MongoCompactor.js +265 -303
  25. package/dist/storage/implementation/MongoCompactor.js.map +1 -1
  26. package/dist/storage/implementation/MongoParameterCompactor.d.ts +12 -7
  27. package/dist/storage/implementation/MongoParameterCompactor.js +24 -9
  28. package/dist/storage/implementation/MongoParameterCompactor.js.map +1 -1
  29. package/dist/storage/implementation/MongoPersistedSyncRules.d.ts +14 -0
  30. package/dist/storage/implementation/MongoPersistedSyncRules.js +64 -0
  31. package/dist/storage/implementation/MongoPersistedSyncRules.js.map +1 -0
  32. package/dist/storage/implementation/MongoPersistedSyncRulesContent.d.ts +3 -0
  33. package/dist/storage/implementation/MongoPersistedSyncRulesContent.js +11 -7
  34. package/dist/storage/implementation/MongoPersistedSyncRulesContent.js.map +1 -1
  35. package/dist/storage/implementation/MongoStorageProvider.js +1 -1
  36. package/dist/storage/implementation/MongoStorageProvider.js.map +1 -1
  37. package/dist/storage/implementation/MongoSyncBucketStorage.d.ts +54 -30
  38. package/dist/storage/implementation/MongoSyncBucketStorage.js +118 -472
  39. package/dist/storage/implementation/MongoSyncBucketStorage.js.map +1 -1
  40. package/dist/storage/implementation/MongoSyncRulesLock.d.ts +7 -5
  41. package/dist/storage/implementation/MongoSyncRulesLock.js +12 -10
  42. package/dist/storage/implementation/MongoSyncRulesLock.js.map +1 -1
  43. package/dist/storage/implementation/MongoWriteCheckpointAPI.d.ts +4 -4
  44. package/dist/storage/implementation/MongoWriteCheckpointAPI.js +1 -1
  45. package/dist/storage/implementation/MongoWriteCheckpointAPI.js.map +1 -1
  46. package/dist/storage/implementation/OperationBatch.js +3 -2
  47. package/dist/storage/implementation/OperationBatch.js.map +1 -1
  48. package/dist/storage/implementation/common/BucketDataDoc.d.ts +35 -0
  49. package/dist/storage/implementation/common/BucketDataDoc.js +2 -0
  50. package/dist/storage/implementation/common/BucketDataDoc.js.map +1 -0
  51. package/dist/storage/implementation/common/MongoSyncBucketStorageContext.d.ts +13 -0
  52. package/dist/storage/implementation/common/MongoSyncBucketStorageContext.js +2 -0
  53. package/dist/storage/implementation/common/MongoSyncBucketStorageContext.js.map +1 -0
  54. package/dist/storage/implementation/common/PersistedBatch.d.ts +108 -0
  55. package/dist/storage/implementation/common/PersistedBatch.js +237 -0
  56. package/dist/storage/implementation/common/PersistedBatch.js.map +1 -0
  57. package/dist/storage/implementation/common/SingleBucketStore.d.ts +54 -0
  58. package/dist/storage/implementation/common/SingleBucketStore.js +3 -0
  59. package/dist/storage/implementation/common/SingleBucketStore.js.map +1 -0
  60. package/dist/storage/implementation/common/SourceRecordStore.d.ts +36 -0
  61. package/dist/storage/implementation/common/SourceRecordStore.js +2 -0
  62. package/dist/storage/implementation/common/SourceRecordStore.js.map +1 -0
  63. package/dist/storage/implementation/common/VersionedPowerSyncMongoBase.d.ts +27 -0
  64. package/dist/storage/implementation/common/VersionedPowerSyncMongoBase.js +57 -0
  65. package/dist/storage/implementation/common/VersionedPowerSyncMongoBase.js.map +1 -0
  66. package/dist/storage/implementation/createMongoSyncBucketStorage.d.ts +7 -0
  67. package/dist/storage/implementation/createMongoSyncBucketStorage.js +9 -0
  68. package/dist/storage/implementation/createMongoSyncBucketStorage.js.map +1 -0
  69. package/dist/storage/implementation/db.d.ts +38 -4
  70. package/dist/storage/implementation/db.js +80 -1
  71. package/dist/storage/implementation/db.js.map +1 -1
  72. package/dist/storage/implementation/models.d.ts +66 -25
  73. package/dist/storage/implementation/models.js +22 -2
  74. package/dist/storage/implementation/models.js.map +1 -1
  75. package/dist/storage/implementation/v1/MongoBucketBatchV1.d.ts +13 -0
  76. package/dist/storage/implementation/v1/MongoBucketBatchV1.js +22 -0
  77. package/dist/storage/implementation/v1/MongoBucketBatchV1.js.map +1 -0
  78. package/dist/storage/implementation/v1/MongoChecksumsV1.d.ts +12 -0
  79. package/dist/storage/implementation/v1/MongoChecksumsV1.js +56 -0
  80. package/dist/storage/implementation/v1/MongoChecksumsV1.js.map +1 -0
  81. package/dist/storage/implementation/v1/MongoCompactorV1.d.ts +23 -0
  82. package/dist/storage/implementation/v1/MongoCompactorV1.js +52 -0
  83. package/dist/storage/implementation/v1/MongoCompactorV1.js.map +1 -0
  84. package/dist/storage/implementation/v1/MongoParameterCompactorV1.d.ts +9 -0
  85. package/dist/storage/implementation/v1/MongoParameterCompactorV1.js +20 -0
  86. package/dist/storage/implementation/v1/MongoParameterCompactorV1.js.map +1 -0
  87. package/dist/storage/implementation/v1/MongoSyncBucketStorageV1.d.ts +41 -0
  88. package/dist/storage/implementation/v1/MongoSyncBucketStorageV1.js +269 -0
  89. package/dist/storage/implementation/v1/MongoSyncBucketStorageV1.js.map +1 -0
  90. package/dist/storage/implementation/v1/PersistedBatchV1.d.ts +26 -0
  91. package/dist/storage/implementation/v1/PersistedBatchV1.js +183 -0
  92. package/dist/storage/implementation/v1/PersistedBatchV1.js.map +1 -0
  93. package/dist/storage/implementation/v1/SingleBucketStoreV1.d.ts +18 -0
  94. package/dist/storage/implementation/v1/SingleBucketStoreV1.js +57 -0
  95. package/dist/storage/implementation/v1/SingleBucketStoreV1.js.map +1 -0
  96. package/dist/storage/implementation/v1/SourceRecordStoreV1.d.ts +19 -0
  97. package/dist/storage/implementation/v1/SourceRecordStoreV1.js +105 -0
  98. package/dist/storage/implementation/v1/SourceRecordStoreV1.js.map +1 -0
  99. package/dist/storage/implementation/v1/VersionedPowerSyncMongoV1.d.ts +12 -0
  100. package/dist/storage/implementation/v1/VersionedPowerSyncMongoV1.js +20 -0
  101. package/dist/storage/implementation/v1/VersionedPowerSyncMongoV1.js.map +1 -0
  102. package/dist/storage/implementation/v1/models.d.ts +34 -0
  103. package/dist/storage/implementation/v1/models.js +37 -0
  104. package/dist/storage/implementation/v1/models.js.map +1 -0
  105. package/dist/storage/implementation/v3/MongoBucketBatchV3.d.ts +13 -0
  106. package/dist/storage/implementation/v3/MongoBucketBatchV3.js +34 -0
  107. package/dist/storage/implementation/v3/MongoBucketBatchV3.js.map +1 -0
  108. package/dist/storage/implementation/v3/MongoChecksumsV3.d.ts +15 -0
  109. package/dist/storage/implementation/v3/MongoChecksumsV3.js +84 -0
  110. package/dist/storage/implementation/v3/MongoChecksumsV3.js.map +1 -0
  111. package/dist/storage/implementation/v3/MongoCompactorV3.d.ts +23 -0
  112. package/dist/storage/implementation/v3/MongoCompactorV3.js +68 -0
  113. package/dist/storage/implementation/v3/MongoCompactorV3.js.map +1 -0
  114. package/dist/storage/implementation/v3/MongoParameterCompactorV3.d.ts +9 -0
  115. package/dist/storage/implementation/v3/MongoParameterCompactorV3.js +18 -0
  116. package/dist/storage/implementation/v3/MongoParameterCompactorV3.js.map +1 -0
  117. package/dist/storage/implementation/v3/MongoParameterLookupV3.d.ts +5 -0
  118. package/dist/storage/implementation/v3/MongoParameterLookupV3.js +9 -0
  119. package/dist/storage/implementation/v3/MongoParameterLookupV3.js.map +1 -0
  120. package/dist/storage/implementation/v3/MongoSyncBucketStorageV3.d.ts +41 -0
  121. package/dist/storage/implementation/v3/MongoSyncBucketStorageV3.js +404 -0
  122. package/dist/storage/implementation/v3/MongoSyncBucketStorageV3.js.map +1 -0
  123. package/dist/storage/implementation/v3/PersistedBatchV3.d.ts +29 -0
  124. package/dist/storage/implementation/v3/PersistedBatchV3.js +259 -0
  125. package/dist/storage/implementation/v3/PersistedBatchV3.js.map +1 -0
  126. package/dist/storage/implementation/v3/SingleBucketStoreV3.d.ts +18 -0
  127. package/dist/storage/implementation/v3/SingleBucketStoreV3.js +48 -0
  128. package/dist/storage/implementation/v3/SingleBucketStoreV3.js.map +1 -0
  129. package/dist/storage/implementation/v3/SourceRecordStoreV3.d.ts +22 -0
  130. package/dist/storage/implementation/v3/SourceRecordStoreV3.js +164 -0
  131. package/dist/storage/implementation/v3/SourceRecordStoreV3.js.map +1 -0
  132. package/dist/storage/implementation/v3/VersionedPowerSyncMongoV3.d.ts +21 -0
  133. package/dist/storage/implementation/v3/VersionedPowerSyncMongoV3.js +71 -0
  134. package/dist/storage/implementation/v3/VersionedPowerSyncMongoV3.js.map +1 -0
  135. package/dist/storage/implementation/v3/models.d.ts +43 -0
  136. package/dist/storage/implementation/v3/models.js +34 -0
  137. package/dist/storage/implementation/v3/models.js.map +1 -0
  138. package/dist/storage/storage-index.d.ts +8 -5
  139. package/dist/storage/storage-index.js +8 -5
  140. package/dist/storage/storage-index.js.map +1 -1
  141. package/dist/utils/test-utils.d.ts +4 -1
  142. package/dist/utils/test-utils.js +15 -12
  143. package/dist/utils/test-utils.js.map +1 -1
  144. package/dist/utils/util.d.ts +13 -5
  145. package/dist/utils/util.js +40 -5
  146. package/dist/utils/util.js.map +1 -1
  147. package/package.json +9 -9
  148. package/src/migrations/db/migrations/1688556755264-initial-sync-rules.ts +1 -1
  149. package/src/migrations/db/migrations/1702295701188-sync-rule-state.ts +7 -7
  150. package/src/migrations/db/migrations/1770213298299-storage-version.ts +1 -1
  151. package/src/storage/MongoBucketStorage.ts +111 -55
  152. package/src/storage/MongoReportStorage.ts +2 -2
  153. package/src/storage/implementation/BucketDefinitionMapping.ts +72 -0
  154. package/src/storage/implementation/MongoBucketBatch.ts +322 -270
  155. package/src/storage/implementation/MongoBucketBatchShared.ts +11 -0
  156. package/src/storage/implementation/MongoChecksums.ts +54 -75
  157. package/src/storage/implementation/MongoCompactor.ts +411 -412
  158. package/src/storage/implementation/MongoParameterCompactor.ts +52 -26
  159. package/src/storage/implementation/MongoPersistedSyncRules.ts +76 -0
  160. package/src/storage/implementation/MongoPersistedSyncRulesContent.ts +19 -10
  161. package/src/storage/implementation/MongoStorageProvider.ts +1 -1
  162. package/src/storage/implementation/MongoSyncBucketStorage.ts +206 -466
  163. package/src/storage/implementation/MongoSyncRulesLock.ts +14 -16
  164. package/src/storage/implementation/MongoWriteCheckpointAPI.ts +7 -5
  165. package/src/storage/implementation/OperationBatch.ts +3 -2
  166. package/src/storage/implementation/common/BucketDataDoc.ts +37 -0
  167. package/src/storage/implementation/common/MongoSyncBucketStorageContext.ts +15 -0
  168. package/src/storage/implementation/common/PersistedBatch.ts +364 -0
  169. package/src/storage/implementation/common/SingleBucketStore.ts +63 -0
  170. package/src/storage/implementation/common/SourceRecordStore.ts +49 -0
  171. package/src/storage/implementation/common/VersionedPowerSyncMongoBase.ts +80 -0
  172. package/src/storage/implementation/createMongoSyncBucketStorage.ts +25 -0
  173. package/src/storage/implementation/db.ts +115 -8
  174. package/src/storage/implementation/models.ts +88 -28
  175. package/src/storage/implementation/v1/MongoBucketBatchV1.ts +32 -0
  176. package/src/storage/implementation/v1/MongoChecksumsV1.ts +75 -0
  177. package/src/storage/implementation/v1/MongoCompactorV1.ts +93 -0
  178. package/src/storage/implementation/v1/MongoParameterCompactorV1.ts +26 -0
  179. package/src/storage/implementation/v1/MongoSyncBucketStorageV1.ts +432 -0
  180. package/src/storage/implementation/v1/PersistedBatchV1.ts +230 -0
  181. package/src/storage/implementation/v1/SingleBucketStoreV1.ts +74 -0
  182. package/src/storage/implementation/v1/SourceRecordStoreV1.ts +156 -0
  183. package/src/storage/implementation/v1/VersionedPowerSyncMongoV1.ts +28 -0
  184. package/src/storage/implementation/v1/models.ts +84 -0
  185. package/src/storage/implementation/v3/MongoBucketBatchV3.ts +44 -0
  186. package/src/storage/implementation/v3/MongoChecksumsV3.ts +120 -0
  187. package/src/storage/implementation/v3/MongoCompactorV3.ts +107 -0
  188. package/src/storage/implementation/v3/MongoParameterCompactorV3.ts +24 -0
  189. package/src/storage/implementation/v3/MongoParameterLookupV3.ts +12 -0
  190. package/src/storage/implementation/v3/MongoSyncBucketStorageV3.ts +545 -0
  191. package/src/storage/implementation/v3/PersistedBatchV3.ts +318 -0
  192. package/src/storage/implementation/v3/SingleBucketStoreV3.ts +68 -0
  193. package/src/storage/implementation/v3/SourceRecordStoreV3.ts +226 -0
  194. package/src/storage/implementation/v3/VersionedPowerSyncMongoV3.ts +112 -0
  195. package/src/storage/implementation/v3/models.ts +96 -0
  196. package/src/storage/storage-index.ts +8 -5
  197. package/src/utils/test-utils.ts +15 -12
  198. package/src/utils/util.ts +53 -9
  199. package/test/src/__snapshots__/storage.test.ts.snap +201 -0
  200. package/test/src/__snapshots__/storage_compacting.test.ts.snap +17 -0
  201. package/test/src/__snapshots__/storage_sync.test.ts.snap +1393 -16
  202. package/test/src/connection-report-storage.test.ts +3 -3
  203. package/test/src/setup.ts +1 -1
  204. package/test/src/storage.test.ts +10 -8
  205. package/test/src/storage_compacting.test.ts +146 -46
  206. package/test/src/storage_sync.test.ts +404 -56
  207. package/test/src/util.ts +3 -3
  208. package/test/tsconfig.json +0 -1
  209. package/tsconfig.tsbuildinfo +1 -1
  210. package/dist/storage/implementation/PersistedBatch.d.ts +0 -64
  211. package/dist/storage/implementation/PersistedBatch.js +0 -323
  212. package/dist/storage/implementation/PersistedBatch.js.map +0 -1
  213. package/src/storage/implementation/PersistedBatch.ts +0 -401
@@ -1,17 +1,27 @@
1
- import { storage, updateSyncRulesFromYaml } from '@powersync/service-core';
2
- import { bucketRequest, register, TEST_TABLE, test_utils } from '@powersync/service-core-tests';
1
+ import { deserializeParameterLookup, JwtPayload, storage, updateSyncRulesFromYaml } from '@powersync/service-core';
2
+ import { bucketRequest, register, test_utils } from '@powersync/service-core-tests';
3
+ import { RequestParameters } from '@powersync/service-sync-rules';
4
+ import * as bson from 'bson';
3
5
  import { describe, expect, test } from 'vitest';
6
+ import { MongoBucketStorage } from '../../src/storage/MongoBucketStorage.js';
7
+ import { MongoSyncBucketStorage } from '../../src/storage/implementation/createMongoSyncBucketStorage.js';
8
+ import { SyncRuleDocument } from '../../src/storage/implementation/models.js';
9
+ import { SourceRecordStoreV3 } from '../../src/storage/implementation/v3/SourceRecordStoreV3.js';
10
+ import type { VersionedPowerSyncMongoV3 } from '../../src/storage/implementation/v3/VersionedPowerSyncMongoV3.js';
11
+ import { CurrentBucketV3 } from '../../src/storage/implementation/v3/models.js';
4
12
  import { INITIALIZED_MONGO_STORAGE_FACTORY, TEST_STORAGE_VERSIONS } from './util.js';
5
13
 
6
- function registerSyncStorageTests(storageFactory: storage.TestStorageFactory, storageVersion: number) {
7
- register.registerSyncTests(storageFactory, { storageVersion });
8
-
14
+ function registerSyncStorageTests(storageConfig: storage.TestStorageConfig, storageVersion: number) {
15
+ register.registerSyncTests(storageConfig.factory, {
16
+ storageVersion,
17
+ tableIdStrings: storageConfig.tableIdStrings
18
+ });
9
19
  // The split of returned results can vary depending on storage drivers
10
20
  test('large batch (2)', async () => {
11
21
  // Test syncing a batch of data that is small in count,
12
22
  // but large enough in size to be split over multiple returned chunks.
13
23
  // Similar to the above test, but splits over 1MB chunks.
14
- await using factory = await storageFactory();
24
+ await using factory = await storageConfig.factory();
15
25
  const syncRules = await factory.updateSyncRules(
16
26
  updateSyncRulesFromYaml(
17
27
  `
@@ -24,61 +34,61 @@ function registerSyncStorageTests(storageFactory: storage.TestStorageFactory, st
24
34
  )
25
35
  );
26
36
  const bucketStorage = factory.getInstance(syncRules);
27
- const globalBucket = bucketRequest(syncRules, 'global[]');
28
37
 
29
- const result = await bucketStorage.startBatch(test_utils.BATCH_OPTIONS, async (batch) => {
30
- const sourceTable = TEST_TABLE;
38
+ await using writer = await bucketStorage.createWriter(test_utils.BATCH_OPTIONS);
31
39
 
32
- const largeDescription = '0123456789'.repeat(2_000_00);
40
+ const sourceTable = await test_utils.resolveTestTable(writer, 'test', ['id'], INITIALIZED_MONGO_STORAGE_FACTORY);
33
41
 
34
- await batch.save({
35
- sourceTable,
36
- tag: storage.SaveOperationTag.INSERT,
37
- after: {
38
- id: 'test1',
39
- description: 'test1'
40
- },
41
- afterReplicaId: test_utils.rid('test1')
42
- });
42
+ const largeDescription = '0123456789'.repeat(2_000_00);
43
43
 
44
- await batch.save({
45
- sourceTable,
46
- tag: storage.SaveOperationTag.INSERT,
47
- after: {
48
- id: 'large1',
49
- description: largeDescription
50
- },
51
- afterReplicaId: test_utils.rid('large1')
52
- });
44
+ await writer.save({
45
+ sourceTable,
46
+ tag: storage.SaveOperationTag.INSERT,
47
+ after: {
48
+ id: 'test1',
49
+ description: 'test1'
50
+ },
51
+ afterReplicaId: test_utils.rid('test1')
52
+ });
53
53
 
54
- // Large enough to split the returned batch
55
- await batch.save({
56
- sourceTable,
57
- tag: storage.SaveOperationTag.INSERT,
58
- after: {
59
- id: 'large2',
60
- description: largeDescription
61
- },
62
- afterReplicaId: test_utils.rid('large2')
63
- });
54
+ await writer.save({
55
+ sourceTable,
56
+ tag: storage.SaveOperationTag.INSERT,
57
+ after: {
58
+ id: 'large1',
59
+ description: largeDescription
60
+ },
61
+ afterReplicaId: test_utils.rid('large1')
62
+ });
64
63
 
65
- await batch.save({
66
- sourceTable,
67
- tag: storage.SaveOperationTag.INSERT,
68
- after: {
69
- id: 'test3',
70
- description: 'test3'
71
- },
72
- afterReplicaId: test_utils.rid('test3')
73
- });
64
+ // Large enough to split the returned batch
65
+ await writer.save({
66
+ sourceTable,
67
+ tag: storage.SaveOperationTag.INSERT,
68
+ after: {
69
+ id: 'large2',
70
+ description: largeDescription
71
+ },
72
+ afterReplicaId: test_utils.rid('large2')
74
73
  });
75
74
 
76
- const checkpoint = result!.flushed_op;
75
+ await writer.save({
76
+ sourceTable,
77
+ tag: storage.SaveOperationTag.INSERT,
78
+ after: {
79
+ id: 'test3',
80
+ description: 'test3'
81
+ },
82
+ afterReplicaId: test_utils.rid('test3')
83
+ });
77
84
 
78
- const options: storage.BucketDataBatchOptions = {};
85
+ const flushResult = await writer.flush();
79
86
 
87
+ const checkpoint = flushResult!.flushed_op;
88
+
89
+ const options: storage.BucketDataBatchOptions = {};
80
90
  const batch1 = await test_utils.fromAsync(
81
- bucketStorage.getBucketDataBatch(checkpoint, new Map([[globalBucket, 0n]]), options)
91
+ bucketStorage.getBucketDataBatch(checkpoint, [bucketRequest(syncRules, 'global[]', 0n)], options)
82
92
  );
83
93
  expect(test_utils.getBatchData(batch1)).toEqual([
84
94
  { op_id: '1', op: 'PUT', object_id: 'test1', checksum: 2871785649 },
@@ -93,7 +103,7 @@ function registerSyncStorageTests(storageFactory: storage.TestStorageFactory, st
93
103
  const batch2 = await test_utils.fromAsync(
94
104
  bucketStorage.getBucketDataBatch(
95
105
  checkpoint,
96
- new Map([[globalBucket, BigInt(batch1[0].chunkData.next_after)]]),
106
+ [bucketRequest(syncRules, 'global[]', batch1[0].chunkData.next_after)],
97
107
  options
98
108
  )
99
109
  );
@@ -109,7 +119,7 @@ function registerSyncStorageTests(storageFactory: storage.TestStorageFactory, st
109
119
  const batch3 = await test_utils.fromAsync(
110
120
  bucketStorage.getBucketDataBatch(
111
121
  checkpoint,
112
- new Map([[globalBucket, BigInt(batch2[0].chunkData.next_after)]]),
122
+ [bucketRequest(syncRules, 'global[]', batch2[0].chunkData.next_after)],
113
123
  options
114
124
  )
115
125
  );
@@ -124,12 +134,350 @@ function registerSyncStorageTests(storageFactory: storage.TestStorageFactory, st
124
134
 
125
135
  // Test that the checksum type is correct.
126
136
  // Specifically, test that it never persisted as double.
127
- const mongoFactory = factory as any;
128
- const checksumTypes = await mongoFactory.db.bucket_data
129
- .aggregate([{ $group: { _id: { $type: '$checksum' }, count: { $sum: 1 } } }])
130
- .toArray();
137
+ const mongoFactory = factory as MongoBucketStorage;
138
+ const checksumTypes =
139
+ storageVersion >= 3
140
+ ? (
141
+ await Promise.all(
142
+ (
143
+ await mongoFactory.db.db
144
+ .listCollections({ name: new RegExp(`^bucket_data_${syncRules.id}_`) }, { nameOnly: true })
145
+ .toArray()
146
+ ).map((collection: { name: string }) =>
147
+ mongoFactory.db.db
148
+ .collection(collection.name)
149
+ .aggregate([{ $group: { _id: { $type: '$checksum' }, count: { $sum: 1 } } }])
150
+ .toArray()
151
+ )
152
+ )
153
+ ).flat()
154
+ : await mongoFactory.db.bucket_data
155
+ .aggregate([{ $group: { _id: { $type: '$checksum' }, count: { $sum: 1 } } }])
156
+ .toArray();
131
157
  expect(checksumTypes).toEqual([{ _id: 'long', count: 4 }]);
132
158
  });
159
+
160
+ test.runIf(storageVersion >= 3)('uses v3 mongodb model shapes', async () => {
161
+ await using factory = await storageConfig.factory();
162
+ const syncRules = await factory.updateSyncRules(
163
+ updateSyncRulesFromYaml(
164
+ `
165
+ bucket_definitions:
166
+ global:
167
+ parameters:
168
+ - SELECT owner_id FROM test WHERE id = token_parameters.test
169
+ data:
170
+ - SELECT id, description, owner_id FROM test WHERE id = bucket.owner_id
171
+ `,
172
+ { storageVersion }
173
+ )
174
+ );
175
+ const bucketStorage = factory.getInstance(syncRules);
176
+ const sync_rules = syncRules.parsed(test_utils.PARSE_OPTIONS).hydratedSyncRules();
177
+ await using writer = await bucketStorage.createWriter(test_utils.BATCH_OPTIONS);
178
+ const sourceTable = await test_utils.resolveTestTable(writer, 'test', ['id'], INITIALIZED_MONGO_STORAGE_FACTORY);
179
+
180
+ await writer.save({
181
+ sourceTable,
182
+ tag: storage.SaveOperationTag.INSERT,
183
+ after: {
184
+ id: 'shape-check',
185
+ description: 'shape',
186
+ owner_id: 'user-1'
187
+ },
188
+ afterReplicaId: test_utils.rid('shape-check')
189
+ });
190
+ await writer.markAllSnapshotDone('1/1');
191
+ await writer.commit('1/1');
192
+
193
+ const checkpoint = await bucketStorage.getCheckpoint();
194
+ const parameters = new RequestParameters(new JwtPayload({ sub: 'u1', parameters: { test: 'shape-check' } }), {});
195
+ const querier = sync_rules.getBucketParameterQuerier(test_utils.querierOptions(parameters)).querier;
196
+ const buckets = await querier.queryDynamicBucketDescriptions({
197
+ async getParameterSets(lookups) {
198
+ expect(lookups.map((l) => l.indexKey)).toEqual([['shape-check']]);
199
+ expect(lookups[0].indexId).toEqual('1');
200
+
201
+ const parameter_sets = await checkpoint.getParameterSets(lookups, 1000);
202
+ expect(parameter_sets).toEqual([{ owner_id: 'user-1' }]);
203
+ return parameter_sets;
204
+ }
205
+ });
206
+ expect(buckets.map((b) => b.bucket)).toEqual([bucketRequest(syncRules, 'global["user-1"]').bucket]);
207
+
208
+ const mongoFactory = factory as MongoBucketStorage;
209
+ const db = (bucketStorage as MongoSyncBucketStorage).db as VersionedPowerSyncMongoV3;
210
+ const currentDataCollections = await db.listSourceRecordCollectionsV3(syncRules.id);
211
+ const currentData = await currentDataCollections[0]?.findOne({});
212
+ const firstBucket: CurrentBucketV3 | undefined = currentData?.buckets[0] as CurrentBucketV3 | undefined;
213
+ expect(firstBucket?.def).toMatch(/^[0-9a-f]+$/);
214
+
215
+ const bucketCollections = await mongoFactory.db.db
216
+ .listCollections({ name: new RegExp(`^bucket_data_${syncRules.id}_`) }, { nameOnly: true })
217
+ .toArray();
218
+ expect(
219
+ bucketCollections.some((collection) => collection.name === `bucket_data_${syncRules.id}_${firstBucket?.def}`)
220
+ ).toBe(true);
221
+
222
+ const syncRule = await mongoFactory.db.sync_rules.findOne({ _id: syncRules.id });
223
+ const ruleMapping: SyncRuleDocument['rule_mapping'] | undefined = syncRule?.rule_mapping;
224
+ expect(Object.keys(ruleMapping?.definitions ?? {})).not.toHaveLength(0);
225
+
226
+ const parameterIndexId = Object.values(ruleMapping?.parameter_indexes ?? {})[0] as string | undefined;
227
+ expect(parameterIndexId).toBeDefined();
228
+ const parameterEntry = await db.parameterIndexV3(syncRules.id, parameterIndexId!).findOne({});
229
+ expect(deserializeParameterLookup(parameterEntry!.lookup)).toEqual(['shape-check']);
230
+ });
231
+
232
+ test.runIf(storageVersion < 3)('uses a single current_data collection for v1 source records', async () => {
233
+ await using factory = await storageConfig.factory();
234
+ const syncRules = await factory.updateSyncRules(
235
+ updateSyncRulesFromYaml(
236
+ `
237
+ bucket_definitions:
238
+ global:
239
+ data:
240
+ - SELECT id, description FROM test
241
+ `,
242
+ { storageVersion }
243
+ )
244
+ );
245
+ const bucketStorage = factory.getInstance(syncRules);
246
+
247
+ await using writer = await bucketStorage.createWriter(test_utils.BATCH_OPTIONS);
248
+ const sourceTable = await test_utils.resolveTestTable(writer, 'test', ['id'], INITIALIZED_MONGO_STORAGE_FACTORY);
249
+
250
+ await writer.save({
251
+ sourceTable,
252
+ tag: storage.SaveOperationTag.INSERT,
253
+ after: {
254
+ id: 'shape-check',
255
+ description: 'shape'
256
+ },
257
+ afterReplicaId: test_utils.rid('shape-check')
258
+ });
259
+ await writer.markAllSnapshotDone('1/1');
260
+ await writer.commit('1/1');
261
+
262
+ const mongoFactory = factory as MongoBucketStorage;
263
+ expect(await mongoFactory.db.current_data.countDocuments({ '_id.g': syncRules.id })).toBe(1);
264
+
265
+ const sourceRecordCollections = await mongoFactory.db.db
266
+ .listCollections({ name: new RegExp(`^source_records_${syncRules.id}_`) }, { nameOnly: true })
267
+ .toArray();
268
+ expect(sourceRecordCollections).toEqual([]);
269
+ });
270
+
271
+ test.runIf(storageVersion < 3)('clear removes v1 current_data rows', async () => {
272
+ await using factory = await storageConfig.factory();
273
+ const syncRules = await factory.updateSyncRules(
274
+ updateSyncRulesFromYaml(
275
+ `
276
+ bucket_definitions:
277
+ global:
278
+ data:
279
+ - SELECT id, description FROM test
280
+ `,
281
+ { storageVersion }
282
+ )
283
+ );
284
+ const bucketStorage = factory.getInstance(syncRules);
285
+
286
+ await using writer = await bucketStorage.createWriter(test_utils.BATCH_OPTIONS);
287
+ const sourceTable = await test_utils.resolveTestTable(writer, 'test', ['id'], INITIALIZED_MONGO_STORAGE_FACTORY);
288
+
289
+ await writer.save({
290
+ sourceTable,
291
+ tag: storage.SaveOperationTag.INSERT,
292
+ after: {
293
+ id: 'clear-check',
294
+ description: 'shape'
295
+ },
296
+ afterReplicaId: test_utils.rid('clear-check')
297
+ });
298
+ await writer.markAllSnapshotDone('1/1');
299
+ await writer.commit('1/1');
300
+
301
+ const mongoFactory = factory as MongoBucketStorage;
302
+ expect(await mongoFactory.db.current_data.countDocuments({ '_id.g': syncRules.id })).toBe(1);
303
+
304
+ await bucketStorage.clear();
305
+
306
+ expect(await mongoFactory.db.current_data.countDocuments({ '_id.g': syncRules.id })).toBe(0);
307
+ });
308
+
309
+ test.runIf(storageVersion < 3)('storage metrics include v1 current_data', async () => {
310
+ await using factory = await storageConfig.factory();
311
+ const syncRules = await factory.updateSyncRules(
312
+ updateSyncRulesFromYaml(
313
+ `
314
+ bucket_definitions:
315
+ global:
316
+ data:
317
+ - SELECT id, description FROM test
318
+ `,
319
+ { storageVersion }
320
+ )
321
+ );
322
+ const bucketStorage = factory.getInstance(syncRules);
323
+ const metricsBefore = await factory.getStorageMetrics();
324
+
325
+ await using writer = await bucketStorage.createWriter(test_utils.BATCH_OPTIONS);
326
+ const sourceTable = await test_utils.resolveTestTable(writer, 'test', ['id'], INITIALIZED_MONGO_STORAGE_FACTORY);
327
+
328
+ await writer.save({
329
+ sourceTable,
330
+ tag: storage.SaveOperationTag.INSERT,
331
+ after: {
332
+ id: 'metric-check',
333
+ description: 'shape'
334
+ },
335
+ afterReplicaId: test_utils.rid('metric-check')
336
+ });
337
+ await writer.markAllSnapshotDone('1/1');
338
+ await writer.commit('1/1');
339
+
340
+ const mongoFactory = factory as MongoBucketStorage;
341
+ expect(await mongoFactory.db.current_data.countDocuments({ '_id.g': syncRules.id })).toBe(1);
342
+
343
+ const metricsAfter = await factory.getStorageMetrics();
344
+ expect(metricsAfter.replication_size_bytes).toBeGreaterThan(metricsBefore.replication_size_bytes);
345
+ });
346
+
347
+ test.runIf(storageVersion >= 3)(
348
+ 'loads parameter checkpoint changes across all v3 parameter index collections',
349
+ async () => {
350
+ await using factory = await storageConfig.factory();
351
+ const syncRules = await factory.updateSyncRules(
352
+ updateSyncRulesFromYaml(
353
+ `
354
+ bucket_definitions:
355
+ by_owner:
356
+ parameters:
357
+ - SELECT owner_id FROM test WHERE id = token_parameters.owner_lookup
358
+ data:
359
+ - SELECT id, owner_id FROM test WHERE owner_id = bucket.owner_id
360
+ by_category:
361
+ parameters:
362
+ - SELECT category_id FROM test WHERE id = token_parameters.category_lookup
363
+ data:
364
+ - SELECT id, category_id FROM test WHERE category_id = bucket.category_id
365
+ `,
366
+ { storageVersion }
367
+ )
368
+ );
369
+ const bucketStorage = factory.getInstance(syncRules) as MongoSyncBucketStorage;
370
+ const previousCheckpoint = await bucketStorage.getCheckpoint();
371
+
372
+ await using writer = await bucketStorage.createWriter(test_utils.BATCH_OPTIONS);
373
+ const sourceTable = await test_utils.resolveTestTable(writer, 'test', ['id'], INITIALIZED_MONGO_STORAGE_FACTORY);
374
+
375
+ await writer.save({
376
+ sourceTable,
377
+ tag: storage.SaveOperationTag.INSERT,
378
+ after: {
379
+ id: 'shape-check',
380
+ owner_id: 'user-1',
381
+ category_id: 'cat-1'
382
+ },
383
+ afterReplicaId: test_utils.rid('shape-check')
384
+ });
385
+ await writer.markAllSnapshotDone('1/1');
386
+ await writer.commit('1/1');
387
+
388
+ const nextCheckpoint = await bucketStorage.getCheckpoint();
389
+ const changes = await bucketStorage.getCheckpointChanges({
390
+ lastCheckpoint: previousCheckpoint,
391
+ nextCheckpoint
392
+ });
393
+
394
+ expect(changes.invalidateParameterBuckets).toBe(false);
395
+ expect(changes.updatedParameterLookups).toEqual(new Set(['["1","","shape-check"]', '["2","","shape-check"]']));
396
+ }
397
+ );
398
+
399
+ test.runIf(storageVersion >= 3)('cleans pending deletes only for tracked v3 source tables', async () => {
400
+ await using factory = await storageConfig.factory();
401
+ const syncRules = await factory.updateSyncRules(
402
+ updateSyncRulesFromYaml(
403
+ `
404
+ bucket_definitions:
405
+ global:
406
+ data:
407
+ - SELECT id, description FROM test
408
+ `,
409
+ { storageVersion }
410
+ )
411
+ );
412
+
413
+ const mongoFactory = factory as MongoBucketStorage;
414
+ const bucketStorage = mongoFactory.getInstance(syncRules) as any;
415
+ const db = bucketStorage.db;
416
+ await db.initializeStreamStorage(syncRules.id);
417
+
418
+ const sourceTableA = new bson.ObjectId();
419
+ const sourceTableB = new bson.ObjectId();
420
+ await db.sourceTablesV3(syncRules.id).insertMany([
421
+ {
422
+ _id: sourceTableA,
423
+ connection_id: 1,
424
+ relation_id: 'a',
425
+ schema_name: 'public',
426
+ table_name: 'table_a',
427
+ replica_id_columns: null,
428
+ replica_id_columns2: [],
429
+ snapshot_done: true,
430
+ snapshot_status: undefined,
431
+ bucket_data_source_ids: [],
432
+ parameter_lookup_source_ids: [],
433
+ latest_pending_delete: 9n
434
+ },
435
+ {
436
+ _id: sourceTableB,
437
+ connection_id: 1,
438
+ relation_id: 'b',
439
+ schema_name: 'public',
440
+ table_name: 'table_b',
441
+ replica_id_columns: null,
442
+ replica_id_columns2: [],
443
+ snapshot_done: true,
444
+ snapshot_status: undefined,
445
+ bucket_data_source_ids: [],
446
+ parameter_lookup_source_ids: [],
447
+ latest_pending_delete: 12n
448
+ }
449
+ ]);
450
+
451
+ await db.sourceRecordsV3(syncRules.id, sourceTableA).insertMany([
452
+ { _id: 'deleted-1', data: null, buckets: [], lookups: [], pending_delete: 5n },
453
+ { _id: 'deleted-2', data: null, buckets: [], lookups: [], pending_delete: 9n },
454
+ { _id: 'active', data: null, buckets: [], lookups: [] }
455
+ ]);
456
+ await db
457
+ .sourceRecordsV3(syncRules.id, sourceTableB)
458
+ .insertMany([{ _id: 'later-delete', data: null, buckets: [], lookups: [], pending_delete: 12n }]);
459
+
460
+ const store = new SourceRecordStoreV3(db, syncRules.id, bucketStorage.sync_rules.mapping);
461
+ const logger = { info() {} } as any;
462
+
463
+ await store.postCommitCleanup(6n, logger);
464
+
465
+ expect(await db.sourceRecordsV3(syncRules.id, sourceTableA).countDocuments({ pending_delete: 5n })).toBe(0);
466
+ expect(await db.sourceRecordsV3(syncRules.id, sourceTableA).countDocuments({ pending_delete: 9n })).toBe(1);
467
+ expect(await db.sourceRecordsV3(syncRules.id, sourceTableB).countDocuments({ pending_delete: 12n })).toBe(1);
468
+ expect((await db.sourceTablesV3(syncRules.id).findOne({ _id: sourceTableA }))?.latest_pending_delete).toBe(9n);
469
+ expect((await db.sourceTablesV3(syncRules.id).findOne({ _id: sourceTableB }))?.latest_pending_delete).toBe(12n);
470
+
471
+ await store.postCommitCleanup(10n, logger);
472
+
473
+ expect(
474
+ await db.sourceRecordsV3(syncRules.id, sourceTableA).countDocuments({ pending_delete: { $exists: true } })
475
+ ).toBe(0);
476
+ expect(
477
+ (await db.sourceTablesV3(syncRules.id).findOne({ _id: sourceTableA }))?.latest_pending_delete
478
+ ).toBeUndefined();
479
+ expect((await db.sourceTablesV3(syncRules.id).findOne({ _id: sourceTableB }))?.latest_pending_delete).toBe(12n);
480
+ });
133
481
  }
134
482
 
135
483
  describe('sync - mongodb', () => {
package/test/src/util.ts CHANGED
@@ -1,6 +1,6 @@
1
- import { env } from './env.js';
2
1
  import { mongoTestReportStorageFactoryGenerator, mongoTestStorageFactoryGenerator } from '@module/utils/test-utils.js';
3
- import { CURRENT_STORAGE_VERSION, LEGACY_STORAGE_VERSION } from '@powersync/service-core';
2
+ import { SUPPORTED_STORAGE_VERSIONS } from '@powersync/service-core';
3
+ import { env } from './env.js';
4
4
 
5
5
  export const INITIALIZED_MONGO_STORAGE_FACTORY = mongoTestStorageFactoryGenerator({
6
6
  url: env.MONGO_TEST_URL,
@@ -12,4 +12,4 @@ export const INITIALIZED_MONGO_REPORT_STORAGE_FACTORY = mongoTestReportStorageFa
12
12
  isCI: env.CI
13
13
  });
14
14
 
15
- export const TEST_STORAGE_VERSIONS = [LEGACY_STORAGE_VERSION, CURRENT_STORAGE_VERSION];
15
+ export const TEST_STORAGE_VERSIONS = SUPPORTED_STORAGE_VERSIONS;
@@ -1,7 +1,6 @@
1
1
  {
2
2
  "extends": "../../../tsconfig.tests.json",
3
3
  "compilerOptions": {
4
- "baseUrl": "./",
5
4
  "paths": {
6
5
  "@/*": ["../../../packages/service-core/src/*"],
7
6
  "@module/*": ["../src/*"],