@powersync/service-core 0.2.2 → 0.4.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 (262) hide show
  1. package/CHANGELOG.md +31 -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/JwtPayload.d.ts +6 -2
  7. package/dist/auth/KeySpec.js.map +1 -1
  8. package/dist/auth/KeyStore.js +3 -9
  9. package/dist/auth/KeyStore.js.map +1 -1
  10. package/dist/auth/LeakyBucket.js.map +1 -1
  11. package/dist/auth/RemoteJWKSCollector.js.map +1 -1
  12. package/dist/auth/SupabaseKeyCollector.js.map +1 -1
  13. package/dist/db/mongo.js.map +1 -1
  14. package/dist/entry/cli-entry.js +2 -2
  15. package/dist/entry/cli-entry.js.map +1 -1
  16. package/dist/entry/commands/config-command.js.map +1 -1
  17. package/dist/entry/commands/migrate-action.js +12 -4
  18. package/dist/entry/commands/migrate-action.js.map +1 -1
  19. package/dist/entry/commands/start-action.js.map +1 -1
  20. package/dist/entry/commands/teardown-action.js.map +1 -1
  21. package/dist/index.d.ts +3 -2
  22. package/dist/index.js +4 -2
  23. package/dist/index.js.map +1 -1
  24. package/dist/locks/LockManager.d.ts +10 -0
  25. package/dist/locks/LockManager.js +7 -0
  26. package/dist/locks/LockManager.js.map +1 -0
  27. package/dist/locks/MongoLocks.d.ts +36 -0
  28. package/dist/locks/MongoLocks.js +81 -0
  29. package/dist/locks/MongoLocks.js.map +1 -0
  30. package/dist/locks/locks-index.d.ts +2 -0
  31. package/dist/locks/locks-index.js +3 -0
  32. package/dist/locks/locks-index.js.map +1 -0
  33. package/dist/metrics/Metrics.js +6 -6
  34. package/dist/metrics/Metrics.js.map +1 -1
  35. package/dist/migrations/db/migrations/1684951997326-init.js.map +1 -1
  36. package/dist/migrations/db/migrations/1702295701188-sync-rule-state.js.map +1 -1
  37. package/dist/migrations/db/migrations/1711543888062-write-checkpoint-index.js.map +1 -1
  38. package/dist/migrations/definitions.d.ts +18 -0
  39. package/dist/migrations/definitions.js +6 -0
  40. package/dist/migrations/definitions.js.map +1 -0
  41. package/dist/migrations/executor.d.ts +16 -0
  42. package/dist/migrations/executor.js +64 -0
  43. package/dist/migrations/executor.js.map +1 -0
  44. package/dist/migrations/migrations-index.d.ts +3 -0
  45. package/dist/migrations/migrations-index.js +4 -0
  46. package/dist/migrations/migrations-index.js.map +1 -0
  47. package/dist/migrations/migrations.d.ts +1 -1
  48. package/dist/migrations/migrations.js +12 -8
  49. package/dist/migrations/migrations.js.map +1 -1
  50. package/dist/migrations/store/migration-store.d.ts +11 -0
  51. package/dist/migrations/store/migration-store.js +46 -0
  52. package/dist/migrations/store/migration-store.js.map +1 -0
  53. package/dist/replication/ErrorRateLimiter.js.map +1 -1
  54. package/dist/replication/PgRelation.js.map +1 -1
  55. package/dist/replication/WalConnection.js.map +1 -1
  56. package/dist/replication/WalStream.d.ts +0 -1
  57. package/dist/replication/WalStream.js +21 -25
  58. package/dist/replication/WalStream.js.map +1 -1
  59. package/dist/replication/WalStreamManager.js +12 -13
  60. package/dist/replication/WalStreamManager.js.map +1 -1
  61. package/dist/replication/WalStreamRunner.js +8 -8
  62. package/dist/replication/WalStreamRunner.js.map +1 -1
  63. package/dist/replication/util.js.map +1 -1
  64. package/dist/routes/auth.d.ts +8 -10
  65. package/dist/routes/auth.js.map +1 -1
  66. package/dist/routes/endpoints/admin.d.ts +1011 -0
  67. package/dist/routes/{admin.js → endpoints/admin.js} +33 -18
  68. package/dist/routes/endpoints/admin.js.map +1 -0
  69. package/dist/routes/endpoints/checkpointing.d.ts +76 -0
  70. package/dist/routes/endpoints/checkpointing.js +36 -0
  71. package/dist/routes/endpoints/checkpointing.js.map +1 -0
  72. package/dist/routes/endpoints/dev.d.ts +312 -0
  73. package/dist/routes/{dev.js → endpoints/dev.js} +25 -16
  74. package/dist/routes/endpoints/dev.js.map +1 -0
  75. package/dist/routes/endpoints/route-endpoints-index.d.ts +6 -0
  76. package/dist/routes/endpoints/route-endpoints-index.js +7 -0
  77. package/dist/routes/endpoints/route-endpoints-index.js.map +1 -0
  78. package/dist/routes/endpoints/socket-route.d.ts +2 -0
  79. package/dist/routes/{socket-route.js → endpoints/socket-route.js} +12 -12
  80. package/dist/routes/endpoints/socket-route.js.map +1 -0
  81. package/dist/routes/endpoints/sync-rules.d.ts +174 -0
  82. package/dist/routes/{sync-rules.js → endpoints/sync-rules.js} +44 -24
  83. package/dist/routes/endpoints/sync-rules.js.map +1 -0
  84. package/dist/routes/endpoints/sync-stream.d.ts +132 -0
  85. package/dist/routes/{sync-stream.js → endpoints/sync-stream.js} +28 -19
  86. package/dist/routes/endpoints/sync-stream.js.map +1 -0
  87. package/dist/routes/hooks.d.ts +10 -0
  88. package/dist/routes/hooks.js +31 -0
  89. package/dist/routes/hooks.js.map +1 -0
  90. package/dist/routes/route-register.d.ts +10 -0
  91. package/dist/routes/route-register.js +87 -0
  92. package/dist/routes/route-register.js.map +1 -0
  93. package/dist/routes/router.d.ts +16 -4
  94. package/dist/routes/router.js +6 -1
  95. package/dist/routes/router.js.map +1 -1
  96. package/dist/routes/routes-index.d.ts +5 -3
  97. package/dist/routes/routes-index.js +5 -3
  98. package/dist/routes/routes-index.js.map +1 -1
  99. package/dist/runner/teardown.js +9 -9
  100. package/dist/runner/teardown.js.map +1 -1
  101. package/dist/storage/BucketStorage.d.ts +3 -0
  102. package/dist/storage/BucketStorage.js.map +1 -1
  103. package/dist/storage/ChecksumCache.js.map +1 -1
  104. package/dist/storage/MongoBucketStorage.js +5 -5
  105. package/dist/storage/MongoBucketStorage.js.map +1 -1
  106. package/dist/storage/SourceTable.js.map +1 -1
  107. package/dist/storage/mongo/MongoBucketBatch.js +23 -18
  108. package/dist/storage/mongo/MongoBucketBatch.js.map +1 -1
  109. package/dist/storage/mongo/MongoIdSequence.js.map +1 -1
  110. package/dist/storage/mongo/MongoSyncBucketStorage.js.map +1 -1
  111. package/dist/storage/mongo/MongoSyncRulesLock.js +3 -3
  112. package/dist/storage/mongo/MongoSyncRulesLock.js.map +1 -1
  113. package/dist/storage/mongo/OperationBatch.js.map +1 -1
  114. package/dist/storage/mongo/PersistedBatch.js +2 -2
  115. package/dist/storage/mongo/PersistedBatch.js.map +1 -1
  116. package/dist/storage/mongo/db.d.ts +2 -2
  117. package/dist/storage/mongo/db.js.map +1 -1
  118. package/dist/storage/mongo/util.js.map +1 -1
  119. package/dist/sync/BroadcastIterable.js.map +1 -1
  120. package/dist/sync/LastValueSink.js.map +1 -1
  121. package/dist/sync/merge.js.map +1 -1
  122. package/dist/sync/safeRace.js.map +1 -1
  123. package/dist/sync/sync.d.ts +2 -2
  124. package/dist/sync/sync.js +5 -5
  125. package/dist/sync/sync.js.map +1 -1
  126. package/dist/sync/util.js.map +1 -1
  127. package/dist/system/CorePowerSyncSystem.d.ts +12 -7
  128. package/dist/system/CorePowerSyncSystem.js +26 -2
  129. package/dist/system/CorePowerSyncSystem.js.map +1 -1
  130. package/dist/system/system-index.d.ts +1 -0
  131. package/dist/system/system-index.js +2 -0
  132. package/dist/system/system-index.js.map +1 -0
  133. package/dist/util/Mutex.js.map +1 -1
  134. package/dist/util/PgManager.js.map +1 -1
  135. package/dist/util/alerting.d.ts +0 -2
  136. package/dist/util/alerting.js +0 -6
  137. package/dist/util/alerting.js.map +1 -1
  138. package/dist/util/config/collectors/config-collector.js +3 -3
  139. package/dist/util/config/collectors/config-collector.js.map +1 -1
  140. package/dist/util/config/collectors/impl/base64-config-collector.js.map +1 -1
  141. package/dist/util/config/collectors/impl/filesystem-config-collector.js +7 -5
  142. package/dist/util/config/collectors/impl/filesystem-config-collector.js.map +1 -1
  143. package/dist/util/config/compound-config-collector.js +4 -4
  144. package/dist/util/config/compound-config-collector.js.map +1 -1
  145. package/dist/util/config/sync-rules/impl/base64-sync-rules-collector.js.map +1 -1
  146. package/dist/util/config/sync-rules/impl/filesystem-sync-rules-collector.js.map +1 -1
  147. package/dist/util/config/sync-rules/impl/inline-sync-rules-collector.js.map +1 -1
  148. package/dist/util/config.js.map +1 -1
  149. package/dist/util/env.d.ts +1 -2
  150. package/dist/util/env.js +3 -2
  151. package/dist/util/env.js.map +1 -1
  152. package/dist/util/memory-tracking.js +2 -2
  153. package/dist/util/memory-tracking.js.map +1 -1
  154. package/dist/util/migration_lib.js.map +1 -1
  155. package/dist/util/pgwire_utils.js +2 -2
  156. package/dist/util/pgwire_utils.js.map +1 -1
  157. package/dist/util/populate_test_data.js.map +1 -1
  158. package/dist/util/secs.js.map +1 -1
  159. package/dist/util/utils.js +4 -4
  160. package/dist/util/utils.js.map +1 -1
  161. package/package.json +13 -10
  162. package/src/api/diagnostics.ts +5 -5
  163. package/src/api/schema.ts +1 -1
  164. package/src/auth/JwtPayload.ts +6 -2
  165. package/src/auth/KeyStore.ts +3 -9
  166. package/src/entry/cli-entry.ts +3 -4
  167. package/src/entry/commands/config-command.ts +1 -1
  168. package/src/entry/commands/migrate-action.ts +14 -6
  169. package/src/entry/commands/start-action.ts +1 -1
  170. package/src/entry/commands/teardown-action.ts +1 -1
  171. package/src/index.ts +5 -2
  172. package/src/locks/LockManager.ts +16 -0
  173. package/src/locks/MongoLocks.ts +142 -0
  174. package/src/locks/locks-index.ts +2 -0
  175. package/src/metrics/Metrics.ts +8 -8
  176. package/src/migrations/db/migrations/1684951997326-init.ts +3 -3
  177. package/src/migrations/db/migrations/1702295701188-sync-rule-state.ts +3 -3
  178. package/src/migrations/db/migrations/1711543888062-write-checkpoint-index.ts +2 -2
  179. package/src/migrations/definitions.ts +21 -0
  180. package/src/migrations/executor.ts +87 -0
  181. package/src/migrations/migrations-index.ts +3 -0
  182. package/src/migrations/migrations.ts +15 -11
  183. package/src/migrations/store/migration-store.ts +63 -0
  184. package/src/replication/WalConnection.ts +2 -2
  185. package/src/replication/WalStream.ts +24 -29
  186. package/src/replication/WalStreamManager.ts +14 -15
  187. package/src/replication/WalStreamRunner.ts +10 -10
  188. package/src/replication/util.ts +1 -1
  189. package/src/routes/auth.ts +22 -16
  190. package/src/routes/endpoints/admin.ts +237 -0
  191. package/src/routes/endpoints/checkpointing.ts +41 -0
  192. package/src/routes/endpoints/dev.ts +199 -0
  193. package/src/routes/endpoints/route-endpoints-index.ts +6 -0
  194. package/src/routes/{socket-route.ts → endpoints/socket-route.ts} +13 -16
  195. package/src/routes/endpoints/sync-rules.ts +227 -0
  196. package/src/routes/endpoints/sync-stream.ts +98 -0
  197. package/src/routes/hooks.ts +45 -0
  198. package/src/routes/route-register.ts +104 -0
  199. package/src/routes/router.ts +34 -6
  200. package/src/routes/routes-index.ts +5 -4
  201. package/src/runner/teardown.ts +9 -9
  202. package/src/storage/BucketStorage.ts +7 -2
  203. package/src/storage/ChecksumCache.ts +2 -2
  204. package/src/storage/MongoBucketStorage.ts +8 -8
  205. package/src/storage/SourceTable.ts +2 -2
  206. package/src/storage/mongo/MongoBucketBatch.ts +29 -22
  207. package/src/storage/mongo/MongoSyncBucketStorage.ts +3 -3
  208. package/src/storage/mongo/MongoSyncRulesLock.ts +3 -3
  209. package/src/storage/mongo/OperationBatch.ts +1 -1
  210. package/src/storage/mongo/PersistedBatch.ts +3 -3
  211. package/src/storage/mongo/db.ts +3 -4
  212. package/src/sync/sync.ts +11 -11
  213. package/src/sync/util.ts +2 -2
  214. package/src/system/CorePowerSyncSystem.ts +31 -10
  215. package/src/system/system-index.ts +1 -0
  216. package/src/util/alerting.ts +0 -8
  217. package/src/util/config/collectors/config-collector.ts +5 -3
  218. package/src/util/config/collectors/impl/filesystem-config-collector.ts +8 -6
  219. package/src/util/config/compound-config-collector.ts +4 -4
  220. package/src/util/env.ts +4 -2
  221. package/src/util/memory-tracking.ts +2 -2
  222. package/src/util/pgwire_utils.ts +3 -3
  223. package/src/util/utils.ts +5 -5
  224. package/test/src/auth.test.ts +4 -2
  225. package/test/src/data_storage.test.ts +181 -19
  226. package/test/src/env.ts +6 -6
  227. package/test/src/setup.ts +7 -0
  228. package/test/src/slow_tests.test.ts +45 -6
  229. package/test/src/sync.test.ts +6 -5
  230. package/test/tsconfig.json +1 -1
  231. package/tsconfig.json +5 -6
  232. package/tsconfig.tsbuildinfo +1 -1
  233. package/vitest.config.ts +1 -3
  234. package/dist/migrations/db/store.d.ts +0 -3
  235. package/dist/migrations/db/store.js +0 -10
  236. package/dist/migrations/db/store.js.map +0 -1
  237. package/dist/routes/admin.d.ts +0 -7
  238. package/dist/routes/admin.js.map +0 -1
  239. package/dist/routes/checkpointing.d.ts +0 -3
  240. package/dist/routes/checkpointing.js +0 -30
  241. package/dist/routes/checkpointing.js.map +0 -1
  242. package/dist/routes/dev.d.ts +0 -6
  243. package/dist/routes/dev.js.map +0 -1
  244. package/dist/routes/route-generators.d.ts +0 -15
  245. package/dist/routes/route-generators.js +0 -32
  246. package/dist/routes/route-generators.js.map +0 -1
  247. package/dist/routes/socket-route.d.ts +0 -2
  248. package/dist/routes/socket-route.js.map +0 -1
  249. package/dist/routes/sync-rules.d.ts +0 -6
  250. package/dist/routes/sync-rules.js.map +0 -1
  251. package/dist/routes/sync-stream.d.ts +0 -5
  252. package/dist/routes/sync-stream.js.map +0 -1
  253. package/src/migrations/db/store.ts +0 -11
  254. package/src/routes/admin.ts +0 -229
  255. package/src/routes/checkpointing.ts +0 -38
  256. package/src/routes/dev.ts +0 -194
  257. package/src/routes/route-generators.ts +0 -39
  258. package/src/routes/sync-rules.ts +0 -210
  259. package/src/routes/sync-stream.ts +0 -95
  260. package/test/src/sql_functions.test.ts +0 -254
  261. package/test/src/sql_operators.test.ts +0 -132
  262. package/test/src/sync_rules.test.ts +0 -1053
