@powersync/service-core 0.2.1 → 0.3.0

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 (255) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/dist/api/diagnostics.js +2 -2
  3. package/dist/api/diagnostics.js.map +1 -1
  4. package/dist/api/schema.js.map +1 -1
  5. package/dist/auth/CachedKeyCollector.js.map +1 -1
  6. package/dist/auth/KeySpec.js.map +1 -1
  7. package/dist/auth/KeyStore.js +2 -2
  8. package/dist/auth/KeyStore.js.map +1 -1
  9. package/dist/auth/LeakyBucket.js.map +1 -1
  10. package/dist/auth/RemoteJWKSCollector.js.map +1 -1
  11. package/dist/auth/SupabaseKeyCollector.js.map +1 -1
  12. package/dist/db/mongo.js.map +1 -1
  13. package/dist/entry/cli-entry.js +2 -2
  14. package/dist/entry/cli-entry.js.map +1 -1
  15. package/dist/entry/commands/config-command.js.map +1 -1
  16. package/dist/entry/commands/migrate-action.js.map +1 -1
  17. package/dist/entry/commands/start-action.js.map +1 -1
  18. package/dist/entry/commands/teardown-action.js.map +1 -1
  19. package/dist/index.d.ts +3 -2
  20. package/dist/index.js +4 -2
  21. package/dist/index.js.map +1 -1
  22. package/dist/locks/LockManager.d.ts +10 -0
  23. package/dist/locks/LockManager.js +7 -0
  24. package/dist/locks/LockManager.js.map +1 -0
  25. package/dist/locks/MongoLocks.d.ts +36 -0
  26. package/dist/locks/MongoLocks.js +81 -0
  27. package/dist/locks/MongoLocks.js.map +1 -0
  28. package/dist/locks/locks-index.d.ts +2 -0
  29. package/dist/locks/locks-index.js +3 -0
  30. package/dist/locks/locks-index.js.map +1 -0
  31. package/dist/metrics/Metrics.js +6 -6
  32. package/dist/metrics/Metrics.js.map +1 -1
  33. package/dist/migrations/db/migrations/1684951997326-init.js.map +1 -1
  34. package/dist/migrations/db/migrations/1702295701188-sync-rule-state.js.map +1 -1
  35. package/dist/migrations/db/migrations/1711543888062-write-checkpoint-index.js.map +1 -1
  36. package/dist/migrations/definitions.d.ts +18 -0
  37. package/dist/migrations/definitions.js +6 -0
  38. package/dist/migrations/definitions.js.map +1 -0
  39. package/dist/migrations/executor.d.ts +16 -0
  40. package/dist/migrations/executor.js +64 -0
  41. package/dist/migrations/executor.js.map +1 -0
  42. package/dist/migrations/migrations-index.d.ts +3 -0
  43. package/dist/migrations/migrations-index.js +4 -0
  44. package/dist/migrations/migrations-index.js.map +1 -0
  45. package/dist/migrations/migrations.d.ts +1 -1
  46. package/dist/migrations/migrations.js +4 -8
  47. package/dist/migrations/migrations.js.map +1 -1
  48. package/dist/migrations/store/migration-store.d.ts +11 -0
  49. package/dist/migrations/store/migration-store.js +46 -0
  50. package/dist/migrations/store/migration-store.js.map +1 -0
  51. package/dist/replication/ErrorRateLimiter.js.map +1 -1
  52. package/dist/replication/PgRelation.js.map +1 -1
  53. package/dist/replication/WalConnection.js.map +1 -1
  54. package/dist/replication/WalStream.d.ts +0 -1
  55. package/dist/replication/WalStream.js +21 -25
  56. package/dist/replication/WalStream.js.map +1 -1
  57. package/dist/replication/WalStreamManager.js +12 -13
  58. package/dist/replication/WalStreamManager.js.map +1 -1
  59. package/dist/replication/WalStreamRunner.js +8 -8
  60. package/dist/replication/WalStreamRunner.js.map +1 -1
  61. package/dist/replication/util.js.map +1 -1
  62. package/dist/routes/auth.d.ts +8 -10
  63. package/dist/routes/auth.js.map +1 -1
  64. package/dist/routes/endpoints/admin.d.ts +1011 -0
  65. package/dist/routes/{admin.js → endpoints/admin.js} +33 -18
  66. package/dist/routes/endpoints/admin.js.map +1 -0
  67. package/dist/routes/endpoints/checkpointing.d.ts +76 -0
  68. package/dist/routes/endpoints/checkpointing.js +36 -0
  69. package/dist/routes/endpoints/checkpointing.js.map +1 -0
  70. package/dist/routes/endpoints/dev.d.ts +312 -0
  71. package/dist/routes/{dev.js → endpoints/dev.js} +25 -16
  72. package/dist/routes/endpoints/dev.js.map +1 -0
  73. package/dist/routes/endpoints/route-endpoints-index.d.ts +6 -0
  74. package/dist/routes/endpoints/route-endpoints-index.js +7 -0
  75. package/dist/routes/endpoints/route-endpoints-index.js.map +1 -0
  76. package/dist/routes/endpoints/socket-route.d.ts +2 -0
  77. package/dist/routes/{socket-route.js → endpoints/socket-route.js} +10 -10
  78. package/dist/routes/endpoints/socket-route.js.map +1 -0
  79. package/dist/routes/endpoints/sync-rules.d.ts +174 -0
  80. package/dist/routes/{sync-rules.js → endpoints/sync-rules.js} +44 -24
  81. package/dist/routes/endpoints/sync-rules.js.map +1 -0
  82. package/dist/routes/endpoints/sync-stream.d.ts +132 -0
  83. package/dist/routes/{sync-stream.js → endpoints/sync-stream.js} +26 -17
  84. package/dist/routes/endpoints/sync-stream.js.map +1 -0
  85. package/dist/routes/hooks.d.ts +10 -0
  86. package/dist/routes/hooks.js +31 -0
  87. package/dist/routes/hooks.js.map +1 -0
  88. package/dist/routes/route-register.d.ts +10 -0
  89. package/dist/routes/route-register.js +87 -0
  90. package/dist/routes/route-register.js.map +1 -0
  91. package/dist/routes/router.d.ts +16 -4
  92. package/dist/routes/router.js +6 -1
  93. package/dist/routes/router.js.map +1 -1
  94. package/dist/routes/routes-index.d.ts +5 -3
  95. package/dist/routes/routes-index.js +5 -3
  96. package/dist/routes/routes-index.js.map +1 -1
  97. package/dist/runner/teardown.js +27 -12
  98. package/dist/runner/teardown.js.map +1 -1
  99. package/dist/storage/BucketStorage.d.ts +3 -0
  100. package/dist/storage/BucketStorage.js.map +1 -1
  101. package/dist/storage/ChecksumCache.js.map +1 -1
  102. package/dist/storage/MongoBucketStorage.js +5 -5
  103. package/dist/storage/MongoBucketStorage.js.map +1 -1
  104. package/dist/storage/SourceTable.js.map +1 -1
  105. package/dist/storage/mongo/MongoBucketBatch.js +23 -18
  106. package/dist/storage/mongo/MongoBucketBatch.js.map +1 -1
  107. package/dist/storage/mongo/MongoIdSequence.js.map +1 -1
  108. package/dist/storage/mongo/MongoSyncBucketStorage.js.map +1 -1
  109. package/dist/storage/mongo/MongoSyncRulesLock.js +3 -3
  110. package/dist/storage/mongo/MongoSyncRulesLock.js.map +1 -1
  111. package/dist/storage/mongo/OperationBatch.js.map +1 -1
  112. package/dist/storage/mongo/PersistedBatch.js +2 -2
  113. package/dist/storage/mongo/PersistedBatch.js.map +1 -1
  114. package/dist/storage/mongo/db.d.ts +2 -2
  115. package/dist/storage/mongo/db.js.map +1 -1
  116. package/dist/storage/mongo/util.js.map +1 -1
  117. package/dist/sync/BroadcastIterable.js.map +1 -1
  118. package/dist/sync/LastValueSink.js.map +1 -1
  119. package/dist/sync/merge.js.map +1 -1
  120. package/dist/sync/safeRace.js.map +1 -1
  121. package/dist/sync/sync.js +4 -4
  122. package/dist/sync/sync.js.map +1 -1
  123. package/dist/sync/util.js.map +1 -1
  124. package/dist/system/CorePowerSyncSystem.d.ts +12 -7
  125. package/dist/system/CorePowerSyncSystem.js +26 -2
  126. package/dist/system/CorePowerSyncSystem.js.map +1 -1
  127. package/dist/system/system-index.d.ts +1 -0
  128. package/dist/system/system-index.js +2 -0
  129. package/dist/system/system-index.js.map +1 -0
  130. package/dist/util/Mutex.js.map +1 -1
  131. package/dist/util/PgManager.js.map +1 -1
  132. package/dist/util/alerting.d.ts +0 -2
  133. package/dist/util/alerting.js +0 -6
  134. package/dist/util/alerting.js.map +1 -1
  135. package/dist/util/config/collectors/config-collector.js +3 -3
  136. package/dist/util/config/collectors/config-collector.js.map +1 -1
  137. package/dist/util/config/collectors/impl/base64-config-collector.js.map +1 -1
  138. package/dist/util/config/collectors/impl/filesystem-config-collector.js +7 -5
  139. package/dist/util/config/collectors/impl/filesystem-config-collector.js.map +1 -1
  140. package/dist/util/config/compound-config-collector.js +4 -4
  141. package/dist/util/config/compound-config-collector.js.map +1 -1
  142. package/dist/util/config/sync-rules/impl/base64-sync-rules-collector.js.map +1 -1
  143. package/dist/util/config/sync-rules/impl/filesystem-sync-rules-collector.js.map +1 -1
  144. package/dist/util/config/sync-rules/impl/inline-sync-rules-collector.js.map +1 -1
  145. package/dist/util/config.js.map +1 -1
  146. package/dist/util/env.d.ts +1 -2
  147. package/dist/util/env.js +3 -2
  148. package/dist/util/env.js.map +1 -1
  149. package/dist/util/memory-tracking.js +2 -2
  150. package/dist/util/memory-tracking.js.map +1 -1
  151. package/dist/util/migration_lib.js.map +1 -1
  152. package/dist/util/pgwire_utils.js +2 -2
  153. package/dist/util/pgwire_utils.js.map +1 -1
  154. package/dist/util/populate_test_data.js.map +1 -1
  155. package/dist/util/secs.js.map +1 -1
  156. package/dist/util/utils.js +4 -4
  157. package/dist/util/utils.js.map +1 -1
  158. package/package.json +13 -10
  159. package/src/api/diagnostics.ts +5 -5
  160. package/src/api/schema.ts +1 -1
  161. package/src/auth/KeyStore.ts +2 -2
  162. package/src/entry/cli-entry.ts +3 -4
  163. package/src/entry/commands/config-command.ts +1 -1
  164. package/src/entry/commands/migrate-action.ts +2 -2
  165. package/src/entry/commands/start-action.ts +1 -1
  166. package/src/entry/commands/teardown-action.ts +1 -1
  167. package/src/index.ts +5 -2
  168. package/src/locks/LockManager.ts +16 -0
  169. package/src/locks/MongoLocks.ts +142 -0
  170. package/src/locks/locks-index.ts +2 -0
  171. package/src/metrics/Metrics.ts +8 -8
  172. package/src/migrations/db/migrations/1684951997326-init.ts +3 -3
  173. package/src/migrations/db/migrations/1702295701188-sync-rule-state.ts +3 -3
  174. package/src/migrations/db/migrations/1711543888062-write-checkpoint-index.ts +2 -2
  175. package/src/migrations/definitions.ts +21 -0
  176. package/src/migrations/executor.ts +87 -0
  177. package/src/migrations/migrations-index.ts +3 -0
  178. package/src/migrations/migrations.ts +7 -11
  179. package/src/migrations/store/migration-store.ts +63 -0
  180. package/src/replication/WalConnection.ts +2 -2
  181. package/src/replication/WalStream.ts +24 -29
  182. package/src/replication/WalStreamManager.ts +14 -15
  183. package/src/replication/WalStreamRunner.ts +10 -10
  184. package/src/replication/util.ts +1 -1
  185. package/src/routes/auth.ts +22 -16
  186. package/src/routes/endpoints/admin.ts +237 -0
  187. package/src/routes/endpoints/checkpointing.ts +41 -0
  188. package/src/routes/endpoints/dev.ts +199 -0
  189. package/src/routes/endpoints/route-endpoints-index.ts +6 -0
  190. package/src/routes/{socket-route.ts → endpoints/socket-route.ts} +11 -11
  191. package/src/routes/endpoints/sync-rules.ts +227 -0
  192. package/src/routes/endpoints/sync-stream.ts +101 -0
  193. package/src/routes/hooks.ts +45 -0
  194. package/src/routes/route-register.ts +104 -0
  195. package/src/routes/router.ts +34 -6
  196. package/src/routes/routes-index.ts +5 -4
  197. package/src/runner/teardown.ts +30 -13
  198. package/src/storage/BucketStorage.ts +7 -2
  199. package/src/storage/ChecksumCache.ts +2 -2
  200. package/src/storage/MongoBucketStorage.ts +8 -8
  201. package/src/storage/SourceTable.ts +2 -2
  202. package/src/storage/mongo/MongoBucketBatch.ts +29 -22
  203. package/src/storage/mongo/MongoSyncBucketStorage.ts +3 -3
  204. package/src/storage/mongo/MongoSyncRulesLock.ts +3 -3
  205. package/src/storage/mongo/OperationBatch.ts +1 -1
  206. package/src/storage/mongo/PersistedBatch.ts +3 -3
  207. package/src/storage/mongo/db.ts +3 -4
  208. package/src/sync/sync.ts +8 -8
  209. package/src/sync/util.ts +2 -2
  210. package/src/system/CorePowerSyncSystem.ts +31 -10
  211. package/src/system/system-index.ts +1 -0
  212. package/src/util/alerting.ts +0 -8
  213. package/src/util/config/collectors/config-collector.ts +5 -3
  214. package/src/util/config/collectors/impl/filesystem-config-collector.ts +8 -6
  215. package/src/util/config/compound-config-collector.ts +4 -4
  216. package/src/util/env.ts +4 -2
  217. package/src/util/memory-tracking.ts +2 -2
  218. package/src/util/pgwire_utils.ts +3 -3
  219. package/src/util/utils.ts +5 -5
  220. package/test/src/auth.test.ts +4 -2
  221. package/test/src/data_storage.test.ts +177 -0
  222. package/test/src/env.ts +6 -6
  223. package/test/src/pg_test.test.ts +18 -0
  224. package/test/src/setup.ts +7 -0
  225. package/test/src/slow_tests.test.ts +45 -6
  226. package/test/tsconfig.json +1 -1
  227. package/tsconfig.json +5 -6
  228. package/tsconfig.tsbuildinfo +1 -1
  229. package/vitest.config.ts +1 -3
  230. package/dist/migrations/db/store.d.ts +0 -3
  231. package/dist/migrations/db/store.js +0 -10
  232. package/dist/migrations/db/store.js.map +0 -1
  233. package/dist/routes/admin.d.ts +0 -7
  234. package/dist/routes/admin.js.map +0 -1
  235. package/dist/routes/checkpointing.d.ts +0 -3
  236. package/dist/routes/checkpointing.js +0 -30
  237. package/dist/routes/checkpointing.js.map +0 -1
  238. package/dist/routes/dev.d.ts +0 -6
  239. package/dist/routes/dev.js.map +0 -1
  240. package/dist/routes/route-generators.d.ts +0 -15
  241. package/dist/routes/route-generators.js +0 -32
  242. package/dist/routes/route-generators.js.map +0 -1
  243. package/dist/routes/socket-route.d.ts +0 -2
  244. package/dist/routes/socket-route.js.map +0 -1
  245. package/dist/routes/sync-rules.d.ts +0 -6
  246. package/dist/routes/sync-rules.js.map +0 -1
  247. package/dist/routes/sync-stream.d.ts +0 -5
  248. package/dist/routes/sync-stream.js.map +0 -1
  249. package/src/migrations/db/store.ts +0 -11
  250. package/src/routes/admin.ts +0 -229
  251. package/src/routes/checkpointing.ts +0 -38
  252. package/src/routes/dev.ts +0 -194
  253. package/src/routes/route-generators.ts +0 -39
  254. package/src/routes/sync-rules.ts +0 -210
  255. package/src/routes/sync-stream.ts +0 -95
