@powersync/service-core 0.0.0-dev-20260602073133 → 0.0.0-dev-20260909133214

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 (230) hide show
  1. package/CHANGELOG.md +152 -4
  2. package/dist/api/RouteAPI.d.ts +42 -14
  3. package/dist/api/diagnostics.d.ts +12 -1
  4. package/dist/api/diagnostics.js +36 -22
  5. package/dist/api/diagnostics.js.map +1 -1
  6. package/dist/entry/commands/compact-action.js +30 -22
  7. package/dist/entry/commands/compact-action.js.map +1 -1
  8. package/dist/metrics/metrics-interfaces.d.ts +5 -1
  9. package/dist/metrics/metrics-interfaces.js.map +1 -1
  10. package/dist/metrics/open-telemetry/MetricAttributeFilteringExporter.d.ts +36 -0
  11. package/dist/metrics/open-telemetry/MetricAttributeFilteringExporter.js +70 -0
  12. package/dist/metrics/open-telemetry/MetricAttributeFilteringExporter.js.map +1 -0
  13. package/dist/metrics/open-telemetry/OpenTelemetryMetricsFactory.js +6 -1
  14. package/dist/metrics/open-telemetry/OpenTelemetryMetricsFactory.js.map +1 -1
  15. package/dist/metrics/open-telemetry/util.js +7 -3
  16. package/dist/metrics/open-telemetry/util.js.map +1 -1
  17. package/dist/modules/AbstractModule.d.ts +6 -2
  18. package/dist/modules/AbstractModule.js.map +1 -1
  19. package/dist/replication/AbstractReplicator.d.ts +31 -7
  20. package/dist/replication/AbstractReplicator.js +168 -74
  21. package/dist/replication/AbstractReplicator.js.map +1 -1
  22. package/dist/replication/ReplicationModule.d.ts +3 -3
  23. package/dist/replication/ReplicationModule.js.map +1 -1
  24. package/dist/routes/RouterEngine.d.ts +1 -0
  25. package/dist/routes/RouterEngine.js +6 -1
  26. package/dist/routes/RouterEngine.js.map +1 -1
  27. package/dist/routes/configure-fastify.d.ts +104 -56
  28. package/dist/routes/configure-fastify.js +15 -2
  29. package/dist/routes/configure-fastify.js.map +1 -1
  30. package/dist/routes/endpoints/admin.d.ts +42 -24
  31. package/dist/routes/endpoints/admin.js +49 -36
  32. package/dist/routes/endpoints/admin.js.map +1 -1
  33. package/dist/routes/endpoints/checkpointing.d.ts +62 -0
  34. package/dist/routes/endpoints/checkpointing.js +65 -7
  35. package/dist/routes/endpoints/checkpointing.js.map +1 -1
  36. package/dist/routes/endpoints/socket-route.js +3 -2
  37. package/dist/routes/endpoints/socket-route.js.map +1 -1
  38. package/dist/routes/endpoints/sync-rules.js +25 -14
  39. package/dist/routes/endpoints/sync-rules.js.map +1 -1
  40. package/dist/routes/endpoints/sync-stream.js +1 -1
  41. package/dist/routes/endpoints/sync-stream.js.map +1 -1
  42. package/dist/runner/teardown.js +8 -8
  43. package/dist/runner/teardown.js.map +1 -1
  44. package/dist/storage/BucketStorageBatch.d.ts +64 -17
  45. package/dist/storage/BucketStorageBatch.js.map +1 -1
  46. package/dist/storage/BucketStorageFactory.d.ts +50 -31
  47. package/dist/storage/BucketStorageFactory.js +10 -20
  48. package/dist/storage/BucketStorageFactory.js.map +1 -1
  49. package/dist/storage/CheckpointChecksumInvalidatedError.d.ts +12 -0
  50. package/dist/storage/CheckpointChecksumInvalidatedError.js +17 -0
  51. package/dist/storage/CheckpointChecksumInvalidatedError.js.map +1 -0
  52. package/dist/storage/ChecksumCache.d.ts +11 -6
  53. package/dist/storage/ChecksumCache.js +5 -4
  54. package/dist/storage/ChecksumCache.js.map +1 -1
  55. package/dist/storage/ParsedSyncConfigSet.d.ts +23 -0
  56. package/dist/storage/ParsedSyncConfigSet.js +2 -0
  57. package/dist/storage/ParsedSyncConfigSet.js.map +1 -0
  58. package/dist/storage/PersistedReplicationStream.d.ts +32 -0
  59. package/dist/storage/PersistedReplicationStream.js +26 -0
  60. package/dist/storage/PersistedReplicationStream.js.map +1 -0
  61. package/dist/storage/PersistedSyncConfigContent.d.ts +65 -0
  62. package/dist/storage/PersistedSyncConfigContent.js +138 -0
  63. package/dist/storage/PersistedSyncConfigContent.js.map +1 -0
  64. package/dist/storage/PersistedSyncConfigStatus.d.ts +13 -0
  65. package/dist/storage/PersistedSyncConfigStatus.js +2 -0
  66. package/dist/storage/PersistedSyncConfigStatus.js.map +1 -0
  67. package/dist/storage/ReplicationEventPayload.d.ts +6 -1
  68. package/dist/storage/SourceEntity.d.ts +13 -2
  69. package/dist/storage/SourceTable.d.ts +53 -4
  70. package/dist/storage/SourceTable.js +62 -7
  71. package/dist/storage/SourceTable.js.map +1 -1
  72. package/dist/storage/SourceTableReconciler.d.ts +83 -0
  73. package/dist/storage/SourceTableReconciler.js +95 -0
  74. package/dist/storage/SourceTableReconciler.js.map +1 -0
  75. package/dist/storage/StorageVersionConfig.d.ts +4 -0
  76. package/dist/storage/StorageVersionConfig.js +10 -1
  77. package/dist/storage/StorageVersionConfig.js.map +1 -1
  78. package/dist/storage/SyncRulesBucketStorage.d.ts +162 -26
  79. package/dist/storage/SyncRulesBucketStorage.js +3 -0
  80. package/dist/storage/SyncRulesBucketStorage.js.map +1 -1
  81. package/dist/storage/WriteCheckpointAPI.d.ts +85 -6
  82. package/dist/storage/WriteCheckpointAPI.js +33 -0
  83. package/dist/storage/WriteCheckpointAPI.js.map +1 -1
  84. package/dist/storage/implementation/BucketDefinitionMapping.d.ts +133 -0
  85. package/dist/storage/implementation/BucketDefinitionMapping.js +351 -0
  86. package/dist/storage/implementation/BucketDefinitionMapping.js.map +1 -0
  87. package/dist/storage/implementation/IncrementalReprocessingCompatibility.d.ts +6 -0
  88. package/dist/storage/implementation/IncrementalReprocessingCompatibility.js +31 -0
  89. package/dist/storage/implementation/IncrementalReprocessingCompatibility.js.map +1 -0
  90. package/dist/storage/implementation/IncrementalReprocessingSyncConfigLog.d.ts +17 -0
  91. package/dist/storage/implementation/IncrementalReprocessingSyncConfigLog.js +70 -0
  92. package/dist/storage/implementation/IncrementalReprocessingSyncConfigLog.js.map +1 -0
  93. package/dist/storage/implementation/ReplicationStreamStorageIds.d.ts +15 -0
  94. package/dist/storage/implementation/ReplicationStreamStorageIds.js +20 -0
  95. package/dist/storage/implementation/ReplicationStreamStorageIds.js.map +1 -0
  96. package/dist/storage/storage-index.d.ts +10 -1
  97. package/dist/storage/storage-index.js +10 -1
  98. package/dist/storage/storage-index.js.map +1 -1
  99. package/dist/storage/storage-metrics.js +120 -4
  100. package/dist/storage/storage-metrics.js.map +1 -1
  101. package/dist/sync/BucketChecksumState.d.ts +12 -2
  102. package/dist/sync/BucketChecksumState.js +69 -21
  103. package/dist/sync/BucketChecksumState.js.map +1 -1
  104. package/dist/sync/RequestTracker.d.ts +7 -0
  105. package/dist/sync/RequestTracker.js +12 -0
  106. package/dist/sync/RequestTracker.js.map +1 -1
  107. package/dist/sync/sync.js +184 -43
  108. package/dist/sync/sync.js.map +1 -1
  109. package/dist/sync/util.d.ts +5 -0
  110. package/dist/sync/util.js +19 -1
  111. package/dist/sync/util.js.map +1 -1
  112. package/dist/system/ServiceContext.d.ts +2 -0
  113. package/dist/system/ServiceContext.js +2 -0
  114. package/dist/system/ServiceContext.js.map +1 -1
  115. package/dist/tracing/PerformanceTracer.d.ts +23 -1
  116. package/dist/tracing/PerformanceTracer.js +11 -0
  117. package/dist/tracing/PerformanceTracer.js.map +1 -1
  118. package/dist/util/checkpointing.d.ts +4 -8
  119. package/dist/util/checkpointing.js +1 -16
  120. package/dist/util/checkpointing.js.map +1 -1
  121. package/dist/util/config/compound-config-collector.js +23 -2
  122. package/dist/util/config/compound-config-collector.js.map +1 -1
  123. package/dist/util/config/defaults.d.ts +2 -0
  124. package/dist/util/config/defaults.js +2 -0
  125. package/dist/util/config/defaults.js.map +1 -1
  126. package/dist/util/config/sync-rules/impl/base64-sync-rules-collector.js +1 -0
  127. package/dist/util/config/sync-rules/impl/base64-sync-rules-collector.js.map +1 -1
  128. package/dist/util/config/sync-rules/impl/filesystem-sync-rules-collector.js +1 -0
  129. package/dist/util/config/sync-rules/impl/filesystem-sync-rules-collector.js.map +1 -1
  130. package/dist/util/config/sync-rules/sync-rules-provider.d.ts +2 -0
  131. package/dist/util/config/sync-rules/sync-rules-provider.js +3 -0
  132. package/dist/util/config/sync-rules/sync-rules-provider.js.map +1 -1
  133. package/dist/util/config/types.d.ts +4 -1
  134. package/dist/util/lsn.d.ts +4 -0
  135. package/dist/util/lsn.js +10 -0
  136. package/dist/util/lsn.js.map +1 -1
  137. package/dist/util/protocol-types.d.ts +4 -4
  138. package/dist/util/protocol-types.js +3 -2
  139. package/dist/util/protocol-types.js.map +1 -1
  140. package/dist/util/util-index.d.ts +1 -0
  141. package/dist/util/util-index.js +1 -0
  142. package/dist/util/util-index.js.map +1 -1
  143. package/dist/util/utils.d.ts +3 -2
  144. package/dist/util/utils.js +17 -2
  145. package/dist/util/utils.js.map +1 -1
  146. package/dist/util/write-checkpoint-batcher.d.ts +19 -0
  147. package/dist/util/write-checkpoint-batcher.js +100 -0
  148. package/dist/util/write-checkpoint-batcher.js.map +1 -0
  149. package/package.json +7 -7
  150. package/src/api/RouteAPI.ts +43 -14
  151. package/src/api/diagnostics.ts +45 -26
  152. package/src/entry/commands/compact-action.ts +37 -23
  153. package/src/metrics/metrics-interfaces.ts +6 -1
  154. package/src/metrics/open-telemetry/MetricAttributeFilteringExporter.ts +85 -0
  155. package/src/metrics/open-telemetry/OpenTelemetryMetricsFactory.ts +7 -2
  156. package/src/metrics/open-telemetry/util.ts +7 -3
  157. package/src/modules/AbstractModule.ts +6 -2
  158. package/src/replication/AbstractReplicator.ts +212 -78
  159. package/src/replication/ReplicationModule.ts +2 -3
  160. package/src/routes/RouterEngine.ts +7 -1
  161. package/src/routes/configure-fastify.ts +25 -2
  162. package/src/routes/endpoints/admin.ts +75 -51
  163. package/src/routes/endpoints/checkpointing.ts +79 -8
  164. package/src/routes/endpoints/socket-route.ts +3 -2
  165. package/src/routes/endpoints/sync-rules.ts +26 -14
  166. package/src/routes/endpoints/sync-stream.ts +1 -1
  167. package/src/runner/teardown.ts +8 -8
  168. package/src/storage/BucketStorageBatch.ts +65 -18
  169. package/src/storage/BucketStorageFactory.ts +73 -46
  170. package/src/storage/CheckpointChecksumInvalidatedError.ts +17 -0
  171. package/src/storage/ChecksumCache.ts +27 -14
  172. package/src/storage/ParsedSyncConfigSet.ts +25 -0
  173. package/src/storage/PersistedReplicationStream.ts +55 -0
  174. package/src/storage/PersistedSyncConfigContent.ts +207 -0
  175. package/src/storage/PersistedSyncConfigStatus.ts +14 -0
  176. package/src/storage/ReplicationEventPayload.ts +6 -1
  177. package/src/storage/SourceEntity.ts +12 -2
  178. package/src/storage/SourceTable.ts +96 -8
  179. package/src/storage/SourceTableReconciler.ts +188 -0
  180. package/src/storage/StorageVersionConfig.ts +11 -1
  181. package/src/storage/SyncRulesBucketStorage.ts +189 -27
  182. package/src/storage/WriteCheckpointAPI.ts +140 -6
  183. package/src/storage/implementation/BucketDefinitionMapping.ts +539 -0
  184. package/src/storage/implementation/IncrementalReprocessingCompatibility.ts +41 -0
  185. package/src/storage/implementation/IncrementalReprocessingSyncConfigLog.ts +126 -0
  186. package/src/storage/implementation/ReplicationStreamStorageIds.ts +21 -0
  187. package/src/storage/storage-index.ts +11 -1
  188. package/src/storage/storage-metrics.ts +154 -8
  189. package/src/sync/BucketChecksumState.ts +105 -21
  190. package/src/sync/RequestTracker.ts +20 -0
  191. package/src/sync/sync.ts +214 -50
  192. package/src/sync/util.ts +33 -8
  193. package/src/system/ServiceContext.ts +7 -0
  194. package/src/tracing/PerformanceTracer.ts +41 -1
  195. package/src/util/checkpointing.ts +4 -22
  196. package/src/util/config/compound-config-collector.ts +31 -1
  197. package/src/util/config/defaults.ts +2 -0
  198. package/src/util/config/sync-rules/impl/base64-sync-rules-collector.ts +1 -0
  199. package/src/util/config/sync-rules/impl/filesystem-sync-rules-collector.ts +1 -0
  200. package/src/util/config/sync-rules/sync-rules-provider.ts +5 -0
  201. package/src/util/config/types.ts +4 -1
  202. package/src/util/lsn.ts +9 -0
  203. package/src/util/protocol-types.ts +3 -2
  204. package/src/util/util-index.ts +1 -0
  205. package/src/util/utils.ts +16 -2
  206. package/src/util/write-checkpoint-batcher.ts +142 -0
  207. package/test/src/AbstractReplicator.test.ts +177 -0
  208. package/test/src/MetricAttributeFilteringExporter.test.ts +96 -0
  209. package/test/src/PersistedSyncConfigContent.test.ts +152 -0
  210. package/test/src/checksum_cache.test.ts +22 -0
  211. package/test/src/config.test.ts +142 -0
  212. package/test/src/diagnostics.test.ts +87 -34
  213. package/test/src/module-loader.test.ts +33 -1
  214. package/test/src/routes/admin.test.ts +188 -18
  215. package/test/src/routes/checkpointing.test.ts +54 -0
  216. package/test/src/routes/mocks.ts +6 -2
  217. package/test/src/routes/stream.test.ts +123 -2
  218. package/test/src/source-table-reconciler.test.ts +244 -0
  219. package/test/src/storage/SourceTable.test.ts +92 -0
  220. package/test/src/storage-metrics.test.ts +148 -0
  221. package/test/src/sync/BucketChecksumState.test.ts +98 -12
  222. package/test/src/sync/util.test.ts +28 -1
  223. package/test/src/util/checkpointing.test.ts +249 -0
  224. package/test/src/util/utils.test.ts +23 -0
  225. package/tsconfig.json +1 -4
  226. package/tsconfig.tsbuildinfo +1 -1
  227. package/dist/storage/PersistedSyncRulesContent.d.ts +0 -59
  228. package/dist/storage/PersistedSyncRulesContent.js +0 -100
  229. package/dist/storage/PersistedSyncRulesContent.js.map +0 -1
  230. package/src/storage/PersistedSyncRulesContent.ts +0 -174
