@powersync/service-core 0.0.2

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 (412) hide show
  1. package/.probes/.gitkeep +0 -0
  2. package/CHANGELOG.md +13 -0
  3. package/LICENSE +67 -0
  4. package/README.md +3 -0
  5. package/dist/api/api-index.d.ts +2 -0
  6. package/dist/api/api-index.js +3 -0
  7. package/dist/api/api-index.js.map +1 -0
  8. package/dist/api/diagnostics.d.ts +21 -0
  9. package/dist/api/diagnostics.js +183 -0
  10. package/dist/api/diagnostics.js.map +1 -0
  11. package/dist/api/schema.d.ts +5 -0
  12. package/dist/api/schema.js +88 -0
  13. package/dist/api/schema.js.map +1 -0
  14. package/dist/auth/CachedKeyCollector.d.ts +46 -0
  15. package/dist/auth/CachedKeyCollector.js +116 -0
  16. package/dist/auth/CachedKeyCollector.js.map +1 -0
  17. package/dist/auth/CompoundKeyCollector.d.ts +8 -0
  18. package/dist/auth/CompoundKeyCollector.js +23 -0
  19. package/dist/auth/CompoundKeyCollector.js.map +1 -0
  20. package/dist/auth/JwtPayload.d.ts +10 -0
  21. package/dist/auth/JwtPayload.js +2 -0
  22. package/dist/auth/JwtPayload.js.map +1 -0
  23. package/dist/auth/KeyCollector.d.ts +24 -0
  24. package/dist/auth/KeyCollector.js +2 -0
  25. package/dist/auth/KeyCollector.js.map +1 -0
  26. package/dist/auth/KeySpec.d.ts +26 -0
  27. package/dist/auth/KeySpec.js +49 -0
  28. package/dist/auth/KeySpec.js.map +1 -0
  29. package/dist/auth/KeyStore.d.ts +39 -0
  30. package/dist/auth/KeyStore.js +131 -0
  31. package/dist/auth/KeyStore.js.map +1 -0
  32. package/dist/auth/LeakyBucket.d.ts +39 -0
  33. package/dist/auth/LeakyBucket.js +57 -0
  34. package/dist/auth/LeakyBucket.js.map +1 -0
  35. package/dist/auth/RemoteJWKSCollector.d.ts +24 -0
  36. package/dist/auth/RemoteJWKSCollector.js +106 -0
  37. package/dist/auth/RemoteJWKSCollector.js.map +1 -0
  38. package/dist/auth/StaticKeyCollector.d.ts +14 -0
  39. package/dist/auth/StaticKeyCollector.js +19 -0
  40. package/dist/auth/StaticKeyCollector.js.map +1 -0
  41. package/dist/auth/SupabaseKeyCollector.d.ts +22 -0
  42. package/dist/auth/SupabaseKeyCollector.js +61 -0
  43. package/dist/auth/SupabaseKeyCollector.js.map +1 -0
  44. package/dist/auth/auth-index.d.ts +10 -0
  45. package/dist/auth/auth-index.js +11 -0
  46. package/dist/auth/auth-index.js.map +1 -0
  47. package/dist/db/db-index.d.ts +1 -0
  48. package/dist/db/db-index.js +2 -0
  49. package/dist/db/db-index.js.map +1 -0
  50. package/dist/db/mongo.d.ts +29 -0
  51. package/dist/db/mongo.js +65 -0
  52. package/dist/db/mongo.js.map +1 -0
  53. package/dist/entry/cli-entry.d.ts +15 -0
  54. package/dist/entry/cli-entry.js +36 -0
  55. package/dist/entry/cli-entry.js.map +1 -0
  56. package/dist/entry/commands/config-command.d.ts +10 -0
  57. package/dist/entry/commands/config-command.js +21 -0
  58. package/dist/entry/commands/config-command.js.map +1 -0
  59. package/dist/entry/commands/migrate-action.d.ts +2 -0
  60. package/dist/entry/commands/migrate-action.js +18 -0
  61. package/dist/entry/commands/migrate-action.js.map +1 -0
  62. package/dist/entry/commands/start-action.d.ts +3 -0
  63. package/dist/entry/commands/start-action.js +15 -0
  64. package/dist/entry/commands/start-action.js.map +1 -0
  65. package/dist/entry/commands/teardown-action.d.ts +2 -0
  66. package/dist/entry/commands/teardown-action.js +17 -0
  67. package/dist/entry/commands/teardown-action.js.map +1 -0
  68. package/dist/entry/entry-index.d.ts +5 -0
  69. package/dist/entry/entry-index.js +6 -0
  70. package/dist/entry/entry-index.js.map +1 -0
  71. package/dist/index.d.ts +24 -0
  72. package/dist/index.js +26 -0
  73. package/dist/index.js.map +1 -0
  74. package/dist/metrics/metrics.d.ts +16 -0
  75. package/dist/metrics/metrics.js +139 -0
  76. package/dist/metrics/metrics.js.map +1 -0
  77. package/dist/migrations/db/migrations/1684951997326-init.d.ts +3 -0
  78. package/dist/migrations/db/migrations/1684951997326-init.js +31 -0
  79. package/dist/migrations/db/migrations/1684951997326-init.js.map +1 -0
  80. package/dist/migrations/db/migrations/1688556755264-initial-sync-rules.d.ts +2 -0
  81. package/dist/migrations/db/migrations/1688556755264-initial-sync-rules.js +5 -0
  82. package/dist/migrations/db/migrations/1688556755264-initial-sync-rules.js.map +1 -0
  83. package/dist/migrations/db/migrations/1702295701188-sync-rule-state.d.ts +3 -0
  84. package/dist/migrations/db/migrations/1702295701188-sync-rule-state.js +54 -0
  85. package/dist/migrations/db/migrations/1702295701188-sync-rule-state.js.map +1 -0
  86. package/dist/migrations/db/migrations/1711543888062-write-checkpoint-index.d.ts +3 -0
  87. package/dist/migrations/db/migrations/1711543888062-write-checkpoint-index.js +27 -0
  88. package/dist/migrations/db/migrations/1711543888062-write-checkpoint-index.js.map +1 -0
  89. package/dist/migrations/db/store.d.ts +3 -0
  90. package/dist/migrations/db/store.js +10 -0
  91. package/dist/migrations/db/store.js.map +1 -0
  92. package/dist/migrations/migrations.d.ts +10 -0
  93. package/dist/migrations/migrations.js +94 -0
  94. package/dist/migrations/migrations.js.map +1 -0
  95. package/dist/replication/ErrorRateLimiter.d.ts +17 -0
  96. package/dist/replication/ErrorRateLimiter.js +42 -0
  97. package/dist/replication/ErrorRateLimiter.js.map +1 -0
  98. package/dist/replication/PgRelation.d.ts +16 -0
  99. package/dist/replication/PgRelation.js +26 -0
  100. package/dist/replication/PgRelation.js.map +1 -0
  101. package/dist/replication/WalConnection.d.ts +34 -0
  102. package/dist/replication/WalConnection.js +190 -0
  103. package/dist/replication/WalConnection.js.map +1 -0
  104. package/dist/replication/WalStream.d.ts +58 -0
  105. package/dist/replication/WalStream.js +517 -0
  106. package/dist/replication/WalStream.js.map +1 -0
  107. package/dist/replication/WalStreamManager.d.ts +30 -0
  108. package/dist/replication/WalStreamManager.js +199 -0
  109. package/dist/replication/WalStreamManager.js.map +1 -0
  110. package/dist/replication/WalStreamRunner.d.ts +38 -0
  111. package/dist/replication/WalStreamRunner.js +155 -0
  112. package/dist/replication/WalStreamRunner.js.map +1 -0
  113. package/dist/replication/replication-index.d.ts +7 -0
  114. package/dist/replication/replication-index.js +8 -0
  115. package/dist/replication/replication-index.js.map +1 -0
  116. package/dist/replication/util.d.ts +9 -0
  117. package/dist/replication/util.js +62 -0
  118. package/dist/replication/util.js.map +1 -0
  119. package/dist/routes/admin.d.ts +7 -0
  120. package/dist/routes/admin.js +192 -0
  121. package/dist/routes/admin.js.map +1 -0
  122. package/dist/routes/auth.d.ts +58 -0
  123. package/dist/routes/auth.js +182 -0
  124. package/dist/routes/auth.js.map +1 -0
  125. package/dist/routes/checkpointing.d.ts +3 -0
  126. package/dist/routes/checkpointing.js +30 -0
  127. package/dist/routes/checkpointing.js.map +1 -0
  128. package/dist/routes/dev.d.ts +6 -0
  129. package/dist/routes/dev.js +163 -0
  130. package/dist/routes/dev.js.map +1 -0
  131. package/dist/routes/route-generators.d.ts +15 -0
  132. package/dist/routes/route-generators.js +32 -0
  133. package/dist/routes/route-generators.js.map +1 -0
  134. package/dist/routes/router-socket.d.ts +10 -0
  135. package/dist/routes/router-socket.js +5 -0
  136. package/dist/routes/router-socket.js.map +1 -0
  137. package/dist/routes/router.d.ts +13 -0
  138. package/dist/routes/router.js +2 -0
  139. package/dist/routes/router.js.map +1 -0
  140. package/dist/routes/routes-index.d.ts +4 -0
  141. package/dist/routes/routes-index.js +5 -0
  142. package/dist/routes/routes-index.js.map +1 -0
  143. package/dist/routes/socket-route.d.ts +2 -0
  144. package/dist/routes/socket-route.js +119 -0
  145. package/dist/routes/socket-route.js.map +1 -0
  146. package/dist/routes/sync-rules.d.ts +6 -0
  147. package/dist/routes/sync-rules.js +182 -0
  148. package/dist/routes/sync-rules.js.map +1 -0
  149. package/dist/routes/sync-stream.d.ts +5 -0
  150. package/dist/routes/sync-stream.js +74 -0
  151. package/dist/routes/sync-stream.js.map +1 -0
  152. package/dist/runner/teardown.d.ts +2 -0
  153. package/dist/runner/teardown.js +79 -0
  154. package/dist/runner/teardown.js.map +1 -0
  155. package/dist/storage/BucketStorage.d.ts +298 -0
  156. package/dist/storage/BucketStorage.js +25 -0
  157. package/dist/storage/BucketStorage.js.map +1 -0
  158. package/dist/storage/MongoBucketStorage.d.ts +51 -0
  159. package/dist/storage/MongoBucketStorage.js +388 -0
  160. package/dist/storage/MongoBucketStorage.js.map +1 -0
  161. package/dist/storage/SourceTable.d.ts +39 -0
  162. package/dist/storage/SourceTable.js +50 -0
  163. package/dist/storage/SourceTable.js.map +1 -0
  164. package/dist/storage/mongo/MongoBucketBatch.d.ts +48 -0
  165. package/dist/storage/mongo/MongoBucketBatch.js +584 -0
  166. package/dist/storage/mongo/MongoBucketBatch.js.map +1 -0
  167. package/dist/storage/mongo/MongoIdSequence.d.ts +12 -0
  168. package/dist/storage/mongo/MongoIdSequence.js +21 -0
  169. package/dist/storage/mongo/MongoIdSequence.js.map +1 -0
  170. package/dist/storage/mongo/MongoPersistedSyncRules.d.ts +9 -0
  171. package/dist/storage/mongo/MongoPersistedSyncRules.js +9 -0
  172. package/dist/storage/mongo/MongoPersistedSyncRules.js.map +1 -0
  173. package/dist/storage/mongo/MongoPersistedSyncRulesContent.d.ts +20 -0
  174. package/dist/storage/mongo/MongoPersistedSyncRulesContent.js +26 -0
  175. package/dist/storage/mongo/MongoPersistedSyncRulesContent.js.map +1 -0
  176. package/dist/storage/mongo/MongoSyncBucketStorage.d.ts +27 -0
  177. package/dist/storage/mongo/MongoSyncBucketStorage.js +379 -0
  178. package/dist/storage/mongo/MongoSyncBucketStorage.js.map +1 -0
  179. package/dist/storage/mongo/MongoSyncRulesLock.d.ts +16 -0
  180. package/dist/storage/mongo/MongoSyncRulesLock.js +65 -0
  181. package/dist/storage/mongo/MongoSyncRulesLock.js.map +1 -0
  182. package/dist/storage/mongo/OperationBatch.d.ts +26 -0
  183. package/dist/storage/mongo/OperationBatch.js +101 -0
  184. package/dist/storage/mongo/OperationBatch.js.map +1 -0
  185. package/dist/storage/mongo/PersistedBatch.d.ts +42 -0
  186. package/dist/storage/mongo/PersistedBatch.js +200 -0
  187. package/dist/storage/mongo/PersistedBatch.js.map +1 -0
  188. package/dist/storage/mongo/db.d.ts +23 -0
  189. package/dist/storage/mongo/db.js +34 -0
  190. package/dist/storage/mongo/db.js.map +1 -0
  191. package/dist/storage/mongo/models.d.ts +137 -0
  192. package/dist/storage/mongo/models.js +27 -0
  193. package/dist/storage/mongo/models.js.map +1 -0
  194. package/dist/storage/mongo/util.d.ts +26 -0
  195. package/dist/storage/mongo/util.js +81 -0
  196. package/dist/storage/mongo/util.js.map +1 -0
  197. package/dist/storage/storage-index.d.ts +14 -0
  198. package/dist/storage/storage-index.js +15 -0
  199. package/dist/storage/storage-index.js.map +1 -0
  200. package/dist/sync/BroadcastIterable.d.ts +38 -0
  201. package/dist/sync/BroadcastIterable.js +153 -0
  202. package/dist/sync/BroadcastIterable.js.map +1 -0
  203. package/dist/sync/LastValueSink.d.ts +25 -0
  204. package/dist/sync/LastValueSink.js +84 -0
  205. package/dist/sync/LastValueSink.js.map +1 -0
  206. package/dist/sync/merge.d.ts +39 -0
  207. package/dist/sync/merge.js +175 -0
  208. package/dist/sync/merge.js.map +1 -0
  209. package/dist/sync/safeRace.d.ts +1 -0
  210. package/dist/sync/safeRace.js +91 -0
  211. package/dist/sync/safeRace.js.map +1 -0
  212. package/dist/sync/sync-index.d.ts +6 -0
  213. package/dist/sync/sync-index.js +7 -0
  214. package/dist/sync/sync-index.js.map +1 -0
  215. package/dist/sync/sync.d.ts +18 -0
  216. package/dist/sync/sync.js +248 -0
  217. package/dist/sync/sync.js.map +1 -0
  218. package/dist/sync/util.d.ts +26 -0
  219. package/dist/sync/util.js +73 -0
  220. package/dist/sync/util.js.map +1 -0
  221. package/dist/system/CorePowerSyncSystem.d.ts +18 -0
  222. package/dist/system/CorePowerSyncSystem.js +28 -0
  223. package/dist/system/CorePowerSyncSystem.js.map +1 -0
  224. package/dist/util/Mutex.d.ts +47 -0
  225. package/dist/util/Mutex.js +132 -0
  226. package/dist/util/Mutex.js.map +1 -0
  227. package/dist/util/PgManager.d.ts +24 -0
  228. package/dist/util/PgManager.js +55 -0
  229. package/dist/util/PgManager.js.map +1 -0
  230. package/dist/util/alerting.d.ts +4 -0
  231. package/dist/util/alerting.js +14 -0
  232. package/dist/util/alerting.js.map +1 -0
  233. package/dist/util/config/collectors/config-collector.d.ts +29 -0
  234. package/dist/util/config/collectors/config-collector.js +116 -0
  235. package/dist/util/config/collectors/config-collector.js.map +1 -0
  236. package/dist/util/config/collectors/impl/base64-config-collector.d.ts +6 -0
  237. package/dist/util/config/collectors/impl/base64-config-collector.js +15 -0
  238. package/dist/util/config/collectors/impl/base64-config-collector.js.map +1 -0
  239. package/dist/util/config/collectors/impl/fallback-config-collector.d.ts +11 -0
  240. package/dist/util/config/collectors/impl/fallback-config-collector.js +19 -0
  241. package/dist/util/config/collectors/impl/fallback-config-collector.js.map +1 -0
  242. package/dist/util/config/collectors/impl/filesystem-config-collector.d.ts +6 -0
  243. package/dist/util/config/collectors/impl/filesystem-config-collector.js +35 -0
  244. package/dist/util/config/collectors/impl/filesystem-config-collector.js.map +1 -0
  245. package/dist/util/config/compound-config-collector.d.ts +32 -0
  246. package/dist/util/config/compound-config-collector.js +126 -0
  247. package/dist/util/config/compound-config-collector.js.map +1 -0
  248. package/dist/util/config/sync-rules/impl/base64-sync-rules-collector.d.ts +7 -0
  249. package/dist/util/config/sync-rules/impl/base64-sync-rules-collector.js +17 -0
  250. package/dist/util/config/sync-rules/impl/base64-sync-rules-collector.js.map +1 -0
  251. package/dist/util/config/sync-rules/impl/filesystem-sync-rules-collector.d.ts +7 -0
  252. package/dist/util/config/sync-rules/impl/filesystem-sync-rules-collector.js +21 -0
  253. package/dist/util/config/sync-rules/impl/filesystem-sync-rules-collector.js.map +1 -0
  254. package/dist/util/config/sync-rules/impl/inline-sync-rules-collector.d.ts +7 -0
  255. package/dist/util/config/sync-rules/impl/inline-sync-rules-collector.js +17 -0
  256. package/dist/util/config/sync-rules/impl/inline-sync-rules-collector.js.map +1 -0
  257. package/dist/util/config/sync-rules/sync-collector.d.ts +6 -0
  258. package/dist/util/config/sync-rules/sync-collector.js +3 -0
  259. package/dist/util/config/sync-rules/sync-collector.js.map +1 -0
  260. package/dist/util/config/types.d.ts +53 -0
  261. package/dist/util/config/types.js +7 -0
  262. package/dist/util/config/types.js.map +1 -0
  263. package/dist/util/config.d.ts +7 -0
  264. package/dist/util/config.js +35 -0
  265. package/dist/util/config.js.map +1 -0
  266. package/dist/util/env.d.ts +10 -0
  267. package/dist/util/env.js +25 -0
  268. package/dist/util/env.js.map +1 -0
  269. package/dist/util/memory-tracking.d.ts +7 -0
  270. package/dist/util/memory-tracking.js +58 -0
  271. package/dist/util/memory-tracking.js.map +1 -0
  272. package/dist/util/migration_lib.d.ts +11 -0
  273. package/dist/util/migration_lib.js +64 -0
  274. package/dist/util/migration_lib.js.map +1 -0
  275. package/dist/util/pgwire_utils.d.ts +24 -0
  276. package/dist/util/pgwire_utils.js +117 -0
  277. package/dist/util/pgwire_utils.js.map +1 -0
  278. package/dist/util/populate_test_data.d.ts +8 -0
  279. package/dist/util/populate_test_data.js +65 -0
  280. package/dist/util/populate_test_data.js.map +1 -0
  281. package/dist/util/protocol-types.d.ts +178 -0
  282. package/dist/util/protocol-types.js +38 -0
  283. package/dist/util/protocol-types.js.map +1 -0
  284. package/dist/util/secs.d.ts +2 -0
  285. package/dist/util/secs.js +49 -0
  286. package/dist/util/secs.js.map +1 -0
  287. package/dist/util/util-index.d.ts +22 -0
  288. package/dist/util/util-index.js +23 -0
  289. package/dist/util/util-index.js.map +1 -0
  290. package/dist/util/utils.d.ts +14 -0
  291. package/dist/util/utils.js +75 -0
  292. package/dist/util/utils.js.map +1 -0
  293. package/package.json +55 -0
  294. package/src/api/api-index.ts +2 -0
  295. package/src/api/diagnostics.ts +221 -0
  296. package/src/api/schema.ts +99 -0
  297. package/src/auth/CachedKeyCollector.ts +132 -0
  298. package/src/auth/CompoundKeyCollector.ts +33 -0
  299. package/src/auth/JwtPayload.ts +11 -0
  300. package/src/auth/KeyCollector.ts +27 -0
  301. package/src/auth/KeySpec.ts +67 -0
  302. package/src/auth/KeyStore.ts +156 -0
  303. package/src/auth/LeakyBucket.ts +66 -0
  304. package/src/auth/RemoteJWKSCollector.ts +130 -0
  305. package/src/auth/StaticKeyCollector.ts +21 -0
  306. package/src/auth/SupabaseKeyCollector.ts +67 -0
  307. package/src/auth/auth-index.ts +10 -0
  308. package/src/db/db-index.ts +1 -0
  309. package/src/db/mongo.ts +72 -0
  310. package/src/entry/cli-entry.ts +41 -0
  311. package/src/entry/commands/config-command.ts +36 -0
  312. package/src/entry/commands/migrate-action.ts +25 -0
  313. package/src/entry/commands/start-action.ts +24 -0
  314. package/src/entry/commands/teardown-action.ts +23 -0
  315. package/src/entry/entry-index.ts +5 -0
  316. package/src/index.ts +37 -0
  317. package/src/metrics/metrics.ts +169 -0
  318. package/src/migrations/db/migrations/1684951997326-init.ts +33 -0
  319. package/src/migrations/db/migrations/1688556755264-initial-sync-rules.ts +5 -0
  320. package/src/migrations/db/migrations/1702295701188-sync-rule-state.ts +99 -0
  321. package/src/migrations/db/migrations/1711543888062-write-checkpoint-index.ts +32 -0
  322. package/src/migrations/db/store.ts +11 -0
  323. package/src/migrations/migrations.ts +122 -0
  324. package/src/replication/ErrorRateLimiter.ts +49 -0
  325. package/src/replication/PgRelation.ts +42 -0
  326. package/src/replication/WalConnection.ts +227 -0
  327. package/src/replication/WalStream.ts +626 -0
  328. package/src/replication/WalStreamManager.ts +214 -0
  329. package/src/replication/WalStreamRunner.ts +180 -0
  330. package/src/replication/replication-index.ts +7 -0
  331. package/src/replication/util.ts +76 -0
  332. package/src/routes/admin.ts +229 -0
  333. package/src/routes/auth.ts +209 -0
  334. package/src/routes/checkpointing.ts +38 -0
  335. package/src/routes/dev.ts +194 -0
  336. package/src/routes/route-generators.ts +39 -0
  337. package/src/routes/router-socket.ts +13 -0
  338. package/src/routes/router.ts +17 -0
  339. package/src/routes/routes-index.ts +5 -0
  340. package/src/routes/socket-route.ts +131 -0
  341. package/src/routes/sync-rules.ts +210 -0
  342. package/src/routes/sync-stream.ts +92 -0
  343. package/src/runner/teardown.ts +91 -0
  344. package/src/storage/BucketStorage.ts +386 -0
  345. package/src/storage/MongoBucketStorage.ts +493 -0
  346. package/src/storage/SourceTable.ts +60 -0
  347. package/src/storage/mongo/MongoBucketBatch.ts +756 -0
  348. package/src/storage/mongo/MongoIdSequence.ts +24 -0
  349. package/src/storage/mongo/MongoPersistedSyncRules.ts +16 -0
  350. package/src/storage/mongo/MongoPersistedSyncRulesContent.ts +47 -0
  351. package/src/storage/mongo/MongoSyncBucketStorage.ts +517 -0
  352. package/src/storage/mongo/MongoSyncRulesLock.ts +81 -0
  353. package/src/storage/mongo/OperationBatch.ts +115 -0
  354. package/src/storage/mongo/PersistedBatch.ts +245 -0
  355. package/src/storage/mongo/db.ts +69 -0
  356. package/src/storage/mongo/models.ts +157 -0
  357. package/src/storage/mongo/util.ts +88 -0
  358. package/src/storage/storage-index.ts +15 -0
  359. package/src/sync/BroadcastIterable.ts +161 -0
  360. package/src/sync/LastValueSink.ts +100 -0
  361. package/src/sync/merge.ts +200 -0
  362. package/src/sync/safeRace.ts +99 -0
  363. package/src/sync/sync-index.ts +6 -0
  364. package/src/sync/sync.ts +312 -0
  365. package/src/sync/util.ts +98 -0
  366. package/src/system/CorePowerSyncSystem.ts +43 -0
  367. package/src/util/Mutex.ts +159 -0
  368. package/src/util/PgManager.ts +64 -0
  369. package/src/util/alerting.ts +17 -0
  370. package/src/util/config/collectors/config-collector.ts +141 -0
  371. package/src/util/config/collectors/impl/base64-config-collector.ts +18 -0
  372. package/src/util/config/collectors/impl/fallback-config-collector.ts +22 -0
  373. package/src/util/config/collectors/impl/filesystem-config-collector.ts +41 -0
  374. package/src/util/config/compound-config-collector.ts +171 -0
  375. package/src/util/config/sync-rules/impl/base64-sync-rules-collector.ts +21 -0
  376. package/src/util/config/sync-rules/impl/filesystem-sync-rules-collector.ts +26 -0
  377. package/src/util/config/sync-rules/impl/inline-sync-rules-collector.ts +21 -0
  378. package/src/util/config/sync-rules/sync-collector.ts +8 -0
  379. package/src/util/config/types.ts +60 -0
  380. package/src/util/config.ts +39 -0
  381. package/src/util/env.ts +28 -0
  382. package/src/util/memory-tracking.ts +67 -0
  383. package/src/util/migration_lib.ts +79 -0
  384. package/src/util/pgwire_utils.ts +139 -0
  385. package/src/util/populate_test_data.ts +78 -0
  386. package/src/util/protocol-types.ts +223 -0
  387. package/src/util/secs.ts +54 -0
  388. package/src/util/util-index.ts +25 -0
  389. package/src/util/utils.ts +102 -0
  390. package/test/src/__snapshots__/pg_test.test.ts.snap +256 -0
  391. package/test/src/__snapshots__/sync.test.ts.snap +235 -0
  392. package/test/src/auth.test.ts +340 -0
  393. package/test/src/broadcast_iterable.test.ts +156 -0
  394. package/test/src/data_storage.test.ts +1176 -0
  395. package/test/src/env.ts +8 -0
  396. package/test/src/large_batch.test.ts +194 -0
  397. package/test/src/merge_iterable.test.ts +355 -0
  398. package/test/src/pg_test.test.ts +432 -0
  399. package/test/src/schema_changes.test.ts +545 -0
  400. package/test/src/slow_tests.test.ts +257 -0
  401. package/test/src/sql_functions.test.ts +254 -0
  402. package/test/src/sql_operators.test.ts +132 -0
  403. package/test/src/sync.test.ts +293 -0
  404. package/test/src/sync_rules.test.ts +1051 -0
  405. package/test/src/util.ts +67 -0
  406. package/test/src/validation.test.ts +63 -0
  407. package/test/src/wal_stream.test.ts +310 -0
  408. package/test/src/wal_stream_utils.ts +147 -0
  409. package/test/tsconfig.json +20 -0
  410. package/tsconfig.json +20 -0
  411. package/tsconfig.tsbuildinfo +1 -0
  412. package/vitest.config.ts +11 -0