@@ -1,14 +1,13 @@
1
1
  import * as pgwire from '@powersync/service-jpgwire';
2
- import * as micro from '@journeyapps-platform/micro';
3
2
  import { hrtime } from 'node:process';
4
3
 
5
- import * as storage from '@/storage/storage-index.js';
6
- import * as util from '@/util/util-index.js';
4
+ import * as storage from '../storage/storage-index.js';
5
+ import * as util from '../util/util-index.js';
7
6
 
8
7
  import { DefaultErrorRateLimiter } from './ErrorRateLimiter.js';
9
- import { touch } from './WalStream.js';
10
8
  import { WalStreamRunner } from './WalStreamRunner.js';
11
9
  import { CorePowerSyncSystem } from '../system/CorePowerSyncSystem.js';
10
+ import { container, logger } from '@powersync/lib-services-framework';
12
11
 
13
12
  // 5 minutes
14
13
  const PING_INTERVAL = 1_000_000_000n * 300n;
@@ -37,8 +36,8 @@ export class WalStreamManager {
37
36
 
38
37
  start() {
39
38
  this.runLoop().catch((e) => {
40
- micro.logger.error(`Fatal WalStream error`, e);
41
- util.captureException(e);
39
+ logger.error(`Fatal WalStream error`, e);
40
+ container.reporter.captureException(e);
42
41
  setTimeout(() => {
43
42
  process.exit(1);
44
43
  }, 1000);
@@ -58,7 +57,7 @@ export class WalStreamManager {
58
57
  const configured_sync_rules = await util.loadSyncRules(this.system.config);
59
58
  let configured_lock: storage.ReplicationLock | undefined = undefined;
60
59
  if (configured_sync_rules != null) {
61
- micro.logger.info('Loading sync rules from configuration');
60
+ logger.info('Loading sync rules from configuration');
62
61
  try {
63
62
  // Configure new sync rules, if it has changed.
64
63
  // In that case, also immediately take out a lock, so that another process doesn't start replication on it.
@@ -70,13 +69,13 @@ export class WalStreamManager {
70
69
  }
71
70
  } catch (e) {
72
71
  // Log, but continue with previous sync rules
73
- micro.logger.error(`Failed to load sync rules from configuration`, e);
72
+ logger.error(`Failed to load sync rules from configuration`, e);
74
73
  }
75
74
  } else {
76
- micro.logger.info('No sync rules configured - configure via API');
75
+ logger.info('No sync rules configured - configure via API');
77
76
  }
78
77
  while (!this.stopped) {
79
- await touch();
78
+ await container.probes.touch();
80
79
  try {
81
80
  const pool = this.system.pgwire_pool;
82
81
  if (pool) {
@@ -93,7 +92,7 @@ export class WalStreamManager {
93
92
  }
94
93
  }
95
94
  } catch (e) {
96
- micro.logger.error(`Failed to refresh wal streams`, e);
95
+ logger.error(`Failed to refresh wal streams`, e);
97
96
  }
98
97
  await new Promise((resolve) => setTimeout(resolve, 5000));
99
98
  }
@@ -117,7 +116,7 @@ export class WalStreamManager {
117
116
  try {
118
117
  await db.query(`SELECT * FROM pg_logical_emit_message(false, 'powersync', 'ping')`);
119
118
  } catch (e) {
120
- micro.logger.warn(`Failed to ping`, e);
119
+ logger.warn(`Failed to ping`, e);
121
120
  }
122
121
  this.lastPing = now;
123
122
  }
@@ -168,7 +167,7 @@ export class WalStreamManager {
168
167
  // for example from stricter validation that was added.
169
168
  // This will be retried every couple of seconds.
170
169
  // When new (valid) sync rules are deployed and processed, this one be disabled.
171
- micro.logger.error(`Failed to start replication for ${syncRules.slot_name}`, e);
170
+ logger.error(`Failed to start replication for ${syncRules.slot_name}`, e);
172
171
  }
173
172
  }
174
173
  }
@@ -184,7 +183,7 @@ export class WalStreamManager {
184
183
  await stream.terminate();
185
184
  } catch (e) {
186
185
  // This will be retried
187
- micro.logger.warn(`Failed to terminate ${stream.slot_name}`, e);
186
+ logger.warn(`Failed to terminate ${stream.slot_name}`, e);
188
187
  }
189
188
  }
190
189
 
@@ -207,7 +206,7 @@ export class WalStreamManager {
207
206
  await lock.release();
208
207
  }
209
208
  } catch (e) {
210
- micro.logger.warn(`Failed to terminate ${syncRules.slot_name}`, e);
209
+ logger.warn(`Failed to terminate ${syncRules.slot_name}`, e);
211
210
  }
212
211
  }
