@gfdelarue/testkit 0.4.5 → 0.6.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/README.md +724 -495
- package/bin/testkit-windows-owner-arm64.exe +0 -0
- package/bin/testkit-windows-owner-x64.exe +0 -0
- package/dist/app/bugs/ci-report.js +2 -2
- package/dist/app/bugs/ci-summary.js +3 -3
- package/dist/app/bugs/draft.js +3 -3
- package/dist/app/bugs/file.js +1 -1
- package/dist/app/bugs/gate.js +1 -1
- package/dist/app/bugs/guide.js +3 -3
- package/dist/app/bugs/issue.js +4 -4
- package/dist/app/bugs/known-failures.js +1 -1
- package/dist/app/bugs/record.js +1 -1
- package/dist/app/bugs/run-reading.js +1 -1
- package/dist/app/bugs/supplied-reproduce.js +1 -0
- package/dist/app/campaigns/campaign.js +1 -1
- package/dist/app/campaigns/index.js +2 -2
- package/dist/app/cleanup/index.js +1 -1
- package/dist/app/db-schema/prepare.js +1 -1
- package/dist/app/destroy/index.js +1 -1
- package/dist/app/discover/index.js +2 -2
- package/dist/app/doctor/index.js +1 -1
- package/dist/app/environments/config-driven.js +1 -1
- package/dist/app/environments/environment-file.js +1 -0
- package/dist/app/environments/file-setup.js +1 -0
- package/dist/app/environments/routing.js +1 -1
- package/dist/app/environments/static-values.js +1 -0
- package/dist/app/environments/supplied-input.js +1 -0
- package/dist/app/environments/supplied.js +1 -1
- package/dist/app/environments/task-databases.js +2 -0
- package/dist/app/inspect/artifacts.js +1 -1
- package/dist/app/inspect/index.js +1 -1
- package/dist/app/lint/index.js +1 -1
- package/dist/app/lint/native.js +1 -1
- package/dist/app/lint/options.js +1 -1
- package/dist/app/lint/rules/engine-imports.js +1 -1
- package/dist/app/lint/rules/syntax.js +1 -1
- package/dist/app/lint/rules/unit.js +1 -1
- package/dist/app/reports/prepare.js +1 -1
- package/dist/app/run/caller-redaction.js +1 -0
- package/dist/app/run/engine-execution-failures.js +1 -1
- package/dist/app/run/engine-execution-reporting.js +1 -1
- package/dist/app/run/engine-execution.js +1 -1
- package/dist/app/run/engine-orchestration.js +1 -1
- package/dist/app/run/environment.js +1 -1
- package/dist/app/run/failure-details.js +1 -1
- package/dist/app/run/live-run.js +1 -1
- package/dist/app/run/orchestrator.js +1 -1
- package/dist/app/run/planning.js +1 -1
- package/dist/app/run/request.js +1 -1
- package/dist/app/run/results.js +1 -1
- package/dist/app/run/run-events.js +1 -1
- package/dist/app/run/run-finalization.js +1 -1
- package/dist/app/run/scheduler/estimates.js +1 -1
- package/dist/app/run/scheduler/identity.js +1 -1
- package/dist/app/run/scheduler/index.js +1 -1
- package/dist/app/run/setup/runtime-node-setup-protocol.js +1 -1
- package/dist/app/run/setup/runtime-node-setup-worker.js +1 -1
- package/dist/app/run/setup/runtime-node-setup.js +2 -2
- package/dist/app/run/task-batches.js +1 -1
- package/dist/app/run/worker-loop.js +1 -1
- package/dist/app/status/model.js +1 -1
- package/dist/app/status/runs.js +1 -1
- package/dist/app/typecheck/index.js +4 -4
- package/dist/cli/command-catalog.js +1 -1
- package/dist/cli/command-flags.js +1 -1
- package/dist/cli/commands/artifacts.js +2 -2
- package/dist/cli/commands/bug/file.js +2 -2
- package/dist/cli/commands/bug/record.js +2 -2
- package/dist/cli/commands/discover.js +1 -1
- package/dist/cli/commands/guard/unsafe-entrypoint.js +1 -1
- package/dist/cli/commands/run.js +2 -2
- package/dist/cli/commands/typecheck.js +1 -1
- package/dist/cli/commands/workspace.js +1 -1
- package/dist/cli/framework/errors.js +1 -1
- package/dist/cli/framework/execute.js +7 -6
- package/dist/cli/framework/parser.js +1 -1
- package/dist/cli/operations/run/operation.js +2 -2
- package/dist/cli/renderers/bug/text.js +1 -1
- package/dist/cli/renderers/discover/report.js +1 -1
- package/dist/cli/renderers/run/events.js +2 -2
- package/dist/cli/renderers/run/formatting.js +1 -1
- package/dist/cli/renderers/run/status.js +1 -1
- package/dist/cli/renderers/run/text-reporter.js +24 -22
- package/dist/cli/terminal/colors.js +1 -1
- package/dist/cli/terminal/layout.js +2 -2
- package/dist/config/authoring/database-fixture-discovery.js +1 -0
- package/dist/config/authoring/http-methods.js +1 -1
- package/dist/config/authoring/index.js +1 -1
- package/dist/config/loader/config-loader.js +1 -1
- package/dist/config/loader/decode-discovery.js +1 -1
- package/dist/config/loader/decode-execution.js +1 -1
- package/dist/config/loader/dependencies.js +1 -1
- package/dist/config/loader/discover-tests.js +1 -1
- package/dist/config/loader/discovery-summary.js +1 -1
- package/dist/config/loader/discovery.js +2 -2
- package/dist/config/loader/env.js +1 -1
- package/dist/config/loader/file-metadata.js +1 -1
- package/dist/config/loader/groups.js +2 -1
- package/dist/config/loader/index.js +1 -1
- package/dist/config/loader/load.js +1 -1
- package/dist/config/loader/managed-configs.js +1 -1
- package/dist/config/loader/owned-discovery.js +1 -0
- package/dist/config/loader/path-policy.js +1 -1
- package/dist/config/loader/runtime-isolation.js +1 -1
- package/dist/config/loader/runtime.js +1 -1
- package/dist/config/loader/skip-config.js +1 -1
- package/dist/config/loader/task-policy.js +1 -0
- package/dist/config/loader/test-selection.js +1 -1
- package/dist/config/loader/validation.js +1 -1
- package/dist/database/admin.js +2 -2
- package/dist/database/bindings.js +1 -0
- package/dist/database/cleanup.js +1 -1
- package/dist/database/configured-steps.js +1 -1
- package/dist/database/docker-metadata.js +3 -3
- package/dist/database/docker.js +3 -3
- package/dist/database/index.js +1 -1
- package/dist/database/local-postgres.js +1 -1
- package/dist/database/naming.js +1 -1
- package/dist/database/postgres/connection-check.js +1 -0
- package/dist/database/postgres/protocol.js +2 -0
- package/dist/database/schema-operations.js +1 -1
- package/dist/database/schema-source.js +3 -3
- package/dist/database/source-schema-env.js +1 -0
- package/dist/database/state-files.js +1 -1
- package/dist/database/state.js +1 -1
- package/dist/database/steps.js +3 -3
- package/dist/domain/file-timeout.js +1 -1
- package/dist/domain/identifiers.js +1 -1
- package/dist/domain/retired-test-types.js +1 -0
- package/dist/domain/runtime-isolation-contract.js +1 -1
- package/dist/domain/suite-selection.js +1 -1
- package/dist/domain/test-types.js +1 -1
- package/dist/engines/adapters/evidence-files.js +1 -1
- package/dist/engines/adapters/execution.js +1 -1
- package/dist/engines/adapters/json-bounds.js +2 -0
- package/dist/engines/adapters/native-report.js +1 -1
- package/dist/engines/api/execution.js +1 -1
- package/dist/engines/api/index.js +1 -1
- package/dist/engines/api/runtime-artifact.js +1 -1
- package/dist/engines/catalog.js +1 -1
- package/dist/engines/contracts/environment-values.js +1 -0
- package/dist/engines/definitions.js +1 -1
- package/dist/engines/environment/handles.js +1 -0
- package/dist/engines/environment/index.js +1 -0
- package/dist/engines/factory.js +1 -1
- package/dist/engines/installations/node-vitest.js +1 -1
- package/dist/engines/installations/package-resolution.js +1 -1
- package/dist/engines/installations/playwright.js +1 -1
- package/dist/engines/managed-module-resolution.js +8 -4
- package/dist/engines/manager.js +1 -1
- package/dist/engines/node-vitest/adapter/adapter.js +1 -1
- package/dist/engines/node-vitest/adapter/driver.js +2 -0
- package/dist/engines/node-vitest/adapter/evidence.js +2 -2
- package/dist/engines/node-vitest/adapter/failure-details.js +4 -2
- package/dist/engines/node-vitest/adapter/reporter.js +1 -1
- package/dist/engines/node-vitest/adapter/workspace.js +17 -195
- package/dist/engines/node-vitest/inventory/index.js +2 -0
- package/dist/engines/node-vitest/inventory/list-files.js +3 -0
- package/dist/engines/node-vitest/runtime/data/index.js +1 -1
- package/dist/engines/node-vitest/runtime/data/report.js +5 -6
- package/dist/engines/node-vitest/runtime/data/scenario.js +2 -2
- package/dist/engines/node-vitest/runtime/runtime-input.js +1 -1
- package/dist/engines/node-vitest/selection.js +1 -0
- package/dist/engines/playwright/adapter/adapter.js +1 -1
- package/dist/engines/playwright/adapter/config.js +3 -3
- package/dist/engines/playwright/adapter/evidence-validation.js +1 -1
- package/dist/engines/playwright/adapter/evidence.js +1 -1
- package/dist/engines/playwright/adapter/reporter.js +1 -1
- package/dist/engines/playwright/runtime/config-file.js +1 -1
- package/dist/engines/playwright/runtime/config.js +1 -1
- package/dist/engines/playwright/runtime/managed-test.js +1 -0
- package/dist/engines/playwright/runtime/runtime-input.js +29 -10
- package/dist/engines/runtime-input-file.js +1 -1
- package/dist/environment/lifecycle-controller.js +1 -1
- package/dist/environment/lifecycle.js +1 -1
- package/dist/environment/ready-environment.js +1 -1
- package/dist/environment/run-environment.js +1 -1
- package/dist/environment/run-stopping.js +1 -1
- package/dist/environment/runtime-contexts.js +1 -1
- package/dist/environment/runtime-manager.js +1 -1
- package/dist/environment/runtime-planning.js +1 -1
- package/dist/environment/runtime-preparation.js +1 -1
- package/dist/environment/services.js +3 -3
- package/dist/environment/template.js +1 -1
- package/dist/evidence/artifact-config.js +1 -1
- package/dist/evidence/artifacts.js +2 -2
- package/dist/evidence/bundles.js +1 -1
- package/dist/evidence/environment-record.js +1 -1
- package/dist/evidence/index.js +1 -1
- package/dist/evidence/maintenance.js +1 -0
- package/dist/evidence/read-run.js +1 -1
- package/dist/evidence/reading.js +1 -1
- package/dist/evidence/redaction.js +4 -0
- package/dist/evidence/reporting.js +1 -1
- package/dist/evidence/run-model.js +1 -0
- package/dist/evidence/storage.js +2 -2
- package/dist/evidence/timing.js +1 -1
- package/dist/kernel/error-serialization.js +1 -1
- package/dist/kernel/known-keys.js +1 -0
- package/dist/kernel/redaction-targets.js +1 -0
- package/dist/kernel/redaction.js +4 -1
- package/dist/kernel/unknown-values.js +1 -1
- package/dist/process/environment.js +1 -1
- package/dist/process/execa-bundle.json +3 -3
- package/dist/process/files/json-file.js +1 -1
- package/dist/process/host/platform.js +1 -1
- package/dist/process/host-process.js +1 -1
- package/dist/process/index.js +1 -1
- package/dist/process/locks.js +4 -4
- package/dist/process/port-listener.js +2 -0
- package/dist/process/process-group.js +1 -1
- package/dist/process/process.js +32 -32
- package/dist/process/readiness.js +1 -1
- package/dist/process/ready-deadline.js +1 -0
- package/dist/process/runs/ownership.js +1 -1
- package/dist/process/runs/stop.js +1 -1
- package/dist/process/service-processes.js +1 -1
- package/dist/process/windows-cli-owner.js +3 -3
- package/dist/process/windows-native.js +1 -1
- package/dist/repo/fingerprint-policy.js +1 -1
- package/dist/repo/managed-git.js +1 -1
- package/dist/repo/physical-tree.js +1 -1
- package/dist/repo/state.js +4 -4
- package/dist/steps/build-config.js +1 -1
- package/dist/steps/configured-step-testkit-modules.js +1 -1
- package/dist/steps/configured-steps.js +1 -1
- package/dist/steps/environment.js +1 -1
- package/dist/steps/index.js +1 -1
- package/dist/toolchains/index.js +3 -3
- package/dist/toolchains/semver.js +1 -1
- package/dist/toolchains/vitest/index.js +1 -1
- package/dist/tracker/redaction.js +1 -2
- package/dist/{config/runtime/build-assets.d.ts → types/config-build-assets.d.ts} +5 -3
- package/dist/{database/steps.d.ts → types/config-database-steps.d.ts} +7 -7
- package/dist/{config/runtime/next-runtime-tsconfig.d.ts → types/config-next-runtime-tsconfig.d.ts} +5 -3
- package/dist/types/config.d.ts +422 -0
- package/dist/types/drizzle.d.ts +13 -0
- package/dist/{config/runtime/env/index.d.ts → types/env.d.ts} +22 -20
- package/dist/types/environment.d.ts +46 -0
- package/dist/types/index.d.ts +21 -0
- package/dist/types/node-vitest-data.d.ts +191 -0
- package/dist/types/playwright.d.ts +130 -0
- package/dist/types/tooling-vitest.d.ts +13 -0
- package/dist/types/workspace.d.ts +633 -0
- package/docs/bug-records.md +35 -40
- package/docs/engines.md +259 -0
- package/docs/release-notes.md +2116 -0
- package/docs/test-types.md +37 -85
- package/package.json +22 -32
- package/testkit-config-schema.md +391 -260
- package/dist/app/bugs/areas.d.ts +0 -38
- package/dist/app/bugs/assessment.d.ts +0 -9
- package/dist/app/bugs/categories.d.ts +0 -41
- package/dist/app/bugs/ci-report.d.ts +0 -21
- package/dist/app/bugs/ci-summary.d.ts +0 -13
- package/dist/app/bugs/draft.d.ts +0 -70
- package/dist/app/bugs/facts.d.ts +0 -49
- package/dist/app/bugs/file.d.ts +0 -45
- package/dist/app/bugs/gate.d.ts +0 -22
- package/dist/app/bugs/guide.d.ts +0 -20
- package/dist/app/bugs/issue.d.ts +0 -18
- package/dist/app/bugs/known-failures.d.ts +0 -49
- package/dist/app/bugs/record.d.ts +0 -14
- package/dist/app/bugs/run-reading.d.ts +0 -51
- package/dist/app/bugs/store.d.ts +0 -34
- package/dist/app/campaigns/brief.d.ts +0 -7
- package/dist/app/campaigns/campaign.d.ts +0 -14
- package/dist/app/campaigns/index.d.ts +0 -38
- package/dist/app/campaigns/store.d.ts +0 -5
- package/dist/app/campaigns/summary.d.ts +0 -13
- package/dist/app/cleanup/index.d.ts +0 -52
- package/dist/app/db-schema/prepare.d.ts +0 -11
- package/dist/app/db-schema/refresh.d.ts +0 -17
- package/dist/app/db-schema/verify.d.ts +0 -11
- package/dist/app/destroy/index.d.ts +0 -16
- package/dist/app/discover/index.d.ts +0 -8
- package/dist/app/doctor/index.d.ts +0 -17
- package/dist/app/environments/config-driven.d.ts +0 -5
- package/dist/app/environments/routing.d.ts +0 -6
- package/dist/app/environments/supplied.d.ts +0 -3
- package/dist/app/file-selection.d.ts +0 -1
- package/dist/app/inspect/artifacts.d.ts +0 -30
- package/dist/app/inspect/index.d.ts +0 -112
- package/dist/app/lint/index.d.ts +0 -28
- package/dist/app/lint/native.d.ts +0 -10
- package/dist/app/lint/options.d.ts +0 -66
- package/dist/app/lint/policy.d.ts +0 -21
- package/dist/app/lint/rules/data.d.ts +0 -3
- package/dist/app/lint/rules/data.js +0 -1
- package/dist/app/lint/rules/engine-imports.d.ts +0 -3
- package/dist/app/lint/rules/syntax.d.ts +0 -4
- package/dist/app/lint/rules/unit.d.ts +0 -3
- package/dist/app/lint/shared.d.ts +0 -34
- package/dist/app/package-inspection.d.ts +0 -20
- package/dist/app/reports/files.d.ts +0 -13
- package/dist/app/reports/prepare.d.ts +0 -20
- package/dist/app/reports/publish.d.ts +0 -21
- package/dist/app/run/endpoint-readiness.d.ts +0 -43
- package/dist/app/run/endpoint-readiness.js +0 -1
- package/dist/app/run/engine-evidence-persistence.d.ts +0 -39
- package/dist/app/run/engine-execution-cleanup.d.ts +0 -8
- package/dist/app/run/engine-execution-cleanup.js +0 -1
- package/dist/app/run/engine-execution-failures.d.ts +0 -41
- package/dist/app/run/engine-execution-observer.d.ts +0 -21
- package/dist/app/run/engine-execution-reporting.d.ts +0 -17
- package/dist/app/run/engine-execution.d.ts +0 -65
- package/dist/app/run/engine-failure-stack.d.ts +0 -1
- package/dist/app/run/engine-orchestration.d.ts +0 -30
- package/dist/app/run/environment.d.ts +0 -33
- package/dist/app/run/failure-details.d.ts +0 -37
- package/dist/app/run/live-run.d.ts +0 -29
- package/dist/app/run/operation-progress.d.ts +0 -13
- package/dist/app/run/orchestrator.d.ts +0 -75
- package/dist/app/run/planning-types.d.ts +0 -68
- package/dist/app/run/planning.d.ts +0 -14
- package/dist/app/run/request.d.ts +0 -30
- package/dist/app/run/results.d.ts +0 -161
- package/dist/app/run/run-events.d.ts +0 -144
- package/dist/app/run/run-finalization.d.ts +0 -56
- package/dist/app/run/run-guards.d.ts +0 -3
- package/dist/app/run/runtime-failures.d.ts +0 -18
- package/dist/app/run/runtime-fixtures.d.ts +0 -2
- package/dist/app/run/runtime-task-types.d.ts +0 -20
- package/dist/app/run/scheduler/estimates.d.ts +0 -32
- package/dist/app/run/scheduler/identity.d.ts +0 -9
- package/dist/app/run/scheduler/index.d.ts +0 -124
- package/dist/app/run/scheduler/locks.d.ts +0 -6
- package/dist/app/run/scheduler/observations.d.ts +0 -20
- package/dist/app/run/selection.d.ts +0 -9
- package/dist/app/run/setup/runtime-node-setup-errors.d.ts +0 -13
- package/dist/app/run/setup/runtime-node-setup-protocol.d.ts +0 -85
- package/dist/app/run/setup/runtime-node-setup-worker.d.ts +0 -1
- package/dist/app/run/setup/runtime-node-setup.d.ts +0 -48
- package/dist/app/run/task-batches.d.ts +0 -23
- package/dist/app/run/task-leases.d.ts +0 -15
- package/dist/app/run/worker-loop.d.ts +0 -66
- package/dist/app/setup/playwright.d.ts +0 -22
- package/dist/app/status/index.d.ts +0 -9
- package/dist/app/status/model.d.ts +0 -29
- package/dist/app/status/runs.d.ts +0 -99
- package/dist/app/stop/index.d.ts +0 -14
- package/dist/app/tracker/index.d.ts +0 -13
- package/dist/app/tracker/remote.d.ts +0 -13
- package/dist/app/tracker/token.d.ts +0 -7
- package/dist/app/typecheck/index.d.ts +0 -63
- package/dist/cli/command-catalog.d.ts +0 -156
- package/dist/cli/command-flags.d.ts +0 -19
- package/dist/cli/command-registry.d.ts +0 -3
- package/dist/cli/commands/artifacts.d.ts +0 -48
- package/dist/cli/commands/bug/file.d.ts +0 -16
- package/dist/cli/commands/bug/guide.d.ts +0 -5
- package/dist/cli/commands/bug/record.d.ts +0 -9
- package/dist/cli/commands/campaign/finish.d.ts +0 -10
- package/dist/cli/commands/campaign/start.d.ts +0 -7
- package/dist/cli/commands/campaign/status.d.ts +0 -13
- package/dist/cli/commands/ci/report.d.ts +0 -15
- package/dist/cli/commands/cleanup.d.ts +0 -13
- package/dist/cli/commands/db/schema/refresh.d.ts +0 -2
- package/dist/cli/commands/db/schema/verify.d.ts +0 -2
- package/dist/cli/commands/destroy.d.ts +0 -9
- package/dist/cli/commands/discover.d.ts +0 -2
- package/dist/cli/commands/doctor.d.ts +0 -2
- package/dist/cli/commands/guard/unsafe-entrypoint.d.ts +0 -2
- package/dist/cli/commands/lint.d.ts +0 -2
- package/dist/cli/commands/report/prepare.d.ts +0 -12
- package/dist/cli/commands/report/publish.d.ts +0 -13
- package/dist/cli/commands/run/status.d.ts +0 -5
- package/dist/cli/commands/run/stop.d.ts +0 -7
- package/dist/cli/commands/run.d.ts +0 -9
- package/dist/cli/commands/setup/playwright.d.ts +0 -2
- package/dist/cli/commands/status.d.ts +0 -5
- package/dist/cli/commands/stop.d.ts +0 -12
- package/dist/cli/commands/typecheck.d.ts +0 -2
- package/dist/cli/commands/workspace.d.ts +0 -2
- package/dist/cli/entrypoint.d.ts +0 -1
- package/dist/cli/framework/command.d.ts +0 -21
- package/dist/cli/framework/definitions.d.ts +0 -100
- package/dist/cli/framework/errors.d.ts +0 -6
- package/dist/cli/framework/execute.d.ts +0 -14
- package/dist/cli/framework/help.d.ts +0 -11
- package/dist/cli/framework/index.d.ts +0 -3
- package/dist/cli/framework/parser.d.ts +0 -6
- package/dist/cli/index.d.ts +0 -4
- package/dist/cli/json-errors.d.ts +0 -3
- package/dist/cli/operations/db/schema/refresh/operation.d.ts +0 -7
- package/dist/cli/operations/db/schema/verify/operation.d.ts +0 -7
- package/dist/cli/operations/run/operation.d.ts +0 -26
- package/dist/cli/process-signal.d.ts +0 -1
- package/dist/cli/renderers/bug/text.d.ts +0 -8
- package/dist/cli/renderers/campaign/text.d.ts +0 -2
- package/dist/cli/renderers/ci/text.d.ts +0 -2
- package/dist/cli/renderers/cleanup/format.d.ts +0 -14
- package/dist/cli/renderers/db-schema/text.d.ts +0 -4
- package/dist/cli/renderers/destroy/text.d.ts +0 -4
- package/dist/cli/renderers/discover/report.d.ts +0 -7
- package/dist/cli/renderers/discover/text.d.ts +0 -3
- package/dist/cli/renderers/doctor/text.d.ts +0 -2
- package/dist/cli/renderers/lint/text.d.ts +0 -2
- package/dist/cli/renderers/run/events.d.ts +0 -8
- package/dist/cli/renderers/run/failure.d.ts +0 -8
- package/dist/cli/renderers/run/formatting.d.ts +0 -79
- package/dist/cli/renderers/run/status.d.ts +0 -6
- package/dist/cli/renderers/run/text-reporter.d.ts +0 -19
- package/dist/cli/renderers/status/text.d.ts +0 -2
- package/dist/cli/renderers/stop/text.d.ts +0 -7
- package/dist/cli/renderers/typecheck/text.d.ts +0 -2
- package/dist/cli/terminal/colors.d.ts +0 -10
- package/dist/cli/terminal/layout.d.ts +0 -11
- package/dist/cli/version-drift.d.ts +0 -25
- package/dist/config/authoring/build-config-contract.d.ts +0 -27
- package/dist/config/authoring/configured-step-contract.d.ts +0 -18
- package/dist/config/authoring/http-execution-contract.d.ts +0 -70
- package/dist/config/authoring/http-methods.d.ts +0 -18
- package/dist/config/authoring/index.d.ts +0 -372
- package/dist/config/authoring/preset-env.d.ts +0 -19
- package/dist/config/authoring/runtime.d.ts +0 -64
- package/dist/config/authoring/runtime.js +0 -1
- package/dist/config/loader/bugs.d.ts +0 -15
- package/dist/config/loader/config-loader.d.ts +0 -9
- package/dist/config/loader/database-materialization.d.ts +0 -19
- package/dist/config/loader/database-roles.d.ts +0 -2
- package/dist/config/loader/database.d.ts +0 -55
- package/dist/config/loader/decode-discovery.d.ts +0 -5
- package/dist/config/loader/decode-execution.d.ts +0 -7
- package/dist/config/loader/dependencies.d.ts +0 -7
- package/dist/config/loader/discover-tests.d.ts +0 -111
- package/dist/config/loader/discovery-summary.d.ts +0 -29
- package/dist/config/loader/discovery.d.ts +0 -53
- package/dist/config/loader/env.d.ts +0 -4
- package/dist/config/loader/execution-model.d.ts +0 -15
- package/dist/config/loader/file-metadata.d.ts +0 -19
- package/dist/config/loader/groups.d.ts +0 -33
- package/dist/config/loader/index.d.ts +0 -10
- package/dist/config/loader/load.d.ts +0 -89
- package/dist/config/loader/managed-configs.d.ts +0 -23
- package/dist/config/loader/path-policy.d.ts +0 -9
- package/dist/config/loader/paths.d.ts +0 -5
- package/dist/config/loader/runtime-isolation.d.ts +0 -44
- package/dist/config/loader/runtime.d.ts +0 -16
- package/dist/config/loader/skip-config.d.ts +0 -44
- package/dist/config/loader/test-selection.d.ts +0 -15
- package/dist/config/loader/validation.d.ts +0 -15
- package/dist/config/loader/values.d.ts +0 -3
- package/dist/config/loader/values.js +0 -1
- package/dist/database/activity-gate.d.ts +0 -22
- package/dist/database/admin.d.ts +0 -31
- package/dist/database/cleanup.d.ts +0 -36
- package/dist/database/configured-steps.d.ts +0 -27
- package/dist/database/constants.d.ts +0 -11
- package/dist/database/docker-metadata.d.ts +0 -3
- package/dist/database/docker.d.ts +0 -65
- package/dist/database/drizzle/index.d.ts +0 -13
- package/dist/database/fingerprint.d.ts +0 -12
- package/dist/database/index.d.ts +0 -39
- package/dist/database/lifecycle-cleanup.d.ts +0 -8
- package/dist/database/local-postgres.d.ts +0 -71
- package/dist/database/naming.d.ts +0 -17
- package/dist/database/postgres/index.d.ts +0 -71
- package/dist/database/progress.d.ts +0 -6
- package/dist/database/roles.d.ts +0 -19
- package/dist/database/runtime-config.d.ts +0 -16
- package/dist/database/runtime-reservations.d.ts +0 -19
- package/dist/database/schema-operations.d.ts +0 -18
- package/dist/database/schema-source.d.ts +0 -96
- package/dist/database/source-refresh-lock.d.ts +0 -8
- package/dist/database/source-url.d.ts +0 -21
- package/dist/database/state-files.d.ts +0 -13
- package/dist/database/state.d.ts +0 -26
- package/dist/database/template-steps.d.ts +0 -39
- package/dist/domain/file-timeout.d.ts +0 -25
- package/dist/domain/identifiers.d.ts +0 -9
- package/dist/domain/operation-display.d.ts +0 -10
- package/dist/domain/operation-reason.d.ts +0 -5
- package/dist/domain/postgres-route.d.ts +0 -4
- package/dist/domain/runtime-identity.d.ts +0 -1
- package/dist/domain/runtime-isolation-contract.d.ts +0 -5
- package/dist/domain/service-name.d.ts +0 -2
- package/dist/domain/suite-selection.d.ts +0 -20
- package/dist/domain/test-outcomes.d.ts +0 -2
- package/dist/domain/test-types.d.ts +0 -16
- package/dist/engines/adapters/evidence-files.d.ts +0 -32
- package/dist/engines/adapters/evidence-identity.d.ts +0 -12
- package/dist/engines/adapters/execution.d.ts +0 -41
- package/dist/engines/adapters/native-report.d.ts +0 -25
- package/dist/engines/adapters/node-startup-cache.d.ts +0 -8
- package/dist/engines/adapters/workspace.d.ts +0 -4
- package/dist/engines/api/capabilities.d.ts +0 -21
- package/dist/engines/api/capabilities.js +0 -1
- package/dist/engines/api/evidence.d.ts +0 -38
- package/dist/engines/api/execution-observer.d.ts +0 -8
- package/dist/engines/api/execution-scope.d.ts +0 -17
- package/dist/engines/api/execution.d.ts +0 -101
- package/dist/engines/api/index.d.ts +0 -5
- package/dist/engines/api/k6-runtime-input.d.ts +0 -26
- package/dist/engines/api/k6-runtime-input.js +0 -1
- package/dist/engines/api/runtime-artifact.d.ts +0 -19
- package/dist/engines/bundle-cache.d.ts +0 -32
- package/dist/engines/catalog.d.ts +0 -27
- package/dist/engines/contracts/fixture-manifest.d.ts +0 -34
- package/dist/engines/contracts/json-bounds.d.ts +0 -36
- package/dist/engines/contracts/json-bounds.js +0 -2
- package/dist/engines/contracts/k6-repository-configuration.d.ts +0 -12
- package/dist/engines/contracts/k6-repository-configuration.js +0 -1
- package/dist/engines/contracts/test-context.d.ts +0 -15
- package/dist/engines/contracts/test-context.js +0 -1
- package/dist/engines/definitions.d.ts +0 -23
- package/dist/engines/factory.d.ts +0 -6
- package/dist/engines/generated-performance.d.ts +0 -1
- package/dist/engines/installation-catalog.d.ts +0 -27
- package/dist/engines/installation-catalog.js +0 -1
- package/dist/engines/installations/k6.d.ts +0 -98
- package/dist/engines/installations/k6.js +0 -1
- package/dist/engines/installations/node-vitest.d.ts +0 -33
- package/dist/engines/installations/package-requirements.d.ts +0 -28
- package/dist/engines/installations/package-resolution.d.ts +0 -51
- package/dist/engines/installations/playwright-resolution-child.d.ts +0 -1
- package/dist/engines/installations/playwright.d.ts +0 -75
- package/dist/engines/installations/types.d.ts +0 -10
- package/dist/engines/k6/adapter/bundle.d.ts +0 -25
- package/dist/engines/k6/adapter/bundle.js +0 -112
- package/dist/engines/k6/adapter/evidence.d.ts +0 -15
- package/dist/engines/k6/adapter/evidence.js +0 -4
- package/dist/engines/k6/adapter/executor.d.ts +0 -13
- package/dist/engines/k6/adapter/executor.js +0 -1
- package/dist/engines/k6/adapter/index.d.ts +0 -1
- package/dist/engines/k6/adapter/index.js +0 -1
- package/dist/engines/k6/adapter/input.d.ts +0 -21
- package/dist/engines/k6/adapter/input.js +0 -1
- package/dist/engines/k6/adapter/native-summary.d.ts +0 -11
- package/dist/engines/k6/adapter/native-summary.js +0 -1
- package/dist/engines/k6/adapter/repository-configuration.d.ts +0 -2
- package/dist/engines/k6/adapter/repository-configuration.js +0 -1
- package/dist/engines/k6/authoring-type-paths.d.ts +0 -3
- package/dist/engines/k6/authoring-type-paths.js +0 -1
- package/dist/engines/k6/runtime/ambient-types/bundle-manifest.json +0 -146
- package/dist/engines/k6/runtime/ambient-types/k6/LICENSE +0 -21
- package/dist/engines/k6/runtime/ambient-types/k6/browser/index.d.ts +0 -6332
- package/dist/engines/k6/runtime/ambient-types/k6/crypto/index.d.ts +0 -218
- package/dist/engines/k6/runtime/ambient-types/k6/data/index.d.ts +0 -16
- package/dist/engines/k6/runtime/ambient-types/k6/encoding/index.d.ts +0 -46
- package/dist/engines/k6/runtime/ambient-types/k6/execution/index.d.ts +0 -127
- package/dist/engines/k6/runtime/ambient-types/k6/experimental/csv/index.d.ts +0 -96
- package/dist/engines/k6/runtime/ambient-types/k6/experimental/fs/index.d.ts +0 -104
- package/dist/engines/k6/runtime/ambient-types/k6/experimental/redis/index.d.ts +0 -821
- package/dist/engines/k6/runtime/ambient-types/k6/experimental/streams/index.d.ts +0 -198
- package/dist/engines/k6/runtime/ambient-types/k6/global.d.ts +0 -83
- package/dist/engines/k6/runtime/ambient-types/k6/html/index.d.ts +0 -1325
- package/dist/engines/k6/runtime/ambient-types/k6/http/index.d.ts +0 -806
- package/dist/engines/k6/runtime/ambient-types/k6/index.d.ts +0 -126
- package/dist/engines/k6/runtime/ambient-types/k6/metrics/index.d.ts +0 -66
- package/dist/engines/k6/runtime/ambient-types/k6/net/grpc/index.d.ts +0 -247
- package/dist/engines/k6/runtime/ambient-types/k6/options/index.d.ts +0 -459
- package/dist/engines/k6/runtime/ambient-types/k6/package.json +0 -79
- package/dist/engines/k6/runtime/ambient-types/k6/secrets/index.d.ts +0 -36
- package/dist/engines/k6/runtime/ambient-types/k6/timers/index.d.ts +0 -47
- package/dist/engines/k6/runtime/ambient-types/k6/websockets/index.d.ts +0 -324
- package/dist/engines/k6/runtime/ambient-types/k6/ws/index.d.ts +0 -283
- package/dist/engines/k6/runtime/ambient-types/xk6-sql/LICENSE +0 -201
- package/dist/engines/k6/runtime/ambient-types/xk6-sql/index.d.ts +0 -397
- package/dist/engines/k6/runtime/ambient-types/xk6-sql/package.json +0 -15
- package/dist/engines/k6/runtime/artifacts.d.ts +0 -2
- package/dist/engines/k6/runtime/artifacts.js +0 -1
- package/dist/engines/k6/runtime/checks.d.ts +0 -17
- package/dist/engines/k6/runtime/checks.js +0 -1
- package/dist/engines/k6/runtime/dal-fixtures.d.ts +0 -24
- package/dist/engines/k6/runtime/dal-fixtures.js +0 -1
- package/dist/engines/k6/runtime/dal-suite.d.ts +0 -24
- package/dist/engines/k6/runtime/dal-suite.js +0 -1
- package/dist/engines/k6/runtime/dal.d.ts +0 -10
- package/dist/engines/k6/runtime/dal.js +0 -1
- package/dist/engines/k6/runtime/fixtures.d.ts +0 -3
- package/dist/engines/k6/runtime/fixtures.js +0 -1
- package/dist/engines/k6/runtime/http-assertions.d.ts +0 -19
- package/dist/engines/k6/runtime/http-assertions.js +0 -1
- package/dist/engines/k6/runtime/http-checks.d.ts +0 -19
- package/dist/engines/k6/runtime/http-checks.js +0 -1
- package/dist/engines/k6/runtime/http-suite-runtime.d.ts +0 -103
- package/dist/engines/k6/runtime/http-suite-runtime.js +0 -1
- package/dist/engines/k6/runtime/http.d.ts +0 -15
- package/dist/engines/k6/runtime/http.js +0 -1
- package/dist/engines/k6/runtime/index.d.ts +0 -11
- package/dist/engines/k6/runtime/index.js +0 -1
- package/dist/engines/k6/runtime/input.d.ts +0 -3
- package/dist/engines/k6/runtime/input.js +0 -1
- package/dist/engines/k6/runtime/k6-repository-configuration.d.ts +0 -9
- package/dist/engines/k6/runtime/k6-repository-configuration.js +0 -1
- package/dist/engines/k6/runtime/node-setup.d.ts +0 -13
- package/dist/engines/k6/runtime/node-setup.js +0 -1
- package/dist/engines/k6/runtime/runtime-globals.d.ts +0 -10
- package/dist/engines/k6/runtime/runtime.d.ts +0 -54
- package/dist/engines/k6/runtime/runtime.js +0 -1
- package/dist/engines/k6/runtime/shared/assertion-details.d.ts +0 -19
- package/dist/engines/k6/runtime/shared/assertion-details.js +0 -2
- package/dist/engines/k6/runtime/shared/db-normalization.d.ts +0 -19
- package/dist/engines/k6/runtime/shared/db-normalization.js +0 -1
- package/dist/engines/k6/runtime/shared/error-body.d.ts +0 -3
- package/dist/engines/k6/runtime/shared/error-body.js +0 -1
- package/dist/engines/k6/runtime/shared/file-data.d.ts +0 -1
- package/dist/engines/k6/runtime/shared/file-data.js +0 -1
- package/dist/engines/k6/runtime/shared/fixture-engine.d.ts +0 -8
- package/dist/engines/k6/runtime/shared/fixture-engine.js +0 -2
- package/dist/engines/k6/runtime/shared/http-check-plan.d.ts +0 -29
- package/dist/engines/k6/runtime/shared/http-check-plan.js +0 -1
- package/dist/engines/k6/runtime/shared/http-parsing.d.ts +0 -10
- package/dist/engines/k6/runtime/shared/http-parsing.js +0 -3
- package/dist/engines/k6/runtime/shared/ordering.d.ts +0 -2
- package/dist/engines/k6/runtime/shared/ordering.js +0 -1
- package/dist/engines/k6/runtime/shared/properties.d.ts +0 -1
- package/dist/engines/k6/runtime/shared/properties.js +0 -1
- package/dist/engines/k6/runtime/suite.d.ts +0 -4
- package/dist/engines/k6/runtime/suite.js +0 -1
- package/dist/engines/k6/runtime/types.d.ts +0 -244
- package/dist/engines/k6/runtime/types.js +0 -1
- package/dist/engines/k6/runtime/xk6-sql-driver-postgres.d.ts +0 -7
- package/dist/engines/k6/runtime/xk6-sql-driver-postgres.js +0 -1
- package/dist/engines/managed-module-resolution-policy-input.d.ts +0 -8
- package/dist/engines/managed-module-resolution-policy.d.ts +0 -25
- package/dist/engines/managed-module-resolution.d.ts +0 -13
- package/dist/engines/manager.d.ts +0 -25
- package/dist/engines/node-vitest/adapter/adapter.d.ts +0 -16
- package/dist/engines/node-vitest/adapter/consumer-typescript-paths.d.ts +0 -17
- package/dist/engines/node-vitest/adapter/consumer-typescript-paths.js +0 -3
- package/dist/engines/node-vitest/adapter/evidence.d.ts +0 -33
- package/dist/engines/node-vitest/adapter/failure-details.d.ts +0 -3
- package/dist/engines/node-vitest/adapter/index.d.ts +0 -1
- package/dist/engines/node-vitest/adapter/reporter.d.ts +0 -7
- package/dist/engines/node-vitest/adapter/resolution.d.ts +0 -12
- package/dist/engines/node-vitest/adapter/resolution.js +0 -1
- package/dist/engines/node-vitest/adapter/workspace.d.ts +0 -19
- package/dist/engines/node-vitest/runtime/contract/index.d.ts +0 -64
- package/dist/engines/node-vitest/runtime/contract/index.js +0 -1
- package/dist/engines/node-vitest/runtime/data/http.d.ts +0 -4
- package/dist/engines/node-vitest/runtime/data/http.js +0 -1
- package/dist/engines/node-vitest/runtime/data/index.d.ts +0 -8
- package/dist/engines/node-vitest/runtime/data/primitives.d.ts +0 -1
- package/dist/engines/node-vitest/runtime/data/report.d.ts +0 -3
- package/dist/engines/node-vitest/runtime/data/runner.d.ts +0 -4
- package/dist/engines/node-vitest/runtime/data/runner.js +0 -1
- package/dist/engines/node-vitest/runtime/data/scenario.d.ts +0 -11
- package/dist/engines/node-vitest/runtime/data/types.d.ts +0 -384
- package/dist/engines/node-vitest/runtime/runtime-input.d.ts +0 -64
- package/dist/engines/playwright/adapter/adapter.d.ts +0 -18
- package/dist/engines/playwright/adapter/attachments.d.ts +0 -26
- package/dist/engines/playwright/adapter/config.d.ts +0 -45
- package/dist/engines/playwright/adapter/evidence-validation.d.ts +0 -9
- package/dist/engines/playwright/adapter/evidence.d.ts +0 -32
- package/dist/engines/playwright/adapter/failure-details.d.ts +0 -2
- package/dist/engines/playwright/adapter/index.d.ts +0 -3
- package/dist/engines/playwright/adapter/reporter.d.ts +0 -12
- package/dist/engines/playwright/runtime/config-file.d.ts +0 -2
- package/dist/engines/playwright/runtime/config.d.ts +0 -24
- package/dist/engines/playwright/runtime/index.d.ts +0 -4
- package/dist/engines/playwright/runtime/runtime-input.d.ts +0 -36
- package/dist/engines/playwright/runtime/sandbox.d.ts +0 -84
- package/dist/engines/runtime-input-file.d.ts +0 -27
- package/dist/environment/lifecycle-artifacts.d.ts +0 -7
- package/dist/environment/lifecycle-controller.d.ts +0 -60
- package/dist/environment/lifecycle.d.ts +0 -32
- package/dist/environment/maintenance.d.ts +0 -68
- package/dist/environment/planning-types.d.ts +0 -33
- package/dist/environment/ports.d.ts +0 -10
- package/dist/environment/ready-environment.d.ts +0 -16
- package/dist/environment/run-environment.d.ts +0 -39
- package/dist/environment/run-stopping.d.ts +0 -13
- package/dist/environment/runtime-contexts.d.ts +0 -37
- package/dist/environment/runtime-manager.d.ts +0 -87
- package/dist/environment/runtime-planning.d.ts +0 -16
- package/dist/environment/runtime-preparation.d.ts +0 -30
- package/dist/environment/runtime-service.d.ts +0 -12
- package/dist/environment/services.d.ts +0 -56
- package/dist/environment/state.d.ts +0 -15
- package/dist/environment/status-runtime.d.ts +0 -53
- package/dist/environment/template.d.ts +0 -164
- package/dist/evidence/artifact-config.d.ts +0 -14
- package/dist/evidence/artifact-validation.d.ts +0 -4
- package/dist/evidence/artifacts.d.ts +0 -75
- package/dist/evidence/bundles.d.ts +0 -27
- package/dist/evidence/ci-provenance.d.ts +0 -9
- package/dist/evidence/contracts.d.ts +0 -154
- package/dist/evidence/environment-record.d.ts +0 -22
- package/dist/evidence/file-evidence.d.ts +0 -23
- package/dist/evidence/finalization.d.ts +0 -9
- package/dist/evidence/index.d.ts +0 -8
- package/dist/evidence/logs.d.ts +0 -31
- package/dist/evidence/operation-timings.d.ts +0 -60
- package/dist/evidence/read-bundle.d.ts +0 -4
- package/dist/evidence/read-fields.d.ts +0 -21
- package/dist/evidence/read-provenance.d.ts +0 -2
- package/dist/evidence/read-redaction.d.ts +0 -2
- package/dist/evidence/read-retention.d.ts +0 -2
- package/dist/evidence/read-run.d.ts +0 -2
- package/dist/evidence/reading.d.ts +0 -28
- package/dist/evidence/reporting-types.d.ts +0 -349
- package/dist/evidence/reporting.d.ts +0 -3
- package/dist/evidence/secure-file-copy.d.ts +0 -24
- package/dist/evidence/setup-progress.d.ts +0 -63
- package/dist/evidence/step-output.d.ts +0 -16
- package/dist/evidence/storage-inventory.d.ts +0 -27
- package/dist/evidence/storage.d.ts +0 -25
- package/dist/evidence/timing.d.ts +0 -23
- package/dist/file.d.ts +0 -11
- package/dist/index.d.ts +0 -4
- package/dist/kernel/error-serialization.d.ts +0 -40
- package/dist/kernel/redaction.d.ts +0 -2
- package/dist/kernel/unknown-values.d.ts +0 -11
- package/dist/kernel/work-metrics.d.ts +0 -6
- package/dist/process/capture-output.d.ts +0 -7
- package/dist/process/command-shell.d.ts +0 -1
- package/dist/process/environment-overlay.d.ts +0 -17
- package/dist/process/environment.d.ts +0 -33
- package/dist/process/files/contained-path.d.ts +0 -8
- package/dist/process/files/directory-status.d.ts +0 -14
- package/dist/process/files/dotenv.d.ts +0 -2
- package/dist/process/files/file-snapshot.d.ts +0 -34
- package/dist/process/files/json-file.d.ts +0 -11
- package/dist/process/files/paths.d.ts +0 -3
- package/dist/process/files/product-dir.d.ts +0 -1
- package/dist/process/files/temp-dir.d.ts +0 -11
- package/dist/process/host/metadata.d.ts +0 -6
- package/dist/process/host/platform.d.ts +0 -57
- package/dist/process/host-command.d.ts +0 -4
- package/dist/process/host-inspection-command.d.ts +0 -13
- package/dist/process/host-process.d.ts +0 -19
- package/dist/process/index.d.ts +0 -9
- package/dist/process/lifecycle-fence.d.ts +0 -10
- package/dist/process/locks.d.ts +0 -28
- package/dist/process/process-contracts.d.ts +0 -78
- package/dist/process/process-group.d.ts +0 -12
- package/dist/process/process-identity.d.ts +0 -27
- package/dist/process/process-limits.d.ts +0 -6
- package/dist/process/process.d.ts +0 -12
- package/dist/process/readiness.d.ts +0 -43
- package/dist/process/recovery.d.ts +0 -3
- package/dist/process/runs/inspection.d.ts +0 -5
- package/dist/process/runs/manifest.d.ts +0 -86
- package/dist/process/runs/ownership.d.ts +0 -11
- package/dist/process/runs/product-state.d.ts +0 -2
- package/dist/process/runs/schema.d.ts +0 -3
- package/dist/process/runs/stop.d.ts +0 -19
- package/dist/process/runtime-socket.d.ts +0 -4
- package/dist/process/service-processes.d.ts +0 -13
- package/dist/process/shutdown.d.ts +0 -10
- package/dist/process/stoppable-settlement.d.ts +0 -26
- package/dist/process/url-redaction.d.ts +0 -1
- package/dist/process/windows-cli-owner.d.ts +0 -2
- package/dist/process/windows-native.d.ts +0 -12
- package/dist/process/windows-process-identity.d.ts +0 -4
- package/dist/repo/ci-environment.d.ts +0 -7
- package/dist/repo/fingerprint-policy.d.ts +0 -13
- package/dist/repo/git-path.d.ts +0 -2
- package/dist/repo/history.d.ts +0 -17
- package/dist/repo/managed-git.d.ts +0 -10
- package/dist/repo/metadata.d.ts +0 -3
- package/dist/repo/physical-tree.d.ts +0 -37
- package/dist/repo/publish-directory.d.ts +0 -20
- package/dist/repo/remote.d.ts +0 -6
- package/dist/repo/state.d.ts +0 -26
- package/dist/repo/tree-listing.d.ts +0 -10
- package/dist/steps/build-config.d.ts +0 -34
- package/dist/steps/configured-step-testkit-modules.d.ts +0 -19
- package/dist/steps/configured-steps.d.ts +0 -51
- package/dist/steps/environment.d.ts +0 -82
- package/dist/steps/index.d.ts +0 -2
- package/dist/steps/module-runner.d.ts +0 -1
- package/dist/steps/run.d.ts +0 -36
- package/dist/toolchains/archive.d.ts +0 -53
- package/dist/toolchains/esbuild.d.ts +0 -9
- package/dist/toolchains/host-node-support.d.ts +0 -3
- package/dist/toolchains/index.d.ts +0 -114
- package/dist/toolchains/managed-node-releases.d.ts +0 -28
- package/dist/toolchains/node-cache.d.ts +0 -20
- package/dist/toolchains/node-integrity.d.ts +0 -18
- package/dist/toolchains/node-provisioning.d.ts +0 -29
- package/dist/toolchains/semver.d.ts +0 -13
- package/dist/toolchains/shared-operation.d.ts +0 -11
- package/dist/toolchains/timeouts.d.ts +0 -1
- package/dist/toolchains/vitest/index.d.ts +0 -13
- package/dist/tracker/gitea.d.ts +0 -14
- package/dist/tracker/github.d.ts +0 -14
- package/dist/tracker/http.d.ts +0 -33
- package/dist/tracker/index.d.ts +0 -4
- package/dist/tracker/redaction.d.ts +0 -4
- package/dist/tracker/requests.d.ts +0 -17
- package/dist/tracker/responses.d.ts +0 -24
- package/dist/tracker/types.d.ts +0 -51
- package/docs/engine-architecture.md +0 -682
- /package/dist/engines/{k6/runtime/runtime-globals.js → api/profiles.js} +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
import{matchesSuiteSelectors as
|
|
1
|
+
import{matchesSuiteSelectors as g,parseSuiteSelectors as O}from"../../domain/suite-selection.js";import{isTypedTestFile as L}from"../../domain/identifiers.js";import{rejectUnknownKeys as d}from"../../kernel/known-keys.js";import{isUnknownRecord as y}from"../../kernel/unknown-values.js";import{DEFAULT_RUNTIME_ISOLATION_POLICY as R,normalizeRuntimeIsolationPolicy as f}from"../../domain/runtime-isolation-contract.js";import{normalizePath as h}from"./paths.js";import{optionalArray as T}from"./skip-config.js";export{DEFAULT_RUNTIME_ISOLATION_POLICY,isRuntimeIsolationPolicy,normalizeRuntimeIsolationPolicy,RUNTIME_ISOLATION_POLICIES}from"../../domain/runtime-isolation-contract.js";const _=String;export function normalizeRuntimeIsolationConfig(i,{owner:t,plainListed:p=!0,suites:u}){if(!i)return $();if(typeof i=="string")return{...$(),defaultPolicy:f(i,`${t} runtimeIsolation`)};if(!y(i))throw new Error(`${t} runtimeIsolation must be a policy string or object`);d(i,["default","files","suites"],`${t} runtimeIsolation`);const c=u??[],n=new Set(c.flatMap(o=>(o.files??[]).map(h))),m=i.default==null?R:f(i.default,`${t} runtimeIsolation.default`),l=[],P=new Set;for(const[o,a]of T(i.suites,`${t} runtimeIsolation.suites`).entries()){if(!y(a))throw new Error(`${t} runtimeIsolation.suites entries must be objects`);const s=a;d(s,["policy","runtimeIsolation","selector"],`${t} runtimeIsolation.suites entry`);const e=_(s.selector||"").trim();if(!e)throw new Error(`${t} runtimeIsolation.suites[${o.toString()}] requires a selector`);const E=O([e]);if(E.length!==1)throw new Error(`${t} runtimeIsolation.suites["${e}"] is invalid`);const r=E[0];if(r.kind!=="typed")throw new Error(`${t} runtimeIsolation.suites["${e}"] must use a typed selector like ui:auth`);if(P.has(r.raw))throw new Error(`${t} defines duplicate runtimeIsolation.suites selector "${r.raw}"`);if(!c.some(w=>g(w.testType,w.name,[r])))throw new Error(`${t} runtimeIsolation.suites selector "${r.raw}" did not match any discovered suite`);P.add(r.raw),l.push({selector:r,policy:f(s.policy||s.runtimeIsolation,`${t} runtimeIsolation.suites["${r.raw}"].policy`)})}const I=[],S=new Set;for(const[o,a]of T(i.files,`${t} runtimeIsolation.files`).entries()){if(!y(a))throw new Error(`${t} runtimeIsolation.files entries must be objects`);const s=a;d(s,["path","policy","runtimeIsolation"],`${t} runtimeIsolation.files entry`);const e=h(b(s.path,`${t} runtimeIsolation.files[${o.toString()}].path`));if(!e)throw new Error(`${t} runtimeIsolation.files[${o.toString()}] requires a path`);if(!n.has(e)&&(p||L(e)))throw new Error(`${t} runtimeIsolation.files["${e}"] did not match any discovered test file`);if(S.has(e))throw new Error(`${t} defines duplicate runtimeIsolation.files path "${e}"`);S.add(e),I.push({path:e,policy:f(s.policy||s.runtimeIsolation,`${t} runtimeIsolation.files["${e}"].policy`)})}return{defaultPolicy:m,suites:l,files:I,filePolicyByPath:new Map(I.map(o=>[o.path,o.policy]))}}export function resolveRuntimeIsolationPolicy(i,t,p){const u=h(p),c=i.testkit?.fileMetadataByPath?.get(u)||null,n=i.testkit?.runtimeIsolation||$(),m=c?.runtimeIsolation||n.filePolicyByPath.get(u);if(m)return m;for(const l of n.suites)if(g(t.testType,t.name,[l.selector]))return l.policy;return n.defaultPolicy}function $(){return{defaultPolicy:R,suites:[],files:[],filePolicyByPath:new Map}}function b(i,t){if(i==null||typeof i=="string")return i;throw new Error(`${t} must be a string`)}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import t from"fs";import i from"path";import{DEFAULT_FILE_TIMEOUT_SECONDS as
|
|
1
|
+
import t from"fs";import i from"path";import{DEFAULT_FILE_TIMEOUT_SECONDS as c}from"../../domain/file-timeout.js";import{normalizeExecutionConfig as u,normalizeRuntimeMaxConcurrentTasks as f,normalizeRuntimeInstances as l}from"./decode-execution.js";import"./execution-model.js";import{normalizeConfiguredInputs as S,normalizeConfiguredSteps as x}from"../../steps/configured-steps.js";import{buildConfigToPrepare as C,normalizeBuildConfig as T}from"../../steps/build-config.js";import{normalizeRuntimeToolchain as z}from"../../toolchains/index.js";import{isNonNullObject as m}from"../../kernel/unknown-values.js";export function normalizeRuntimeConfig(e,n,a){if(!e)return{build:null,instances:1,maxConcurrentTasks:Number.POSITIVE_INFINITY,prepare:{inputs:[],steps:[]},toolchain:null};const r=m(e)?e:{},o=T(r.build,`Service "${n}" runtime.build`),s=normalizeRuntimePrepareConfig(r.prepare,n),p=C(o);return{build:o,instances:l(r.instances??1,`Service "${n}" runtime.instances`),maxConcurrentTasks:f(r.maxConcurrentTasks,`Service "${n}" runtime.maxConcurrentTasks`),prepare:{inputs:[...new Set([...p.inputs,...s.inputs])],steps:[...p.steps,...s.steps]},toolchain:z(r.toolchain,`Service "${n}" runtime.toolchain`,a||{})}}export function normalizeRuntimePrepareConfig(e,n){if(e==null)return{inputs:[],steps:[]};if(!m(e))throw new Error(`Service "${n}" runtime.prepare must be an object`);return{inputs:S(e.inputs,`Service "${n}" runtime.prepare`),steps:x(e.steps,`Service "${n}" runtime.prepare.steps`)}}export function normalizeRepoExecution(e){return e?u(e):u({workers:1,fileTimeoutSeconds:c})}export function detectNextApp(e){return t.existsSync(i.join(e,"next.config.js"))||t.existsSync(i.join(e,"next.config.mjs"))||t.existsSync(i.join(e,"next.config.ts"))}export{normalizeConfiguredStep as normalizeTemplateLifecycleStep,normalizeConfiguredSteps as normalizeTemplateLifecycleSteps,parseModuleSpecifier}from"../../steps/configured-steps.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{matchesSuiteSelectors as
|
|
1
|
+
import{isTypedTestFile as w}from"../../domain/identifiers.js";import{matchesSuiteSelectors as S,parseSuiteSelectors as E}from"../../domain/suite-selection.js";import{normalizePath as g}from"./paths.js";import{rejectUnknownKeys as p}from"../../kernel/known-keys.js";import{isUnknownRecord as j}from"../../kernel/unknown-values.js";import{requireObjectRecord as R}from"../../evidence/read-fields.js";const k=String;export function normalizeSkipReason(r,e){const u=k(r||"").trim();if(!u)throw new Error(`${e} requires a non-empty reason`);return u}export function normalizeSkipConfig(r,{owner:e,plainListed:u=!0,suites:l}){if(!r)return;const c=R(r,`${e} skip`);p(c,["files","suites"],`${e} skip`);const m=new Set(l.flatMap(t=>t.files.map(g))),a=new Set,f=[];for(const t of optionalArray(c.files,`${e} skip.files`)){const i=y(t,`${e} skip.files entries must be objects`);p(i,["path","reason"],`${e} skip.files entry`);const s=g(typeof i.path=="string"?i.path:void 0),n=normalizeSkipReason(i.reason,`${e} skip.files["${s}"]`);if(!s)throw new Error(`${e} skip.files entries require a non-empty path`);if(a.has(s))throw new Error(`${e} defines duplicate skip.files path "${s}"`);if(!m.has(s)&&(u||w(s)))throw new Error(`${e} skip.files path "${s}" did not match any discovered suite file`);a.add(s),f.push({path:s,reason:n})}const h=optionalArray(c.suites,`${e} skip.suites`).map((t,i)=>{const s=y(t,`${e} skip.suites entries must be objects`);p(s,["reason","selector"],`${e} skip.suites entry`);const n=k(s.selector||"").trim();if(!n)throw new Error(`${e} skip.suites[${String(i)}] requires a non-empty selector`);const q=normalizeSkipReason(s.reason,`${e} skip.suites["${n}"]`),$=E([n]),b=$[0];if($.length!==1)throw new Error(`${e} skip.suites["${n}"] is invalid`);return{selector:b,reason:q}}),d=new Set,o=[];for(const t of h){if(d.has(t.selector.raw))throw new Error(`${e} defines duplicate skip.suites selector "${t.selector.raw}"`);if(!l.some(s=>S(s.testType,s.name,[t.selector]))&&(u||t.selector.kind==="typed"))throw new Error(`${e} skip.suites selector "${t.selector.raw}" did not match any discovered suite`);d.add(t.selector.raw),o.push(t)}if(!(f.length===0&&o.length===0))return{files:f,fileReasonByPath:new Map(f.map(t=>[t.path,t.reason])),suites:o}}export function normalizeServiceRequirements(r,{owner:e,plainListed:u=!0,suites:l}){if(!r)return x();const c=R(r,`${e} requirements`);p(c,["files","suites"],`${e} requirements`);const m=new Set(l.flatMap(o=>o.files)),a=[],f=new Set;for(const o of optionalArray(c.suites,`${e} requirements.suites`)){const t=y(o,`${e} requirements.suites entries must be objects`);p(t,["locks","selector"],`${e} requirements.suites entry`);const i=k(t.selector||"").trim();if(!i)throw new Error(`${e} requirements.suites entries require a selector`);const s=E([i]),n=s[0];if(s.length!==1)throw new Error(`${e} requirements.suites["${i}"] is invalid`);if(n.kind!=="typed")throw new Error(`${e} requirements.suites["${i}"] must use a typed selector like ui:health`);if(f.has(n.raw))throw new Error(`${e} defines duplicate requirements.suites selector "${n.raw}"`);if(!l.some($=>S($.testType,$.name,[n])))throw new Error(`${e} requirements.suites selector "${n.raw}" did not match any discovered suite`);f.add(n.raw),a.push({selector:n,locks:normalizeRequirementLocks(t.locks,`${e} requirements.suites["${n.raw}"].locks`)})}const h=[],d=new Set;for(const[o,t]of optionalArray(c.files,`${e} requirements.files`).entries()){const i=y(t,`${e} requirements.files entries must be objects`);p(i,["locks","path"],`${e} requirements.files entry`);const s=k(i.path||"").trim();if(!s)throw new Error(`${e} requirements.files[${String(o)}] requires a path`);if(!m.has(s)&&(u||w(s)))throw new Error(`${e} requirements.files["${s}"] did not match any discovered test file`);if(d.has(s))throw new Error(`${e} defines duplicate requirements.files path "${s}"`);d.add(s),h.push({path:s,locks:normalizeRequirementLocks(i.locks,`${e} requirements.files["${s}"].locks`)})}return{suites:a,files:h,fileLocksByPath:new Map(h.map(o=>[o.path,o.locks]))}}export function normalizeRequirementLocks(r,e){const u=Array.isArray(r)?r:r==null?[]:[r],l=new Set,c=[];for(const m of u){const a=k(m||"").trim();if(!a)throw new Error(`${e} entries must be non-empty strings`);l.has(a)||(l.add(a),c.push(a))}return c.sort()}function x(){return{suites:[],files:[],fileLocksByPath:new Map}}function y(r,e){if(!j(r))throw new Error(e);return r}export function optionalArray(r,e){if(r==null)return[];if(!Array.isArray(r))throw new Error(`${e} must be an array`);return r}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{matchesSuiteSelectors as a}from"../../domain/suite-selection.js";import{normalizePath as l}from"./paths.js";import{resolveRuntimeIsolationPolicy as i}from"./runtime-isolation.js";export function resolveTaskPolicy(o,t,r){return{locks:m(o,t,r),runtimeIsolation:i({testkit:o},t,r)}}function m(o,t,r){const n=l(r),s=new Set;for(const e of o.requirements.suites)if(a(t.testType,t.name,[e.selector]))for(const c of e.locks)s.add(c);for(const e of o.requirements.fileLocksByPath.get(n)??[])s.add(e);for(const e of o.fileMetadataByPath.get(n)?.locks??[])s.add(e);return[...s].sort()}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{isTypedTestFile as o}from"../../domain/identifiers.js";import{matchesSelectedTypes as i,matchesSuiteSelectors as a}from"../../domain/suite-selection.js";export function selectDiscoveredSuites(r,e){const l=new Set(e.fileNames?.map(p));return Object.fromEntries(Object.entries(r).map(([s,f])=>[s,e.group&&s!==e.group?[]:(f??[]).filter(t=>i(t.testType,e.typeValues??[])&&a(t.testType,t.name,e.suiteSelectors??[])).flatMap(t=>{const n=l.size?t.files.filter(c=>l.has(p(c))):t.files;return n.length?[{...t,files:n}]:[]})]))}export function selectsOnlyTypedFiles({fileNames:r=[],typeValues:e=[]}){return!i(void 0,e)||r.length>0&&r.every(o)}function p(r){return r.replaceAll("\\","/")}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{finalizeConfiguredInputs as
|
|
1
|
+
import{finalizeConfiguredInputs as c,finalizeConfiguredSteps as f,validateConfiguredCollection as p}from"../../steps/configured-steps.js";import{findDatabasePlaceholders as l}from"../../steps/environment.js";import{ensureExistingContainedPath as v,ensureExistingPath as m}from"./paths.js";const d=["migrate","seed","verify"];export function validateConfigCoverage(t){const n=new Set(t.map(r=>r.name));for(const r of t){for(const e of r.testkit.dependsOn)if(!n.has(e))throw new Error(`Service "${r.name}" depends on "${e}", but ${e} is not defined`);const o=r.testkit.databaseFrom;if(o&&!n.has(o))throw new Error(`Service "${r.name}" databaseFrom "${o}" is not defined`)}}export function validateServiceConfig({name:t,local:n,database:r,databaseFrom:o,runtime:e,dependsOn:s,productDir:i}){if(r&&o)throw new Error(`Service "${t}" cannot define both database and databaseFrom`);if(e.instances<1)throw new Error(`Service "${t}" runtime.instances must be a positive integer`);if(e.maxConcurrentTasks<=0)throw new Error(`Service "${t}" runtime.maxConcurrentTasks must be a positive integer when provided`);for(const a of s)if(a===t)throw new Error(`Service "${t}" cannot depend on itself`);if(w({name:t,local:n,database:r,runtime:e}),n?.cwd&&m(i,n.cwd,`Service "${t}" local.cwd`),e.toolchain?.cwd&&v(i,e.toolchain.cwd,`Service "${t}" runtime.toolchain.cwd`),r)for(const a of d)p({productDir:i,label:`Service "${t}" database.template.${a}`,steps:r.template[a]});p({productDir:i,label:`Service "${t}" database.template`,inputs:r?.template.inputs||[]}),p({productDir:i,label:`Service "${t}" runtime.prepare`,inputs:e.prepare.inputs,steps:e.prepare.steps})}function w({name:t,local:n,database:r,runtime:o}){const e=s=>i=>{const a=l(i).at(0);if(a)throw new Error(`Service "${t}" ${s} uses ${a.placeholder}. Database placeholders resolve only in env values; commands read DATABASE_URL or a service env entry at run time.`);return i};if(n&&(e("local.start")(n.start),e("local.cwd")(n.cwd)),c(o.prepare.inputs,e("runtime.prepare.inputs")),f(o.prepare.steps,e("runtime.prepare.steps")),r){c(r.template.inputs,e("database.template.inputs"));for(const s of d)f(r.template[s],e(`database.template.${s}`))}}
|
package/dist/database/admin.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{randomUUID as
|
|
2
|
-
`)}function
|
|
1
|
+
import{randomUUID as M}from"node:crypto";import{managedHostProcessInfrastructureError as _,runManagedHostProcess as L,startManagedHostProcess as h}from"../process/index.js";import{LOCAL_ADMIN_DB as p,LOCAL_ADMIN_QUERY_RETRY_INTERVAL_MS as x,LOCAL_ADMIN_QUERY_RETRY_MS as P,LOCAL_DROP_DATABASE_POLL_INTERVAL_MS as $,LOCAL_DROP_DATABASE_TIMEOUT_MS as I}from"./constants.js";import{buildDatabaseUrl as v,escapeIdentifier as d,escapeSqlLiteral as E}from"./naming.js";import{runDockerPsql as N}from"./postgres/index.js";import{readDynamicProperty as b}from"../kernel/unknown-values.js";import{toError as k}from"../kernel/error-serialization.js";export{v as buildDatabaseUrl};export const ADMIN_QUERY_COMMAND_TIMEOUT_MS=15e3;export async function databaseExists(t,e,o={}){return(await runAdminQuery(t,["-tAc",`SELECT 1 FROM pg_database WHERE datname = '${E(e)}'`],o)).trim()==="1"}export async function createEmptyDatabase(t,e,o={}){await runAdminQuery(t,["-c",`CREATE DATABASE "${d(e)}"`],o)}export async function cloneDatabaseFromTemplate(t,e,o,r={}){await runAdminQuery(t,["-c",`CREATE DATABASE "${d(e)}" TEMPLATE "${d(o)}"`],r)}export async function dropDatabaseWithForceOrDrain(t,e,o={}){const r={signal:o.signal};try{await runAdminQuery(t,["-c",`DROP DATABASE IF EXISTS "${d(e)}" WITH (FORCE)`],r);return}catch(s){if(!U(s))throw s}if(!await databaseExists(t,e,r))return;let a=!1;try{await runAdminQuery(t,["-c",`ALTER DATABASE "${d(e)}" WITH ALLOW_CONNECTIONS false`],r),a=!0,await waitForDatabaseConnectionsToDrain(t,e,{signal:o.signal}),await runAdminQuery(t,["-c",`DROP DATABASE IF EXISTS "${d(e)}"`],r),a=!1}finally{a&&await databaseExists(t,e)&&await runAdminQuery(t,["-c",`ALTER DATABASE "${d(e)}" WITH ALLOW_CONNECTIONS true`]).catch(()=>{})}}export async function waitForDatabaseConnectionsToDrain(t,e,o={}){const r=o.timeoutMs??I,a=Date.now()+r,s={signal:o.signal};for(;;){O(o.signal),await W(t,e,s);const n=await F(t,e,s);if(n===0)return;if(Date.now()>=a)throw new Error(`Timed out waiting for database "${e}" connections to close (${String(n)} remaining)`);await A($,o.signal)}}export async function runAdminQuery(t,e,o={}){const r=buildAdminQueryCommandArgs(t,e),a=Date.now();for(;;){O(o.signal);let s=null;try{let n;if(C(t)){const i=`testkit-admin-${M()}`,g=(c,m)=>L({args:buildAdminQueryCommandArgs(t,["-tAc",`SELECT ${c} FROM pg_stat_activity WHERE application_name = '${i}'`]),cwd:process.cwd(),displayLabel:`Docker Postgres admin session for ${t.containerName}`,env:process.env,executable:"docker",signal:m,timeoutMs:ADMIN_QUERY_COMMAND_TIMEOUT_MS}),D=h({args:["exec","-e",`PGAPPNAME=${i}`,...r.slice(1)],cwd:process.cwd(),displayLabel:`Docker Postgres admin query for ${t.containerName}`,env:process.env,executable:"docker",outputMode:"bounded-capture",recoveryIdentity:"none",signal:o.signal,timeoutMs:null}),u=new AbortController,y=(async()=>{for(let c=0;c<2;){await A(ADMIN_QUERY_COMMAND_TIMEOUT_MS,u.signal);const m=await g("count(*)",u.signal);if(u.signal.aborted)return;if(m.timedOut||m.exitCode!==0)break;c=m.stdout.trim()==="0"?c+1:0}u.abort("stalled"),await D.stop()})().catch(()=>{}),l=await D.completion.finally(()=>{u.abort()});await y;const T=u.signal.reason==="stalled";(l.cancelled||T)&&(s=await Promise.resolve().then(()=>g("pg_terminate_backend(pid)")).then(c=>H(t.containerName,c),k));const f=_(`Docker Postgres admin query for ${t.containerName}`,T?{...l,timedOut:!0}:l);if(f)throw B(f,s);n={exitCode:l.exitCode,stderr:l.stderr,stdout:l.stdout}}else{const i={...process.env,PGPASSWORD:t.password,...t.sslMode?{PGSSLMODE:t.sslMode}:{}};n=await N({args:r,env:i,timeoutMs:ADMIN_QUERY_COMMAND_TIMEOUT_MS,connection:{host:t.host,port:t.port,user:t.user,password:t.password,database:t.adminDatabase||p,sslMode:t.sslMode},signal:o.signal})}if(n.exitCode!=null&&n.exitCode!==0){const i=new Error(n.shortMessage||n.stderr||n.stdout||`psql failed with exit code ${String(n.exitCode)}`);throw Reflect.set(i,"stdout",n.stdout||""),Reflect.set(i,"stderr",n.stderr||""),i}const{stdout:R}=n;return R}catch(n){if(s!==null||Date.now()-a>=P||!isTransientAdminQueryConnectionError(n))throw n;await A(x,o.signal)}}}export function buildAdminQueryCommandArgs(t,e){return C(t)?["exec","-e",`PGPASSWORD=${t.password}`,t.containerId,"psql","-v","ON_ERROR_STOP=1","-U",t.user,"-d",t.adminDatabase||p,"-p",String(t.containerPort||5432),...e]:["-v","ON_ERROR_STOP=1","-h",t.host,"-p",String(t.port),"-U",t.user,"-d",t.adminDatabase||p,...e]}export function isTransientAdminQueryConnectionError(t){const e=S(t);return e.includes('connection to server on socket "/var/run/postgresql/.s.PGSQL.5432" failed')||e.includes("No such file or directory")||e.includes("the database system is starting up")||e.includes("could not connect to server")||e.includes("connection refused")||e.includes("Connection timed out")||e.includes("connection timed out")||e.includes("Operation timed out")||e.includes("i/o timeout")||e.includes("server closed the connection unexpectedly")||e.includes("terminating connection due to administrator command")}async function W(t,e,o){await runAdminQuery(t,["-c",["SELECT pg_terminate_backend(pid)","FROM pg_stat_activity",`WHERE datname = '${E(e)}' AND pid <> pg_backend_pid();`].join(" ")],o)}async function F(t,e,o){const r=await runAdminQuery(t,["-tAc",["SELECT COUNT(*)","FROM pg_stat_activity",`WHERE datname = '${E(e)}' AND pid <> pg_backend_pid();`].join(" ")],o);return Number.parseInt(r.trim(),10)||0}function U(t){const e=S(t);return e.includes('syntax error at or near "WITH"')||e.includes('option "force"')}function A(t,e){return new Promise((o,r)=>{if(e?.aborted){r(w(e));return}const a=setTimeout(s,t);e?.addEventListener("abort",n,{once:!0});function s(){e?.removeEventListener("abort",n),o()}function n(){clearTimeout(a),r(e?w(e):new Error("Database administration aborted"))}})}function O(t){if(t?.aborted)throw w(t)}function w(t){return t.reason instanceof Error?t.reason:new Error("Database administration aborted",{cause:t.reason})}function H(t,e){const o=`Docker Postgres admin session for ${t}`,r=_(o,e);return r!==null||e.exitCode===0?r:new Error(`${o} exited with code ${String(e.exitCode)}: ${e.stderr.trim()}`)}function B(t,e){if(e===null)return t;const o=new AggregateError([t,e],`${t.message}; terminating its server backend also failed: ${e.message}`,{cause:t});return Object.defineProperty(o,"result",{enumerable:!1,value:b(t,"result")}),o}function S(t){return["shortMessage","stderr","stdout","message"].map(e=>q(b(t,e)||"")).join(`
|
|
2
|
+
`)}function q(t){if(typeof t=="symbol")throw new TypeError("Cannot convert a Symbol value to a string");return String(t)}function C(t){return!!t.containerName}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export function bindDatabaseUrl(n){const e=new URL(n),s=e.searchParams.get("sslmode")||"disable";return{url:n,host:e.hostname,port:Number(e.port||"5432"),database:e.pathname.replace(/^\//,""),user:decodeURIComponent(e.username),password:decodeURIComponent(e.password),sslMode:s,ssl:s!=="disable"}}export function lendDatabases(n,e){const s=new Map(e);for(const t of n){const a=t.testkit.databaseFrom,o=a?e.get(a):void 0;o&&s.set(t.name,o)}return s}
|
package/dist/database/cleanup.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{hasActiveRunManifest as A,hasActiveRunManifestWithTerminalAuthority as v}from"../process/runs/ownership.js";import{observeProcessCreationIdentity as l}from"../process/index.js";import{TESTKIT_PRODUCT_ID_LABEL as y,TESTKIT_RESOURCE_KIND_LABEL as g,TESTKIT_SCOPE_LABEL as k,buildProductIdentity as T,listManagedDockerContainers as L,listManagedDockerVolumes as P,removeDockerContainer as C,removeDockerVolume as w,stopDockerContainer as E}from"./docker.js";import{requireProductLifecycleFenceProductDir as i}from"../process/lifecycle-fence.js";import{hasRemainingLocalArtifacts as D,readOwnedStateValues as R}from"./state.js";export async function cleanupLocalPostgresDockerResources(e={}){return f({...e,force:!1})}export async function destroyLocalPostgresDockerResources(e,r={}){const t=i(e);return f({...r,assertMutationAuthority:()=>{if(i(e)!==t)throw new Error("Database destroy lifecycle authority changed products")},force:!0,productDir:t})}export async function cleanupTerminalRunLocalPostgresDockerResources(e,r,t={}){const s=i(e),a=()=>{assertTerminalRunDatabaseCleanupAuthority(e,r)};return a(),f({...t,assertMutationAuthority:a,force:!1,productDir:s,terminalRunAuthority:r})}export function assertTerminalRunDatabaseCleanupAuthority(e,r){const t=i(e);if(v(t,l,r))throw new Error(`Cannot clean terminal Testkit run ${r.runId} database resources while another product run is active or its terminal ownership is invalid`)}async function f(e){e.assertMutationAuthority?.();const r=T(e.productDir||process.cwd()),t=await L(r),s=await P(r),a=[],u=[],d=[],c=[],h=[];for(const o of t){if(!
|
|
1
|
+
import{hasActiveRunManifest as A,hasActiveRunManifestWithTerminalAuthority as v}from"../process/runs/ownership.js";import{observeProcessCreationIdentity as l}from"../process/index.js";import{TESTKIT_PRODUCT_ID_LABEL as y,TESTKIT_RESOURCE_KIND_LABEL as g,TESTKIT_SCOPE_LABEL as k,buildProductIdentity as T,listManagedDockerContainers as L,listManagedDockerVolumes as P,removeDockerContainer as C,removeDockerVolume as w,stopDockerContainer as E}from"./docker.js";import{requireProductLifecycleFenceProductDir as i}from"../process/lifecycle-fence.js";import{hasRemainingLocalArtifacts as D,readOwnedStateValues as R}from"./state.js";export async function cleanupLocalPostgresDockerResources(e={}){return f({...e,force:!1})}export async function destroyLocalPostgresDockerResources(e,r={}){const t=i(e);return f({...r,assertMutationAuthority:()=>{if(i(e)!==t)throw new Error("Database destroy lifecycle authority changed products")},force:!0,productDir:t})}export async function cleanupTerminalRunLocalPostgresDockerResources(e,r,t={}){const s=i(e),a=()=>{assertTerminalRunDatabaseCleanupAuthority(e,r)};return a(),f({...t,assertMutationAuthority:a,force:!1,productDir:s,terminalRunAuthority:r})}export function assertTerminalRunDatabaseCleanupAuthority(e,r){const t=i(e);if(v(t,l,r))throw new Error(`Cannot clean terminal Testkit run ${r.runId} database resources while another product run is active or its terminal ownership is invalid`)}async function f(e){e.assertMutationAuthority?.();const r=T(e.productDir||process.cwd()),t=await L(r),s=await P(r),a=[],u=[],d=[],c=[],h=[];for(const o of t){if(!b(o))continue;const n=M(o,r,e);n.action==="remove"?a.push({...o,action:"remove",reason:n.reason}):n.action==="stop"?a.push({...o,action:"stop",reason:n.reason}):n.reason&&u.push({...o,reason:n.reason})}for(const o of s){if(!I(o))continue;const n=_(o,r,e);n.action==="remove"?a.push({...o,action:"remove",reason:n.reason,resourceType:"volume"}):n.reason&&u.push({...o,reason:n.reason,resourceType:"volume"})}if(!e.dryRun)for(const o of a)e.assertMutationAuthority?.(),o.action==="stop"?await E(o.id,o.name)?h.push(o):c.push(p(o)):o.resourceType==="volume"?await w(o.name,o)?d.push(o):c.push(p(o)):await C(o.id,o.name)?d.push(o):c.push(p(o));return{removed:e.dryRun?[]:d,stopped:e.dryRun?[]:h,skipped:e.dryRun?[]:c,targets:a,kept:u}}function p(e){return e.resourceType==="volume"?{...e,action:"skip",intendedAction:"remove",reason:"resource-changed-or-disappeared-before-mutation",selectionReason:e.reason}:{...e,action:"skip",intendedAction:e.action==="stop"?"stop":"remove",reason:"resource-changed-or-disappeared-before-mutation",selectionReason:e.reason}}function M(e,r,t){return e.labels[y]!==r.id?{action:"keep",reason:"different-product"}:t.force?{action:"remove",reason:"destroy-current-product"}:m(r.dir,t)?{action:"keep",reason:"current-product-active"}:D(r.dir)?R(r.dir,["container_name","local_container_name"]).has(e.name)?t.stopIdle&&e.running&&!m(r.dir,t)?{action:"stop",reason:"current-product-idle"}:{action:"keep",reason:"current-product-referenced"}:{action:"remove",reason:"current-product-unreferenced"}:{action:"remove",reason:"current-product-no-local-artifacts"}}function _(e,r,t){return e.labels[y]!==r.id?{action:"keep",reason:"different-product"}:t.force?{action:"remove",reason:"destroy-current-product"}:m(r.dir,t)?{action:"keep",reason:"current-product-active"}:D(r.dir)?R(r.dir,["volume_name"]).has(e.name)?{action:"keep",reason:"current-product-referenced"}:{action:"remove",reason:"current-product-unreferenced"}:{action:"remove",reason:"current-product-no-local-artifacts"}}function b(e){const{labels:r}=e;return r[g]==="postgres-container"&&r[k]==="local-postgres"}function I(e){const{labels:r}=e;return r[g]==="postgres-volume"&&r[k]==="local-postgres"}export function hasActiveProductRuntime(e){return A(e,l)}function m(e,r){return r.terminalRunAuthority===void 0?hasActiveProductRuntime(e):v(e,l,r.terminalRunAuthority)}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{runDockerPsql as E}from"./postgres/index.js";import{awaitCapturedResult as
|
|
1
|
+
import{runDockerPsql as E}from"./postgres/index.js";import{awaitCapturedResult as M}from"../evidence/step-output.js";import{applySetupNodeOptions as _,runConfiguredStep as D}from"../steps/run.js";import{announceResolvedToolchain as R,applyToolchainEnv as y,resolveConfiguredToolchain as A}from"../toolchains/index.js";import{resolveConfiguredCwd as P,resolveConfiguredPath as N,summarizeConfiguredStep as f}from"../steps/configured-steps.js";const U=3600*1e3;export async function runConfiguredSteps({config:e,steps:n=[],env:O,labelPrefix:C,ownedContainer:p=null,reporter:s=null,setupRegistry:o=null,parentOperation:b=null,startIndex:c=0,afterStep:h=null,signal:r,stepTimeoutMs:g}){if(n.length===0)return;const v=L(g),a=await A(e,{signal:r});await R(e,a,s);for(const[w,t]of n.entries()){const T=c+w+1,m=`${C}:${e.name}:${String(T)}`,u=o?.start({config:e,stage:m,kind:"setup-step",summary:f(t),parentId:$(b)});s?.phaseStarted?.(m);try{const i=y(_(O),a),l={livePrefix:`[${e.runtimeLabel||e.name}:${e.name}]`,liveWriter:s?.outputMode==="debug"?d=>{s.writeDebugLine?.(d)}:null,logRecord:u?._logRecord||null};if(t.kind==="sql-file"){const d=await E({databaseUrl:i.DATABASE_URL,args:[i.DATABASE_URL,"-v","ON_ERROR_STOP=1","-X","-f",N(e.productDir,t.cwd,t.path)],cwd:P(e.productDir,t.cwd),env:i,ownedContainer:p,signal:r,timeoutMs:v});M(d,l)}else await D(e,t,i,a,{...l,ownedContainer:p,signal:r,timeoutMs:v});const S=u&&o?o.finish(u,{status:"passed",summary:f(t)}):null;S&&s?.setupOperationFinished?.(S)}catch(i){const l=u&&o?o.finish(u,{status:"failed",summary:f(t),error:I(i)}):null;throw l&&s?.setupOperationFinished?.(l),i}h&&await h({step:t,index:c+w,stepNumber:T,label:m})}}function $(e){return typeof e=="string"?e||null:e?.id||null}function I(e){return(typeof e!="object"||e===null)&&typeof e!="function"?e:Reflect.get(e,"message")||e}function L(e){const n=e??U;if(!Number.isSafeInteger(n)||n<=0)throw new Error("Configured step timeout must be a positive safe integer in milliseconds");return n}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import
|
|
2
|
-
`)}else{const
|
|
3
|
-
`)}}return Buffer.byteLength(
|
|
1
|
+
import H from"node:crypto";import O from"node:fs";import I from"node:http";import E from"node:path";import{MAX_SUPERVISED_PROCESS_CAPTURE_BYTES as L}from"../process/process-limits.js";import{managedHostProcessInfrastructureError as _,normalizeManagedHostEnvironment as B,resolveManagedHostExecutable as U,runManagedHostProcess as K}from"../process/index.js";const v=5e3,F=256*1024,J=8*1024*1024,N=L/2,Y=["DOCKER_HOST","DOCKER_TLS","DOCKER_TLS_VERIFY","DOCKER_CERT_PATH","DOCKER_API_VERSION","DOCKER_CUSTOM_HEADERS"];let b,D=!1;export async function runDockerMetadataCommand(e,r,t,o,f){const i=performance.now(),S=i+t,y=()=>Math.floor(S-performance.now()),k=async(d,s)=>{const u=performance.now();try{return await f(d)}catch(w){throw P(w,`the direct read ${s}, then the docker CLI ran ${A(u)}`)}},g=()=>{const d=V(""),s=_(r,{...d,exitCode:null,cancelled:o?.aborted===!0,timedOut:o?.aborted!==!0});throw P(s??new Error(`${r} exceeded its managed deadline`),`the direct read took ${A(i)}, and the docker CLI did not run`)};if(o?.aborted||D)return k(Math.max(1,y()),"was skipped after an earlier one timed out");const n=new AbortController,m=()=>{n.abort(o?.reason)};o?.addEventListener("abort",m,{once:!0});const a=setTimeout(()=>{n.signal.aborted||(D=!0,n.abort(new Error("Docker metadata probe timed out")))},Math.min(v,t));let c;try{const d=await R(n.signal);if(d!==null){let s=b?.key===d.key?b.value:void 0;s===void 0&&(s=await z(d,y,n.signal),n.signal.aborted||(b={key:d.key,value:s})),s!==null&&(c=await W(s,e,n.signal),c!==void 0&&(await R(n.signal))?.key!==d.key&&(c=void 0,b=void 0))}}catch(d){if(l(l(d)?.result)?.terminationError)throw d;c=void 0}finally{clearTimeout(a),o?.removeEventListener("abort",m)}const p=`took ${A(i)}`,h=y();return(o?.aborted||h<=0)&&g(),c!==void 0&&!n.signal.aborted?V(c):k(h,p)}function A(e){return`${((performance.now()-e)/1e3).toFixed(1)} s`}function P(e,r){const t=l(l(e)?.result);if(t?.timedOut!==!0||!(e instanceof Error))return e;const o=new Error(`${e.message}: ${r}`,{cause:e});return Object.defineProperty(o,"result",{enumerable:!1,value:t}),o}async function R(e){e.throwIfAborted();const r=process.cwd(),t=B(process.env),o=U("docker",r,t),f=u=>Q(t,u);if(Y.some(u=>f(u)!==""))return null;const i=f(process.platform==="win32"?"USERPROFILE":"HOME"),S=f("DOCKER_CONFIG");if(!S&&(!i||!E.isAbsolute(i)))return null;const y=E.resolve(r,S||E.join(i,".docker")),k=await T(E.join(y,"config.json"),e),g=k?.toString("utf8"),n=g===void 0?{}:l(JSON.parse(g));if(n===null||g!==void 0&&G(g))return null;for(const u of Object.keys(n))if(u.toLowerCase()==="httpheaders"&&(l(n[u])===null||Object.keys(n[u]).length>0)||u.toLowerCase()==="currentcontext"&&u!=="currentContext")return null;if(n.currentContext!==void 0&&typeof n.currentContext!="string")return null;const m=f("DOCKER_CONTEXT")||n.currentContext||"default";let a=null,c=null,p=null,h=null;if(m!=="default"){const u=H.createHash("sha256").update(m).digest("hex");if(a=E.join(y,"contexts","meta",u),c=await T(E.join(a,"meta.json"),e),c===null)return null;const w=l(JSON.parse(c.toString("utf8"))),C=l(l(w?.Endpoints)?.docker);if(w?.Name!==m||typeof C?.Host!="string"||C.SkipTLSVerify!==!1)return null;p=C.Host;try{if(h=await O.promises.readdir(E.join(y,"contexts","tls",u)),h.length!==0)return null}catch(j){if(!$(j,"ENOENT"))return null}}const d=await O.promises.realpath(o),s=await O.promises.stat(d,{bigint:!0});return e.throwIfAborted(),{executable:o,cwd:r,env:t,context:m,metadataPath:a,endpoint:p,key:JSON.stringify([r,Object.entries(t).sort(([u],[w])=>u<w?-1:u>w?1:0),o,d,s.dev.toString(),s.ino.toString(),s.size.toString(),s.mtimeNs.toString(),s.ctimeNs.toString(),y,k?.toString("base64")??null,c?.toString("base64")??null,h])}}async function z(e,r,t){const o=async m=>{t.throwIfAborted();const a=Math.min(v,r());if(a<=0)return null;const c=await K({executable:e.executable,args:m,cwd:e.cwd,env:e.env,displayLabel:"Docker metadata transport resolution",timeoutMs:a,signal:t}),p=_("Docker metadata transport resolution",c);if(c.terminationError&&p!==null)throw p;return c.exitCode!==0||p!==null?null:JSON.parse(c.stdout)},f=await o(["context","inspect"]),i=Array.isArray(f)&&f.length===1?l(f[0]):null,S=l(l(i?.Endpoints)?.docker),y=l(i?.TLSMaterial),k=l(i?.Storage);if(i?.Name!==e.context||typeof S?.Host!="string"||S.SkipTLSVerify!==!1||y===null||Object.keys(y).length!==0||e.metadataPath!==null&&(k?.MetadataPath!==e.metadataPath||S.Host!==e.endpoint))return null;const g=X(S.Host);if(g===null)return null;const n=l(await o(["version","--format","{{json .Client}}"]));return typeof n?.Version!="string"||!/^29\.\d+\.\d+$/u.test(n.Version)||n.Context!==e.context||typeof n.Os!="string"||!x(n.DefaultAPIVersion)||!x(n.ApiVersion)?null:{key:e.key,socketPath:g,userAgent:`Docker-Client/${n.Version} (${n.Os})`,maximum:n.DefaultAPIVersion,negotiated:n.ApiVersion,server:null}}async function W(e,r,t){const o=q(r);if(o===null)return;const f=new I.Agent({keepAlive:!0,maxSockets:1}),i=new Map;let S;const y=k=>new Promise((g,n)=>{const m=I.get({socketPath:e.socketPath,path:k,agent:f,signal:t,headers:{Host:"api.moby.localhost","User-Agent":e.userAgent}},a=>{const c=[];let p=0;a.on("data",h=>{p+=h.length,p>J?m.destroy(new Error("Docker metadata response exceeded its byte limit")):c.push(h)}),a.once("error",n),a.once("end",()=>{a.statusCode!==200||!a.complete?n(new Error("Docker metadata response was not a complete success")):g({headers:a.headers,body:Buffer.concat(c,p)})})});m.once("error",n),m.once("socket",a=>{i.has(a)||i.set(a,new Promise(c=>a.once("close",()=>{c()}))),S!==void 0&&a!==S&&m.destroy(new Error("Docker metadata connection changed during negotiation")),S=a})});try{const k=await y("/_ping"),g=l(JSON.parse((await y("/version")).body.toString("utf8"))),n=g?.ApiVersion,m=g?.MinAPIVersion;if(!x(n)||!x(m)||k.headers["api-version"]!==n)return;const a=M(e.maximum,n)<=0?e.maximum:n;if(a!==e.negotiated||M(a,m)<0){b=void 0;return}const c=JSON.stringify(g);if(e.server!==null&&e.server!==c){b=void 0;return}e.server=c;const p=await y(`/v${a}${o.path}`),h=JSON.parse(p.body.toString("utf8"));let d;if(o.kind==="container"||o.kind==="volume"){const s=l(h)?.[o.kind==="container"?"Id":"Name"];if(typeof s!="string"||s.length===0)return;const u=JSON.stringify([h],null,4).replace(/[<>&\u2028\u2029]/gu,w=>`\\u${w.charCodeAt(0).toString(16).padStart(4,"0")}`);if(p.body.length+Buffer.byteLength(u)+16>N)return;d=`[${p.body.toString("utf8")}]`}else if(o.kind==="containers"){if(!Array.isArray(h)||h.some(s=>typeof l(s)?.Id!="string"||!/^[a-f0-9]{64}$/u.test(l(s)?.Id)))return;d=h.map(s=>(l(s)?.Id).slice(0,12)).join(`
|
|
2
|
+
`)}else{const s=l(h)?.Volumes;if(s===null)d="";else{if(!Array.isArray(s)||s.length>1)return;const u=s.map(w=>l(w)?.Name);if(u.some(w=>typeof w!="string"||w.length===0))return;d=u.join(`
|
|
3
|
+
`)}}return Buffer.byteLength(d)+1<=N?d:void 0}finally{f.destroy();for(const k of i.keys())k.destroy();await Promise.all(i.values())}}function q(e){if(e.length===2&&e[0]==="inspect"&&e[1]&&!e[1].startsWith("-"))return{kind:"container",path:`/containers/${encodeURIComponent(e[1])}/json`};if(e.length===3&&e[0]==="volume"&&e[1]==="inspect"&&e[2]&&!e[2].startsWith("-"))return{kind:"volume",path:`/volumes/${encodeURIComponent(e[2])}`};let r,t;if(e[0]==="ps"&&e[1]==="-a"&&e[2]==="--format"&&e[3]==="{{.ID}}")r="containers",t=e.slice(4);else if(e[0]==="volume"&&e[1]==="ls"&&e[2]==="-q")r="volumes",t=e.slice(3);else return null;const o=[];for(let i=0;i<t.length;i+=2){if(t[i]!=="--filter"||!t[i+1]?.startsWith("label="))return null;o.push(t[i+1].slice(6))}const f=encodeURIComponent(JSON.stringify({label:o}));return{kind:r,path:r==="containers"?`/containers/json?all=true&filters=${f}`:`/volumes?filters=${f}`}}async function T(e,r){const t=O.createReadStream(e,{signal:r,highWaterMark:65536}),o=[];let f=0;try{for await(const i of t){if(f+=i.length,f>F)throw new Error("Docker metadata configuration exceeded its byte limit");o.push(i)}return Buffer.concat(o,f)}catch(i){if($(i,"ENOENT"))return null;throw i}finally{t.destroy(),t.closed||await new Promise(i=>t.once("close",i))}}function X(e){if(process.platform==="win32"){const t=/^npipe:\/\/\/\/\.\/pipe\/([a-z0-9_.-]+)$/iu.exec(e);return t===null?null:`\\\\.\\pipe\\${t[1]}`}if(!e.startsWith("unix:///"))return null;const r=e.slice(7);return r.includes("\0")||r.includes("?")||r.includes("#")?null:r}function G(e){for(const r of e.matchAll(/"(?:\\.|[^"\\])*"/gu)){let t=r.index+r[0].length;for(;t<e.length&&/\s/u.test(e[t]);)t+=1;if(e[t]!==":")continue;const o=JSON.parse(r[0]);if(typeof o=="string"&&/^httpheaders$/iu.test(o))return!0}return!1}function Q(e,r){return process.platform!=="win32"?e[r]||"":Object.entries(e).find(([t])=>t.toUpperCase()===r)?.[1]||""}function l(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)?e:null}function $(e,r){return l(e)?.code===r}function x(e){return typeof e=="string"&&/^1\.[1-9]\d*$/u.test(e)&&Number.isSafeInteger(Number(e.slice(2)))}function M(e,r){return Number(e.slice(2))-Number(r.slice(2))}function V(e){return{cancelled:!1,error:null,exitCode:0,exitSignal:null,outputLimitExceeded:!1,stderr:"",stdout:e,terminationError:null,timedOut:!1}}
|
package/dist/database/docker.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import{runDockerMetadataCommand as f}from"./docker-metadata.js";import
|
|
2
|
-
${t.stdout}`.toLowerCase();return/\bno such (?:container|object)\b/u.test(e)}function
|
|
1
|
+
import{runDockerMetadataCommand as f}from"./docker-metadata.js";import{managedHostProcessInfrastructureError as h,runManagedHostProcess as C}from"../process/index.js";import{canonicalizeProductDir as I,hashString as T}from"./naming.js";export const TESTKIT_MANAGED_LABEL="com.elench.testkit.managed",TESTKIT_RESOURCE_KIND_LABEL="com.elench.testkit.resource.kind",TESTKIT_RESOURCE_NAME_LABEL="com.elench.testkit.resource.name",TESTKIT_PRODUCT_DIR_LABEL="com.elench.testkit.product.dir",TESTKIT_PRODUCT_ID_LABEL="com.elench.testkit.product.id",TESTKIT_SCOPE_LABEL="com.elench.testkit.scope",TESTKIT_CACHE_POLICY_LABEL="com.elench.testkit.cache.policy",TESTKIT_CREATED_AT_LABEL="com.elench.testkit.created-at",TESTKIT_RESOURCE_GENERATION_LABEL="com.elench.testkit.resource.generation";const w=4e3,g=String;export const DOCKER_OWNERSHIP_OPERATION_TIMEOUTS_MS=Object.freeze({inspect:3e4,list:3e4,remove:12e4,stop:3e4});export function buildProductIdentity(t){const e=I(t||process.cwd());return{dir:e,id:T(e,24)}}export function buildDockerResourceLabels(t,e){if(!e.generation)throw new Error("Docker resource generation must be non-empty");const n=buildProductIdentity(t);return{[TESTKIT_MANAGED_LABEL]:"true",[TESTKIT_RESOURCE_KIND_LABEL]:e.kind,[TESTKIT_RESOURCE_NAME_LABEL]:e.name,[TESTKIT_PRODUCT_DIR_LABEL]:n.dir,[TESTKIT_PRODUCT_ID_LABEL]:n.id,[TESTKIT_SCOPE_LABEL]:e.scope||e.kind,[TESTKIT_CACHE_POLICY_LABEL]:e.cachePolicy||"ephemeral",[TESTKIT_CREATED_AT_LABEL]:e.createdAt||new Date().toISOString(),[TESTKIT_RESOURCE_GENERATION_LABEL]:e.generation}}export function dockerLabelArgs(t){return Object.entries(t).flatMap(([e,n])=>["--label",`${e}=${g(n)}`])}export async function inspectDockerContainer(t,e={}){const n=`Docker container inspect for ${t}`,r=["inspect",t],o=await f(r,n,DOCKER_OWNERSHIP_OPERATION_TIMEOUTS_MS.inspect,e.signal,c=>runDockerCommand(r,n,c,e.signal));if(o.exitCode!==0){if(m(o))return null;throw a(n,o)}return k(o,n,"Id")}export async function listManagedDockerContainers(t){return _(["--filter",`label=${TESTKIT_MANAGED_LABEL}=true`,"--filter",`label=${TESTKIT_PRODUCT_ID_LABEL}=${t.id}`])}export async function inspectDockerVolume(t,e={}){const n=`Docker volume inspect for ${t}`,r=["volume","inspect",t],o=await f(r,n,DOCKER_OWNERSHIP_OPERATION_TIMEOUTS_MS.inspect,e.signal,c=>runDockerCommand(r,n,c,e.signal));if(o.exitCode!==0){if(E(o))return null;throw a(n,o)}return k(o,n,"Name")}export async function listManagedDockerVolumes(t){return y(["--filter",`label=${TESTKIT_MANAGED_LABEL}=true`,"--filter",`label=${TESTKIT_PRODUCT_ID_LABEL}=${t.id}`])}export async function removeDockerContainer(t,e=t){const n=`Docker container removal for ${e}`,r=await runDockerCommand(["rm","-f","-v",t],n,DOCKER_OWNERSHIP_OPERATION_TIMEOUTS_MS.remove);if(r.exitCode===0)return r.stdout.trim().length>0;if(m(r))return!1;throw a(n,r)}export async function removeDockerVolume(t,e,n={}){const r=normalizeDockerVolumeInspect(await inspectDockerVolume(t,n));if(r===null||!sameDockerVolumeGeneration(r,e))return!1;const o=`Docker volume removal for ${t}`,c=await runDockerCommand(["volume","rm","-f",t],o,DOCKER_OWNERSHIP_OPERATION_TIMEOUTS_MS.remove,n.signal);if(c.exitCode===0)return!0;if(E(c))return!1;throw a(o,c)}export function sameDockerVolumeGeneration(t,e){return t.name===e.name&&t.createdAt===e.createdAt&&t.driver===e.driver&&t.mountpoint===e.mountpoint&&L(t.labels,e.labels)}export async function stopDockerContainer(t,e=t){const n=`Docker container stop for ${e}`,r=await runDockerCommand(["stop","--time","5",t],n,DOCKER_OWNERSHIP_OPERATION_TIMEOUTS_MS.stop);if(r.exitCode===0)return!0;if(m(r))return!1;throw a(n,r)}async function _(t){const e="Docker container inventory",n=["ps","-a","--format","{{.ID}}",...t],r=await f(n,e,DOCKER_OWNERSHIP_OPERATION_TIMEOUTS_MS.list,void 0,s=>runDockerCommand(n,e,s,void 0));if(r.exitCode!==0)throw a(e,r);const{stdout:o}=r,c=o.split(/\r?\n/).map(s=>s.trim()).filter(Boolean);if(c.length===0)return[];const i=[];for(const s of c){const p=await inspectDockerContainer(s),d=normalizeDockerContainerInspect(p);d&&i.push(d)}return i}async function y(t){const e="Docker volume inventory",n=["volume","ls","-q",...t],r=await f(n,e,DOCKER_OWNERSHIP_OPERATION_TIMEOUTS_MS.list,void 0,s=>runDockerCommand(n,e,s,void 0));if(r.exitCode!==0)throw a(e,r);const{stdout:o}=r,c=o.split(/\r?\n/).map(s=>s.trim()).filter(Boolean);if(c.length===0)return[];const i=[];for(const s of c){const p=await inspectDockerVolume(s),d=normalizeDockerVolumeInspect(p);d&&i.push(d)}return i}export function normalizeDockerContainerInspect(t){const e=l(t);if(!e||typeof e.Id!="string"||!e.Id)return null;const n=l(e.Config),r=l(e.State),o=g(e.Name||"").replace(/^\//,"");return{id:e.Id,shortId:e.Id.slice(0,12),name:o,image:u(n?.Image)||"",createdAt:u(e.Created),running:!!r?.Running,status:u(r?.Status)||"unknown",labels:D(n?.Labels)}}export function normalizeDockerVolumeInspect(t){const e=l(t);return!e||typeof e.Name!="string"||!e.Name?null:{name:e.Name,driver:u(e.Driver)||"",mountpoint:u(e.Mountpoint)||"",labels:D(e.Labels),createdAt:u(e.CreatedAt)}}function k(t,e,n){let r;try{r=JSON.parse(t.stdout)}catch(i){throw b(e,n,t,i)}const o=Array.isArray(r)?r:null,c=o?l(o[0]):null;if(o?.length!==1||!c||typeof c[n]!="string"||c[n].length===0)throw b(e,n,t);return c}function D(t){const e={},n=l(t);if(!n)return e;for(const[r,o]of Object.entries(n))typeof o=="string"&&(e[r]=o);return e}function L(t,e){const n=Object.entries(t).sort(([o],[c])=>o.localeCompare(c)),r=Object.entries(e).sort(([o],[c])=>o.localeCompare(c));return n.length===r.length&&n.every(([o,c],i)=>r[i]?.[0]===o&&r[i]?.[1]===c)}function l(t){return x(t)?t:null}function x(t){return typeof t=="object"&&t!==null&&!Array.isArray(t)}function u(t){return typeof t=="string"&&t?t:null}export async function runDockerCommand(t,e,n,r){const o=await C({args:t,cwd:process.cwd(),displayLabel:e,env:process.env,executable:"docker",signal:r,timeoutMs:n}),c=h(e,o);if(c)throw c;return o}function m(t){const e=`${t.stderr}
|
|
2
|
+
${t.stdout}`.toLowerCase();return/\bno such (?:container|object)\b/u.test(e)}function E(t){const e=`${t.stderr}
|
|
3
3
|
${t.stdout}`.toLowerCase();return/\bno such (?:object|volume)\b/u.test(e)}function a(t,e){const n=[e.stderr,e.stdout].filter(Boolean).join(`
|
|
4
4
|
`).trim(),r=n?`
|
|
5
|
-
${n.slice(-
|
|
5
|
+
${n.slice(-w)}`:"",o=new Error(`${t} exited with code ${String(e.exitCode)}${r}`);return Object.defineProperty(o,"result",{enumerable:!1,value:Object.freeze({...e})}),o}function b(t,e,n,r){const o=new Error(`${t} returned malformed Docker inspect output; expected exactly one record with a non-empty ${e}`,r===void 0?void 0:{cause:r});return Object.defineProperty(o,"result",{enumerable:!1,value:Object.freeze({...n})}),o}
|
package/dist/database/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export{cleanupOrphanedLocalInfrastructure,cleanupTerminalRunLocalInfrastructure}from"./lifecycle-cleanup.js";import
|
|
1
|
+
import{requireContainedTestkitStateDirectory as w}from"../process/runs/product-state.js";export{cleanupOrphanedLocalInfrastructure,cleanupTerminalRunLocalInfrastructure}from"./lifecycle-cleanup.js";import l from"fs";import i from"path";import{computeTemplateFingerprint as C}from"./fingerprint.js";import{buildDatabaseUrl as v,buildRuntimeDatabaseName as E,buildTaskDatabaseName as M,buildTemplateDatabaseName as q,hashString as B}from"./naming.js";import{getLocalLocksDir as L,getLocalServiceCacheDir as T,readStateValue as h}from"./state.js";import{createOwnedPgDumpClientImageResolver as V,runTemplateStage as W,runTemplateStep as K}from"./template-steps.js";import{materializeDatabaseConfig as U}from"../config/loader/index.js";import{applySourceSchemaCache as z,createSourceSchemaMismatchError as H,forceRefreshSourceSchemaCache as Q,prepareSourceSchemaCache as G,verifyLocalSchemaMatchesSource as J}from"./schema-source.js";import{cloneDatabaseFromTemplate as F,createEmptyDatabase as X,databaseExists as _,dropDatabaseWithForceOrDrain as p}from"./admin.js";import{withUserHostLocalPostgresActivity as f}from"./activity-gate.js";import{ensureLocalContainer as Y,loadExistingLocalContainer as g}from"./local-postgres.js";import{beginTemplateCacheRebuild as Z,writeCacheState as O,writeStateValue as D}from"./state-files.js";import{withProcessLock as N}from"../process/locks.js";import{ensureDatabaseRoles as ee}from"./roles.js";import{cleanupLocalPostgresDockerResources as ae,destroyLocalPostgresDockerResources as te}from"./cleanup.js";import{requireProductLifecycleFenceProductDir as I}from"../process/lifecycle-fence.js";export{dropDatabaseWithForceOrDrain,isTransientAdminQueryConnectionError,waitForDatabaseConnectionsToDrain}from"./admin.js";export{refreshDatabaseSchema,verifyDatabaseSchema}from"./schema-operations.js";const k="task-databases";export async function provisionDatabase(e,a={}){const t=e.testkit.database;if(!t)return null;const s=U(t),r={...e,stateDir:w(e.productDir,e.stateDir,`Service "${e.name}" database runtime state directory`),testkit:{...e.testkit,database:s}},c={reporter:a.progress?.reporter||null,setupRegistry:a.progress?.setupRegistry||null,signal:a.signal,skipSchemaSourceVerify:a.skipSchemaSourceVerify};return f(r.productDir,async()=>(l.mkdirSync(r.stateDir,{recursive:!0}),ne(r,c,a.provisionedSharedDatabases)),{signal:a.signal})}export async function destroyRuntimeDatabase({productDir:e,stateDir:a},t={}){const s=w(e,a,"Runtime database state directory");await f(e,async()=>{await le(e,s)},t)}export async function destroyServiceDatabaseCache(e,a,t={}){await f(e,()=>re(e,a),t)}async function re(e,a){const t=T(e,a);if(!l.existsSync(t))return;const s=L(e);l.mkdirSync(s,{recursive:!0}),await N(i.join(s,`template-${a}.lock`),async()=>{const r=await g(e);if(!r){l.rmSync(t,{recursive:!0,force:!0});return}const c=h(i.join(t,"template_database_name"));c&&await p(r,c),l.rmSync(t,{recursive:!0,force:!0})})}export async function destroyOwnedLocalDatabaseResources(e,a={}){const t=I(e);return f(t,()=>(I(e),te(e,a)))}export async function cleanupOwnedLocalDatabaseResources(e,a={}){return f(e,()=>se(e,a))}async function se(e,a){return ae({productDir:e,dryRun:!!a.dryRun,stopIdle:a.stopIdle!==!1})}export function isDatabaseStateDir(e){return l.existsSync(e)?l.existsSync(i.join(e,"database_backend")):!1}async function ne(e,a={},t){const s=e.testkit.database,r=e.productDir,c=e.name,u=me(e),d=L(r),n=T(r,c);l.mkdirSync(d,{recursive:!0}),l.mkdirSync(n,{recursive:!0});const o=await Y(r,s,{signal:a.signal}),b=await N(i.join(d,`template-${c}.lock`),async()=>{const y=await G(e,a),R=await C(e,{sourceSchemaState:y});return await ee(e,o,a),ie(e,o,n,R,{...a,sourceSchemaState:y})},{signal:a.signal,timeoutMs:null}),m=P(s.binding)==="shared"?t:void 0,S=await N(i.join(d,`runtime-${c}-${B(u,10)}.lock`),()=>oe(e,o,b,u,m,a.signal),{signal:a.signal,timeoutMs:null});return{container:o.containerName,databaseName:S.databaseName,templateFingerprint:S.templateFingerprint,url:v(o,S.databaseName)}}async function ie(e,a,t,s,r={}){const c=e.name;let u=s,d=r.sourceSchemaState||null,n=!1;for(;;){const o=h(i.join(t,"template_fingerprint")),b=h(i.join(t,"template_database_name")),m=q(c,u);if(o===u&&b&&await _(a,b,{signal:r.signal}))return r.setupRegistry?.recordCached({config:e,stage:"template",kind:"database-template",summary:"template cache hit"}),O(t,a,b,u),{databaseName:b,fingerprint:u};b&&b!==m&&await p(a,b,{signal:r.signal}),await _(a,m,{signal:r.signal})&&await p(a,m,{signal:r.signal}),Z(t,m);const S=v(a,m);if(await X(a,m,{signal:r.signal}),(await ce(e,a,m,S,{...r,sourceSchemaState:d,refreshedSourceAfterMismatch:n})).refreshSourceSchema){await p(a,m,{signal:r.signal}),d=await Q(e,d,r),n=!0,u=await C(e,{sourceSchemaState:d});continue}return O(t,a,m,u),{databaseName:m,fingerprint:u}}}async function ce(e,a,t,s,r={}){const c=r.setupRegistry?.start({config:e,stage:"template",kind:"database-template",summary:"template rebuild",recordLog:!1}),u={ownedContainer:{containerId:a.containerId,containerPort:a.containerPort},parentOperation:c,reporter:r.reporter||null,setupRegistry:r.setupRegistry||null,signal:r.signal},d=V(e,s,u);try{await z(e,s,r.sourceSchemaState,u);const n=async()=>{const y=await J(e,s,r.sourceSchemaState,{...u,resolveLocalSnapshotImage:d,skipSchemaSourceVerify:r.skipSchemaSourceVerify});if(y.status!=="mismatch")return null;if(r.refreshedSourceAfterMismatch)throw H(e,de(r.sourceSchemaState),y);return y},o=e.testkit.database?.template,b=!!(o?.migrate?.length||o?.seed?.length),m=async y=>{const R=o?.[y]||[];for(const[$,A]of R.entries())if(await K(e,y,A,$,s,u),await n())return!0;return!1};return await m("migrate")||await m("seed")||!b&&!!await n()?(j(c,r,{status:"passed",summary:"source schema refresh requested"}),{refreshSourceSchema:!0}):(await W(e,"verify",s,u),j(c,r,{status:"passed",summary:"template rebuild"}),{refreshSourceSchema:!1})}catch(n){throw j(c,r,{status:"failed",summary:"template rebuild",error:be(n)||n}),await p(a,t),n}}function j(e,a,t){const s=e&&a.setupRegistry?a.setupRegistry.finish(e,t):null;s&&a.reporter?.setupOperationFinished?.(s)}async function oe(e,a,t,s,r,c){const u=e.name,d=t.fingerprint,n=E(u,s,d);await x(a,e.stateDir);const o=h(i.join(e.stateDir,"local_database_name")),b=h(i.join(e.stateDir,"local_template_fingerprint")),m=!r?.has(n)&&(pe(e.testkit.database.reset)||b!==d||o!==n)||!await _(a,n,{signal:c});o&&o!==n&&await p(a,o,{signal:c}),m&&(await p(a,n,{signal:c}),await F(a,n,t.databaseName,{signal:c})),r?.add(n),l.mkdirSync(e.stateDir,{recursive:!0});const S={bindingKey:s,databaseName:n,templateDatabaseName:t.databaseName,templateFingerprint:d};return ue(e,a,S),S}function ue(e,a,t){D(i.join(e.stateDir,"database_backend"),""),D(i.join(e.stateDir,"local_database_name"),t.databaseName),D(i.join(e.stateDir,"local_template_fingerprint"),t.templateFingerprint),D(i.join(e.stateDir,"local_template_database_name"),t.templateDatabaseName),D(i.join(e.stateDir,"local_container_name"),a.containerName),a.volumeName&&D(i.join(e.stateDir,"volume_name"),a.volumeName),a.volumeMountPath&&D(i.join(e.stateDir,"volume_mount_path"),a.volumeMountPath)}function me(e){return P(e.testkit.database.binding)==="shared"?`${e.name}:shared`:e.runtimeId?`${e.name}:${String(e.runtimeId)}`:`${e.name}:${e.stateDir}`}async function le(e,a){const t=h(i.join(a,"local_database_name"));if(!t)return;const s=await g(e);s&&(await x(s,a),await p(s,t))}export async function cloneTaskDatabase({productDir:e,stateDir:a},t,s,r={}){const c=w(e,a,"Runtime database state directory");return f(e,async()=>{const u=h(i.join(c,"local_database_name")),d=h(i.join(c,"local_template_database_name")),n=await g(e,{signal:r.signal});if(!u||!d||!n)throw new Error(`Service "${t}" has no provisioned database to clone for this task`);const o=M(t,u,s),b=i.join(c,k,o);l.mkdirSync(i.dirname(b),{recursive:!0}),l.writeFileSync(b,"");try{await F(n,o,d,{signal:r.signal})}catch(m){throw await p(n,o),l.rmSync(b,{force:!0}),m}return{databaseName:o,url:v(n,o)}},{signal:r.signal})}export async function dropTaskDatabase({productDir:e,stateDir:a},t){const s=w(e,a,"Runtime database state directory");await f(e,async()=>{const r=await g(e);r&&await p(r,t),l.rmSync(i.join(s,k,t),{force:!0})})}export async function dropRecordedTaskDatabases({productDir:e,stateDir:a}){const t=w(e,a,"Runtime database state directory");l.existsSync(i.join(t,k))&&await f(e,async()=>{const s=await g(e);s&&await x(s,t)})}async function x(e,a){const t=i.join(a,k);if(l.existsSync(t))for(const s of l.readdirSync(t))await p(e,s),l.rmSync(i.join(t,s),{force:!0})}function de(e){if(!e?.active)throw new Error("Source schema mismatch requires active source schema state");return e}function be(e){return typeof e!="object"&&typeof e!="function"||e===null?void 0:Reflect.get(e,"message")}function pe(e){return e!==!1}function P(e){return e||"per-runtime"}function Fe(e){return e}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{recordWork as M}from"../kernel/work-metrics.js";import{runDockerMetadataCommand as oe}from"./docker-metadata.js";import{randomUUID as C}from"node:crypto";import ie from"fs";import u from"path";import{LOCAL_ADMIN_DB as D,LOCAL_IMAGE as R,LOCAL_MAX_CONNECTIONS as N,LOCAL_PASSWORD as $,LOCAL_POLL_INTERVAL_MS as ae,LOCAL_READY_TIMEOUT_MS as j,LOCAL_USER as V}from"./constants.js";import{buildContainerName as se,buildVolumeName as x}from"./naming.js";import{getLocalInfraDir as U,readStateValue as l}from"./state.js";import{TESTKIT_CREATED_AT_LABEL as z,TESTKIT_RESOURCE_GENERATION_LABEL as v,buildDockerResourceLabels as G,dockerLabelArgs as H,inspectDockerVolume as h,normalizeDockerVolumeInspect as E,removeDockerVolume as ce,runDockerCommand as L}from"./docker.js";import{writeLocalInfraState as ue}from"./state-files.js";import{isUnknownArray as P,isUnknownRecord as m}from"../kernel/unknown-values.js";const le="/var/lib/postgresql/data",fe="/var/lib/postgresql",I=String;export const LOCAL_POSTGRES_DOCKER_TIMEOUTS_MS=Object.freeze({inspect:3e4,readinessAttempt:1e4,remove:12e4,run:10*6e4,start:12e4,volumeCreate:12e4});export async function ensureLocalContainer(e,n={},r={}){const o=U(e);ie.mkdirSync(o,{recursive:!0});const t=l(u.join(o,"container_name"))||se(e),i=l(u.join(o,"volume_name")),a=n.image||l(u.join(o,"image"))||R,c=l(u.join(o,"image")),f=resolveLocalPostgresVolumeTarget(a),w=n.user||l(u.join(o,"user"))||V,b=n.password||l(u.join(o,"password"))||$,_=F(n.maxConnections||l(u.join(o,"max_connections"))||N);let s=await inspectContainer(t,r);s&&p(s,e,t);const te=!s&&!i;let d=Y(s,f)||i||x(e,C());s&&await W(e,d,r);const A=s?.Config?.Image||c||null;if(s&&!isReusableLocalPostgresContainer(s,{image:a,maxConnections:_,volumeMountPath:f,volumeName:d})&&(await stopAndRemoveContainer(s.Id,t,r),A&&A!==a&&(await be(e,d,r),d=x(e,C())),s=null),s)s.State?.Running||(await y(["start",s.Id],`Docker local Postgres container start for ${t}`,LOCAL_POSTGRES_DOCKER_TIMEOUTS_MS.start,r.signal),s=await inspectContainer(t,r),s&&p(s,e,t));else{d=te?await K(e,r):await ge(e,d,r);const re=X(e,t,C());await y(buildDockerRunArgs({containerName:t,labels:re,volumeName:d,volumeMountPath:f,user:w,password:b,image:a,maxConnections:_}),`Docker local Postgres container creation for ${t}`,LOCAL_POSTGRES_DOCKER_TIMEOUTS_MS.run,r.signal),s=await inspectContainer(t,r),s&&p(s,e,t)}const O=q(s);if(!s||!O)throw new Error(`Could not determine host port for local database container ${t}`);const S={containerName:t,containerId:s.Id,volumeName:d,volumeMountPath:f,image:a,user:w,password:b,maxConnections:_,host:"127.0.0.1",port:Number(O),containerPort:5432};return await J(S,r),ue(o,S),S}export function buildDockerRunArgs({containerName:e,labels:n,volumeName:r,volumeMountPath:o,user:t,password:i,image:a,maxConnections:c}){return["run","-d","--name",e,...H(n),"-p","127.0.0.1::5432","--mount",`type=volume,source=${r},target=${o}`,"-e",`POSTGRES_USER=${t}`,"-e",`POSTGRES_PASSWORD=${i}`,"-e",`POSTGRES_DB=${D}`,a,"postgres","-c",`max_connections=${String(c)}`]}export async function loadExistingLocalContainer(e,n={}){const r=U(e),o=l(u.join(r,"container_name"));if(!o)return null;const t=await inspectContainer(o,n);if(!t)return null;p(t,e,o);const i=t.Config?.Image||l(u.join(r,"image"))||R,a=l(u.join(r,"volume_mount_path"))||resolveLocalPostgresVolumeTarget(i),c=Y(t,a)||l(u.join(r,"volume_name"));if(!c)return null;await W(e,c,n),t.State?.Running||await y(["start",t.Id],`Docker local Postgres container start for ${o}`,LOCAL_POSTGRES_DOCKER_TIMEOUTS_MS.start,n.signal);const f=t.State?.Running?t:await inspectContainer(t.Id,n);f&&p(f,e,o);const w=q(f);if(!f||!w)return null;const b={containerName:o,containerId:f.Id,volumeName:c,volumeMountPath:a,image:i,user:l(u.join(r,"user"))||V,password:l(u.join(r,"password"))||$,maxConnections:F(l(u.join(r,"max_connections"))||N),host:"127.0.0.1",port:Number(w),containerPort:B(f)==="host"?Number(w):5432};return await J(b,n),b}function me(e,n){const r=e.Config?.Cmd;return Array.isArray(r)?r.includes("-c")&&r.includes(`max_connections=${String(n)}`):!1}function de(e,n,r){return(Array.isArray(e.Mounts)?e.Mounts:[]).some(t=>t?.Type==="volume"&&t.Name===n&&t.Destination===r)}export function isReusableLocalPostgresContainer(e,n){return e.Config?.Image===n.image&&me(e,n.maxConnections)&&de(e,n.volumeName,n.volumeMountPath)&&B(e)==="bridge"}function B(e){return e.HostConfig?.NetworkMode==="host"?"host":"bridge"}function q(e){return e?.NetworkSettings?.Ports?.["5432/tcp"]?.[0]?.HostPort||null}function F(e){const n=Number(e);if(!Number.isInteger(n)||n<10)throw new Error("database.maxConnections must be an integer greater than or equal to 10");return n}export async function inspectContainer(e,n={}){M("database.containerInspections");const r=`Docker local Postgres container inspect for ${e}`,o=["inspect",e],t=await oe(o,r,LOCAL_POSTGRES_DOCKER_TIMEOUTS_MS.inspect,n.signal,c=>L(o,r,c,n.signal));if(t.exitCode!==0){if(ne(t))return null;throw T(r,t)}let i;try{i=JSON.parse(t.stdout)}catch(c){throw ee(r,t,c)}const a=P(i)&&i.length===1?normalizeLocalPostgresContainerInspect(i[0]):null;if(!a?.Id)throw ee(r,t);return{...a,Id:a.Id}}export function normalizeLocalPostgresContainerInspect(e){return m(e)?{Config:he(e.Config),HostConfig:Pe(e.HostConfig),Id:g(e.Id),Mounts:ke(e.Mounts),NetworkSettings:ye(e.NetworkSettings),State:_e(e.State)}:null}export async function stopAndRemoveContainer(e,n=e,r={}){const o=`Docker local Postgres container removal for ${n}`,t=await L(["rm","-f","-v",e],o,LOCAL_POSTGRES_DOCKER_TIMEOUTS_MS.remove,r.signal);if(!(t.exitCode===0||ne(t)))throw T(o,t)}async function ge(e,n,r={}){const o=k(e,n,"existing-generation"),t=await h(n,r);if(t){if(!matchesStableDockerLabels(Z(t)||{},o))throw new Error(`Docker volume ${n} already exists but is not owned by this Testkit product. Remove or rename it before running Testkit.`);return n}return K(e,r)}async function K(e,n){const r=C(),o=x(e,r),t=k(e,o,r);await y(["volume","create",...H(t),o],`Docker local Postgres volume creation for ${o}`,LOCAL_POSTGRES_DOCKER_TIMEOUTS_MS.volumeCreate,n.signal);const i=E(await h(o,n));if(i===null||!we(i.labels,t))throw new Error(`Docker volume ${o} was not published with the expected Testkit generation identity`);return o}async function W(e,n,r){const o=k(e,n,"existing-generation"),t=E(await h(n,r));if(t===null||!matchesStableDockerLabels(t.labels,o))throw new Error(`Docker volume ${n} is missing or is not owned by this Testkit product`);return t}export function matchesStableDockerLabels(e,n){const r=e[z],o=e[v];return typeof r=="string"&&r.length>0&&typeof o=="string"&&o.length>0&&Object.entries(n).every(([t,i])=>t===z||t===v||e[t]===I(i))}function we(e,n){const r=Object.entries(e).sort(([t],[i])=>t.localeCompare(i)),o=Object.entries(n).map(([t,i])=>[t,I(i)]).sort(([t],[i])=>t.localeCompare(i));return r.length===o.length&&r.every(([t,i],a)=>o[a]?.[0]===t&&o[a]?.[1]===i)}async function be(e,n,r={}){const o=k(e,n,"existing-generation"),t=await h(n,r);if(!t)return;const i=Z(t)||{};if(!matchesStableDockerLabels(i,o))return;const c=E(t);c!==null&&await ce(n,c,r)}function Y(e,n){return(Array.isArray(e?.Mounts)?e.Mounts:[]).find(t=>t?.Type==="volume"&&typeof t.Name=="string"&&t.Name.length>0&&t.Destination===n)?.Name||null}function p(e,n,r){const o=X(n,r);if(!matchesStableDockerLabels(e.Config?.Labels||{},o))throw new Error(`Docker container ${r} already exists but is not owned by this Testkit product. Remove or rename it before running Testkit.`)}export function resolveLocalPostgresVolumeTarget(e){return pe(e)>=18?fe:le}function pe(e){const r=I(e||"").match(/(?:^|[:@/-])(?:pg|postgres)?(\d{2})(?:\D|$)/i);return r?Number(r[1]):16}async function J(e,n={}){M("database.hostReadinessChecks");const r=Date.now();let o=10;for(;Date.now()-r<j;){const t=Math.max(1,j-(Date.now()-r));if((await L(["exec",e.containerId,"pg_isready","-h","127.0.0.1","-U",e.user,"-d",D,"-p",String(e.containerPort||5432)],`Docker local Postgres readiness probe for ${e.containerName}`,Math.min(LOCAL_POSTGRES_DOCKER_TIMEOUTS_MS.readinessAttempt,t),n.signal)).exitCode===0)return;await Ce(Math.min(o,t),n.signal),o=Math.min(o*2,ae)}throw new Error(`Timed out waiting for local database container ${e.containerName}`)}function X(e,n,r="existing-generation"){return G(e,{generation:r,kind:"postgres-container",name:n,scope:"local-postgres",cachePolicy:"product-cache"})}function k(e,n,r){return G(e,{generation:r,kind:"postgres-volume",name:n,scope:"local-postgres",cachePolicy:"product-cache"})}function Ce(e,n){return new Promise((r,o)=>{if(n?.aborted){o(Q(n));return}const t=setTimeout(i,e);n?.addEventListener("abort",a,{once:!0});function i(){n?.removeEventListener("abort",a),r()}function a(){clearTimeout(t),o(n?Q(n):new Error("Local Postgres operation aborted"))}})}function Q(e){return e.reason instanceof Error?e.reason:new Error("Local Postgres operation aborted",{cause:e.reason})}function Z(e){if(!m(e))return null;const n=e.Labels;return m(n)?n:null}function he(e){if(e===null)return null;if(m(e))return{Cmd:Se(e.Cmd),Image:g(e.Image),Labels:Le(e.Labels)}}function Le(e){if(!m(e))return null;const n={};for(const[r,o]of Object.entries(e))typeof o=="string"&&(n[r]=o);return n}function Pe(e){if(e===null)return null;if(m(e))return{NetworkMode:g(e.NetworkMode)}}function ke(e){if(e===null)return null;if(P(e))return e.flatMap(n=>m(n)?[{Destination:g(n.Destination),Name:g(n.Name),Type:g(n.Type)}]:[])}function ye(e){if(e===null)return null;if(!m(e))return;const n=e.Ports;if(n===null)return{Ports:null};if(!m(n))return{};const r={};for(const[o,t]of Object.entries(n)){if(t===null){r[o]=null;continue}P(t)&&(r[o]=t.flatMap(i=>{if(!m(i))return[];const a=g(i.HostPort);return i.HostPort!==void 0&&a===void 0?[]:[{HostPort:a}]}))}return{Ports:r}}function _e(e){if(e===null)return null;if(m(e))return{Running:e.Running===null||typeof e.Running=="boolean"?e.Running:void 0}}function g(e){if(e===null||typeof e=="string")return e}function Se(e){if(e===null)return null;if(P(e))return e.filter(n=>typeof n=="string")}async function y(e,n,r,o){const t=await L(e,n,r,o);if(t.exitCode===0)return t;throw T(n,t)}function T(e,n){const r=n.stderr.trim()||n.stdout.trim(),o=new Error(`${e} failed with exit code ${String(n.exitCode)}${r?`: ${r}`:""}`);return Object.defineProperty(o,"result",{enumerable:!1,value:Object.freeze({...n})}),o}function ee(e,n,r){const o=new Error(`${e} returned malformed Docker inspect output; expected exactly one container record with a non-empty Id`,r===void 0?void 0:{cause:r});return Object.defineProperty(o,"result",{enumerable:!1,value:Object.freeze({...n})}),o}function ne(e){return/\bno such (?:container|object)\b/iu.test(`${e.stderr}
|
|
1
|
+
import{recordWork as D}from"../kernel/work-metrics.js";import{runDockerMetadataCommand as oe}from"./docker-metadata.js";import{randomUUID as C}from"node:crypto";import ie from"fs";import u from"path";import{LOCAL_ADMIN_DB as M,LOCAL_IMAGE as R,LOCAL_MAX_CONNECTIONS as N,LOCAL_PASSWORD as $,LOCAL_POLL_INTERVAL_MS as se,LOCAL_READY_TIMEOUT_MS as j,LOCAL_USER as V}from"./constants.js";import{buildContainerName as ae,buildVolumeName as E}from"./naming.js";import{getLocalInfraDir as U,readStateValue as l}from"./state.js";import{TESTKIT_CREATED_AT_LABEL as z,TESTKIT_RESOURCE_GENERATION_LABEL as v,buildDockerResourceLabels as G,dockerLabelArgs as H,inspectDockerVolume as h,normalizeDockerVolumeInspect as S,removeDockerVolume as ce,runDockerCommand as L}from"./docker.js";import{writeLocalInfraState as ue}from"./state-files.js";import{isUnknownArray as P,isUnknownRecord as f}from"../kernel/unknown-values.js";const le="/var/lib/postgresql/data",fe="/var/lib/postgresql",O=String;export const LOCAL_POSTGRES_DOCKER_TIMEOUTS_MS=Object.freeze({inspect:3e4,readinessAttempt:1e4,remove:12e4,run:10*6e4,start:12e4,volumeCreate:12e4});export async function ensureLocalContainer(e,n={},r={}){const o=U(e);ie.mkdirSync(o,{recursive:!0});const t=l(u.join(o,"container_name"))||ae(e),i=l(u.join(o,"volume_name")),s=n.image||l(u.join(o,"image"))||R,c=l(u.join(o,"image")),m=resolveLocalPostgresVolumeTarget(s),w=n.user||l(u.join(o,"user"))||V,b=n.password||l(u.join(o,"password"))||$,_=F(n.maxConnections||l(u.join(o,"max_connections"))||N);let a=await inspectContainer(t,r);a&&p(a,e,t);const te=!a&&!i;let d=Y(a,m)||i||E(e,C());a&&await W(e,d,r);const A=a?.Config?.Image||c||null;if(a&&!isReusableLocalPostgresContainer(a,{image:s,maxConnections:_,volumeMountPath:m,volumeName:d})&&(await stopAndRemoveContainer(a.Id,t,r),A&&A!==s&&(await be(e,d,r),d=E(e,C())),a=null),a)a.State?.Running||(await y(["start",a.Id],`Docker local Postgres container start for ${t}`,LOCAL_POSTGRES_DOCKER_TIMEOUTS_MS.start,r.signal),a=await inspectContainer(t,r),a&&p(a,e,t));else{d=te?await K(e,r):await ge(e,d,r);const re=X(e,t,C());await y(buildDockerRunArgs({containerName:t,labels:re,volumeName:d,volumeMountPath:m,user:w,password:b,image:s,maxConnections:_}),`Docker local Postgres container creation for ${t}`,LOCAL_POSTGRES_DOCKER_TIMEOUTS_MS.run,r.signal),a=await inspectContainer(t,r),a&&p(a,e,t)}const I=q(a);if(!a||!I)throw new Error(`Could not determine host port for local database container ${t}`);const x={containerName:t,containerId:a.Id,volumeName:d,volumeMountPath:m,image:s,user:w,password:b,maxConnections:_,host:"127.0.0.1",port:Number(I),containerPort:5432};return await J(x,r),ue(o,x),x}export function buildDockerRunArgs({containerName:e,labels:n,volumeName:r,volumeMountPath:o,user:t,password:i,image:s,maxConnections:c}){return["run","-d","--name",e,...H(n),"-p","127.0.0.1::5432","--mount",`type=volume,source=${r},target=${o}`,"-e",`POSTGRES_USER=${t}`,"-e",`POSTGRES_PASSWORD=${i}`,"-e",`POSTGRES_DB=${M}`,s,"postgres","-c",`max_connections=${String(c)}`]}export async function loadExistingLocalContainer(e,n={}){const r=U(e),o=l(u.join(r,"container_name"));if(!o)return null;const t=await inspectContainer(o,n);if(!t)return null;p(t,e,o);const i=t.Config?.Image||l(u.join(r,"image"))||R,s=l(u.join(r,"volume_mount_path"))||resolveLocalPostgresVolumeTarget(i),c=Y(t,s)||l(u.join(r,"volume_name"));if(!c)return null;await W(e,c,n),t.State?.Running||await y(["start",t.Id],`Docker local Postgres container start for ${o}`,LOCAL_POSTGRES_DOCKER_TIMEOUTS_MS.start,n.signal);const m=t.State?.Running?t:await inspectContainer(t.Id,n);m&&p(m,e,o);const w=q(m);if(!m||!w)return null;const b={containerName:o,containerId:m.Id,volumeName:c,volumeMountPath:s,image:i,user:l(u.join(r,"user"))||V,password:l(u.join(r,"password"))||$,maxConnections:F(l(u.join(r,"max_connections"))||N),host:"127.0.0.1",port:Number(w),containerPort:B(m)==="host"?Number(w):5432};return await J(b,n),b}function me(e,n){const r=e.Config?.Cmd;return Array.isArray(r)?r.includes("-c")&&r.includes(`max_connections=${String(n)}`):!1}function de(e,n,r){return(Array.isArray(e.Mounts)?e.Mounts:[]).some(t=>t?.Type==="volume"&&t.Name===n&&t.Destination===r)}export function isReusableLocalPostgresContainer(e,n){return e.Config?.Image===n.image&&me(e,n.maxConnections)&&de(e,n.volumeName,n.volumeMountPath)&&B(e)==="bridge"}function B(e){return e.HostConfig?.NetworkMode==="host"?"host":"bridge"}function q(e){return e?.NetworkSettings?.Ports?.["5432/tcp"]?.[0]?.HostPort||null}function F(e){const n=Number(e);if(!Number.isInteger(n)||n<10)throw new Error("database.maxConnections must be an integer greater than or equal to 10");return n}export async function inspectContainer(e,n={}){D("database.containerInspections");const r=`Docker local Postgres container inspect for ${e}`,o=["inspect",e],t=await oe(o,r,LOCAL_POSTGRES_DOCKER_TIMEOUTS_MS.inspect,n.signal,c=>L(o,r,c,n.signal));if(t.exitCode!==0){if(ne(t))return null;throw T(r,t)}let i;try{i=JSON.parse(t.stdout)}catch(c){throw ee(r,t,c)}const s=P(i)&&i.length===1?normalizeLocalPostgresContainerInspect(i[0]):null;if(!s?.Id)throw ee(r,t);return{...s,Id:s.Id}}export function normalizeLocalPostgresContainerInspect(e){return f(e)?{Config:Le(e.Config),HostConfig:ke(e.HostConfig),Id:g(e.Id),Mounts:ye(e.Mounts),NetworkSettings:_e(e.NetworkSettings),State:xe(e.State)}:null}export async function stopAndRemoveContainer(e,n=e,r={}){const o=`Docker local Postgres container removal for ${n}`,t=await L(["rm","-f","-v",e],o,LOCAL_POSTGRES_DOCKER_TIMEOUTS_MS.remove,r.signal);if(!(t.exitCode===0||ne(t)))throw T(o,t)}async function ge(e,n,r={}){const o=k(e,n,"existing-generation"),t=await h(n,r);if(t){if(!matchesStableDockerLabels(Z(t)||{},o))throw new Error(`Docker volume ${n} already exists but is not owned by this Testkit product. Remove or rename it before running Testkit.`);return n}return K(e,r)}async function K(e,n){const r=C(),o=E(e,r),t=k(e,o,r);await y(["volume","create",...H(t),o],`Docker local Postgres volume creation for ${o}`,LOCAL_POSTGRES_DOCKER_TIMEOUTS_MS.volumeCreate,n.signal);const i=S(await h(o,n));if(i===null||!we(i.labels,t))throw new Error(`Docker volume ${o} was not published with the expected Testkit generation identity`);return o}async function W(e,n,r){const o=k(e,n,"existing-generation"),t=S(await h(n,r));if(t===null||!matchesStableDockerLabels(t.labels,o))throw new Error(`Docker volume ${n} is missing or is not owned by this Testkit product`);return t}export function matchesStableDockerLabels(e,n){const r=e[z],o=e[v];return typeof r=="string"&&r.length>0&&typeof o=="string"&&o.length>0&&Object.entries(n).every(([t,i])=>t===z||t===v||e[t]===O(i))}function we(e,n){const r=Object.entries(e).sort(([t],[i])=>t.localeCompare(i)),o=Object.entries(n).map(([t,i])=>[t,O(i)]).sort(([t],[i])=>t.localeCompare(i));return r.length===o.length&&r.every(([t,i],s)=>o[s]?.[0]===t&&o[s]?.[1]===i)}async function be(e,n,r={}){const o=k(e,n,"existing-generation"),t=await h(n,r);if(!t)return;const i=Z(t)||{};if(!matchesStableDockerLabels(i,o))return;const c=S(t);c!==null&&await ce(n,c,r)}function Y(e,n){return(Array.isArray(e?.Mounts)?e.Mounts:[]).find(t=>t?.Type==="volume"&&typeof t.Name=="string"&&t.Name.length>0&&t.Destination===n)?.Name||null}function p(e,n,r){const o=X(n,r);if(!matchesStableDockerLabels(e.Config?.Labels||{},o))throw new Error(`Docker container ${r} already exists but is not owned by this Testkit product. Remove or rename it before running Testkit.`)}export function resolveLocalPostgresVolumeTarget(e){return pe(e)>=18?fe:le}function pe(e){const r=O(e||"").match(/(?:^|[:@/-])(?:pg|postgres)?(\d{2})(?:\D|$)/i);return r?Number(r[1]):16}async function J(e,n={}){D("database.hostReadinessChecks");const r=Date.now();let o=10;for(;Date.now()-r<j;){const t=Math.max(1,j-(Date.now()-r));if((await L(["exec",e.containerId,"pg_isready","-h","127.0.0.1","-U",e.user,"-d",M,"-p",String(e.containerPort||5432)],`Docker local Postgres readiness probe for ${e.containerName}`,Math.min(LOCAL_POSTGRES_DOCKER_TIMEOUTS_MS.readinessAttempt,t),n.signal).catch(s=>{if(Ce(s))return null;throw s}))?.exitCode===0)return;await he(Math.min(o,t),n.signal),o=Math.min(o*2,se)}throw new Error(`Timed out waiting for local database container ${e.containerName}`)}function Ce(e){const n=f(e)?Reflect.get(e,"result"):void 0;return f(n)&&n.timedOut===!0&&n.cancelled===!1&&n.error===null&&n.terminationError===null&&n.outputLimitExceeded===!1}function X(e,n,r="existing-generation"){return G(e,{generation:r,kind:"postgres-container",name:n,scope:"local-postgres",cachePolicy:"product-cache"})}function k(e,n,r){return G(e,{generation:r,kind:"postgres-volume",name:n,scope:"local-postgres",cachePolicy:"product-cache"})}function he(e,n){return new Promise((r,o)=>{if(n?.aborted){o(Q(n));return}const t=setTimeout(i,e);n?.addEventListener("abort",s,{once:!0});function i(){n?.removeEventListener("abort",s),r()}function s(){clearTimeout(t),o(n?Q(n):new Error("Local Postgres operation aborted"))}})}function Q(e){return e.reason instanceof Error?e.reason:new Error("Local Postgres operation aborted",{cause:e.reason})}function Z(e){if(!f(e))return null;const n=e.Labels;return f(n)?n:null}function Le(e){if(e===null)return null;if(f(e))return{Cmd:Ee(e.Cmd),Image:g(e.Image),Labels:Pe(e.Labels)}}function Pe(e){if(!f(e))return null;const n={};for(const[r,o]of Object.entries(e))typeof o=="string"&&(n[r]=o);return n}function ke(e){if(e===null)return null;if(f(e))return{NetworkMode:g(e.NetworkMode)}}function ye(e){if(e===null)return null;if(P(e))return e.flatMap(n=>f(n)?[{Destination:g(n.Destination),Name:g(n.Name),Type:g(n.Type)}]:[])}function _e(e){if(e===null)return null;if(!f(e))return;const n=e.Ports;if(n===null)return{Ports:null};if(!f(n))return{};const r={};for(const[o,t]of Object.entries(n)){if(t===null){r[o]=null;continue}P(t)&&(r[o]=t.flatMap(i=>{if(!f(i))return[];const s=g(i.HostPort);return i.HostPort!==void 0&&s===void 0?[]:[{HostPort:s}]}))}return{Ports:r}}function xe(e){if(e===null)return null;if(f(e))return{Running:e.Running===null||typeof e.Running=="boolean"?e.Running:void 0}}function g(e){if(e===null||typeof e=="string")return e}function Ee(e){if(e===null)return null;if(P(e))return e.filter(n=>typeof n=="string")}async function y(e,n,r,o){const t=await L(e,n,r,o);if(t.exitCode===0)return t;throw T(n,t)}function T(e,n){const r=n.stderr.trim()||n.stdout.trim(),o=new Error(`${e} failed with exit code ${String(n.exitCode)}${r?`: ${r}`:""}`);return Object.defineProperty(o,"result",{enumerable:!1,value:Object.freeze({...n})}),o}function ee(e,n,r){const o=new Error(`${e} returned malformed Docker inspect output; expected exactly one container record with a non-empty Id`,r===void 0?void 0:{cause:r});return Object.defineProperty(o,"result",{enumerable:!1,value:Object.freeze({...n})}),o}function ne(e){return/\bno such (?:container|object)\b/iu.test(`${e.stderr}
|
|
2
2
|
${e.stdout}`)}
|
package/dist/database/naming.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import
|
|
1
|
+
import d from"crypto";import p from"fs";import s from"path";import{hasErrorCode as f}from"../kernel/error-serialization.js";export function buildDatabaseUrl(e,t){const r=e.sslMode||"disable";return`postgresql://${encodeURIComponent(e.user)}:${encodeURIComponent(e.password)}@${e.host}:${String(e.port)}/${t}?sslmode=${encodeURIComponent(r)}`}export function buildContainerName(e){const t=canonicalizeProductDir(e);return o("testkit_pg",s.basename(t),[hashString(t,10)],63)}export function buildVolumeName(e,t){const r=canonicalizeProductDir(e);if(!t)throw new Error("Local Postgres volume generation must be non-empty");return o("testkit_pgdata",s.basename(r),[hashString(r,10),hashString(t,32)],128)}export function buildTemplateDatabaseName(e,t){return o("tk_tpl",e,[t.slice(0,16)],63)}export function buildRuntimeDatabaseName(e,t,r){return o("tk",e,[hashString(t,10),r.slice(0,12)],63)}export function buildTaskDatabaseName(e,t,r){return o("tk_task",e,[hashString(t,8),hashString(r,10)],63)}function o(e,t,r,a){const c=`${e}_`,l=slugSegment(t);let n=`_${r.join("_")}`,i=a-c.length-n.length;if(l.length>i&&(n=`_${hashString(t,10)}${n}`,i=a-c.length-n.length),i<1)throw new Error(`Cannot retain the identity suffix within a ${String(a)}-character identifier`);const u=limitIdentifier(l,i);return`${c}${u}${n}`}export function canonicalizeProductDir(e){const t=s.resolve(e);try{return p.realpathSync.native(t)}catch(r){if(f(r,"ENOENT"))return t;throw r}}export function hashString(e,t=12){return d.createHash("sha256").update(e).digest("hex").slice(0,t)}export function slugSegment(e){return e.toLowerCase().replace(/[^a-z0-9]+/g,"_").replace(/^_+|_+$/g,"")||"db"}export function limitIdentifier(e,t){return e.length<=t?e:e.slice(0,t)}export function escapeIdentifier(e){return String(e).replace(/"/g,'""')}export function escapeSqlLiteral(e){return String(e).replace(/'/g,"''")}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{randomBytes as C}from"node:crypto";import R from"node:fs";import v from"node:net";import M from"node:os";import E from"node:tls";import{NotPostgresError as b,PostgresCheckError as n,cstring as g,errorFields as U,frontendMessage as S,int32 as I,md5PasswordResponse as x,parseBackendMessage as F,scramClientFinal as Q,scramClientFirst as B,scramServerVerified as _,sslRequest as D,startupMessage as N}from"./protocol.js";const A=["disable","allow","prefer","require","verify-ca","verify-full"],O={2:"Kerberos V5",7:"GSSAPI",9:"SSPI"},j=new Set("08004 08P01 0A000 42501 53100 53200 53300 57P01 57P02 57P03".split(" ")),W={disable:[!1],allow:[!1,!0],prefer:[!0,!1],require:[!0],"verify-ca":[!0],"verify-full":[!0]};export function parsePostgresUrl(t){try{return H(t)}catch(e){throw T(e,"could not be read")}}export async function checkPostgresConnection(t,e){e.signal?.throwIfAborted();const r=AbortSignal.timeout(e.timeoutMs),i=e.signal===void 0?r:AbortSignal.any([e.signal,r]);try{const o=parsePostgresUrl(t),c=o.host.startsWith("/")?[!1]:W[o.sslmode];for(const[s,a]of c.entries()){const l={retryable:!1,tls:!1};try{await V(o,a,l,i);return}catch(f){const h=l.retryable&&(o.sslmode!=="prefer"||l.tls);if(s===c.length-1||i.aborted||!h)throw f}}}catch(o){throw e.signal?.aborted?e.signal.reason:r.aborted?new n(`there was no PostgreSQL answer within ${String(e.timeoutMs)} ms`,{cause:T(o,"the check failed unexpectedly")}):T(o,"the check failed unexpectedly")}}function H(t){const e=z(t),r=G(e.query);for(const l of["sslcert","sslkey"])if(r.has(l))throw new n(`sets ${l}, and Testkit's database check presents no client certificate`);if(r.get("channel_binding")==="require")throw new n("requires channel binding, which Testkit's database check does not offer");const i=r.get("host")||e.host;if(i==="")throw new n("names no host");if(i.includes(","))throw new n("names more than one host, and Testkit's database check connects to one");const o=Number(r.get("port")||e.port||"5432");if(!Number.isInteger(o)||o<1||o>65535)throw new n("names a port outside 1 to 65535");const c=r.get("sslrootcert")||null,s=r.get("sslmode")??(c==="system"?"verify-full":"prefer");if(!J(s))throw new n(`sets an sslmode that is not one of ${A.join(", ")}`);if(c==="system"&&s!=="verify-full")throw new n(`sets sslrootcert=system with sslmode=${s}, and libpq allows only verify-full`);if(!i.startsWith("/")&&(s==="verify-ca"||s==="verify-full")&&c===null)throw new n(`sets sslmode=${s} without sslrootcert, a CA file or "system", to verify against`);const a=r.get("user")||e.user||X();return{database:r.get("dbname")||e.database||a,host:i,password:r.get("password")??e.password,port:o,sslmode:s,sslrootcert:c,user:a}}function z(t){const e=/^postgres(?:ql)?:\/\//u.exec(t);if(e===null)throw new n("must be a postgres:// or postgresql:// URL");let r=t.slice(e[0].length),i="",o=null;const c=r.search(/[@/]/u);if(r[c]==="@"){const[u,...m]=r.slice(0,c).split(":");i=y(u),o=m.length===0?null:y(m.join(":")),r=r.slice(c+1)}const s=r.search(/[/?]/u),a=s<0?r:r.slice(0,s);r=s<0?"":r.slice(s);const l=/^\[([^\]]*)\](?::(.*))?$/su.exec(a);if(a.startsWith("[")&&l===null)throw new n("names a malformed IPv6 host");const f=a.lastIndexOf(":"),[h,w]=l?[l[1],l[2]||""]:f<0?[a,""]:[a.slice(0,f),a.slice(f+1)],d=r.indexOf("?");return{database:y((d<0?r:r.slice(0,d)).slice(1)),host:y(h),password:o,port:y(w),query:d<0?"":r.slice(d+1),user:i}}function G(t){const e=new Map,r=t.split("&");r.at(-1)===""&&r.pop();for(const i of r){const[o,c,s]=i.split("=");if(o===""||c===void 0||s!==void 0)throw new n("has a query parameter that is not one name=value pair");e.set(y(o),y(c))}return e}async function V(t,e,r,i){const o=t.host.startsWith("/")?v.connect(`${t.host}/.s.PGSQL.${String(t.port)}`):v.connect(t.port,t.host),c=[o],s=()=>{for(const a of c)a.destroy(new n("The database check stopped"))};i.addEventListener("abort",s,{once:!0});try{let a=L(o);await new Promise((f,h)=>{o.once("connect",f),o.once("error",w=>{h($(w))})});let l=o;if(e){o.write(D());const f=await a.byte();if(f==="S"){r.tls=!0,a.release();try{l=await Z(o,t,c)}catch(h){throw r.retryable=!0,h}a=L(l)}else{if(f!=="N")throw new b;if(t.sslmode!=="allow"&&t.sslmode!=="prefer")throw new n(`TLS failed: the server does not offer TLS, which sslmode=${t.sslmode} needs`)}}l.write(N({user:t.user,database:t.database,application_name:"testkit"})),await K(l,a,t,r,i),l.write(S("Q",g("SELECT 1")));for(let f=await a.message();f.type!=="Z";f=await a.message())if(f.type==="E")throw q(f.body);await new Promise(f=>{l.end(S("X"),()=>{f()})})}finally{i.removeEventListener("abort",s);for(const a of c)a.destroy()}}function Z(t,e,r){const i=e.sslrootcert,o=i!==null&&e.sslmode!=="allow"&&e.sslmode!=="prefer";let c;if(o&&i!=="system")try{c=R.readFileSync(i)}catch(a){return Promise.reject(new n(`TLS failed: sslrootcert could not be read (${k(a,"unreadable")})`))}const s=E.connect({ca:c,checkServerIdentity:(a,l)=>e.sslmode==="verify-full"?E.checkServerIdentity(e.host,l):void 0,rejectUnauthorized:o,socket:t,...v.isIP(e.host)===0?{servername:e.host}:{}});return r.push(s),new Promise((a,l)=>{s.once("secureConnect",()=>{a(s)}),s.on("error",f=>{l(new n(`TLS failed (${k(f,"handshake")})`))})})}async function K(t,e,r,i,o){const c=`n=,r=${C(18).toString("base64")}`;let s=null,a=null,l=!1,f=!1;for(let h=!0;;h=!1){const{body:w,type:d}=await e.message();if(h&&d!=="R"&&d!=="E"&&d!=="v")throw new b;if(d==="E")throw i.retryable=!f,q(w);if(d==="Z"){if(!f)throw new n("authentication failed: the server reported ready before it accepted the client");return}if(d!=="R")continue;if(w.length<4)throw new b;const u=w.readInt32BE(0),m=w.subarray(4);if(f)throw new n("authentication failed: the server asked again after it accepted the client");if(u===0){if(s===10&&!l)throw new n("authentication failed: the server ended SCRAM without proving it holds the password");f=!0}else if(s===null&&u===3)s=u,t.write(S("p",g(P(r))));else if(s===null&&u===5){s=u;const p=x(r.user,P(r),m.subarray(0,4));t.write(S("p",g(p)))}else if(s===null&&u===10){if(s=u,!m.toString("utf8").split("\0").includes("SCRAM-SHA-256"))throw new n("the server offers no SASL mechanism that Testkit's database check supports");const p=Buffer.from(B(c));t.write(S("p",g("SCRAM-SHA-256"),I(p.length),p))}else if(s===10&&u===11&&a===null){const p=await Q(P(r),c,m.toString("utf8"),o);a=p.serverSignature,t.write(S("p",Buffer.from(p.clientFinal)))}else if(s===10&&u===12&&a!==null&&!l){if(!_(m.toString("utf8"),a))throw new n("authentication failed: the server's SCRAM signature does not verify");l=!0}else throw s===null&&u!==11&&u!==12?new n(`the server asks for ${O[u]??`method ${String(u)}`} authentication, which Testkit's database check does not support`):new n("authentication failed: the server sent authentication requests out of order")}}function L(t){let e=Buffer.alloc(0),r=null,i=()=>{};const o=s=>{e=Buffer.concat([e,s]),i()};t.on("data",o),t.on("error",s=>{r??=$(s),i()}),t.on("close",()=>{r??=new n("the server closed the connection before startup finished"),i()});const c=async()=>{if(r!==null)throw r;await new Promise(s=>{i=s})};return{async byte(){for(;e.length===0;)await c();const s=e.toString("latin1",0,1);return e=e.subarray(1),s},async message(){for(;;){const s=F(e);if(s!==null)return e=s.rest,s.message;await c()}},release(){if(e.length>0)throw new b;t.off("data",o)}}}function q(t){const e=U(t).C;return e==="28P01"||e==="28000"?new n(`authentication failed (SQLSTATE ${e})`):e==="3D000"?new n("the database does not exist (SQLSTATE 3D000)"):new n(e!==void 0&&j.has(e)?`the server refused the connection (SQLSTATE ${e})`:"the server refused the connection")}function $(t){return new n(`the connection failed (${k(t,"socket error")})`)}function k(t,e){return t instanceof Error&&"code"in t&&typeof t.code=="string"?t.code:e}function T(t,e){return t instanceof n?t:new n(e)}function P(t){if(t.password===null)throw new n("authentication failed: the server asks for a password the URL lacks");return t.password}function X(){try{return M.userInfo().username}catch{throw new n("names no user, and the operating system's user name is unavailable")}}function J(t){return A.some(e=>e===t)}function y(t){if(/%(?![0-9A-Fa-f]{2})/u.test(t))throw new n("holds a malformed percent-encoding");if(/%00|\0/u.test(t))throw new n("holds a NUL");try{return decodeURIComponent(t)}catch{throw new n("holds percent-encoded bytes that are not UTF-8, and Testkit's database check reads only UTF-8")}}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{createHash as a,createHmac as C,pbkdf2 as T,timingSafeEqual as R}from"node:crypto";import{once as B}from"node:events";import{promisify as A}from"node:util";import{Worker as x}from"node:worker_threads";const I=196608,O=80877103,w=1024*1024,E="n,,",S=1e7,$=1e5,D=`const { parentPort, workerData: input } = require("node:worker_threads");
|
|
2
|
+
parentPort.postMessage(require("node:crypto").pbkdf2Sync(input.password, input.salt, input.iterations, 32, "sha256"));`,c="\\p{Script=Hebrew}\\p{Script=Arabic}\\p{Script=Syriac}\\p{Script=Thaana}\\p{Script=Nko}",N=/[\u00A0\u1680\u2000-\u200B\u202F\u205F\u3000]/gu,k=/\u00AD|\u034F|\u1806|[\u180B-\u180D]|\u200C|\u200D|\u2060|[\uFE00-\uFE0F]|\uFEFF/gu,y=/[\p{Cc}\p{Cn}\p{Co}\p{Cs}\u0340\u0341\u06DD\u070F\u180E\u200E\u200F\u2028-\u202E\u2061-\u2063\u206A-\u206F\u2FF0-\u2FFB\uFFF9-\uFFFD\u{1D173}-\u{1D17A}\u{E0001}\u{E0020}-\u{E007F}]/u,M=new RegExp(`[${c}]`,"u"),P=new RegExp(`(?![${c}])\\p{L}`,"u"),b=new RegExp(`^[${c}](?:.*[${c}])?$`,"su"),v=A(T);export class PostgresCheckError extends Error{name="PostgresCheckError"}export class NotPostgresError extends PostgresCheckError{constructor(){super("the server did not answer as PostgreSQL")}}export function sslRequest(){return Buffer.concat([int32(8),int32(O)])}export function startupMessage(t){const e=Buffer.concat([int32(I),...Object.entries(t).flatMap(([r,n])=>[cstring(r),cstring(n)]),Buffer.from([0])]);return Buffer.concat([int32(4+e.length),e])}export function frontendMessage(t,...e){const r=Buffer.concat(e);return Buffer.concat([Buffer.from(t,"latin1"),int32(4+r.length),r])}export function cstring(t){return Buffer.concat([Buffer.from(t,"utf8"),Buffer.from([0])])}export function int32(t){const e=Buffer.alloc(4);return e.writeInt32BE(t),e}export function parseBackendMessage(t){if(t.length<5)return null;const e=t.readInt32BE(1);if(e<4||e>w)throw new NotPostgresError;return t.length<1+e?null:{message:{type:String.fromCharCode(t[0]),body:t.subarray(5,1+e)},rest:t.subarray(1+e)}}export function errorFields(t){const e={};for(let r=0;r<t.length&&t[r]!==0;){const n=t.indexOf(0,r+1);if(n<0)break;e[String.fromCharCode(t[r])]=t.toString("utf8",r+1,n),r=n+1}return e}export function md5PasswordResponse(t,e,r){const n=a("md5").update(e).update(t).digest("hex");return`md5${a("md5").update(n).update(r).digest("hex")}`}export function scramClientFirst(t){return`${E}${t}`}export async function scramClientFinal(t,e,r,n){const o=g(r),s=g(e).r??"",u=Number(o.i);if(!o.r?.startsWith(s)||!o.s||!Number.isSafeInteger(u)||u<1)throw new PostgresCheckError("the server sent a malformed SCRAM challenge");if(u>S)throw new PostgresCheckError(`the server asks for ${String(u)} SCRAM iterations, more than the ${String(S)} Testkit's database check computes`);const f=await H(saslprep(t),Buffer.from(o.s,"base64"),u,n),p=i(f,"Client Key"),l=`c=${Buffer.from(E).toString("base64")},r=${o.r}`,m=`${e},${r},${l}`,d=i(a("sha256").update(p).digest(),m),_=Buffer.from(p.map((h,F)=>h^d[F]));return{clientFinal:`${l},p=${_.toString("base64")}`,serverSignature:i(i(f,"Server Key"),m)}}export function scramServerVerified(t,e){const[r,...n]=t.split(",");if(!r.startsWith("v=")||n.some(s=>/^[ev]=/u.test(s)))return!1;const o=Buffer.from(r.slice(2),"base64");return o.length===e.length&&R(o,e)}export function saslprep(t){if(/^[\0-\x7F]*$/u.test(t))return t;const e=t.replace(N," ").replace(k,"").normalize("NFKC");return(!M.test(e)||!P.test(e)&&b.test(e))&&!y.test(e)?e:t}async function H(t,e,r,n){if(n?.throwIfAborted(),r<=$)return v(t,e,r,32,"sha256");const o=new x(D,{eval:!0,workerData:{iterations:r,password:t,salt:e}});try{const[s]=await B(o,"message",{signal:n});return Buffer.from(s)}finally{o.terminate()}}function g(t){return Object.fromEntries(t.split(",").map(e=>[e.slice(0,1),e.slice(2)]))}function i(t,e){return C("sha256",t).update(e).digest()}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import
|
|
1
|
+
import l from"fs";import{resolveContainedTestkitStateDirectory as p}from"../process/runs/product-state.js";import{makeTempDir as y,removeTempDir as g}from"../process/files/temp-dir.js";import{cleanupOrphanedLocalInfrastructure as S,destroyRuntimeDatabase as b,provisionDatabase as v}from"./index.js";import{forceRefreshSourceSchemaCache as d}from"./schema-source.js";import{bindDatabaseUrl as D,lendDatabases as w}from"./bindings.js";import{withDatabaseBindings as R}from"../steps/index.js";export async function verifyDatabaseSchema(r,o,t={}){await h(r,"verify",o,t,async(e,s)=>{for(const a of e.configs)await s(a)})}export async function refreshDatabaseSchema(r,o,t={}){return h(r,"refresh",o,t,async(e,s,a)=>{for(const i of e.configs){if(i.name===e.target.name)break;await s(i)}return d(a(e.target),null,{reporter:t.progress?.reporter||null,setupRegistry:t.progress?.setupRegistry||null,signal:t.signal})})}async function h(r,o,t,e,s){const a=k(r,o),i=[],m=new Map;try{const c=await t(a.path),f=n=>R(n,w(c.configs,m));return await s(c,async n=>{if(!n.testkit.database)return;i.push(n.stateDir);const u=await v(f(n),{progress:e.progress,signal:e.signal,skipSchemaSourceVerify:e.skipSchemaSourceVerify});u&&m.set(n.name,D(u.url))},f)}finally{for(const c of i.reverse())await b({productDir:r,stateDir:c}).catch(()=>{});await S(r).catch(()=>{}),a.remove()}}function k(r,o){const t=p(r);l.mkdirSync(t,{recursive:!0});const e=p(r),s=y(`_schema-${o}-`,{parentDir:e});let a=!1;return{path:s,remove(){a||(a=!0,g(s))}}}
|