@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,13 @@
1
+ import * as t from 'ts-codec';
2
+ import { ReactiveSocketRouter, IReactiveStream } from '@powersync/service-rsocket-router';
3
+
4
+ import { Context } from './router.js';
5
+
6
+ export const RSocketContextMeta = t.object({
7
+ token: t.string
8
+ });
9
+
10
+ /**
11
+ * Creates a socket route handler given a router instance
12
+ */
13
+ export type SocketRouteGenerator = (router: ReactiveSocketRouter<Context>) => IReactiveStream;
@@ -0,0 +1,17 @@
1
+ import * as micro from '@journeyapps-platform/micro';
2
+
3
+ import * as auth from '@/auth/auth-index.js';
4
+ import { CorePowerSyncSystem } from '../system/CorePowerSyncSystem.js';
5
+
6
+ export type Context = {
7
+ user_id?: string;
8
+ system: CorePowerSyncSystem;
9
+
10
+ token_payload?: auth.JwtPayload;
11
+ };
12
+
13
+ /**
14
+ * Creates a route handler given a router instance
15
+ * TODO move away from Fastify specific types
16
+ */
17
+ export type RouteGenerator = (router: micro.fastify.FastifyRouter<Context>) => micro.router.Route;
@@ -0,0 +1,5 @@
1
+ export * from './router.js';
2
+ export * from './router-socket.js';
3
+ export * from './route-generators.js';
4
+
5
+ export * as auth from './auth.js';
@@ -0,0 +1,131 @@
1
+ import { serialize } from 'bson';
2
+ import { SyncParameters, normalizeTokenParameters } from '@powersync/service-sync-rules';
3
+ import * as micro from '@journeyapps-platform/micro';
4
+
5
+ import * as util from '@/util/util-index.js';
6
+ import { concurrent_connections, data_synced_bytes } from '../metrics/metrics.js';
7
+ import { streamResponse } from '../sync/sync.js';
8
+ import { SyncRoutes } from './sync-stream.js';
9
+ import { SocketRouteGenerator } from './router-socket.js';
10
+
11
+ export const sync_stream_reactive: SocketRouteGenerator = (router) =>
12
+ router.reactiveStream<util.StreamingSyncRequest, any>(SyncRoutes.STREAM, {
13
+ authorize: ({ context }) => {
14
+ return {
15
+ authorized: !!context.token_payload,
16
+ errors: ['Authentication required']
17
+ };
18
+ },
19
+ validator: micro.schema.createTsCodecValidator(util.StreamingSyncRequest, { allowAdditional: true }),
20
+ handler: async ({ context, params, responder, observer, initialN }) => {
21
+ const { system } = context;
22
+
23
+ if (system.closed) {
24
+ responder.onError(
25
+ new micro.errors.JourneyError({
26
+ status: 503,
27
+ code: 'SERVICE_UNAVAILABLE',
28
+ description: 'Service temporarily unavailable'
29
+ })
30
+ );
31
+ responder.onComplete();
32
+ return;
33
+ }
34
+
35
+ const controller = new AbortController();
36
+
37
+ const syncParams: SyncParameters = normalizeTokenParameters(context.token_payload?.parameters ?? {});
38
+ const storage = system.storage;
39
+ // Sanity check before we start the stream
40
+ const cp = await storage.getActiveCheckpoint();
41
+ if (!cp.hasSyncRules()) {
42
+ responder.onError(
43
+ new micro.errors.JourneyError({
44
+ status: 500,
45
+ code: 'NO_SYNC_RULES',
46
+ description: 'No sync rules available'
47
+ })
48
+ );
49
+ responder.onComplete();
50
+ return;
51
+ }
52
+
53
+ let requestedN = initialN;
54
+ const disposer = observer.registerListener({
55
+ request(n) {
56
+ requestedN += n;
57
+ },
58
+ cancel: () => {
59
+ controller.abort();
60
+ }
61
+ });
62
+
63
+ const removeStopHandler = system.addStopHandler(() => {
64
+ observer.triggerCancel();
65
+ });
66
+
67
+ concurrent_connections.add(1);
68
+ try {
69
+ for await (const data of streamResponse({
70
+ storage,
71
+ params: {
72
+ ...params,
73
+ binary_data: true // always true for web sockets
74
+ },
75
+ syncParams,
76
+ token: context!.token_payload!,
77
+ tokenStreamOptions: {
78
+ // RSocket handles keepalive events by default
79
+ keep_alive: false
80
+ },
81
+ signal: controller.signal
82
+ })) {
83
+ if (data == null) {
84
+ // Empty value just to flush iterator memory
85
+ continue;
86
+ } else if (typeof data == 'string') {
87
+ // Should not happen with binary_data: true
88
+ throw new Error(`Unexpected string data: ${data}`);
89
+ }
90
+
91
+ {
92
+ // On NodeJS, serialize always returns a Buffer
93
+ const serialized = serialize(data) as Buffer;
94
+ responder.onNext({ data: serialized }, false);
95
+ requestedN--;
96
+ data_synced_bytes.add(serialized.length);
97
+ }
98
+
99
+ if (requestedN <= 0) {
100
+ await new Promise<void>((resolve) => {
101
+ const l = observer.registerListener({
102
+ request() {
103
+ if (requestedN > 0) {
104
+ // Management of updating the total requested items is done above
105
+ resolve();
106
+ l();
107
+ }
108
+ },
109
+ cancel: () => {
110
+ // Don't wait here if the request is cancelled
111
+ resolve();
112
+ l();
113
+ }
114
+ });
115
+ });
116
+ }
117
+ }
118
+ } catch (ex) {
119
+ // Convert to our standard form before responding.
120
+ // This ensures the error can be serialized.
121
+ const error = new micro.errors.InternalServerError(ex);
122
+ micro.logger.error('Sync stream error', error);
123
+ responder.onError(error);
124
+ } finally {
125
+ responder.onComplete();
126
+ removeStopHandler();
127
+ disposer();
128
+ concurrent_connections.add(-1);
129
+ }
130
+ }
131
+ });
@@ -0,0 +1,210 @@
1
+ import * as t from 'ts-codec';
2
+ import { FastifyPluginAsync, FastifyReply } from 'fastify';
3
+ import * as micro from '@journeyapps-platform/micro';
4
+ import * as pgwire from '@powersync/service-jpgwire';
5
+ import { SqlSyncRules, SyncRulesErrors } from '@powersync/service-sync-rules';
6
+
7
+ import * as replication from '@/replication/replication-index.js';
8
+ import { authApi } from './auth.js';
9
+ import { RouteGenerator } from './router.js';
10
+
11
+ const DeploySyncRulesRequest = t.object({
12
+ content: t.string
13
+ });
14
+
15
+ const yamlPlugin: FastifyPluginAsync = async (fastify) => {
16
+ fastify.addContentTypeParser('application/yaml', async (request, payload, _d) => {
17
+ const data = await micro.streaming.drain(payload);
18
+
19
+ request.params = { content: Buffer.concat(data).toString('utf8') };
20
+ });
21
+ };
22
+
23
+ export const deploySyncRules: RouteGenerator = (router) =>
24
+ router.post('/api/sync-rules/v1/deploy', {
25
+ authorize: authApi,
26
+ parse: true,
27
+ plugins: [yamlPlugin],
28
+ validator: micro.schema.createTsCodecValidator(DeploySyncRulesRequest, { allowAdditional: true }),
29
+ handler: async (payload) => {
30
+ if (payload.context.system.config.sync_rules.present) {
31
+ // If sync rules are configured via the config, disable deploy via the API.
32
+ throw new micro.errors.JourneyError({
33
+ status: 422,
34
+ code: 'API_DISABLED',
35
+ description: 'Sync rules API disabled',
36
+ details: 'Use the management API to deploy sync rules'
37
+ });
38
+ }
39
+ const content = payload.params.content;
40
+
41
+ try {
42
+ SqlSyncRules.fromYaml(payload.params.content);
43
+ } catch (e) {
44
+ throw new micro.errors.JourneyError({
45
+ status: 422,
46
+ code: 'INVALID_SYNC_RULES',
47
+ description: 'Sync rules parsing failed',
48
+ details: e.message
49
+ });
50
+ }
51
+
52
+ const sync_rules = await payload.context.system.storage.updateSyncRules({
53
+ content: content
54
+ });
55
+
56
+ return {
57
+ slot_name: sync_rules.slot_name
58
+ };
59
+ }
60
+ });
61
+
62
+ const ValidateSyncRulesRequest = t.object({
63
+ content: t.string
64
+ });
65
+
66
+ export const validateSyncRules: RouteGenerator = (router) =>
67
+ router.post('/api/sync-rules/v1/validate', {
68
+ authorize: authApi,
69
+ parse: true,
70
+ plugins: [yamlPlugin],
71
+ validator: micro.schema.createTsCodecValidator(ValidateSyncRulesRequest, { allowAdditional: true }),
72
+ handler: async (payload) => {
73
+ const content = payload.params.content;
74
+
75
+ const info = await debugSyncRules(payload.context.system.requirePgPool(), content);
76
+
77
+ replyPrettyJson(payload.reply, info);
78
+ }
79
+ });
80
+
81
+ export const currentSyncRules: RouteGenerator = (router) =>
82
+ router.get('/api/sync-rules/v1/current', {
83
+ authorize: authApi,
84
+ handler: async (payload) => {
85
+ const storage = payload.context.system.storage;
86
+ const sync_rules = await storage.getActiveSyncRulesContent();
87
+ if (!sync_rules) {
88
+ throw new micro.errors.JourneyError({
89
+ status: 422,
90
+ code: 'NO_SYNC_RULES',
91
+ description: 'No active sync rules'
92
+ });
93
+ }
94
+ const info = await debugSyncRules(payload.context.system.requirePgPool(), sync_rules.sync_rules_content);
95
+ const next = await storage.getNextSyncRulesContent();
96
+
97
+ const next_info = next
98
+ ? await debugSyncRules(payload.context.system.requirePgPool(), next.sync_rules_content)
99
+ : null;
100
+
101
+ const response = {
102
+ current: {
103
+ slot_name: sync_rules.slot_name,
104
+ content: sync_rules.sync_rules_content,
105
+ ...info
106
+ },
107
+ next:
108
+ next == null
109
+ ? null
110
+ : {
111
+ slot_name: next.slot_name,
112
+ content: next.sync_rules_content,
113
+ ...next_info
114
+ }
115
+ };
116
+ replyPrettyJson(payload.reply, { data: response });
117
+ }
118
+ });
119
+
120
+ const ReprocessSyncRulesRequest = t.object({});
121
+
122
+ export const reprocessSyncRules: RouteGenerator = (router) =>
123
+ router.post('/api/sync-rules/v1/reprocess', {
124
+ authorize: authApi,
125
+ validator: micro.schema.createTsCodecValidator(ReprocessSyncRulesRequest),
126
+ handler: async (payload) => {
127
+ const storage = payload.context.system.storage;
128
+ const sync_rules = await storage.getActiveSyncRules();
129
+ if (sync_rules == null) {
130
+ throw new micro.errors.JourneyError({
131
+ status: 422,
132
+ code: 'NO_SYNC_RULES',
133
+ description: 'No active sync rules'
134
+ });
135
+ }
136
+
137
+ const new_rules = await storage.updateSyncRules({
138
+ content: sync_rules.sync_rules.content
139
+ });
140
+ return {
141
+ slot_name: new_rules.slot_name
142
+ };
143
+ }
144
+ });
145
+
146
+ export const syncRulesRoutes = [validateSyncRules, deploySyncRules, reprocessSyncRules, currentSyncRules];
147
+
148
+ function replyPrettyJson(reply: FastifyReply, payload: any) {
149
+ reply
150
+ .status(200)
151
+ .header('Content-Type', 'application/json')
152
+ .send(JSON.stringify(payload, null, 2) + '\n');
153
+ }
154
+
155
+ async function debugSyncRules(db: pgwire.PgClient, sync_rules: string) {
156
+ try {
157
+ const rules = SqlSyncRules.fromYaml(sync_rules);
158
+ const source_table_patterns = rules.getSourceTables();
159
+ const wc = new replication.WalConnection({
160
+ db: db,
161
+ sync_rules: rules
162
+ });
163
+ const resolved_tables = await wc.getDebugTablesInfo(source_table_patterns);
164
+
165
+ return {
166
+ valid: true,
167
+ bucket_definitions: rules.bucket_descriptors.map((d) => {
168
+ let all_parameter_queries = [...d.parameter_queries.values()].flat();
169
+ let all_data_queries = [...d.data_queries.values()].flat();
170
+ return {
171
+ name: d.name,
172
+ bucket_parameters: d.bucket_parameters,
173
+ global_parameter_queries: d.global_parameter_queries.map((q) => {
174
+ return {
175
+ sql: q.sql
176
+ };
177
+ }),
178
+ parameter_queries: all_parameter_queries.map((q) => {
179
+ return {
180
+ sql: q.sql,
181
+ table: q.sourceTable,
182
+ input_parameters: q.input_parameters
183
+ };
184
+ }),
185
+
186
+ data_queries: all_data_queries.map((q) => {
187
+ return {
188
+ sql: q.sql,
189
+ table: q.sourceTable,
190
+ columns: q.columnOutputNames()
191
+ };
192
+ })
193
+ };
194
+ }),
195
+ source_tables: resolved_tables,
196
+ data_tables: rules.debugGetOutputTables()
197
+ };
198
+ } catch (e) {
199
+ if (e instanceof SyncRulesErrors) {
200
+ return {
201
+ valid: false,
202
+ errors: e.errors.map((e) => e.message)
203
+ };
204
+ }
205
+ return {
206
+ valid: false,
207
+ errors: [e.message]
208
+ };
209
+ }
210
+ }
@@ -0,0 +1,92 @@
1
+ import { Readable } from 'stream';
2
+ import * as micro from '@journeyapps-platform/micro';
3
+ import { SyncParameters, normalizeTokenParameters } from '@powersync/service-sync-rules';
4
+
5
+ import * as sync from '@/sync/sync-index.js';
6
+ import * as util from '@/util/util-index.js';
7
+ import { concurrent_connections } from '../metrics/metrics.js';
8
+
9
+ import { authUser } from './auth.js';
10
+ import { RouteGenerator } from './router.js';
11
+
12
+ export enum SyncRoutes {
13
+ STREAM = '/sync/stream'
14
+ }
15
+
16
+ export const syncStreamed: RouteGenerator = (router) =>
17
+ router.post(SyncRoutes.STREAM, {
18
+ authorize: authUser,
19
+ validator: micro.schema.createTsCodecValidator(util.StreamingSyncRequest, { allowAdditional: true }),
20
+ handler: async (payload) => {
21
+ const userId = payload.context.user_id!;
22
+ const system = payload.context.system;
23
+
24
+ if (system.closed) {
25
+ throw new micro.errors.JourneyError({
26
+ status: 503,
27
+ code: 'SERVICE_UNAVAILABLE',
28
+ description: 'Service temporarily unavailable'
29
+ });
30
+ }
31
+
32
+ const params: util.StreamingSyncRequest = payload.params;
33
+ const syncParams: SyncParameters = normalizeTokenParameters(payload.context.token_payload!.parameters ?? {});
34
+
35
+ const storage = system.storage;
36
+ // Sanity check before we start the stream
37
+ const cp = await storage.getActiveCheckpoint();
38
+ if (!cp.hasSyncRules()) {
39
+ throw new micro.errors.JourneyError({
40
+ status: 500,
41
+ code: 'NO_SYNC_RULES',
42
+ description: 'No sync rules available'
43
+ });
44
+ }
45
+
46
+ const res = payload.reply;
47
+
48
+ res.status(200).header('Content-Type', 'application/x-ndjson');
49
+
50
+ const controller = new AbortController();
51
+ try {
52
+ concurrent_connections.add(1);
53
+ const stream = Readable.from(
54
+ sync.transformToBytesTracked(
55
+ sync.ndjson(
56
+ sync.streamResponse({
57
+ storage,
58
+ params,
59
+ syncParams,
60
+ token: payload.context.token_payload!,
61
+ signal: controller.signal
62
+ })
63
+ )
64
+ ),
65
+ { objectMode: false, highWaterMark: 16 * 1024 }
66
+ );
67
+
68
+ const deregister = system.addStopHandler(() => {
69
+ // This error is not currently propagated to the client
70
+ controller.abort();
71
+ stream.destroy(new Error('Shutting down system'));
72
+ });
73
+ stream.on('close', () => {
74
+ deregister();
75
+ });
76
+
77
+ stream.on('error', (error) => {
78
+ controller.abort();
79
+ // Note: This appears as a 200 response in the logs.
80
+ if (error.message != 'Shutting down system') {
81
+ micro.logger.error('Streaming sync request failed', error);
82
+ }
83
+ });
84
+ await res.send(stream);
85
+ } finally {
86
+ controller.abort();
87
+ concurrent_connections.add(-1);
88
+ // Prevent double-send
89
+ res.hijack();
90
+ }
91
+ }
92
+ });
@@ -0,0 +1,91 @@
1
+ // Script to tear down the data when deleting an instance.
2
+ // This deletes:
3
+ // 1. The replication slots on the source postgres instance (if available).
4
+ // 2. The mongo database.
5
+
6
+ import * as micro from '@journeyapps-platform/micro';
7
+ import * as timers from 'timers/promises';
8
+
9
+ import * as db from '../db/db-index.js';
10
+ import * as storage from '../storage/storage-index.js';
11
+ import * as utils from '../util/util-index.js';
12
+ import * as replication from '../replication/replication-index.js';
13
+
14
+ /**
15
+ * Attempt to terminate a single sync rules instance.
16
+ *
17
+ * This may fail with a lock error.
18
+ */
19
+ async function terminateReplicator(
20
+ storageFactory: storage.BucketStorageFactory,
21
+ connection: utils.ResolvedConnection,
22
+ syncRules: storage.PersistedSyncRulesContent
23
+ ) {
24
+ // The lock may still be active if the current replication instance
25
+ // hasn't stopped yet.
26
+ const lock = await syncRules.lock();
27
+ try {
28
+ const parsed = syncRules.parsed();
29
+ const storage = storageFactory.getInstance(parsed);
30
+ const stream = new replication.WalStreamRunner({
31
+ factory: storageFactory,
32
+ storage: storage,
33
+ source_db: connection,
34
+ lock
35
+ });
36
+ console.log('terminating', stream.slot_name);
37
+ await stream.terminate();
38
+ console.log('terminated', stream.slot_name);
39
+ } finally {
40
+ await lock.release();
41
+ }
42
+ }
43
+
44
+ /**
45
+ * Terminate all replicating sync rules, deleting the replication slots.
46
+ *
47
+ * Retries lock and other errors for up to two minutes.
48
+ *
49
+ * This is a best-effot attempt. In some cases it may not be possible to delete the replication
50
+ * slot, such as when the postgres instance is unreachable.
51
+ */
52
+ async function terminateReplicators(
53
+ storageFactory: storage.BucketStorageFactory,
54
+ connection: utils.ResolvedConnection
55
+ ) {
56
+ const start = Date.now();
57
+ while (Date.now() - start < 12_000) {
58
+ let retry = false;
59
+ const replicationRules = await storageFactory.getReplicatingSyncRules();
60
+ for (let syncRules of replicationRules) {
61
+ try {
62
+ await terminateReplicator(storageFactory, connection, syncRules);
63
+ } catch (e) {
64
+ retry = true;
65
+ console.error(e);
66
+ micro.logger.warn(`Failed to terminate ${syncRules.slot_name}`, e);
67
+ }
68
+ }
69
+ if (!retry) {
70
+ break;
71
+ }
72
+ await timers.setTimeout(5_000);
73
+ }
74
+ }
75
+
76
+ export async function teardown(runnerConfig: utils.RunnerConfig) {
77
+ const config = await utils.loadConfig(runnerConfig);
78
+ const mongoDB = storage.createPowerSyncMongo(config.storage);
79
+ await db.mongo.waitForAuth(mongoDB.db);
80
+
81
+ const bucketStorage = new storage.MongoBucketStorage(mongoDB, { slot_name_prefix: config.slot_name_prefix });
82
+ const connection = config.connection;
83
+
84
+ if (connection) {
85
+ await terminateReplicators(bucketStorage, connection);
86
+ }
87
+
88
+ const database = mongoDB.db;
89
+ await database.dropDatabase();
90
+ await mongoDB.client.close();
91
+ }