@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.
- package/.probes/.gitkeep +0 -0
- package/CHANGELOG.md +82 -0
- package/LICENSE +67 -0
- package/README.md +3 -0
- package/dist/api/api-index.d.ts +2 -0
- package/dist/api/api-index.js +3 -0
- package/dist/api/api-index.js.map +1 -0
- package/dist/api/diagnostics.d.ts +21 -0
- package/dist/api/diagnostics.js +183 -0
- package/dist/api/diagnostics.js.map +1 -0
- package/dist/api/schema.d.ts +5 -0
- package/dist/api/schema.js +88 -0
- package/dist/api/schema.js.map +1 -0
- package/dist/auth/CachedKeyCollector.d.ts +46 -0
- package/dist/auth/CachedKeyCollector.js +116 -0
- package/dist/auth/CachedKeyCollector.js.map +1 -0
- package/dist/auth/CompoundKeyCollector.d.ts +8 -0
- package/dist/auth/CompoundKeyCollector.js +23 -0
- package/dist/auth/CompoundKeyCollector.js.map +1 -0
- package/dist/auth/JwtPayload.d.ts +10 -0
- package/dist/auth/JwtPayload.js +2 -0
- package/dist/auth/JwtPayload.js.map +1 -0
- package/dist/auth/KeyCollector.d.ts +24 -0
- package/dist/auth/KeyCollector.js +2 -0
- package/dist/auth/KeyCollector.js.map +1 -0
- package/dist/auth/KeySpec.d.ts +26 -0
- package/dist/auth/KeySpec.js +49 -0
- package/dist/auth/KeySpec.js.map +1 -0
- package/dist/auth/KeyStore.d.ts +39 -0
- package/dist/auth/KeyStore.js +131 -0
- package/dist/auth/KeyStore.js.map +1 -0
- package/dist/auth/LeakyBucket.d.ts +39 -0
- package/dist/auth/LeakyBucket.js +57 -0
- package/dist/auth/LeakyBucket.js.map +1 -0
- package/dist/auth/RemoteJWKSCollector.d.ts +24 -0
- package/dist/auth/RemoteJWKSCollector.js +106 -0
- package/dist/auth/RemoteJWKSCollector.js.map +1 -0
- package/dist/auth/StaticKeyCollector.d.ts +14 -0
- package/dist/auth/StaticKeyCollector.js +19 -0
- package/dist/auth/StaticKeyCollector.js.map +1 -0
- package/dist/auth/SupabaseKeyCollector.d.ts +22 -0
- package/dist/auth/SupabaseKeyCollector.js +61 -0
- package/dist/auth/SupabaseKeyCollector.js.map +1 -0
- package/dist/auth/auth-index.d.ts +10 -0
- package/dist/auth/auth-index.js +11 -0
- package/dist/auth/auth-index.js.map +1 -0
- package/dist/db/db-index.d.ts +1 -0
- package/dist/db/db-index.js +2 -0
- package/dist/db/db-index.js.map +1 -0
- package/dist/db/mongo.d.ts +29 -0
- package/dist/db/mongo.js +65 -0
- package/dist/db/mongo.js.map +1 -0
- package/dist/entry/cli-entry.d.ts +15 -0
- package/dist/entry/cli-entry.js +36 -0
- package/dist/entry/cli-entry.js.map +1 -0
- package/dist/entry/commands/config-command.d.ts +10 -0
- package/dist/entry/commands/config-command.js +21 -0
- package/dist/entry/commands/config-command.js.map +1 -0
- package/dist/entry/commands/migrate-action.d.ts +2 -0
- package/dist/entry/commands/migrate-action.js +18 -0
- package/dist/entry/commands/migrate-action.js.map +1 -0
- package/dist/entry/commands/start-action.d.ts +3 -0
- package/dist/entry/commands/start-action.js +15 -0
- package/dist/entry/commands/start-action.js.map +1 -0
- package/dist/entry/commands/teardown-action.d.ts +2 -0
- package/dist/entry/commands/teardown-action.js +17 -0
- package/dist/entry/commands/teardown-action.js.map +1 -0
- package/dist/entry/entry-index.d.ts +5 -0
- package/dist/entry/entry-index.js +6 -0
- package/dist/entry/entry-index.js.map +1 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.js +28 -0
- package/dist/index.js.map +1 -0
- package/dist/locks/LockManager.d.ts +10 -0
- package/dist/locks/LockManager.js +7 -0
- package/dist/locks/LockManager.js.map +1 -0
- package/dist/locks/MongoLocks.d.ts +36 -0
- package/dist/locks/MongoLocks.js +81 -0
- package/dist/locks/MongoLocks.js.map +1 -0
- package/dist/locks/locks-index.d.ts +2 -0
- package/dist/locks/locks-index.js +3 -0
- package/dist/locks/locks-index.js.map +1 -0
- package/dist/metrics/Metrics.d.ts +30 -0
- package/dist/metrics/Metrics.js +176 -0
- package/dist/metrics/Metrics.js.map +1 -0
- package/dist/migrations/db/migrations/1684951997326-init.d.ts +3 -0
- package/dist/migrations/db/migrations/1684951997326-init.js +31 -0
- package/dist/migrations/db/migrations/1684951997326-init.js.map +1 -0
- package/dist/migrations/db/migrations/1688556755264-initial-sync-rules.d.ts +2 -0
- package/dist/migrations/db/migrations/1688556755264-initial-sync-rules.js +5 -0
- package/dist/migrations/db/migrations/1688556755264-initial-sync-rules.js.map +1 -0
- package/dist/migrations/db/migrations/1702295701188-sync-rule-state.d.ts +3 -0
- package/dist/migrations/db/migrations/1702295701188-sync-rule-state.js +54 -0
- package/dist/migrations/db/migrations/1702295701188-sync-rule-state.js.map +1 -0
- package/dist/migrations/db/migrations/1711543888062-write-checkpoint-index.d.ts +3 -0
- package/dist/migrations/db/migrations/1711543888062-write-checkpoint-index.js +27 -0
- package/dist/migrations/db/migrations/1711543888062-write-checkpoint-index.js.map +1 -0
- package/dist/migrations/definitions.d.ts +18 -0
- package/dist/migrations/definitions.js +6 -0
- package/dist/migrations/definitions.js.map +1 -0
- package/dist/migrations/executor.d.ts +16 -0
- package/dist/migrations/executor.js +64 -0
- package/dist/migrations/executor.js.map +1 -0
- package/dist/migrations/migrations-index.d.ts +3 -0
- package/dist/migrations/migrations-index.js +4 -0
- package/dist/migrations/migrations-index.js.map +1 -0
- package/dist/migrations/migrations.d.ts +10 -0
- package/dist/migrations/migrations.js +90 -0
- package/dist/migrations/migrations.js.map +1 -0
- package/dist/migrations/store/migration-store.d.ts +11 -0
- package/dist/migrations/store/migration-store.js +46 -0
- package/dist/migrations/store/migration-store.js.map +1 -0
- package/dist/replication/ErrorRateLimiter.d.ts +17 -0
- package/dist/replication/ErrorRateLimiter.js +43 -0
- package/dist/replication/ErrorRateLimiter.js.map +1 -0
- package/dist/replication/PgRelation.d.ts +16 -0
- package/dist/replication/PgRelation.js +26 -0
- package/dist/replication/PgRelation.js.map +1 -0
- package/dist/replication/WalConnection.d.ts +34 -0
- package/dist/replication/WalConnection.js +190 -0
- package/dist/replication/WalConnection.js.map +1 -0
- package/dist/replication/WalStream.d.ts +57 -0
- package/dist/replication/WalStream.js +515 -0
- package/dist/replication/WalStream.js.map +1 -0
- package/dist/replication/WalStreamManager.d.ts +30 -0
- package/dist/replication/WalStreamManager.js +198 -0
- package/dist/replication/WalStreamManager.js.map +1 -0
- package/dist/replication/WalStreamRunner.d.ts +38 -0
- package/dist/replication/WalStreamRunner.js +155 -0
- package/dist/replication/WalStreamRunner.js.map +1 -0
- package/dist/replication/replication-index.d.ts +7 -0
- package/dist/replication/replication-index.js +8 -0
- package/dist/replication/replication-index.js.map +1 -0
- package/dist/replication/util.d.ts +9 -0
- package/dist/replication/util.js +62 -0
- package/dist/replication/util.js.map +1 -0
- package/dist/routes/auth.d.ts +56 -0
- package/dist/routes/auth.js +182 -0
- package/dist/routes/auth.js.map +1 -0
- package/dist/routes/endpoints/admin.d.ts +1011 -0
- package/dist/routes/endpoints/admin.js +207 -0
- package/dist/routes/endpoints/admin.js.map +1 -0
- package/dist/routes/endpoints/checkpointing.d.ts +76 -0
- package/dist/routes/endpoints/checkpointing.js +36 -0
- package/dist/routes/endpoints/checkpointing.js.map +1 -0
- package/dist/routes/endpoints/dev.d.ts +312 -0
- package/dist/routes/endpoints/dev.js +172 -0
- package/dist/routes/endpoints/dev.js.map +1 -0
- package/dist/routes/endpoints/route-endpoints-index.d.ts +6 -0
- package/dist/routes/endpoints/route-endpoints-index.js +7 -0
- package/dist/routes/endpoints/route-endpoints-index.js.map +1 -0
- package/dist/routes/endpoints/socket-route.d.ts +2 -0
- package/dist/routes/endpoints/socket-route.js +119 -0
- package/dist/routes/endpoints/socket-route.js.map +1 -0
- package/dist/routes/endpoints/sync-rules.d.ts +174 -0
- package/dist/routes/endpoints/sync-rules.js +202 -0
- package/dist/routes/endpoints/sync-rules.js.map +1 -0
- package/dist/routes/endpoints/sync-stream.d.ts +132 -0
- package/dist/routes/endpoints/sync-stream.js +83 -0
- package/dist/routes/endpoints/sync-stream.js.map +1 -0
- package/dist/routes/hooks.d.ts +10 -0
- package/dist/routes/hooks.js +32 -0
- package/dist/routes/hooks.js.map +1 -0
- package/dist/routes/route-register.d.ts +10 -0
- package/dist/routes/route-register.js +87 -0
- package/dist/routes/route-register.js.map +1 -0
- package/dist/routes/router-socket.d.ts +10 -0
- package/dist/routes/router-socket.js +5 -0
- package/dist/routes/router-socket.js.map +1 -0
- package/dist/routes/router.d.ts +26 -0
- package/dist/routes/router.js +7 -0
- package/dist/routes/router.js.map +1 -0
- package/dist/routes/routes-index.d.ts +6 -0
- package/dist/routes/routes-index.js +7 -0
- package/dist/routes/routes-index.js.map +1 -0
- package/dist/runner/teardown.d.ts +2 -0
- package/dist/runner/teardown.js +94 -0
- package/dist/runner/teardown.js.map +1 -0
- package/dist/storage/BucketStorage.d.ts +307 -0
- package/dist/storage/BucketStorage.js +25 -0
- package/dist/storage/BucketStorage.js.map +1 -0
- package/dist/storage/ChecksumCache.d.ts +50 -0
- package/dist/storage/ChecksumCache.js +234 -0
- package/dist/storage/ChecksumCache.js.map +1 -0
- package/dist/storage/MongoBucketStorage.d.ts +52 -0
- package/dist/storage/MongoBucketStorage.js +409 -0
- package/dist/storage/MongoBucketStorage.js.map +1 -0
- package/dist/storage/SourceTable.d.ts +39 -0
- package/dist/storage/SourceTable.js +50 -0
- package/dist/storage/SourceTable.js.map +1 -0
- package/dist/storage/mongo/MongoBucketBatch.d.ts +48 -0
- package/dist/storage/mongo/MongoBucketBatch.js +581 -0
- package/dist/storage/mongo/MongoBucketBatch.js.map +1 -0
- package/dist/storage/mongo/MongoIdSequence.d.ts +12 -0
- package/dist/storage/mongo/MongoIdSequence.js +21 -0
- package/dist/storage/mongo/MongoIdSequence.js.map +1 -0
- package/dist/storage/mongo/MongoPersistedSyncRules.d.ts +9 -0
- package/dist/storage/mongo/MongoPersistedSyncRules.js +9 -0
- package/dist/storage/mongo/MongoPersistedSyncRules.js.map +1 -0
- package/dist/storage/mongo/MongoPersistedSyncRulesContent.d.ts +20 -0
- package/dist/storage/mongo/MongoPersistedSyncRulesContent.js +26 -0
- package/dist/storage/mongo/MongoPersistedSyncRulesContent.js.map +1 -0
- package/dist/storage/mongo/MongoSyncBucketStorage.d.ts +29 -0
- package/dist/storage/mongo/MongoSyncBucketStorage.js +391 -0
- package/dist/storage/mongo/MongoSyncBucketStorage.js.map +1 -0
- package/dist/storage/mongo/MongoSyncRulesLock.d.ts +16 -0
- package/dist/storage/mongo/MongoSyncRulesLock.js +65 -0
- package/dist/storage/mongo/MongoSyncRulesLock.js.map +1 -0
- package/dist/storage/mongo/OperationBatch.d.ts +26 -0
- package/dist/storage/mongo/OperationBatch.js +101 -0
- package/dist/storage/mongo/OperationBatch.js.map +1 -0
- package/dist/storage/mongo/PersistedBatch.d.ts +46 -0
- package/dist/storage/mongo/PersistedBatch.js +213 -0
- package/dist/storage/mongo/PersistedBatch.js.map +1 -0
- package/dist/storage/mongo/db.d.ts +26 -0
- package/dist/storage/mongo/db.js +35 -0
- package/dist/storage/mongo/db.js.map +1 -0
- package/dist/storage/mongo/models.d.ts +140 -0
- package/dist/storage/mongo/models.js +27 -0
- package/dist/storage/mongo/models.js.map +1 -0
- package/dist/storage/mongo/util.d.ts +26 -0
- package/dist/storage/mongo/util.js +81 -0
- package/dist/storage/mongo/util.js.map +1 -0
- package/dist/storage/storage-index.d.ts +14 -0
- package/dist/storage/storage-index.js +15 -0
- package/dist/storage/storage-index.js.map +1 -0
- package/dist/sync/BroadcastIterable.d.ts +38 -0
- package/dist/sync/BroadcastIterable.js +153 -0
- package/dist/sync/BroadcastIterable.js.map +1 -0
- package/dist/sync/LastValueSink.d.ts +25 -0
- package/dist/sync/LastValueSink.js +84 -0
- package/dist/sync/LastValueSink.js.map +1 -0
- package/dist/sync/merge.d.ts +39 -0
- package/dist/sync/merge.js +175 -0
- package/dist/sync/merge.js.map +1 -0
- package/dist/sync/safeRace.d.ts +1 -0
- package/dist/sync/safeRace.js +91 -0
- package/dist/sync/safeRace.js.map +1 -0
- package/dist/sync/sync-index.d.ts +6 -0
- package/dist/sync/sync-index.js +7 -0
- package/dist/sync/sync-index.js.map +1 -0
- package/dist/sync/sync.d.ts +18 -0
- package/dist/sync/sync.js +259 -0
- package/dist/sync/sync.js.map +1 -0
- package/dist/sync/util.d.ts +26 -0
- package/dist/sync/util.js +73 -0
- package/dist/sync/util.js.map +1 -0
- package/dist/system/CorePowerSyncSystem.d.ts +23 -0
- package/dist/system/CorePowerSyncSystem.js +52 -0
- package/dist/system/CorePowerSyncSystem.js.map +1 -0
- package/dist/system/system-index.d.ts +1 -0
- package/dist/system/system-index.js +2 -0
- package/dist/system/system-index.js.map +1 -0
- package/dist/util/Mutex.d.ts +47 -0
- package/dist/util/Mutex.js +132 -0
- package/dist/util/Mutex.js.map +1 -0
- package/dist/util/PgManager.d.ts +24 -0
- package/dist/util/PgManager.js +55 -0
- package/dist/util/PgManager.js.map +1 -0
- package/dist/util/alerting.d.ts +2 -0
- package/dist/util/alerting.js +8 -0
- package/dist/util/alerting.js.map +1 -0
- package/dist/util/config/collectors/config-collector.d.ts +29 -0
- package/dist/util/config/collectors/config-collector.js +116 -0
- package/dist/util/config/collectors/config-collector.js.map +1 -0
- package/dist/util/config/collectors/impl/base64-config-collector.d.ts +6 -0
- package/dist/util/config/collectors/impl/base64-config-collector.js +15 -0
- package/dist/util/config/collectors/impl/base64-config-collector.js.map +1 -0
- package/dist/util/config/collectors/impl/fallback-config-collector.d.ts +11 -0
- package/dist/util/config/collectors/impl/fallback-config-collector.js +19 -0
- package/dist/util/config/collectors/impl/fallback-config-collector.js.map +1 -0
- package/dist/util/config/collectors/impl/filesystem-config-collector.d.ts +6 -0
- package/dist/util/config/collectors/impl/filesystem-config-collector.js +37 -0
- package/dist/util/config/collectors/impl/filesystem-config-collector.js.map +1 -0
- package/dist/util/config/compound-config-collector.d.ts +32 -0
- package/dist/util/config/compound-config-collector.js +130 -0
- package/dist/util/config/compound-config-collector.js.map +1 -0
- package/dist/util/config/sync-rules/impl/base64-sync-rules-collector.d.ts +7 -0
- package/dist/util/config/sync-rules/impl/base64-sync-rules-collector.js +17 -0
- package/dist/util/config/sync-rules/impl/base64-sync-rules-collector.js.map +1 -0
- package/dist/util/config/sync-rules/impl/filesystem-sync-rules-collector.d.ts +7 -0
- package/dist/util/config/sync-rules/impl/filesystem-sync-rules-collector.js +21 -0
- package/dist/util/config/sync-rules/impl/filesystem-sync-rules-collector.js.map +1 -0
- package/dist/util/config/sync-rules/impl/inline-sync-rules-collector.d.ts +7 -0
- package/dist/util/config/sync-rules/impl/inline-sync-rules-collector.js +17 -0
- package/dist/util/config/sync-rules/impl/inline-sync-rules-collector.js.map +1 -0
- package/dist/util/config/sync-rules/sync-collector.d.ts +6 -0
- package/dist/util/config/sync-rules/sync-collector.js +3 -0
- package/dist/util/config/sync-rules/sync-collector.js.map +1 -0
- package/dist/util/config/types.d.ts +57 -0
- package/dist/util/config/types.js +7 -0
- package/dist/util/config/types.js.map +1 -0
- package/dist/util/config.d.ts +7 -0
- package/dist/util/config.js +35 -0
- package/dist/util/config.js.map +1 -0
- package/dist/util/env.d.ts +9 -0
- package/dist/util/env.js +26 -0
- package/dist/util/env.js.map +1 -0
- package/dist/util/memory-tracking.d.ts +7 -0
- package/dist/util/memory-tracking.js +58 -0
- package/dist/util/memory-tracking.js.map +1 -0
- package/dist/util/migration_lib.d.ts +11 -0
- package/dist/util/migration_lib.js +64 -0
- package/dist/util/migration_lib.js.map +1 -0
- package/dist/util/pgwire_utils.d.ts +24 -0
- package/dist/util/pgwire_utils.js +117 -0
- package/dist/util/pgwire_utils.js.map +1 -0
- package/dist/util/populate_test_data.d.ts +8 -0
- package/dist/util/populate_test_data.js +65 -0
- package/dist/util/populate_test_data.js.map +1 -0
- package/dist/util/protocol-types.d.ts +182 -0
- package/dist/util/protocol-types.js +42 -0
- package/dist/util/protocol-types.js.map +1 -0
- package/dist/util/secs.d.ts +2 -0
- package/dist/util/secs.js +49 -0
- package/dist/util/secs.js.map +1 -0
- package/dist/util/util-index.d.ts +22 -0
- package/dist/util/util-index.js +23 -0
- package/dist/util/util-index.js.map +1 -0
- package/dist/util/utils.d.ts +17 -0
- package/dist/util/utils.js +92 -0
- package/dist/util/utils.js.map +1 -0
- package/package.json +59 -0
- package/src/api/api-index.ts +2 -0
- package/src/api/diagnostics.ts +221 -0
- package/src/api/schema.ts +99 -0
- package/src/auth/CachedKeyCollector.ts +132 -0
- package/src/auth/CompoundKeyCollector.ts +33 -0
- package/src/auth/JwtPayload.ts +11 -0
- package/src/auth/KeyCollector.ts +27 -0
- package/src/auth/KeySpec.ts +67 -0
- package/src/auth/KeyStore.ts +156 -0
- package/src/auth/LeakyBucket.ts +66 -0
- package/src/auth/RemoteJWKSCollector.ts +130 -0
- package/src/auth/StaticKeyCollector.ts +21 -0
- package/src/auth/SupabaseKeyCollector.ts +67 -0
- package/src/auth/auth-index.ts +10 -0
- package/src/db/db-index.ts +1 -0
- package/src/db/mongo.ts +72 -0
- package/src/entry/cli-entry.ts +40 -0
- package/src/entry/commands/config-command.ts +36 -0
- package/src/entry/commands/migrate-action.ts +25 -0
- package/src/entry/commands/start-action.ts +24 -0
- package/src/entry/commands/teardown-action.ts +23 -0
- package/src/entry/entry-index.ts +5 -0
- package/src/index.ts +40 -0
- package/src/locks/LockManager.ts +16 -0
- package/src/locks/MongoLocks.ts +142 -0
- package/src/locks/locks-index.ts +2 -0
- package/src/metrics/Metrics.ts +265 -0
- package/src/migrations/db/migrations/1684951997326-init.ts +33 -0
- package/src/migrations/db/migrations/1688556755264-initial-sync-rules.ts +5 -0
- package/src/migrations/db/migrations/1702295701188-sync-rule-state.ts +99 -0
- package/src/migrations/db/migrations/1711543888062-write-checkpoint-index.ts +32 -0
- package/src/migrations/definitions.ts +21 -0
- package/src/migrations/executor.ts +87 -0
- package/src/migrations/migrations-index.ts +3 -0
- package/src/migrations/migrations.ts +118 -0
- package/src/migrations/store/migration-store.ts +63 -0
- package/src/replication/ErrorRateLimiter.ts +50 -0
- package/src/replication/PgRelation.ts +42 -0
- package/src/replication/WalConnection.ts +227 -0
- package/src/replication/WalStream.ts +624 -0
- package/src/replication/WalStreamManager.ts +213 -0
- package/src/replication/WalStreamRunner.ts +180 -0
- package/src/replication/replication-index.ts +7 -0
- package/src/replication/util.ts +76 -0
- package/src/routes/auth.ts +215 -0
- package/src/routes/endpoints/admin.ts +237 -0
- package/src/routes/endpoints/checkpointing.ts +41 -0
- package/src/routes/endpoints/dev.ts +199 -0
- package/src/routes/endpoints/route-endpoints-index.ts +6 -0
- package/src/routes/endpoints/socket-route.ts +135 -0
- package/src/routes/endpoints/sync-rules.ts +227 -0
- package/src/routes/endpoints/sync-stream.ts +101 -0
- package/src/routes/hooks.ts +46 -0
- package/src/routes/route-register.ts +104 -0
- package/src/routes/router-socket.ts +13 -0
- package/src/routes/router.ts +46 -0
- package/src/routes/routes-index.ts +6 -0
- package/src/runner/teardown.ts +108 -0
- package/src/storage/BucketStorage.ts +396 -0
- package/src/storage/ChecksumCache.ts +294 -0
- package/src/storage/MongoBucketStorage.ts +519 -0
- package/src/storage/SourceTable.ts +60 -0
- package/src/storage/mongo/MongoBucketBatch.ts +752 -0
- package/src/storage/mongo/MongoIdSequence.ts +24 -0
- package/src/storage/mongo/MongoPersistedSyncRules.ts +16 -0
- package/src/storage/mongo/MongoPersistedSyncRulesContent.ts +47 -0
- package/src/storage/mongo/MongoSyncBucketStorage.ts +533 -0
- package/src/storage/mongo/MongoSyncRulesLock.ts +81 -0
- package/src/storage/mongo/OperationBatch.ts +115 -0
- package/src/storage/mongo/PersistedBatch.ts +268 -0
- package/src/storage/mongo/db.ts +73 -0
- package/src/storage/mongo/models.ts +162 -0
- package/src/storage/mongo/util.ts +88 -0
- package/src/storage/storage-index.ts +15 -0
- package/src/sync/BroadcastIterable.ts +161 -0
- package/src/sync/LastValueSink.ts +100 -0
- package/src/sync/merge.ts +200 -0
- package/src/sync/safeRace.ts +99 -0
- package/src/sync/sync-index.ts +6 -0
- package/src/sync/sync.ts +319 -0
- package/src/sync/util.ts +98 -0
- package/src/system/CorePowerSyncSystem.ts +64 -0
- package/src/system/system-index.ts +1 -0
- package/src/util/Mutex.ts +159 -0
- package/src/util/PgManager.ts +64 -0
- package/src/util/alerting.ts +9 -0
- package/src/util/config/collectors/config-collector.ts +143 -0
- package/src/util/config/collectors/impl/base64-config-collector.ts +18 -0
- package/src/util/config/collectors/impl/fallback-config-collector.ts +22 -0
- package/src/util/config/collectors/impl/filesystem-config-collector.ts +43 -0
- package/src/util/config/compound-config-collector.ts +176 -0
- package/src/util/config/sync-rules/impl/base64-sync-rules-collector.ts +21 -0
- package/src/util/config/sync-rules/impl/filesystem-sync-rules-collector.ts +26 -0
- package/src/util/config/sync-rules/impl/inline-sync-rules-collector.ts +21 -0
- package/src/util/config/sync-rules/sync-collector.ts +8 -0
- package/src/util/config/types.ts +66 -0
- package/src/util/config.ts +39 -0
- package/src/util/env.ts +30 -0
- package/src/util/memory-tracking.ts +67 -0
- package/src/util/migration_lib.ts +79 -0
- package/src/util/pgwire_utils.ts +139 -0
- package/src/util/populate_test_data.ts +78 -0
- package/src/util/protocol-types.ts +228 -0
- package/src/util/secs.ts +54 -0
- package/src/util/util-index.ts +25 -0
- package/src/util/utils.ts +122 -0
- package/test/src/__snapshots__/pg_test.test.ts.snap +256 -0
- package/test/src/__snapshots__/sync.test.ts.snap +247 -0
- package/test/src/auth.test.ts +342 -0
- package/test/src/broadcast_iterable.test.ts +156 -0
- package/test/src/checksum_cache.test.ts +436 -0
- package/test/src/data_storage.test.ts +1176 -0
- package/test/src/env.ts +8 -0
- package/test/src/large_batch.test.ts +194 -0
- package/test/src/merge_iterable.test.ts +355 -0
- package/test/src/pg_test.test.ts +450 -0
- package/test/src/schema_changes.test.ts +545 -0
- package/test/src/setup.ts +7 -0
- package/test/src/slow_tests.test.ts +257 -0
- package/test/src/sql_functions.test.ts +254 -0
- package/test/src/sql_operators.test.ts +132 -0
- package/test/src/sync.test.ts +293 -0
- package/test/src/sync_rules.test.ts +1053 -0
- package/test/src/util.ts +76 -0
- package/test/src/validation.test.ts +63 -0
- package/test/src/wal_stream.test.ts +319 -0
- package/test/src/wal_stream_utils.ts +147 -0
- package/test/tsconfig.json +20 -0
- package/tsconfig.json +31 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/vitest.config.ts +9 -0
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import * as t from 'ts-codec';
|
|
2
|
+
import type { FastifyPluginAsync } from 'fastify';
|
|
3
|
+
import * as pgwire from '@powersync/service-jpgwire';
|
|
4
|
+
import { errors, router, schema } from '@powersync/lib-services-framework';
|
|
5
|
+
import { SqlSyncRules, SyncRulesErrors } from '@powersync/service-sync-rules';
|
|
6
|
+
|
|
7
|
+
import * as replication from '../../replication/replication-index.js';
|
|
8
|
+
import { authApi } from '../auth.js';
|
|
9
|
+
import { routeDefinition } from '../router.js';
|
|
10
|
+
|
|
11
|
+
const DeploySyncRulesRequest = t.object({
|
|
12
|
+
content: t.string
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
export const yamlPlugin: FastifyPluginAsync = async (fastify) => {
|
|
16
|
+
fastify.addContentTypeParser('application/yaml', async (request, payload, _d) => {
|
|
17
|
+
const data: any[] = [];
|
|
18
|
+
for await (const chunk of payload) {
|
|
19
|
+
data.push(chunk);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
request.params = { content: Buffer.concat(data).toString('utf8') };
|
|
23
|
+
});
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Declares the plugin should be available on the same scope
|
|
28
|
+
* without requiring the `fastify-plugin` package as a dependency.
|
|
29
|
+
* https://fastify.dev/docs/latest/Reference/Plugins/#handle-the-scope
|
|
30
|
+
*/
|
|
31
|
+
//@ts-expect-error
|
|
32
|
+
yamlPlugin[Symbol.for('skip-override')] = true;
|
|
33
|
+
|
|
34
|
+
export const deploySyncRules = routeDefinition({
|
|
35
|
+
path: '/api/sync-rules/v1/deploy',
|
|
36
|
+
method: router.HTTPMethod.POST,
|
|
37
|
+
authorize: authApi,
|
|
38
|
+
parse: true,
|
|
39
|
+
plugins: [yamlPlugin],
|
|
40
|
+
validator: schema.createTsCodecValidator(DeploySyncRulesRequest, { allowAdditional: true }),
|
|
41
|
+
handler: async (payload) => {
|
|
42
|
+
if (payload.context.system.config.sync_rules.present) {
|
|
43
|
+
// If sync rules are configured via the config, disable deploy via the API.
|
|
44
|
+
throw new errors.JourneyError({
|
|
45
|
+
status: 422,
|
|
46
|
+
code: 'API_DISABLED',
|
|
47
|
+
description: 'Sync rules API disabled',
|
|
48
|
+
details: 'Use the management API to deploy sync rules'
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
const content = payload.params.content;
|
|
52
|
+
|
|
53
|
+
try {
|
|
54
|
+
SqlSyncRules.fromYaml(payload.params.content);
|
|
55
|
+
} catch (e) {
|
|
56
|
+
throw new errors.JourneyError({
|
|
57
|
+
status: 422,
|
|
58
|
+
code: 'INVALID_SYNC_RULES',
|
|
59
|
+
description: 'Sync rules parsing failed',
|
|
60
|
+
details: e.message
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const sync_rules = await payload.context.system.storage.updateSyncRules({
|
|
65
|
+
content: content
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
slot_name: sync_rules.slot_name
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
const ValidateSyncRulesRequest = t.object({
|
|
75
|
+
content: t.string
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
export const validateSyncRules = routeDefinition({
|
|
79
|
+
path: '/api/sync-rules/v1/validate',
|
|
80
|
+
method: router.HTTPMethod.POST,
|
|
81
|
+
authorize: authApi,
|
|
82
|
+
parse: true,
|
|
83
|
+
plugins: [yamlPlugin],
|
|
84
|
+
validator: schema.createTsCodecValidator(ValidateSyncRulesRequest, { allowAdditional: true }),
|
|
85
|
+
handler: async (payload) => {
|
|
86
|
+
const content = payload.params.content;
|
|
87
|
+
|
|
88
|
+
const info = await debugSyncRules(payload.context.system.requirePgPool(), content);
|
|
89
|
+
|
|
90
|
+
return replyPrettyJson(info);
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
export const currentSyncRules = routeDefinition({
|
|
95
|
+
path: '/api/sync-rules/v1/current',
|
|
96
|
+
method: router.HTTPMethod.GET,
|
|
97
|
+
authorize: authApi,
|
|
98
|
+
handler: async (payload) => {
|
|
99
|
+
const storage = payload.context.system.storage;
|
|
100
|
+
const sync_rules = await storage.getActiveSyncRulesContent();
|
|
101
|
+
if (!sync_rules) {
|
|
102
|
+
throw new errors.JourneyError({
|
|
103
|
+
status: 422,
|
|
104
|
+
code: 'NO_SYNC_RULES',
|
|
105
|
+
description: 'No active sync rules'
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
const info = await debugSyncRules(payload.context.system.requirePgPool(), sync_rules.sync_rules_content);
|
|
109
|
+
const next = await storage.getNextSyncRulesContent();
|
|
110
|
+
|
|
111
|
+
const next_info = next
|
|
112
|
+
? await debugSyncRules(payload.context.system.requirePgPool(), next.sync_rules_content)
|
|
113
|
+
: null;
|
|
114
|
+
|
|
115
|
+
const response = {
|
|
116
|
+
current: {
|
|
117
|
+
slot_name: sync_rules.slot_name,
|
|
118
|
+
content: sync_rules.sync_rules_content,
|
|
119
|
+
...info
|
|
120
|
+
},
|
|
121
|
+
next:
|
|
122
|
+
next == null
|
|
123
|
+
? null
|
|
124
|
+
: {
|
|
125
|
+
slot_name: next.slot_name,
|
|
126
|
+
content: next.sync_rules_content,
|
|
127
|
+
...next_info
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
return replyPrettyJson({ data: response });
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
const ReprocessSyncRulesRequest = t.object({});
|
|
136
|
+
|
|
137
|
+
export const reprocessSyncRules = routeDefinition({
|
|
138
|
+
path: '/api/sync-rules/v1/reprocess',
|
|
139
|
+
method: router.HTTPMethod.POST,
|
|
140
|
+
authorize: authApi,
|
|
141
|
+
validator: schema.createTsCodecValidator(ReprocessSyncRulesRequest),
|
|
142
|
+
handler: async (payload) => {
|
|
143
|
+
const storage = payload.context.system.storage;
|
|
144
|
+
const sync_rules = await storage.getActiveSyncRules();
|
|
145
|
+
if (sync_rules == null) {
|
|
146
|
+
throw new errors.JourneyError({
|
|
147
|
+
status: 422,
|
|
148
|
+
code: 'NO_SYNC_RULES',
|
|
149
|
+
description: 'No active sync rules'
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const new_rules = await storage.updateSyncRules({
|
|
154
|
+
content: sync_rules.sync_rules.content
|
|
155
|
+
});
|
|
156
|
+
return {
|
|
157
|
+
slot_name: new_rules.slot_name
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
export const SYNC_RULES_ROUTES = [validateSyncRules, deploySyncRules, reprocessSyncRules, currentSyncRules];
|
|
163
|
+
|
|
164
|
+
function replyPrettyJson(payload: any) {
|
|
165
|
+
return new router.RouterResponse({
|
|
166
|
+
status: 200,
|
|
167
|
+
data: JSON.stringify(payload, null, 2) + '\n',
|
|
168
|
+
headers: { 'Content-Type': 'application/json' }
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
async function debugSyncRules(db: pgwire.PgClient, sync_rules: string) {
|
|
173
|
+
try {
|
|
174
|
+
const rules = SqlSyncRules.fromYaml(sync_rules);
|
|
175
|
+
const source_table_patterns = rules.getSourceTables();
|
|
176
|
+
const wc = new replication.WalConnection({
|
|
177
|
+
db: db,
|
|
178
|
+
sync_rules: rules
|
|
179
|
+
});
|
|
180
|
+
const resolved_tables = await wc.getDebugTablesInfo(source_table_patterns);
|
|
181
|
+
|
|
182
|
+
return {
|
|
183
|
+
valid: true,
|
|
184
|
+
bucket_definitions: rules.bucket_descriptors.map((d) => {
|
|
185
|
+
let all_parameter_queries = [...d.parameter_queries.values()].flat();
|
|
186
|
+
let all_data_queries = [...d.data_queries.values()].flat();
|
|
187
|
+
return {
|
|
188
|
+
name: d.name,
|
|
189
|
+
bucket_parameters: d.bucket_parameters,
|
|
190
|
+
global_parameter_queries: d.global_parameter_queries.map((q) => {
|
|
191
|
+
return {
|
|
192
|
+
sql: q.sql
|
|
193
|
+
};
|
|
194
|
+
}),
|
|
195
|
+
parameter_queries: all_parameter_queries.map((q) => {
|
|
196
|
+
return {
|
|
197
|
+
sql: q.sql,
|
|
198
|
+
table: q.sourceTable,
|
|
199
|
+
input_parameters: q.input_parameters
|
|
200
|
+
};
|
|
201
|
+
}),
|
|
202
|
+
|
|
203
|
+
data_queries: all_data_queries.map((q) => {
|
|
204
|
+
return {
|
|
205
|
+
sql: q.sql,
|
|
206
|
+
table: q.sourceTable,
|
|
207
|
+
columns: q.columnOutputNames()
|
|
208
|
+
};
|
|
209
|
+
})
|
|
210
|
+
};
|
|
211
|
+
}),
|
|
212
|
+
source_tables: resolved_tables,
|
|
213
|
+
data_tables: rules.debugGetOutputTables()
|
|
214
|
+
};
|
|
215
|
+
} catch (e) {
|
|
216
|
+
if (e instanceof SyncRulesErrors) {
|
|
217
|
+
return {
|
|
218
|
+
valid: false,
|
|
219
|
+
errors: e.errors.map((e) => e.message)
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
return {
|
|
223
|
+
valid: false,
|
|
224
|
+
errors: [e.message]
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { Readable } from 'stream';
|
|
2
|
+
import { SyncParameters, normalizeTokenParameters } from '@powersync/service-sync-rules';
|
|
3
|
+
import { errors, logger, router, schema } from '@powersync/lib-services-framework';
|
|
4
|
+
|
|
5
|
+
import * as sync from '../../sync/sync-index.js';
|
|
6
|
+
import * as util from '../../util/util-index.js';
|
|
7
|
+
|
|
8
|
+
import { authUser } from '../auth.js';
|
|
9
|
+
import { routeDefinition } from '../router.js';
|
|
10
|
+
import { Metrics } from '../../metrics/Metrics.js';
|
|
11
|
+
|
|
12
|
+
export enum SyncRoutes {
|
|
13
|
+
STREAM = '/sync/stream'
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const syncStreamed = routeDefinition({
|
|
17
|
+
path: SyncRoutes.STREAM,
|
|
18
|
+
method: router.HTTPMethod.POST,
|
|
19
|
+
authorize: authUser,
|
|
20
|
+
validator: schema.createTsCodecValidator(util.StreamingSyncRequest, { allowAdditional: true }),
|
|
21
|
+
handler: async (payload) => {
|
|
22
|
+
const system = payload.context.system;
|
|
23
|
+
|
|
24
|
+
if (system.closed) {
|
|
25
|
+
throw new errors.JourneyError({
|
|
26
|
+
status: 503,
|
|
27
|
+
code: 'SERVICE_UNAVAILABLE',
|
|
28
|
+
description: 'Service temporarily unavailable'
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const params: util.StreamingSyncRequest = payload.params;
|
|
33
|
+
const syncParams: SyncParameters = normalizeTokenParameters(
|
|
34
|
+
payload.context.token_payload!.parameters ?? {},
|
|
35
|
+
payload.params.parameters ?? {}
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
const storage = system.storage;
|
|
39
|
+
// Sanity check before we start the stream
|
|
40
|
+
const cp = await storage.getActiveCheckpoint();
|
|
41
|
+
if (!cp.hasSyncRules()) {
|
|
42
|
+
throw new errors.JourneyError({
|
|
43
|
+
status: 500,
|
|
44
|
+
code: 'NO_SYNC_RULES',
|
|
45
|
+
description: 'No sync rules available'
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
const controller = new AbortController();
|
|
49
|
+
try {
|
|
50
|
+
Metrics.getInstance().concurrent_connections.add(1);
|
|
51
|
+
const stream = Readable.from(
|
|
52
|
+
sync.transformToBytesTracked(
|
|
53
|
+
sync.ndjson(
|
|
54
|
+
sync.streamResponse({
|
|
55
|
+
storage,
|
|
56
|
+
params,
|
|
57
|
+
syncParams,
|
|
58
|
+
token: payload.context.token_payload!,
|
|
59
|
+
signal: controller.signal
|
|
60
|
+
})
|
|
61
|
+
)
|
|
62
|
+
),
|
|
63
|
+
{ objectMode: false, highWaterMark: 16 * 1024 }
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
const deregister = system.addStopHandler(() => {
|
|
67
|
+
// This error is not currently propagated to the client
|
|
68
|
+
controller.abort();
|
|
69
|
+
stream.destroy(new Error('Shutting down system'));
|
|
70
|
+
});
|
|
71
|
+
stream.on('close', () => {
|
|
72
|
+
deregister();
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
stream.on('error', (error) => {
|
|
76
|
+
controller.abort();
|
|
77
|
+
// Note: This appears as a 200 response in the logs.
|
|
78
|
+
if (error.message != 'Shutting down system') {
|
|
79
|
+
logger.error('Streaming sync request failed', error);
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
return new router.RouterResponse({
|
|
84
|
+
status: 200,
|
|
85
|
+
headers: {
|
|
86
|
+
'Content-Type': 'application/x-ndjson'
|
|
87
|
+
},
|
|
88
|
+
data: stream,
|
|
89
|
+
afterSend: async () => {
|
|
90
|
+
controller.abort();
|
|
91
|
+
Metrics.getInstance().concurrent_connections.add(-1);
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
} catch (ex) {
|
|
95
|
+
controller.abort();
|
|
96
|
+
Metrics.getInstance().concurrent_connections.add(-1);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
export const SYNC_STREAM_ROUTES = [syncStreamed];
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type fastify from 'fastify';
|
|
2
|
+
import a from 'async';
|
|
3
|
+
import { logger } from '@powersync/lib-services-framework';
|
|
4
|
+
|
|
5
|
+
export type CreateRequestQueueParams = {
|
|
6
|
+
max_queue_depth: number;
|
|
7
|
+
concurrency: number;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Creates a request queue which limits the amount of concurrent connections which
|
|
12
|
+
* are active at any time.
|
|
13
|
+
*/
|
|
14
|
+
export const createRequestQueueHook = (params: CreateRequestQueueParams): fastify.onRequestHookHandler => {
|
|
15
|
+
const request_queue = a.queue<() => Promise<void>>((event, done) => {
|
|
16
|
+
event().finally(done);
|
|
17
|
+
}, params.concurrency);
|
|
18
|
+
|
|
19
|
+
return (request, reply, next) => {
|
|
20
|
+
if (
|
|
21
|
+
(params.max_queue_depth == 0 && request_queue.running() == params.concurrency) ||
|
|
22
|
+
(params.max_queue_depth > 0 && request_queue.length() >= params.max_queue_depth)
|
|
23
|
+
) {
|
|
24
|
+
logger.warn(`${request.method} ${request.url}`, {
|
|
25
|
+
status: 429,
|
|
26
|
+
method: request.method,
|
|
27
|
+
path: request.url,
|
|
28
|
+
route: request.routerPath,
|
|
29
|
+
queue_overflow: true
|
|
30
|
+
});
|
|
31
|
+
return reply.status(429).send();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const finished = new Promise<void>((resolve) => {
|
|
35
|
+
reply.then(
|
|
36
|
+
() => resolve(),
|
|
37
|
+
() => resolve()
|
|
38
|
+
);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
request_queue.push(() => {
|
|
42
|
+
next();
|
|
43
|
+
return finished;
|
|
44
|
+
});
|
|
45
|
+
};
|
|
46
|
+
};
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import fastify from 'fastify';
|
|
2
|
+
|
|
3
|
+
import { errors, router, HTTPMethod, logger } from '@powersync/lib-services-framework';
|
|
4
|
+
import { Context, ContextProvider, RequestEndpoint, RequestEndpointHandlerPayload } from './router.js';
|
|
5
|
+
|
|
6
|
+
export type FastifyEndpoint<I, O, C> = RequestEndpoint<I, O, C> & {
|
|
7
|
+
parse?: boolean;
|
|
8
|
+
plugins?: fastify.FastifyPluginAsync[];
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Registers endpoint definitions as routes on a Fastify app instance.
|
|
13
|
+
*/
|
|
14
|
+
export function registerFastifyRoutes(
|
|
15
|
+
app: fastify.FastifyInstance,
|
|
16
|
+
contextProvider: ContextProvider,
|
|
17
|
+
endpoints: FastifyEndpoint<any, any, Context>[]
|
|
18
|
+
) {
|
|
19
|
+
for (const e of endpoints) {
|
|
20
|
+
// Create a new context for each route
|
|
21
|
+
app.register(async function (fastify) {
|
|
22
|
+
fastify.route({
|
|
23
|
+
url: e.path,
|
|
24
|
+
method: e.method as HTTPMethod,
|
|
25
|
+
handler: async (request, reply) => {
|
|
26
|
+
const startTime = new Date();
|
|
27
|
+
let response: router.RouterResponse;
|
|
28
|
+
try {
|
|
29
|
+
const context = await contextProvider(request);
|
|
30
|
+
|
|
31
|
+
let combined = {
|
|
32
|
+
...(request.params as any),
|
|
33
|
+
...(request.query as any)
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
if (typeof request.body === 'object' && !Buffer.isBuffer(request.body) && !Array.isArray(request.body)) {
|
|
37
|
+
combined = {
|
|
38
|
+
...combined,
|
|
39
|
+
...request.body
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const payload: RequestEndpointHandlerPayload = {
|
|
44
|
+
context: context,
|
|
45
|
+
params: combined,
|
|
46
|
+
request
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const endpointResponse = await router.executeEndpoint(e, payload);
|
|
50
|
+
|
|
51
|
+
if (router.RouterResponse.isRouterResponse(endpointResponse)) {
|
|
52
|
+
response = endpointResponse;
|
|
53
|
+
} else if (router.isAsyncIterable(endpointResponse) || Buffer.isBuffer(endpointResponse)) {
|
|
54
|
+
response = new router.RouterResponse({
|
|
55
|
+
status: 200,
|
|
56
|
+
data: endpointResponse
|
|
57
|
+
});
|
|
58
|
+
} else {
|
|
59
|
+
response = new router.RouterResponse({
|
|
60
|
+
status: 200,
|
|
61
|
+
data: { data: endpointResponse }
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
} catch (ex) {
|
|
65
|
+
const journeyError = errors.JourneyError.isJourneyError(ex) ? ex : new errors.InternalServerError(ex);
|
|
66
|
+
|
|
67
|
+
response = new router.RouterResponse({
|
|
68
|
+
status: journeyError.errorData.status || 500,
|
|
69
|
+
headers: {
|
|
70
|
+
'Content-Type': 'application/json'
|
|
71
|
+
},
|
|
72
|
+
data: {
|
|
73
|
+
error: journeyError.errorData
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
Object.keys(response.headers).forEach((key) => {
|
|
79
|
+
reply.header(key, response.headers[key]);
|
|
80
|
+
});
|
|
81
|
+
reply.status(response.status);
|
|
82
|
+
try {
|
|
83
|
+
await reply.send(response.data);
|
|
84
|
+
} finally {
|
|
85
|
+
await response.afterSend?.();
|
|
86
|
+
logger.info(`${e.method} ${request.url}`, {
|
|
87
|
+
duration_ms: Math.round(new Date().valueOf() - startTime.valueOf() + Number.EPSILON),
|
|
88
|
+
status: response.status,
|
|
89
|
+
method: e.method,
|
|
90
|
+
path: request.url,
|
|
91
|
+
route: e.path
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
if (!(e.parse ?? true)) {
|
|
98
|
+
fastify.removeAllContentTypeParsers();
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
e.plugins?.forEach((plugin) => fastify.register(plugin));
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import * as t from 'ts-codec';
|
|
2
|
+
import { ReactiveSocketRouter, IReactiveStream } from '@powersync/service-rsocket-router';
|
|
3
|
+
|
|
4
|
+
import { Context } from './router.js';
|
|
5
|
+
|
|
6
|
+
export const RSocketContextMeta = t.object({
|
|
7
|
+
token: t.string
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Creates a socket route handler given a router instance
|
|
12
|
+
*/
|
|
13
|
+
export type SocketRouteGenerator = (router: ReactiveSocketRouter<Context>) => IReactiveStream;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { router } from '@powersync/lib-services-framework';
|
|
2
|
+
import * as auth from '../auth/auth-index.js';
|
|
3
|
+
import { CorePowerSyncSystem } from '../system/CorePowerSyncSystem.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Common context for routes
|
|
7
|
+
*/
|
|
8
|
+
export type Context = {
|
|
9
|
+
user_id?: string;
|
|
10
|
+
system: CorePowerSyncSystem;
|
|
11
|
+
|
|
12
|
+
token_payload?: auth.JwtPayload;
|
|
13
|
+
token_errors?: string[];
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export type BasicRouterRequest = {
|
|
17
|
+
headers: Record<string, string | string[] | undefined>;
|
|
18
|
+
protocol: string;
|
|
19
|
+
hostname: string;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export type ContextProvider = (request: BasicRouterRequest) => Promise<Context>;
|
|
23
|
+
|
|
24
|
+
export type RequestEndpoint<
|
|
25
|
+
I,
|
|
26
|
+
O,
|
|
27
|
+
C = Context,
|
|
28
|
+
Payload = RequestEndpointHandlerPayload<I, C, BasicRouterRequest>
|
|
29
|
+
> = router.Endpoint<I, O, C, Payload> & {};
|
|
30
|
+
|
|
31
|
+
export type RequestEndpointHandlerPayload<
|
|
32
|
+
I = any,
|
|
33
|
+
C = Context,
|
|
34
|
+
Request = BasicRouterRequest
|
|
35
|
+
> = router.EndpointHandlerPayload<I, C> & {
|
|
36
|
+
request: Request;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Helper function for making generics work well when defining routes
|
|
41
|
+
*/
|
|
42
|
+
export function routeDefinition<I, O, C = Context, Extension = {}>(
|
|
43
|
+
params: RequestEndpoint<I, O, C> & Extension
|
|
44
|
+
): RequestEndpoint<I, O, C> & Extension {
|
|
45
|
+
return params;
|
|
46
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
// Script to tear down the data when deleting an instance.
|
|
2
|
+
// This deletes:
|
|
3
|
+
// 1. The replication slots on the source postgres instance (if available).
|
|
4
|
+
// 2. The mongo database.
|
|
5
|
+
|
|
6
|
+
import * as timers from 'timers/promises';
|
|
7
|
+
|
|
8
|
+
import * as db from '../db/db-index.js';
|
|
9
|
+
import * as storage from '../storage/storage-index.js';
|
|
10
|
+
import * as utils from '../util/util-index.js';
|
|
11
|
+
import * as replication from '../replication/replication-index.js';
|
|
12
|
+
import { logger } from '@powersync/lib-services-framework';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Attempt to terminate a single sync rules instance.
|
|
16
|
+
*
|
|
17
|
+
* This may fail with a lock error.
|
|
18
|
+
*/
|
|
19
|
+
async function terminateReplicator(
|
|
20
|
+
storageFactory: storage.BucketStorageFactory,
|
|
21
|
+
connection: utils.ResolvedConnection,
|
|
22
|
+
syncRules: storage.PersistedSyncRulesContent
|
|
23
|
+
) {
|
|
24
|
+
// The lock may still be active if the current replication instance
|
|
25
|
+
// hasn't stopped yet.
|
|
26
|
+
const lock = await syncRules.lock();
|
|
27
|
+
try {
|
|
28
|
+
const parsed = syncRules.parsed();
|
|
29
|
+
const storage = storageFactory.getInstance(parsed);
|
|
30
|
+
const stream = new replication.WalStreamRunner({
|
|
31
|
+
factory: storageFactory,
|
|
32
|
+
storage: storage,
|
|
33
|
+
source_db: connection,
|
|
34
|
+
lock
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
logger.info(`Terminating replication slot ${stream.slot_name}`);
|
|
38
|
+
await stream.terminate();
|
|
39
|
+
logger.info(`Terminated replication slot ${stream.slot_name}`);
|
|
40
|
+
} finally {
|
|
41
|
+
await lock.release();
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Terminate all replicating sync rules, deleting the replication slots.
|
|
47
|
+
*
|
|
48
|
+
* Retries lock and other errors for up to two minutes.
|
|
49
|
+
*
|
|
50
|
+
* This is a best-effot attempt. In some cases it may not be possible to delete the replication
|
|
51
|
+
* slot, such as when the postgres instance is unreachable.
|
|
52
|
+
*/
|
|
53
|
+
async function terminateReplicators(
|
|
54
|
+
storageFactory: storage.BucketStorageFactory,
|
|
55
|
+
connection: utils.ResolvedConnection
|
|
56
|
+
) {
|
|
57
|
+
const start = Date.now();
|
|
58
|
+
while (Date.now() - start < 12_000) {
|
|
59
|
+
let retry = false;
|
|
60
|
+
const replicationRules = await storageFactory.getReplicatingSyncRules();
|
|
61
|
+
for (let syncRules of replicationRules) {
|
|
62
|
+
try {
|
|
63
|
+
await terminateReplicator(storageFactory, connection, syncRules);
|
|
64
|
+
} catch (e) {
|
|
65
|
+
retry = true;
|
|
66
|
+
console.error(e);
|
|
67
|
+
logger.warn(`Failed to terminate ${syncRules.slot_name}`, e);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
if (!retry) {
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
await timers.setTimeout(5_000);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export async function teardown(runnerConfig: utils.RunnerConfig) {
|
|
78
|
+
const config = await utils.loadConfig(runnerConfig);
|
|
79
|
+
const mongoDB = storage.createPowerSyncMongo(config.storage);
|
|
80
|
+
try {
|
|
81
|
+
logger.info(`Waiting for auth`);
|
|
82
|
+
await db.mongo.waitForAuth(mongoDB.db);
|
|
83
|
+
|
|
84
|
+
const bucketStorage = new storage.MongoBucketStorage(mongoDB, { slot_name_prefix: config.slot_name_prefix });
|
|
85
|
+
const connection = config.connection;
|
|
86
|
+
|
|
87
|
+
logger.info(`Terminating replication slots`);
|
|
88
|
+
|
|
89
|
+
if (connection) {
|
|
90
|
+
await terminateReplicators(bucketStorage, connection);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const database = mongoDB.db;
|
|
94
|
+
logger.info(`Dropping database ${database.namespace}`);
|
|
95
|
+
await database.dropDatabase();
|
|
96
|
+
logger.info(`Done`);
|
|
97
|
+
await mongoDB.client.close();
|
|
98
|
+
|
|
99
|
+
// If there was an error connecting to postgress, the process may stay open indefinitely.
|
|
100
|
+
// This forces an exit.
|
|
101
|
+
// We do not consider those errors a teardown failure.
|
|
102
|
+
process.exit(0);
|
|
103
|
+
} catch (e) {
|
|
104
|
+
logger.error(`Teardown failure`, e);
|
|
105
|
+
await mongoDB.client.close();
|
|
106
|
+
process.exit(1);
|
|
107
|
+
}
|
|
108
|
+
}
|