@endora-commerce/platform 0.100.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +58 -0
- package/dist/cli/dispatch.d.ts +146 -0
- package/dist/cli/dispatch.d.ts.map +1 -0
- package/dist/cli/dispatch.js +259 -0
- package/dist/cli/dispatch.js.map +1 -0
- package/dist/cli/index.d.ts +58 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +58 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/module-commands.d.ts +118 -0
- package/dist/cli/module-commands.d.ts.map +1 -0
- package/dist/cli/module-commands.js +120 -0
- package/dist/cli/module-commands.js.map +1 -0
- package/dist/commands/actor.d.ts +19 -0
- package/dist/commands/actor.d.ts.map +1 -0
- package/dist/commands/actor.js +45 -0
- package/dist/commands/actor.js.map +1 -0
- package/dist/commands/audit-from-context.d.ts +24 -0
- package/dist/commands/audit-from-context.d.ts.map +1 -0
- package/dist/commands/audit-from-context.js +28 -0
- package/dist/commands/audit-from-context.js.map +1 -0
- package/dist/commands/command-bus.d.ts +40 -0
- package/dist/commands/command-bus.d.ts.map +1 -0
- package/dist/commands/command-bus.js +46 -0
- package/dist/commands/command-bus.js.map +1 -0
- package/dist/commands/command.d.ts +76 -0
- package/dist/commands/command.d.ts.map +1 -0
- package/dist/commands/command.js +2 -0
- package/dist/commands/command.js.map +1 -0
- package/dist/commands/index.d.ts +65 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/index.js +57 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/commands/reversible.d.ts +66 -0
- package/dist/commands/reversible.d.ts.map +1 -0
- package/dist/commands/reversible.js +41 -0
- package/dist/commands/reversible.js.map +1 -0
- package/dist/composition/compose-app.d.ts +305 -0
- package/dist/composition/compose-app.d.ts.map +1 -0
- package/dist/composition/compose-app.js +939 -0
- package/dist/composition/compose-app.js.map +1 -0
- package/dist/composition/index.d.ts +92 -0
- package/dist/composition/index.d.ts.map +1 -0
- package/dist/composition/index.js +175 -0
- package/dist/composition/index.js.map +1 -0
- package/dist/composition/newsletter-token-secret.d.ts +19 -0
- package/dist/composition/newsletter-token-secret.d.ts.map +1 -0
- package/dist/composition/newsletter-token-secret.js +24 -0
- package/dist/composition/newsletter-token-secret.js.map +1 -0
- package/dist/composition/process-runs-workers.d.ts +17 -0
- package/dist/composition/process-runs-workers.d.ts.map +1 -0
- package/dist/composition/process-runs-workers.js +19 -0
- package/dist/composition/process-runs-workers.js.map +1 -0
- package/dist/db/baseline-migration-inventory.d.ts +15 -0
- package/dist/db/baseline-migration-inventory.d.ts.map +1 -0
- package/dist/db/baseline-migration-inventory.js +115 -0
- package/dist/db/baseline-migration-inventory.js.map +1 -0
- package/dist/db/configured-entities.d.ts +74 -0
- package/dist/db/configured-entities.d.ts.map +1 -0
- package/dist/db/configured-entities.js +20 -0
- package/dist/db/configured-entities.js.map +1 -0
- package/dist/db/configured-migrations.d.ts +199 -0
- package/dist/db/configured-migrations.d.ts.map +1 -0
- package/dist/db/configured-migrations.js +181 -0
- package/dist/db/configured-migrations.js.map +1 -0
- package/dist/db/index.d.ts +54 -0
- package/dist/db/index.d.ts.map +1 -0
- package/dist/db/index.js +54 -0
- package/dist/db/index.js.map +1 -0
- package/dist/db/migrate.d.ts +21 -0
- package/dist/db/migrate.d.ts.map +1 -0
- package/dist/db/migrate.js +48 -0
- package/dist/db/migrate.js.map +1 -0
- package/dist/db/migration-order.d.ts +159 -0
- package/dist/db/migration-order.d.ts.map +1 -0
- package/dist/db/migration-order.js +273 -0
- package/dist/db/migration-order.js.map +1 -0
- package/dist/db/mikro-orm.config.d.ts +51 -0
- package/dist/db/mikro-orm.config.d.ts.map +1 -0
- package/dist/db/mikro-orm.config.js +64 -0
- package/dist/db/mikro-orm.config.js.map +1 -0
- package/dist/db/orm.d.ts +40 -0
- package/dist/db/orm.d.ts.map +1 -0
- package/dist/db/orm.js +25 -0
- package/dist/db/orm.js.map +1 -0
- package/dist/db/platform-schema.d.ts +102 -0
- package/dist/db/platform-schema.d.ts.map +1 -0
- package/dist/db/platform-schema.js +79 -0
- package/dist/db/platform-schema.js.map +1 -0
- package/dist/db/pluralizing-naming-strategy.d.ts +16 -0
- package/dist/db/pluralizing-naming-strategy.d.ts.map +1 -0
- package/dist/db/pluralizing-naming-strategy.js +66 -0
- package/dist/db/pluralizing-naming-strategy.js.map +1 -0
- package/dist/demo/guard.d.ts +101 -0
- package/dist/demo/guard.d.ts.map +1 -0
- package/dist/demo/guard.js +168 -0
- package/dist/demo/guard.js.map +1 -0
- package/dist/demo/host-command.d.ts +133 -0
- package/dist/demo/host-command.d.ts.map +1 -0
- package/dist/demo/host-command.js +113 -0
- package/dist/demo/host-command.js.map +1 -0
- package/dist/demo/index.d.ts +82 -0
- package/dist/demo/index.d.ts.map +1 -0
- package/dist/demo/index.js +82 -0
- package/dist/demo/index.js.map +1 -0
- package/dist/demo/packages.d.ts +67 -0
- package/dist/demo/packages.d.ts.map +1 -0
- package/dist/demo/packages.js +132 -0
- package/dist/demo/packages.js.map +1 -0
- package/dist/demo/plan.d.ts +111 -0
- package/dist/demo/plan.d.ts.map +1 -0
- package/dist/demo/plan.js +74 -0
- package/dist/demo/plan.js.map +1 -0
- package/dist/demo/report.d.ts +17 -0
- package/dist/demo/report.d.ts.map +1 -0
- package/dist/demo/report.js +85 -0
- package/dist/demo/report.js.map +1 -0
- package/dist/demo/runner.d.ts +207 -0
- package/dist/demo/runner.d.ts.map +1 -0
- package/dist/demo/runner.js +169 -0
- package/dist/demo/runner.js.map +1 -0
- package/dist/demo/scope.d.ts +44 -0
- package/dist/demo/scope.d.ts.map +1 -0
- package/dist/demo/scope.js +44 -0
- package/dist/demo/scope.js.map +1 -0
- package/dist/env/index.d.ts +68 -0
- package/dist/env/index.d.ts.map +1 -0
- package/dist/env/index.js +427 -0
- package/dist/env/index.js.map +1 -0
- package/dist/events/bus.d.ts +29 -0
- package/dist/events/bus.d.ts.map +1 -0
- package/dist/events/bus.js +67 -0
- package/dist/events/bus.js.map +1 -0
- package/dist/events/index.d.ts +19 -0
- package/dist/events/index.d.ts.map +1 -0
- package/dist/events/index.js +19 -0
- package/dist/events/index.js.map +1 -0
- package/dist/http/cursor.d.ts +10 -0
- package/dist/http/cursor.d.ts.map +1 -0
- package/dist/http/cursor.js +20 -0
- package/dist/http/cursor.js.map +1 -0
- package/dist/http/error-envelope.d.ts +127 -0
- package/dist/http/error-envelope.d.ts.map +1 -0
- package/dist/http/error-envelope.js +402 -0
- package/dist/http/error-envelope.js.map +1 -0
- package/dist/http/health.d.ts +88 -0
- package/dist/http/health.d.ts.map +1 -0
- package/dist/http/health.js +77 -0
- package/dist/http/health.js.map +1 -0
- package/dist/http/index.d.ts +50 -0
- package/dist/http/index.d.ts.map +1 -0
- package/dist/http/index.js +50 -0
- package/dist/http/index.js.map +1 -0
- package/dist/http/interceptors/dispatch.d.ts +14 -0
- package/dist/http/interceptors/dispatch.d.ts.map +1 -0
- package/dist/http/interceptors/dispatch.js +113 -0
- package/dist/http/interceptors/dispatch.js.map +1 -0
- package/dist/http/interceptors/index.d.ts +6 -0
- package/dist/http/interceptors/index.d.ts.map +1 -0
- package/dist/http/interceptors/index.js +5 -0
- package/dist/http/interceptors/index.js.map +1 -0
- package/dist/http/interceptors/registry.d.ts +44 -0
- package/dist/http/interceptors/registry.d.ts.map +1 -0
- package/dist/http/interceptors/registry.js +123 -0
- package/dist/http/interceptors/registry.js.map +1 -0
- package/dist/http/interceptors/route-table.d.ts +33 -0
- package/dist/http/interceptors/route-table.d.ts.map +1 -0
- package/dist/http/interceptors/route-table.js +39 -0
- package/dist/http/interceptors/route-table.js.map +1 -0
- package/dist/http/interceptors/types.d.ts +101 -0
- package/dist/http/interceptors/types.d.ts.map +1 -0
- package/dist/http/interceptors/types.js +2 -0
- package/dist/http/interceptors/types.js.map +1 -0
- package/dist/http/interceptors/validation.d.ts +22 -0
- package/dist/http/interceptors/validation.d.ts.map +1 -0
- package/dist/http/interceptors/validation.js +39 -0
- package/dist/http/interceptors/validation.js.map +1 -0
- package/dist/http/logger.d.ts +9 -0
- package/dist/http/logger.d.ts.map +1 -0
- package/dist/http/logger.js +25 -0
- package/dist/http/logger.js.map +1 -0
- package/dist/http/openapi.d.ts +21 -0
- package/dist/http/openapi.d.ts.map +1 -0
- package/dist/http/openapi.js +114 -0
- package/dist/http/openapi.js.map +1 -0
- package/dist/http/product-audience.d.ts +78 -0
- package/dist/http/product-audience.d.ts.map +1 -0
- package/dist/http/product-audience.js +92 -0
- package/dist/http/product-audience.js.map +1 -0
- package/dist/http/request-actor.d.ts +82 -0
- package/dist/http/request-actor.d.ts.map +1 -0
- package/dist/http/request-actor.js +2 -0
- package/dist/http/request-actor.js.map +1 -0
- package/dist/http/server.d.ts +60 -0
- package/dist/http/server.d.ts.map +1 -0
- package/dist/http/server.js +117 -0
- package/dist/http/server.js.map +1 -0
- package/dist/http/storefront-revalidator.d.ts +29 -0
- package/dist/http/storefront-revalidator.d.ts.map +1 -0
- package/dist/http/storefront-revalidator.js +34 -0
- package/dist/http/storefront-revalidator.js.map +1 -0
- package/dist/http/test-actor-carrier.d.ts +28 -0
- package/dist/http/test-actor-carrier.d.ts.map +1 -0
- package/dist/http/test-actor-carrier.js +10 -0
- package/dist/http/test-actor-carrier.js.map +1 -0
- package/dist/http/trusted-proxy.d.ts +48 -0
- package/dist/http/trusted-proxy.d.ts.map +1 -0
- package/dist/http/trusted-proxy.js +136 -0
- package/dist/http/trusted-proxy.js.map +1 -0
- package/dist/kernel/audit/audit-log-entry.entity.d.ts +33 -0
- package/dist/kernel/audit/audit-log-entry.entity.d.ts.map +1 -0
- package/dist/kernel/audit/audit-log-entry.entity.js +102 -0
- package/dist/kernel/audit/audit-log-entry.entity.js.map +1 -0
- package/dist/kernel/audit/audit-log-service.d.ts +63 -0
- package/dist/kernel/audit/audit-log-service.d.ts.map +1 -0
- package/dist/kernel/audit/audit-log-service.js +107 -0
- package/dist/kernel/audit/audit-log-service.js.map +1 -0
- package/dist/kernel/cache/in-process-cache-registry.d.ts +93 -0
- package/dist/kernel/cache/in-process-cache-registry.d.ts.map +1 -0
- package/dist/kernel/cache/in-process-cache-registry.js +78 -0
- package/dist/kernel/cache/in-process-cache-registry.js.map +1 -0
- package/dist/kernel/cache/shared-drop-marks.d.ts +39 -0
- package/dist/kernel/cache/shared-drop-marks.d.ts.map +1 -0
- package/dist/kernel/cache/shared-drop-marks.js +68 -0
- package/dist/kernel/cache/shared-drop-marks.js.map +1 -0
- package/dist/kernel/compose.d.ts +234 -0
- package/dist/kernel/compose.d.ts.map +1 -0
- package/dist/kernel/compose.js +279 -0
- package/dist/kernel/compose.js.map +1 -0
- package/dist/kernel/container.d.ts +95 -0
- package/dist/kernel/container.d.ts.map +1 -0
- package/dist/kernel/container.js +90 -0
- package/dist/kernel/container.js.map +1 -0
- package/dist/kernel/contribution-sinks.d.ts +106 -0
- package/dist/kernel/contribution-sinks.d.ts.map +1 -0
- package/dist/kernel/contribution-sinks.js +153 -0
- package/dist/kernel/contribution-sinks.js.map +1 -0
- package/dist/kernel/crypto/password-hasher.d.ts +3 -0
- package/dist/kernel/crypto/password-hasher.d.ts.map +1 -0
- package/dist/kernel/crypto/password-hasher.js +44 -0
- package/dist/kernel/crypto/password-hasher.js.map +1 -0
- package/dist/kernel/i18n/error-envelope-options.d.ts +56 -0
- package/dist/kernel/i18n/error-envelope-options.d.ts.map +1 -0
- package/dist/kernel/i18n/error-envelope-options.js +46 -0
- package/dist/kernel/i18n/error-envelope-options.js.map +1 -0
- package/dist/kernel/i18n/error-translation.d.ts +144 -0
- package/dist/kernel/i18n/error-translation.d.ts.map +1 -0
- package/dist/kernel/i18n/error-translation.js +84 -0
- package/dist/kernel/i18n/error-translation.js.map +1 -0
- package/dist/kernel/i18n/request-language.d.ts +80 -0
- package/dist/kernel/i18n/request-language.d.ts.map +1 -0
- package/dist/kernel/i18n/request-language.js +168 -0
- package/dist/kernel/i18n/request-language.js.map +1 -0
- package/dist/kernel/index.d.ts +142 -0
- package/dist/kernel/index.d.ts.map +1 -0
- package/dist/kernel/index.js +160 -0
- package/dist/kernel/index.js.map +1 -0
- package/dist/kernel/lazy-port.d.ts +59 -0
- package/dist/kernel/lazy-port.d.ts.map +1 -0
- package/dist/kernel/lazy-port.js +78 -0
- package/dist/kernel/lazy-port.js.map +1 -0
- package/dist/kernel/lifecycle/activation-resolver.d.ts +63 -0
- package/dist/kernel/lifecycle/activation-resolver.d.ts.map +1 -0
- package/dist/kernel/lifecycle/activation-resolver.js +70 -0
- package/dist/kernel/lifecycle/activation-resolver.js.map +1 -0
- package/dist/kernel/lifecycle/capability-registry.d.ts +145 -0
- package/dist/kernel/lifecycle/capability-registry.d.ts.map +1 -0
- package/dist/kernel/lifecycle/capability-registry.js +170 -0
- package/dist/kernel/lifecycle/capability-registry.js.map +1 -0
- package/dist/kernel/lifecycle/effective-state.d.ts +126 -0
- package/dist/kernel/lifecycle/effective-state.d.ts.map +1 -0
- package/dist/kernel/lifecycle/effective-state.js +111 -0
- package/dist/kernel/lifecycle/effective-state.js.map +1 -0
- package/dist/kernel/lifecycle/module-registration.entity.d.ts +57 -0
- package/dist/kernel/lifecycle/module-registration.entity.d.ts.map +1 -0
- package/dist/kernel/lifecycle/module-registration.entity.js +111 -0
- package/dist/kernel/lifecycle/module-registration.entity.js.map +1 -0
- package/dist/kernel/lifecycle/plugin-helpers.d.ts +165 -0
- package/dist/kernel/lifecycle/plugin-helpers.d.ts.map +1 -0
- package/dist/kernel/lifecycle/plugin-helpers.js +395 -0
- package/dist/kernel/lifecycle/plugin-helpers.js.map +1 -0
- package/dist/kernel/lifecycle/registry-cache.d.ts +318 -0
- package/dist/kernel/lifecycle/registry-cache.d.ts.map +1 -0
- package/dist/kernel/lifecycle/registry-cache.js +509 -0
- package/dist/kernel/lifecycle/registry-cache.js.map +1 -0
- package/dist/kernel/lifecycle/required-modules.d.ts +137 -0
- package/dist/kernel/lifecycle/required-modules.d.ts.map +1 -0
- package/dist/kernel/lifecycle/required-modules.js +80 -0
- package/dist/kernel/lifecycle/required-modules.js.map +1 -0
- package/dist/kernel/lifecycle/unique-module-ids.d.ts +58 -0
- package/dist/kernel/lifecycle/unique-module-ids.d.ts.map +1 -0
- package/dist/kernel/lifecycle/unique-module-ids.js +82 -0
- package/dist/kernel/lifecycle/unique-module-ids.js.map +1 -0
- package/dist/kernel/logging.d.ts +116 -0
- package/dist/kernel/logging.d.ts.map +1 -0
- package/dist/kernel/logging.js +71 -0
- package/dist/kernel/logging.js.map +1 -0
- package/dist/kernel/module-context.d.ts +563 -0
- package/dist/kernel/module-context.d.ts.map +1 -0
- package/dist/kernel/module-context.js +670 -0
- package/dist/kernel/module-context.js.map +1 -0
- package/dist/kernel/ports/audit.d.ts +115 -0
- package/dist/kernel/ports/audit.d.ts.map +1 -0
- package/dist/kernel/ports/audit.js +2 -0
- package/dist/kernel/ports/audit.js.map +1 -0
- package/dist/kernel/ports/organizations.d.ts +56 -0
- package/dist/kernel/ports/organizations.d.ts.map +1 -0
- package/dist/kernel/ports/organizations.js +2 -0
- package/dist/kernel/ports/organizations.js.map +1 -0
- package/dist/kernel/ports/provide.d.ts +14 -0
- package/dist/kernel/ports/provide.d.ts.map +1 -0
- package/dist/kernel/ports/provide.js +50 -0
- package/dist/kernel/ports/provide.js.map +1 -0
- package/dist/kernel/ports/require-admin.d.ts +61 -0
- package/dist/kernel/ports/require-admin.d.ts.map +1 -0
- package/dist/kernel/ports/require-admin.js +2 -0
- package/dist/kernel/ports/require-admin.js.map +1 -0
- package/dist/kernel/ports/require-customer.d.ts +17 -0
- package/dist/kernel/ports/require-customer.d.ts.map +1 -0
- package/dist/kernel/ports/require-customer.js +2 -0
- package/dist/kernel/ports/require-customer.js.map +1 -0
- package/dist/kernel/ports/sales-channel.d.ts +84 -0
- package/dist/kernel/ports/sales-channel.d.ts.map +1 -0
- package/dist/kernel/ports/sales-channel.js +2 -0
- package/dist/kernel/ports/sales-channel.js.map +1 -0
- package/dist/kernel/ports/settings.d.ts +59 -0
- package/dist/kernel/ports/settings.d.ts.map +1 -0
- package/dist/kernel/ports/settings.js +2 -0
- package/dist/kernel/ports/settings.js.map +1 -0
- package/dist/kernel/public-api-base-url.d.ts +98 -0
- package/dist/kernel/public-api-base-url.d.ts.map +1 -0
- package/dist/kernel/public-api-base-url.js +144 -0
- package/dist/kernel/public-api-base-url.js.map +1 -0
- package/dist/kernel/request-scope-hook.d.ts +63 -0
- package/dist/kernel/request-scope-hook.d.ts.map +1 -0
- package/dist/kernel/request-scope-hook.js +67 -0
- package/dist/kernel/request-scope-hook.js.map +1 -0
- package/dist/kernel/sales-channels/channel-bridge-registry.d.ts +109 -0
- package/dist/kernel/sales-channels/channel-bridge-registry.d.ts.map +1 -0
- package/dist/kernel/sales-channels/channel-bridge-registry.js +75 -0
- package/dist/kernel/sales-channels/channel-bridge-registry.js.map +1 -0
- package/dist/kernel/sales-channels/compose.d.ts +69 -0
- package/dist/kernel/sales-channels/compose.d.ts.map +1 -0
- package/dist/kernel/sales-channels/compose.js +43 -0
- package/dist/kernel/sales-channels/compose.js.map +1 -0
- package/dist/kernel/sales-channels/default-channel-reconciler.d.ts +66 -0
- package/dist/kernel/sales-channels/default-channel-reconciler.d.ts.map +1 -0
- package/dist/kernel/sales-channels/default-channel-reconciler.js +91 -0
- package/dist/kernel/sales-channels/default-channel-reconciler.js.map +1 -0
- package/dist/kernel/sales-channels/no-system-default-channel.error.d.ts +28 -0
- package/dist/kernel/sales-channels/no-system-default-channel.error.d.ts.map +1 -0
- package/dist/kernel/sales-channels/no-system-default-channel.error.js +33 -0
- package/dist/kernel/sales-channels/no-system-default-channel.error.js.map +1 -0
- package/dist/kernel/sales-channels/request-channel-assortment.d.ts +72 -0
- package/dist/kernel/sales-channels/request-channel-assortment.d.ts.map +1 -0
- package/dist/kernel/sales-channels/request-channel-assortment.js +83 -0
- package/dist/kernel/sales-channels/request-channel-assortment.js.map +1 -0
- package/dist/kernel/sales-channels/sales-channel-membership.service.d.ts +157 -0
- package/dist/kernel/sales-channels/sales-channel-membership.service.d.ts.map +1 -0
- package/dist/kernel/sales-channels/sales-channel-membership.service.js +302 -0
- package/dist/kernel/sales-channels/sales-channel-membership.service.js.map +1 -0
- package/dist/kernel/sales-channels/sales-channel-resolver.middleware.d.ts +76 -0
- package/dist/kernel/sales-channels/sales-channel-resolver.middleware.d.ts.map +1 -0
- package/dist/kernel/sales-channels/sales-channel-resolver.middleware.js +184 -0
- package/dist/kernel/sales-channels/sales-channel-resolver.middleware.js.map +1 -0
- package/dist/kernel/sales-channels/sales-channel-resolver.service.d.ts +82 -0
- package/dist/kernel/sales-channels/sales-channel-resolver.service.d.ts.map +1 -0
- package/dist/kernel/sales-channels/sales-channel-resolver.service.js +126 -0
- package/dist/kernel/sales-channels/sales-channel-resolver.service.js.map +1 -0
- package/dist/kernel/sales-channels/sales-channel.entity.d.ts +50 -0
- package/dist/kernel/sales-channels/sales-channel.entity.d.ts.map +1 -0
- package/dist/kernel/sales-channels/sales-channel.entity.js +132 -0
- package/dist/kernel/sales-channels/sales-channel.entity.js.map +1 -0
- package/dist/kernel/sales-channels/sales-channels-cache.d.ts +170 -0
- package/dist/kernel/sales-channels/sales-channels-cache.d.ts.map +1 -0
- package/dist/kernel/sales-channels/sales-channels-cache.js +243 -0
- package/dist/kernel/sales-channels/sales-channels-cache.js.map +1 -0
- package/dist/kernel/scope.d.ts +153 -0
- package/dist/kernel/scope.d.ts.map +1 -0
- package/dist/kernel/scope.js +172 -0
- package/dist/kernel/scope.js.map +1 -0
- package/dist/kernel/settings/compose.d.ts +49 -0
- package/dist/kernel/settings/compose.d.ts.map +1 -0
- package/dist/kernel/settings/compose.js +21 -0
- package/dist/kernel/settings/compose.js.map +1 -0
- package/dist/kernel/settings/manifest-reconciler.d.ts +78 -0
- package/dist/kernel/settings/manifest-reconciler.d.ts.map +1 -0
- package/dist/kernel/settings/manifest-reconciler.js +343 -0
- package/dist/kernel/settings/manifest-reconciler.js.map +1 -0
- package/dist/kernel/settings/secret-value-codec.d.ts +52 -0
- package/dist/kernel/settings/secret-value-codec.d.ts.map +1 -0
- package/dist/kernel/settings/secret-value-codec.js +102 -0
- package/dist/kernel/settings/secret-value-codec.js.map +1 -0
- package/dist/kernel/settings/setting-group.entity.d.ts +25 -0
- package/dist/kernel/settings/setting-group.entity.d.ts.map +1 -0
- package/dist/kernel/settings/setting-group.entity.js +79 -0
- package/dist/kernel/settings/setting-group.entity.js.map +1 -0
- package/dist/kernel/settings/setting-value.entity.d.ts +23 -0
- package/dist/kernel/settings/setting-value.entity.d.ts.map +1 -0
- package/dist/kernel/settings/setting-value.entity.js +66 -0
- package/dist/kernel/settings/setting-value.entity.js.map +1 -0
- package/dist/kernel/settings/setting.entity.d.ts +56 -0
- package/dist/kernel/settings/setting.entity.d.ts.map +1 -0
- package/dist/kernel/settings/setting.entity.js +147 -0
- package/dist/kernel/settings/setting.entity.js.map +1 -0
- package/dist/kernel/settings/settings-cache.d.ts +158 -0
- package/dist/kernel/settings/settings-cache.d.ts.map +1 -0
- package/dist/kernel/settings/settings-cache.js +253 -0
- package/dist/kernel/settings/settings-cache.js.map +1 -0
- package/dist/kernel/settings/settings.service.d.ts +142 -0
- package/dist/kernel/settings/settings.service.d.ts.map +1 -0
- package/dist/kernel/settings/settings.service.js +294 -0
- package/dist/kernel/settings/settings.service.js.map +1 -0
- package/dist/lifecycle/backend.d.ts +51 -0
- package/dist/lifecycle/backend.d.ts.map +1 -0
- package/dist/lifecycle/backend.js +40 -0
- package/dist/lifecycle/backend.js.map +1 -0
- package/dist/lifecycle/commands/activation.commands.d.ts +97 -0
- package/dist/lifecycle/commands/activation.commands.d.ts.map +1 -0
- package/dist/lifecycle/commands/activation.commands.js +142 -0
- package/dist/lifecycle/commands/activation.commands.js.map +1 -0
- package/dist/lifecycle/commands/disable.d.ts +3 -0
- package/dist/lifecycle/commands/disable.d.ts.map +1 -0
- package/dist/lifecycle/commands/disable.js +126 -0
- package/dist/lifecycle/commands/disable.js.map +1 -0
- package/dist/lifecycle/commands/enable.d.ts +3 -0
- package/dist/lifecycle/commands/enable.d.ts.map +1 -0
- package/dist/lifecycle/commands/enable.js +102 -0
- package/dist/lifecycle/commands/enable.js.map +1 -0
- package/dist/lifecycle/commands/install.d.ts +3 -0
- package/dist/lifecycle/commands/install.d.ts.map +1 -0
- package/dist/lifecycle/commands/install.js +263 -0
- package/dist/lifecycle/commands/install.js.map +1 -0
- package/dist/lifecycle/commands/operator-entry.d.ts +104 -0
- package/dist/lifecycle/commands/operator-entry.d.ts.map +1 -0
- package/dist/lifecycle/commands/operator-entry.js +127 -0
- package/dist/lifecycle/commands/operator-entry.js.map +1 -0
- package/dist/lifecycle/commands/operator-runtime.d.ts +147 -0
- package/dist/lifecycle/commands/operator-runtime.d.ts.map +1 -0
- package/dist/lifecycle/commands/operator-runtime.js +40 -0
- package/dist/lifecycle/commands/operator-runtime.js.map +1 -0
- package/dist/lifecycle/commands/status.d.ts +3 -0
- package/dist/lifecycle/commands/status.d.ts.map +1 -0
- package/dist/lifecycle/commands/status.js +108 -0
- package/dist/lifecycle/commands/status.js.map +1 -0
- package/dist/lifecycle/commands/uninstall.d.ts +3 -0
- package/dist/lifecycle/commands/uninstall.d.ts.map +1 -0
- package/dist/lifecycle/commands/uninstall.js +223 -0
- package/dist/lifecycle/commands/uninstall.js.map +1 -0
- package/dist/lifecycle/divergence-declaration.d.ts +74 -0
- package/dist/lifecycle/divergence-declaration.d.ts.map +1 -0
- package/dist/lifecycle/divergence-declaration.js +92 -0
- package/dist/lifecycle/divergence-declaration.js.map +1 -0
- package/dist/lifecycle/i18n/en.json +4 -0
- package/dist/lifecycle/i18n/pl.json +4 -0
- package/dist/lifecycle/index.d.ts +110 -0
- package/dist/lifecycle/index.d.ts.map +1 -0
- package/dist/lifecycle/index.js +189 -0
- package/dist/lifecycle/index.js.map +1 -0
- package/dist/lifecycle/manifest-registry.d.ts +244 -0
- package/dist/lifecycle/manifest-registry.d.ts.map +1 -0
- package/dist/lifecycle/manifest-registry.js +223 -0
- package/dist/lifecycle/manifest-registry.js.map +1 -0
- package/dist/lifecycle/manifest.d.ts +166 -0
- package/dist/lifecycle/manifest.d.ts.map +1 -0
- package/dist/lifecycle/manifest.js +121 -0
- package/dist/lifecycle/manifest.js.map +1 -0
- package/dist/lifecycle/plugin.d.ts +56 -0
- package/dist/lifecycle/plugin.d.ts.map +1 -0
- package/dist/lifecycle/plugin.js +59 -0
- package/dist/lifecycle/plugin.js.map +1 -0
- package/dist/lifecycle/resident.d.ts +16 -0
- package/dist/lifecycle/resident.d.ts.map +1 -0
- package/dist/lifecycle/resident.js +59 -0
- package/dist/lifecycle/resident.js.map +1 -0
- package/dist/lifecycle/routes.admin.d.ts +57 -0
- package/dist/lifecycle/routes.admin.d.ts.map +1 -0
- package/dist/lifecycle/routes.admin.js +162 -0
- package/dist/lifecycle/routes.admin.js.map +1 -0
- package/dist/lifecycle/routes.storefront.d.ts +26 -0
- package/dist/lifecycle/routes.storefront.d.ts.map +1 -0
- package/dist/lifecycle/routes.storefront.js +36 -0
- package/dist/lifecycle/routes.storefront.js.map +1 -0
- package/dist/lifecycle/services/deactivation-ledger.d.ts +216 -0
- package/dist/lifecycle/services/deactivation-ledger.d.ts.map +1 -0
- package/dist/lifecycle/services/deactivation-ledger.js +171 -0
- package/dist/lifecycle/services/deactivation-ledger.js.map +1 -0
- package/dist/lifecycle/services/dep-graph.d.ts +119 -0
- package/dist/lifecycle/services/dep-graph.d.ts.map +1 -0
- package/dist/lifecycle/services/dep-graph.js +334 -0
- package/dist/lifecycle/services/dep-graph.js.map +1 -0
- package/dist/lifecycle/services/gating-graph.d.ts +164 -0
- package/dist/lifecycle/services/gating-graph.d.ts.map +1 -0
- package/dist/lifecycle/services/gating-graph.js +203 -0
- package/dist/lifecycle/services/gating-graph.js.map +1 -0
- package/dist/lifecycle/services/lock.d.ts +27 -0
- package/dist/lifecycle/services/lock.d.ts.map +1 -0
- package/dist/lifecycle/services/lock.js +82 -0
- package/dist/lifecycle/services/lock.js.map +1 -0
- package/dist/lifecycle/services/manifest-loader.d.ts +125 -0
- package/dist/lifecycle/services/manifest-loader.d.ts.map +1 -0
- package/dist/lifecycle/services/manifest-loader.js +210 -0
- package/dist/lifecycle/services/manifest-loader.js.map +1 -0
- package/dist/lifecycle/services/migration-ownership.d.ts +36 -0
- package/dist/lifecycle/services/migration-ownership.d.ts.map +1 -0
- package/dist/lifecycle/services/migration-ownership.js +15 -0
- package/dist/lifecycle/services/migration-ownership.js.map +1 -0
- package/dist/lifecycle/services/module-id-claims.d.ts +38 -0
- package/dist/lifecycle/services/module-id-claims.d.ts.map +1 -0
- package/dist/lifecycle/services/module-id-claims.js +123 -0
- package/dist/lifecycle/services/module-id-claims.js.map +1 -0
- package/dist/lifecycle/services/module-origin.d.ts +55 -0
- package/dist/lifecycle/services/module-origin.d.ts.map +1 -0
- package/dist/lifecycle/services/module-origin.js +47 -0
- package/dist/lifecycle/services/module-origin.js.map +1 -0
- package/dist/lifecycle/services/orchestrator.d.ts +235 -0
- package/dist/lifecycle/services/orchestrator.d.ts.map +1 -0
- package/dist/lifecycle/services/orchestrator.js +954 -0
- package/dist/lifecycle/services/orchestrator.js.map +1 -0
- package/dist/lifecycle/services/presence-load.d.ts +281 -0
- package/dist/lifecycle/services/presence-load.d.ts.map +1 -0
- package/dist/lifecycle/services/presence-load.js +437 -0
- package/dist/lifecycle/services/presence-load.js.map +1 -0
- package/dist/lifecycle/services/static-registry.d.ts +47 -0
- package/dist/lifecycle/services/static-registry.d.ts.map +1 -0
- package/dist/lifecycle/services/static-registry.js +41 -0
- package/dist/lifecycle/services/static-registry.js.map +1 -0
- package/dist/migrations/20260424T165847_core_foundation_init.d.ts +25 -0
- package/dist/migrations/20260424T165847_core_foundation_init.d.ts.map +1 -0
- package/dist/migrations/20260424T165847_core_foundation_init.js +238 -0
- package/dist/migrations/20260424T165847_core_foundation_init.js.map +1 -0
- package/dist/migrations/20260425T050720_core_commerce_init.d.ts +19 -0
- package/dist/migrations/20260425T050720_core_commerce_init.d.ts.map +1 -0
- package/dist/migrations/20260425T050720_core_commerce_init.js +200 -0
- package/dist/migrations/20260425T050720_core_commerce_init.js.map +1 -0
- package/dist/migrations/20260430T101450_core_settings_init.d.ts +25 -0
- package/dist/migrations/20260430T101450_core_settings_init.d.ts.map +1 -0
- package/dist/migrations/20260430T101450_core_settings_init.js +113 -0
- package/dist/migrations/20260430T101450_core_settings_init.js.map +1 -0
- package/dist/migrations/20260430T170044_core_sales_channels_promote.d.ts +67 -0
- package/dist/migrations/20260430T170044_core_sales_channels_promote.d.ts.map +1 -0
- package/dist/migrations/20260430T170044_core_sales_channels_promote.js +101 -0
- package/dist/migrations/20260430T170044_core_sales_channels_promote.js.map +1 -0
- package/dist/migrations/20260506T200657_core_module_lifecycle_init.d.ts +21 -0
- package/dist/migrations/20260506T200657_core_module_lifecycle_init.d.ts.map +1 -0
- package/dist/migrations/20260506T200657_core_module_lifecycle_init.js +41 -0
- package/dist/migrations/20260506T200657_core_module_lifecycle_init.js.map +1 -0
- package/dist/migrations/20260514T111329_core_settings_global_value.d.ts +30 -0
- package/dist/migrations/20260514T111329_core_settings_global_value.d.ts.map +1 -0
- package/dist/migrations/20260514T111329_core_settings_global_value.js +34 -0
- package/dist/migrations/20260514T111329_core_settings_global_value.js.map +1 -0
- package/dist/migrations/20260611T140411_core_settings_secret_value_type.d.ts +26 -0
- package/dist/migrations/20260611T140411_core_settings_secret_value_type.d.ts.map +1 -0
- package/dist/migrations/20260611T140411_core_settings_secret_value_type.js +63 -0
- package/dist/migrations/20260611T140411_core_settings_secret_value_type.js.map +1 -0
- package/dist/migrations/20260611T140419_core_settings_enum_options.d.ts +19 -0
- package/dist/migrations/20260611T140419_core_settings_enum_options.d.ts.map +1 -0
- package/dist/migrations/20260611T140419_core_settings_enum_options.js +23 -0
- package/dist/migrations/20260611T140419_core_settings_enum_options.js.map +1 -0
- package/dist/migrations/20260629T090100_core_settings_hidden_flag.d.ts +20 -0
- package/dist/migrations/20260629T090100_core_settings_hidden_flag.d.ts.map +1 -0
- package/dist/migrations/20260629T090100_core_settings_hidden_flag.js +24 -0
- package/dist/migrations/20260629T090100_core_settings_hidden_flag.js.map +1 -0
- package/dist/migrations/20260717T134752_core_tenant_scope_indexes.d.ts +27 -0
- package/dist/migrations/20260717T134752_core_tenant_scope_indexes.d.ts.map +1 -0
- package/dist/migrations/20260717T134752_core_tenant_scope_indexes.js +32 -0
- package/dist/migrations/20260717T134752_core_tenant_scope_indexes.js.map +1 -0
- package/dist/migrations/20260721T011510_core_settings_credential_ref_value_type.d.ts +24 -0
- package/dist/migrations/20260721T011510_core_settings_credential_ref_value_type.d.ts.map +1 -0
- package/dist/migrations/20260721T011510_core_settings_credential_ref_value_type.js +57 -0
- package/dist/migrations/20260721T011510_core_settings_credential_ref_value_type.js.map +1 -0
- package/dist/migrations/20260816T203339_core_retire_core_activation_settings.d.ts +39 -0
- package/dist/migrations/20260816T203339_core_retire_core_activation_settings.d.ts.map +1 -0
- package/dist/migrations/20260816T203339_core_retire_core_activation_settings.js +59 -0
- package/dist/migrations/20260816T203339_core_retire_core_activation_settings.js.map +1 -0
- package/dist/migrations/20260919T101500_core_module_registrations_boot_converged.d.ts +34 -0
- package/dist/migrations/20260919T101500_core_module_registrations_boot_converged.d.ts.map +1 -0
- package/dist/migrations/20260919T101500_core_module_registrations_boot_converged.js +38 -0
- package/dist/migrations/20260919T101500_core_module_registrations_boot_converged.js.map +1 -0
- package/dist/migrations/baseline-migrations.generated.d.ts +2 -0
- package/dist/migrations/baseline-migrations.generated.d.ts.map +1 -0
- package/dist/migrations/baseline-migrations.generated.js +136 -0
- package/dist/migrations/baseline-migrations.generated.js.map +1 -0
- package/dist/migrations/index.d.ts +62 -0
- package/dist/migrations/index.d.ts.map +1 -0
- package/dist/migrations/index.js +75 -0
- package/dist/migrations/index.js.map +1 -0
- package/dist/overlay/deployment-roots.d.ts +37 -0
- package/dist/overlay/deployment-roots.d.ts.map +1 -0
- package/dist/overlay/deployment-roots.js +90 -0
- package/dist/overlay/deployment-roots.js.map +1 -0
- package/dist/overlay/index.d.ts +62 -0
- package/dist/overlay/index.d.ts.map +1 -0
- package/dist/overlay/index.js +62 -0
- package/dist/overlay/index.js.map +1 -0
- package/dist/overlay/overlay-runtime.d.ts +173 -0
- package/dist/overlay/overlay-runtime.d.ts.map +1 -0
- package/dist/overlay/overlay-runtime.js +281 -0
- package/dist/overlay/overlay-runtime.js.map +1 -0
- package/dist/overlay/resolve-overlay.d.ts +41 -0
- package/dist/overlay/resolve-overlay.d.ts.map +1 -0
- package/dist/overlay/resolve-overlay.js +72 -0
- package/dist/overlay/resolve-overlay.js.map +1 -0
- package/dist/packages/index.d.ts +57 -0
- package/dist/packages/index.d.ts.map +1 -0
- package/dist/packages/index.js +57 -0
- package/dist/packages/index.js.map +1 -0
- package/dist/packages/installed-packages.d.ts +79 -0
- package/dist/packages/installed-packages.d.ts.map +1 -0
- package/dist/packages/installed-packages.js +312 -0
- package/dist/packages/installed-packages.js.map +1 -0
- package/dist/packages/package-entity-lookup.d.ts +120 -0
- package/dist/packages/package-entity-lookup.d.ts.map +1 -0
- package/dist/packages/package-entity-lookup.js +17 -0
- package/dist/packages/package-entity-lookup.js.map +1 -0
- package/dist/packages/package-runtime.d.ts +177 -0
- package/dist/packages/package-runtime.d.ts.map +1 -0
- package/dist/packages/package-runtime.js +386 -0
- package/dist/packages/package-runtime.js.map +1 -0
- package/dist/tenancy/derived-scope.d.ts +43 -0
- package/dist/tenancy/derived-scope.d.ts.map +1 -0
- package/dist/tenancy/derived-scope.js +76 -0
- package/dist/tenancy/derived-scope.js.map +1 -0
- package/dist/tenancy/escape-hatch.d.ts +30 -0
- package/dist/tenancy/escape-hatch.d.ts.map +1 -0
- package/dist/tenancy/escape-hatch.js +50 -0
- package/dist/tenancy/escape-hatch.js.map +1 -0
- package/dist/tenancy/filters.d.ts +87 -0
- package/dist/tenancy/filters.d.ts.map +1 -0
- package/dist/tenancy/filters.js +126 -0
- package/dist/tenancy/filters.js.map +1 -0
- package/dist/tenancy/index.d.ts +63 -0
- package/dist/tenancy/index.d.ts.map +1 -0
- package/dist/tenancy/index.js +61 -0
- package/dist/tenancy/index.js.map +1 -0
- package/dist/tenancy/org-scoped.decorator.d.ts +150 -0
- package/dist/tenancy/org-scoped.decorator.d.ts.map +1 -0
- package/dist/tenancy/org-scoped.decorator.js +339 -0
- package/dist/tenancy/org-scoped.decorator.js.map +1 -0
- package/dist/tenancy/org-write-guard.d.ts +106 -0
- package/dist/tenancy/org-write-guard.d.ts.map +1 -0
- package/dist/tenancy/org-write-guard.js +141 -0
- package/dist/tenancy/org-write-guard.js.map +1 -0
- package/dist/tenancy/resolve-tenant-context.d.ts +91 -0
- package/dist/tenancy/resolve-tenant-context.d.ts.map +1 -0
- package/dist/tenancy/resolve-tenant-context.js +85 -0
- package/dist/tenancy/resolve-tenant-context.js.map +1 -0
- package/dist/tenancy/scoped-em.d.ts +10 -0
- package/dist/tenancy/scoped-em.d.ts.map +1 -0
- package/dist/tenancy/scoped-em.js +11 -0
- package/dist/tenancy/scoped-em.js.map +1 -0
- package/dist/tenancy/tenant-context.d.ts +154 -0
- package/dist/tenancy/tenant-context.d.ts.map +1 -0
- package/dist/tenancy/tenant-context.js +109 -0
- package/dist/tenancy/tenant-context.js.map +1 -0
- package/package.json +123 -0
|
@@ -0,0 +1,954 @@
|
|
|
1
|
+
import { dirname } from 'node:path';
|
|
2
|
+
import { settingsManifestWithRecentActivity } from '@endora-commerce/contracts';
|
|
3
|
+
import { ManifestReconciler } from '../../kernel/settings/manifest-reconciler.js';
|
|
4
|
+
import { Setting } from '../../kernel/settings/setting.entity.js';
|
|
5
|
+
import { SettingGroup } from '../../kernel/settings/setting-group.entity.js';
|
|
6
|
+
import { ModuleRegistration } from '../../kernel/lifecycle/module-registration.entity.js';
|
|
7
|
+
import { acquireLifecycleLock, } from './lock.js';
|
|
8
|
+
import { publishStateChanged, registryCache, } from '../../kernel/lifecycle/registry-cache.js';
|
|
9
|
+
import { pauseWorkersFor, resumeWorkersFor, } from '../../kernel/lifecycle/plugin-helpers.js';
|
|
10
|
+
import { moduleDependencyCycles } from './dep-graph.js';
|
|
11
|
+
export class LifecycleError extends Error {
|
|
12
|
+
kind;
|
|
13
|
+
details;
|
|
14
|
+
constructor(kind, message, details = {}) {
|
|
15
|
+
super(message);
|
|
16
|
+
this.kind = kind;
|
|
17
|
+
this.details = details;
|
|
18
|
+
this.name = 'LifecycleError';
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
// Orchestrator
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
export class ModuleLifecycleOrchestrator {
|
|
25
|
+
deps;
|
|
26
|
+
log;
|
|
27
|
+
constructor(deps) {
|
|
28
|
+
this.deps = deps;
|
|
29
|
+
this.log = deps.log ?? consoleLogger();
|
|
30
|
+
}
|
|
31
|
+
// -------------------------------------------------------------------------
|
|
32
|
+
// INSTALL
|
|
33
|
+
// -------------------------------------------------------------------------
|
|
34
|
+
async install(moduleId) {
|
|
35
|
+
const start = Date.now();
|
|
36
|
+
const entry = this.deps.registry.modules.get(moduleId);
|
|
37
|
+
if (!entry) {
|
|
38
|
+
throw new LifecycleError('unknown-module', `unknown module "${moduleId}"`);
|
|
39
|
+
}
|
|
40
|
+
const manifest = entry.manifest;
|
|
41
|
+
const lease = await this.acquireLock();
|
|
42
|
+
try {
|
|
43
|
+
// Read existing registration row.
|
|
44
|
+
const existing = await this.deps.em().findOne(ModuleRegistration, {
|
|
45
|
+
moduleId,
|
|
46
|
+
});
|
|
47
|
+
// `installed` **and** unmarked — a row this orchestrator wrote itself, so
|
|
48
|
+
// there is nothing left to do.
|
|
49
|
+
//
|
|
50
|
+
// The marker is what narrows this, and the narrowing is the whole repair.
|
|
51
|
+
// `reconcileExistingModules` converges `module_registrations` to the shipped
|
|
52
|
+
// manifest list at boot and runs no hook — it cannot, and D-157.6(b) is why
|
|
53
|
+
// it must not — so a database whose first action after the migrations was a
|
|
54
|
+
// `start` carried `installed` rows no install had produced. This branch
|
|
55
|
+
// answered `already-installed` over them, for ever: no migration, no
|
|
56
|
+
// settings reconcile, no participant pass and **no install hook**, with
|
|
57
|
+
// nothing saying so. Invisible until feature 134's W7, because until then
|
|
58
|
+
// the tree had no `installHook` at all.
|
|
59
|
+
//
|
|
60
|
+
// With `bootConvergedAt` set, the normal body below runs instead, and every
|
|
61
|
+
// step of it is already safe on a converged database: `getPendingMigrations`
|
|
62
|
+
// returns none so the rollback set is empty, the settings reconcile is
|
|
63
|
+
// idempotent, the participants upsert-and-prune, and the hook is idempotent
|
|
64
|
+
// by contract (`module-composition.md` item 9). Step 4 clears the marker, so
|
|
65
|
+
// the second run is this no-op again — leaving it set would re-run every
|
|
66
|
+
// hook on every `module:install --all`, which is the write-on-every-
|
|
67
|
+
// invocation shape issue #96 took out of the gateway reconcilers.
|
|
68
|
+
//
|
|
69
|
+
// That is what makes `module:install --all` the command that **repairs** a
|
|
70
|
+
// boot-first database rather than reporting it as already fine.
|
|
71
|
+
if (existing?.state === 'installed' && existing.bootConvergedAt == null) {
|
|
72
|
+
return {
|
|
73
|
+
moduleId,
|
|
74
|
+
version: existing.version,
|
|
75
|
+
state: 'already-installed',
|
|
76
|
+
appliedMigrations: [],
|
|
77
|
+
settings: { addedGroups: 0, addedSettings: 0, updatedGroups: 0, updatedSettings: 0 },
|
|
78
|
+
hookDurationMs: 0,
|
|
79
|
+
totalDurationMs: Date.now() - start,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
if (existing?.state === 'disabled') {
|
|
83
|
+
throw new LifecycleError('wrong-state', `module "${moduleId}" is installed but disabled; use module:enable`, { state: existing.state });
|
|
84
|
+
}
|
|
85
|
+
if (existing?.state === 'installing') {
|
|
86
|
+
throw new LifecycleError('lock-busy', `module "${moduleId}" has a stale 'installing' record; see runbook`);
|
|
87
|
+
}
|
|
88
|
+
// Verify deps are installed (or disabled — they're still "installed" semantically).
|
|
89
|
+
const installedSet = await this.installedSet();
|
|
90
|
+
const missing = this.deps.registry.graph.unresolvedDependenciesOf(moduleId, installedSet);
|
|
91
|
+
if (missing.length > 0) {
|
|
92
|
+
await this.deps.auditLog.record({
|
|
93
|
+
actorAdminUserId: null,
|
|
94
|
+
action: 'module.dependency_blocked',
|
|
95
|
+
objectType: 'module',
|
|
96
|
+
objectId: moduleId,
|
|
97
|
+
stateAfter: { command: 'install', conflicting: missing },
|
|
98
|
+
});
|
|
99
|
+
throw new LifecycleError('missing-deps', `module "${moduleId}" missing dependencies: ${missing.join(', ')}`, { missing });
|
|
100
|
+
}
|
|
101
|
+
await this.assertInstallClosesNoCycle(moduleId, installedSet);
|
|
102
|
+
// Before the registration row moves: an install this orchestrator cannot
|
|
103
|
+
// reconcile is refused rather than performed and reported as done.
|
|
104
|
+
const participants = this.participantsOrRefuse('install', moduleId);
|
|
105
|
+
// Mark 'installing' so a crash leaves a paper trail.
|
|
106
|
+
await this.upsertRegistration(moduleId, {
|
|
107
|
+
state: 'installing',
|
|
108
|
+
version: manifest.version,
|
|
109
|
+
installedAt: existing?.installedAt ?? new Date(),
|
|
110
|
+
lastStateChangeAt: new Date(),
|
|
111
|
+
lastInstallFailedAt: null,
|
|
112
|
+
lastInstallError: null,
|
|
113
|
+
});
|
|
114
|
+
let appliedMigrations = [];
|
|
115
|
+
let settingsResult = { addedGroups: 0, addedSettings: 0, updatedGroups: 0, updatedSettings: 0 };
|
|
116
|
+
let hookDurationMs = 0;
|
|
117
|
+
try {
|
|
118
|
+
// 1. Migrations — run all pending. Per research §R3, on a single
|
|
119
|
+
// instance migrations are global (linear log); installing a
|
|
120
|
+
// module's migrations also runs any prior pending ones, which is
|
|
121
|
+
// the expected outcome.
|
|
122
|
+
const migrator = await this.migrator();
|
|
123
|
+
const pendingBefore = await migrator.getPendingMigrations();
|
|
124
|
+
if (pendingBefore.length > 0) {
|
|
125
|
+
const applied = await migrator.up();
|
|
126
|
+
appliedMigrations = applied.map((m) => m.name);
|
|
127
|
+
}
|
|
128
|
+
// 2. Settings — reuse feature 004's reconciler.
|
|
129
|
+
//
|
|
130
|
+
// The manifest's own declaration **plus** the visibility Setting its
|
|
131
|
+
// recent-activity eligibility implies (feature 080, T042j / D-163.1).
|
|
132
|
+
// One derivation with two callers, this and the boot reconcile: a
|
|
133
|
+
// package has only this one, since D-157.6(b) makes `install` its sole
|
|
134
|
+
// settings author, so a second copy of the merge would mean a packaged
|
|
135
|
+
// module's dashboard control existing on one path and not the other.
|
|
136
|
+
const settingsManifest = settingsManifestWithRecentActivity(manifest, entry.recentActivity);
|
|
137
|
+
if (settingsManifest) {
|
|
138
|
+
const reconciler = new ManifestReconciler(this.deps.em());
|
|
139
|
+
const result = await reconciler.apply([settingsManifest]);
|
|
140
|
+
const r = result.perModule[0];
|
|
141
|
+
if (r) {
|
|
142
|
+
settingsResult = {
|
|
143
|
+
addedGroups: r.addedGroups,
|
|
144
|
+
addedSettings: r.addedSettings,
|
|
145
|
+
updatedGroups: r.updatedGroups,
|
|
146
|
+
updatedSettings: r.updatedSettings,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
// 2b. Every module that keeps a projection of the manifest set gets
|
|
151
|
+
// told, in one pass (feature 080, T036a / D-159). `_i18n` refreshes
|
|
152
|
+
// `translation_bundles` from `<modulePath>/<bundlesDir>/<lang>.json`
|
|
153
|
+
// and `admin_actions` upserts-and-prunes `module_actions` from
|
|
154
|
+
// `manifest.actions`; each decides for itself whether this manifest
|
|
155
|
+
// is one it projects, which is why the orchestrator no longer asks
|
|
156
|
+
// about `manifest.i18n`. A participant that throws aborts the install
|
|
157
|
+
// and reverts its migrations, which is what FR-016 rests on — an
|
|
158
|
+
// operator installing a module with an unreadable bundle is told while
|
|
159
|
+
// they can still choose not to install it.
|
|
160
|
+
//
|
|
161
|
+
// The `em` is **this operation's**: the two reconcilers accepted a
|
|
162
|
+
// transactional one and were called without it, so their rows landed
|
|
163
|
+
// on a fork made when the platform was composed rather than in the
|
|
164
|
+
// work the orchestrator is doing.
|
|
165
|
+
{
|
|
166
|
+
const participantEm = this.deps.em();
|
|
167
|
+
for (const { moduleId: owner, participant } of participants) {
|
|
168
|
+
await participant.onModuleInstalled({
|
|
169
|
+
moduleId: manifest.id,
|
|
170
|
+
manifest,
|
|
171
|
+
modulePath: dirname(entry.filePath),
|
|
172
|
+
em: participantEm,
|
|
173
|
+
log: taggedLogger(this.log, owner),
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
// 3. Install hook — runs inside the same em context.
|
|
178
|
+
if (entry.installHook) {
|
|
179
|
+
const hookStart = Date.now();
|
|
180
|
+
await entry.installHook({
|
|
181
|
+
em: this.deps.em(),
|
|
182
|
+
redis: this.deps.redis,
|
|
183
|
+
log: this.log,
|
|
184
|
+
module: { id: manifest.id, version: manifest.version },
|
|
185
|
+
});
|
|
186
|
+
hookDurationMs = Date.now() - hookStart;
|
|
187
|
+
}
|
|
188
|
+
// 4. Mark installed — and clear the boot-convergence marker, because an
|
|
189
|
+
// install has now run. It is cleared *here*, after the hook, so a
|
|
190
|
+
// failure above leaves it set and the next `module:install` repairs
|
|
191
|
+
// the row again instead of finding it already fine. On the failure
|
|
192
|
+
// path the row goes to `uninstalled` anyway, but this ordering is what
|
|
193
|
+
// makes that a consequence rather than the only thing holding it.
|
|
194
|
+
await this.upsertRegistration(moduleId, {
|
|
195
|
+
state: 'installed',
|
|
196
|
+
version: manifest.version,
|
|
197
|
+
lastStateChangeAt: new Date(),
|
|
198
|
+
bootConvergedAt: null,
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
catch (err) {
|
|
202
|
+
// Rollback path: revert any migrations we just applied (in reverse).
|
|
203
|
+
for (const name of [...appliedMigrations].reverse()) {
|
|
204
|
+
try {
|
|
205
|
+
await (await this.migrator()).down({ migrations: [name] });
|
|
206
|
+
}
|
|
207
|
+
catch (revertErr) {
|
|
208
|
+
this.log.error(`[install-rollback] failed to revert migration ${name}: ` +
|
|
209
|
+
(revertErr instanceof Error ? revertErr.message : String(revertErr)));
|
|
210
|
+
break;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
// Mark the registry as failed-uninstalled so a re-run is allowed.
|
|
214
|
+
await this.upsertRegistration(moduleId, {
|
|
215
|
+
state: 'uninstalled',
|
|
216
|
+
lastInstallFailedAt: new Date(),
|
|
217
|
+
lastInstallError: err instanceof Error ? `${err.name}: ${err.message}` : String(err),
|
|
218
|
+
lastStateChangeAt: new Date(),
|
|
219
|
+
});
|
|
220
|
+
await this.deps.auditLog.record({
|
|
221
|
+
actorAdminUserId: null,
|
|
222
|
+
action: 'module.install_failed',
|
|
223
|
+
objectType: 'module',
|
|
224
|
+
objectId: moduleId,
|
|
225
|
+
stateAfter: {
|
|
226
|
+
version: manifest.version,
|
|
227
|
+
error: err instanceof Error ? `${err.name}: ${err.message}` : String(err),
|
|
228
|
+
},
|
|
229
|
+
});
|
|
230
|
+
throw new LifecycleError('install-failed', `install of "${moduleId}" failed: ` +
|
|
231
|
+
(err instanceof Error ? err.message : String(err)), {
|
|
232
|
+
cause: err instanceof Error ? err.message : String(err),
|
|
233
|
+
revertedMigrations: appliedMigrations,
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
// Audit + pub/sub on the success path.
|
|
237
|
+
await this.deps.auditLog.record({
|
|
238
|
+
actorAdminUserId: null,
|
|
239
|
+
action: 'module.installed',
|
|
240
|
+
objectType: 'module',
|
|
241
|
+
objectId: moduleId,
|
|
242
|
+
stateAfter: {
|
|
243
|
+
version: manifest.version,
|
|
244
|
+
settings: settingsResult,
|
|
245
|
+
hooks: entry.installHook ? ['install'] : [],
|
|
246
|
+
migrations: appliedMigrations,
|
|
247
|
+
},
|
|
248
|
+
});
|
|
249
|
+
await publishStateChanged(this.deps.redis, {
|
|
250
|
+
moduleId,
|
|
251
|
+
newState: 'installed',
|
|
252
|
+
});
|
|
253
|
+
// Update the in-process cache immediately so the same process sees
|
|
254
|
+
// the change without waiting for the pub/sub round-trip.
|
|
255
|
+
await registryCache.refreshFromDb(this.deps.em);
|
|
256
|
+
return {
|
|
257
|
+
moduleId,
|
|
258
|
+
version: manifest.version,
|
|
259
|
+
state: 'installed',
|
|
260
|
+
appliedMigrations,
|
|
261
|
+
settings: settingsResult,
|
|
262
|
+
hookDurationMs,
|
|
263
|
+
totalDurationMs: Date.now() - start,
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
finally {
|
|
267
|
+
await lease.release();
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
// -------------------------------------------------------------------------
|
|
271
|
+
// UNINSTALL (soft + hard)
|
|
272
|
+
// -------------------------------------------------------------------------
|
|
273
|
+
async uninstall(moduleId, opts = { hard: false }) {
|
|
274
|
+
const entry = this.deps.registry.modules.get(moduleId);
|
|
275
|
+
// unknown-module is OK for uninstall — the registry might have an
|
|
276
|
+
// orphan row we want to clean up. Use whatever we know.
|
|
277
|
+
const manifest = entry?.manifest ?? null;
|
|
278
|
+
const lease = await this.acquireLock();
|
|
279
|
+
try {
|
|
280
|
+
const row = await this.deps.em().findOne(ModuleRegistration, {
|
|
281
|
+
moduleId,
|
|
282
|
+
});
|
|
283
|
+
if (!row || row.state === 'uninstalled') {
|
|
284
|
+
return {
|
|
285
|
+
moduleId,
|
|
286
|
+
hard: opts.hard,
|
|
287
|
+
state: 'already-uninstalled',
|
|
288
|
+
removedSettings: 0,
|
|
289
|
+
removedGroups: 0,
|
|
290
|
+
revertedMigrations: [],
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
if (row.state === 'installing') {
|
|
294
|
+
throw new LifecycleError('lock-busy', `module "${moduleId}" has a stale 'installing' record; see runbook`);
|
|
295
|
+
}
|
|
296
|
+
// D-69 (issue #145) — the declaration that refuses `disable` refuses this
|
|
297
|
+
// too, soft and hard alike. Uninstall is disable *plus* the settings sweep
|
|
298
|
+
// below and, on `--hard`, the migration revert, so a lock that forbids the
|
|
299
|
+
// smaller operation cannot coherently permit the larger one. Raised after
|
|
300
|
+
// the `already-uninstalled` no-op (nothing left to protect) and before the
|
|
301
|
+
// dependents check, the uninstall hook and the sweep — the dependents block
|
|
302
|
+
// covers the locked set only by accident today, since nothing stops an
|
|
303
|
+
// operator uninstalling the dependents first.
|
|
304
|
+
this.assertDeactivatable(moduleId);
|
|
305
|
+
// A hard uninstall is the half that does not self-heal (D-159 §7): both
|
|
306
|
+
// boot reconcilers iterate the manifest registry, so `uninstall --hard`
|
|
307
|
+
// followed by `pnpm remove` leaves rows no boot pass will ever see the
|
|
308
|
+
// manifest for again. Refuse before the uninstall hook rather than after,
|
|
309
|
+
// for the reason the migration-revert refusal states in the same file: a
|
|
310
|
+
// hard uninstall that removes the registration and leaves the projection
|
|
311
|
+
// is the one outcome it may not have. A **soft** uninstall reaches no
|
|
312
|
+
// participant at all — it preserves the module's data by design — so it
|
|
313
|
+
// is not gated on this.
|
|
314
|
+
const participants = opts.hard
|
|
315
|
+
? this.participantsOrRefuse('uninstall --hard', moduleId)
|
|
316
|
+
: [];
|
|
317
|
+
// Block on dependents (any state except uninstalled).
|
|
318
|
+
const dependents = await this.installedDependentsOf(moduleId);
|
|
319
|
+
if (dependents.length > 0) {
|
|
320
|
+
await this.deps.auditLog.record({
|
|
321
|
+
actorAdminUserId: null,
|
|
322
|
+
action: 'module.dependency_blocked',
|
|
323
|
+
objectType: 'module',
|
|
324
|
+
objectId: moduleId,
|
|
325
|
+
stateAfter: { command: 'uninstall', conflicting: dependents },
|
|
326
|
+
});
|
|
327
|
+
throw new LifecycleError('dependents-block', `cannot uninstall "${moduleId}": other modules depend on it: ${dependents.join(', ')}`, { dependents });
|
|
328
|
+
}
|
|
329
|
+
// Run the manifest's uninstall hook, before any removal. A throw aborts
|
|
330
|
+
// the whole uninstall and removes nothing — the settings sweep and the
|
|
331
|
+
// migration revert below are both downstream of it.
|
|
332
|
+
if (entry?.uninstallHook) {
|
|
333
|
+
try {
|
|
334
|
+
await entry.uninstallHook({
|
|
335
|
+
em: this.deps.em(),
|
|
336
|
+
redis: this.deps.redis,
|
|
337
|
+
log: this.log,
|
|
338
|
+
module: {
|
|
339
|
+
id: moduleId,
|
|
340
|
+
version: manifest?.version ?? row.version,
|
|
341
|
+
},
|
|
342
|
+
hard: opts.hard,
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
catch (err) {
|
|
346
|
+
this.log.error(`[uninstall] hook for "${moduleId}" threw: ` +
|
|
347
|
+
(err instanceof Error ? err.message : String(err)));
|
|
348
|
+
throw new LifecycleError('uninstall-failed', `uninstall hook for "${moduleId}" failed: ` +
|
|
349
|
+
(err instanceof Error ? err.message : String(err)));
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
// Unregister settings (always — soft and hard both clear settings).
|
|
353
|
+
const em = this.deps.em();
|
|
354
|
+
const ownedSettings = await em.find(Setting, { ownerModule: moduleId });
|
|
355
|
+
for (const s of ownedSettings)
|
|
356
|
+
em.remove(s);
|
|
357
|
+
const ownedGroups = await em.find(SettingGroup, {
|
|
358
|
+
ownerModule: moduleId,
|
|
359
|
+
isSystemProtected: false,
|
|
360
|
+
});
|
|
361
|
+
for (const g of ownedGroups)
|
|
362
|
+
em.remove(g);
|
|
363
|
+
await em.flush();
|
|
364
|
+
let revertedMigrations = [];
|
|
365
|
+
if (opts.hard) {
|
|
366
|
+
revertedMigrations = await this.revertMigrationsFor(moduleId);
|
|
367
|
+
// Hard uninstall deletes the row; soft preserves it as 'uninstalled'.
|
|
368
|
+
await em.removeAndFlush(row);
|
|
369
|
+
// Hard-uninstall drops every projection of this module's manifest —
|
|
370
|
+
// its translation bundles and its palette actions. Soft-uninstall
|
|
371
|
+
// preserves them so a re-install picks them up unchanged
|
|
372
|
+
// (data-model §3), which is why this sits in the `hard` branch.
|
|
373
|
+
//
|
|
374
|
+
// `manifest` is nullable here and the participants are told so: an
|
|
375
|
+
// orphan registration row, whose module this instance no longer has,
|
|
376
|
+
// is exactly the case whose rows nothing else will ever remove.
|
|
377
|
+
for (const { moduleId: owner, participant } of participants) {
|
|
378
|
+
await participant.onModuleHardUninstalled({
|
|
379
|
+
moduleId,
|
|
380
|
+
manifest,
|
|
381
|
+
em,
|
|
382
|
+
log: taggedLogger(this.log, owner),
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
else {
|
|
387
|
+
row.state = 'uninstalled';
|
|
388
|
+
row.lastStateChangeAt = new Date();
|
|
389
|
+
await em.flush();
|
|
390
|
+
}
|
|
391
|
+
await this.deps.auditLog.record({
|
|
392
|
+
actorAdminUserId: null,
|
|
393
|
+
action: 'module.uninstalled',
|
|
394
|
+
objectType: 'module',
|
|
395
|
+
objectId: moduleId,
|
|
396
|
+
stateAfter: {
|
|
397
|
+
hard: opts.hard,
|
|
398
|
+
settings: {
|
|
399
|
+
removedSettings: ownedSettings.length,
|
|
400
|
+
removedGroups: ownedGroups.length,
|
|
401
|
+
},
|
|
402
|
+
revertedMigrations,
|
|
403
|
+
hooks: entry?.uninstallHook ? ['uninstall'] : [],
|
|
404
|
+
},
|
|
405
|
+
});
|
|
406
|
+
await publishStateChanged(this.deps.redis, {
|
|
407
|
+
moduleId,
|
|
408
|
+
newState: 'uninstalled',
|
|
409
|
+
});
|
|
410
|
+
await registryCache.refreshFromDb(this.deps.em);
|
|
411
|
+
return {
|
|
412
|
+
moduleId,
|
|
413
|
+
hard: opts.hard,
|
|
414
|
+
state: 'uninstalled',
|
|
415
|
+
removedSettings: ownedSettings.length,
|
|
416
|
+
removedGroups: ownedGroups.length,
|
|
417
|
+
revertedMigrations,
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
finally {
|
|
421
|
+
await lease.release();
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
// -------------------------------------------------------------------------
|
|
425
|
+
// ENABLE / DISABLE
|
|
426
|
+
// -------------------------------------------------------------------------
|
|
427
|
+
async enable(moduleId) {
|
|
428
|
+
const entry = this.deps.registry.modules.get(moduleId);
|
|
429
|
+
if (!entry) {
|
|
430
|
+
throw new LifecycleError('unknown-module', `unknown module "${moduleId}"`);
|
|
431
|
+
}
|
|
432
|
+
const lease = await this.acquireLock();
|
|
433
|
+
try {
|
|
434
|
+
const row = await this.deps.em().findOne(ModuleRegistration, {
|
|
435
|
+
moduleId,
|
|
436
|
+
});
|
|
437
|
+
if (!row || row.state === 'uninstalled' || row.state === 'installing') {
|
|
438
|
+
throw new LifecycleError('wrong-state', `module "${moduleId}" is not installed (state=${row?.state ?? 'none'})`);
|
|
439
|
+
}
|
|
440
|
+
if (row.state === 'installed') {
|
|
441
|
+
return { moduleId, state: 'already-enabled' };
|
|
442
|
+
}
|
|
443
|
+
// state === 'disabled' — verify deps are enabled.
|
|
444
|
+
const enabledSet = await this.enabledSet();
|
|
445
|
+
const missing = this.deps.registry.graph.unresolvedDependenciesOf(moduleId, enabledSet);
|
|
446
|
+
if (missing.length > 0) {
|
|
447
|
+
throw new LifecycleError('missing-deps', `cannot enable "${moduleId}": dependencies not enabled: ${missing.join(', ')}`, { missing });
|
|
448
|
+
}
|
|
449
|
+
row.state = 'installed';
|
|
450
|
+
row.lastStateChangeAt = new Date();
|
|
451
|
+
await this.deps.em().flush();
|
|
452
|
+
await this.deps.auditLog.record({
|
|
453
|
+
actorAdminUserId: null,
|
|
454
|
+
action: 'module.enabled',
|
|
455
|
+
objectType: 'module',
|
|
456
|
+
objectId: moduleId,
|
|
457
|
+
stateAfter: { version: row.version },
|
|
458
|
+
});
|
|
459
|
+
await publishStateChanged(this.deps.redis, { moduleId, newState: 'installed' });
|
|
460
|
+
await registryCache.refreshFromDb(this.deps.em);
|
|
461
|
+
await resumeWorkersFor(moduleId);
|
|
462
|
+
return { moduleId, state: 'installed' };
|
|
463
|
+
}
|
|
464
|
+
finally {
|
|
465
|
+
await lease.release();
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
async disable(moduleId, opts = { cascade: false }) {
|
|
469
|
+
const entry = this.deps.registry.modules.get(moduleId);
|
|
470
|
+
if (!entry) {
|
|
471
|
+
throw new LifecycleError('unknown-module', `unknown module "${moduleId}"`);
|
|
472
|
+
}
|
|
473
|
+
const lease = await this.acquireLock();
|
|
474
|
+
try {
|
|
475
|
+
const row = await this.deps.em().findOne(ModuleRegistration, {
|
|
476
|
+
moduleId,
|
|
477
|
+
});
|
|
478
|
+
if (!row || row.state === 'uninstalled' || row.state === 'installing') {
|
|
479
|
+
throw new LifecycleError('wrong-state', `module "${moduleId}" is not installed (state=${row?.state ?? 'none'})`);
|
|
480
|
+
}
|
|
481
|
+
if (row.state === 'disabled') {
|
|
482
|
+
return { moduleId, state: 'already-disabled', cascade: false, cascaded: [] };
|
|
483
|
+
}
|
|
484
|
+
this.assertDeactivatable(moduleId);
|
|
485
|
+
// Find currently-enabled dependents, over both kinds of edge.
|
|
486
|
+
const directDependents = this.gatingDependentsOf(moduleId);
|
|
487
|
+
const em = this.deps.em();
|
|
488
|
+
const dependentRows = await em.find(ModuleRegistration, {
|
|
489
|
+
moduleId: { $in: directDependents },
|
|
490
|
+
state: 'installed',
|
|
491
|
+
});
|
|
492
|
+
const enabledDependents = dependentRows.map((r) => r.moduleId);
|
|
493
|
+
const cascaded = [];
|
|
494
|
+
if (enabledDependents.length > 0) {
|
|
495
|
+
if (!opts.cascade) {
|
|
496
|
+
await this.deps.auditLog.record({
|
|
497
|
+
actorAdminUserId: null,
|
|
498
|
+
action: 'module.dependency_blocked',
|
|
499
|
+
objectType: 'module',
|
|
500
|
+
objectId: moduleId,
|
|
501
|
+
stateAfter: { command: 'disable', conflicting: enabledDependents },
|
|
502
|
+
});
|
|
503
|
+
throw new LifecycleError('dependents-block', `cannot disable "${moduleId}": dependent enabled modules: ${enabledDependents.join(', ')}`, { dependents: enabledDependents });
|
|
504
|
+
}
|
|
505
|
+
// Cascade: disable transitive dependents in reverse-topo order, then this one.
|
|
506
|
+
const transitive = this.deps.registry.graph.transitiveDependentsOf(moduleId);
|
|
507
|
+
// Filter to those currently enabled.
|
|
508
|
+
const transitiveRows = await em.find(ModuleRegistration, {
|
|
509
|
+
moduleId: { $in: transitive },
|
|
510
|
+
state: 'installed',
|
|
511
|
+
});
|
|
512
|
+
const enabledTransitive = new Set(transitiveRows.map((r) => r.moduleId));
|
|
513
|
+
const order = this.deps.registry.graph
|
|
514
|
+
.reverseTopologicalOrder()
|
|
515
|
+
.filter((id) => enabledTransitive.has(id));
|
|
516
|
+
// Every module the cascade would reach is checked before the first
|
|
517
|
+
// write. A cascade that stops half-way through is worse than the
|
|
518
|
+
// refusal it was trying to avoid, and the dangerous shape here is a
|
|
519
|
+
// perfectly ordinary target whose dependent is the module the platform
|
|
520
|
+
// cannot run without.
|
|
521
|
+
for (const dep of order)
|
|
522
|
+
this.assertDeactivatable(dep);
|
|
523
|
+
// A cascade cannot cross an acknowledged edge, and refusing is the only
|
|
524
|
+
// honest answer: those edges are mutual by construction — that is why
|
|
525
|
+
// they are withheld from `dependencies` — so there is no reverse-topo
|
|
526
|
+
// position for them, and disabling the target anyway would leave the
|
|
527
|
+
// acknowledging module resolving a port whose owner is gone. Naming it
|
|
528
|
+
// lets the operator disable it explicitly first (feature 073, FR-008).
|
|
529
|
+
const cascadeSet = new Set([...order, moduleId]);
|
|
530
|
+
const stranded = new Set();
|
|
531
|
+
for (const member of cascadeSet) {
|
|
532
|
+
for (const dependent of this.acknowledgedDependentsOf(member)) {
|
|
533
|
+
if (!cascadeSet.has(dependent))
|
|
534
|
+
stranded.add(dependent);
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
const strandedRows = await em.find(ModuleRegistration, {
|
|
538
|
+
moduleId: { $in: [...stranded] },
|
|
539
|
+
state: 'installed',
|
|
540
|
+
});
|
|
541
|
+
if (strandedRows.length > 0) {
|
|
542
|
+
const blocking = strandedRows.map((r) => r.moduleId).sort();
|
|
543
|
+
await this.deps.auditLog.record({
|
|
544
|
+
actorAdminUserId: null,
|
|
545
|
+
action: 'module.dependency_blocked',
|
|
546
|
+
objectType: 'module',
|
|
547
|
+
objectId: moduleId,
|
|
548
|
+
stateAfter: { command: 'disable', conflicting: blocking },
|
|
549
|
+
});
|
|
550
|
+
throw new LifecycleError('dependents-block', `cannot disable "${moduleId}": these modules resolve a port it owns ` +
|
|
551
|
+
`through an acknowledged edge and cannot be cascaded: ${blocking.join(', ')}`, { dependents: blocking });
|
|
552
|
+
}
|
|
553
|
+
for (const dep of order) {
|
|
554
|
+
const depRow = await em.findOne(ModuleRegistration, { moduleId: dep });
|
|
555
|
+
if (!depRow || depRow.state !== 'installed')
|
|
556
|
+
continue;
|
|
557
|
+
depRow.state = 'disabled';
|
|
558
|
+
depRow.lastStateChangeAt = new Date();
|
|
559
|
+
await em.flush();
|
|
560
|
+
cascaded.push(dep);
|
|
561
|
+
await pauseWorkersFor(dep);
|
|
562
|
+
await this.deps.auditLog.record({
|
|
563
|
+
actorAdminUserId: null,
|
|
564
|
+
action: 'module.disabled',
|
|
565
|
+
objectType: 'module',
|
|
566
|
+
objectId: dep,
|
|
567
|
+
stateAfter: { version: depRow.version, cascade: true, cascaded: [] },
|
|
568
|
+
});
|
|
569
|
+
await publishStateChanged(this.deps.redis, { moduleId: dep, newState: 'disabled' });
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
// Disable the target itself.
|
|
573
|
+
row.state = 'disabled';
|
|
574
|
+
row.lastStateChangeAt = new Date();
|
|
575
|
+
await em.flush();
|
|
576
|
+
await pauseWorkersFor(moduleId);
|
|
577
|
+
await this.deps.auditLog.record({
|
|
578
|
+
actorAdminUserId: null,
|
|
579
|
+
action: 'module.disabled',
|
|
580
|
+
objectType: 'module',
|
|
581
|
+
objectId: moduleId,
|
|
582
|
+
stateAfter: {
|
|
583
|
+
version: row.version,
|
|
584
|
+
cascade: opts.cascade,
|
|
585
|
+
cascaded,
|
|
586
|
+
},
|
|
587
|
+
});
|
|
588
|
+
await publishStateChanged(this.deps.redis, { moduleId, newState: 'disabled' });
|
|
589
|
+
await registryCache.refreshFromDb(this.deps.em);
|
|
590
|
+
return { moduleId, state: 'disabled', cascade: opts.cascade, cascaded };
|
|
591
|
+
}
|
|
592
|
+
finally {
|
|
593
|
+
await lease.release();
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
/**
|
|
597
|
+
* The platform axis honours `nonDeactivatable` — D-36a item 3, issue #37.
|
|
598
|
+
*
|
|
599
|
+
* Until this existed nothing on this axis read the field for any module, so
|
|
600
|
+
* `module:disable auth` proceeded and the declaration was a comment that
|
|
601
|
+
* looked like a guard.
|
|
602
|
+
*
|
|
603
|
+
* It guards **every platform-axis withdrawal**: `disable` (the target and
|
|
604
|
+
* every member of a cascade) and, since D-69 / issue #145, `uninstall` on the
|
|
605
|
+
* soft and hard path alike. Uninstall is disable plus the settings sweep plus
|
|
606
|
+
* the schema revert, so the same declaration has to reach it.
|
|
607
|
+
*
|
|
608
|
+
* There is deliberately **no `--force`**. `uninstall --hard --force` guards
|
|
609
|
+
* data loss, a consequence an operator can weigh at the prompt; this guards
|
|
610
|
+
* a deployment that can no longer authenticate the operator who would undo
|
|
611
|
+
* it, which they cannot. If the declaration is wrong for a module, the fix is
|
|
612
|
+
* the manifest, which is reviewed. The operator axis already refuses the same
|
|
613
|
+
* flip (`activation.commands.ts`), so both axes agree.
|
|
614
|
+
*
|
|
615
|
+
* It reads the **manifest**, so a registry row with no manifest — an orphan —
|
|
616
|
+
* is not covered, deliberately: cleaning those up is the one job `uninstall`
|
|
617
|
+
* has that nothing else does. `test/unit/_lifecycle/orchestrator.test.ts`
|
|
618
|
+
* pins that carve-out.
|
|
619
|
+
*/
|
|
620
|
+
/**
|
|
621
|
+
* Refuse an install whose arrival closes a dependency cycle, naming every
|
|
622
|
+
* member (feature 081, FR-012).
|
|
623
|
+
*
|
|
624
|
+
* A cycle is not thrown by `orderMigrations`: the manifest graph is the whole
|
|
625
|
+
* cross-module migration order now, and a manifest can arrive from an
|
|
626
|
+
* installed package, so refusing there would let one stranger's declaration
|
|
627
|
+
* stop a shop's own schema from migrating. It comes back as a diagnostic with
|
|
628
|
+
* three readers instead, and this is the third — the moment the loop would be
|
|
629
|
+
* *created* is the one moment where refusing costs nothing, because nothing
|
|
630
|
+
* downstream of it exists yet.
|
|
631
|
+
*
|
|
632
|
+
* The graph is the **installed** set plus the arriving module, and only a
|
|
633
|
+
* component holding the arriving module is refused: a loop between two other
|
|
634
|
+
* modules is somebody else's problem, and blocking every later install on it
|
|
635
|
+
* would reproduce the platform-wide stall the no-throw rule exists to
|
|
636
|
+
* prevent. It reuses `moduleDependencyCycles` rather than
|
|
637
|
+
* `ModuleDepGraph.hasCycle` — one walk, so the install refusal and the
|
|
638
|
+
* migration order cannot disagree about what a cycle is, and the operator
|
|
639
|
+
* reads the same member list in both. The walk is `dep-graph.ts`'s since
|
|
640
|
+
* D-160.11; `src/db/migration-order.ts` reads the same function and adds its
|
|
641
|
+
* own wording.
|
|
642
|
+
*/
|
|
643
|
+
async assertInstallClosesNoCycle(moduleId, installed) {
|
|
644
|
+
const nodes = new Set([...installed, moduleId]);
|
|
645
|
+
const graph = new Map();
|
|
646
|
+
for (const id of nodes) {
|
|
647
|
+
// An orphan registration row — installed, no manifest — declares nothing
|
|
648
|
+
// we can read, so it contributes a node and no edge.
|
|
649
|
+
const declared = this.deps.registry.modules.get(id)?.manifest.dependencies ?? [];
|
|
650
|
+
graph.set(id, declared.filter((dependency) => nodes.has(dependency)));
|
|
651
|
+
}
|
|
652
|
+
const closed = moduleDependencyCycles(graph).find((members) => members.includes(moduleId));
|
|
653
|
+
if (!closed)
|
|
654
|
+
return;
|
|
655
|
+
await this.deps.auditLog.record({
|
|
656
|
+
actorAdminUserId: null,
|
|
657
|
+
action: 'module.dependency_blocked',
|
|
658
|
+
objectType: 'module',
|
|
659
|
+
objectId: moduleId,
|
|
660
|
+
stateAfter: { command: 'install', cycle: closed },
|
|
661
|
+
});
|
|
662
|
+
throw new LifecycleError('manifest-cycle', `module "${moduleId}" cannot be installed: with it, the modules ` +
|
|
663
|
+
`[${closed.join(', ')}] depend on each other in a loop. Nothing can be ` +
|
|
664
|
+
`installed, migrated or removed before the modules it depends on, so a loop has ` +
|
|
665
|
+
`no order at all. Fix the \`dependencies\` array in one of them.`, { moduleId, cycle: closed });
|
|
666
|
+
}
|
|
667
|
+
assertDeactivatable(moduleId) {
|
|
668
|
+
const activation = this.deps.registry.modules.get(moduleId)?.manifest.activation;
|
|
669
|
+
if (!activation || !('nonDeactivatable' in activation))
|
|
670
|
+
return;
|
|
671
|
+
throw new LifecycleError('non-deactivatable', `module "${moduleId}" declares itself non-deactivatable: ${activation.reason}`, { moduleId, reason: activation.reason });
|
|
672
|
+
}
|
|
673
|
+
// -------------------------------------------------------------------------
|
|
674
|
+
// STATUS
|
|
675
|
+
// -------------------------------------------------------------------------
|
|
676
|
+
async status() {
|
|
677
|
+
const rows = await this.deps.em().find(ModuleRegistration, {});
|
|
678
|
+
const byId = new Map(rows.map((r) => [r.moduleId, r]));
|
|
679
|
+
const out = [];
|
|
680
|
+
const enabledSet = await this.installedSet();
|
|
681
|
+
const seen = new Set();
|
|
682
|
+
for (const [id, entry] of this.deps.registry.modules) {
|
|
683
|
+
seen.add(id);
|
|
684
|
+
const row = byId.get(id);
|
|
685
|
+
const flags = [];
|
|
686
|
+
if (row && entry.manifest.version !== row.version) {
|
|
687
|
+
flags.push('pending-upgrade');
|
|
688
|
+
}
|
|
689
|
+
const missing = this.deps.registry.graph.unresolvedDependenciesOf(id, new Set([...byId.entries()].filter(([, r]) => r.state !== 'uninstalled').map(([k]) => k)));
|
|
690
|
+
if (missing.length > 0)
|
|
691
|
+
flags.push('dep-missing');
|
|
692
|
+
if (row?.state === 'installed') {
|
|
693
|
+
const depDisabled = entry.manifest.dependencies.some((d) => !enabledSet.has(d));
|
|
694
|
+
if (depDisabled)
|
|
695
|
+
flags.push('dep-disabled');
|
|
696
|
+
}
|
|
697
|
+
out.push({
|
|
698
|
+
id,
|
|
699
|
+
name: entry.manifest.name,
|
|
700
|
+
description: entry.manifest.description ?? null,
|
|
701
|
+
version: {
|
|
702
|
+
registered: row?.version ?? null,
|
|
703
|
+
onDisk: entry.manifest.version,
|
|
704
|
+
},
|
|
705
|
+
state: (row?.state ?? 'not-installed'),
|
|
706
|
+
dependencies: entry.manifest.dependencies,
|
|
707
|
+
flags,
|
|
708
|
+
installedAt: row?.installedAt.toISOString() ?? null,
|
|
709
|
+
lastStateChangeAt: row?.lastStateChangeAt.toISOString() ?? null,
|
|
710
|
+
});
|
|
711
|
+
}
|
|
712
|
+
// Orphan rows: registry has it but no manifest on disk.
|
|
713
|
+
for (const [id, row] of byId) {
|
|
714
|
+
if (seen.has(id))
|
|
715
|
+
continue;
|
|
716
|
+
out.push({
|
|
717
|
+
id,
|
|
718
|
+
name: id,
|
|
719
|
+
description: null,
|
|
720
|
+
version: {
|
|
721
|
+
registered: row.version,
|
|
722
|
+
onDisk: null,
|
|
723
|
+
},
|
|
724
|
+
state: row.state,
|
|
725
|
+
dependencies: [],
|
|
726
|
+
flags: ['orphan'],
|
|
727
|
+
installedAt: row.installedAt.toISOString(),
|
|
728
|
+
lastStateChangeAt: row.lastStateChangeAt.toISOString(),
|
|
729
|
+
});
|
|
730
|
+
}
|
|
731
|
+
out.sort((a, b) => a.id.localeCompare(b.id));
|
|
732
|
+
return out;
|
|
733
|
+
}
|
|
734
|
+
// -------------------------------------------------------------------------
|
|
735
|
+
// Internals
|
|
736
|
+
// -------------------------------------------------------------------------
|
|
737
|
+
async acquireLock() {
|
|
738
|
+
try {
|
|
739
|
+
return await acquireLifecycleLock(this.deps.redis);
|
|
740
|
+
}
|
|
741
|
+
catch (err) {
|
|
742
|
+
throw new LifecycleError('lock-busy', `lifecycle lock is held by another process; retry shortly`, { cause: err instanceof Error ? err.message : String(err) });
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
async upsertRegistration(moduleId, patch) {
|
|
746
|
+
const em = this.deps.em();
|
|
747
|
+
const row = await em.findOne(ModuleRegistration, { moduleId });
|
|
748
|
+
if (row) {
|
|
749
|
+
Object.assign(row, patch);
|
|
750
|
+
await em.flush();
|
|
751
|
+
return;
|
|
752
|
+
}
|
|
753
|
+
const created = em.create(ModuleRegistration, {
|
|
754
|
+
moduleId,
|
|
755
|
+
state: patch.state ?? 'installing',
|
|
756
|
+
version: patch.version ?? '0.0.0',
|
|
757
|
+
installedAt: patch.installedAt ?? new Date(),
|
|
758
|
+
lastStateChangeAt: patch.lastStateChangeAt ?? new Date(),
|
|
759
|
+
lastInstallFailedAt: patch.lastInstallFailedAt ?? null,
|
|
760
|
+
lastInstallError: patch.lastInstallError ?? null,
|
|
761
|
+
// A row this orchestrator creates is by definition not boot convergence's,
|
|
762
|
+
// so `null` is the only value this branch can write. Stated rather than
|
|
763
|
+
// left to the entity's field default, like every other column here.
|
|
764
|
+
bootConvergedAt: patch.bootConvergedAt ?? null,
|
|
765
|
+
});
|
|
766
|
+
await em.persistAndFlush(created);
|
|
767
|
+
}
|
|
768
|
+
/** Set of moduleIds whose state is 'installed' OR 'disabled' (i.e. present, not removed). */
|
|
769
|
+
async installedSet() {
|
|
770
|
+
const rows = await this.deps.em().find(ModuleRegistration, {
|
|
771
|
+
state: { $in: ['installed', 'disabled'] },
|
|
772
|
+
});
|
|
773
|
+
return new Set(rows.map((r) => r.moduleId));
|
|
774
|
+
}
|
|
775
|
+
/**
|
|
776
|
+
* Set of moduleIds that are currently ENABLED (state='installed'). Distinct
|
|
777
|
+
* from {@link installedSet}, which also counts disabled modules as present.
|
|
778
|
+
* Enabling a module requires every dependency to be enabled — a merely
|
|
779
|
+
* present-but-disabled dependency must NOT satisfy the check.
|
|
780
|
+
*/
|
|
781
|
+
async enabledSet() {
|
|
782
|
+
const rows = await this.deps.em().find(ModuleRegistration, {
|
|
783
|
+
state: 'installed',
|
|
784
|
+
});
|
|
785
|
+
return new Set(rows.map((r) => r.moduleId));
|
|
786
|
+
}
|
|
787
|
+
async installedDependentsOf(moduleId) {
|
|
788
|
+
const rows = await this.deps.em().find(ModuleRegistration, {
|
|
789
|
+
state: { $in: ['installed', 'disabled'] },
|
|
790
|
+
});
|
|
791
|
+
const installed = new Set(rows.map((r) => r.moduleId));
|
|
792
|
+
return this.gatingDependentsOf(moduleId).filter((id) => installed.has(id));
|
|
793
|
+
}
|
|
794
|
+
/**
|
|
795
|
+
* Direct dependents over **both** kinds of edge — feature 073, FR-008.
|
|
796
|
+
*
|
|
797
|
+
* `registry.graph` is built from `manifest.dependencies` alone because it has
|
|
798
|
+
* to be a DAG: it computes the install order. That leaves out the handful of
|
|
799
|
+
* real port edges the manifests withhold for exactly that reason, and reading
|
|
800
|
+
* only the DAG here is what let the two presence axes disagree — the operator
|
|
801
|
+
* axis refuses to switch `price_lists` off under `catalog`, so this one must
|
|
802
|
+
* too, or the same platform answers the same question two ways depending on
|
|
803
|
+
* which door the operator used.
|
|
804
|
+
*/
|
|
805
|
+
gatingDependentsOf(moduleId) {
|
|
806
|
+
return [
|
|
807
|
+
...new Set([
|
|
808
|
+
...this.deps.registry.graph.dependentsOf(moduleId),
|
|
809
|
+
...this.acknowledgedDependentsOf(moduleId),
|
|
810
|
+
]),
|
|
811
|
+
].sort();
|
|
812
|
+
}
|
|
813
|
+
/** The modules that acknowledge a withheld port edge onto `moduleId`. */
|
|
814
|
+
acknowledgedDependentsOf(moduleId) {
|
|
815
|
+
const out = [];
|
|
816
|
+
for (const [id, entry] of this.deps.registry.modules) {
|
|
817
|
+
const acknowledged = entry.manifest.acknowledgedDependencies ?? [];
|
|
818
|
+
if (acknowledged.some((edge) => edge.moduleId === moduleId))
|
|
819
|
+
out.push(id);
|
|
820
|
+
}
|
|
821
|
+
return out.sort();
|
|
822
|
+
}
|
|
823
|
+
async migrator() {
|
|
824
|
+
const accessor = this.deps.migratorFor ?? (async (orm) => orm.getMigrator());
|
|
825
|
+
return accessor(this.deps.orm);
|
|
826
|
+
}
|
|
827
|
+
/**
|
|
828
|
+
* Revert the migrations a module owns, newest first.
|
|
829
|
+
*
|
|
830
|
+
* Ownership comes from an injected {@link MigrationOwnership} — a data-only
|
|
831
|
+
* value, because the orchestrator must never pull in the ORM config — and the
|
|
832
|
+
* migration name is always `cls.name`, which is exactly what
|
|
833
|
+
* `mikro_orm_migrations.name` stores. Ordering is ascending class name, which
|
|
834
|
+
* for a single module is the resolved execution order: `orderMigrations`
|
|
835
|
+
* emits a module's migrations contiguously and in ascending timestamp order,
|
|
836
|
+
* and the timestamp is the head of the name
|
|
837
|
+
* (specs/081-per-module-migration-order/contracts/ordering-algorithm.md,
|
|
838
|
+
* invariants J2 and J3).
|
|
839
|
+
*
|
|
840
|
+
* ## The empty case is two different answers, and only one of them is fine
|
|
841
|
+
*
|
|
842
|
+
* This used to warn and return `[]` whenever the filter came back empty, so
|
|
843
|
+
* an installed extension package — whose migrations reach the order at
|
|
844
|
+
* runtime and never reach the committed core registry (D-119/D-155) — **hard
|
|
845
|
+
* uninstalled to a warning and left its table in the database**. That is data
|
|
846
|
+
* left behind by an operation the operator believes removed it, on precisely
|
|
847
|
+
* the population the mechanism exists for (D-155.3(c)).
|
|
848
|
+
*
|
|
849
|
+
* So the two are separated at the seam that knows the difference:
|
|
850
|
+
* `migrationNamesFor` answers `[]` for *"this module owns no migration"* —
|
|
851
|
+
* ordinary, and 39 core modules are in it — and `null` for *"the registry I
|
|
852
|
+
* was handed cannot answer for this module"*. The first is logged; the second
|
|
853
|
+
* throws, because reverting nothing and reporting success is the one outcome
|
|
854
|
+
* a hard uninstall may not have.
|
|
855
|
+
*
|
|
856
|
+
* A failing revert still stops the loop rather than widening the gap.
|
|
857
|
+
*/
|
|
858
|
+
/**
|
|
859
|
+
* The lifecycle participants this operation must run — or a refusal.
|
|
860
|
+
*
|
|
861
|
+
* The same two-answers distinction {@link revertMigrationsFor} makes, on the
|
|
862
|
+
* other half of the same operation. `[]` is *"I enumerated the modules and
|
|
863
|
+
* none keeps a projection of the manifest set"* and is an ordinary answer a
|
|
864
|
+
* fixture registry gives; `null` is *"I cannot enumerate them"*, and an
|
|
865
|
+
* install performed on that answer reconciles nothing and reports success —
|
|
866
|
+
* which is precisely the shape this task closed on the CLI side, so it is not
|
|
867
|
+
* re-introduced here as a default.
|
|
868
|
+
*/
|
|
869
|
+
participantsOrRefuse(operation, moduleId) {
|
|
870
|
+
const participants = this.deps.registry.participants;
|
|
871
|
+
if (participants === null) {
|
|
872
|
+
throw new Error(`[${operation}] refusing to ${operation} "${moduleId}": the manifest registry this ` +
|
|
873
|
+
`orchestrator was given cannot enumerate the lifecycle participants, so it cannot ` +
|
|
874
|
+
`refresh the projections the manifest set drives — the module's translation bundles ` +
|
|
875
|
+
`and its command-palette actions. Performing the operation anyway would report ` +
|
|
876
|
+
`success for an install that installed no bundle, or for a hard uninstall that left ` +
|
|
877
|
+
`rows nothing will ever remove. Build the registry with buildStaticRegistry() or ` +
|
|
878
|
+
`discoverManifests(), both of which collect participants; a registry literal must ` +
|
|
879
|
+
`say \`participants: []\` when it deliberately carries none.`);
|
|
880
|
+
}
|
|
881
|
+
return participants;
|
|
882
|
+
}
|
|
883
|
+
async revertMigrationsFor(moduleId) {
|
|
884
|
+
const ownership = this.deps.migrationOwnership;
|
|
885
|
+
if (ownership === undefined) {
|
|
886
|
+
throw new Error(`[uninstall] refusing to hard-uninstall "${moduleId}": this orchestrator was given no ` +
|
|
887
|
+
`'migrationOwnership', so it cannot enumerate any module's migration chain and ` +
|
|
888
|
+
`cannot say whether there is anything to revert. Reverting nothing here would drop ` +
|
|
889
|
+
`the module's registration and leave its tables in the database, which is the one ` +
|
|
890
|
+
`outcome a hard uninstall may not have. A composition root passes ` +
|
|
891
|
+
`(await configuredMigrations()).ownership; a platform command that composes nothing ` +
|
|
892
|
+
`passes coreMigrationOwnership() and is then refused for a package module, which is ` +
|
|
893
|
+
`the honest answer — see src/db/configured-migrations.ts.`);
|
|
894
|
+
}
|
|
895
|
+
const names = ownership.migrationNamesFor(moduleId);
|
|
896
|
+
if (names === null) {
|
|
897
|
+
throw new Error(`[uninstall] refusing to hard-uninstall "${moduleId}": the migration registry this ` +
|
|
898
|
+
`orchestrator was given covers ${ownership.coveredModuleIds.size} module(s) and not ` +
|
|
899
|
+
`that one, so it cannot enumerate the module's chain and cannot say whether there is ` +
|
|
900
|
+
`anything to revert. Reverting nothing here would drop the module's registration and ` +
|
|
901
|
+
`leave its tables in the database, which is the one outcome a hard uninstall may not ` +
|
|
902
|
+
`have. An installed extension package is the case this happens in: its migrations are ` +
|
|
903
|
+
`discovered at runtime (D-119/D-155), so the orchestrator must be constructed with the ` +
|
|
904
|
+
`merged 'migrationOwnership' — see src/db/configured-migrations.ts.`);
|
|
905
|
+
}
|
|
906
|
+
if (names.length === 0) {
|
|
907
|
+
this.log.warn(`[uninstall] module "${moduleId}" owns no registered migration; ` +
|
|
908
|
+
`skipping migration revert. Hard-uninstall relies on the uninstall hook.`);
|
|
909
|
+
return [];
|
|
910
|
+
}
|
|
911
|
+
const migrator = await this.migrator();
|
|
912
|
+
const reverted = [];
|
|
913
|
+
// Copied before reversing: the ownership value is shared for the process
|
|
914
|
+
// and `reverse()` mutates in place.
|
|
915
|
+
for (const name of [...names].reverse()) {
|
|
916
|
+
try {
|
|
917
|
+
const result = await migrator.down({ migrations: [name] });
|
|
918
|
+
for (const m of result)
|
|
919
|
+
reverted.push(m.name);
|
|
920
|
+
}
|
|
921
|
+
catch (err) {
|
|
922
|
+
this.log.warn(`[uninstall] failed to revert migration ${name}: ` +
|
|
923
|
+
(err instanceof Error ? err.message : String(err)));
|
|
924
|
+
// Stop reverting further migrations to avoid making the gap worse.
|
|
925
|
+
break;
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
return reverted;
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
// ---------------------------------------------------------------------------
|
|
932
|
+
// helpers
|
|
933
|
+
// ---------------------------------------------------------------------------
|
|
934
|
+
/**
|
|
935
|
+
* The orchestrator's logger, tagged with the participant that is writing.
|
|
936
|
+
*
|
|
937
|
+
* A participant runs on *another* module's install, so an untagged line reads
|
|
938
|
+
* as the installed module's own and sends a reader to the wrong `manifest.ts`.
|
|
939
|
+
*/
|
|
940
|
+
function taggedLogger(log, moduleId) {
|
|
941
|
+
return {
|
|
942
|
+
info: (msg) => log.info(`[${moduleId}] ${msg}`),
|
|
943
|
+
warn: (msg) => log.warn(`[${moduleId}] ${msg}`),
|
|
944
|
+
error: (msg) => log.error(`[${moduleId}] ${msg}`),
|
|
945
|
+
};
|
|
946
|
+
}
|
|
947
|
+
function consoleLogger() {
|
|
948
|
+
return {
|
|
949
|
+
info: (m) => console.log(m),
|
|
950
|
+
warn: (m) => console.warn(m),
|
|
951
|
+
error: (m) => console.error(m),
|
|
952
|
+
};
|
|
953
|
+
}
|
|
954
|
+
//# sourceMappingURL=orchestrator.js.map
|