@@ -10,33 +10,45 @@ import { PerformanceTracer } from '../tracing/PerformanceTracer.js';
10
10
  import * as util from '../util/util-index.js';
11
11
  import { BucketStorageBatch, FlushedResult, SaveUpdate } from './BucketStorageBatch.js';
12
12
  import { BucketStorageFactory } from './BucketStorageFactory.js';
13
- import { ParseSyncRulesOptions } from './PersistedSyncRulesContent.js';
13
+ import { ParsedSyncConfigSet } from './ParsedSyncConfigSet.js';
14
+ import { ParseSyncConfigOptions } from './PersistedSyncConfigContent.js';
14
15
  import { SourceEntityDescriptor } from './SourceEntity.js';
15
16
  import { SourceTable } from './SourceTable.js';
17
+ import { SourceTableCandidateReconciler } from './SourceTableReconciler.js';
16
18
  import { StorageVersionConfig } from './StorageVersionConfig.js';
17
19
  import { SyncStorageWriteCheckpointAPI } from './WriteCheckpointAPI.js';
18
20
 
19
21
  /**
20
22
  * Storage for a specific replication stream.
23
+ *
24
+ * Replication writes source changes, source table metadata, snapshot progress,
25
+ * resume positions, and checkpoint boundaries through this interface. The sync
26
+ * API reads the same stream state as checkpoints, checksums, bucket data,
27
+ * parameter lookup rows, and write-checkpoint acknowledgements.
21
28
  */