@@ -1,11 +1,10 @@
1
1
  import { Command } from 'commander';
2
2
 
3
- import * as micro from '@journeyapps-platform/micro';
4
-
5
- import * as utils from '@/util/util-index.js';
3
+ import * as utils from '../util/util-index.js';
6
4
  import { registerMigrationAction } from './commands/migrate-action.js';
7
5
  import { registerTearDownAction } from './commands/teardown-action.js';
8
6
  import { registerStartAction } from './entry-index.js';
7
+ import { logger } from '@powersync/lib-services-framework';
9
8
 
10
9
  /**
11
10
  * Generates a Commander program which serves as the entry point
@@ -33,7 +32,7 @@ export function generateEntryProgram(startHandlers?: Record<utils.ServiceRunner,
33
32
  try {
34
33
  await entryProgram.parseAsync();
35
34
  } catch (e) {
36
- micro.logger.error('Fatal error', e);
35
+ logger.error('Fatal error', e);
37
36
  process.exit(1);
38
37
  }
39
38
  }
@@ -1,6 +1,6 @@
1
1
  import { Command } from 'commander';
2
2
 
3
- import * as util from '@/util/util-index.js';
3
+ import * as util from '../../util/util-index.js';
4
4
 
5
5
  /**
6
6
  * Wraps a Command with the standard config options
@@ -1,8 +1,9 @@
1
1
  import { Command } from 'commander';
2
- import { Direction } from '@journeyapps-platform/micro-migrate';
3
2
 
4
3
  import { extractRunnerOptions, wrapConfigCommand } from './config-command.js';
5
- import { migrate } from '@/migrations/migrations.js';
4
+ import { migrate } from '../../migrations/migrations.js';
5
+ import { Direction } from '../../migrations/definitions.js';
6
+ import { logger } from '@powersync/lib-services-framework';
6
7
 
7
8
  const COMMAND_NAME = 'migrate';
8
9
 
@@ -17,9 +18,16 @@ export function registerMigrationAction(program: Command) {
17
18
  .action(async (direction: Direction, options) => {
18
19
  const runnerConfig = extractRunnerOptions(options);
19
20
 
20
- await migrate({
21
- direction,
22
- runner_config: runnerConfig
23
- });
21
+ try {
22
+ await migrate({
23
+ direction,
24
+ runner_config: runnerConfig
25
+ });
26
+
27
+ process.exit(0);
28
+ } catch (e) {
29
+ logger.error(`Migration failure`, e);
30
+ process.exit(1);
31
+ }
24
32
  });
25
33
  }
@@ -1,6 +1,6 @@
1
1
  import { Command } from 'commander';
2
2
 
3
- import * as utils from '@/util/util-index.js';
3
+ import * as utils from '../../util/util-index.js';
4
4
  import { extractRunnerOptions, wrapConfigCommand } from './config-command.js';
5
5
 
6
6
  const COMMAND_NAME = 'start';
@@ -1,7 +1,7 @@
1
1
  import { Command } from 'commander';
2
2
 
3
3
  import { extractRunnerOptions, wrapConfigCommand } from './config-command.js';
4
- import { teardown } from '@/runner/teardown.js';
4
+ import { teardown } from '../../runner/teardown.js';
5
5
 
6
6
  const COMMAND_NAME = 'teardown';
7
7
 
package/src/index.ts CHANGED
@@ -12,11 +12,14 @@ export * as db from './db/db-index.js';
12
12
  export * from './entry/entry-index.js';
13
13
  export * as entry from './entry/entry-index.js';
14
14
 
15
+ // Re-export framework for easy use of Container API
16
+ export * as framework from '@powersync/lib-services-framework';
17
+
15
18
  export * from './metrics/Metrics.js';
16
19
  export * as metrics from './metrics/Metrics.js';
17
20
 
18
21
  export * from './migrations/migrations.js';
19
- export * as migrations from './migrations/migrations.js';
22
+ export * as migrations from './migrations/migrations-index.js';
20
23
 
21
24
  export * from './replication/replication-index.js';
22
25
  export * as replication from './replication/replication-index.js';
@@ -31,7 +34,7 @@ export * from './sync/sync-index.js';
31
34
  export * as sync from './sync/sync-index.js';
32
35
 
33
36
  export * from './system/CorePowerSyncSystem.js';
34
- export * as system from './system/CorePowerSyncSystem.js';
37
+ export * as system from './system/system-index.js';
35
38
 
36
39
  export * from './util/util-index.js';
37
40
  export * as utils from './util/util-index.js';
@@ -0,0 +1,16 @@
1
+ import * as bson from 'bson';
2
+
3
+ export class LockActiveError extends Error {
4
+ constructor() {
5
+ super('Lock is already active');
6
+ this.name = this.constructor.name;
7
+ }
8
+ }
9
+
10
+ export type LockManager = {
11
+ acquire: () => Promise<bson.ObjectId | null>;
12
+ refresh: (lock_id: bson.ObjectId) => Promise<void>;
13
+ release: (lock_id: bson.ObjectId) => Promise<void>;
14
+
15
+ lock: (handler: (refresh: () => Promise<void>) => Promise<void>) => Promise<void>;
16
+ };
@@ -0,0 +1,142 @@
1
+ import * as mongo from 'mongodb';
2
+ import * as bson from 'bson';
3
+ import { LockActiveError, LockManager } from './LockManager.js';
4
+
5
+ /**
6
+ * Lock Document Schema
7
+ */
8
+ export type Lock = {
9
+ name: string;
10
+ active_lock?: {
11
+ lock_id: bson.ObjectId;
12
+ ts: Date;
13
+ };
14
+ };
15
+
16
+ export type Collection = mongo.Collection<Lock>;
17
+
18
+ export type AcquireLockParams = {
19
+ /**
20
+ * Name of the process/user trying to acquire the lock.
21
+ */
22
+ name: string;
23
+ /**
24
+ * The TTL of the lock (ms). Default: 60000 ms (1 min)
25
+ */
26
+ timeout?: number;
27
+ };
28
+
29
+ const DEFAULT_LOCK_TIMEOUT = 60 * 1000; // 1 minute
30
+
31
+ const acquireLock = async (collection: Collection, params: AcquireLockParams) => {
32
+ const now = new Date();
33
+ const lock_timeout = params.timeout ?? DEFAULT_LOCK_TIMEOUT;
34
+ const lock_id = new bson.ObjectId();
35
+
36
+ await collection.updateOne(
37
+ {
38
+ name: params.name
39
+ },
40
+ {
41
+ $setOnInsert: {
42
+ name: params.name
43
+ }
44
+ },
45
+ {
46
+ upsert: true
47
+ }
48
+ );
49
+
50
+ const expired_ts = now.getTime() - lock_timeout;
51
+
52
+ const res = await collection.updateOne(
53
+ {
54
+ $and: [
55
+ { name: params.name },
56
+ {
57
+ $or: [{ active_lock: { $exists: false } }, { 'active_lock.ts': { $lte: new Date(expired_ts) } }]
58
+ }
59
+ ]
60
+ },
61
+ {
62
+ $set: {
63
+ active_lock: {
64
+ lock_id: lock_id,
65
+ ts: now
66
+ }
67
+ }
68
+ }
69
+ );
70
+
71
+ if (res.modifiedCount === 0) {
72
+ return null;
73
+ }
74
+
75
+ return lock_id;
76
+ };
77
+
78
+ const refreshLock = async (collection: Collection, lock_id: bson.ObjectId) => {
79
+ const res = await collection.updateOne(
80
+ {
81
+ 'active_lock.lock_id': lock_id
82
+ },
83
+ {
84
+ $set: {
85
+ 'active_lock.ts': new Date()
86
+ }
87
+ }
88
+ );
89
+
90
+ if (res.modifiedCount === 0) {
91
+ throw new Error('Lock not found, could not refresh');
92
+ }
93
+ };
94
+
95
+ export const releaseLock = async (collection: Collection, lock_id: bson.ObjectId) => {
96
+ const res = await collection.updateOne(
97
+ {
98
+ 'active_lock.lock_id': lock_id
99
+ },
100
+ {
101
+ $unset: {
102
+ active_lock: true
103
+ }
104
+ }
105
+ );
106
+
107
+ if (res.modifiedCount === 0) {
108
+ throw new Error('Lock not found, could not release');
109
+ }
110
+ };
111
+
112
+ export type CreateLockManagerParams = {
113
+ /**
114
+ * Name of the process/user trying to acquire the lock.
115
+ */
116
+ name: string;
117
+ /**
118
+ * The TTL for each lock (ms). Default: 60000 ms (1 min)
119
+ */
120
+ timeout?: number;
121
+ };
122
+
123
+ export const createMongoLockManager = (collection: Collection, params: CreateLockManagerParams): LockManager => {
124
+ return {
125
+ acquire: () => acquireLock(collection, params),
126
+ refresh: (lock_id: bson.ObjectId) => refreshLock(collection, lock_id),
127
+ release: (lock_id: bson.ObjectId) => releaseLock(collection, lock_id),
128
+
129
+ lock: async (handler) => {
130
+ const lock_id = await acquireLock(collection, params);
131
+ if (!lock_id) {
132
+ throw new LockActiveError();
133
+ }
134
+
135
+ try {
136
+ await handler(() => refreshLock(collection, lock_id));
137
+ } finally {
138
+ await releaseLock(collection, lock_id);
139
+ }
140
+ }
141
+ };
142
+ };
@@ -0,0 +1,2 @@
1
+ export * from './LockManager.js';
2
+ export * from './MongoLocks.js';
@@ -1,13 +1,13 @@
1
- import * as micro from '@journeyapps-platform/micro';
2
1
  import { Attributes, Counter, ObservableGauge, UpDownCounter, ValueType } from '@opentelemetry/api';
