@powersync/service-module-mongodb-storage 0.16.0 → 0.18.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 (206) hide show
  1. package/CHANGELOG.md +53 -0
  2. package/dist/storage/MongoBucketStorage.d.ts +35 -15
  3. package/dist/storage/MongoBucketStorage.js +309 -86
  4. package/dist/storage/MongoBucketStorage.js.map +1 -1
  5. package/dist/storage/implementation/CheckpointState.d.ts +20 -0
  6. package/dist/storage/implementation/CheckpointState.js +31 -0
  7. package/dist/storage/implementation/CheckpointState.js.map +1 -0
  8. package/dist/storage/implementation/MongoBucketBatch.d.ts +69 -39
  9. package/dist/storage/implementation/MongoBucketBatch.js +104 -313
  10. package/dist/storage/implementation/MongoBucketBatch.js.map +1 -1
  11. package/dist/storage/implementation/MongoChecksums.d.ts +11 -9
  12. package/dist/storage/implementation/MongoChecksums.js +3 -111
  13. package/dist/storage/implementation/MongoChecksums.js.map +1 -1
  14. package/dist/storage/implementation/MongoCompactor.d.ts +3 -1
  15. package/dist/storage/implementation/MongoCompactor.js +18 -9
  16. package/dist/storage/implementation/MongoCompactor.js.map +1 -1
  17. package/dist/storage/implementation/MongoHydrationState.d.ts +11 -0
  18. package/dist/storage/implementation/MongoHydrationState.js +49 -0
  19. package/dist/storage/implementation/MongoHydrationState.js.map +1 -0
  20. package/dist/storage/implementation/MongoParameterCompactor.d.ts +6 -5
  21. package/dist/storage/implementation/MongoParameterCompactor.js +24 -1
  22. package/dist/storage/implementation/MongoParameterCompactor.js.map +1 -1
  23. package/dist/storage/implementation/MongoParsedSyncConfigSet.d.ts +13 -0
  24. package/dist/storage/implementation/MongoParsedSyncConfigSet.js +64 -0
  25. package/dist/storage/implementation/MongoParsedSyncConfigSet.js.map +1 -0
  26. package/dist/storage/implementation/MongoPersistedReplicationStream.d.ts +36 -0
  27. package/dist/storage/implementation/MongoPersistedReplicationStream.js +90 -0
  28. package/dist/storage/implementation/MongoPersistedReplicationStream.js.map +1 -0
  29. package/dist/storage/implementation/MongoPersistedSyncConfigContent.d.ts +25 -0
  30. package/dist/storage/implementation/MongoPersistedSyncConfigContent.js +121 -0
  31. package/dist/storage/implementation/MongoPersistedSyncConfigContent.js.map +1 -0
  32. package/dist/storage/implementation/MongoStorageProvider.js +5 -2
  33. package/dist/storage/implementation/MongoStorageProvider.js.map +1 -1
  34. package/dist/storage/implementation/MongoSyncBucketStorage.d.ts +50 -21
  35. package/dist/storage/implementation/MongoSyncBucketStorage.js +62 -199
  36. package/dist/storage/implementation/MongoSyncBucketStorage.js.map +1 -1
  37. package/dist/storage/implementation/MongoSyncRulesLock.d.ts +5 -1
  38. package/dist/storage/implementation/MongoSyncRulesLock.js +9 -5
  39. package/dist/storage/implementation/MongoSyncRulesLock.js.map +1 -1
  40. package/dist/storage/implementation/SyncRuleStateUpdate.d.ts +14 -0
  41. package/dist/storage/implementation/SyncRuleStateUpdate.js +36 -0
  42. package/dist/storage/implementation/SyncRuleStateUpdate.js.map +1 -0
  43. package/dist/storage/implementation/common/BucketDataDoc.d.ts +1 -1
  44. package/dist/storage/implementation/common/MongoSyncBucketStorageCheckpoint.d.ts +6 -0
  45. package/dist/storage/implementation/common/MongoSyncBucketStorageCheckpoint.js +2 -0
  46. package/dist/storage/implementation/common/MongoSyncBucketStorageCheckpoint.js.map +1 -0
  47. package/dist/storage/implementation/common/PersistedBatch.d.ts +2 -3
  48. package/dist/storage/implementation/common/PersistedBatch.js +5 -0
  49. package/dist/storage/implementation/common/PersistedBatch.js.map +1 -1
  50. package/dist/storage/implementation/common/SourceRecordStore.d.ts +1 -2
  51. package/dist/storage/implementation/common/VersionedPowerSyncMongoBase.d.ts +1 -1
  52. package/dist/storage/implementation/common/VersionedPowerSyncMongoBase.js +1 -1
  53. package/dist/storage/implementation/common/VersionedPowerSyncMongoBase.js.map +1 -1
  54. package/dist/storage/implementation/createMongoSyncBucketStorage.d.ts +2 -2
  55. package/dist/storage/implementation/createMongoSyncBucketStorage.js +4 -4
  56. package/dist/storage/implementation/createMongoSyncBucketStorage.js.map +1 -1
  57. package/dist/storage/implementation/db.d.ts +10 -2
  58. package/dist/storage/implementation/db.js +9 -14
  59. package/dist/storage/implementation/db.js.map +1 -1
  60. package/dist/storage/implementation/models.d.ts +25 -50
  61. package/dist/storage/implementation/models.js.map +1 -1
  62. package/dist/storage/implementation/v1/MongoBucketBatchV1.d.ts +23 -1
  63. package/dist/storage/implementation/v1/MongoBucketBatchV1.js +403 -2
  64. package/dist/storage/implementation/v1/MongoBucketBatchV1.js.map +1 -1
  65. package/dist/storage/implementation/v1/MongoChecksumsV1.js +114 -3
  66. package/dist/storage/implementation/v1/MongoChecksumsV1.js.map +1 -1
  67. package/dist/storage/implementation/v1/MongoCompactorV1.d.ts +1 -1
  68. package/dist/storage/implementation/v1/MongoSyncBucketStorageV1.d.ts +22 -15
  69. package/dist/storage/implementation/v1/MongoSyncBucketStorageV1.js +100 -27
  70. package/dist/storage/implementation/v1/MongoSyncBucketStorageV1.js.map +1 -1
  71. package/dist/storage/implementation/v1/PersistedBatchV1.d.ts +1 -2
  72. package/dist/storage/implementation/v1/PersistedBatchV1.js.map +1 -1
  73. package/dist/storage/implementation/v1/VersionedPowerSyncMongoV1.d.ts +2 -1
  74. package/dist/storage/implementation/v1/VersionedPowerSyncMongoV1.js +3 -0
  75. package/dist/storage/implementation/v1/VersionedPowerSyncMongoV1.js.map +1 -1
  76. package/dist/storage/implementation/v1/models.d.ts +21 -1
  77. package/dist/storage/implementation/v1/models.js.map +1 -1
  78. package/dist/storage/implementation/v3/MongoBucketBatchV3.d.ts +21 -0
  79. package/dist/storage/implementation/v3/MongoBucketBatchV3.js +459 -3
  80. package/dist/storage/implementation/v3/MongoBucketBatchV3.js.map +1 -1
  81. package/dist/storage/implementation/v3/MongoChecksumsV3.d.ts +27 -7
  82. package/dist/storage/implementation/v3/MongoChecksumsV3.js +202 -18
  83. package/dist/storage/implementation/v3/MongoChecksumsV3.js.map +1 -1
  84. package/dist/storage/implementation/v3/MongoCompactorV3.d.ts +20 -3
  85. package/dist/storage/implementation/v3/MongoCompactorV3.js +511 -22
  86. package/dist/storage/implementation/v3/MongoCompactorV3.js.map +1 -1
  87. package/dist/storage/implementation/v3/MongoStoppedSyncConfigCleanup.d.ts +62 -0
  88. package/dist/storage/implementation/v3/MongoStoppedSyncConfigCleanup.js +350 -0
  89. package/dist/storage/implementation/v3/MongoStoppedSyncConfigCleanup.js.map +1 -0
  90. package/dist/storage/implementation/v3/MongoSyncBucketStorageV3.d.ts +50 -16
  91. package/dist/storage/implementation/v3/MongoSyncBucketStorageV3.js +292 -75
  92. package/dist/storage/implementation/v3/MongoSyncBucketStorageV3.js.map +1 -1
  93. package/dist/storage/implementation/v3/PersistedBatchV3.d.ts +2 -3
  94. package/dist/storage/implementation/v3/PersistedBatchV3.js +43 -20
  95. package/dist/storage/implementation/v3/PersistedBatchV3.js.map +1 -1
  96. package/dist/storage/implementation/v3/SingleBucketStoreV3.js +52 -16
  97. package/dist/storage/implementation/v3/SingleBucketStoreV3.js.map +1 -1
  98. package/dist/storage/implementation/v3/SourceRecordStoreV3.d.ts +3 -3
  99. package/dist/storage/implementation/v3/SourceRecordStoreV3.js +13 -11
  100. package/dist/storage/implementation/v3/SourceRecordStoreV3.js.map +1 -1
  101. package/dist/storage/implementation/v3/VersionedPowerSyncMongoV3.d.ts +16 -14
  102. package/dist/storage/implementation/v3/VersionedPowerSyncMongoV3.js +32 -27
  103. package/dist/storage/implementation/v3/VersionedPowerSyncMongoV3.js.map +1 -1
  104. package/dist/storage/implementation/v3/bucket-format.d.ts +4 -0
  105. package/dist/storage/implementation/v3/bucket-format.js +58 -0
  106. package/dist/storage/implementation/v3/bucket-format.js.map +1 -0
  107. package/dist/storage/implementation/v3/chunking.d.ts +10 -0
  108. package/dist/storage/implementation/v3/chunking.js +28 -0
  109. package/dist/storage/implementation/v3/chunking.js.map +1 -0
  110. package/dist/storage/implementation/v3/models.d.ts +111 -10
  111. package/dist/storage/implementation/v3/models.js +11 -28
  112. package/dist/storage/implementation/v3/models.js.map +1 -1
  113. package/dist/storage/implementation/v3/source-table-utils.d.ts +55 -0
  114. package/dist/storage/implementation/v3/source-table-utils.js +214 -0
  115. package/dist/storage/implementation/v3/source-table-utils.js.map +1 -0
  116. package/dist/storage/storage-index.d.ts +4 -4
  117. package/dist/storage/storage-index.js +4 -4
  118. package/dist/storage/storage-index.js.map +1 -1
  119. package/dist/types/types.d.ts +2 -2
  120. package/dist/utils/test-utils.d.ts +1 -2
  121. package/dist/utils/test-utils.js +5 -1
  122. package/dist/utils/test-utils.js.map +1 -1
  123. package/dist/utils/util.d.ts +1 -1
  124. package/dist/utils/util.js +3 -2
  125. package/dist/utils/util.js.map +1 -1
  126. package/package.json +9 -19
  127. package/src/migrations/db/migrations/1702295701188-sync-rule-state.ts +1 -1
  128. package/src/storage/MongoBucketStorage.ts +464 -95
  129. package/src/storage/implementation/CheckpointState.ts +59 -0
  130. package/src/storage/implementation/MongoBucketBatch.ts +159 -405
  131. package/src/storage/implementation/MongoChecksums.ts +7 -138
  132. package/src/storage/implementation/MongoCompactor.ts +20 -10
  133. package/src/storage/implementation/MongoHydrationState.ts +61 -0
  134. package/src/storage/implementation/MongoParameterCompactor.ts +24 -5
  135. package/src/storage/implementation/MongoParsedSyncConfigSet.ts +88 -0
  136. package/src/storage/implementation/MongoPersistedReplicationStream.ts +118 -0
  137. package/src/storage/implementation/MongoPersistedSyncConfigContent.ts +152 -0
  138. package/src/storage/implementation/MongoStorageProvider.ts +5 -2
  139. package/src/storage/implementation/MongoSyncBucketStorage.ts +89 -250
  140. package/src/storage/implementation/MongoSyncRulesLock.ts +14 -5
  141. package/src/storage/implementation/SyncRuleStateUpdate.ts +38 -0
  142. package/src/storage/implementation/common/BucketDataDoc.ts +1 -1
  143. package/src/storage/implementation/common/MongoSyncBucketStorageCheckpoint.ts +7 -0
  144. package/src/storage/implementation/common/PersistedBatch.ts +8 -4
  145. package/src/storage/implementation/common/SourceRecordStore.ts +1 -2
  146. package/src/storage/implementation/common/VersionedPowerSyncMongoBase.ts +1 -1
  147. package/src/storage/implementation/createMongoSyncBucketStorage.ts +21 -7
  148. package/src/storage/implementation/db.ts +16 -21
  149. package/src/storage/implementation/models.ts +28 -61
  150. package/src/storage/implementation/v1/MongoBucketBatchV1.ts +503 -5
  151. package/src/storage/implementation/v1/MongoChecksumsV1.ts +137 -4
  152. package/src/storage/implementation/v1/MongoCompactorV1.ts +1 -1
  153. package/src/storage/implementation/v1/MongoSyncBucketStorageV1.ts +150 -49
  154. package/src/storage/implementation/v1/PersistedBatchV1.ts +1 -2
  155. package/src/storage/implementation/v1/VersionedPowerSyncMongoV1.ts +11 -1
  156. package/src/storage/implementation/v1/models.ts +25 -0
  157. package/src/storage/implementation/v3/MongoBucketBatchV3.ts +611 -4
  158. package/src/storage/implementation/v3/MongoChecksumsV3.ts +262 -26
  159. package/src/storage/implementation/v3/MongoCompactorV3.ts +647 -30
  160. package/src/storage/implementation/v3/MongoStoppedSyncConfigCleanup.ts +480 -0
  161. package/src/storage/implementation/v3/MongoSyncBucketStorageV3.ts +397 -111
  162. package/src/storage/implementation/v3/PersistedBatchV3.ts +63 -38
  163. package/src/storage/implementation/v3/SingleBucketStoreV3.ts +59 -19
  164. package/src/storage/implementation/v3/SourceRecordStoreV3.ts +12 -12
  165. package/src/storage/implementation/v3/VersionedPowerSyncMongoV3.ts +48 -38
  166. package/src/storage/implementation/v3/bucket-format.ts +70 -0
  167. package/src/storage/implementation/v3/chunking.ts +38 -0
  168. package/src/storage/implementation/v3/models.ts +150 -44
  169. package/src/storage/implementation/v3/source-table-utils.ts +392 -0
  170. package/src/storage/storage-index.ts +12 -4
  171. package/src/utils/test-utils.ts +6 -3
  172. package/src/utils/util.ts +3 -2
  173. package/test/src/__snapshots__/storage.test.ts.snap +0 -75
  174. package/test/src/__snapshots__/storage_sync.test.ts.snap +48 -48
  175. package/test/src/cleanup-stopped-sync-configs.test.ts +648 -0
  176. package/test/src/storage.test.ts +11 -11
  177. package/test/src/storage_compacting.test.ts +1956 -6
  178. package/test/src/storage_sync.test.ts +1429 -42
  179. package/test/src/storeCurrentData.test.ts +221 -0
  180. package/test/src/util.ts +2 -1
  181. package/tsconfig.json +1 -4
  182. package/tsconfig.tsbuildinfo +1 -1
  183. package/dist/storage/implementation/BucketDefinitionMapping.d.ts +0 -17
  184. package/dist/storage/implementation/BucketDefinitionMapping.js +0 -58
  185. package/dist/storage/implementation/BucketDefinitionMapping.js.map +0 -1
  186. package/dist/storage/implementation/MongoPersistedSyncRules.d.ts +0 -14
  187. package/dist/storage/implementation/MongoPersistedSyncRules.js +0 -64
  188. package/dist/storage/implementation/MongoPersistedSyncRules.js.map +0 -1
  189. package/dist/storage/implementation/MongoPersistedSyncRulesContent.d.ts +0 -15
  190. package/dist/storage/implementation/MongoPersistedSyncRulesContent.js +0 -42
  191. package/dist/storage/implementation/MongoPersistedSyncRulesContent.js.map +0 -1
  192. package/dist/storage/implementation/common/MongoSyncBucketStorageContext.d.ts +0 -13
  193. package/dist/storage/implementation/common/MongoSyncBucketStorageContext.js +0 -2
  194. package/dist/storage/implementation/common/MongoSyncBucketStorageContext.js.map +0 -1
  195. package/dist/storage/implementation/v3/MongoParameterCompactorV3.d.ts +0 -9
  196. package/dist/storage/implementation/v3/MongoParameterCompactorV3.js +0 -18
  197. package/dist/storage/implementation/v3/MongoParameterCompactorV3.js.map +0 -1
  198. package/dist/storage/implementation/v3/MongoParameterLookupV3.d.ts +0 -5
  199. package/dist/storage/implementation/v3/MongoParameterLookupV3.js +0 -9
  200. package/dist/storage/implementation/v3/MongoParameterLookupV3.js.map +0 -1
  201. package/src/storage/implementation/BucketDefinitionMapping.ts +0 -72
  202. package/src/storage/implementation/MongoPersistedSyncRules.ts +0 -76
  203. package/src/storage/implementation/MongoPersistedSyncRulesContent.ts +0 -56
  204. package/src/storage/implementation/common/MongoSyncBucketStorageContext.ts +0 -15
  205. package/src/storage/implementation/v3/MongoParameterCompactorV3.ts +0 -24
  206. package/src/storage/implementation/v3/MongoParameterLookupV3.ts +0 -12
