sonamu 0.10.11 → 0.10.13
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/dist/ai/agents/agent.d.ts +1 -1
- package/dist/ai/agents/agent.d.ts.map +1 -1
- package/dist/ai/agents/agent.js +5 -6
- package/dist/ai/agents/types.d.ts +1 -1
- package/dist/ai/agents/types.d.ts.map +1 -1
- package/dist/ai/providers/rtzr/api.js +18 -18
- package/dist/ai/providers/rtzr/error.d.ts.map +1 -1
- package/dist/ai/providers/rtzr/error.js +17 -15
- package/dist/ai/providers/rtzr/model.d.ts.map +1 -1
- package/dist/ai/providers/rtzr/model.js +3 -2
- package/dist/ai/providers/rtzr/options.d.ts.map +1 -1
- package/dist/ai/providers/rtzr/options.js +16 -16
- package/dist/ai/providers/rtzr/provider.d.ts.map +1 -1
- package/dist/ai/providers/rtzr/provider.js +9 -9
- package/dist/ai/providers/rtzr/utils.d.ts.map +1 -1
- package/dist/ai/providers/rtzr/utils.js +1 -1
- package/dist/api/caster.d.ts.map +1 -1
- package/dist/api/caster.js +17 -17
- package/dist/api/code-converters.d.ts +4 -2
- package/dist/api/code-converters.d.ts.map +1 -1
- package/dist/api/code-converters.js +100 -19
- package/dist/api/config.d.ts +16 -1
- package/dist/api/config.d.ts.map +1 -1
- package/dist/api/config.js +28 -21
- package/dist/api/context.d.ts +2 -2
- package/dist/api/context.d.ts.map +1 -1
- package/dist/api/context.js +1 -1
- package/dist/api/decorators.d.ts.map +1 -1
- package/dist/api/decorators.js +18 -11
- package/dist/api/http-validator.d.ts +36 -0
- package/dist/api/http-validator.d.ts.map +1 -0
- package/dist/api/http-validator.js +72 -0
- package/dist/api/index.js +6 -4
- package/dist/api/sonamu.d.ts +59 -20
- package/dist/api/sonamu.d.ts.map +1 -1
- package/dist/api/sonamu.js +250 -106
- package/dist/api/websocket-helpers.d.ts +7 -5
- package/dist/api/websocket-helpers.d.ts.map +1 -1
- package/dist/api/websocket-helpers.js +6 -6
- package/dist/auth/audit-log/builders.d.ts.map +1 -1
- package/dist/auth/audit-log/builders.js +1 -1
- package/dist/auth/audit-log/client-ip.d.ts +1 -1
- package/dist/auth/audit-log/client-ip.d.ts.map +1 -1
- package/dist/auth/audit-log/client-ip.js +4 -2
- package/dist/auth/audit-log/events.d.ts +5 -1
- package/dist/auth/audit-log/events.d.ts.map +1 -1
- package/dist/auth/audit-log/events.js +1 -1
- package/dist/auth/audit-log/plugin.d.ts +0 -8
- package/dist/auth/audit-log/plugin.d.ts.map +1 -1
- package/dist/auth/audit-log/plugin.js +48 -52
- package/dist/auth/audit-log-ingestor.d.ts.map +1 -1
- package/dist/auth/audit-log-ingestor.js +16 -12
- package/dist/auth/auth-generator.d.ts.map +1 -1
- package/dist/auth/auth-generator.js +1 -1
- package/dist/auth/better-auth-request.d.ts.map +1 -1
- package/dist/auth/better-auth-request.js +6 -5
- package/dist/auth/knex-adapter.js +1 -1
- package/dist/auth/plugins/entity-definitions/index.d.ts +13 -1
- package/dist/auth/plugins/entity-definitions/index.d.ts.map +1 -1
- package/dist/auth/plugins/entity-definitions/index.js +1 -1
- package/dist/auth/plugins/wrappers/admin.d.ts +1 -1
- package/dist/bin/build-config.d.ts +25 -0
- package/dist/bin/build-config.d.ts.map +1 -1
- package/dist/bin/build-config.js +105 -2
- package/dist/bin/cli.js +133 -29
- package/dist/bin/compiler-policy.d.ts +3 -0
- package/dist/bin/compiler-policy.d.ts.map +1 -0
- package/dist/bin/compiler-policy.js +24 -0
- package/dist/bin/fixture.d.ts.map +1 -1
- package/dist/bin/fixture.js +5 -3
- package/dist/bin/test-command.d.ts +22 -1
- package/dist/bin/test-command.d.ts.map +1 -1
- package/dist/bin/test-command.js +18 -10
- package/dist/bin/ts-loader-registration.d.ts +7 -1
- package/dist/bin/ts-loader-registration.d.ts.map +1 -1
- package/dist/bin/ts-loader-registration.js +13 -4
- package/dist/cache/decorator.d.ts.map +1 -1
- package/dist/cache/decorator.js +11 -4
- package/dist/compress/compress.d.ts.map +1 -1
- package/dist/compress/compress.js +1 -1
- package/dist/cone/cone-generator.d.ts +14 -1
- package/dist/cone/cone-generator.d.ts.map +1 -1
- package/dist/cone/cone-generator.js +13 -9
- package/dist/database/_batch_update.js +1 -1
- package/dist/database/base-model.d.ts +22 -10
- package/dist/database/base-model.d.ts.map +1 -1
- package/dist/database/base-model.js +26 -16
- package/dist/database/base-model.types.d.ts +15 -3
- package/dist/database/base-model.types.d.ts.map +1 -1
- package/dist/database/db.d.ts +12 -2
- package/dist/database/db.d.ts.map +1 -1
- package/dist/database/db.js +10 -7
- package/dist/database/knex.d.ts.map +1 -1
- package/dist/database/knex.js +17 -14
- package/dist/database/puri.d.ts +11 -9
- package/dist/database/puri.d.ts.map +1 -1
- package/dist/database/puri.js +93 -79
- package/dist/database/puri.types.d.ts +21 -21
- package/dist/database/puri.types.d.ts.map +1 -1
- package/dist/database/puri.types.js +1 -1
- package/dist/database/upsert-builder.d.ts +12 -10
- package/dist/database/upsert-builder.d.ts.map +1 -1
- package/dist/database/upsert-builder.js +20 -12
- package/dist/dict/sd.d.ts.map +1 -1
- package/dist/dict/sd.js +1 -1
- package/dist/dict/sonamu-dictionary.d.ts.map +1 -1
- package/dist/dict/sonamu-dictionary.js +1 -1
- package/dist/dict/utils.d.ts.map +1 -1
- package/dist/dict/utils.js +7 -2
- package/dist/entity/entity-manager.d.ts +13 -4
- package/dist/entity/entity-manager.d.ts.map +1 -1
- package/dist/entity/entity-manager.js +19 -11
- package/dist/entity/entity-template-cone.d.ts +73 -5
- package/dist/entity/entity-template-cone.d.ts.map +1 -1
- package/dist/entity/entity-template-cone.js +4 -2
- package/dist/entity/entity.d.ts +6 -2
- package/dist/entity/entity.d.ts.map +1 -1
- package/dist/entity/entity.js +30 -28
- package/dist/env.d.ts +3 -1
- package/dist/env.d.ts.map +1 -1
- package/dist/env.js +1 -1
- package/dist/exceptions/error-handler.d.ts.map +1 -1
- package/dist/exceptions/error-handler.js +7 -2
- package/dist/exceptions/so-exceptions.d.ts +24 -20
- package/dist/exceptions/so-exceptions.d.ts.map +1 -1
- package/dist/exceptions/so-exceptions.js +4 -2
- package/dist/filter/utils.d.ts +7 -2
- package/dist/filter/utils.d.ts.map +1 -1
- package/dist/filter/utils.js +8 -6
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -7
- package/dist/logger/configure.d.ts.map +1 -1
- package/dist/logger/configure.js +10 -10
- package/dist/migration/code-generation.d.ts.map +1 -1
- package/dist/migration/code-generation.js +50 -46
- package/dist/migration/index-where-predicate.d.ts.map +1 -1
- package/dist/migration/index-where-predicate.js +22 -8
- package/dist/migration/migration-set.d.ts.map +1 -1
- package/dist/migration/migration-set.js +1 -1
- package/dist/migration/migrator.d.ts +61 -36
- package/dist/migration/migrator.d.ts.map +1 -1
- package/dist/migration/migrator.js +276 -167
- package/dist/migration/postgresql-schema-reader.d.ts.map +1 -1
- package/dist/migration/postgresql-schema-reader.js +51 -36
- package/dist/migration/slack-confirm.d.ts.map +1 -1
- package/dist/migration/slack-confirm.js +1 -1
- package/dist/migration/types.d.ts +54 -0
- package/dist/migration/types.d.ts.map +1 -1
- package/dist/naite/naite-reporter.js +2 -2
- package/dist/naite/naite.d.ts +74 -15
- package/dist/naite/naite.d.ts.map +1 -1
- package/dist/naite/naite.js +5 -19
- package/dist/runtime-type.d.ts +6 -0
- package/dist/runtime-type.d.ts.map +1 -0
- package/dist/runtime-type.js +26 -0
- package/dist/ssr/hmr-opt-out.d.ts +26 -0
- package/dist/ssr/hmr-opt-out.d.ts.map +1 -0
- package/dist/ssr/hmr-opt-out.js +59 -0
- package/dist/ssr/renderer.d.ts.map +1 -1
- package/dist/ssr/renderer.js +4 -1
- package/dist/storage/base-file.js +14 -14
- package/dist/storage/buffered-file.d.ts +2 -2
- package/dist/storage/buffered-file.d.ts.map +1 -1
- package/dist/storage/buffered-file.js +12 -12
- package/dist/storage/s3-driver.d.ts.map +1 -1
- package/dist/storage/s3-driver.js +1 -1
- package/dist/storage/uploaded-file.d.ts +2 -2
- package/dist/storage/uploaded-file.d.ts.map +1 -1
- package/dist/storage/uploaded-file.js +8 -8
- package/dist/stream/sse.js +11 -11
- package/dist/stream/ws-core.d.ts +1 -1
- package/dist/stream/ws-core.d.ts.map +1 -1
- package/dist/stream/ws-delivery.d.ts +1 -1
- package/dist/stream/ws-delivery.d.ts.map +1 -1
- package/dist/stream/ws-delivery.js +6 -4
- package/dist/stream/ws-local-connection-store.d.ts.map +1 -1
- package/dist/stream/ws-local-connection-store.js +4 -2
- package/dist/stream/ws-presence-store.js +1 -1
- package/dist/stream/ws-registry.d.ts +4 -4
- package/dist/stream/ws-registry.d.ts.map +1 -1
- package/dist/stream/ws-registry.js +4 -2
- package/dist/stream/ws-telemetry.d.ts +16 -12
- package/dist/stream/ws-telemetry.d.ts.map +1 -1
- package/dist/stream/ws-telemetry.js +22 -18
- package/dist/stream/ws.d.ts +18 -12
- package/dist/stream/ws.d.ts.map +1 -1
- package/dist/stream/ws.js +26 -22
- package/dist/syncer/api-parser.js +1 -1
- package/dist/syncer/checksum.d.ts.map +1 -1
- package/dist/syncer/checksum.js +7 -8
- package/dist/syncer/code-generator.d.ts.map +1 -1
- package/dist/syncer/code-generator.js +13 -14
- package/dist/syncer/entity-operations.d.ts.map +1 -1
- package/dist/syncer/entity-operations.js +5 -6
- package/dist/syncer/file-copy.d.ts +5 -0
- package/dist/syncer/file-copy.d.ts.map +1 -0
- package/dist/syncer/file-copy.js +36 -0
- package/dist/syncer/file-patterns.d.ts +1 -0
- package/dist/syncer/file-patterns.d.ts.map +1 -1
- package/dist/syncer/file-patterns.js +5 -2
- package/dist/syncer/file-tracking.d.ts.map +1 -1
- package/dist/syncer/file-tracking.js +5 -4
- package/dist/syncer/filesystem-dependencies.d.ts +15 -0
- package/dist/syncer/filesystem-dependencies.d.ts.map +1 -0
- package/dist/syncer/filesystem-dependencies.js +53 -0
- package/dist/syncer/module-loader.d.ts +1 -1
- package/dist/syncer/module-loader.d.ts.map +1 -1
- package/dist/syncer/module-loader.js +6 -4
- package/dist/syncer/syncer-actions.d.ts +4 -0
- package/dist/syncer/syncer-actions.d.ts.map +1 -1
- package/dist/syncer/syncer-actions.js +34 -17
- package/dist/syncer/syncer.d.ts +17 -6
- package/dist/syncer/syncer.d.ts.map +1 -1
- package/dist/syncer/syncer.js +71 -29
- package/dist/syncer/watcher.d.ts.map +1 -1
- package/dist/syncer/watcher.js +3 -3
- package/dist/tasks/decorator.d.ts +6 -6
- package/dist/tasks/decorator.d.ts.map +1 -1
- package/dist/tasks/decorator.js +4 -4
- package/dist/tasks/step-wrapper.d.ts +6 -4
- package/dist/tasks/step-wrapper.d.ts.map +1 -1
- package/dist/tasks/step-wrapper.js +5 -5
- package/dist/tasks/workflow-manager.d.ts.map +1 -1
- package/dist/tasks/workflow-manager.js +13 -7
- package/dist/template/entity-converter.d.ts +1 -1
- package/dist/template/entity-converter.d.ts.map +1 -1
- package/dist/template/entity-converter.js +2 -2
- package/dist/template/helpers.d.ts +8 -6
- package/dist/template/helpers.d.ts.map +1 -1
- package/dist/template/helpers.js +1 -1
- package/dist/template/implementations/generated.template.d.ts.map +1 -1
- package/dist/template/implementations/generated.template.js +4 -4
- package/dist/template/implementations/generated_http.template.d.ts +7 -4
- package/dist/template/implementations/generated_http.template.d.ts.map +1 -1
- package/dist/template/implementations/generated_http.template.js +21 -21
- package/dist/template/implementations/generated_sso.template.d.ts.map +1 -1
- package/dist/template/implementations/generated_sso.template.js +6 -3
- package/dist/template/implementations/http_validators.template.d.ts +18 -0
- package/dist/template/implementations/http_validators.template.d.ts.map +1 -0
- package/dist/template/implementations/http_validators.template.js +127 -0
- package/dist/template/implementations/model.template.d.ts.map +1 -1
- package/dist/template/implementations/model.template.js +1 -1
- package/dist/template/implementations/queries.template.js +1 -1
- package/dist/template/implementations/sd.template.d.ts.map +1 -1
- package/dist/template/implementations/sd.template.js +79 -39
- package/dist/template/implementations/services.template.d.ts.map +1 -1
- package/dist/template/implementations/services.template.js +6 -4
- package/dist/template/implementations/view_form.template.d.ts +6 -1
- package/dist/template/implementations/view_form.template.d.ts.map +1 -1
- package/dist/template/implementations/view_form.template.js +23 -15
- package/dist/template/implementations/view_list.template.d.ts.map +1 -1
- package/dist/template/implementations/view_list.template.js +11 -6
- package/dist/template/template-manager.d.ts.map +1 -1
- package/dist/template/template-manager.js +9 -7
- package/dist/template/template-types.d.ts +12 -5
- package/dist/template/template-types.d.ts.map +1 -1
- package/dist/template/template-types.js +1 -1
- package/dist/template/template.d.ts +2 -2
- package/dist/template/template.d.ts.map +1 -1
- package/dist/template/template.js +6 -4
- package/dist/template/zod-converter.d.ts +0 -19
- package/dist/template/zod-converter.d.ts.map +1 -1
- package/dist/template/zod-converter.js +100 -84
- package/dist/testing/_relation-graph.d.ts.map +1 -1
- package/dist/testing/_relation-graph.js +2 -1
- package/dist/testing/bootstrap.d.ts.map +1 -1
- package/dist/testing/bootstrap.js +9 -6
- package/dist/testing/data-explorer.d.ts +32 -7
- package/dist/testing/data-explorer.d.ts.map +1 -1
- package/dist/testing/data-explorer.js +77 -31
- package/dist/testing/dev-test-routes.d.ts +8 -1
- package/dist/testing/dev-test-routes.d.ts.map +1 -1
- package/dist/testing/dev-test-routes.js +125 -108
- package/dist/testing/dev-vitest-manager.d.ts +54 -8
- package/dist/testing/dev-vitest-manager.d.ts.map +1 -1
- package/dist/testing/dev-vitest-manager.js +24 -9
- package/dist/testing/faker-mappings.d.ts +412 -1
- package/dist/testing/faker-mappings.d.ts.map +1 -1
- package/dist/testing/faker-mappings.js +1 -1
- package/dist/testing/filesystem.d.ts +2 -0
- package/dist/testing/filesystem.d.ts.map +1 -0
- package/dist/testing/filesystem.js +7 -0
- package/dist/testing/fixture-generator.d.ts +25 -8
- package/dist/testing/fixture-generator.d.ts.map +1 -1
- package/dist/testing/fixture-generator.js +132 -73
- package/dist/testing/fixture-loader.d.ts +3 -1
- package/dist/testing/fixture-loader.d.ts.map +1 -1
- package/dist/testing/fixture-loader.js +1 -20
- package/dist/testing/fixture-manager.d.ts +8 -7
- package/dist/testing/fixture-manager.d.ts.map +1 -1
- package/dist/testing/fixture-manager.js +91 -59
- package/dist/testing/global-setup.d.ts.map +1 -1
- package/dist/testing/global-setup.js +5 -2
- package/dist/testing/index.d.ts +2 -0
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/index.js +6 -3
- package/dist/types/types.d.ts +101 -99
- package/dist/types/types.d.ts.map +1 -1
- package/dist/types/types.js +234 -225
- package/dist/ui/ai-api.d.ts.map +1 -1
- package/dist/ui/ai-api.js +1 -1
- package/dist/ui/ai-client.d.ts.map +1 -1
- package/dist/ui/ai-client.js +28 -18
- package/dist/ui/api.d.ts.map +1 -1
- package/dist/ui/api.js +146 -210
- package/dist/ui/async-route.d.ts +3 -0
- package/dist/ui/async-route.d.ts.map +1 -0
- package/dist/ui/async-route.js +12 -0
- package/dist/ui/cdd-service.d.ts +9 -7
- package/dist/ui/cdd-service.d.ts.map +1 -1
- package/dist/ui/cdd-service.js +7 -5
- package/dist/ui/migrations-api.d.ts +3 -0
- package/dist/ui/migrations-api.d.ts.map +1 -0
- package/dist/ui/migrations-api.js +215 -0
- package/dist/ui-web/assets/index-D3QNH-Dv.css +1 -0
- package/dist/ui-web/assets/index-DV1AAq6W.js +178 -0
- package/dist/ui-web/index.html +2 -2
- package/dist/utils/formatter.d.ts.map +1 -1
- package/dist/utils/formatter.js +7 -5
- package/dist/utils/fs-utils.d.ts +3 -3
- package/dist/utils/fs-utils.d.ts.map +1 -1
- package/dist/utils/fs-utils.js +6 -6
- package/dist/utils/model.d.ts +3 -5
- package/dist/utils/model.d.ts.map +1 -1
- package/dist/utils/model.js +1 -1
- package/dist/utils/object-utils.d.ts +1 -1
- package/dist/utils/object-utils.d.ts.map +1 -1
- package/dist/utils/object-utils.js +17 -16
- package/dist/utils/process-utils.d.ts.map +1 -1
- package/dist/utils/process-utils.js +4 -2
- package/dist/utils/runtime-value.d.ts +13 -0
- package/dist/utils/runtime-value.d.ts.map +1 -0
- package/dist/utils/runtime-value.js +40 -0
- package/dist/utils/sql-parser.d.ts +8 -1
- package/dist/utils/sql-parser.d.ts.map +1 -1
- package/dist/utils/sql-parser.js +6 -3
- package/dist/utils/type-utils.d.ts.map +1 -1
- package/dist/utils/type-utils.js +10 -8
- package/dist/utils/utils.d.ts +2 -2
- package/dist/utils/utils.d.ts.map +1 -1
- package/dist/utils/utils.js +11 -8
- package/dist/utils/zod-error.d.ts.map +1 -1
- package/dist/utils/zod-error.js +7 -4
- package/dist/vector/chunking.d.ts.map +1 -1
- package/dist/vector/chunking.js +1 -1
- package/dist/vector/embedding.d.ts.map +1 -1
- package/dist/vector/embedding.js +1 -1
- package/dist/vector/types.d.ts +3 -2
- package/dist/vector/types.d.ts.map +1 -1
- package/package.json +11 -7
- package/src/ai/agents/agent.ts +5 -5
- package/src/ai/agents/types.ts +1 -1
- package/src/ai/providers/rtzr/__tests__/model.test.ts +2 -2
- package/src/ai/providers/rtzr/error.ts +5 -3
- package/src/ai/providers/rtzr/model.ts +3 -2
- package/src/ai/providers/rtzr/options.ts +10 -5
- package/src/ai/providers/rtzr/provider.ts +10 -10
- package/src/ai/providers/rtzr/utils.ts +3 -1
- package/src/api/__tests__/config.test.ts +11 -21
- package/src/api/__tests__/sonamu.http-validator.test.ts +512 -0
- package/src/api/__tests__/sonamu.websocket.test.ts +51 -41
- package/src/api/__tests__/zod-compiler-policy.test.ts +95 -0
- package/src/api/caster.ts +6 -8
- package/src/api/code-converters.ts +136 -36
- package/src/api/config.ts +62 -4
- package/src/api/context.ts +3 -3
- package/src/api/decorators.ts +28 -17
- package/src/api/http-validator.ts +149 -0
- package/src/api/sonamu.ts +459 -152
- package/src/api/websocket-helpers.ts +24 -17
- package/src/auth/audit-log/builders.ts +3 -2
- package/src/auth/audit-log/client-ip.ts +4 -2
- package/src/auth/audit-log/events.ts +15 -1
- package/src/auth/audit-log/plugin.ts +129 -200
- package/src/auth/audit-log-ingestor.ts +19 -15
- package/src/auth/auth-generator.ts +10 -5
- package/src/auth/better-auth-request.ts +4 -3
- package/src/auth/knex-adapter.ts +8 -2
- package/src/auth/plugins/entity-definitions/index.ts +6 -5
- package/src/bin/__tests__/http-validator-benchmark.test.ts +94 -0
- package/src/bin/__tests__/test-command.test.ts +9 -15
- package/src/bin/__tests__/ts-loader-register.test.ts +15 -17
- package/src/bin/__tests__/zod-compiler-build.test.ts +307 -0
- package/src/bin/build-config.ts +167 -0
- package/src/bin/cli.ts +155 -38
- package/src/bin/compiler-policy.ts +35 -0
- package/src/bin/fixture.ts +14 -3
- package/src/bin/test-command.ts +74 -29
- package/src/bin/ts-loader-registration.ts +22 -4
- package/src/cache/decorator.ts +10 -3
- package/src/compress/compress.ts +17 -4
- package/src/cone/cone-generator.ts +34 -6
- package/src/database/__tests__/db.test.ts +1 -0
- package/src/database/__tests__/puri.test.ts +16 -15
- package/src/database/__tests__/transaction-scope.test.ts +5 -0
- package/src/database/_batch_update.ts +1 -0
- package/src/database/base-model.ts +81 -50
- package/src/database/base-model.types.ts +6 -6
- package/src/database/db.ts +29 -19
- package/src/database/knex.ts +30 -17
- package/src/database/puri-subset.test-d.ts +17 -0
- package/src/database/puri.ts +181 -143
- package/src/database/puri.types.test-d.ts +31 -1
- package/src/database/puri.types.ts +23 -21
- package/src/database/upsert-builder.ts +75 -53
- package/src/dict/sd.ts +15 -5
- package/src/dict/sonamu-dictionary.ts +6 -6
- package/src/dict/utils.ts +9 -1
- package/src/entity/entity-manager.ts +26 -9
- package/src/entity/entity-template-cone.ts +5 -10
- package/src/entity/entity.ts +50 -34
- package/src/env.ts +7 -3
- package/src/exceptions/error-handler.ts +17 -2
- package/src/exceptions/so-exceptions.ts +26 -20
- package/src/filter/utils.ts +21 -11
- package/src/index.ts +3 -0
- package/src/logger/configure.ts +26 -18
- package/src/migration/__tests__/migrator.test.ts +50 -57
- package/src/migration/code-generation.ts +132 -104
- package/src/migration/index-where-predicate.ts +51 -24
- package/src/migration/migration-set.ts +11 -5
- package/src/migration/migrator.ts +392 -244
- package/src/migration/postgresql-schema-reader.ts +52 -47
- package/src/migration/slack-confirm.ts +17 -15
- package/src/migration/types.ts +57 -0
- package/src/naite/naite-reporter.ts +1 -1
- package/src/naite/naite.ts +97 -24
- package/src/runtime-type.ts +34 -0
- package/src/ssr/hmr-opt-out.ts +78 -0
- package/src/ssr/renderer.ts +6 -0
- package/src/storage/base-file.ts +13 -13
- package/src/storage/buffered-file.ts +11 -11
- package/src/storage/s3-driver.ts +4 -1
- package/src/storage/uploaded-file.ts +8 -8
- package/src/stream/__tests__/ws-contracts.test.ts +4 -2
- package/src/stream/__tests__/ws-telemetry.test.ts +1 -1
- package/src/stream/__tests__/ws.test.ts +30 -6
- package/src/stream/sse.ts +12 -11
- package/src/stream/ws-core.ts +1 -1
- package/src/stream/ws-delivery.ts +16 -7
- package/src/stream/ws-local-connection-store.ts +2 -3
- package/src/stream/ws-presence-store.ts +6 -1
- package/src/stream/ws-registry.ts +16 -7
- package/src/stream/ws-telemetry.ts +66 -43
- package/src/stream/ws.ts +73 -39
- package/src/syncer/__tests__/api-parser.websocket-type-ref.test.ts +3 -2
- package/src/syncer/__tests__/http-validator-registry.test.ts +137 -0
- package/src/syncer/api-parser.ts +67 -27
- package/src/syncer/checksum.ts +17 -11
- package/src/syncer/code-generator.ts +25 -17
- package/src/syncer/entity-operations.ts +3 -5
- package/src/syncer/file-copy.ts +46 -0
- package/src/syncer/file-patterns.ts +11 -8
- package/src/syncer/file-tracking.ts +3 -4
- package/src/syncer/filesystem-dependencies.ts +73 -0
- package/src/syncer/module-loader.ts +8 -4
- package/src/syncer/syncer-actions.ts +56 -18
- package/src/syncer/syncer.ts +116 -43
- package/src/syncer/watcher.ts +8 -4
- package/src/tasks/decorator.ts +20 -12
- package/src/tasks/step-wrapper.ts +19 -9
- package/src/tasks/workflow-manager.ts +15 -9
- package/src/template/__tests__/http_validators.template.test.ts +160 -0
- package/src/template/__tests__/services.template.mutation.test.ts +24 -23
- package/src/template/__tests__/services.template.websocket.test.ts +21 -25
- package/src/template/entity-converter.ts +5 -2
- package/src/template/helpers.ts +8 -9
- package/src/template/implementations/generated.template.ts +5 -4
- package/src/template/implementations/generated_http.template.ts +61 -15
- package/src/template/implementations/generated_sso.template.ts +15 -4
- package/src/template/implementations/http_validators.template.ts +175 -0
- package/src/template/implementations/model.template.ts +3 -1
- package/src/template/implementations/sd.template.ts +81 -41
- package/src/template/implementations/services.template.ts +2 -2
- package/src/template/implementations/view_form.template.ts +56 -39
- package/src/template/implementations/view_list.template.ts +31 -23
- package/src/template/template-manager.ts +15 -13
- package/src/template/template-types.ts +13 -5
- package/src/template/template.ts +4 -3
- package/src/template/zod-converter.ts +206 -91
- package/src/testing/__tests__/dev-test-routes.test.ts +85 -25
- package/src/testing/__tests__/dev-vitest-manager.test.ts +15 -15
- package/src/testing/_relation-graph.ts +4 -1
- package/src/testing/bootstrap.ts +12 -5
- package/src/testing/data-explorer.ts +102 -53
- package/src/testing/dev-test-routes.ts +87 -50
- package/src/testing/dev-vitest-manager.ts +88 -32
- package/src/testing/faker-mappings.ts +2 -2
- package/src/testing/filesystem.ts +1 -0
- package/src/testing/fixture-generator.ts +191 -131
- package/src/testing/fixture-loader.ts +5 -1
- package/src/testing/fixture-manager.ts +129 -90
- package/src/testing/global-setup.ts +7 -3
- package/src/testing/index.ts +5 -0
- package/src/types/__tests__/entity-json-schema-search-text.test.ts +9 -3
- package/src/types/types.ts +184 -158
- package/src/ui/ai-api.ts +9 -7
- package/src/ui/ai-client.ts +61 -56
- package/src/ui/api.ts +726 -770
- package/src/ui/async-route.ts +4 -0
- package/src/ui/cdd-service.ts +35 -12
- package/src/ui/migrations-api.ts +381 -0
- package/src/utils/formatter.ts +7 -3
- package/src/utils/fs-utils.ts +5 -5
- package/src/utils/model.ts +4 -5
- package/src/utils/object-utils.ts +32 -22
- package/src/utils/process-utils.ts +4 -1
- package/src/utils/runtime-value.ts +43 -0
- package/src/utils/sql-parser.ts +13 -6
- package/src/utils/type-utils.ts +9 -7
- package/src/utils/utils.ts +19 -21
- package/src/utils/zod-error.ts +4 -2
- package/src/vector/chunking.ts +6 -1
- package/src/vector/embedding.ts +2 -1
- package/src/vector/types.ts +4 -2
- package/tsdown.api.config.ts +5 -2
- package/dist/ui-web/assets/index-CJf8uJYf.js +0 -176
- package/dist/ui-web/assets/index-GMMIVGja.css +0 -1
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import assert from "assert";
|
|
2
|
-
import { mkdir, readdir,
|
|
2
|
+
import { mkdir, readdir, writeFile } from "fs/promises";
|
|
3
3
|
import path from "path";
|
|
4
4
|
|
|
5
5
|
import chalk from "chalk";
|
|
6
6
|
import { type Knex } from "knex";
|
|
7
|
-
import { group,
|
|
7
|
+
import { group, unique } from "radashi";
|
|
8
8
|
|
|
9
9
|
import { Sonamu } from "../api/sonamu";
|
|
10
10
|
import { DB } from "../database/db";
|
|
@@ -18,10 +18,22 @@ import { Naite } from "../naite/naite";
|
|
|
18
18
|
import { type GenMigrationCode, type MigrationSet } from "../types/types";
|
|
19
19
|
import { isLocal, isTest } from "../utils/controller";
|
|
20
20
|
import { exists } from "../utils/fs-utils";
|
|
21
|
+
import { isObjectValue, isStringValue } from "../utils/runtime-value";
|
|
21
22
|
import { generateAlterCode, generateCreateCode } from "./code-generation";
|
|
22
23
|
import { getMigrationSetFromEntity } from "./migration-set";
|
|
23
24
|
import { PostgreSQLSchemaReader } from "./postgresql-schema-reader";
|
|
24
|
-
import {
|
|
25
|
+
import { SlackConfirm } from "./slack-confirm";
|
|
26
|
+
import {
|
|
27
|
+
type ConnString,
|
|
28
|
+
type MigrationAction,
|
|
29
|
+
type MigrationCode,
|
|
30
|
+
type MigrationConnectionMeta,
|
|
31
|
+
type MigrationConnectionStatus,
|
|
32
|
+
type MigrationProgressEvent,
|
|
33
|
+
type MigrationRunOptions,
|
|
34
|
+
type MigrationStatus,
|
|
35
|
+
type MigrationTarget,
|
|
36
|
+
} from "./types";
|
|
25
37
|
|
|
26
38
|
export type MigrationResult = {
|
|
27
39
|
connKey: string;
|
|
@@ -29,10 +41,25 @@ export type MigrationResult = {
|
|
|
29
41
|
applied: string[];
|
|
30
42
|
}[];
|
|
31
43
|
|
|
44
|
+
function padDatePart(number: number, size: number = 2): string {
|
|
45
|
+
return number.toString().padStart(size, "0");
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export class MigrationTargetExecutionError extends Error {
|
|
49
|
+
constructor(
|
|
50
|
+
public readonly connKey: MigrationTarget | "shadow",
|
|
51
|
+
cause: unknown,
|
|
52
|
+
) {
|
|
53
|
+
super(cause instanceof Error ? cause.message : String(cause));
|
|
54
|
+
this.name = "MigrationTargetExecutionError";
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
32
58
|
// 마이그레이션 상태 조회 시 DB 연결 확인 타임아웃(ms).
|
|
33
59
|
// createKnexInstance의 커넥션 획득 타임아웃(30초)에 status/list/currentVersion이
|
|
34
60
|
// 각각 걸리면 CLI/UI가 최대 수십 초 hang한다. 정상 응답은 수백 ms이므로 5초면 충분하다.
|
|
35
61
|
const MIGRATION_CONN_TIMEOUT_MS = 5000;
|
|
62
|
+
const LOCAL_DB_HOSTS = new Set(["localhost", "127.0.0.1", "0.0.0.0", "::1"]);
|
|
36
63
|
|
|
37
64
|
/**
|
|
38
65
|
* 주어진 Promise가 ms 안에 완료되지 않으면 onTimeout 에러로 reject한다.
|
|
@@ -54,24 +81,35 @@ function withTimeout<T>(promise: Promise<T>, ms: number, onTimeout: () => Error)
|
|
|
54
81
|
});
|
|
55
82
|
}
|
|
56
83
|
|
|
84
|
+
/**
|
|
85
|
+
* Note 2026-08-26, 병준
|
|
86
|
+
* 이 클래스에서 deprecated 붙어있는 친구들은 현 CLI를 위한 하위 호환이니,
|
|
87
|
+
* CLI 리팩토링 시점에서 제거해주셔도 무방합니다.
|
|
88
|
+
*/
|
|
57
89
|
export class Migrator {
|
|
58
|
-
private isMissingMigrationTableError(error:
|
|
59
|
-
if (
|
|
90
|
+
private isMissingMigrationTableError<Value>(error: Value): boolean {
|
|
91
|
+
if (!isObjectValue(error) || error === null) {
|
|
60
92
|
return false;
|
|
61
93
|
}
|
|
62
94
|
|
|
63
|
-
const maybePostgresError =
|
|
95
|
+
const maybePostgresError =
|
|
96
|
+
/* SAFETY: Knex와 PostgreSQL 스키마 조회 계약이 이 값의 타입을 보장한다. */ error as {
|
|
97
|
+
code?: unknown;
|
|
98
|
+
message?: unknown;
|
|
99
|
+
};
|
|
64
100
|
return (
|
|
65
101
|
maybePostgresError.code === "42P01" &&
|
|
66
|
-
|
|
102
|
+
isStringValue(maybePostgresError.message) &&
|
|
67
103
|
maybePostgresError.message.includes("knex_migrations")
|
|
68
104
|
);
|
|
69
105
|
}
|
|
70
106
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
107
|
+
/** 현재 실행 환경에서 허용된 마이그레이션 대상 키를 반환합니다. */
|
|
108
|
+
getMigrationTargetKeys(): MigrationTarget[] {
|
|
109
|
+
const connKeys =
|
|
110
|
+
/* SAFETY: Knex와 PostgreSQL 스키마 조회 계약이 이 값의 타입을 보장한다. */ Object.keys(
|
|
111
|
+
Sonamu.dbConfig,
|
|
112
|
+
).filter((key) => !key.endsWith("_readonly")) as (keyof SonamuDBConfig)[];
|
|
75
113
|
|
|
76
114
|
if (isLocal()) {
|
|
77
115
|
return connKeys;
|
|
@@ -84,24 +122,143 @@ export class Migrator {
|
|
|
84
122
|
private async runMigrationsSequentially(
|
|
85
123
|
conns: { connKey: keyof SonamuDBConfig; knex: Knex }[],
|
|
86
124
|
action: "apply" | "rollback",
|
|
125
|
+
options?: MigrationRunOptions,
|
|
87
126
|
): Promise<MigrationResult> {
|
|
88
127
|
const results: MigrationResult = [];
|
|
89
128
|
|
|
90
129
|
for (const { connKey, knex } of conns) {
|
|
91
|
-
const
|
|
92
|
-
|
|
130
|
+
const progress = this.createProgressHooks(action, connKey, options);
|
|
131
|
+
let migrationResult: [number, string[]];
|
|
132
|
+
try {
|
|
133
|
+
migrationResult =
|
|
134
|
+
action === "apply"
|
|
135
|
+
? await knex.migrate.latest(progress)
|
|
136
|
+
: await knex.migrate.rollback(progress);
|
|
137
|
+
} catch (caught) {
|
|
138
|
+
// lifecycle hook 이전의 연결·lock·목록 조회 실패도 실제 대상 DB에 귀속합니다.
|
|
139
|
+
throw new MigrationTargetExecutionError(connKey, caught);
|
|
140
|
+
}
|
|
141
|
+
const [batchNo, applied] = migrationResult;
|
|
93
142
|
|
|
94
143
|
results.push({
|
|
95
144
|
connKey,
|
|
96
145
|
batchNo,
|
|
97
146
|
applied,
|
|
98
147
|
});
|
|
148
|
+
this.emitProgress(options, {
|
|
149
|
+
type: "target-complete",
|
|
150
|
+
action,
|
|
151
|
+
connKey,
|
|
152
|
+
batchNo,
|
|
153
|
+
files: applied,
|
|
154
|
+
});
|
|
99
155
|
}
|
|
100
156
|
|
|
101
157
|
return results;
|
|
102
158
|
}
|
|
103
159
|
|
|
104
|
-
private
|
|
160
|
+
private migrationName<Value>(migration: Value): string {
|
|
161
|
+
if (isStringValue(migration)) {
|
|
162
|
+
return path.basename(migration);
|
|
163
|
+
}
|
|
164
|
+
if (isObjectValue(migration) && migration !== null) {
|
|
165
|
+
const candidate =
|
|
166
|
+
/* SAFETY: Knex와 PostgreSQL 스키마 조회 계약이 이 값의 타입을 보장한다. */ migration as {
|
|
167
|
+
file?: unknown;
|
|
168
|
+
name?: unknown;
|
|
169
|
+
};
|
|
170
|
+
if (isStringValue(candidate.file)) {
|
|
171
|
+
return path.basename(candidate.file);
|
|
172
|
+
}
|
|
173
|
+
if (isStringValue(candidate.name)) {
|
|
174
|
+
return path.basename(candidate.name);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return String(migration);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
private emitProgress(options: MigrationRunOptions | undefined, event: MigrationProgressEvent) {
|
|
181
|
+
try {
|
|
182
|
+
options?.onProgress?.(event);
|
|
183
|
+
} catch (error) {
|
|
184
|
+
// 진행률 소비자의 실패가 이미 시작한 DB 작업과 정리를 방해하면 안 됩니다.
|
|
185
|
+
console.warn("Migration progress observer failed:", error);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
private createProgressHooks(
|
|
190
|
+
action: MigrationAction,
|
|
191
|
+
connKey: MigrationTarget | "shadow",
|
|
192
|
+
options?: MigrationRunOptions,
|
|
193
|
+
): Knex.MigratorConfigWithLifecycleHooks {
|
|
194
|
+
let files: string[] = [];
|
|
195
|
+
const emitFile = <Value>(type: "file-start" | "file-executed", migration: Value) => {
|
|
196
|
+
const file = this.migrationName(migration);
|
|
197
|
+
const index = Math.max(files.indexOf(file), 0);
|
|
198
|
+
this.emitProgress(options, {
|
|
199
|
+
type,
|
|
200
|
+
action,
|
|
201
|
+
connKey,
|
|
202
|
+
file,
|
|
203
|
+
index,
|
|
204
|
+
total: files.length,
|
|
205
|
+
});
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
return {
|
|
209
|
+
beforeAll: async (_knex, migrations) => {
|
|
210
|
+
files = migrations.map((migration) => this.migrationName(migration));
|
|
211
|
+
this.emitProgress(options, { type: "target-start", action, connKey, files });
|
|
212
|
+
},
|
|
213
|
+
beforeEach: async (_knex, migrations) => {
|
|
214
|
+
migrations.forEach((migration) => emitFile("file-start", migration));
|
|
215
|
+
},
|
|
216
|
+
afterEach: async (_knex, migrations) => {
|
|
217
|
+
migrations.forEach((migration) => emitFile("file-executed", migration));
|
|
218
|
+
},
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
private assertMigrationTarget(connKey: MigrationTarget): void {
|
|
223
|
+
if (!this.getMigrationTargetKeys().includes(connKey)) {
|
|
224
|
+
throw new Error(
|
|
225
|
+
`Migration target is not allowed in NODE_ENV=${getSonamuEnvironment()}: ${String(connKey)}`,
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* 마이그레이션 대상 커넥션의 정적 메타데이터를 반환합니다.
|
|
232
|
+
* DB에 연결하지 않으므로 CLI와 Web이 상태 조회 전에 즉시 사용할 수 있습니다.
|
|
233
|
+
*
|
|
234
|
+
* @category 분리형 마이그레이션 API
|
|
235
|
+
*/
|
|
236
|
+
getConnections(): MigrationConnectionMeta[] {
|
|
237
|
+
const slackConfirm = new SlackConfirm();
|
|
238
|
+
return this.getMigrationTargetKeys().map((connKey) => {
|
|
239
|
+
const connection =
|
|
240
|
+
/* SAFETY: Knex와 PostgreSQL 스키마 조회 계약이 이 값의 타입을 보장한다. */ Sonamu.dbConfig[
|
|
241
|
+
connKey
|
|
242
|
+
].connection as Knex.PgConnectionConfig;
|
|
243
|
+
const host = connection.host ?? "localhost";
|
|
244
|
+
return {
|
|
245
|
+
connKey,
|
|
246
|
+
name: String(connKey),
|
|
247
|
+
host,
|
|
248
|
+
port: connection.port ?? 5432,
|
|
249
|
+
database: connection.database ?? "",
|
|
250
|
+
remote: !LOCAL_DB_HOSTS.has(host.toLowerCase()),
|
|
251
|
+
requiresApproval: slackConfirm.isTargetRequiresApproval(connKey),
|
|
252
|
+
};
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* 소스에 존재하는 마이그레이션 파일 목록을 반환합니다.
|
|
258
|
+
*
|
|
259
|
+
* @category 분리형 마이그레이션 API
|
|
260
|
+
*/
|
|
261
|
+
async getMigrationCodes(): Promise<MigrationCode[]> {
|
|
105
262
|
const srcMigrationsDir = path.join(Sonamu.apiRootPath, "src", "migrations"); // 이건 환경에 관계없이 항상 src에서 찾아야 해요.
|
|
106
263
|
|
|
107
264
|
if (!(await exists(srcMigrationsDir))) {
|
|
@@ -123,183 +280,214 @@ export class Migrator {
|
|
|
123
280
|
}
|
|
124
281
|
|
|
125
282
|
/**
|
|
126
|
-
*
|
|
127
|
-
*
|
|
128
|
-
* 필요하다면 적용할 수 있는 코드를 생성까지 해옵니다.
|
|
283
|
+
* 커넥션 하나의 현재 버전과 pending 목록을 독립적으로 조회합니다.
|
|
284
|
+
* 여러 대상은 호출자가 병렬로 조립할 수 있습니다.
|
|
129
285
|
*
|
|
130
|
-
*
|
|
131
|
-
*
|
|
132
|
-
* @returns
|
|
286
|
+
* @category 분리형 마이그레이션 API
|
|
133
287
|
*/
|
|
134
|
-
async
|
|
288
|
+
async getConnectionStatus(connKey: MigrationTarget): Promise<MigrationConnectionStatus> {
|
|
289
|
+
this.assertMigrationTarget(connKey);
|
|
290
|
+
const startedAt = performance.now();
|
|
135
291
|
const codes = await this.getMigrationCodes();
|
|
136
|
-
|
|
292
|
+
const knexOptions = Sonamu.dbConfig[connKey];
|
|
293
|
+
const connection =
|
|
294
|
+
/* SAFETY: Knex와 PostgreSQL 스키마 조회 계약이 이 값의 타입을 보장한다. */ knexOptions.connection as Knex.PgConnectionConfig;
|
|
295
|
+
// SAFETY: Knex가 제공한 PostgreSQL 연결 구성을 복사해 timeout 필드만 덮어쓴다.
|
|
296
|
+
const tConn = createKnexInstance({
|
|
297
|
+
...knexOptions,
|
|
298
|
+
connection: {
|
|
299
|
+
...connection,
|
|
300
|
+
connectionTimeoutMillis: MIGRATION_CONN_TIMEOUT_MS,
|
|
301
|
+
},
|
|
302
|
+
pool: {
|
|
303
|
+
...knexOptions.pool,
|
|
304
|
+
min: 0,
|
|
305
|
+
acquireTimeoutMillis: MIGRATION_CONN_TIMEOUT_MS,
|
|
306
|
+
createTimeoutMillis: MIGRATION_CONN_TIMEOUT_MS,
|
|
307
|
+
propagateCreateError: true,
|
|
308
|
+
},
|
|
309
|
+
});
|
|
137
310
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
const knexOptions = Sonamu.dbConfig[connKey];
|
|
145
|
-
const connection = knexOptions.connection as Knex.PgConnectionConfig;
|
|
146
|
-
// 상태 조회용 커넥션은 fail-fast로 만든다: 불통 DB에서 커넥션 획득을 무한정
|
|
147
|
-
// 재시도하며 CLI 프로세스가 종료되지 않는 것을 막는다.
|
|
148
|
-
// - connectionTimeoutMillis: 소켓 연결 자체를 5초 안에 포기 → 소켓이 정리되어 프로세스가 종료됨
|
|
149
|
-
// - propagateCreateError: 첫 연결 실패를 즉시 전파(풀 재시도 없이 바로 실패)
|
|
150
|
-
const tConn = createKnexInstance({
|
|
151
|
-
...knexOptions,
|
|
152
|
-
connection: {
|
|
153
|
-
...(connection as Record<string, unknown>),
|
|
154
|
-
connectionTimeoutMillis: MIGRATION_CONN_TIMEOUT_MS,
|
|
155
|
-
},
|
|
156
|
-
pool: {
|
|
157
|
-
...knexOptions.pool,
|
|
158
|
-
min: 0,
|
|
159
|
-
acquireTimeoutMillis: MIGRATION_CONN_TIMEOUT_MS,
|
|
160
|
-
createTimeoutMillis: MIGRATION_CONN_TIMEOUT_MS,
|
|
161
|
-
propagateCreateError: true,
|
|
162
|
-
},
|
|
163
|
-
});
|
|
164
|
-
const connString =
|
|
165
|
-
`pg://${connection.user ?? ""}@${connection.host}:${connection.port}/${connection.database}` as ConnString;
|
|
166
|
-
|
|
167
|
-
try {
|
|
168
|
-
// DB 연결 확인: 불통 DB에서 status/list/currentVersion이 커넥션 획득
|
|
169
|
-
// 타임아웃까지 hang하는 것을 막기 위해, 먼저 5초 안에 응답하는지 확인한다.
|
|
170
|
-
try {
|
|
171
|
-
await withTimeout(
|
|
172
|
-
tConn.raw("select 1"),
|
|
173
|
-
MIGRATION_CONN_TIMEOUT_MS,
|
|
174
|
-
() =>
|
|
175
|
-
new Error(
|
|
176
|
-
`DB 연결 시간 초과 (${MIGRATION_CONN_TIMEOUT_MS}ms) — ${connection.host}:${connection.port}/${connection.database}`,
|
|
177
|
-
),
|
|
178
|
-
);
|
|
179
|
-
} catch (err) {
|
|
180
|
-
console.warn(
|
|
181
|
-
chalk.yellow(
|
|
182
|
-
`${connKey}의 마이그레이션 상태를 가져오는 데에 실패하였습니다. 데이터베이스에 연결할 수 없습니다.\n시도한 연결 설정:\n${JSON.stringify(knexOptions.connection, null, 2)}\n발생한 에러:\n${err}\n`,
|
|
183
|
-
),
|
|
184
|
-
);
|
|
185
|
-
migrationStatusError = err instanceof Error ? err.message : String(err);
|
|
186
|
-
return {
|
|
187
|
-
name: connKey,
|
|
188
|
-
connKey,
|
|
189
|
-
connString,
|
|
190
|
-
currentVersion: "error" as const,
|
|
191
|
-
status: "error" as const,
|
|
192
|
-
pending: [] as string[],
|
|
193
|
-
};
|
|
194
|
-
}
|
|
311
|
+
try {
|
|
312
|
+
await withTimeout(tConn.raw("select 1"), MIGRATION_CONN_TIMEOUT_MS, () => {
|
|
313
|
+
return new Error(
|
|
314
|
+
`DB 연결 시간 초과 (${MIGRATION_CONN_TIMEOUT_MS}ms) — ${connection.host}:${connection.port}/${connection.database}`,
|
|
315
|
+
);
|
|
316
|
+
});
|
|
195
317
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
if (this.isMissingMigrationTableError(err)) {
|
|
201
|
-
return codes.length;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
console.warn(
|
|
205
|
-
chalk.yellow(
|
|
206
|
-
`${connKey}의 마이그레이션 상태를 가져오는 데에 실패하였습니다. 데이터베이스가 올바르게 구성되지 않은 것 같습니다. 확인하시고 다시 시도해주세요.\n시도한 연결 설정:\n${JSON.stringify(knexOptions.connection, null, 2)}\n발생한 에러:\n${err}\n`,
|
|
207
|
-
),
|
|
208
|
-
);
|
|
209
|
-
migrationStatusError = err instanceof Error ? err.message : String(err);
|
|
210
|
-
return "error";
|
|
211
|
-
}
|
|
212
|
-
})();
|
|
213
|
-
const pending: string[] = await (async () => {
|
|
214
|
-
try {
|
|
215
|
-
const [, fdList] = await tConn.migrate.list();
|
|
216
|
-
return fdList.map((fd: { file: string }) => fd.file.replace(".ts", ""));
|
|
217
|
-
} catch (err) {
|
|
218
|
-
if (this.isMissingMigrationTableError(err)) {
|
|
219
|
-
return codes.map((code) => code.name);
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
migrationStatusError = err instanceof Error ? err.message : String(err);
|
|
223
|
-
return [];
|
|
224
|
-
}
|
|
225
|
-
})();
|
|
226
|
-
const currentVersion: string | "error" = await (async () => {
|
|
227
|
-
try {
|
|
228
|
-
return await tConn.migrate.currentVersion();
|
|
229
|
-
} catch (_err) {
|
|
230
|
-
if (this.isMissingMigrationTableError(_err)) {
|
|
231
|
-
return "none";
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
migrationStatusError = _err instanceof Error ? _err.message : String(_err);
|
|
235
|
-
return "error";
|
|
236
|
-
}
|
|
237
|
-
})();
|
|
238
|
-
Naite.t("migrator:getStatus:status", status);
|
|
239
|
-
|
|
240
|
-
return {
|
|
241
|
-
name: connKey,
|
|
242
|
-
connKey,
|
|
243
|
-
connString,
|
|
244
|
-
currentVersion,
|
|
245
|
-
status: status,
|
|
246
|
-
pending,
|
|
247
|
-
};
|
|
248
|
-
} finally {
|
|
249
|
-
// 불통 DB의 커넥션 정리가 커넥션 획득 타임아웃까지 hang하지 않도록 감싼다.
|
|
250
|
-
await withTimeout(
|
|
251
|
-
tConn.destroy(),
|
|
252
|
-
MIGRATION_CONN_TIMEOUT_MS,
|
|
253
|
-
() => new Error("connection destroy timeout"),
|
|
254
|
-
).catch(() => {});
|
|
318
|
+
let error: string | undefined;
|
|
319
|
+
const status: number | "error" = await tConn.migrate.status().catch((caught) => {
|
|
320
|
+
if (this.isMissingMigrationTableError(caught)) {
|
|
321
|
+
return codes.length;
|
|
255
322
|
}
|
|
256
|
-
|
|
257
|
-
|
|
323
|
+
error = caught instanceof Error ? caught.message : String(caught);
|
|
324
|
+
return "error" as const;
|
|
325
|
+
});
|
|
326
|
+
const pending: string[] = await tConn.migrate.list().then(
|
|
327
|
+
([, files]: [unknown[], { file: string }[]]) =>
|
|
328
|
+
files.map(({ file }) => file.replace(/\.ts$/, "")),
|
|
329
|
+
(cause: unknown) => {
|
|
330
|
+
if (this.isMissingMigrationTableError(cause)) {
|
|
331
|
+
return codes.map(({ name }) => name);
|
|
332
|
+
}
|
|
333
|
+
error ??= cause instanceof Error ? cause.message : String(cause);
|
|
334
|
+
return [];
|
|
335
|
+
},
|
|
336
|
+
);
|
|
337
|
+
const currentVersion: string | "none" | "error" = await tConn.migrate
|
|
338
|
+
.currentVersion()
|
|
339
|
+
.catch((cause: unknown) => {
|
|
340
|
+
if (this.isMissingMigrationTableError(cause)) {
|
|
341
|
+
return "none" as const;
|
|
342
|
+
}
|
|
343
|
+
error ??= cause instanceof Error ? cause.message : String(cause);
|
|
344
|
+
return "error" as const;
|
|
345
|
+
});
|
|
346
|
+
Naite.t("migrator:getStatus:status", status);
|
|
347
|
+
const connectionStatus: MigrationConnectionStatus = {
|
|
348
|
+
connKey,
|
|
349
|
+
currentVersion,
|
|
350
|
+
status,
|
|
351
|
+
pending,
|
|
352
|
+
latencyMs: performance.now() - startedAt,
|
|
353
|
+
};
|
|
354
|
+
if (error !== undefined) connectionStatus.error = error;
|
|
355
|
+
return connectionStatus;
|
|
356
|
+
} catch (caught) {
|
|
357
|
+
const error = caught instanceof Error ? caught.message : String(caught);
|
|
358
|
+
console.warn(
|
|
359
|
+
chalk.yellow(
|
|
360
|
+
`${String(connKey)}의 마이그레이션 상태를 가져오는 데에 실패하였습니다.\n${error}`,
|
|
361
|
+
),
|
|
362
|
+
);
|
|
363
|
+
return {
|
|
364
|
+
connKey,
|
|
365
|
+
currentVersion: "error",
|
|
366
|
+
status: "error",
|
|
367
|
+
pending: [],
|
|
368
|
+
latencyMs: performance.now() - startedAt,
|
|
369
|
+
error,
|
|
370
|
+
};
|
|
371
|
+
} finally {
|
|
372
|
+
await withTimeout(tConn.destroy(), MIGRATION_CONN_TIMEOUT_MS, () => {
|
|
373
|
+
return new Error("connection destroy timeout");
|
|
374
|
+
}).catch(() => {});
|
|
375
|
+
}
|
|
376
|
+
}
|
|
258
377
|
|
|
259
|
-
|
|
378
|
+
/**
|
|
379
|
+
* 최신 상태인 기준 DB와 엔티티 정의를 비교해 생성 예정 코드를 반환합니다.
|
|
380
|
+
*
|
|
381
|
+
* @category 분리형 마이그레이션 API
|
|
382
|
+
*/
|
|
383
|
+
async getPreparedCodes(compareConnKey: MigrationTarget): Promise<GenMigrationCode[]> {
|
|
384
|
+
const status = await this.getConnectionStatus(compareConnKey);
|
|
385
|
+
if (status.status !== 0 || status.error !== undefined) {
|
|
386
|
+
throw new Error(
|
|
387
|
+
`Migration comparison requires an up-to-date database: ${String(compareConnKey)}`,
|
|
388
|
+
);
|
|
389
|
+
}
|
|
260
390
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
if (status0conn === undefined) {
|
|
264
|
-
console.warn(
|
|
265
|
-
chalk.yellow(
|
|
266
|
-
`While trying to prepare migration codes, we found that there is no database to compare migrations. We need at least one database where every migration is applied(status === 0). You might want to apply your existing migrations to one of the databases.`,
|
|
267
|
-
),
|
|
268
|
-
);
|
|
269
|
-
return [];
|
|
270
|
-
}
|
|
391
|
+
return this.compareWithConnection(compareConnKey);
|
|
392
|
+
}
|
|
271
393
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
}
|
|
394
|
+
private async compareWithConnection(compareConnKey: MigrationTarget) {
|
|
395
|
+
const compareDB = createKnexInstance(Sonamu.dbConfig[compareConnKey]);
|
|
396
|
+
try {
|
|
397
|
+
return await this.compareMigrations(compareDB);
|
|
398
|
+
} finally {
|
|
399
|
+
await compareDB.destroy();
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* @deprecated 신규 UI에서는 커넥션별 조회 API를 사용합니다.
|
|
405
|
+
* 기존 CLI와 스크립트 호환을 위해 전체 상태를 조립합니다.
|
|
406
|
+
* @category 호환용 마이그레이션 API
|
|
407
|
+
*/
|
|
408
|
+
async getStatus(): Promise<MigrationStatus> {
|
|
409
|
+
const [codes, connections] = await Promise.all([
|
|
410
|
+
this.getMigrationCodes(),
|
|
411
|
+
Promise.resolve(this.getConnections()),
|
|
412
|
+
]);
|
|
413
|
+
Naite.t("migrator:getStatus:codes", codes);
|
|
279
414
|
|
|
415
|
+
const connectionStatuses = await Promise.all(
|
|
416
|
+
connections.map(({ connKey }) => this.getConnectionStatus(connKey)),
|
|
417
|
+
);
|
|
418
|
+
const conns = connections.map((connection, index) => {
|
|
419
|
+
const status = connectionStatuses[index];
|
|
420
|
+
assert(status !== undefined);
|
|
421
|
+
const configured =
|
|
422
|
+
/* SAFETY: Knex와 PostgreSQL 스키마 조회 계약이 이 값의 타입을 보장한다. */ Sonamu.dbConfig[
|
|
423
|
+
connection.connKey
|
|
424
|
+
].connection as Knex.PgConnectionConfig;
|
|
425
|
+
return {
|
|
426
|
+
name: connection.name,
|
|
427
|
+
connKey: connection.connKey,
|
|
428
|
+
connString:
|
|
429
|
+
/* SAFETY: Knex와 PostgreSQL 스키마 조회 계약이 이 값의 타입을 보장한다. */ `pg://${configured.user ?? ""}@${configured.host}:${configured.port}/${configured.database}` as ConnString,
|
|
430
|
+
currentVersion: status.currentVersion,
|
|
431
|
+
status: status.status,
|
|
432
|
+
pending: status.pending,
|
|
433
|
+
};
|
|
434
|
+
});
|
|
435
|
+
Naite.t("migrator:getStatus:conns", conns);
|
|
436
|
+
|
|
437
|
+
const compareConn = connectionStatuses.find(
|
|
438
|
+
({ status, error }) => status === 0 && error === undefined,
|
|
439
|
+
);
|
|
440
|
+
const preparedCodes =
|
|
441
|
+
compareConn === undefined ? [] : await this.compareWithConnection(compareConn.connKey);
|
|
280
442
|
Naite.t("migrator:getStatus:preparedCodes", preparedCodes);
|
|
281
443
|
|
|
282
444
|
return {
|
|
283
|
-
conns
|
|
445
|
+
conns,
|
|
284
446
|
codes,
|
|
285
447
|
preparedCodes,
|
|
286
|
-
error:
|
|
448
|
+
error: connectionStatuses.find(({ error }) => error !== undefined)?.error,
|
|
287
449
|
};
|
|
288
450
|
}
|
|
289
451
|
|
|
290
452
|
/**
|
|
291
|
-
* 마이그레이션을
|
|
292
|
-
*
|
|
453
|
+
* 대상 DB에 pending 마이그레이션을 적용합니다.
|
|
454
|
+
* CLI와 Web은 같은 메서드를 사용하며, Web은 `onProgress`를 stream으로 변환합니다.
|
|
293
455
|
*
|
|
294
|
-
*
|
|
456
|
+
* @category 분리형 마이그레이션 API
|
|
457
|
+
*/
|
|
458
|
+
async apply(targets: MigrationTarget[], options?: MigrationRunOptions): Promise<MigrationResult> {
|
|
459
|
+
return this.runActionWithProgress("apply", targets, options);
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
/**
|
|
463
|
+
* 대상 DB에서 가장 최근 migration batch를 롤백합니다.
|
|
464
|
+
* CLI와 Web은 같은 메서드를 사용하며, Web은 `onProgress`를 stream으로 변환합니다.
|
|
295
465
|
*
|
|
296
|
-
* @
|
|
297
|
-
|
|
298
|
-
|
|
466
|
+
* @category 분리형 마이그레이션 API
|
|
467
|
+
*/
|
|
468
|
+
async rollback(
|
|
469
|
+
targets: MigrationTarget[],
|
|
470
|
+
options?: MigrationRunOptions,
|
|
471
|
+
): Promise<MigrationResult> {
|
|
472
|
+
return this.runActionWithProgress("rollback", targets, options);
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
/**
|
|
476
|
+
* @deprecated action 문자열 대신 {@link apply} 또는 {@link rollback}을 직접 사용합니다.
|
|
477
|
+
* 기존 외부 호출의 하위 호환만을 위해 유지합니다.
|
|
478
|
+
* @category 호환용 마이그레이션 API
|
|
299
479
|
*/
|
|
300
480
|
async runAction(
|
|
301
481
|
action: "apply" | "rollback",
|
|
302
482
|
targets: (keyof SonamuDBConfig)[],
|
|
483
|
+
): Promise<MigrationResult> {
|
|
484
|
+
return action === "apply" ? this.apply(targets) : this.rollback(targets);
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
private async runActionWithProgress(
|
|
488
|
+
action: "apply" | "rollback",
|
|
489
|
+
targets: MigrationTarget[],
|
|
490
|
+
options?: MigrationRunOptions,
|
|
303
491
|
): Promise<MigrationResult> {
|
|
304
492
|
Naite.t("migrator:runAction:action", action);
|
|
305
493
|
Naite.t("migrator:runAction:targets", targets);
|
|
@@ -320,10 +508,12 @@ export class Migrator {
|
|
|
320
508
|
options: Sonamu.dbConfig[target],
|
|
321
509
|
}))
|
|
322
510
|
.filter((c) => c.options !== undefined),
|
|
323
|
-
({ options }) =>
|
|
324
|
-
`${(
|
|
325
|
-
|
|
326
|
-
|
|
511
|
+
({ options: dbOptions }) =>
|
|
512
|
+
`${/* SAFETY: Knex와 PostgreSQL 스키마 조회 계약이 이 값의 타입을 보장한다. */ (dbOptions.connection as Knex.PgConnectionConfig).host}:${
|
|
513
|
+
/* SAFETY: Knex와 PostgreSQL 스키마 조회 계약이 이 값의 타입을 보장한다. */ (
|
|
514
|
+
dbOptions.connection as Knex.PgConnectionConfig
|
|
515
|
+
).port ?? 5432
|
|
516
|
+
}/${/* SAFETY: Knex와 PostgreSQL 스키마 조회 계약이 이 값의 타입을 보장한다. */ (dbOptions.connection as Knex.PgConnectionConfig).database}`,
|
|
327
517
|
);
|
|
328
518
|
|
|
329
519
|
// get connections
|
|
@@ -335,15 +525,7 @@ export class Migrator {
|
|
|
335
525
|
);
|
|
336
526
|
|
|
337
527
|
try {
|
|
338
|
-
|
|
339
|
-
const result = await (async () => {
|
|
340
|
-
switch (action) {
|
|
341
|
-
case "apply":
|
|
342
|
-
return this.runMigrationsSequentially(conns, "apply");
|
|
343
|
-
case "rollback":
|
|
344
|
-
return this.runMigrationsSequentially(conns, "rollback");
|
|
345
|
-
}
|
|
346
|
-
})();
|
|
528
|
+
const result = await this.runMigrationsSequentially(conns, action, options);
|
|
347
529
|
|
|
348
530
|
Naite.t("migrator:runAction:result", result);
|
|
349
531
|
|
|
@@ -357,65 +539,15 @@ export class Migrator {
|
|
|
357
539
|
}
|
|
358
540
|
}
|
|
359
541
|
|
|
360
|
-
/**
|
|
361
|
-
* 삭제 가능한 마이그레이션 코드 파일을 검증합니다.
|
|
362
|
-
*
|
|
363
|
-
* @param conns 마이그레이션 상태 배열
|
|
364
|
-
* @param codeNames 삭제할 마이그레이션 코드 파일 이름 배열
|
|
365
|
-
* @returns 삭제 가능 여부 및 적용된 마이그레이션 코드 파일 이름
|
|
366
|
-
*/
|
|
367
|
-
validateDeletable(conns: MigrationStatus["conns"], codeNames: string[]) {
|
|
368
|
-
const appliedCodes = codeNames.filter((codeName) =>
|
|
369
|
-
conns.some((conn) => !conn.pending.includes(codeName)),
|
|
370
|
-
);
|
|
371
|
-
|
|
372
|
-
return {
|
|
373
|
-
canDelete: appliedCodes.length === 0,
|
|
374
|
-
appliedCodes,
|
|
375
|
-
};
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
/**
|
|
379
|
-
* 마이그레이션 코드 파일을 삭제합니다.
|
|
380
|
-
*
|
|
381
|
-
* Sonamu UI에서 사용됩니다.
|
|
382
|
-
*
|
|
383
|
-
* @param codeNames 삭제할 마이그레이션 코드 파일 이름 배열
|
|
384
|
-
* @returns 삭제된 마이그레이션 코드 파일 개수
|
|
385
|
-
*/
|
|
386
|
-
async delCodes(codeNames: string[]): Promise<number> {
|
|
387
|
-
const { conns } = await this.getStatus();
|
|
388
|
-
const { canDelete, appliedCodes } = this.validateDeletable(conns, codeNames);
|
|
389
|
-
if (!canDelete) {
|
|
390
|
-
throw new Error(
|
|
391
|
-
`You cannot delete a migration file if there is already applied. Applied codes: ${appliedCodes.join(", ")}`,
|
|
392
|
-
);
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
return sum(
|
|
396
|
-
await Promise.all(
|
|
397
|
-
codeNames.map(async (codeName) => {
|
|
398
|
-
const filePath = `${Sonamu.apiRootPath}/src/migrations/${codeName}.ts`;
|
|
399
|
-
if (await exists(filePath)) {
|
|
400
|
-
await unlink(filePath);
|
|
401
|
-
return 1;
|
|
402
|
-
}
|
|
403
|
-
return 0;
|
|
404
|
-
}),
|
|
405
|
-
),
|
|
406
|
-
);
|
|
407
|
-
}
|
|
408
|
-
|
|
409
542
|
private genDateTag(index: number, baseDate: Date = new Date()): string {
|
|
410
543
|
const date = new Date(baseDate.getTime() + index * 1000);
|
|
411
|
-
const pad = (num: number, size: number = 2) => num.toString().padStart(size, "0");
|
|
412
544
|
return (
|
|
413
545
|
date.getFullYear().toString() +
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
546
|
+
padDatePart(date.getMonth() + 1) +
|
|
547
|
+
padDatePart(date.getDate()) +
|
|
548
|
+
padDatePart(date.getHours()) +
|
|
549
|
+
padDatePart(date.getMinutes()) +
|
|
550
|
+
padDatePart(date.getSeconds())
|
|
419
551
|
);
|
|
420
552
|
}
|
|
421
553
|
|
|
@@ -426,8 +558,11 @@ export class Migrator {
|
|
|
426
558
|
*
|
|
427
559
|
* @returns 생성된 마이그레이션 코드 파일 개수
|
|
428
560
|
*/
|
|
429
|
-
async generatePreparedCodes(): Promise<number> {
|
|
430
|
-
const
|
|
561
|
+
async generatePreparedCodes(compareConnKey?: MigrationTarget): Promise<number> {
|
|
562
|
+
const preparedCodes =
|
|
563
|
+
compareConnKey === undefined
|
|
564
|
+
? (await this.getStatus()).preparedCodes
|
|
565
|
+
: await this.getPreparedCodes(compareConnKey);
|
|
431
566
|
Naite.t("migrator:generatePreparedCodes:preparedCodes", preparedCodes);
|
|
432
567
|
if (preparedCodes.length === 0) {
|
|
433
568
|
console.log(chalk.green("\n현재 모두 싱크된 상태입니다."));
|
|
@@ -519,14 +654,15 @@ export class Migrator {
|
|
|
519
654
|
}
|
|
520
655
|
|
|
521
656
|
/**
|
|
522
|
-
* Shadow DB
|
|
657
|
+
* test DB의 snapshot으로 임시 Shadow DB를 만들고 전체 마이그레이션을 검증합니다.
|
|
658
|
+
* CLI와 Web은 `onProgress` 유무만 달리해 같은 실행 의미를 공유할 수 있습니다.
|
|
523
659
|
*
|
|
524
|
-
*
|
|
525
|
-
*
|
|
526
|
-
* @returns Shadow DB 테스트 결과
|
|
660
|
+
* @category 분리형 마이그레이션 API
|
|
527
661
|
*/
|
|
528
|
-
async runShadowTest(): Promise<MigrationResult> {
|
|
529
|
-
const baseTestConn =
|
|
662
|
+
async runShadowTest(options?: MigrationRunOptions): Promise<MigrationResult> {
|
|
663
|
+
const baseTestConn =
|
|
664
|
+
/* SAFETY: Knex와 PostgreSQL 스키마 조회 계약이 이 값의 타입을 보장한다. */ Sonamu.dbConfig
|
|
665
|
+
.test.connection as Knex.PgConnectionConfig;
|
|
530
666
|
const workerId = process.env.SONAMU_WORKER_DB === "true" ? process.env.VITEST_POOL_ID : null;
|
|
531
667
|
const templateDatabase =
|
|
532
668
|
workerId !== null ? `${baseTestConn.database}_${workerId ?? "1"}` : baseTestConn.database;
|
|
@@ -570,13 +706,22 @@ export class Migrator {
|
|
|
570
706
|
|
|
571
707
|
// shadow DB 테스트 진행
|
|
572
708
|
try {
|
|
573
|
-
const
|
|
709
|
+
const progress = this.createProgressHooks("shadow", "shadow", options);
|
|
710
|
+
const [batchNo, applied] = await sdb.migrate.latest(progress);
|
|
574
711
|
!isTest() &&
|
|
575
712
|
console.log(chalk.green("Shadow DB 테스트에 성공했습니다!"), {
|
|
576
713
|
batchNo,
|
|
577
714
|
applied,
|
|
578
715
|
});
|
|
579
716
|
|
|
717
|
+
this.emitProgress(options, {
|
|
718
|
+
type: "target-complete",
|
|
719
|
+
action: "shadow",
|
|
720
|
+
connKey: "shadow",
|
|
721
|
+
batchNo,
|
|
722
|
+
files: applied,
|
|
723
|
+
});
|
|
724
|
+
|
|
580
725
|
return [
|
|
581
726
|
{
|
|
582
727
|
connKey: "shadow",
|
|
@@ -586,7 +731,10 @@ export class Migrator {
|
|
|
586
731
|
];
|
|
587
732
|
} catch (e) {
|
|
588
733
|
console.error(e);
|
|
589
|
-
throw new
|
|
734
|
+
throw new MigrationTargetExecutionError(
|
|
735
|
+
"shadow",
|
|
736
|
+
new ServiceUnavailableException(SD("sonamu.error.shadowDbTestFailed")),
|
|
737
|
+
);
|
|
590
738
|
} finally {
|
|
591
739
|
await sdb.destroy();
|
|
592
740
|
}
|