213
212
  }
@@ -1,12 +1,12 @@
1
1
  import * as pgwire from '@powersync/service-jpgwire';
2
- import * as micro from '@journeyapps-platform/micro';
3
2
 
4
- import * as storage from '@/storage/storage-index.js';
5
- import * as util from '@/util/util-index.js';
3
+ import * as storage from '../storage/storage-index.js';
4
+ import * as util from '../util/util-index.js';
6
5
 
7
6
  import { ErrorRateLimiter } from './ErrorRateLimiter.js';
8
7
  import { MissingReplicationSlotError, WalStream } from './WalStream.js';
9
8
  import { ResolvedConnection } from '../util/config/types.js';
9
+ import { container, logger } from '@powersync/lib-services-framework';
10
10
 
11
11
  export interface WalStreamRunnerOptions {
12
12
  factory: storage.BucketStorageFactory;
@@ -46,12 +46,12 @@ export class WalStreamRunner {
46
46
  await this.replicateLoop();
47
47
  } catch (e) {
48
48
  // Fatal exception
49
- util.captureException(e, {
49
+ container.reporter.captureException(e, {
50
50
  metadata: {
51
51
  replication_slot: this.slot_name
52
52
  }
53
53
  });
54
- micro.logger.error(`Replication failed on ${this.slot_name}`, e);
54
+ logger.error(`Replication failed on ${this.slot_name}`, e);
55
55
 
56
56
  if (e instanceof MissingReplicationSlotError) {
57
57
  // This stops replication on this slot, and creates a new slot
@@ -96,7 +96,7 @@ export class WalStreamRunner {
96
96
  });
97
97
  await stream.replicate();
98
98
  } catch (e) {
99
- micro.logger.error(`Replication error`, e);
99
+ logger.error(`Replication error`, e);
100
100
  if (e.cause != null) {
101
101
  // Example:
102
102
  // PgError.conn_ended: Unable to do postgres query on ended connection
@@ -118,13 +118,13 @@ export class WalStreamRunner {
118
118
  // [Symbol(pg.ErrorResponse)]: undefined
119
119
  // }
120
120
  // Without this additional log, the cause would not be visible in the logs.
121
- micro.logger.error(`cause`, e.cause);
121
+ logger.error(`cause`, e.cause);
122
122
  }
123
123
  if (e instanceof MissingReplicationSlotError) {
124
124
  throw e;
125
125
  } else {
126
126
  // Report the error if relevant, before retrying
127
- util.captureException(e, {
127
+ container.reporter.captureException(e, {
128
128
  metadata: {
129
129
  replication_slot: this.slot_name
130
130
  }
@@ -144,7 +144,7 @@ export class WalStreamRunner {
144
144
  * This will also release the lock if start() was called earlier.
145
145
  */
146
146
  async stop(options?: { force?: boolean }) {
147
- micro.logger.info(`${this.slot_name} Stopping replication`);
147
+ logger.info(`${this.slot_name} Stopping replication`);
148
148
  // End gracefully
149
149
  this.abortController.abort();
150
150
 
@@ -161,7 +161,7 @@ export class WalStreamRunner {
161
161
  * Stops replication if needed.
162
162
  */
163
163
  async terminate(options?: { force?: boolean }) {
164
- micro.logger.info(`${this.slot_name} Terminating replication`);
164
+ logger.info(`${this.slot_name} Terminating replication`);
165
165
  await this.stop(options);
166
166
 
167
167
  const slotName = this.slot_name;
@@ -1,6 +1,6 @@
1
1
  import * as pgwire from '@powersync/service-jpgwire';
2
2
 
3
- import * as util from '@/util/util-index.js';
3
+ import * as util from '../util/util-index.js';
4
4
  import { ReplicationColumn, ReplicationIdentity } from './PgRelation.js';
5
5
 
6
6
  export interface ReplicaIdentityResult {
@@ -1,19 +1,17 @@
1
- import * as micro from '@journeyapps-platform/micro';
2
- import { FastifyRequest } from 'fastify';
3
1
  import * as jose from 'jose';
4
2
 
5
- import * as auth from '@/auth/auth-index.js';
6
- import * as util from '@/util/util-index.js';
7
- import { Context } from './router.js';
3
+ import * as auth from '../auth/auth-index.js';
4
+ import * as util from '../util/util-index.js';
5
+ import { BasicRouterRequest, Context, RequestEndpointHandlerPayload } from './router.js';
8
6
  import { CorePowerSyncSystem } from '../system/CorePowerSyncSystem.js';
9
7
 
10
- export function endpoint(req: FastifyRequest) {
8
+ export function endpoint(req: BasicRouterRequest) {
11
9
  const protocol = req.headers['x-forwarded-proto'] ?? req.protocol;
12
10
  const host = req.hostname;
13
11
  return `${protocol}://${host}`;
14
12
  }
15
13
 
16
- function devAudience(req: FastifyRequest): string {
14
+ function devAudience(req: BasicRouterRequest): string {
17
15
  return `${endpoint(req)}/dev`;
18
16
  }
19
17
 
@@ -22,7 +20,7 @@ function devAudience(req: FastifyRequest): string {
22
20
  *
23
21
  * Will be replaced by temporary tokens issued by PowerSync Management service.
24
22
  */
25
- export async function issueDevToken(req: FastifyRequest, user_id: string, config: util.ResolvedPowerSyncConfig) {
23
+ export async function issueDevToken(req: BasicRouterRequest, user_id: string, config: util.ResolvedPowerSyncConfig) {
26
24
  const iss = devAudience(req);
27
25
  const aud = devAudience(req);
28
26
 
@@ -42,7 +40,11 @@ export async function issueDevToken(req: FastifyRequest, user_id: string, config
42
40
  }
43
41
 
44
42
  /** @deprecated */
45
- export async function issueLegacyDevToken(req: FastifyRequest, user_id: string, config: util.ResolvedPowerSyncConfig) {
43
+ export async function issueLegacyDevToken(
44
+ req: BasicRouterRequest,
45
+ user_id: string,
46
+ config: util.ResolvedPowerSyncConfig
47
+ ) {
46
48
  const iss = devAudience(req);
47
49
  const aud = config.jwt_audiences[0];
48
50
 
@@ -61,7 +63,11 @@ export async function issueLegacyDevToken(req: FastifyRequest, user_id: string,
61
63
  .sign(key.key);
62
64
  }
63
65
 
64
- export async function issuePowerSyncToken(req: FastifyRequest, user_id: string, config: util.ResolvedPowerSyncConfig) {
66
+ export async function issuePowerSyncToken(
67
+ req: BasicRouterRequest,
68
+ user_id: string,
69
+ config: util.ResolvedPowerSyncConfig
70
+ ) {
65
71
  const iss = devAudience(req);
66
72
  const aud = config.jwt_audiences[0];
67
73
  const key = config.dev.dev_key;
@@ -89,8 +95,8 @@ export function getTokenFromHeader(authHeader: string = ''): string | null {
89
95
  return token ?? null;
90
96
  }
91
97
 
92
- export const authUser = async (payload: micro.fastify.FastifyHandlerPayload<any, Context>) => {
93
- return authorizeUser(payload.context, payload.request.headers.authorization);
98
+ export const authUser = async (payload: RequestEndpointHandlerPayload) => {
99
+ return authorizeUser(payload.context, payload.request.headers.authorization as string);
94
100
  };
95
101
 
96
102
  export async function authorizeUser(context: Context, authHeader: string = '') {
@@ -142,9 +148,9 @@ export async function generateContext(system: CorePowerSyncSystem, token: string
142
148
  /**
143
149
  * @deprecated
144
150
  */
145
- export const authDevUser = async (payload: micro.fastify.FastifyHandlerPayload<any, Context>) => {
151
+ export const authDevUser = async (payload: RequestEndpointHandlerPayload) => {
146
152
  const context = payload.context;
147
- const token = getTokenFromHeader(payload.request.headers.authorization);
153
+ const token = getTokenFromHeader(payload.request.headers.authorization as string);
148
154
  if (!context.system.config.dev.demo_auth) {
149
155
  return {
150
156
  authorized: false,
@@ -179,7 +185,7 @@ export const authDevUser = async (payload: micro.fastify.FastifyHandlerPayload<a
179
185
  return { authorized: true };
180
186
  };
181
187
 
182
- export const authApi = (payload: micro.fastify.FastifyHandlerPayload<any, Context>) => {
188
+ export const authApi = (payload: RequestEndpointHandlerPayload) => {
183
189
  const context = payload.context;
184
190
  const api_keys = context.system.config.api_tokens;
185
191
  if (api_keys.length == 0) {
@@ -188,7 +194,7 @@ export const authApi = (payload: micro.fastify.FastifyHandlerPayload<any, Contex
188
194
  errors: ['Authentication disabled']
189
195
  };
190
196
  }
191
- const auth = payload.request.headers.authorization ?? '';
197
+ const auth = (payload.request.headers.authorization as string) ?? '';
192
198
 
193
199
  const tokenMatch = /^(Token|Bearer) (\S+)$/.exec(auth);
194
200
  if (!tokenMatch) {
@@ -0,0 +1,237 @@
1
+ import { errors, router, schema } from '@powersync/lib-services-framework';
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 { routeDefinition } from '../router.js';
9
+ import { PersistedSyncRulesContent } from '../../storage/BucketStorage.js';
10
+ import { authApi } from '../auth.js';
11
+
12
+ const demoCredentials = routeDefinition({
13
+ path: '/api/admin/v1/demo-credentials',
14
+ method: router.HTTPMethod.POST,
15
+ authorize: authApi,
16
+ validator: schema.createTsCodecValidator(internal_routes.DemoCredentialsRequest, {
17
+ allowAdditional: true
18
+ }),
19
+ handler: async (payload) => {
20
+ const connection = payload.context.system.config.connection;
21
+ if (connection == null || !connection.demo_database) {
22
+ return internal_routes.DemoCredentialsResponse.encode({});
23
+ }
24
+
25
+ const uri = util.buildDemoPgUri(connection);
26
+ return internal_routes.DemoCredentialsResponse.encode({
27
+ credentials: {
28
+ postgres_uri: uri
29
+ }
30
+ });
31
+ }
32
+ });
33
+
34
+ export const executeSql = routeDefinition({
35
+ path: '/api/admin/v1/execute-sql',
36
+ method: router.HTTPMethod.POST,
37
+ authorize: authApi,
38
+ validator: schema.createTsCodecValidator(internal_routes.ExecuteSqlRequest, { allowAdditional: true }),
39
+ handler: async (payload) => {
40
+ const connection = payload.context.system.config.connection;
41
+ if (connection == null || !connection.debug_api) {
42
+ return internal_routes.ExecuteSqlResponse.encode({
43
+ results: {
44
+ columns: [],
45
+ rows: []
46
+ },
47
+ success: false,
48
+ error: 'SQL querying is not enabled'
49
+ });
50
+ }
51
+
52
+ const pool = payload.context.system.requirePgPool();
53
+
54
+ const { query, args } = payload.params.sql;
55
+
56
+ try {
57
+ const result = await pool.query({
58
+ statement: query,
59
+ params: args.map(util.autoParameter)
60
+ });
61
+
62
+ return internal_routes.ExecuteSqlResponse.encode({
63
+ success: true,
64
+ results: {
65
+ columns: result.columns.map((c) => c.name),
66
+ rows: result.rows.map((row) => {
67
+ return row.map((value) => mapColumnValue(toSyncRulesValue(value)));
68
+ })
69
+ }
70
+ });
71
+ } catch (e) {
72
+ return internal_routes.ExecuteSqlResponse.encode({
73
+ results: {
74
+ columns: [],
75
+ rows: []
76
+ },
77
+ success: false,
78
+ error: e.message
79
+ });
80
+ }
81
+ }
82
+ });
83
+
84
+ export const diagnostics = routeDefinition({
85
+ path: '/api/admin/v1/diagnostics',
86
+ method: router.HTTPMethod.POST,
87
+ authorize: authApi,
88
+ validator: schema.createTsCodecValidator(internal_routes.DiagnosticsRequest, { allowAdditional: true }),
89
+ handler: async (payload) => {
90
+ const include_content = payload.params.sync_rules_content ?? false;
91
+ const system = payload.context.system;
92
+
93
+ const status = await api.getConnectionStatus(system);
94
+ if (status == null) {
95
+ return internal_routes.DiagnosticsResponse.encode({
96
+ connections: []
97
+ });
98
+ }
99
+
100
+ const { storage } = system;
101
+ const active = await storage.getActiveSyncRulesContent();
102
+ const next = await storage.getNextSyncRulesContent();
103
+
104
+ const active_status = await api.getSyncRulesStatus(active, system, {
105
+ include_content,
106
+ check_connection: status.connected,
107
+ live_status: true
108
+ });
109
+
110
+ const next_status = await api.getSyncRulesStatus(next, system, {
111
+ include_content,
112
+ check_connection: status.connected,
113
+ live_status: true
114
+ });
115
+
116
+ return internal_routes.DiagnosticsResponse.encode({
117
+ connections: [status],
118
+ active_sync_rules: active_status,
119
+ deploying_sync_rules: next_status
120
+ });
121
+ }
122
+ });
123
+
124
+ export const getSchema = routeDefinition({
125
+ path: '/api/admin/v1/schema',
126
+ method: router.HTTPMethod.POST,
127
+ authorize: authApi,
128
+ validator: schema.createTsCodecValidator(internal_routes.GetSchemaRequest, { allowAdditional: true }),
129
+ handler: async (payload) => {
130
+ const system = payload.context.system;
131
+
132
+ return internal_routes.GetSchemaResponse.encode(await api.getConnectionsSchema(system));
133
+ }
134
+ });
135
+
136
+ export const reprocess = routeDefinition({
137
+ path: '/api/admin/v1/reprocess',
138
+ method: router.HTTPMethod.POST,
139
+ authorize: authApi,
140
+ validator: schema.createTsCodecValidator(internal_routes.ReprocessRequest, { allowAdditional: true }),
141
+ handler: async (payload) => {
142
+ const system = payload.context.system;
143
+
144
+ const storage = system.storage;
145
+ const next = await storage.getNextSyncRules();
146
+ if (next != null) {
147
+ throw new Error(`Busy processing sync rules - cannot reprocess`);
148
+ }
149
+
150
+ const active = await storage.getActiveSyncRules();
151
+ if (active == null) {
152
+ throw new errors.JourneyError({
153
+ status: 422,
154
+ code: 'NO_SYNC_RULES',
155
+ description: 'No active sync rules'
156
+ });
157
+ }
158
+
159
+ const new_rules = await storage.updateSyncRules({
160
+ content: active.sync_rules.content
161
+ });
162
+
163
+ return internal_routes.ReprocessResponse.encode({
164
+ connections: [
165
+ {
166
+ tag: system.config.connection!.tag,
167
+ id: system.config.connection!.id,
168
+ slot_name: new_rules.slot_name
169
+ }
170
+ ]
171
+ });
172
+ }
173
+ });
174
+
175
+ export const validate = routeDefinition({
176
+ path: '/api/admin/v1/validate',
177
+ method: router.HTTPMethod.POST,
178
+ authorize: authApi,
179
+ validator: schema.createTsCodecValidator(internal_routes.ValidateRequest, { allowAdditional: true }),
180
+ handler: async (payload) => {
181
+ const system = payload.context.system;
182
+
183
+ const content = payload.params.sync_rules;
184
+
185
+ const schemaData = await api.getConnectionsSchema(system);
186
+ const schema = new StaticSchema(schemaData.connections);
187
+
188
+ const sync_rules: PersistedSyncRulesContent = {
189
+ // Dummy values
190
+ id: 0,
191
+ slot_name: '',
192
+
193
+ parsed() {
194
+ return {
195
+ ...this,
196
+ sync_rules: SqlSyncRules.fromYaml(content, { throwOnError: false, schema })
197
+ };
198
+ },
199
+ sync_rules_content: content,
200
+ async lock() {
201
+ throw new Error('Lock not implemented');
202
+ }
203
+ };
204
+
205
+ const connectionStatus = await api.getConnectionStatus(system);
206
+ if (connectionStatus == null) {
207
+ return internal_routes.ValidateResponse.encode({
208
+ errors: [{ level: 'fatal', message: 'No connection configured' }],
209
+ connections: []
210
+ });
211
+ }
212
+
213
+ const status = (await api.getSyncRulesStatus(sync_rules, system, {
214
+ include_content: false,
215
+ check_connection: connectionStatus?.connected,
216
+ live_status: false
217
+ }))!;
218
+
219
+ if (connectionStatus == null) {
220
+ status.errors.push({ level: 'fatal', message: 'No connection configured' });
221
+ }
222
+
223
+ return internal_routes.ValidateResponse.encode(status);
224
+ }
225
+ });
226
+
227
+ function mapColumnValue(value: SqliteValue) {
228
+ if (typeof value == 'bigint') {
229
+ return Number(value);
230
+ } else if (isJsonValue(value)) {
231
+ return value;
232
+ } else {
233
+ return null;
234
+ }
235
+ }
236
+
237
+ export const ADMIN_ROUTES = [demoCredentials, executeSql, diagnostics, getSchema, reprocess, validate];
@@ -0,0 +1,41 @@
1
+ import * as t from 'ts-codec';
2
+ import { router, schema } from '@powersync/lib-services-framework';
3
+
4
+ import * as util from '../../util/util-index.js';
5
+ import { authUser } from '../auth.js';
6
+ import { routeDefinition } from '../router.js';
7
+
8
+ const WriteCheckpointRequest = t.object({});
9
+
10
+ export const writeCheckpoint = routeDefinition({
11
+ path: '/write-checkpoint.json',
12
+ method: router.HTTPMethod.GET,
13
+ authorize: authUser,
14
+ validator: 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 = routeDefinition({
27
+ path: '/write-checkpoint2.json',
28
+ method: router.HTTPMethod.GET,
29
+ authorize: authUser,
30
+ validator: schema.createTsCodecValidator(WriteCheckpointRequest, { allowAdditional: true }),
31
+ handler: async (payload) => {
32
+ const { user_id, system } = payload.context;
33
+ const storage = system.storage;
34
+ const write_checkpoint = await util.createWriteCheckpoint(system.requirePgPool(), storage, user_id!);
35
+ return {
36
+ write_checkpoint: String(write_checkpoint)
37
+ };
38
+ }
39
+ });
40
+
41
+ export const CHECKPOINT_ROUTES = [writeCheckpoint, writeCheckpoint2];