@powersync/service-core 0.0.0-dev-20240620165206

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 (454) hide show
  1. package/.probes/.gitkeep +0 -0
  2. package/CHANGELOG.md +82 -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 +25 -0
  72. package/dist/index.js +28 -0
  73. package/dist/index.js.map +1 -0
  74. package/dist/locks/LockManager.d.ts +10 -0
  75. package/dist/locks/LockManager.js +7 -0
  76. package/dist/locks/LockManager.js.map +1 -0
  77. package/dist/locks/MongoLocks.d.ts +36 -0
  78. package/dist/locks/MongoLocks.js +81 -0
  79. package/dist/locks/MongoLocks.js.map +1 -0
  80. package/dist/locks/locks-index.d.ts +2 -0
  81. package/dist/locks/locks-index.js +3 -0
  82. package/dist/locks/locks-index.js.map +1 -0
  83. package/dist/metrics/Metrics.d.ts +30 -0
  84. package/dist/metrics/Metrics.js +176 -0
  85. package/dist/metrics/Metrics.js.map +1 -0
  86. package/dist/migrations/db/migrations/1684951997326-init.d.ts +3 -0
  87. package/dist/migrations/db/migrations/1684951997326-init.js +31 -0
  88. package/dist/migrations/db/migrations/1684951997326-init.js.map +1 -0
  89. package/dist/migrations/db/migrations/1688556755264-initial-sync-rules.d.ts +2 -0
  90. package/dist/migrations/db/migrations/1688556755264-initial-sync-rules.js +5 -0
  91. package/dist/migrations/db/migrations/1688556755264-initial-sync-rules.js.map +1 -0
  92. package/dist/migrations/db/migrations/1702295701188-sync-rule-state.d.ts +3 -0
  93. package/dist/migrations/db/migrations/1702295701188-sync-rule-state.js +54 -0
  94. package/dist/migrations/db/migrations/1702295701188-sync-rule-state.js.map +1 -0
  95. package/dist/migrations/db/migrations/1711543888062-write-checkpoint-index.d.ts +3 -0
  96. package/dist/migrations/db/migrations/1711543888062-write-checkpoint-index.js +27 -0
  97. package/dist/migrations/db/migrations/1711543888062-write-checkpoint-index.js.map +1 -0
  98. package/dist/migrations/definitions.d.ts +18 -0
  99. package/dist/migrations/definitions.js +6 -0
  100. package/dist/migrations/definitions.js.map +1 -0
  101. package/dist/migrations/executor.d.ts +16 -0
  102. package/dist/migrations/executor.js +64 -0
  103. package/dist/migrations/executor.js.map +1 -0
  104. package/dist/migrations/migrations-index.d.ts +3 -0
  105. package/dist/migrations/migrations-index.js +4 -0
  106. package/dist/migrations/migrations-index.js.map +1 -0
  107. package/dist/migrations/migrations.d.ts +10 -0
  108. package/dist/migrations/migrations.js +90 -0
  109. package/dist/migrations/migrations.js.map +1 -0
  110. package/dist/migrations/store/migration-store.d.ts +11 -0
  111. package/dist/migrations/store/migration-store.js +46 -0
  112. package/dist/migrations/store/migration-store.js.map +1 -0
  113. package/dist/replication/ErrorRateLimiter.d.ts +17 -0
  114. package/dist/replication/ErrorRateLimiter.js +43 -0
  115. package/dist/replication/ErrorRateLimiter.js.map +1 -0
  116. package/dist/replication/PgRelation.d.ts +16 -0
  117. package/dist/replication/PgRelation.js +26 -0
  118. package/dist/replication/PgRelation.js.map +1 -0
  119. package/dist/replication/WalConnection.d.ts +34 -0
  120. package/dist/replication/WalConnection.js +190 -0
  121. package/dist/replication/WalConnection.js.map +1 -0
  122. package/dist/replication/WalStream.d.ts +57 -0
  123. package/dist/replication/WalStream.js +515 -0
  124. package/dist/replication/WalStream.js.map +1 -0
  125. package/dist/replication/WalStreamManager.d.ts +30 -0
  126. package/dist/replication/WalStreamManager.js +198 -0
  127. package/dist/replication/WalStreamManager.js.map +1 -0
  128. package/dist/replication/WalStreamRunner.d.ts +38 -0
  129. package/dist/replication/WalStreamRunner.js +155 -0
  130. package/dist/replication/WalStreamRunner.js.map +1 -0
  131. package/dist/replication/replication-index.d.ts +7 -0
  132. package/dist/replication/replication-index.js +8 -0
  133. package/dist/replication/replication-index.js.map +1 -0
  134. package/dist/replication/util.d.ts +9 -0
  135. package/dist/replication/util.js +62 -0
  136. package/dist/replication/util.js.map +1 -0
  137. package/dist/routes/auth.d.ts +56 -0
  138. package/dist/routes/auth.js +182 -0
  139. package/dist/routes/auth.js.map +1 -0
  140. package/dist/routes/endpoints/admin.d.ts +1011 -0
  141. package/dist/routes/endpoints/admin.js +207 -0
  142. package/dist/routes/endpoints/admin.js.map +1 -0
  143. package/dist/routes/endpoints/checkpointing.d.ts +76 -0
  144. package/dist/routes/endpoints/checkpointing.js +36 -0
  145. package/dist/routes/endpoints/checkpointing.js.map +1 -0
  146. package/dist/routes/endpoints/dev.d.ts +312 -0
  147. package/dist/routes/endpoints/dev.js +172 -0
  148. package/dist/routes/endpoints/dev.js.map +1 -0
  149. package/dist/routes/endpoints/route-endpoints-index.d.ts +6 -0
  150. package/dist/routes/endpoints/route-endpoints-index.js +7 -0
  151. package/dist/routes/endpoints/route-endpoints-index.js.map +1 -0
  152. package/dist/routes/endpoints/socket-route.d.ts +2 -0
  153. package/dist/routes/endpoints/socket-route.js +119 -0
  154. package/dist/routes/endpoints/socket-route.js.map +1 -0
  155. package/dist/routes/endpoints/sync-rules.d.ts +174 -0
  156. package/dist/routes/endpoints/sync-rules.js +202 -0
  157. package/dist/routes/endpoints/sync-rules.js.map +1 -0
  158. package/dist/routes/endpoints/sync-stream.d.ts +132 -0
  159. package/dist/routes/endpoints/sync-stream.js +83 -0
  160. package/dist/routes/endpoints/sync-stream.js.map +1 -0
  161. package/dist/routes/hooks.d.ts +10 -0
  162. package/dist/routes/hooks.js +32 -0
  163. package/dist/routes/hooks.js.map +1 -0
  164. package/dist/routes/route-register.d.ts +10 -0
  165. package/dist/routes/route-register.js +87 -0
  166. package/dist/routes/route-register.js.map +1 -0
  167. package/dist/routes/router-socket.d.ts +10 -0
  168. package/dist/routes/router-socket.js +5 -0
  169. package/dist/routes/router-socket.js.map +1 -0
  170. package/dist/routes/router.d.ts +26 -0
  171. package/dist/routes/router.js +7 -0
  172. package/dist/routes/router.js.map +1 -0
  173. package/dist/routes/routes-index.d.ts +6 -0
  174. package/dist/routes/routes-index.js +7 -0
  175. package/dist/routes/routes-index.js.map +1 -0
  176. package/dist/runner/teardown.d.ts +2 -0
  177. package/dist/runner/teardown.js +94 -0
  178. package/dist/runner/teardown.js.map +1 -0
  179. package/dist/storage/BucketStorage.d.ts +307 -0
  180. package/dist/storage/BucketStorage.js +25 -0
  181. package/dist/storage/BucketStorage.js.map +1 -0
  182. package/dist/storage/ChecksumCache.d.ts +50 -0
  183. package/dist/storage/ChecksumCache.js +234 -0
  184. package/dist/storage/ChecksumCache.js.map +1 -0
  185. package/dist/storage/MongoBucketStorage.d.ts +52 -0
  186. package/dist/storage/MongoBucketStorage.js +409 -0
  187. package/dist/storage/MongoBucketStorage.js.map +1 -0
  188. package/dist/storage/SourceTable.d.ts +39 -0
  189. package/dist/storage/SourceTable.js +50 -0
  190. package/dist/storage/SourceTable.js.map +1 -0
  191. package/dist/storage/mongo/MongoBucketBatch.d.ts +48 -0
  192. package/dist/storage/mongo/MongoBucketBatch.js +581 -0
  193. package/dist/storage/mongo/MongoBucketBatch.js.map +1 -0
  194. package/dist/storage/mongo/MongoIdSequence.d.ts +12 -0
  195. package/dist/storage/mongo/MongoIdSequence.js +21 -0
  196. package/dist/storage/mongo/MongoIdSequence.js.map +1 -0
  197. package/dist/storage/mongo/MongoPersistedSyncRules.d.ts +9 -0
  198. package/dist/storage/mongo/MongoPersistedSyncRules.js +9 -0
  199. package/dist/storage/mongo/MongoPersistedSyncRules.js.map +1 -0
  200. package/dist/storage/mongo/MongoPersistedSyncRulesContent.d.ts +20 -0
  201. package/dist/storage/mongo/MongoPersistedSyncRulesContent.js +26 -0
  202. package/dist/storage/mongo/MongoPersistedSyncRulesContent.js.map +1 -0
  203. package/dist/storage/mongo/MongoSyncBucketStorage.d.ts +29 -0
  204. package/dist/storage/mongo/MongoSyncBucketStorage.js +391 -0
  205. package/dist/storage/mongo/MongoSyncBucketStorage.js.map +1 -0
  206. package/dist/storage/mongo/MongoSyncRulesLock.d.ts +16 -0
  207. package/dist/storage/mongo/MongoSyncRulesLock.js +65 -0
  208. package/dist/storage/mongo/MongoSyncRulesLock.js.map +1 -0
  209. package/dist/storage/mongo/OperationBatch.d.ts +26 -0
  210. package/dist/storage/mongo/OperationBatch.js +101 -0
  211. package/dist/storage/mongo/OperationBatch.js.map +1 -0
  212. package/dist/storage/mongo/PersistedBatch.d.ts +46 -0
  213. package/dist/storage/mongo/PersistedBatch.js +213 -0
  214. package/dist/storage/mongo/PersistedBatch.js.map +1 -0
  215. package/dist/storage/mongo/db.d.ts +26 -0
  216. package/dist/storage/mongo/db.js +35 -0
  217. package/dist/storage/mongo/db.js.map +1 -0
  218. package/dist/storage/mongo/models.d.ts +140 -0
  219. package/dist/storage/mongo/models.js +27 -0
  220. package/dist/storage/mongo/models.js.map +1 -0
  221. package/dist/storage/mongo/util.d.ts +26 -0
  222. package/dist/storage/mongo/util.js +81 -0
  223. package/dist/storage/mongo/util.js.map +1 -0
  224. package/dist/storage/storage-index.d.ts +14 -0
  225. package/dist/storage/storage-index.js +15 -0
  226. package/dist/storage/storage-index.js.map +1 -0
  227. package/dist/sync/BroadcastIterable.d.ts +38 -0
  228. package/dist/sync/BroadcastIterable.js +153 -0
  229. package/dist/sync/BroadcastIterable.js.map +1 -0
  230. package/dist/sync/LastValueSink.d.ts +25 -0
  231. package/dist/sync/LastValueSink.js +84 -0
  232. package/dist/sync/LastValueSink.js.map +1 -0
  233. package/dist/sync/merge.d.ts +39 -0
  234. package/dist/sync/merge.js +175 -0
  235. package/dist/sync/merge.js.map +1 -0
  236. package/dist/sync/safeRace.d.ts +1 -0
  237. package/dist/sync/safeRace.js +91 -0
  238. package/dist/sync/safeRace.js.map +1 -0
  239. package/dist/sync/sync-index.d.ts +6 -0
  240. package/dist/sync/sync-index.js +7 -0
  241. package/dist/sync/sync-index.js.map +1 -0
  242. package/dist/sync/sync.d.ts +18 -0
  243. package/dist/sync/sync.js +259 -0
  244. package/dist/sync/sync.js.map +1 -0
  245. package/dist/sync/util.d.ts +26 -0
  246. package/dist/sync/util.js +73 -0
  247. package/dist/sync/util.js.map +1 -0
  248. package/dist/system/CorePowerSyncSystem.d.ts +23 -0
  249. package/dist/system/CorePowerSyncSystem.js +52 -0
  250. package/dist/system/CorePowerSyncSystem.js.map +1 -0
  251. package/dist/system/system-index.d.ts +1 -0
  252. package/dist/system/system-index.js +2 -0
  253. package/dist/system/system-index.js.map +1 -0
  254. package/dist/util/Mutex.d.ts +47 -0
  255. package/dist/util/Mutex.js +132 -0
  256. package/dist/util/Mutex.js.map +1 -0
  257. package/dist/util/PgManager.d.ts +24 -0
  258. package/dist/util/PgManager.js +55 -0
  259. package/dist/util/PgManager.js.map +1 -0
  260. package/dist/util/alerting.d.ts +2 -0
  261. package/dist/util/alerting.js +8 -0
  262. package/dist/util/alerting.js.map +1 -0
  263. package/dist/util/config/collectors/config-collector.d.ts +29 -0
  264. package/dist/util/config/collectors/config-collector.js +116 -0
  265. package/dist/util/config/collectors/config-collector.js.map +1 -0
  266. package/dist/util/config/collectors/impl/base64-config-collector.d.ts +6 -0
  267. package/dist/util/config/collectors/impl/base64-config-collector.js +15 -0
  268. package/dist/util/config/collectors/impl/base64-config-collector.js.map +1 -0
  269. package/dist/util/config/collectors/impl/fallback-config-collector.d.ts +11 -0
  270. package/dist/util/config/collectors/impl/fallback-config-collector.js +19 -0
  271. package/dist/util/config/collectors/impl/fallback-config-collector.js.map +1 -0
  272. package/dist/util/config/collectors/impl/filesystem-config-collector.d.ts +6 -0
  273. package/dist/util/config/collectors/impl/filesystem-config-collector.js +37 -0
  274. package/dist/util/config/collectors/impl/filesystem-config-collector.js.map +1 -0
  275. package/dist/util/config/compound-config-collector.d.ts +32 -0
  276. package/dist/util/config/compound-config-collector.js +130 -0
  277. package/dist/util/config/compound-config-collector.js.map +1 -0
  278. package/dist/util/config/sync-rules/impl/base64-sync-rules-collector.d.ts +7 -0
  279. package/dist/util/config/sync-rules/impl/base64-sync-rules-collector.js +17 -0
  280. package/dist/util/config/sync-rules/impl/base64-sync-rules-collector.js.map +1 -0
  281. package/dist/util/config/sync-rules/impl/filesystem-sync-rules-collector.d.ts +7 -0
  282. package/dist/util/config/sync-rules/impl/filesystem-sync-rules-collector.js +21 -0
  283. package/dist/util/config/sync-rules/impl/filesystem-sync-rules-collector.js.map +1 -0
  284. package/dist/util/config/sync-rules/impl/inline-sync-rules-collector.d.ts +7 -0
  285. package/dist/util/config/sync-rules/impl/inline-sync-rules-collector.js +17 -0
  286. package/dist/util/config/sync-rules/impl/inline-sync-rules-collector.js.map +1 -0
  287. package/dist/util/config/sync-rules/sync-collector.d.ts +6 -0
  288. package/dist/util/config/sync-rules/sync-collector.js +3 -0
  289. package/dist/util/config/sync-rules/sync-collector.js.map +1 -0
  290. package/dist/util/config/types.d.ts +57 -0
  291. package/dist/util/config/types.js +7 -0
  292. package/dist/util/config/types.js.map +1 -0
  293. package/dist/util/config.d.ts +7 -0
  294. package/dist/util/config.js +35 -0
  295. package/dist/util/config.js.map +1 -0
  296. package/dist/util/env.d.ts +9 -0
  297. package/dist/util/env.js +26 -0
  298. package/dist/util/env.js.map +1 -0
  299. package/dist/util/memory-tracking.d.ts +7 -0
  300. package/dist/util/memory-tracking.js +58 -0
  301. package/dist/util/memory-tracking.js.map +1 -0
  302. package/dist/util/migration_lib.d.ts +11 -0
  303. package/dist/util/migration_lib.js +64 -0
  304. package/dist/util/migration_lib.js.map +1 -0
  305. package/dist/util/pgwire_utils.d.ts +24 -0
  306. package/dist/util/pgwire_utils.js +117 -0
  307. package/dist/util/pgwire_utils.js.map +1 -0
  308. package/dist/util/populate_test_data.d.ts +8 -0
  309. package/dist/util/populate_test_data.js +65 -0
  310. package/dist/util/populate_test_data.js.map +1 -0
  311. package/dist/util/protocol-types.d.ts +182 -0
  312. package/dist/util/protocol-types.js +42 -0
  313. package/dist/util/protocol-types.js.map +1 -0
  314. package/dist/util/secs.d.ts +2 -0
  315. package/dist/util/secs.js +49 -0
  316. package/dist/util/secs.js.map +1 -0
  317. package/dist/util/util-index.d.ts +22 -0
  318. package/dist/util/util-index.js +23 -0
  319. package/dist/util/util-index.js.map +1 -0
  320. package/dist/util/utils.d.ts +17 -0
  321. package/dist/util/utils.js +92 -0
  322. package/dist/util/utils.js.map +1 -0
  323. package/package.json +59 -0
  324. package/src/api/api-index.ts +2 -0
  325. package/src/api/diagnostics.ts +221 -0
  326. package/src/api/schema.ts +99 -0
  327. package/src/auth/CachedKeyCollector.ts +132 -0
  328. package/src/auth/CompoundKeyCollector.ts +33 -0
  329. package/src/auth/JwtPayload.ts +11 -0
  330. package/src/auth/KeyCollector.ts +27 -0
  331. package/src/auth/KeySpec.ts +67 -0
  332. package/src/auth/KeyStore.ts +156 -0
  333. package/src/auth/LeakyBucket.ts +66 -0
  334. package/src/auth/RemoteJWKSCollector.ts +130 -0
  335. package/src/auth/StaticKeyCollector.ts +21 -0
  336. package/src/auth/SupabaseKeyCollector.ts +67 -0
  337. package/src/auth/auth-index.ts +10 -0
  338. package/src/db/db-index.ts +1 -0
  339. package/src/db/mongo.ts +72 -0
  340. package/src/entry/cli-entry.ts +40 -0
  341. package/src/entry/commands/config-command.ts +36 -0
  342. package/src/entry/commands/migrate-action.ts +25 -0
  343. package/src/entry/commands/start-action.ts +24 -0
  344. package/src/entry/commands/teardown-action.ts +23 -0
  345. package/src/entry/entry-index.ts +5 -0
  346. package/src/index.ts +40 -0
  347. package/src/locks/LockManager.ts +16 -0
  348. package/src/locks/MongoLocks.ts +142 -0
  349. package/src/locks/locks-index.ts +2 -0
  350. package/src/metrics/Metrics.ts +265 -0
  351. package/src/migrations/db/migrations/1684951997326-init.ts +33 -0
  352. package/src/migrations/db/migrations/1688556755264-initial-sync-rules.ts +5 -0
  353. package/src/migrations/db/migrations/1702295701188-sync-rule-state.ts +99 -0
  354. package/src/migrations/db/migrations/1711543888062-write-checkpoint-index.ts +32 -0
  355. package/src/migrations/definitions.ts +21 -0
  356. package/src/migrations/executor.ts +87 -0
  357. package/src/migrations/migrations-index.ts +3 -0
  358. package/src/migrations/migrations.ts +118 -0
  359. package/src/migrations/store/migration-store.ts +63 -0
  360. package/src/replication/ErrorRateLimiter.ts +50 -0
  361. package/src/replication/PgRelation.ts +42 -0
  362. package/src/replication/WalConnection.ts +227 -0
  363. package/src/replication/WalStream.ts +624 -0
  364. package/src/replication/WalStreamManager.ts +213 -0
  365. package/src/replication/WalStreamRunner.ts +180 -0
  366. package/src/replication/replication-index.ts +7 -0
  367. package/src/replication/util.ts +76 -0
  368. package/src/routes/auth.ts +215 -0
  369. package/src/routes/endpoints/admin.ts +237 -0
  370. package/src/routes/endpoints/checkpointing.ts +41 -0
  371. package/src/routes/endpoints/dev.ts +199 -0
  372. package/src/routes/endpoints/route-endpoints-index.ts +6 -0
  373. package/src/routes/endpoints/socket-route.ts +135 -0
  374. package/src/routes/endpoints/sync-rules.ts +227 -0
  375. package/src/routes/endpoints/sync-stream.ts +101 -0
  376. package/src/routes/hooks.ts +46 -0
  377. package/src/routes/route-register.ts +104 -0
  378. package/src/routes/router-socket.ts +13 -0
  379. package/src/routes/router.ts +46 -0
  380. package/src/routes/routes-index.ts +6 -0
  381. package/src/runner/teardown.ts +108 -0
  382. package/src/storage/BucketStorage.ts +396 -0
  383. package/src/storage/ChecksumCache.ts +294 -0
  384. package/src/storage/MongoBucketStorage.ts +519 -0
  385. package/src/storage/SourceTable.ts +60 -0
  386. package/src/storage/mongo/MongoBucketBatch.ts +752 -0
  387. package/src/storage/mongo/MongoIdSequence.ts +24 -0
  388. package/src/storage/mongo/MongoPersistedSyncRules.ts +16 -0
  389. package/src/storage/mongo/MongoPersistedSyncRulesContent.ts +47 -0
  390. package/src/storage/mongo/MongoSyncBucketStorage.ts +533 -0
  391. package/src/storage/mongo/MongoSyncRulesLock.ts +81 -0
  392. package/src/storage/mongo/OperationBatch.ts +115 -0
  393. package/src/storage/mongo/PersistedBatch.ts +268 -0
  394. package/src/storage/mongo/db.ts +73 -0
  395. package/src/storage/mongo/models.ts +162 -0
  396. package/src/storage/mongo/util.ts +88 -0
  397. package/src/storage/storage-index.ts +15 -0
  398. package/src/sync/BroadcastIterable.ts +161 -0
  399. package/src/sync/LastValueSink.ts +100 -0
  400. package/src/sync/merge.ts +200 -0
  401. package/src/sync/safeRace.ts +99 -0
  402. package/src/sync/sync-index.ts +6 -0
  403. package/src/sync/sync.ts +319 -0
  404. package/src/sync/util.ts +98 -0
  405. package/src/system/CorePowerSyncSystem.ts +64 -0
  406. package/src/system/system-index.ts +1 -0
  407. package/src/util/Mutex.ts +159 -0
  408. package/src/util/PgManager.ts +64 -0
  409. package/src/util/alerting.ts +9 -0
  410. package/src/util/config/collectors/config-collector.ts +143 -0
  411. package/src/util/config/collectors/impl/base64-config-collector.ts +18 -0
  412. package/src/util/config/collectors/impl/fallback-config-collector.ts +22 -0
  413. package/src/util/config/collectors/impl/filesystem-config-collector.ts +43 -0
  414. package/src/util/config/compound-config-collector.ts +176 -0
  415. package/src/util/config/sync-rules/impl/base64-sync-rules-collector.ts +21 -0
  416. package/src/util/config/sync-rules/impl/filesystem-sync-rules-collector.ts +26 -0
  417. package/src/util/config/sync-rules/impl/inline-sync-rules-collector.ts +21 -0
  418. package/src/util/config/sync-rules/sync-collector.ts +8 -0
  419. package/src/util/config/types.ts +66 -0
  420. package/src/util/config.ts +39 -0
  421. package/src/util/env.ts +30 -0
  422. package/src/util/memory-tracking.ts +67 -0
  423. package/src/util/migration_lib.ts +79 -0
  424. package/src/util/pgwire_utils.ts +139 -0
  425. package/src/util/populate_test_data.ts +78 -0
  426. package/src/util/protocol-types.ts +228 -0
  427. package/src/util/secs.ts +54 -0
  428. package/src/util/util-index.ts +25 -0
  429. package/src/util/utils.ts +122 -0
  430. package/test/src/__snapshots__/pg_test.test.ts.snap +256 -0
  431. package/test/src/__snapshots__/sync.test.ts.snap +247 -0
  432. package/test/src/auth.test.ts +342 -0
  433. package/test/src/broadcast_iterable.test.ts +156 -0
  434. package/test/src/checksum_cache.test.ts +436 -0
  435. package/test/src/data_storage.test.ts +1176 -0
  436. package/test/src/env.ts +8 -0
  437. package/test/src/large_batch.test.ts +194 -0
  438. package/test/src/merge_iterable.test.ts +355 -0
  439. package/test/src/pg_test.test.ts +450 -0
  440. package/test/src/schema_changes.test.ts +545 -0
  441. package/test/src/setup.ts +7 -0
  442. package/test/src/slow_tests.test.ts +257 -0
  443. package/test/src/sql_functions.test.ts +254 -0
  444. package/test/src/sql_operators.test.ts +132 -0
  445. package/test/src/sync.test.ts +293 -0
  446. package/test/src/sync_rules.test.ts +1053 -0
  447. package/test/src/util.ts +76 -0
  448. package/test/src/validation.test.ts +63 -0
  449. package/test/src/wal_stream.test.ts +319 -0
  450. package/test/src/wal_stream_utils.ts +147 -0
  451. package/test/tsconfig.json +20 -0
  452. package/tsconfig.json +31 -0
  453. package/tsconfig.tsbuildinfo +1 -0
  454. package/vitest.config.ts +9 -0