22
29
  export interface SyncRulesBucketStorage
23
30
  extends ObserverClient<SyncRulesBucketStorageListener>,
24
31
  SyncStorageWriteCheckpointAPI {
25
- readonly group_id: number;
26
- readonly slot_name: string;
32
+ readonly replicationStreamId: number;
33
+ readonly replicationStreamName: string;
27
34
  readonly storageConfig: StorageVersionConfig;
28
35
 
29
36
  readonly factory: BucketStorageFactory;
30
37
  readonly logger: Logger;
31
38
 
32
39
  /**
33
- * Create a new writer.
40
+ * Create a writer for a source-specific unit of replication work.
34
41
  *
35
- * The writer must be flushed and disposed when done.
42
+ * The writer may be used for a long-running streaming attempt, a table
43
+ * snapshot, a page of snapshot work, or a short setup step. Pending work
44
+ * that should be persisted must be flushed, committed, or kept alive at the
45
+ * appropriate source boundary before the writer is disposed.
36
46
  */
37
47
  createWriter(options: CreateWriterOptions): Promise<BucketStorageBatch>;
38
48
 
39
49
  /**
50
+ * Callback wrapper around `createWriter()`.
51
+ *
40
52
  * @deprecated Use `createWriter()` with `await using` instead.
41
53
  */
42
54
  startBatch(
@@ -44,7 +56,20 @@ export interface SyncRulesBucketStorage
44
56
  callback: (batch: BucketStorageBatch) => Promise<void>
45
57
  ): Promise<FlushedResult | null>;
46
58
 
47
- getParsedSyncRules(options: ParseSyncRulesOptions): HydratedSyncConfig;
59
+ /**
60
+ * Get the canonical parsed sync config set for the given parse options.
61
+ *
62
+ * Repeated calls with the same options return the same instance for the lifetime of
63
+ * this storage instance. This is the identity boundary for parsed sync config state:
64
+ * all operations against this storage instance must use parsed objects from sets
65
+ * returned here, never from an independent parse of the same content.
66
+ */
67
+ getParsedSyncConfigSet(options: ParseSyncConfigOptions): ParsedSyncConfigSet;
68
+
69
+ /**
70
+ * Shortcut for `getParsedSyncConfigSet(options).hydratedSyncConfig`.
71
+ */
72
+ getParsedSyncRules(options: ParseSyncConfigOptions): HydratedSyncConfig;
48
73
 
49
74
  /**
50
75
  * Terminate the replication stream.
@@ -55,7 +80,7 @@ export interface SyncRulesBucketStorage
55
80
  */
56
81
  terminate(options?: TerminateOptions): Promise<void>;
57
82
 
58
- getStatus(): Promise<SyncRuleStatus>;
83
+ getStatus(): Promise<ReplicationStreamStatus>;
59
84
 
60
85
  /**
61
86
  * Clear the storage, without changing state.
@@ -72,12 +97,20 @@ export interface SyncRulesBucketStorage
72
97
  */
73
98
  reportError(e: any): Promise<void>;
74
99
 
100
+ /**
101
+ * Whether parameter compaction can run when {@link CompactOptions.incrementalOnly} is set.
102
+ *
103
+ * Storage implementations that do not support incremental parameter compaction must return
104
+ * false; the compact command will skip parameter compaction in that mode.
105
+ */
106
+ supportsIncrementalParameterCompaction(): boolean;
107
+
75
108
  compact(options?: CompactOptions): Promise<void>;
76
109
 
77
110
  /**
78
- * Lightweight "compact" process to populate the checksum cache, if any.
111
+ * Compact storage after initial replication, before the first checkpoint exists.
79
112
  */
80
- populatePersistentChecksumCache(options: PopulateChecksumCacheOptions): Promise<PopulateChecksumCacheResults>;
113
+ compactInitialReplication(options: CompactInitialReplicationOptions): Promise<CompactInitialReplicationResults>;
81
114
 
82
115
  // ## Read operations
83
116
 
@@ -89,7 +122,7 @@ export interface SyncRulesBucketStorage
89
122
  *
90
123
  * This is a best-effort optimization:
91
124
  * 1. This may include more changes than what actually occurred.
92
- * 2. This may return invalidateDataBuckets or invalidateParameterBuckets instead of of returning
125
+ * 2. This may return invalidateDataBuckets or invalidateParameterBuckets instead of returning
93
126
  * specific changes.
94
127
  * @param options
95
128
  */
@@ -109,15 +142,22 @@ export interface SyncRulesBucketStorage
109
142
  * 1. Separate buckets.
110
143
  * 2. Limit the size of each individual chunk according to options.batchSizeLimitBytes.
111
144
  *
145
+ * The batch may not contain all data for the checkpoint, if the checkpoint is large. The caller must
146
+ * continue querying if either:
147
+ * 1. The last chunk for any bucket has has_more = true.
148
+ * 2. A SyncBucketDataBatchEnd is returned with hasMore = true.
149
+ *
150
+ * The first check can be skipped if a SyncBucketDataBatchEnd is returned with hasMore = false.
151
+ *
112
152
  * @param checkpoint the checkpoint
113
153
  * @param dataBuckets current bucket states
114
154
  * @param options batch size options
115
155
  */
116
156
  getBucketDataBatch(
117
- checkpoint: util.InternalOpId,
157
+ checkpoint: ReplicationCheckpoint,
118
158
  dataBuckets: BucketDataRequest[],
119
159
  options?: BucketDataBatchOptions
120
- ): AsyncIterable<SyncBucketDataChunk>;
160
+ ): AsyncIterable<SyncBucketDataChunk | SyncBucketDataBatchEnd>;
121
161
 