@@ -1,70 +1,79 @@
1
1
  import { mongo } from '@powersync/lib-service-mongodb';
2
2
  import { ReplicationAssertionError } from '@powersync/lib-services-framework';
3
3
  import { InternalOpId, storage } from '@powersync/service-core';
4
- import { BucketDataSource } from '@powersync/service-sync-rules';
4
+ import { BucketDataSource, BucketDefinitionId } from '@powersync/service-sync-rules';
5
5
  import * as bson from 'bson';
6
6
  import { mongoTableId } from '../../../utils/util.js';
7
- import { BucketDefinitionId } from '../BucketDefinitionMapping.js';
7
+ import { BucketDataDoc } from '../common/BucketDataDoc.js';
8
8
  import {
9
9
  BucketStateUpdate,
10
10
  PersistedBatch,
11
11
  SaveParameterDataOptions,
12
12
  UpsertCurrentDataOptions
13
13
  } from '../common/PersistedBatch.js';
14
- import { SourceTableKey } from '../models.js';
14
+ import { SourceRecordLookupState } from '../common/SourceRecordStore.js';
15
+ import { serializeBucketData } from './bucket-format.js';
16
+ import { chunkBucketData } from './chunking.js';
15
17
  import {
16
- BucketParameterDocumentV3,
18
+ BucketDataDocumentV3,
17
19
  BucketStateDocumentV3,
18
20
  CurrentDataDocumentV3,
19
- serializeBucketDataV3,
21
+ serializeParameterLookup,
20
22
  SourceTableDocumentV3,
21
- taggedBucketParameterDocumentToV3
23
+ taggedBucketParameterDocumentToTagged
22
24
  } from './models.js';
