@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,229 @@
1
+ import * as micro from '@journeyapps-platform/micro';
2
+ import { SqlSyncRules, SqliteValue, StaticSchema, isJsonValue, toSyncRulesValue } from '@powersync/service-sync-rules';
3
+ import { internal_routes } from '@powersync/service-types';
4
+
5
+ import * as api from '@/api/api-index.js';
6
+ import * as util from '@/util/util-index.js';
7
+
8
+ import { RouteGenerator } from './router.js';
9
+ import { PersistedSyncRulesContent } from '../storage/BucketStorage.js';
10
+ import { authApi } from './auth.js';
11
+
12
+ const demoCredentials: RouteGenerator = (router) =>
13
+ router.post('/api/admin/v1/demo-credentials', {
14
+ authorize: authApi,
15
+ validator: micro.schema.createTsCodecValidator(internal_routes.DemoCredentialsRequest, { allowAdditional: true }),
16
+ handler: async (payload) => {
17
+ const connection = payload.context.system.config.connection;
18
+ if (connection == null || !connection.demo_database) {
19
+ return internal_routes.DemoCredentialsResponse.encode({});
20
+ }
21
+
22
+ const uri = util.buildDemoPgUri(connection);
23
+ return internal_routes.DemoCredentialsResponse.encode({
24
+ credentials: {
25
+ postgres_uri: uri
26
+ }
27
+ });
28
+ }
29
+ });
30
+
31
+ export const executeSql: RouteGenerator = (router) =>
32
+ router.post('/api/admin/v1/execute-sql', {
33
+ authorize: authApi,
34
+ validator: micro.schema.createTsCodecValidator(internal_routes.ExecuteSqlRequest, { allowAdditional: true }),
35
+ handler: async (payload) => {
36
+ const connection = payload.context.system.config.connection;
37
+ if (connection == null || !connection.debug_api) {
38
+ return internal_routes.ExecuteSqlResponse.encode({
39
+ results: {
40
+ columns: [],
41
+ rows: []
42
+ },
43
+ success: false,
44
+ error: 'SQL querying is not enabled'
45
+ });
46
+ }
47
+
48
+ const pool = payload.context.system.requirePgPool();
49
+
50
+ const { query, args } = payload.params.sql;
51
+
52
+ try {
53
+ const result = await pool.query({
54
+ statement: query,
55
+ params: args.map(util.autoParameter)
56
+ });
57
+
58
+ return internal_routes.ExecuteSqlResponse.encode({
59
+ success: true,
60
+ results: {
61
+ columns: result.columns.map((c) => c.name),
62
+ rows: result.rows.map((row) => {
63
+ return row.map((value) => mapColumnValue(toSyncRulesValue(value)));
64
+ })
65
+ }
66
+ });
67
+ } catch (e) {
68
+ return internal_routes.ExecuteSqlResponse.encode({
69
+ results: {
70
+ columns: [],
71
+ rows: []
72
+ },
73
+ success: false,
74
+ error: e.message
75
+ });
76
+ }
77
+ }
78
+ });
79
+
80
+ export const diagnostics: RouteGenerator = (router) =>
81
+ router.post('/api/admin/v1/diagnostics', {
82
+ authorize: authApi,
83
+ validator: micro.schema.createTsCodecValidator(internal_routes.DiagnosticsRequest, { allowAdditional: true }),
84
+ handler: async (payload) => {
85
+ const include_content = payload.params.sync_rules_content ?? false;
86
+ const system = payload.context.system;
87
+
88
+ const status = await api.getConnectionStatus(system);
89
+ if (status == null) {
90
+ return internal_routes.DiagnosticsResponse.encode({
91
+ connections: []
92
+ });
93
+ }
94
+
95
+ const { storage } = system;
96
+ const active = await storage.getActiveSyncRulesContent();
97
+ const next = await storage.getNextSyncRulesContent();
98
+
99
+ const active_status = await api.getSyncRulesStatus(active, system, {
100
+ include_content,
101
+ check_connection: status.connected,
102
+ live_status: true
103
+ });
104
+
105
+ const next_status = await api.getSyncRulesStatus(next, system, {
106
+ include_content,
107
+ check_connection: status.connected,
108
+ live_status: true
109
+ });
110
+
111
+ return internal_routes.DiagnosticsResponse.encode({
112
+ connections: [status],
113
+ active_sync_rules: active_status,
114
+ deploying_sync_rules: next_status
115
+ });
116
+ }
117
+ });
118
+
119
+ export const getSchema: RouteGenerator = (router) =>
120
+ router.post('/api/admin/v1/schema', {
121
+ authorize: authApi,
122
+ validator: micro.schema.createTsCodecValidator(internal_routes.GetSchemaRequest, { allowAdditional: true }),
123
+ handler: async (payload) => {
124
+ const system = payload.context.system;
125
+
126
+ return internal_routes.GetSchemaResponse.encode(await api.getConnectionsSchema(system));
127
+ }
128
+ });
129
+
130
+ export const reprocess: RouteGenerator = (router) =>
131
+ router.post('/api/admin/v1/reprocess', {
132
+ authorize: authApi,
133
+ validator: micro.schema.createTsCodecValidator(internal_routes.ReprocessRequest, { allowAdditional: true }),
134
+ handler: async (payload) => {
135
+ const system = payload.context.system;
136
+
137
+ const storage = system.storage;
138
+ const next = await storage.getNextSyncRules();
139
+ if (next != null) {
140
+ throw new Error(`Busy processing sync rules - cannot reprocess`);
141
+ }
142
+
143
+ const active = await storage.getActiveSyncRules();
144
+ if (active == null) {
145
+ throw new micro.errors.JourneyError({
146
+ status: 422,
147
+ code: 'NO_SYNC_RULES',
148
+ description: 'No active sync rules'
149
+ });
150
+ }
151
+
152
+ const new_rules = await storage.updateSyncRules({
153
+ content: active.sync_rules.content
154
+ });
155
+
156
+ return internal_routes.ReprocessResponse.encode({
157
+ connections: [
158
+ {
159
+ tag: system.config.connection!.tag,
160
+ id: system.config.connection!.id,
161
+ slot_name: new_rules.slot_name
162
+ }
163
+ ]
164
+ });
165
+ }
166
+ });
167
+
168
+ export const validate: RouteGenerator = (router) =>
169
+ router.post('/api/admin/v1/validate', {
170
+ authorize: authApi,
171
+ validator: micro.schema.createTsCodecValidator(internal_routes.ValidateRequest, { allowAdditional: true }),
172
+ handler: async (payload) => {
173
+ const system = payload.context.system;
174
+
175
+ const content = payload.params.sync_rules;
176
+
177
+ const schemaData = await api.getConnectionsSchema(system);
178
+ const schema = new StaticSchema(schemaData.connections);
179
+
180
+ const sync_rules: PersistedSyncRulesContent = {
181
+ // Dummy values
182
+ id: 0,
183
+ slot_name: '',
184
+
185
+ parsed() {
186
+ return {
187
+ ...this,
188
+ sync_rules: SqlSyncRules.fromYaml(content, { throwOnError: false, schema })
189
+ };
190
+ },
191
+ sync_rules_content: content,
192
+ async lock() {
193
+ throw new Error('Lock not implemented');
194
+ }
195
+ };
196
+
197
+ const connectionStatus = await api.getConnectionStatus(system);
198
+ if (connectionStatus == null) {
199
+ return internal_routes.ValidateResponse.encode({
200
+ errors: [{ level: 'fatal', message: 'No connection configured' }],
201
+ connections: []
202
+ });
203
+ }
204
+
205
+ const status = (await api.getSyncRulesStatus(sync_rules, system, {
206
+ include_content: false,
207
+ check_connection: connectionStatus?.connected,
208
+ live_status: false
209
+ }))!;
210
+
211
+ if (connectionStatus == null) {
212
+ status.errors.push({ level: 'fatal', message: 'No connection configured' });
213
+ }
214
+
215
+ return internal_routes.ValidateResponse.encode(status);
216
+ }
217
+ });
218
+
219
+ function mapColumnValue(value: SqliteValue) {
220
+ if (typeof value == 'bigint') {
221
+ return Number(value);
222
+ } else if (isJsonValue(value)) {
223
+ return value;
224
+ } else {
225
+ return null;
226
+ }
227
+ }
228
+
229
+ export const admin_routes = [demoCredentials, executeSql, diagnostics, getSchema, reprocess, validate];
@@ -0,0 +1,209 @@
1
+ import * as micro from '@journeyapps-platform/micro';
2
+ import { FastifyRequest } from 'fastify';
3
+ import * as jose from 'jose';
4
+
5
+ import * as auth from '@/auth/auth-index.js';
6
+ import * as util from '@/util/util-index.js';
7
+ import { Context } from './router.js';
8
+ import { CorePowerSyncSystem } from '../system/CorePowerSyncSystem.js';
9
+
10
+ export function endpoint(req: FastifyRequest) {
11
+ const protocol = req.headers['x-forwarded-proto'] ?? req.protocol;
12
+ const host = req.hostname;
13
+ return `${protocol}://${host}`;
14
+ }
15
+
16
+ function devAudience(req: FastifyRequest): string {
17
+ return `${endpoint(req)}/dev`;
18
+ }
19
+
20
+ /**
21
+ * @deprecated
22
+ *
23
+ * Will be replaced by temporary tokens issued by PowerSync Management service.
24
+ */
25
+ export async function issueDevToken(req: FastifyRequest, user_id: string, config: util.ResolvedPowerSyncConfig) {
26
+ const iss = devAudience(req);
27
+ const aud = devAudience(req);
28
+
29
+ const key = config.dev.dev_key;
30
+ if (key == null) {
31
+ throw new Error('Auth disabled');
32
+ }
33
+
34
+ return await new jose.SignJWT({})
35
+ .setProtectedHeader({ alg: key.source.alg!, kid: key.kid })
36
+ .setSubject(user_id)
37
+ .setIssuedAt()
38
+ .setIssuer(iss)
39
+ .setAudience(aud)
40
+ .setExpirationTime('30d')
41
+ .sign(key.key);
42
+ }
43
+
44
+ /** @deprecated */
45
+ export async function issueLegacyDevToken(req: FastifyRequest, user_id: string, config: util.ResolvedPowerSyncConfig) {
46
+ const iss = devAudience(req);
47
+ const aud = config.jwt_audiences[0];
48
+
49
+ const key = config.dev.dev_key;
50
+ if (key == null || aud == null) {
51
+ throw new Error('Auth disabled');
52
+ }
53
+
54
+ return await new jose.SignJWT({})
55
+ .setProtectedHeader({ alg: key.source.alg!, kid: key.kid })
56
+ .setSubject(user_id)
57
+ .setIssuedAt()
58
+ .setIssuer(iss)
59
+ .setAudience(aud)
60
+ .setExpirationTime('60m')
61
+ .sign(key.key);
62
+ }
63
+
64
+ export async function issuePowerSyncToken(req: FastifyRequest, user_id: string, config: util.ResolvedPowerSyncConfig) {
65
+ const iss = devAudience(req);
66
+ const aud = config.jwt_audiences[0];
67
+ const key = config.dev.dev_key;
68
+ if (key == null || aud == null) {
69
+ throw new Error('Auth disabled');
70
+ }
71
+
72
+ const jwt = await new jose.SignJWT({})
73
+ .setProtectedHeader({ alg: key.source.alg!, kid: key.kid })
74
+ .setSubject(user_id)
75
+ .setIssuedAt()
76
+ .setIssuer(iss)
77
+ .setAudience(aud)
78
+ .setExpirationTime('5m')
79
+ .sign(key.key);
80
+ return jwt;
81
+ }
82
+
83
+ export function getTokenFromHeader(authHeader: string = ''): string | null {
84
+ const tokenMatch = /^(Token|Bearer) (\S+)$/.exec(authHeader);
85
+ if (!tokenMatch) {
86
+ return null;
87
+ }
88
+ const token = tokenMatch[2];
89
+ return token ?? null;
90
+ }
91
+
92
+ export const authUser = async (payload: micro.fastify.FastifyHandlerPayload<any, Context>) => {
93
+ return authorizeUser(payload.context, payload.request.headers.authorization);
94
+ };
95
+
96
+ export async function authorizeUser(context: Context, authHeader: string = '') {
97
+ const token = getTokenFromHeader(authHeader);
98
+ if (token == null) {
99
+ return {
100
+ authorized: false,
101
+ errors: ['Authentication required']
102
+ };
103
+ }
104
+
105
+ const { context: tokenContext, errors } = await generateContext(context.system, token);
106
+
107
+ if (!tokenContext) {
108
+ return {
109
+ authorized: false,
110
+ errors
111
+ };
112
+ }
113
+
114
+ Object.assign(context, tokenContext);
115
+ return { authorized: true };
116
+ }
117
+
118
+ export async function generateContext(system: CorePowerSyncSystem, token: string) {
119
+ const config = system.config;
120
+
121
+ let tokenPayload: auth.JwtPayload;
122
+ try {
123
+ const maxAge = config.token_max_expiration;
124
+ tokenPayload = await system.client_keystore.verifyJwt(token, {
125
+ defaultAudiences: config.jwt_audiences,
126
+ maxAge: maxAge
127
+ });
128
+ return {
129
+ context: {
130
+ user_id: tokenPayload.sub,
131
+ token_payload: tokenPayload
132
+ }
133
+ };
134
+ } catch (err) {
135
+ return {
136
+ context: null,
137
+ errors: [err.message]
138
+ };
139
+ }
140
+ }
141
+
142
+ /**
143
+ * @deprecated
144
+ */
145
+ export const authDevUser = async (payload: micro.fastify.FastifyHandlerPayload<any, Context>) => {
146
+ const context = payload.context;
147
+ const token = getTokenFromHeader(payload.request.headers.authorization);
148
+ if (!context.system.config.dev.demo_auth) {
149
+ return {
150
+ authorized: false,
151
+ errors: ['Authentication disabled']
152
+ };
153
+ }
154
+ if (token == null) {
155
+ return {
156
+ authorized: false,
157
+ errors: ['Authentication required']
158
+ };
159
+ }
160
+
161
+ // Different from the configured audience.
162
+ // Should also not be changed by keys
163
+ const audience = [devAudience(payload.request)];
164
+
165
+ let tokenPayload: auth.JwtPayload;
166
+ try {
167
+ tokenPayload = await context.system.dev_client_keystore.verifyJwt(token, {
168
+ defaultAudiences: audience,
169
+ maxAge: '31d'
170
+ });
171
+ } catch (err) {
172
+ return {
173
+ authorized: false,
174
+ errors: [err.message]
175
+ };
176
+ }
177
+
178
+ payload.context.user_id = tokenPayload.sub;
179
+ return { authorized: true };
180
+ };
181
+
182
+ export const authApi = (payload: micro.fastify.FastifyHandlerPayload<any, Context>) => {
183
+ const context = payload.context;
184
+ const api_keys = context.system.config.api_tokens;
185
+ if (api_keys.length == 0) {
186
+ return {
187
+ authorized: false,
188
+ errors: ['Authentication disabled']
189
+ };
190
+ }
191
+ const auth = payload.request.headers.authorization ?? '';
192
+
193
+ const tokenMatch = /^(Token|Bearer) (\S+)$/.exec(auth);
194
+ if (!tokenMatch) {
195
+ return {
196
+ authorized: false,
197
+ errors: ['Authentication required']
198
+ };
199
+ }
200
+ const token = tokenMatch[2];
201
+ if (api_keys.includes(token)) {
202
+ return { authorized: true };
203
+ } else {
204
+ return {
205
+ authorized: false,
206
+ errors: ['Authentication failed']
207
+ };
208
+ }
209
+ };
@@ -0,0 +1,38 @@
1
+ import * as t from 'ts-codec';
2
+ import * as micro from '@journeyapps-platform/micro';
3
+
4
+ import * as util from '@/util/util-index.js';
5
+
6
+ import { authUser } from './auth.js';
7
+ import { RouteGenerator } from './router.js';
8
+
9
+ const WriteCheckpointRequest = t.object({});
10
+
11
+ export const writeCheckpoint: RouteGenerator = (router) =>
12
+ router.get('/write-checkpoint.json', {
13
+ authorize: authUser,
14
+ validator: micro.schema.createTsCodecValidator(WriteCheckpointRequest, { allowAdditional: true }),
15
+ handler: async (payload) => {
16
+ const system = payload.context.system;
17
+ const storage = system.storage;
18
+
19
+ const checkpoint = await util.getClientCheckpoint(system.requirePgPool(), storage);
20
+ return {
21
+ checkpoint
22
+ };
23
+ }
24
+ });
25
+
26
+ export const writeCheckpoint2: RouteGenerator = (router) =>
27
+ router.get('/write-checkpoint2.json', {
28
+ authorize: authUser,
29
+ validator: micro.schema.createTsCodecValidator(WriteCheckpointRequest, { allowAdditional: true }),
30
+ handler: async (payload) => {
31
+ const { user_id, system } = payload.context;
32
+ const storage = system.storage;
33
+ const write_checkpoint = await util.createWriteCheckpoint(system.requirePgPool(), storage, user_id!);
34
+ return {
35
+ write_checkpoint: String(write_checkpoint)
36
+ };
37
+ }
38
+ });
@@ -0,0 +1,194 @@
1
+ import * as t from 'ts-codec';
2
+ import * as micro from '@journeyapps-platform/micro';
3
+ import * as pgwire from '@powersync/service-jpgwire';
4
+
5
+ import * as util from '@/util/util-index.js';
6
+ import { authDevUser, authUser, endpoint, issueDevToken, issueLegacyDevToken, issuePowerSyncToken } from './auth.js';
7
+ import { RouteGenerator } from './router.js';
8
+
9
+ const AuthParams = t.object({
10
+ user: t.string,
11
+ password: t.string
12
+ });
13
+
14
+ // For legacy web client only. Remove soon.
15
+ export const auth: RouteGenerator = (router) =>
16
+ router.post('/auth.json', {
17
+ validator: micro.schema.createTsCodecValidator(AuthParams, { allowAdditional: true }),
18
+ handler: async (payload) => {
19
+ const { user, password } = payload.params;
20
+ const config = payload.context.system.config;
21
+
22
+ if (config.dev.demo_auth == false || config.dev.demo_password == null) {
23
+ throw new micro.errors.AuthorizationError(['Demo auth disabled']);
24
+ }
25
+
26
+ if (password == config.dev.demo_password) {
27
+ const token = await issueLegacyDevToken(payload.request, user, payload.context.system.config);
28
+ return { token, user_id: user, endpoint: endpoint(payload.request) };
29
+ } else {
30
+ throw new micro.errors.AuthorizationError(['Authentication failed']);
31
+ }
32
+ }
33
+ });
34
+
35
+ export const auth2: RouteGenerator = (router) =>
36
+ router.post('/dev/auth.json', {
37
+ validator: micro.schema.createTsCodecValidator(AuthParams, { allowAdditional: true }),
38
+ handler: async (payload) => {
39
+ const { user, password } = payload.params;
40
+ const config = payload.context.system.config;
41
+
42
+ if (config.dev.demo_auth == false || config.dev.demo_password == null) {
43
+ throw new micro.errors.AuthorizationError(['Demo auth disabled']);
44
+ }
45
+
46
+ if (password == config.dev.demo_password) {
47
+ const token = await issueDevToken(payload.request, user, payload.context.system.config);
48
+ return { token, user_id: user };
49
+ } else {
50
+ throw new micro.errors.AuthorizationError(['Authentication failed']);
51
+ }
52
+ }
53
+ });
54
+
55
+ const TokenParams = t.object({});
56
+
57
+ export const token: RouteGenerator = (router) =>
58
+ router.post('/dev/token.json', {
59
+ validator: micro.schema.createTsCodecValidator(TokenParams, { allowAdditional: true }),
60
+ authorize: authDevUser,
61
+ handler: async (payload) => {
62
+ const { user_id } = payload.context;
63
+ const outToken = await issuePowerSyncToken(payload.request, user_id!, payload.context.system.config);
64
+ return { token: outToken, user_id: user_id, endpoint: endpoint(payload.request) };
65
+ }
66
+ });
67
+
68
+ const OpType = {
69
+ PUT: 'PUT',
70
+ PATCH: 'PATCH',
71
+ DELETE: 'DELETE'
72
+ };
73
+
74
+ const CrudEntry = t.object({
75
+ op: t.Enum(OpType),
76
+ type: t.string,
77
+ id: t.string,
78
+ op_id: t.number.optional(),
79
+ data: t.any.optional()
80
+ });
81
+
82
+ const CrudRequest = t.object({
83
+ data: t.array(CrudEntry),
84
+ write_checkpoint: t.boolean.optional()
85
+ });
86
+ export const crud: RouteGenerator = (router) =>
87
+ router.post('/crud.json', {
88
+ validator: micro.schema.createTsCodecValidator(CrudRequest, { allowAdditional: true }),
89
+ authorize: authUser,
90
+
91
+ handler: async (payload) => {
92
+ const { user_id, system } = payload.context;
93
+
94
+ const pool = system.requirePgPool();
95
+
96
+ if (!system.config.dev.crud_api) {
97
+ throw new Error('CRUD api disabled');
98
+ }
99
+
100
+ const params = payload.params;
101
+
102
+ let statements: pgwire.Statement[] = [];
103
+
104
+ // Implementation note:
105
+ // Postgres does automatic "assigment cast" for query literals,
106
+ // e.g. a string literal to uuid. However, the same doesn't apply
107
+ // to query parameters.
108
+ // To handle those automatically, we use `json_populate_record`
109
+ // to automatically cast to the correct types.
110
+
111
+ for (let op of params.data) {
112
+ const table = util.escapeIdentifier(op.type);
113
+ if (op.op == 'PUT') {
114
+ const data = op.data as Record<string, any>;
115
+ const with_id = { ...data, id: op.id };
116
+
117
+ const columnsEscaped = Object.keys(with_id).map(util.escapeIdentifier);
118
+ const columnsJoined = columnsEscaped.join(', ');
119
+
120
+ let updateClauses: string[] = [];
121
+
122
+ for (let key of Object.keys(data)) {
123
+ updateClauses.push(`${util.escapeIdentifier(key)} = EXCLUDED.${util.escapeIdentifier(key)}`);
124
+ }
125
+
126
+ const updateClause = updateClauses.length > 0 ? `DO UPDATE SET ${updateClauses.join(', ')}` : `DO NOTHING`;
127
+
128
+ const statement = `
129
+ WITH data_row AS (
130
+ SELECT (json_populate_record(null::${table}, $1::json)).*
131
+ )
132
+ INSERT INTO ${table} (${columnsJoined})
133
+ SELECT ${columnsJoined} FROM data_row
134
+ ON CONFLICT(id) ${updateClause}`;
135
+
136
+ statements.push({
137
+ statement: statement,
138
+ params: [{ type: 'varchar', value: JSON.stringify(with_id) }]
139
+ });
140
+ } else if (op.op == 'PATCH') {
141
+ const data = op.data as Record<string, any>;
142
+ const with_id = { ...data, id: op.id };
143
+
144
+ let updateClauses: string[] = [];
145
+
146
+ for (let key of Object.keys(data)) {
147
+ updateClauses.push(`${util.escapeIdentifier(key)} = data_row.${util.escapeIdentifier(key)}`);
148
+ }
149
+
150
+ const statement = `
151
+ WITH data_row AS (
152
+ SELECT (json_populate_record(null::${table}, $1::json)).*
153
+ )
154
+ UPDATE ${table}
155
+ SET ${updateClauses.join(', ')}
156
+ FROM data_row
157
+ WHERE ${table}.id = data_row.id`;
158
+
159
+ statements.push({
160
+ statement: statement,
161
+ params: [{ type: 'varchar', value: JSON.stringify(with_id) }]
162
+ });
163
+ } else if (op.op == 'DELETE') {
164
+ statements.push({
165
+ statement: `
166
+ WITH data_row AS (
167
+ SELECT (json_populate_record(null::${table}, $1::json)).*
168
+ )
169
+ DELETE FROM ${table}
170
+ USING data_row
171
+ WHERE ${table}.id = data_row.id`,
172
+ params: [{ type: 'varchar', value: JSON.stringify({ id: op.id }) }]
173
+ });
174
+ }
175
+ }
176
+ await pool.query(...statements);
177
+
178
+ const storage = system.storage;
179
+ if (payload.params.write_checkpoint === true) {
180
+ const write_checkpoint = await util.createWriteCheckpoint(pool, storage, payload.context.user_id!);
181
+ return { write_checkpoint: String(write_checkpoint) };
182
+ } else if (payload.params.write_checkpoint === false) {
183
+ return {};
184
+ } else {
185
+ // Legacy
186
+ const checkpoint = await util.getClientCheckpoint(pool, storage);
187
+ return {
188
+ checkpoint
189
+ };
190
+ }
191
+ }
192
+ });
193
+
194
+ export const dev_routes = [auth, auth2, token, crud];
@@ -0,0 +1,39 @@
1
+ import * as micro from '@journeyapps-platform/micro';
2
+
3
+ import { Context, RouteGenerator } from './router.js';
4
+ import { admin_routes } from './admin.js';
5
+ import { writeCheckpoint, writeCheckpoint2 } from './checkpointing.js';
6
+ import { dev_routes } from './dev.js';
7
+ import { syncRulesRoutes } from './sync-rules.js';
8
+ import { IReactiveStream, ReactiveSocketRouter } from '@powersync/service-rsocket-router';
9
+ import { sync_stream_reactive } from './socket-route.js';
10
+ import { syncStreamed } from './sync-stream.js';
11
+
12
+ /**
13
+ * Generates router endpoints using the specified router instance
14
+ */
15
+ export const generateHTTPRoutes = (router: micro.fastify.FastifyRouter<Context>): micro.router.Route[] => {
16
+ const generators: RouteGenerator[] = [
17
+ ...dev_routes,
18
+ writeCheckpoint,
19
+ writeCheckpoint2,
20
+ ...syncRulesRoutes,
21
+ ...admin_routes
22
+ ];
23
+
24
+ return generators.map((generateRoute) => generateRoute(router));
25
+ };
26
+
27
+ /**
28
+ * Generates route endpoint for HTTP sync streaming
29
+ */
30
+ export const generateHTTPStreamRoutes = (router: micro.fastify.FastifyRouter<Context>): micro.router.Route[] => {
31
+ return [syncStreamed].map((generateRoute) => generateRoute(router));
32
+ };
33
+
34
+ /**
35
+ * Generates socket router endpoints using the specified router instance
36
+ */
37
+ export const generateSocketRoutes = (router: ReactiveSocketRouter<Context>): IReactiveStream[] => {
38
+ return [sync_stream_reactive].map((generateRoute) => generateRoute(router));
39
+ };