122
162
  /**
123
163
  * Compute checksums for a given list of buckets.
@@ -127,12 +167,22 @@ export interface SyncRulesBucketStorage
127
167
  * This may be slow, depending on the size of the buckets.
128
168
  * The checksums are cached internally to compensate for this, but does not cover all cases.
129
169
  */
130
- getChecksums(checkpoint: util.InternalOpId, buckets: BucketChecksumRequest[]): Promise<util.ChecksumMap>;
170
+ getChecksums(
171
+ checkpoint: ReplicationCheckpoint,
172
+ buckets: BucketChecksumRequest[],
173
+ options?: BucketChecksumOptions
174
+ ): Promise<util.ChecksumMap>;
131
175
 
132
176
  /**
133
177
  * Clear checksum cache. Primarily intended for tests.
134
178
  */
135
179
  clearChecksumCache(): void;
180
+
181
+ /**
182
+ * Optional storage-provider cleanup for sync configs that have been stopped
183
+ * inside this replication stream.
184
+ */
185
+ cleanupStoppedSyncConfigs?(options: CleanupStoppedSyncConfigsOptions): Promise<CleanupStoppedSyncConfigsResult>;
136
186
  }
137
187
 
138
188
  export interface SyncRulesBucketStorageListener {
@@ -149,35 +199,61 @@ export interface BucketChecksumRequest {
149
199
  source: BucketDataSource;
150
200
  }
151
201
 
152
- export interface SyncRuleStatus {
153
- checkpoint_lsn: string | null;
154
- active: boolean;
155
- snapshot_done: boolean;
156
- snapshot_lsn: string | null;
202
+ export interface BucketChecksumOptions {
203
+ requestHint?: BucketRequestHint;
204
+ }
205
+
206
+ export type BucketRequestHint = 'bulk' | 'incremental';
207
+
208
+ export interface ReplicationStreamStatus {
209
+ /**
210
+ * Source position to resume replication from.
211
+ */
212
+ resumeLsn: string | null;
157
213
  /**
158
- * Last persisted operation that must be included in the next checkpoint once checkpointing is unblocked.
214
+ * True if _every_ active/processing sync config has a client-visible checkpoint.
159
215
  */
160
- keepalive_op: util.InternalOpId | null;
216
+ snapshotDone: boolean;
161
217
  }
162
218
  export interface ResolveTablesOptions {
163
219
  connection_id: number;
220
+ /**
221
+ * Source table or collection metadata discovered during snapshot or streaming.
222
+ */
164
223
  source: SourceEntityDescriptor;
224
+ /**
225
+ * Classifies overlapping persisted tables. Defaults to identity-based reconciliation.
226
+ * This may run inside a storage transaction and must not mutate storage.
227
+ */
228
+ reconcileSourceTables?: SourceTableCandidateReconciler;
165
229
  /**
166
230
  * For tests only - custom id generator for stable ids.
167
231
  */
168
232
  idGenerator?: () => string | bson.ObjectId;
169
233
  /**
170
- * For tests only - override the sync rules used.
234
+ * For tests only - override the parsed sync config set used.
171
235
  */
172
- syncRules?: HydratedSyncConfig;
236
+ parsedSyncConfig?: ParsedSyncConfigSet;
173
237
  }
174
238
 
175
239
  export interface ResolveTablesResult {
240
+ /**
241
+ * Current source table mappings that should receive replicated data.
242
+ */
176
243
  tables: SourceTable[];
244
+ /**
245
+ * Outdated source table mappings that should be removed by the connector.
246
+ */
177
247
  dropTables: SourceTable[];
178
248
  }
179
249
 
180
- export interface CreateWriterOptions extends ParseSyncRulesOptions {
250
+ /**
251
+ * Options for creating a storage writer.
252
+ */
253
+ export interface CreateWriterOptions extends ParseSyncConfigOptions {
254
+ /**
255
+ * Source-specific zero or empty replication position.
256
+ */
181
257
  zeroLSN: string;
182
258
  /**
183
259
  * Whether or not to store a copy of the current data.
@@ -209,6 +285,14 @@ export interface CreateWriterOptions extends ParseSyncRulesOptions {
209
285
  tracer?: PerformanceTracer<'storage' | 'evaluate'>;
210
286
 
211
287
  logger?: Logger;
288
+
289
+ /**
290
+ * Aborts long-running storage work started by this writer, such as uploads to object storage.
291
+ *
292
+ * This does not replace flushing or committing at the appropriate source boundary: it only
293
+ * cancels work that is still in flight when replication stops.
294
+ */
295
+ signal?: AbortSignal;
212
296
  }
213
297
 
214
298
  export interface StorageHooks {
@@ -253,6 +337,22 @@ export interface CompactOptions {
253
337
 
254
338
  compactParameterData?: boolean;
255
339
 
340
+ /**
341
+ * Only perform compaction that can be done incrementally.
342
+ *
343
+ * This includes full bucket compaction on MongoDB V3 storage.
344
+ *
345
+ * On MongoDB v1 and Postgres storage, this makes compacting a no-op.
346
+ */
347
+ incrementalOnly?: boolean;
348
+
349
+ /**
350
+ * Delete client-requested write checkpoints created before this time.
351
+ *
352
+ * Generated write checkpoints are not affected.
353
+ */
354
+ deleteCheckpointRequestsBefore?: Date;
355
+
256
356
  /** Minimum of 2 */
257
357
  clearBatchLimit?: number;
258
358
 
@@ -262,6 +362,9 @@ export interface CompactOptions {
262
362
  /** Minimum of 1 */
263
363
  moveBatchQueryLimit?: number;
264
364
 
365
+ /** Byte cap per read batch for streaming compaction. Default 16MB. */
366
+ moveBatchByteLimit?: number;
367
+
265
368
  /**
266
369
  * Minimum number new operations in a bucket to trigger compaction of that bucket.
267
370
  *
@@ -276,6 +379,18 @@ export interface CompactOptions {
276
379
  */
277
380
  minChangeRatio?: number;
278
381
 
382
+ /** Minimum delay before a V3 bucket is checked for chunk compaction. Default: five minutes. */
383
+ minCompactChunkIntervalMs?: number;
384
+
385
+ /** Minimum elapsed write pressure before the v3 sliding-scale full compact. Default: two hours. */
386
+ minCompactFullIntervalMs?: number;
387
+
388
+ /** Maximum age of writes not covered by a v3 full compact. */
389
+ maxCompactFullIntervalMs?: number;
390
+
391
+ /** How long a v3 worker owns a claimed bucket before another worker may recover it. */
392
+ compactLeaseDurationMs?: number;
393
+
279
394
  /**
280
395
  * Internal/testing use: Cache size for compacting parameters.
281
396
  */
@@ -286,15 +401,22 @@ export interface CompactOptions {
286
401
  logger?: Logger;
287
402
  }
288
403
 
289
- export interface PopulateChecksumCacheOptions {
290
- maxOpId: util.InternalOpId;
404
+ export interface CompactInitialReplicationOptions {
405
+ /**
406
+ * Compact data up to this op id.
407
+ *
408
+ * Defaults to the highest persisted op id for the replication stream, which covers
409
+ * the common case of compacting right after initial replication, before
410
+ * the first checkpoint exists.
411
+ */
412
+ maxOpId?: util.InternalOpId;
291
413
  minBucketChanges?: number;
292
414
  signal?: AbortSignal;
293
415
  }
294
416
 
295
- export interface PopulateChecksumCacheResults {
417
+ export interface CompactInitialReplicationResults {
296
418
  /**
297
- * Number of buckets we have calculated checksums for.
419
+ * Number of buckets processed.
298
420
  */
299
421
  buckets: number;
300
422
  }
@@ -303,6 +425,24 @@ export interface ClearStorageOptions {
303
425
  signal?: AbortSignal;
304
426
  }
305
427
 
428
+ export interface CleanupStoppedSyncConfigsOptions {
429
+ signal?: AbortSignal;
430
+ logger?: Logger;
431
+ defaultSchema: string;
432
+ sourceConnectionTag: string;
433
+ }
434
+
435
+ export interface CleanupStoppedSyncConfigsResult {
436
+ stoppedSyncConfigsRemoved: number;
437
+ bucketDataCollectionsDropped: number;
438
+ parameterIndexCollectionsDropped: number;
439
+ /** Best-effort number, not accurate if the operation takes longer than the timeout. */
440
+ bucketStateDocumentsDeleted: number;
441
+ sourceRecordCollectionsDropped: number;
442
+ sourceTablesUpdated: number;
443
+ sourceTablesDeleted: number;
444
+ }
445
+
306
446
  export interface TerminateOptions extends ClearStorageOptions {
307
447
  /**
308
448
  * If true, also clear the storage before terminating.
@@ -311,6 +451,14 @@ export interface TerminateOptions extends ClearStorageOptions {
311
451
  }
312
452
 
313
453
  export interface BucketDataBatchOptions {
454
+ requestHint?: BucketRequestHint;
455
+
456
+ /** Abort any in-progress work for this batch, including object-storage downloads. */
457
+ signal?: AbortSignal;
458
+
459
+ /** Traces data reads performed by the storage implementation. */
460
+ tracer?: PerformanceTracer<string>;
461
+
314
462
  /** Limit number of documents returned. Defaults to 1000. */
315
463
  limit?: number;
316
464
 
@@ -331,6 +479,20 @@ export interface SyncBucketDataChunk {
331
479
  targetOp: util.InternalOpId | null;
332
480
  }
333
481
 
482
+ export interface SyncBucketDataBatchEnd {
483
+ /**
484
+ * True if there may be more data for this checkpoint, and the caller should continue querying.
485
+ *
486
+ * This is different from `SyncBucketDataChunk.has_more`, which is per-bucket. This is a global signal for the
487
+ * entire request, and may be true even if there is no returned chunk with has_more: true.
488
+ */
489
+ hasMore: boolean;
490
+ }
491
+
492
+ export function isBatchEnd(chunk: SyncBucketDataChunk | SyncBucketDataBatchEnd): chunk is SyncBucketDataBatchEnd {
493
+ return (chunk as SyncBucketDataBatchEnd).hasMore !== undefined;
494
+ }
495
+
334
496
  export interface ReplicationCheckpoint {
335
497
  readonly checkpoint: util.InternalOpId;
336
498
  readonly lsn: string | null;
@@ -1,3 +1,5 @@
1
+ import { EventDefinitionId } from '@powersync/service-sync-rules';
2
+
1
3
  export enum WriteCheckpointMode {
2
4
  /**
3
5
  * Raw mappings of `user_id` to `write_checkpoint`s should
@@ -19,7 +21,21 @@ export interface BaseWriteCheckpointIdentifier {
19
21
  user_id: string;
20
22
  }
21
23
 
22
- export interface CustomWriteCheckpointFilters extends BaseWriteCheckpointIdentifier {
24
+ export interface ClientRequestedCheckpointOptions {
25
+ /**
26
+ * Supplied for client-generated checkpoint requests.
27
+ *
28
+ * If omitted, storage creates or stores a regular write checkpoint.
29
+ * If supplied for managed write checkpoints, storage only uses it when it is
30
+ * greater than the stored id for the user_id. Same or lower supplied ids are
31
+ * no-ops, and storage returns the current stored id.
32
+ */
33
+ checkpoint_request_id?: bigint;
34
+ }
35
+
36
+ export type SyncStorageCustomWriteCheckpointFilters = BaseWriteCheckpointIdentifier;
37
+
38
+ export interface CustomWriteCheckpointFilters extends SyncStorageCustomWriteCheckpointFilters {
23
39
  /**
24
40
  * Replication stream which was active when this checkpoint was created.
25
41
  */
@@ -28,9 +44,25 @@ export interface CustomWriteCheckpointFilters extends BaseWriteCheckpointIdentif
28
44
 
29
45
  export interface BatchedCustomWriteCheckpointOptions extends BaseWriteCheckpointIdentifier {
30
46
  /**
31
- * A supplied incrementing Write Checkpoint number
47
+ * A supplied incrementing checkpoint request id. This is still named
48
+ * "write checkpoint" in storage APIs for backwards compatibility.
32
49
  */
33
50
  checkpoint: bigint;
51
+ /**
52
+ * Compiled event definition which created the checkpoint.
53
+ *
54
+ * Incremental replication uses this to keep checkpoint records produced by
55
+ * active and processing sync configs separate. This is required by v3
56
+ * storage and unused by legacy storage versions.
57
+ */
58
+ event_id?: EventDefinitionId;
59
+ /**
60
+ * Required when this custom checkpoint was created from a client checkpoint
61
+ * request and should be eligible for checkpoint request retention cleanup.
62
+ * Omit or set to null for persistent custom checkpoints owned by the source
63
+ * or integration.
64
+ */
65
+ checkpoint_requested_at?: Date | null;
34
66
  }
35
67
 
36
68
  export interface CustomWriteCheckpointOptions extends BatchedCustomWriteCheckpointOptions {
@@ -50,15 +82,117 @@ export interface ManagedWriteCheckpointFilters extends BaseWriteCheckpointIdenti
50
82
  heads: Record<string, string>;
51
83
  }
52
84
 
53
- export type ManagedWriteCheckpointOptions = ManagedWriteCheckpointFilters;
85
+ export type ManagedWriteCheckpointOptions = ManagedWriteCheckpointFilters & ClientRequestedCheckpointOptions;
86
+
87
+ export interface CreateManagedWriteCheckpointsResult {
88
+ /**
89
+ * Current managed checkpoint id for each full user_id after applying the batch.
90
+ */
91
+ writeCheckpoints: Map<string, bigint>;
92
+ /**
93
+ * True when the source marker must be forced so replication observes the
94
+ * stored head. Callers use this to advance the source marker once for the
95
+ * whole batch.
96
+ *
97
+ * This must be true whenever a checkpoint in the batch has not yet been
98
+ * processed by replication - both freshly created/advanced checkpoints and
99
+ * existing checkpoints matched by a stale or duplicate request that are still
100
+ * pending. Already-processed checkpoints do not need a new marker.
101
+ *
102
+ * The case this guards against is a lost source marker on a stale retry. The
103
+ * source marker is forced after the checkpoint is persisted, so the two are
104
+ * not atomic (storage and source are usually different databases). Without
105
+ * this flag, the following sequence strands the checkpoint:
106
+ *
107
+ * 1. A client-supplied checkpoint id is persisted (write checkpoint stored,
108
+ * processed_at_lsn null).
109
+ * 2. Forcing the source marker fails, so the request errors and the client
110
+ * retries with the same id.
111
+ * 3. The retry is a no-op for the stored id (monotonic, not greater), so if
112
+ * we only advanced on "a row changed" we would skip the marker.
113
+ * 4. On an idle source nothing else moves replication past the stored head,
114
+ * so the checkpoint never gets acknowledged and the client waits forever.
115
+ *
116
+ * Reporting true while the checkpoint is still pending makes the retry re-force
117
+ * the marker, which resolves the wait.
118
+ *
119
+ * Backends that do not track a per-row processed indicator may conservatively
120
+ * report true whenever any checkpoint was matched.
121
+ */
122
+ shouldAdvance: boolean;
123
+ }
124
+
125
+ export function uniqueManagedWriteCheckpoints(
126
+ checkpoints: ManagedWriteCheckpointOptions[]
127
+ ): ManagedWriteCheckpointOptions[] {
128
+ const byUser = new Map<string, ManagedWriteCheckpointOptions>();
129
+
130
+ for (const checkpoint of checkpoints) {
131
+ const existing = byUser.get(checkpoint.user_id);
132
+ // A batch can contain entries for many users, and different users may use
133
+ // different request types (generated vs client-supplied). For a single full
134
+ // user_id, though, all entries should be the same type - a batch can still
135
+ // hold multiple entries for that user (e.g. coalesced requests), so we
136
+ // collapse them to one. For client-supplied ids we keep the greatest
137
+ // requested value, so lower stale ids cannot hide the request that should
138
+ // advance storage. For generated ids the entries are equivalent, so any one
139
+ // is kept.
140
+ //
141
+ // Mixing generated and supplied entries for the same user_id is not expected;
142
+ // shouldReplaceManagedWriteCheckpoint still resolves it deterministically
143
+ // (supplied wins) if that invariant is ever violated.
144
+ if (existing == null || shouldReplaceManagedWriteCheckpoint(existing, checkpoint)) {
145
+ byUser.set(checkpoint.user_id, checkpoint);
146
+ }
147
+ }
148
+
149
+ return [...byUser.values()];
150
+ }
54
151
 
55
- export type SyncStorageLastWriteCheckpointFilters = BaseWriteCheckpointIdentifier | ManagedWriteCheckpointFilters;
152
+ function shouldReplaceManagedWriteCheckpoint(
153
+ existing: ManagedWriteCheckpointOptions,
154
+ candidate: ManagedWriteCheckpointOptions
155
+ ) {
156
+ const existingRequestId = existing.checkpoint_request_id;
157
+ const candidateRequestId = candidate.checkpoint_request_id;
158
+
159
+ if (candidateRequestId == null) {
160
+ return existingRequestId == null;
161
+ }
162
+
163
+ if (existingRequestId == null) {
164
+ return true;
165
+ }
166
+
167
+ return candidateRequestId > existingRequestId;
168
+ }
169
+
170
+ export type SyncStorageLastWriteCheckpointFilters =
171
+ | SyncStorageCustomWriteCheckpointFilters
172
+ | ManagedWriteCheckpointFilters;
56
173
  export type LastWriteCheckpointFilters = CustomWriteCheckpointFilters | ManagedWriteCheckpointFilters;
57
174
 
175
+ /** Configures checkpoint ownership and generation for a storage instance. */
176
+ export type WriteCheckpointModeConfig =
177
+ | {
178
+ mode: WriteCheckpointMode.CUSTOM;
179
+ /**
180
+ * Name of the event whose custom checkpoints are served. Storage resolves this name through the active sync
181
+ * config's persisted mapping; integrations must not construct or persist event ids themselves. Required by
182
+ * storage implementations that scope custom checkpoints by event definition.
183
+ */
184
+ eventName?: string;
185
+ }
186
+ | {
187
+ mode: WriteCheckpointMode.MANAGED;
188
+ };
189
+
58
190
  export interface BaseWriteCheckpointAPI {
59
191
  readonly writeCheckpointMode: WriteCheckpointMode;
60
- setWriteCheckpointMode(mode: WriteCheckpointMode): void;
61
- createManagedWriteCheckpoint(checkpoint: ManagedWriteCheckpointOptions): Promise<bigint>;
192
+ setWriteCheckpointMode(config: WriteCheckpointModeConfig): void;
193
+ createManagedWriteCheckpoints(
194
+ checkpoints: ManagedWriteCheckpointOptions[]
195
+ ): Promise<CreateManagedWriteCheckpointsResult>;
62
196
  }
63
197
 
64
198
  /**