23
- import { serializeParameterLookupV3 } from './MongoParameterLookupV3.js';
24
25
  import { VersionedPowerSyncMongoV3 } from './VersionedPowerSyncMongoV3.js';
25
26
 
26
27
  export class PersistedBatchV3 extends PersistedBatch {
27
- declare protected readonly db: VersionedPowerSyncMongoV3;
28
-
29
28
  currentData: { sourceTableId: bson.ObjectId; operation: mongo.AnyBulkWriteOperation<CurrentDataDocumentV3> }[] = [];
30
29
  sourceTablePendingDeletes = new Map<string, InternalOpId>();
31
30
 
32
- protected checkDefinitionId(definitionId: BucketDefinitionId | null): BucketDefinitionId {
31
+ declare protected readonly db: VersionedPowerSyncMongoV3;
32
+
33
+ // Abstract override from PersistedBatch (V3-specific error message)
34
+
35
+ protected override checkDefinitionId(definitionId: BucketDefinitionId | null): BucketDefinitionId {
33
36
  if (definitionId == null) {
34
- // This is required for V3 storage.
35
37
  throw new ReplicationAssertionError('Expected v3 bucket when incrementalReprocessing is enabled');
36
38
  }
37
39
  return definitionId;
38
40
  }
39
41
 
42
+ // Concrete implementations from PersistedBatchShared
43
+
40
44
  protected getBucketDefinitionId(bucketSource: BucketDataSource): BucketDefinitionId {
41
45
  return this.mapping.bucketSourceId(bucketSource);
42
46
  }
43
47
 
44
48
  saveParameterData(data: SaveParameterDataOptions) {
45
49
  const { sourceTable, sourceKey, evaluated } = data;
46
- const remaining_lookups = new Map<string, SaveParameterDataOptions['existing_lookups'][number]>();
50
+ const remaining_lookups = new Map<string, SourceRecordLookupState>();
47
51
 
48
52
  for (let lookup of data.existing_lookups) {
49
53
  if (lookup.indexId == null) {
50
- throw new ReplicationAssertionError('Expected v3 lookup when incrementalReprocessing is enabled');
54
+ throw new ReplicationAssertionError('Expected lookup when incrementalReprocessing is enabled');
55
+ }
56
+ if (sourceTable.parameterLookupSourceIds != null && !sourceTable.parameterLookupSourceIds.has(lookup.indexId)) {
57
+ // The parameter index is not active anymore.
58
+ // We don't cleanup these references upfront, but do need to ignore them after the definition is removed.
59
+ continue;
51
60
  }
52
61
  remaining_lookups.set(`${lookup.indexId}.${lookup.lookup.toString('base64')}`, lookup);
53
62
  }
54
63
 
55
64
  for (let result of evaluated) {
56
65
  const sourceDefinitionId = this.mapping.parameterLookupId(result.lookup.source);
57
- const binLookup = serializeParameterLookupV3(result.lookup);
66
+ const binLookup = serializeParameterLookup(result.lookup);
58
67
  remaining_lookups.delete(`${sourceDefinitionId}.${binLookup.toString('base64')}`);
59
68
 
60
69
  const op_id = data.op_seq.next();
61
70
  this.debugLastOpId = op_id;
62
- const values: BucketParameterDocumentV3 = {
71
+ const values = {
63
72
  _id: op_id,
64
73
  key: {
65
74
  t: mongoTableId(sourceTable.id),
66
75
  k: sourceKey
67
- } satisfies SourceTableKey,
76
+ },
68
77
  lookup: binLookup,
69
78
  bucket_parameters: result.bucketParameters
70
79
  };
@@ -81,14 +90,14 @@ export class PersistedBatchV3 extends PersistedBatch {
81
90
  this.debugLastOpId = op_id;
82
91
  const indexId = lookup.indexId;
83
92
  if (indexId == null) {
84
- throw new ReplicationAssertionError('Expected v3 lookup when incrementalReprocessing is enabled');
93
+ throw new ReplicationAssertionError('Expected lookup when incrementalReprocessing is enabled');
85
94
  }
86
- const values: BucketParameterDocumentV3 = {
95
+ const values = {
87
96
  _id: op_id,
88
97
  key: {
89
98
  t: mongoTableId(sourceTable.id),
90
99
  k: sourceKey
91
- } satisfies SourceTableKey,
100
+ },
92
101
  lookup: lookup.lookup,
93
102
  bucket_parameters: []
94
103
  };
@@ -126,8 +135,8 @@ export class PersistedBatchV3 extends PersistedBatch {
126
135
  update: {
127
136
  $set: {
128
137
  data: null,
129
- buckets: [] as CurrentDataDocumentV3['buckets'],
130
- lookups: [] as CurrentDataDocumentV3['lookups'],
138
+ buckets: [],
139
+ lookups: [],
131
140
  pending_delete: checkpointGreaterThan
132
141
  }
133
142
  },
@@ -147,7 +156,7 @@ export class PersistedBatchV3 extends PersistedBatch {
147
156
  upsertCurrentData(values: UpsertCurrentDataOptions) {
148
157
  const buckets = values.buckets.map((bucket) => {
149
158
  if (bucket.definitionId == null) {
150
- throw new ReplicationAssertionError('Expected v3 bucket when incrementalReprocessing is enabled');
159
+ throw new ReplicationAssertionError('Expected bucket when incrementalReprocessing is enabled');
151
160
  }
152
161
  return {
153
162
  def: bucket.definitionId,
@@ -158,7 +167,7 @@ export class PersistedBatchV3 extends PersistedBatch {
158
167
  });
159
168
  const lookups = values.lookups.map((lookup) => {
160
169
  if (lookup.indexId == null) {
161
- throw new ReplicationAssertionError('Expected v3 lookup when incrementalReprocessing is enabled');
170
+ throw new ReplicationAssertionError('Expected lookup when incrementalReprocessing is enabled');
162
171
  }
163
172
  return {
164
173
  i: lookup.indexId,
@@ -190,8 +199,10 @@ export class PersistedBatchV3 extends PersistedBatch {
190
199
  return this.currentData.length;
191
200
  }
192
201
 
202
+ // Flush methods
203
+
193
204
  protected async flushBucketData(session: mongo.ClientSession) {
194
- const operationsByDefinition = new Map<BucketDefinitionId, typeof this.bucketData>();
205
+ const operationsByDefinition = new Map<BucketDefinitionId, BucketDataDoc[]>();
195
206
  for (const document of this.bucketData) {
196
207
  const existing = operationsByDefinition.get(document.bucketKey.definitionId) ?? [];
197
208
  existing.push(document);
@@ -199,17 +210,31 @@ export class PersistedBatchV3 extends PersistedBatch {
199
210
  }
200
211
 
201
212
  for (const [definitionId, documents] of operationsByDefinition.entries()) {
202
- await this.db.bucketDataV3(this.group_id, definitionId).bulkWrite(
203
- documents.map((document) => ({
204
- insertOne: {
205
- document: serializeBucketDataV3(document)
206
- }
207
- })),
208
- {
213
+ const operationsByBucket = new Map<string, BucketDataDoc[]>();
214
+ for (const document of documents) {
215
+ const existing = operationsByBucket.get(document.bucketKey.bucket) ?? [];
216
+ existing.push(document);
217
+ operationsByBucket.set(document.bucketKey.bucket, existing);
218
+ }
219
+
220
+ const inserts: mongo.AnyBulkWriteOperation<BucketDataDocumentV3>[] = [];
221
+ for (const [bucket, ops] of operationsByBucket.entries()) {
222
+ const chunks = chunkBucketData(ops);
223
+ for (const chunk of chunks) {
224
+ inserts.push({
225
+ insertOne: {
226
+ document: serializeBucketData(bucket, chunk)
227
+ }
228
+ });
229
+ }
230
+ }
231
+
232
+ if (inserts.length > 0) {
233
+ await this.db.bucketData(this.group_id, definitionId).bulkWrite(inserts, {
209
234
  session,
210
235
  ordered: false
211
- }
212
- );
236
+ });
237
+ }
213
238
  }
214
239
  }
215
240
 
@@ -222,10 +247,10 @@ export class PersistedBatchV3 extends PersistedBatch {
222
247
  }
223
248
 
224
249
  for (const [indexId, documents] of operationsByIndex.entries()) {
225
- await this.db.parameterIndexV3(this.group_id, indexId).bulkWrite(
250
+ await this.db.parameterIndex(this.group_id, indexId).bulkWrite(
226
251
  documents.map((document) => ({
227
252
  insertOne: {
228
- document: taggedBucketParameterDocumentToV3(document)
253
+ document: taggedBucketParameterDocumentToTagged(document)
229
254
  }
230
255
  })),
231
256
  {
@@ -261,12 +286,12 @@ export class PersistedBatchV3 extends PersistedBatch {
261
286
  });
262
287
 
263
288
  if (sourceTableUpdates.length > 0) {
264
- await this.db.sourceTablesV3(this.group_id).bulkWrite(sourceTableUpdates, { session, ordered: false });
289
+ await this.db.sourceTables(this.group_id).bulkWrite(sourceTableUpdates, { session, ordered: false });
265
290
  }
266
291
 
267
292
  for (const operations of operationsBySourceTable.values()) {
268
293
  const sourceTableId = operations[0]!.sourceTableId;
269
- await this.db.sourceRecordsV3(this.group_id, sourceTableId).bulkWrite(
294
+ await this.db.sourceRecords(this.group_id, sourceTableId).bulkWrite(
270
295
  operations.map((entry) => entry.operation),
271
296
  {
272
297
  session,
@@ -277,7 +302,7 @@ export class PersistedBatchV3 extends PersistedBatch {
277
302
  }
278
303
 
279
304
  protected async flushBucketStates(session: mongo.ClientSession) {
280
- await this.db.bucketStateV3(this.group_id).bulkWrite(this.getBucketStateUpdates(), {
305
+ await this.db.bucketState(this.group_id).bulkWrite(this.getBucketStateUpdates(), {
281
306
  session,
282
307
  ordered: false
283
308
  });
@@ -6,9 +6,38 @@ import {
6
6
  BucketDataDocumentGenericId,
7
7
  SingleBucketStore
8
8
  } from '../common/SingleBucketStore.js';
9
- import { BucketDataProperties } from '../models.js';
9
+ import { BucketDataKey, BucketDataProperties } from '../models.js';
10
+ import { loadBucketDataDocument, serializeBucketData } from './bucket-format.js';
11
+ import { BucketDataDocumentV3 } from './models.js';
10
12
  import { VersionedPowerSyncMongoV3 } from './VersionedPowerSyncMongoV3.js';
11
- import { BucketDataDocumentV3, BucketDataKeyV3, loadBucketDataDocumentV3, serializeBucketDataV3 } from './models.js';
13
+
14
+ function extractPartialDocumentFields(doc: unknown): { _id: { o: bigint }; [key: string]: unknown } {
15
+ if (typeof doc !== 'object' || doc === null) {
16
+ throw new Error('Invalid partial document: expected object');
17
+ }
18
+ const d = doc as Record<string, unknown>;
19
+ const id = d._id;
20
+ if (typeof id !== 'object' || id === null || !('o' in id)) {
21
+ throw new Error('Invalid partial document: missing _id.o');
22
+ }
23
+ return d as { _id: { o: bigint }; [key: string]: unknown };
24
+ }
25
+
26
+ // MongoDB's MinKey/MaxKey are special sentinel values that don't match the bigint type
27
+ // for _id.o in BucketDataDocumentGenericId, so we need an explicit cast.
28
+ function minKeyForBucket(bucket: string): BucketDataDocumentGenericId {
29
+ return {
30
+ b: bucket,
31
+ o: new mongo.MinKey()
32
+ } as unknown as BucketDataDocumentGenericId;
33
+ }
34
+
35
+ function maxKeyForBucket(bucket: string): BucketDataDocumentGenericId {
36
+ return {
37
+ b: bucket,
38
+ o: new mongo.MaxKey()
39
+ } as unknown as BucketDataDocumentGenericId;
40
+ }
12
41
 
13
42
  export class SingleBucketStoreV3 implements SingleBucketStore {
14
43
  public readonly collection: mongo.Collection<BucketDataDocumentGeneric>;
@@ -17,52 +46,63 @@ export class SingleBucketStoreV3 implements SingleBucketStore {
17
46
  private db: VersionedPowerSyncMongoV3,
18
47
  public readonly key: BucketKey
19
48
  ) {
20
- this.collection = db.bucketDataV3(
49
+ // Cast from the version-specific collection type to the generic interface
50
+ // used across storage versions.
51
+ this.collection = db.bucketData(
21
52
  key.replicationStreamId,
22
53
  key.definitionId
23
54
  ) as unknown as mongo.Collection<BucketDataDocumentGeneric>;
24
55
  }
25
56
 
26
57
  docId(o: InternalOpId): BucketDataDocumentGenericId {
27
- // `satisfies BucketDataKeyV3` checks that we use the correct type for V3 storage
58
+ // `satisfies BucketDataKey` checks that we use the correct type for V3 storage
28
59
  // `as BucketDataDocumentGenericId` does a cast to get the interface virtual type
29
60
  return {
30
61
  b: this.key.bucket,
31
62
  o
32
- } satisfies BucketDataKeyV3 as BucketDataDocumentGenericId;
63
+ } satisfies BucketDataKey as BucketDataDocumentGenericId;
33
64
  }
34
65
 
35
66
  get minId(): BucketDataDocumentGenericId {
36
- return {
37
- b: this.key.bucket,
38
- o: new mongo.MinKey()
39
- } as any; // No way to properly type this
67
+ return minKeyForBucket(this.key.bucket);
40
68
  }
41
69
 
42
70
  get maxId(): BucketDataDocumentGenericId {
43
- return {
44
- b: this.key.bucket,
45
- o: new mongo.MaxKey()
46
- } as any; // No way to properly type this
71
+ return maxKeyForBucket(this.key.bucket);
47
72
  }
48
73
 
49
74
  toPersistedDocument(source: Omit<BucketDataDoc, 'bucketKey'>): BucketDataDocumentGeneric {
50
- return serializeBucketDataV3({ bucketKey: this.key, ...source }) as BucketDataDocumentGeneric;
75
+ return serializeBucketData(this.key.bucket, [
76
+ { bucketKey: this.key, ...source }
77
+ ]) as unknown as BucketDataDocumentGeneric;
51
78
  }
52
79
 
53
80
  fromPersistedDocument(doc: BucketDataDocumentGeneric): BucketDataDoc {
54
- return loadBucketDataDocumentV3(this.key, doc as BucketDataDocumentV3);
81
+ const generator = loadBucketDataDocument(this.key, doc as unknown as BucketDataDocumentV3);
82
+ const first = generator.next();
83
+ if (first.done) {
84
+ throw new Error('Empty ops array in BucketDataDocumentV3');
85
+ }
86
+ return first.value;
55
87
  }
56
88
 
57
89
  fromPartialPersistedDocument<T extends keyof BucketDataProperties>(
58
90
  doc: Pick<BucketDataDocumentGeneric, '_id' | T>
59
91
  ): Pick<BucketDataDoc, 'bucketKey' | 'o' | T> {
60
- const document = doc as Pick<BucketDataDocumentV3, '_id' | T>;
61
- const { _id, ...rest } = document;
92
+ const document = doc as unknown as Pick<BucketDataDocumentV3, '_id' | 'ops'>;
93
+ const op = document.ops?.[0];
94
+ if (op == null) {
95
+ const fields = extractPartialDocumentFields(doc);
96
+ const { _id, ...rest } = fields;
97
+ return {
98
+ bucketKey: this.key,
99
+ o: _id.o,
100
+ ...rest
101
+ } as Pick<BucketDataDoc, 'bucketKey' | 'o' | T>;
102
+ }
62
103
  return {
63
104
  bucketKey: this.key,
64
- o: _id.o,
65
- ...rest
105
+ ...op
66
106
  } as Pick<BucketDataDoc, 'bucketKey' | 'o' | T>;
67
107
  }
68
108
  }
@@ -1,21 +1,19 @@
1
1
  import * as lib_mongo from '@powersync/lib-service-mongodb';
2
2
  import { mongo } from '@powersync/lib-service-mongodb';
3
3
  import { Logger } from '@powersync/lib-services-framework';
4
- import { storage } from '@powersync/service-core';
4
+ import { BucketDefinitionMapping, storage } from '@powersync/service-core';
5
5
  import { EvaluatedParameters, EvaluatedRow } from '@powersync/service-sync-rules';
6
6
  import * as bson from 'bson';
7
7
  import { retryOnMongoMaxTimeMSExpired } from '../../../utils/util.js';
8
- import { BucketDefinitionMapping } from '../BucketDefinitionMapping.js';
9
8
  import { cacheKey } from '../OperationBatch.js';
10
9
  import { LoadedSourceRecord, SourceRecordLookupEntry, SourceRecordStore } from '../common/SourceRecordStore.js';
11
- import { serializeParameterLookupV3 } from './MongoParameterLookupV3.js';
12
10
  import { VersionedPowerSyncMongoV3 } from './VersionedPowerSyncMongoV3.js';
13
- import { CurrentDataDocumentV3, SourceTableDocumentV3 } from './models.js';
11
+ import { CurrentDataDocumentV3, serializeParameterLookup, SourceTableDocumentV3 } from './models.js';
14
12
 
15
13
  export class SourceRecordStoreV3 implements SourceRecordStore {
16
14
  constructor(
17
15
  private readonly db: VersionedPowerSyncMongoV3,
18
- private readonly groupId: number,
16
+ private readonly replicationStreamId: number,
19
17
  private readonly mapping: BucketDefinitionMapping
20
18
  ) {}
21
19
 
@@ -31,7 +29,7 @@ export class SourceRecordStoreV3 implements SourceRecordStore {
31
29
  mapParameterLookups(paramEvaluated: EvaluatedParameters[]): LoadedSourceRecord['lookups'] {
32
30
  return paramEvaluated.map((entry) => ({
33
31
  indexId: this.mapping.parameterLookupId(entry.lookup.source),
34
- lookup: serializeParameterLookupV3(entry.lookup)
32
+ lookup: serializeParameterLookup(entry.lookup)
35
33
  }));
36
34
  }
37
35
 
@@ -67,7 +65,7 @@ export class SourceRecordStoreV3 implements SourceRecordStore {
67
65
  _id: { $in: replicaIds as any[] }
68
66
  } as unknown as mongo.Filter<CurrentDataDocumentV3>;
69
67
  const sizeCursor: mongo.AggregationCursor<CurrentDataDocumentV3 & { size: number }> = this.db
70
- .sourceRecordsV3(this.groupId, sourceTableId)
68
+ .sourceRecords(this.replicationStreamId, sourceTableId)
71
69
  .aggregate(
72
70
  [
73
71
  {
@@ -100,7 +98,9 @@ export class SourceRecordStoreV3 implements SourceRecordStore {
100
98
  const filter = {
101
99
  _id: { $in: replicaIds as any[] }
102
100
  } as unknown as mongo.Filter<CurrentDataDocumentV3>;
103
- const cursor = this.db.sourceRecordsV3(this.groupId, sourceTableId).find(filter, { session, projection });
101
+ const cursor = this.db
102
+ .sourceRecords(this.replicationStreamId, sourceTableId)
103
+ .find(filter, { session, projection });
104
104
  for await (const doc of cursor.stream()) {
105
105
  const loaded = this.createLoadedDocument(
106
106
  sourceTableId,
@@ -120,7 +120,7 @@ export class SourceRecordStoreV3 implements SourceRecordStore {
120
120
  sourceTableId: bson.ObjectId,
121
121
  limit: number
122
122
  ): Promise<LoadedSourceRecord[]> {
123
- const cursor = this.db.sourceRecordsV3(this.groupId, sourceTableId).find(
123
+ const cursor = this.db.sourceRecords(this.replicationStreamId, sourceTableId).find(
124
124
  {
125
125
  pending_delete: { $exists: false }
126
126
  },
@@ -145,7 +145,7 @@ export class SourceRecordStoreV3 implements SourceRecordStore {
145
145
  // ones have dirty deletes in source_tables.
146
146
 
147
147
  const dirtySourceTables = await this.db
148
- .sourceTablesV3(this.groupId)
148
+ .sourceTables(this.replicationStreamId)
149
149
  .find(
150
150
  {
151
151
  latest_pending_delete: { $exists: true }
@@ -159,7 +159,7 @@ export class SourceRecordStoreV3 implements SourceRecordStore {
159
159
  let deletedCount = 0;
160
160
  const sourceTableUpdates: mongo.AnyBulkWriteOperation<SourceTableDocumentV3>[] = [];
161
161
  for (const sourceTable of dirtySourceTables) {
162
- const collection = this.db.sourceRecordsV3(this.groupId, sourceTable._id);
162
+ const collection = this.db.sourceRecords(this.replicationStreamId, sourceTable._id);
163
163
  const result = await this.deletePendingDeletes(collection, sourceTable._id, lastCheckpoint, logger);
164
164
  deletedCount += result.deletedCount;
165
165
 
@@ -182,7 +182,7 @@ export class SourceRecordStoreV3 implements SourceRecordStore {
182
182
  }
183
183
 
184
184
  if (sourceTableUpdates.length > 0) {
185
- await this.db.sourceTablesV3(this.groupId).bulkWrite(sourceTableUpdates, { ordered: false });
185
+ await this.db.sourceTables(this.replicationStreamId).bulkWrite(sourceTableUpdates, { ordered: false });
186
186
  }
187
187
  if (deletedCount > 0) {
188
188
  logger.info(`Cleaned up ${deletedCount} pending delete current_data records for checkpoint ${lastCheckpoint}`);
@@ -1,5 +1,5 @@
1
1
  import { mongo } from '@powersync/lib-service-mongodb';
2
- import { BucketDefinitionId, ParameterIndexId } from '../BucketDefinitionMapping.js';
2
+ import { BucketDefinitionId, ParameterIndexId } from '@powersync/service-sync-rules';
3
3
  import { BaseVersionedPowerSyncMongo } from '../common/VersionedPowerSyncMongoBase.js';
4
4
  import { CommonSourceTableDocument } from '../models.js';
5
5
  import {
@@ -7,53 +7,85 @@ import {
7
7
  BucketParameterDocumentV3,
8
8
  BucketStateDocumentV3,
9
9
  CurrentDataDocumentV3,
10
- SourceTableDocumentV3
10
+ SourceTableDocumentV3,
11
+ SyncConfigDefinition
11
12
  } from './models.js';
12
13
 
13
14
  export class VersionedPowerSyncMongoV3 extends BaseVersionedPowerSyncMongo {
14
- sourceRecordsV3(replicationStreamId: number, sourceTableId: mongo.ObjectId): mongo.Collection<CurrentDataDocumentV3> {
15
+ constructor(
16
+ upstream: ConstructorParameters<typeof BaseVersionedPowerSyncMongo>[0],
17
+ storageConfig: ConstructorParameters<typeof BaseVersionedPowerSyncMongo>[1]
18
+ ) {
19
+ super(upstream, storageConfig);
20
+ }
21
+
22
+ get syncConfigDefinitions() {
23
+ return this.db.collection<SyncConfigDefinition>('sync_config');
24
+ }
25
+
26
+ sourceRecords(replicationStreamId: number, sourceTableId: mongo.ObjectId): mongo.Collection<CurrentDataDocumentV3> {
15
27
  const collectionName = this.sourceRecordsCollectionName(replicationStreamId, sourceTableId);
16
28
  return this.db.collection<CurrentDataDocumentV3>(collectionName);
17
29
  }
18
30
 
19
- async listSourceRecordCollectionsV3(replicationStreamId: number): Promise<mongo.Collection<CurrentDataDocumentV3>[]> {
31
+ async listSourceRecordCollections(replicationStreamId: number): Promise<mongo.Collection<CurrentDataDocumentV3>[]> {
20
32
  return this.listCollectionsByPrefix<CurrentDataDocumentV3>(`source_records_${replicationStreamId}_`);
21
33
  }
22
34
 
23
- async initializeSourceRecordsCollection(replicationStreamId: number, sourceTableId: mongo.ObjectId) {
24
- await this.sourceRecordsV3(replicationStreamId, sourceTableId).createIndex(
35
+ async initializeSourceRecordsCollection(
36
+ replicationStreamId: number,
37
+ sourceTableId: mongo.ObjectId,
38
+ session?: mongo.ClientSession
39
+ ) {
40
+ await this.sourceRecords(replicationStreamId, sourceTableId).createIndex(
25
41
  {
26
42
  pending_delete: 1
27
43
  },
28
44
  {
29
45
  partialFilterExpression: { pending_delete: { $exists: true } },
30
- name: 'pending_delete'
46
+ name: 'pending_delete',
47
+ session
31
48
  }
32
49
  );
33
50
  }
34
51
 
35
52
  commonSourceTables(replicationStreamId: number): mongo.Collection<CommonSourceTableDocument> {
36
- return this.sourceTablesV3(replicationStreamId) as mongo.Collection<CommonSourceTableDocument>;
53
+ return this.sourceTables(replicationStreamId) as any as mongo.Collection<CommonSourceTableDocument>;
37
54
  }
38
55
 
39
- bucketStateV3(replicationStreamId: number): mongo.Collection<BucketStateDocumentV3> {
56
+ bucketState(replicationStreamId: number): mongo.Collection<BucketStateDocumentV3> {
40
57
  return this.db.collection(`bucket_state_${replicationStreamId}`);
41
58
  }
42
59
 
43
- parameterIndexV3(
44
- replicationStreamId: number,
45
- indexId: ParameterIndexId
46
- ): mongo.Collection<BucketParameterDocumentV3> {
60
+ parameterIndex(replicationStreamId: number, indexId: ParameterIndexId): mongo.Collection<BucketParameterDocumentV3> {
47
61
  return this.db.collection(`parameter_index_${replicationStreamId}_${indexId}`);
48
62
  }
49
63
 
50
- sourceTablesV3(replicationStreamId: number): mongo.Collection<SourceTableDocumentV3> {
64
+ sourceTables(replicationStreamId: number): mongo.Collection<SourceTableDocumentV3> {
51
65
  return this.db.collection<SourceTableDocumentV3>(this.sourceTableCollectionName(replicationStreamId));
52
66
  }
53
67
 
68
+ bucketData(replicationStreamId: number, definitionId: BucketDefinitionId): mongo.Collection<BucketDataDocumentV3> {
69
+ return this.db.collection<BucketDataDocumentV3>(`bucket_data_${replicationStreamId}_${definitionId}`);
70
+ }
71
+
72
+ listBucketDataCollections(replicationStreamId: number) {
73
+ return this.listCollectionsByPrefix(`bucket_data_${replicationStreamId}_`);
74
+ }
75
+
76
+ async listParameterIndexCollections(replicationStreamId: number) {
77
+ const prefix = `parameter_index_${replicationStreamId}_`;
78
+ const collections = await this.listCollectionsByPrefix(prefix);
79
+
80
+ return collections.map((collection) => ({
81
+ collection,
82
+ indexId: collection.collectionName.slice(prefix.length)
83
+ }));
84
+ }
85
+
54
86
  async initializeStreamStorage(replicationStreamId: number) {
55
- const sourceTables = this.sourceTablesV3(replicationStreamId);
56
- const bucketState = this.bucketStateV3(replicationStreamId);
87
+ const sourceTables = this.sourceTables(replicationStreamId);
88
+ const bucketState = this.bucketState(replicationStreamId);
57
89
  await sourceTables.createIndex(
58
90
  {
59
91
  connection_id: 1,
@@ -87,26 +119,4 @@ export class VersionedPowerSyncMongoV3 extends BaseVersionedPowerSyncMongo {
87
119
  { name: 'dirty_count' }
88
120
  );
89
121
  }
90
-
91
- bucketDataV3(replicationStreamId: number, definitionId: BucketDefinitionId) {
92
- return this.db.collection<BucketDataDocumentV3>(`bucket_data_${replicationStreamId}_${definitionId}`);
93
- }
94
-
95
- listBucketDataCollectionsV3(replicationStreamId: number) {
96
- return this.upstream.listBucketDataCollectionsV3(replicationStreamId);
97
- }
98
-
99
- async listParameterIndexCollectionsV3(
100
- replicationStreamId: number
101
- ): Promise<{ collection: mongo.Collection<BucketParameterDocumentV3>; indexId: ParameterIndexId }[]> {
102
- const prefix = `parameter_index_${replicationStreamId}_`;
103
- const collections = await this.db.listCollections({ name: new RegExp(`^${prefix}`) }, { nameOnly: true }).toArray();
104
-
105
- return collections
106
- .filter((collection) => collection.name.startsWith(prefix))
107
- .map((collection) => ({
108
- collection: this.db.collection<BucketParameterDocumentV3>(collection.name),
109
- indexId: collection.name.slice(prefix.length)
110
- }));
111
- }
112
122
  }
@@ -0,0 +1,70 @@
1
+ import { BucketDataDoc, BucketKey } from '../common/BucketDataDoc.js';
2
+ import { BucketDataDocumentV3, BucketOperation } from './models.js';
3
+
4
+ export function serializeBucketData(bucket: string, operations: BucketDataDoc[]): BucketDataDocumentV3 {
5
+ const minOp = operations[0].o;
6
+ const maxOp = operations[operations.length - 1].o;
7
+
8
+ let totalChecksum = 0n;
9
+ let totalSize = 0;
10
+ let maxTargetOp: bigint | null = null;
11
+
12
+ const ops: BucketOperation[] = operations.map((op) => {
13
+ totalChecksum += op.checksum;
14
+ totalSize += op.data?.length ?? 0;
15
+
16
+ if (op.target_op != null && (maxTargetOp == null || op.target_op > maxTargetOp)) {
17
+ maxTargetOp = op.target_op;
18
+ }
19
+
20
+ return {
21
+ o: op.o,
22
+ op: op.op,
23
+ source_table: op.source_table,
24
+ source_key: op.source_key,
25
+ table: op.table,
26
+ row_id: op.row_id,
27
+ checksum: op.checksum,
28
+ data: op.data
29
+ };
30
+ });
31
+
32
+ return {
33
+ _id: {
34
+ b: bucket,
35
+ o: maxOp
36
+ },
37
+ min_op: minOp,
38
+ checksum: totalChecksum,
39
+ count: operations.length,
40
+ size: totalSize,
41
+ target_op: maxTargetOp,
42
+ ops
43
+ };
44
+ }
45
+
46
+ export function* loadBucketDataDocument(
47
+ context: Pick<BucketKey, 'replicationStreamId' | 'definitionId'>,
48
+ doc: BucketDataDocumentV3
49
+ ): Generator<BucketDataDoc> {
50
+ const { _id, ops } = doc;
51
+ const bucketKey = {
52
+ ...context,
53
+ bucket: _id.b
54
+ };
55
+
56
+ for (const op of ops) {
57
+ yield {
58
+ bucketKey,
59
+ o: op.o,
60
+ op: op.op,
61
+ source_table: op.source_table,
62
+ source_key: op.source_key,
63
+ table: op.table,
64
+ row_id: op.row_id,
65
+ checksum: op.checksum,
66
+ data: op.data,
67
+ target_op: doc.target_op ?? null
68
+ };
69
+ }
70
+ }