3
2
  import { PrometheusExporter } from '@opentelemetry/exporter-prometheus';
4
3
  import { MeterProvider, MetricReader, PeriodicExportingMetricReader } from '@opentelemetry/sdk-metrics';
5
4
  import { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-http';
6
5
  import * as jpgwire from '@powersync/service-jpgwire';
7
- import * as util from '@/util/util-index.js';
8
- import * as storage from '@/storage/storage-index.js';
6
+ import * as util from '../util/util-index.js';
7
+ import * as storage from '../storage/storage-index.js';
9
8
  import { CorePowerSyncSystem } from '../system/CorePowerSyncSystem.js';
10
9
  import { Resource } from '@opentelemetry/resources';
10
+ import { logger } from '@powersync/lib-services-framework';
11
11
 
12
12
  export interface MetricsOptions {
13
13
  disable_telemetry_sharing: boolean;
@@ -144,9 +144,9 @@ export class Metrics {
144
144
  if (Metrics.instance) {
145
145
  return;
146
146
  }
147
- micro.logger.info('Configuring telemetry.');
147
+ logger.info('Configuring telemetry.');
148
148
 
149
- micro.logger.info(
149
+ logger.info(
150
150
  `
151
151
  Attention:
152
152
  PowerSync collects completely anonymous telemetry regarding usage.
@@ -164,7 +164,7 @@ Anonymous telemetry is currently: ${options.disable_telemetry_sharing ? 'disable
164
164
  configuredExporters.push(prometheusExporter);
165
165
 
166
166
  if (!options.disable_telemetry_sharing) {
167
- micro.logger.info('Sharing anonymous telemetry');
167
+ logger.info('Sharing anonymous telemetry');
168
168
  const periodicExporter = new PeriodicExportingMetricReader({
169
169
  exporter: new OTLPMetricExporter({
170
170
  url: options.internal_metrics_endpoint
@@ -189,7 +189,7 @@ Anonymous telemetry is currently: ${options.disable_telemetry_sharing ? 'disable
189
189
 
190
190
  Metrics.instance = new Metrics(meterProvider, prometheusExporter);
191
191
 
192
- micro.logger.info('Telemetry configuration complete.');
192
+ logger.info('Telemetry configuration complete.');
193
193
  }
194
194
 
195
195
  public async shutdown(): Promise<void> {
@@ -212,7 +212,7 @@ Anonymous telemetry is currently: ${options.disable_telemetry_sharing ? 'disable
212
212
  function getMetrics() {
213
213
  if (cachedRequest == null || Date.now() - cacheTimestamp > MINIMUM_INTERVAL) {
214
214
  cachedRequest = system.storage.getStorageMetrics().catch((e) => {
215
- micro.logger.error(`Failed to get storage metrics`, e);
215
+ logger.error(`Failed to get storage metrics`, e);
216
216
  return null;
217
217
  });
218
218
  cacheTimestamp = Date.now();
@@ -1,6 +1,6 @@
1
- import * as mongo from '@/db/mongo.js';
2
- import * as storage from '@/storage/storage-index.js';
3
- import * as utils from '@/util/util-index.js';
1
+ import * as mongo from '../../../db/mongo.js';
2
+ import * as storage from '../../../storage/storage-index.js';
3
+ import * as utils from '../../../util/util-index.js';
4
4
 
5
5
  export const up = async (context?: utils.MigrationContext) => {
6
6
  const config = await utils.loadConfig(context?.runner_config);
@@ -1,6 +1,6 @@
1
- import * as mongo from '@/db/mongo.js';
2
- import * as storage from '@/storage/storage-index.js';
3
- import * as utils from '@/util/util-index.js';
1
+ import * as mongo from '../../../db/mongo.js';
2
+ import * as storage from '../../../storage/storage-index.js';
3
+ import * as utils from '../../../util/util-index.js';
4
4
 
5
5
  interface LegacySyncRulesDocument extends storage.SyncRuleDocument {
6
6
  /**
@@ -1,5 +1,5 @@
1
- import * as storage from '@/storage/storage-index.js';
2
- import * as utils from '@/util/util-index.js';
1
+ import * as storage from '../../../storage/storage-index.js';
2
+ import * as utils from '../../../util/util-index.js';
3
3
 
4
4
  export const up = async (context?: utils.MigrationContext) => {
5
5
  const config = await utils.loadConfig(context?.runner_config);
@@ -0,0 +1,21 @@
1
+ export type Migration = {
2
+ name: string;
3
+ up: () => Promise<void>;
4
+ down: () => Promise<void>;
5
+ };
6
+
7
+ export enum Direction {
8
+ Up = 'up',
9
+ Down = 'down'
10
+ }
11
+
12
+ export type ExecutedMigration = {
13
+ name: string;
14
+ direction: Direction;
15
+ timestamp: Date;
16
+ };
17
+
18
+ export type MigrationState = {
19
+ last_run: string;
20
+ log: ExecutedMigration[];
21
+ };
@@ -0,0 +1,87 @@
1
+ import { logger } from '@powersync/lib-services-framework';
2
+ import * as defs from './definitions.js';
3
+ import { MigrationStore } from './store/migration-store.js';
4
+
5
+ type ExecuteParams = {
6
+ migrations: defs.Migration[];
7
+ state?: defs.MigrationState;
8
+
9
+ direction: defs.Direction;
10
+ count?: number;
11
+ };
12
+
13
+ export async function* execute(params: ExecuteParams): AsyncGenerator<defs.ExecutedMigration> {
14
+ let migrations = [...params.migrations];
15
+ if (params.direction === defs.Direction.Down) {
16
+ migrations = migrations.reverse();
17
+ }
18
+
19
+ let index = 0;
20
+
21
+ if (params.state) {
22
+ // Find the index of the last run
23
+ index = migrations.findIndex((migration) => {
24
+ return migration.name === params.state!.last_run;
25
+ });
26
+
27
+ if (index === -1) {
28
+ throw new Error(`The last run migration ${params.state?.last_run} was not found in the given set of migrations`);
29
+ }
30
+
31
+ // If we are migrating down then we want to include the last run migration, otherwise we want to start at the next one
32
+ if (params.direction === defs.Direction.Up) {
33
+ index += 1;
34
+ }
35
+ }
36
+
37
+ migrations = migrations.slice(index);
38
+
39
+ let i = 0;
40
+ for (const migration of migrations) {
41
+ if (params.count && params.count === i) {
42
+ return;
43
+ }
44
+
45
+ logger.info(`Executing ${migration.name} (${params.direction})`);
46
+ try {
47
+ switch (params.direction) {
48
+ case defs.Direction.Up: {
49
+ await migration.up();
50
+ break;
51
+ }
52
+ case defs.Direction.Down: {
53
+ await migration.down();
54
+ break;
55
+ }
56
+ }
57
+ logger.debug(`Success`);
58
+ } catch (err) {
59
+ logger.error(`Failed`, err);
60
+ process.exit(1);
61
+ }
62
+
63
+ yield {
64
+ name: migration.name,
65
+ direction: params.direction,
66
+ timestamp: new Date()
67
+ };
68
+
69
+ i++;
70
+ }
71
+ }
72
+
73
+ type WriteLogsParams = {
74
+ store: MigrationStore;
75
+ state?: defs.MigrationState;
76
+ log_stream: Iterable<defs.ExecutedMigration> | AsyncIterable<defs.ExecutedMigration>;
77
+ };
78
+ export const writeLogsToStore = async (params: WriteLogsParams): Promise<void> => {
79
+ const log = [...(params.state?.log || [])];
80
+ for await (const migration of params.log_stream) {
81
+ log.push(migration);
82
+ await params.store.save({
83
+ last_run: migration.name,
84
+ log: log
85
+ });
86
+ }
87
+ };
@@ -0,0 +1,3 @@
1
+ export * from './definitions.js';
2
+ export * from './executor.js';
3
+ export * from './migrations.js';
@@ -1,12 +1,14 @@
1
- import { locks } from '@journeyapps-platform/micro';
2
1
  import * as fs from 'fs/promises';
3
2
  import * as path from 'path';
4
3
  import { fileURLToPath } from 'url';
5
4
 
6
- import { Direction, createMongoMigrationStore, execute, writeLogsToStore } from '@journeyapps-platform/micro-migrate';
7
-
8
- import * as db from '@/db/db-index.js';
9
- import * as util from '@/util/util-index.js';
5
+ import * as db from '../db/db-index.js';
6
+ import * as util from '../util/util-index.js';
7
+ import * as locks from '../locks/locks-index.js';
8
+ import { Direction } from './definitions.js';
9
+ import { createMongoMigrationStore } from './store/migration-store.js';
10
+ import { execute, writeLogsToStore } from './executor.js';
11
+ import { logger } from '@powersync/lib-services-framework';
10
12
 
11
13
  const DEFAULT_MONGO_LOCK_COLLECTION = 'locks';
12
14
  const MONGO_LOCK_PROCESS = 'migrations';
@@ -62,6 +64,7 @@ export const migrate = async (options: MigrationOptions) => {
62
64
  const { storage } = config;
63
65
 
64
66
  const client = db.mongo.createMongoClient(storage);
67
+ logger.info('Connecting to MongoDB');
65
68
  await client.connect();
66
69
 
67
70
  const clientDB = client.db(storage.database);
@@ -72,6 +75,7 @@ export const migrate = async (options: MigrationOptions) => {
72
75
  });
73
76
 
74
77
  // Only one process should execute this at a time.
78
+ logger.info('Acquiring lock');
75
79
  const lockId = await manager.acquire();
76
80
 
77
81
  if (!lockId) {
@@ -91,18 +95,15 @@ export const migrate = async (options: MigrationOptions) => {
91
95
  process.addListener('beforeExit', releaseLock);
92
96
 
93
97
  try {
98
+ logger.info('Loading migrations');
94
99
  const migrations = await loadMigrations(MIGRATIONS_DIR, runner_config);
95
100
 
96
101
  // Use the provided config to connect to Mongo
97
- const store = createMongoMigrationStore({
98
- uri: storage.uri,
99
- database: storage.database,
100
- username: storage.username,
101
- password: storage.password
102
- });
102
+ const store = createMongoMigrationStore(clientDB);
103
103
 
104
104
  const state = await store.load();
105
105
 
106
+ logger.info('Running migrations');
106
107
  const logStream = execute({
107
108
  direction: direction,
108
109
  migrations,
@@ -115,8 +116,11 @@ export const migrate = async (options: MigrationOptions) => {
115
116
  state
116
117
  });
117
118
  } finally {
119
+ logger.info('Releasing lock');
118
120
  await releaseLock();
121
+ logger.info('Closing database');
119
122
  await client.close(true);
120
123
  process.removeListener('beforeExit', releaseLock);
124
+ logger.info('Done with migrations');
121
125
  }
122
126
  };
@@ -0,0 +1,63 @@
1
+ import { Db } from 'mongodb';
2
+ import * as path from 'path';
3
+ import * as defs from '../definitions.js';
4
+
5
+ export type MigrationStore = {
6
+ load: () => Promise<defs.MigrationState | undefined>;
7
+ save: (state: defs.MigrationState) => Promise<void>;
8
+ };
9
+
10
+ /**
11
+ * A custom store for node-migrate which is used to save and load migrations that have
12
+ * been operated on to mongo.
13
+ */
14
+ export const createMongoMigrationStore = (db: Db): MigrationStore => {
15
+ const collection = db.collection<defs.MigrationState>('migrations');
16
+
17
+ return {
18
+ load: async () => {
19
+ const state_entry = await collection.findOne();
20
+ if (!state_entry) {
21
+ return;
22
+ }
23
+
24
+ const { _id, ...state } = state_entry;
25
+
26
+ /**
27
+ * This is for backwards compatibility. A previous version of the migration tool used to save
28
+ * state as `lastRun`.
29
+ */
30
+ let last_run = state.last_run;
31
+ if ('lastRun' in state) {
32
+ last_run = (state as any).lastRun;
33
+ }
34
+
35
+ /**
36
+ * This is for backwards compatibility. A previous version of the migration tool used to include the
37
+ * file extension in migration names. This strips that extension off if it exists
38
+ */
39
+ const extension = path.extname(last_run);
40
+ if (extension) {
41
+ last_run = last_run.replace(extension, '');
42
+ }
43
+
44
+ return {
45
+ last_run,
46
+ log: state.log || []
47
+ };
48
+ },
49
+
50
+ save: async (state: defs.MigrationState) => {
51
+ await collection.replaceOne(
52
+ {},
53
+ {
54
+ last_run: state.last_run,
55
+ log: state.log
56
+ },
57
+ {
58
+ upsert: true
59
+ }
60
+ );
61
+ }
62
+ };
63
+ };
@@ -3,8 +3,8 @@ import { pgwireRows } from '@powersync/service-jpgwire';
3
3
  import { DEFAULT_TAG, SqlSyncRules, TablePattern } from '@powersync/service-sync-rules';
4
4
  import { ReplicationError, TableInfo } from '@powersync/service-types';
5
5
 
6
- import * as storage from '@/storage/storage-index.js';
7
- import * as util from '@/util/util-index.js';
6
+ import * as storage from '../storage/storage-index.js';
7
+ import * as util from '../util/util-index.js';
8
8
 
9
9
  import { ReplicaIdentityResult, getReplicationIdentityColumns } from './util.js';
10
10
  /**