@@ -0,0 +1,545 @@
1
+ import { describe, expect, test } from 'vitest';
2
+ import { BucketStorageFactory } from '../../src/storage/BucketStorage.js';
3
+ import { MONGO_STORAGE_FACTORY } from './util.js';
4
+ import { compareIds, putOp, removeOp, walStreamTest } from './wal_stream_utils.js';
5
+
6
+ type StorageFactory = () => Promise<BucketStorageFactory>;
7
+
8
+ describe(
9
+ 'schema changes',
10
+ function () {
11
+ defineTests(MONGO_STORAGE_FACTORY);
12
+ },
13
+ { timeout: 20_000 }
14
+ );
15
+
16
+ const BASIC_SYNC_RULES = `
17
+ bucket_definitions:
18
+ global:
19
+ data:
20
+ - SELECT id, * FROM "test_data"
21
+ `;
22
+
23
+ const PUT_T1 = putOp('test_data', { id: 't1', description: 'test1' });
24
+ const PUT_T2 = putOp('test_data', { id: 't2', description: 'test2' });
25
+ const PUT_T3 = putOp('test_data', { id: 't3', description: 'test3' });
26
+
27
+ const REMOVE_T1 = removeOp('test_data', 't1');
28
+ const REMOVE_T2 = removeOp('test_data', 't2');
29
+
30
+ function defineTests(factory: StorageFactory) {
31
+ test(
32
+ 're-create table',
33
+ walStreamTest(factory, async (context) => {
34
+ // Drop a table and re-create it.
35
+ await context.updateSyncRules(BASIC_SYNC_RULES);
36
+ const { pool } = context;
37
+
38
+ await pool.query(`DROP TABLE IF EXISTS test_data`);
39
+ await pool.query(`CREATE TABLE test_data(id text primary key, description text)`);
40
+ await pool.query(`INSERT INTO test_data(id, description) VALUES('t1', 'test1')`);
41
+
42
+ await context.replicateSnapshot();
43
+ context.startStreaming();
44
+
45
+ await pool.query(`INSERT INTO test_data(id, description) VALUES('t2', 'test2')`);
46
+
47
+ await pool.query(
48
+ { statement: `DROP TABLE test_data` },
49
+ { statement: `CREATE TABLE test_data(id text primary key, description text)` },
50
+ { statement: `INSERT INTO test_data(id, description) VALUES('t3', 'test3')` }
51
+ );
52
+
53
+ const data = await context.getBucketData('global[]');
54
+
55
+ // Initial inserts
56
+ expect(data.slice(0, 2)).toMatchObject([PUT_T1, PUT_T2]);
57
+
58
+ // Truncate - order doesn't matter
59
+ expect(data.slice(2, 4).sort(compareIds)).toMatchObject([REMOVE_T1, REMOVE_T2]);
60
+
61
+ expect(data.slice(4)).toMatchObject([
62
+ // Snapshot insert
63
+ PUT_T3,
64
+ // Replicated insert
65
+ // We may eventually be able to de-duplicate this
66
+ PUT_T3
67
+ ]);
68
+ })
69
+ );
70
+
71
+ test(
72
+ 'add table',
73
+ walStreamTest(factory, async (context) => {
74
+ // Add table after initial replication
75
+ await context.updateSyncRules(BASIC_SYNC_RULES);
76
+ const { pool } = context;
77
+
78
+ await context.replicateSnapshot();
79
+ context.startStreaming();
80
+
81
+ await pool.query(`CREATE TABLE test_data(id text primary key, description text)`);
82
+ await pool.query(`INSERT INTO test_data(id, description) VALUES('t1', 'test1')`);
83
+
84
+ const data = await context.getBucketData('global[]');
85
+
86
+ expect(data).toMatchObject([
87
+ // Snapshot insert
88
+ PUT_T1,
89
+ // Replicated insert
90
+ // We may eventually be able to de-duplicate this
91
+ PUT_T1
92
+ ]);
93
+ })
94
+ );
95
+
96
+ test(
97
+ 'rename table (1)',
98
+ walStreamTest(factory, async (context) => {
99
+ const { pool } = context;
100
+
101
+ await context.updateSyncRules(BASIC_SYNC_RULES);
102
+
103
+ // Rename table not in sync rules -> in sync rules
104
+ await pool.query(`CREATE TABLE test_data_old(id text primary key, description text)`);
105
+ await pool.query(`INSERT INTO test_data_old(id, description) VALUES('t1', 'test1')`);
106
+
107
+ await context.replicateSnapshot();
108
+ context.startStreaming();
109
+
110
+ await pool.query(
111
+ { statement: `ALTER TABLE test_data_old RENAME TO test_data` },
112
+ // We need an operation to detect the change
113
+ { statement: `INSERT INTO test_data(id, description) VALUES('t2', 'test2')` }
114
+ );
115
+
116
+ const data = await context.getBucketData('global[]');
117
+
118
+ expect(data.slice(0, 2).sort(compareIds)).toMatchObject([
119
+ // Snapshot insert
120
+ PUT_T1,
121
+ PUT_T2
122
+ ]);
123
+ expect(data.slice(2)).toMatchObject([
124
+ // Replicated insert
125
+ // We may eventually be able to de-duplicate this
126
+ PUT_T2
127
+ ]);
128
+ })
129
+ );
130
+
131
+ test(
132
+ 'rename table (2)',
133
+ walStreamTest(factory, async (context) => {
134
+ // Rename table in sync rules -> in sync rules
135
+ const { pool } = context;
136
+
137
+ await context.updateSyncRules(`
138
+ bucket_definitions:
139
+ global:
140
+ data:
141
+ - SELECT id, * FROM "test_data%"
142
+ `);
143
+
144
+ await pool.query(`CREATE TABLE test_data1(id text primary key, description text)`);
145
+ await pool.query(`INSERT INTO test_data1(id, description) VALUES('t1', 'test1')`);
146
+
147
+ await context.replicateSnapshot();
148
+ context.startStreaming();
149
+
150
+ await pool.query(
151
+ { statement: `ALTER TABLE test_data1 RENAME TO test_data2` },
152
+ // We need an operation to detect the change
153
+ { statement: `INSERT INTO test_data2(id, description) VALUES('t2', 'test2')` }
154
+ );
155
+
156
+ const data = await context.getBucketData('global[]');
157
+
158
+ expect(data.slice(0, 2)).toMatchObject([
159
+ // Initial replication
160
+ putOp('test_data1', { id: 't1', description: 'test1' }),
161
+ // Initial truncate
162
+ removeOp('test_data1', 't1')
163
+ ]);
164
+
165
+ expect(data.slice(2, 4).sort(compareIds)).toMatchObject([
166
+ // Snapshot insert
167
+ putOp('test_data2', { id: 't1', description: 'test1' }),
168
+ putOp('test_data2', { id: 't2', description: 'test2' })
169
+ ]);
170
+ expect(data.slice(4)).toMatchObject([
171
+ // Replicated insert
172
+ // We may eventually be able to de-duplicate this
173
+ putOp('test_data2', { id: 't2', description: 'test2' })
174
+ ]);
175
+ })
176
+ );
177
+
178
+ test(
179
+ 'rename table (3)',
180
+ walStreamTest(factory, async (context) => {
181
+ // Rename table in sync rules -> not in sync rules
182
+
183
+ const { pool } = context;
184
+
185
+ await context.updateSyncRules(BASIC_SYNC_RULES);
186
+
187
+ await pool.query(`CREATE TABLE test_data(id text primary key, description text)`);
188
+ await pool.query(`INSERT INTO test_data(id, description) VALUES('t1', 'test1')`);
189
+
190
+ await context.replicateSnapshot();
191
+ context.startStreaming();
192
+
193
+ await pool.query(
194
+ { statement: `ALTER TABLE test_data RENAME TO test_data_na` },
195
+ // We need an operation to detect the change
196
+ { statement: `INSERT INTO test_data_na(id, description) VALUES('t2', 'test2')` }
197
+ );
198
+
199
+ const data = await context.getBucketData('global[]');
200
+
201
+ expect(data).toMatchObject([
202
+ // Initial replication
203
+ PUT_T1,
204
+ // Truncate
205
+ REMOVE_T1
206
+ ]);
207
+ })
208
+ );
209
+
210
+ test(
211
+ 'change replica id',
212
+ walStreamTest(factory, async (context) => {
213
+ // Change replica id from default to full
214
+ // Causes a re-import of the table.
215
+
216
+ const { pool } = context;
217
+ await context.updateSyncRules(BASIC_SYNC_RULES);
218
+
219
+ await pool.query(`CREATE TABLE test_data(id text primary key, description text)`);
220
+ await pool.query(`INSERT INTO test_data(id, description) VALUES('t1', 'test1')`);
221
+
222
+ await context.replicateSnapshot();
223
+ context.startStreaming();
224
+
225
+ await pool.query(
226
+ { statement: `ALTER TABLE test_data REPLICA IDENTITY FULL` },
227
+ // We need an operation to detect the change
228
+ { statement: `INSERT INTO test_data(id, description) VALUES('t2', 'test2')` }
229
+ );
230
+
231
+ const data = await context.getBucketData('global[]');
232
+
233
+ expect(data.slice(0, 2)).toMatchObject([
234
+ // Initial inserts
235
+ PUT_T1,
236
+ // Truncate
237
+ REMOVE_T1
238
+ ]);
239
+
240
+ // Snapshot - order doesn't matter
241
+ expect(data.slice(2, 4).sort(compareIds)).toMatchObject([PUT_T1, PUT_T2]);
242
+
243
+ expect(data.slice(4).sort(compareIds)).toMatchObject([
244
+ // Replicated insert
245
+ // We may eventually be able to de-duplicate this
246
+ PUT_T2
247
+ ]);
248
+ })
249
+ );
250
+
251
+ test(
252
+ 'change full replica id by adding column',
253
+ walStreamTest(factory, async (context) => {
254
+ // Change replica id from full by adding column
255
+ // Causes a re-import of the table.
256
+ // Other changes such as renaming column would have the same effect
257
+
258
+ const { pool } = context;
259
+ await context.updateSyncRules(BASIC_SYNC_RULES);
260
+
261
+ await pool.query(`CREATE TABLE test_data(id text primary key, description text)`);
262
+ await pool.query(`ALTER TABLE test_data REPLICA IDENTITY FULL`);
263
+ await pool.query(`INSERT INTO test_data(id, description) VALUES('t1', 'test1')`);
264
+
265
+ await context.replicateSnapshot();
266
+ context.startStreaming();
267
+
268
+ await pool.query(
269
+ { statement: `ALTER TABLE test_data ADD COLUMN other TEXT` },
270
+ { statement: `INSERT INTO test_data(id, description) VALUES('t2', 'test2')` }
271
+ );
272
+
273
+ const data = await context.getBucketData('global[]');
274
+
275
+ expect(data.slice(0, 2)).toMatchObject([
276
+ // Initial inserts
277
+ PUT_T1,
278
+ // Truncate
279
+ REMOVE_T1
280
+ ]);
281
+
282
+ // Snapshot - order doesn't matter
283
+ expect(data.slice(2, 4).sort(compareIds)).toMatchObject([
284
+ putOp('test_data', { id: 't1', description: 'test1', other: null }),
285
+ putOp('test_data', { id: 't2', description: 'test2', other: null })
286
+ ]);
287
+
288
+ expect(data.slice(4).sort(compareIds)).toMatchObject([
289
+ // Replicated insert
290
+ // We may eventually be able to de-duplicate this
291
+ putOp('test_data', { id: 't2', description: 'test2', other: null })
292
+ ]);
293
+ })
294
+ );
295
+
296
+ test(
297
+ 'change default replica id by changing column type',
298
+ walStreamTest(factory, async (context) => {
299
+ // Change default replica id by changing column type
300
+ // Causes a re-import of the table.
301
+ const { pool } = context;
302
+ await context.updateSyncRules(BASIC_SYNC_RULES);
303
+
304
+ await pool.query(`CREATE TABLE test_data(id text primary key, description text)`);
305
+ await pool.query(`INSERT INTO test_data(id, description) VALUES('t1', 'test1')`);
306
+
307
+ await context.replicateSnapshot();
308
+ context.startStreaming();
309
+
310
+ await pool.query(
311
+ { statement: `ALTER TABLE test_data ALTER COLUMN id TYPE varchar` },
312
+ { statement: `INSERT INTO test_data(id, description) VALUES('t2', 'test2')` }
313
+ );
314
+
315
+ const data = await context.getBucketData('global[]');
316
+
317
+ expect(data.slice(0, 2)).toMatchObject([
318
+ // Initial inserts
319
+ PUT_T1,
320
+ // Truncate
321
+ REMOVE_T1
322
+ ]);
323
+
324
+ // Snapshot - order doesn't matter
325
+ expect(data.slice(2, 4).sort(compareIds)).toMatchObject([PUT_T1, PUT_T2]);
326
+
327
+ expect(data.slice(4).sort(compareIds)).toMatchObject([
328
+ // Replicated insert
329
+ // We may eventually be able to de-duplicate this
330
+ PUT_T2
331
+ ]);
332
+ })
333
+ );
334
+
335
+ test(
336
+ 'change index id by changing column type',
337
+ walStreamTest(factory, async (context) => {
338
+ // Change index replica id by changing column type
339
+ // Causes a re-import of the table.
340
+ // Secondary functionality tested here is that replica id column order stays
341
+ // the same between initial and incremental replication.
342
+ const { pool } = context;
343
+ await context.updateSyncRules(BASIC_SYNC_RULES);
344
+
345
+ await pool.query(`CREATE TABLE test_data(id text primary key, description text not null)`);
346
+ await pool.query(`CREATE UNIQUE INDEX i1 ON test_data(description, id)`);
347
+ await pool.query(`ALTER TABLE test_data REPLICA IDENTITY USING INDEX i1`);
348
+
349
+ await pool.query(`INSERT INTO test_data(id, description) VALUES('t1', 'test1')`);
350
+
351
+ await context.replicateSnapshot();
352
+ context.startStreaming();
353
+
354
+ await pool.query(`INSERT INTO test_data(id, description) VALUES('t2', 'test2')`);
355
+
356
+ await pool.query(
357
+ { statement: `ALTER TABLE test_data ALTER COLUMN description TYPE varchar` },
358
+ { statement: `INSERT INTO test_data(id, description) VALUES('t3', 'test3')` }
359
+ );
360
+
361
+ const data = await context.getBucketData('global[]');
362
+
363
+ expect(data.slice(0, 2)).toMatchObject([
364
+ // Initial snapshot
365
+ PUT_T1,
366
+ // Streamed
367
+ PUT_T2
368
+ ]);
369
+
370
+ expect(data.slice(2, 4).sort(compareIds)).toMatchObject([
371
+ // Truncate - any order
372
+ REMOVE_T1,
373
+ REMOVE_T2
374
+ ]);
375
+
376
+ // Snapshot - order doesn't matter
377
+ expect(data.slice(4, 7).sort(compareIds)).toMatchObject([PUT_T1, PUT_T2, PUT_T3]);
378
+
379
+ expect(data.slice(7).sort(compareIds)).toMatchObject([
380
+ // Replicated insert
381
+ // We may eventually be able to de-duplicate this
382
+ PUT_T3
383
+ ]);
384
+ })
385
+ );
386
+
387
+ test(
388
+ 'add to publication',
389
+ walStreamTest(factory, async (context) => {
390
+ // Add table to publication after initial replication
391
+ const { pool } = context;
392
+
393
+ await pool.query(`DROP PUBLICATION powersync`);
394
+ await pool.query(`CREATE TABLE test_foo(id text primary key, description text)`);
395
+ await pool.query(`CREATE PUBLICATION powersync FOR table test_foo`);
396
+
397
+ const storage = await context.updateSyncRules(BASIC_SYNC_RULES);
398
+
399
+ await pool.query(`CREATE TABLE test_data(id text primary key, description text)`);
400
+ await pool.query(`INSERT INTO test_data(id, description) VALUES('t1', 'test1')`);
401
+
402
+ await context.replicateSnapshot();
403
+ context.startStreaming();
404
+
405
+ await pool.query(`INSERT INTO test_data(id, description) VALUES('t2', 'test2')`);
406
+
407
+ await pool.query(`ALTER PUBLICATION powersync ADD TABLE test_data`);
408
+ await pool.query(`INSERT INTO test_data(id, description) VALUES('t3', 'test3')`);
409
+
410
+ const data = await context.getBucketData('global[]');
411
+
412
+ expect(data.slice(0, 3).sort(compareIds)).toMatchObject([
413
+ // Snapshot insert - any order
414
+ PUT_T1,
415
+ PUT_T2,
416
+ PUT_T3
417
+ ]);
418
+
419
+ expect(data.slice(3)).toMatchObject([
420
+ // Replicated insert
421
+ // We may eventually be able to de-duplicate this
422
+ PUT_T3
423
+ ]);
424
+
425
+ const metrics = await storage.factory.getStorageMetrics();
426
+ expect(metrics.replication_size_bytes).toBeGreaterThan(0);
427
+ })
428
+ );
429
+
430
+ test(
431
+ 'add to publication (not in sync rules)',
432
+ walStreamTest(factory, async (context) => {
433
+ // Add table to publication after initial replication
434
+ // Since the table is not in sync rules, it should not be replicated.
435
+ const { pool } = context;
436
+
437
+ await pool.query(`DROP PUBLICATION powersync`);
438
+ await pool.query(`CREATE TABLE test_foo(id text primary key, description text)`);
439
+ await pool.query(`CREATE PUBLICATION powersync FOR table test_foo`);
440
+
441
+ const storage = await context.updateSyncRules(BASIC_SYNC_RULES);
442
+
443
+ await pool.query(`CREATE TABLE test_other(id text primary key, description text)`);
444
+ await pool.query(`INSERT INTO test_other(id, description) VALUES('t1', 'test1')`);
445
+
446
+ await context.replicateSnapshot();
447
+ context.startStreaming();
448
+
449
+ await pool.query(`INSERT INTO test_other(id, description) VALUES('t2', 'test2')`);
450
+
451
+ await pool.query(`ALTER PUBLICATION powersync ADD TABLE test_other`);
452
+ await pool.query(`INSERT INTO test_other(id, description) VALUES('t3', 'test3')`);
453
+
454
+ const data = await context.getBucketData('global[]');
455
+ expect(data).toMatchObject([]);
456
+
457
+ const metrics = await storage.factory.getStorageMetrics();
458
+ expect(metrics.replication_size_bytes).toEqual(0);
459
+ })
460
+ );
461
+
462
+ test(
463
+ 'replica identity nothing',
464
+ walStreamTest(factory, async (context) => {
465
+ // Technically not a schema change, but fits here.
466
+ // Replica ID works a little differently here - the table doesn't have
467
+ // one defined, but we generate a unique one for each replicated row.
468
+
469
+ const { pool } = context;
470
+ await context.updateSyncRules(BASIC_SYNC_RULES);
471
+
472
+ await pool.query(`CREATE TABLE test_data(id text primary key, description text)`);
473
+ await pool.query(`ALTER TABLE test_data REPLICA IDENTITY NOTHING`);
474
+ await pool.query(`INSERT INTO test_data(id, description) VALUES('t1', 'test1')`);
475
+
476
+ await context.replicateSnapshot();
477
+ context.startStreaming();
478
+
479
+ await pool.query(`INSERT INTO test_data(id, description) VALUES('t2', 'test2')`);
480
+
481
+ // Just as an FYI - cannot update or delete here
482
+ expect(pool.query(`UPDATE test_data SET description = 'test2b' WHERE id = 't2'`)).rejects.toThrow(
483
+ 'does not have a replica identity and publishes updates'
484
+ );
485
+
486
+ // Testing TRUNCATE is important here - this depends on current_data having unique
487
+ // ids.
488
+ await pool.query(`TRUNCATE TABLE test_data`);
489
+
490
+ const data = await context.getBucketData('global[]');
491
+
492
+ expect(data.slice(0, 2)).toMatchObject([
493
+ // Initial inserts
494
+ PUT_T1,
495
+ PUT_T2
496
+ ]);
497
+
498
+ expect(data.slice(2).sort(compareIds)).toMatchObject([
499
+ // Truncate
500
+ REMOVE_T1,
501
+ REMOVE_T2
502
+ ]);
503
+ })
504
+ );
505
+
506
+ test(
507
+ 'replica identity default without PK',
508
+ walStreamTest(factory, async (context) => {
509
+ // Same as no replica identity
510
+ const { pool } = context;
511
+ await context.updateSyncRules(BASIC_SYNC_RULES);
512
+
513
+ await pool.query(`CREATE TABLE test_data(id text, description text)`);
514
+ await pool.query(`INSERT INTO test_data(id, description) VALUES('t1', 'test1')`);
515
+
516
+ await context.replicateSnapshot();
517
+ context.startStreaming();
518
+
519
+ await pool.query(`INSERT INTO test_data(id, description) VALUES('t2', 'test2')`);
520
+
521
+ // Just as an FYI - cannot update or delete here
522
+ expect(pool.query(`UPDATE test_data SET description = 'test2b' WHERE id = 't2'`)).rejects.toThrow(
523
+ 'does not have a replica identity and publishes updates'
524
+ );
525
+
526
+ // Testing TRUNCATE is important here - this depends on current_data having unique
527
+ // ids.
528
+ await pool.query(`TRUNCATE TABLE test_data`);
529
+
530
+ const data = await context.getBucketData('global[]');
531
+
532
+ expect(data.slice(0, 2)).toMatchObject([
533
+ // Initial inserts
534
+ PUT_T1,
535
+ PUT_T2
536
+ ]);
537
+
538
+ expect(data.slice(2).sort(compareIds)).toMatchObject([
539
+ // Truncate
540
+ REMOVE_T1,
541
+ REMOVE_T2
542
+ ]);
543
+ })
544
+ );
545
+ }