package/package.json ADDED
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "@powersync/service-core",
3
+ "repository": "https://github.com/powersync-ja/powersync-service",
4
+ "types": "dist/index.d.ts",
5
+ "publishConfig": {
6
+ "access": "public"
7
+ },
8
+ "version": "0.0.0-dev-20240620165206",
9
+ "main": "dist/index.js",
10
+ "license": "FSL-1.1-Apache-2.0",
11
+ "type": "module",
12
+ "dependencies": {
13
+ "@js-sdsl/ordered-set": "^4.4.2",
14
+ "@opentelemetry/api": "~1.8.0",
15
+ "@opentelemetry/exporter-metrics-otlp-http": "^0.51.1",
16
+ "@opentelemetry/exporter-prometheus": "^0.51.1",
17
+ "@opentelemetry/resources": "^1.24.1",
18
+ "@opentelemetry/sdk-metrics": "1.24.1",
19
+ "async": "^3.2.4",
20
+ "async-mutex": "^0.5.0",
21
+ "bson": "^6.6.0",
22
+ "commander": "^12.0.0",
23
+ "cors": "^2.8.5",
24
+ "ipaddr.js": "^2.1.0",
25
+ "ix": "^5.0.0",
26
+ "jose": "^4.15.1",
27
+ "lodash": "^4.17.21",
28
+ "lru-cache": "^10.2.2",
29
+ "mongodb": "^6.7.0",
30
+ "node-fetch": "^3.3.2",
31
+ "pgwire": "github:kagis/pgwire#f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87",
32
+ "ts-codec": "^1.2.2",
33
+ "uuid": "^9.0.1",
34
+ "winston": "^3.13.0",
35
+ "yaml": "^2.3.2",
36
+ "@powersync/lib-services-framework": "0.0.0-dev-20240620165206",
37
+ "@powersync/service-jpgwire": "0.17.13",
38
+ "@powersync/service-jsonbig": "0.17.10",
39
+ "@powersync/service-rsocket-router": "0.0.0-dev-20240620165206",
40
+ "@powersync/service-sync-rules": "0.17.10",
41
+ "@powersync/service-types": "0.1.0"
42
+ },
43
+ "devDependencies": {
44
+ "@types/async": "^3.2.24",
45
+ "@types/lodash": "^4.17.5",
46
+ "@types/uuid": "^9.0.4",
47
+ "fastify": "4.23.2",
48
+ "fastify-plugin": "^4.5.1",
49
+ "typescript": "^5.2.2",
50
+ "vite-tsconfig-paths": "^4.3.2",
51
+ "vitest": "^0.34.6"
52
+ },
53
+ "scripts": {
54
+ "build": "tsc -b",
55
+ "build:tests": "tsc -b test/tsconfig.json",
56
+ "test": "vitest --no-threads",
57
+ "clean": "rm -rf ./lib && tsc -b --clean"
58
+ }
59
+ }
@@ -0,0 +1,2 @@
1
+ export * from './diagnostics.js';
2
+ export * from './schema.js';
@@ -0,0 +1,221 @@
1
+ import { DEFAULT_TAG, SourceTableInterface, SqlSyncRules } from '@powersync/service-sync-rules';
2
+ import { pgwireRows } from '@powersync/service-jpgwire';
3
+ import { ConnectionStatus, SyncRulesStatus, TableInfo, baseUri } from '@powersync/service-types';
4
+
5
+ import * as replication from '../replication/replication-index.js';
6
+ import * as storage from '../storage/storage-index.js';
7
+ import * as util from '../util/util-index.js';
8
+
9
+ import { CorePowerSyncSystem } from '../system/CorePowerSyncSystem.js';
10
+ import { logger } from '@powersync/lib-services-framework';
11
+
12
+ export async function getConnectionStatus(system: CorePowerSyncSystem): Promise<ConnectionStatus | null> {
13
+ if (system.pgwire_pool == null) {
14
+ return null;
15
+ }
16
+
17
+ const pool = system.requirePgPool();
18
+
19
+ const base = {
20
+ id: system.config.connection!.id,
21
+ postgres_uri: baseUri(system.config.connection!)
22
+ };
23
+ try {
24
+ await util.retriedQuery(pool, `SELECT 'PowerSync connection test'`);
25
+ } catch (e) {
26
+ return {
27
+ ...base,
28
+ connected: false,
29
+ errors: [{ level: 'fatal', message: e.message }]
30
+ };
31
+ }
32
+
33
+ try {
34
+ await replication.checkSourceConfiguration(pool);
35
+ } catch (e) {
36
+ return {
37
+ ...base,
38
+ connected: true,
39
+ errors: [{ level: 'fatal', message: e.message }]
40
+ };
41
+ }
42
+
43
+ return {
44
+ ...base,
45
+ connected: true,
46
+ errors: []
47
+ };
48
+ }
49
+
50
+ export interface DiagnosticsOptions {
51
+ /**
52
+ * Include sync rules content in response.
53
+ */
54
+ include_content?: boolean;
55
+
56
+ /**
57
+ * Check against storage database.
58
+ *
59
+ * If false, uses placeholder values for e.g. initial_replication_done.
60
+ */
61
+ live_status: boolean;
62
+
63
+ /**
64
+ * Check against the source postgres connection.
65
+ */
66
+ check_connection: boolean;
67
+ }
68
+
69
+ export async function getSyncRulesStatus(
70
+ sync_rules: storage.PersistedSyncRulesContent | null,
71
+ system: CorePowerSyncSystem,
72
+ options: DiagnosticsOptions
73
+ ): Promise<SyncRulesStatus | undefined> {
74
+ if (sync_rules == null) {
75
+ return undefined;
76
+ }
77
+
78
+ const include_content = options.include_content ?? false;
79
+ const live_status = options.live_status ?? false;
80
+ const check_connection = options.check_connection ?? false;
81
+
82
+ let rules: SqlSyncRules;
83
+ let persisted: storage.PersistedSyncRules;
84
+ try {
85
+ persisted = sync_rules.parsed();
86
+ rules = persisted.sync_rules;
87
+ } catch (e) {
88
+ return {
89
+ content: include_content ? sync_rules.sync_rules_content : undefined,
90
+ connections: [],
91
+ errors: [{ level: 'fatal', message: e.message }]
92
+ };
93
+ }
94
+
95
+ const systemStorage = live_status ? await system.storage.getInstance(persisted) : undefined;
96
+ const status = await systemStorage?.getStatus();
97
+ let replication_lag_bytes: number | undefined = undefined;
98
+
99
+ let tables_flat: TableInfo[] = [];
100
+
101
+ if (check_connection) {
102
+ const pool = system.requirePgPool();
103
+
104
+ const source_table_patterns = rules.getSourceTables();
105
+ const wc = new replication.WalConnection({
106
+ db: pool,
107
+ sync_rules: rules
108
+ });
109
+ const resolved_tables = await wc.getDebugTablesInfo(source_table_patterns);
110
+ tables_flat = resolved_tables.flatMap((info) => {
111
+ if (info.table) {
112
+ return [info.table];
113
+ } else if (info.tables) {
114
+ return info.tables;
115
+ } else {
116
+ return [];
117
+ }
118
+ });
119
+
120
+ if (systemStorage) {
121
+ try {
122
+ const results = await util.retriedQuery(pool, {
123
+ statement: `SELECT
124
+ slot_name,
125
+ confirmed_flush_lsn,
126
+ pg_current_wal_lsn(),
127
+ (pg_current_wal_lsn() - confirmed_flush_lsn) AS lsn_distance
128
+ FROM pg_replication_slots WHERE slot_name = $1 LIMIT 1;`,
129
+ params: [{ type: 'varchar', value: systemStorage!.slot_name }]
130
+ });
131
+ const [row] = pgwireRows(results);
132
+ if (row) {
133
+ replication_lag_bytes = Number(row.lsn_distance);
134
+ }
135
+ } catch (e) {
136
+ // Ignore
137
+ logger.warn(`Unable to get replication lag`, e);
138
+ }
139
+ }
140
+ } else {
141
+ const source_table_patterns = rules.getSourceTables();
142
+ const tag = system.config.connection!.tag ?? DEFAULT_TAG;
143
+
144
+ tables_flat = source_table_patterns.map((pattern): TableInfo => {
145
+ if (pattern.isWildcard) {
146
+ return {
147
+ schema: pattern.schema,
148
+ name: pattern.tablePrefix,
149
+ pattern: pattern.isWildcard ? pattern.tablePattern : undefined,
150
+
151
+ data_queries: false,
152
+ parameter_queries: false,
153
+ replication_id: [],
154
+ errors: [{ level: 'fatal', message: 'connection failed' }]
155
+ };
156
+ } else {
157
+ const source: SourceTableInterface = {
158
+ connectionTag: tag,
159
+ schema: pattern.schema,
160
+ table: pattern.tablePattern
161
+ };
162
+ const syncData = rules.tableSyncsData(source);
163
+ const syncParameters = rules.tableSyncsParameters(source);
164
+ return {
165
+ schema: pattern.schema,
166
+ name: pattern.name,
167
+ data_queries: syncData,
168
+ parameter_queries: syncParameters,
169
+ replication_id: [],
170
+ errors: [{ level: 'fatal', message: 'connection failed' }]
171
+ };
172
+ }
173
+ });
174
+ }
175
+
176
+ const errors = tables_flat.flatMap((info) => info.errors);
177
+ if (sync_rules.last_fatal_error) {
178
+ errors.push({ level: 'fatal', message: sync_rules.last_fatal_error });
179
+ }
180
+ errors.push(
181
+ ...rules.errors.map((e) => {
182
+ return {
183
+ level: e.type,
184
+ message: e.message
185
+ };
186
+ })
187
+ );
188
+
189
+ return {
190
+ content: include_content ? sync_rules.sync_rules_content : undefined,
191
+ connections: [
192
+ {
193
+ id: system.config.connection!.id,
194
+ tag: system.config.connection!.tag ?? DEFAULT_TAG,
195
+ slot_name: sync_rules.slot_name,
196
+ initial_replication_done: status?.snapshot_done ?? false,
197
+ // TODO: Rename?
198
+ last_lsn: status?.checkpoint_lsn ?? undefined,
199
+ last_checkpoint_ts: sync_rules.last_checkpoint_ts?.toISOString(),
200
+ last_keepalive_ts: sync_rules.last_keepalive_ts?.toISOString(),
201
+ replication_lag_bytes: replication_lag_bytes,
202
+ tables: tables_flat
203
+ }
204
+ ],
205
+ errors: deduplicate(errors)
206
+ };
207
+ }
208
+
209
+ function deduplicate(errors: { level: 'warning' | 'fatal'; message: string }[]) {
210
+ let seen = new Set<string>();
211
+ let result: { level: 'warning' | 'fatal'; message: string }[] = [];
212
+ for (let error of errors) {
213
+ const key = JSON.stringify(error);
214
+ if (seen.has(key)) {
215
+ continue;
216
+ }
217
+ seen.add(key);
218
+ result.push(error);
219
+ }
220
+ return result;
221
+ }
@@ -0,0 +1,99 @@
1
+ import type * as pgwire from '@powersync/service-jpgwire';
2
+ import { pgwireRows } from '@powersync/service-jpgwire';
3
+ import { DatabaseSchema, internal_routes } from '@powersync/service-types';
4
+
5
+ import * as util from '../util/util-index.js';
6
+ import { CorePowerSyncSystem } from '../system/CorePowerSyncSystem.js';
7
+
8
+ export async function getConnectionsSchema(system: CorePowerSyncSystem): Promise<internal_routes.GetSchemaResponse> {
9
+ if (system.config.connection == null) {
10
+ return { connections: [] };
11
+ }
12
+ const schemas = await getConnectionSchema(system.requirePgPool());
13
+ return {
14
+ connections: [
15
+ {
16
+ schemas,
17
+ tag: system.config.connection!.tag,
18
+ id: system.config.connection!.id
19
+ }
20
+ ]
21
+ };
22
+ }
23
+
24
+ export async function getConnectionSchema(db: pgwire.PgClient): Promise<DatabaseSchema[]> {
25
+ // https://github.com/Borvik/vscode-postgres/blob/88ec5ed061a0c9bced6c5d4ec122d0759c3f3247/src/language/server.ts
26
+ const results = await util.retriedQuery(
27
+ db,
28
+ `SELECT
29
+ tbl.schemaname,
30
+ tbl.tablename,
31
+ tbl.quoted_name,
32
+ json_agg(a ORDER BY attnum) as columns
33
+ FROM
34
+ (
35
+ SELECT
36
+ n.nspname as schemaname,
37
+ c.relname as tablename,
38
+ (quote_ident(n.nspname) || '.' || quote_ident(c.relname)) as quoted_name
39
+ FROM
40
+ pg_catalog.pg_class c
41
+ JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
42
+ WHERE
43
+ c.relkind = 'r'
44
+ AND n.nspname not in ('information_schema', 'pg_catalog', 'pg_toast')
45
+ AND n.nspname not like 'pg_temp_%'
46
+ AND n.nspname not like 'pg_toast_temp_%'
47
+ AND c.relnatts > 0
48
+ AND has_schema_privilege(n.oid, 'USAGE') = true
49
+ AND has_table_privilege(quote_ident(n.nspname) || '.' || quote_ident(c.relname), 'SELECT, INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER') = true
50
+ ) as tbl
51
+ LEFT JOIN (
52
+ SELECT
53
+ attrelid,
54
+ attname,
55
+ format_type(atttypid, atttypmod) as data_type,
56
+ (SELECT typname FROM pg_catalog.pg_type WHERE oid = atttypid) as pg_type,
57
+ attnum,
58
+ attisdropped
59
+ FROM
60
+ pg_attribute
61
+ ) as a ON (
62
+ a.attrelid = tbl.quoted_name::regclass
63
+ AND a.attnum > 0
64
+ AND NOT a.attisdropped
65
+ AND has_column_privilege(tbl.quoted_name, a.attname, 'SELECT, INSERT, UPDATE, REFERENCES')
66
+ )
67
+ GROUP BY schemaname, tablename, quoted_name`
68
+ );
69
+ const rows = pgwireRows(results);
70
+
71
+ let schemas: Record<string, any> = {};
72
+
73
+ for (let row of rows) {
74
+ const schema = (schemas[row.schemaname] ??= {
75
+ name: row.schemaname,
76
+ tables: []
77
+ });
78
+ const table = {
79
+ name: row.tablename,
80
+ columns: [] as any[]
81
+ };
82
+ schema.tables.push(table);
83
+
84
+ const columnInfo = JSON.parse(row.columns);
85
+ for (let column of columnInfo) {
86
+ let pg_type = column.pg_type as string;
87
+ if (pg_type.startsWith('_')) {
88
+ pg_type = `${pg_type.substring(1)}[]`;
89
+ }
90
+ table.columns.push({
91
+ name: column.attname,
92
+ type: column.data_type,
93
+ pg_type: pg_type
94
+ });
95
+ }
96
+ }
97
+
98
+ return Object.values(schemas);
99
+ }
@@ -0,0 +1,132 @@
1
+ import * as jose from 'jose';
2
+ import timers from 'timers/promises';
3
+ import { KeySpec } from './KeySpec.js';
4
+ import { LeakyBucket } from './LeakyBucket.js';
5
+ import { KeyCollector, KeyResult } from './KeyCollector.js';
6
+
7
+ /**
8
+ * Manages caching and refreshing for a key collector.
9
+ *
10
+ * Cache refreshing is activity-based, instead of automatically refreshing.
11
+ *
12
+ * Generally:
13
+ * * If the last refresh was > 5 minutes ago, trigger a background refresh, but use the cached keys.
14
+ * * If the last refresh was > 60 minutes ago, discard the cache, and force a refresh.
15
+ * * If the last refresh resulted in an error, refresh based on a retry delay, but use cached keys.
16
+ */
17
+
18
+ export class CachedKeyCollector implements KeyCollector {
19
+ private currentKeys: KeySpec[] = [];
20
+ /**
21
+ * The time that currentKeys was set.
22
+ */
23
+ private keyTimestamp: number = 0;
24
+
25
+ /**
26
+ * Refresh every 5 minutes - the default refresh rate.
27
+ */
28
+ private backgroundRefreshInterval = 300000;
29
+
30
+ /**
31
+ * Refresh a _max_ of once every minute at steady state.
32
+ *
33
+ * This controls the refresh rate under error conditions.
34
+ */
35
+ private rateLimiter = new LeakyBucket({ maxCapacity: 10, periodMs: 60000 });
36
+
37
+ /**
38
+ * Expire keys after an hour, if we failed to refresh in that time.
39
+ */
40
+ private keyExpiry = 3600000;
41
+
42
+ private currentErrors: jose.errors.JOSEError[] = [];
43
+ /**
44
+ * Indicates a "fatal" error that should be retried.
45
+ */
46
+ private error = false;
47
+
48
+ private refreshPromise: Promise<void> | undefined = undefined;
49
+
50
+ constructor(private source: KeyCollector) {}
51
+
52
+ async getKeys(): Promise<KeyResult> {
53
+ const now = Date.now();
54
+ if (now - this.keyTimestamp > this.keyExpiry) {
55
+ // Keys have expired - clear
56
+ this.currentKeys = [];
57
+ }
58
+
59
+ if (this.wantsRefresh()) {
60
+ // Trigger background refresh.
61
+ // This also sets refreshPromise
62
+ this.refresh();
63
+ }
64
+
65
+ if (now - this.keyTimestamp > this.keyExpiry) {
66
+ // Keys have expired - wait for fetching new keys
67
+ // It is possible that the refresh was actually triggered,
68
+ // e.g. in the case of waiting for error retries.
69
+ // In the case of very slow requests, we don't wait for it to complete, but the
70
+ // request can still complete in the background.
71
+ const timeout = timers.setTimeout(3000);
72
+ await Promise.race([this.refreshPromise, timeout]);
73
+ }
74
+
75
+ return { keys: this.currentKeys, errors: this.currentErrors };
76
+ }
77
+
78
+ private refresh() {
79
+ if (this.refreshPromise == null) {
80
+ if (!this.rateLimiter.allowed()) {
81
+ return;
82
+ }
83
+ this.refreshPromise = this.refreshInner().finally(() => {
84
+ this.refreshPromise = undefined;
85
+ });
86
+ }
87
+ return this.refreshPromise;
88
+ }
89
+
90
+ async noKeyFound(): Promise<void> {
91
+ // Refresh keys if allowed by the rate limiter
92
+ await this.refresh();
93
+ }
94
+
95
+ private async refreshInner() {
96
+ try {
97
+ const { keys, errors } = await this.source.getKeys();
98
+ // Partial or full result
99
+ this.currentKeys = keys;
100
+ this.currentErrors = errors;
101
+ this.keyTimestamp = Date.now();
102
+ this.error = false;
103
+ } catch (e) {
104
+ this.error = true;
105
+ // No result - keep previous keys
106
+ if (e instanceof jose.errors.JOSEError) {
107
+ this.currentErrors = [e];
108
+ } else {
109
+ this.currentErrors = [new jose.errors.JOSEError(e.message ?? 'Failed to fetch keys')];
110
+ }
111
+ }
112
+ }
113
+
114
+ private wantsRefresh() {
115
+ if (this.error) {
116
+ return true;
117
+ }
118
+
119
+ if (Date.now() - this.rateLimiter.lastGrantedRequest >= this.backgroundRefreshInterval) {
120
+ return true;
121
+ }
122
+
123
+ return false;
124
+ }
125
+
126
+ async addTimeForTests(time: number) {
127
+ this.keyTimestamp -= time;
128
+ this.rateLimiter.reset();
129
+ this.rateLimiter.lastGrantedRequest -= time;
130
+ await this.refreshPromise;
131
+ }
132
+ }
@@ -0,0 +1,33 @@
1
+ import * as jose from 'jose';
2
+ import { KeySpec } from './KeySpec.js';
3
+ import { KeyCollector, KeyResult } from './KeyCollector.js';
4
+
5
+ export class CompoundKeyCollector implements KeyCollector {
6
+ private collectors: KeyCollector[];
7
+
8
+ constructor(collectors?: KeyCollector[]) {
9
+ this.collectors = collectors ?? [];
10
+ }
11
+
12
+ add(collector: KeyCollector) {
13
+ this.collectors.push(collector);
14
+ }
15
+
16
+ async getKeys(): Promise<KeyResult> {
17
+ let keys: KeySpec[] = [];
18
+ let errors: jose.errors.JOSEError[] = [];
19
+ const promises = this.collectors.map((collector) =>
20
+ collector.getKeys().then((result) => {
21
+ keys.push(...result.keys);
22
+ errors.push(...result.errors);
23
+ })
24
+ );
25
+ await Promise.all(promises);
26
+ return { keys, errors };
27
+ }
28
+
29
+ async noKeyFound(): Promise<void> {
30
+ const promises = this.collectors.map((collector) => collector.noKeyFound?.());
31
+ await Promise.all(promises);
32
+ }
33
+ }
@@ -0,0 +1,11 @@
1
+ export interface JwtPayload {
2
+ /**
3
+ * user_id
4
+ */
5
+ sub: string;
6
+ parameters: Record<string, any>;
7
+
8
+ iss?: string | undefined;
9
+ exp: number;
10
+ iat: number;
11
+ }
@@ -0,0 +1,27 @@
1
+ import * as jose from 'jose';
2
+ import { KeySpec } from './KeySpec.js';
3
+
4
+ export interface KeyCollector {
5
+ /**
6
+ * Fetch keys for this collector.
7
+ *
8
+ * If a partial result is available, return keys and errors array.
9
+ * These errors are not retried, and previous keys not cached.
10
+ *
11
+ * If the request fails completely, throw an error. These errors are retried.
12
+ * In that case, previous keys may be cached and used.
13
+ */
14
+ getKeys(): Promise<KeyResult>;
15
+
16
+ /**
17
+ * Indicates that no matching key was found.
18
+ *
19
+ * The collector may use this as a hint to reload keys, although this is not a requirement.
20
+ */
21
+ noKeyFound?: () => Promise<void>;
22
+ }
23
+
24
+ export interface KeyResult {
25
+ errors: jose.errors.JOSEError[];
26
+ keys: KeySpec[];
27
+ }
@@ -0,0 +1,67 @@
1
+ import * as jose from 'jose';
2
+
3
+ export const HS_ALGORITHMS = ['HS256', 'HS384', 'HS512'];
4
+ export const RSA_ALGORITHMS = ['RS256', 'RS384', 'RS512'];
5
+ export const SUPPORTED_ALGORITHMS = [...HS_ALGORITHMS, ...RSA_ALGORITHMS];
6
+
7
+ export interface KeyOptions {
8
+ /**
9
+ * If configured, JWTs verified by this key must have one of these audiences
10
+ * in the `aud` claim, instead of the default.
11
+ */
12
+ requiresAudience?: string[];
13
+
14
+ /**
15
+ * If configured, JWTs verified by this key can have a maximum lifetime up to
16
+ * this value, instead of the default.
17
+ */
18
+ maxLifetimeSeconds?: number;
19
+ }
20
+
21
+ export class KeySpec {
22
+ key: jose.KeyLike;
23
+ source: jose.JWK;
24
+ options: KeyOptions;
25
+
26
+ static async importKey(key: jose.JWK, options?: KeyOptions): Promise<KeySpec> {
27
+ const parsed = (await jose.importJWK(key)) as jose.KeyLike;
28
+ return new KeySpec(key, parsed, options);
29
+ }
30
+
31
+ constructor(source: jose.JWK, key: jose.KeyLike, options?: KeyOptions) {
32
+ this.source = source;
33
+ this.key = key;
34
+ this.options = options ?? {};
35
+ }
36
+
37
+ get kid(): string | undefined {
38
+ return this.source.kid;
39
+ }
40
+
41
+ matchesAlgorithm(jwtAlg: string) {
42
+ if (this.source.alg) {
43
+ return jwtAlg == this.source.alg;
44
+ } else if (this.source.kty == 'RSA') {
45
+ return RSA_ALGORITHMS.includes(jwtAlg);
46
+ } else if (this.source.kty == 'oct') {
47
+ return HS_ALGORITHMS.includes(jwtAlg);
48
+ } else {
49
+ // We don't support 'ec' yet
50
+ return false;
51
+ }
52
+ }
53
+
54
+ async isValidSignature(token: string): Promise<boolean> {
55
+ try {
56
+ await jose.compactVerify(token, this.key);
57
+ return true;
58
+ } catch (e) {
59
+ if (e.code == 'ERR_JWS_SIGNATURE_VERIFICATION_FAILED') {
60
+ return false;
61
+ } else {
62
+ // Token format error most likely
63
+ throw e;
64
+ }
65
+ }
66
+ }
67
+ }