@gfdelarue/testkit 0.4.4 → 0.5.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 +752 -462
- package/bin/testkit-windows-owner-arm64.exe +0 -0
- package/bin/testkit-windows-owner-x64.exe +0 -0
- package/dist/app/bugs/categories.js +1 -1
- package/dist/app/bugs/ci-report.js +2 -2
- package/dist/app/bugs/ci-summary.js +3 -3
- package/dist/app/bugs/draft.js +4 -4
- package/dist/app/bugs/facts.js +2 -2
- 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 +2 -2
- 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/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 -0
- 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 -0
- 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 -0
- package/dist/app/environments/task-databases.js +2 -0
- package/dist/app/file-selection.js +1 -1
- 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/data.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/lint/shared.js +1 -1
- package/dist/app/package-inspection.js +1 -1
- package/dist/app/reports/files.js +1 -0
- package/dist/app/reports/prepare.js +2 -0
- package/dist/app/reports/publish.js +1 -0
- package/dist/app/run/caller-redaction.js +1 -0
- package/dist/app/run/engine-evidence-persistence.js +1 -1
- 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 -0
- 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/runtime-fixtures.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/scheduler/locks.js +1 -0
- package/dist/app/run/selection.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/task-leases.js +1 -0
- package/dist/app/run/worker-loop.js +1 -1
- package/dist/app/status/runs.js +1 -1
- package/dist/app/tracker/index.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/command-registry.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/report/prepare.js +5 -0
- package/dist/cli/commands/report/publish.js +2 -0
- 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/json-errors.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/doctor/text.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/http-methods.js +1 -1
- package/dist/config/authoring/index.js +1 -1
- package/dist/config/authoring/preset-env.js +1 -1
- package/dist/config/loader/config-loader.js +1 -1
- package/dist/config/loader/database.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/execution-model.js +1 -1
- package/dist/config/loader/file-metadata.js +1 -1
- package/dist/config/loader/groups.js +1 -0
- 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/activity-gate.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/docker-metadata.js +3 -3
- package/dist/database/docker.js +3 -3
- package/dist/database/index.js +1 -1
- package/dist/database/lifecycle-cleanup.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/source-url.js +2 -2
- 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/operation-display.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-outcomes.js +1 -1
- package/dist/domain/test-types.js +1 -1
- package/dist/engines/adapters/evidence-files.js +1 -1
- package/dist/engines/adapters/evidence-identity.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-scope.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/bundle-cache.js +1 -1
- package/dist/engines/catalog.js +1 -1
- package/dist/engines/contracts/environment-values.js +1 -0
- package/dist/engines/contracts/fixture-manifest.js +1 -1
- 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-resolution-child.js +1 -1
- package/dist/engines/installations/playwright.js +1 -1
- package/dist/engines/managed-module-resolution-policy-input.js +1 -1
- package/dist/engines/managed-module-resolution-policy.js +1 -1
- package/dist/engines/managed-module-resolution.js +2 -2
- 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 +31 -192
- 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/contract/index.js +1 -1
- package/dist/engines/node-vitest/runtime/data/http.js +1 -1
- 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 +4 -4
- 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/runtime-input.js +43 -20
- package/dist/engines/runtime-input-file.js +1 -1
- package/dist/environment/lifecycle-artifacts.js +1 -1
- package/dist/environment/lifecycle-controller.js +1 -1
- package/dist/environment/lifecycle.js +1 -1
- package/dist/environment/ports.js +1 -0
- 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 -1
- package/dist/environment/state.js +1 -1
- package/dist/environment/template.js +1 -1
- package/dist/evidence/artifact-config.js +1 -1
- package/dist/evidence/artifact-validation.js +1 -1
- package/dist/evidence/artifacts.js +2 -2
- package/dist/evidence/bundles.js +1 -1
- package/dist/evidence/environment-record.js +1 -0
- package/dist/evidence/index.js +1 -1
- package/dist/evidence/logs.js +2 -2
- package/dist/evidence/operation-timings.js +1 -1
- package/dist/evidence/read-fields.js +1 -1
- 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/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/files/paths.js +1 -1
- package/dist/process/host/metadata.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/lifecycle-fence.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-identity.js +1 -1
- package/dist/process/process.js +33 -33
- package/dist/process/readiness.js +1 -1
- package/dist/process/ready-deadline.js +1 -0
- package/dist/process/recovery.js +1 -1
- package/dist/process/runs/manifest.js +1 -1
- package/dist/process/runs/ownership.js +1 -1
- package/dist/process/runs/product-state.js +1 -1
- package/dist/process/runs/stop.js +1 -1
- package/dist/process/service-processes.js +1 -1
- package/dist/process/stoppable-settlement.js +1 -0
- package/dist/process/url-redaction.js +1 -0
- package/dist/process/windows-cli-owner.js +3 -3
- package/dist/process/windows-native.js +1 -1
- package/dist/repo/ci-environment.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/publish-directory.js +3 -0
- 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/steps/run.js +2 -2
- package/dist/toolchains/archive.js +1 -1
- package/dist/toolchains/index.js +3 -3
- package/dist/toolchains/node-provisioning.js +2 -2
- 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 +501 -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 +40 -0
- package/dist/types/index.d.ts +21 -0
- package/dist/types/node-vitest-contract.d.ts +75 -0
- package/dist/types/node-vitest-data.d.ts +407 -0
- package/dist/types/playwright.d.ts +130 -0
- package/dist/types/tooling-vitest.d.ts +13 -0
- package/dist/types/workspace.d.ts +617 -0
- package/docs/bug-records.md +83 -81
- package/docs/engines.md +213 -0
- package/docs/test-types.md +35 -33
- package/package.json +22 -29
- package/testkit-config-schema.md +403 -171
- 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 -48
- 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/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 -67
- package/dist/app/lint/policy.d.ts +0 -21
- package/dist/app/lint/rules/data.d.ts +0 -3
- 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 -36
- package/dist/app/package-inspection.d.ts +0 -20
- 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 -63
- package/dist/app/run/engine-failure-stack.d.ts +0 -1
- package/dist/app/run/engine-orchestration.d.ts +0 -49
- package/dist/app/run/failure-details.d.ts +0 -37
- package/dist/app/run/live-run.d.ts +0 -27
- package/dist/app/run/operation-progress.d.ts +0 -13
- package/dist/app/run/orchestrator.d.ts +0 -68
- package/dist/app/run/planning-types.d.ts +0 -68
- package/dist/app/run/planning.d.ts +0 -15
- package/dist/app/run/playwright-configuration.d.ts +0 -2
- package/dist/app/run/playwright-configuration.js +0 -1
- 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 -52
- 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/observations.d.ts +0 -20
- package/dist/app/run/selection.d.ts +0 -10
- 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/worker-loop.d.ts +0 -57
- 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 -8
- 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 -137
- 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/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 -24
- 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 -381
- package/dist/config/authoring/preset-env.d.ts +0 -19
- package/dist/config/authoring/runtime.d.ts +0 -64
- 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 -9
- 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 -54
- package/dist/config/loader/env.d.ts +0 -4
- package/dist/config/loader/execution-model.d.ts +0 -16
- package/dist/config/loader/file-metadata.d.ts +0 -19
- package/dist/config/loader/index.d.ts +0 -10
- package/dist/config/loader/load.d.ts +0 -91
- 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 -25
- 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 -16
- package/dist/config/loader/values.d.ts +0 -4
- 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 -7
- 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 -22
- 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 -28
- 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 -3
- 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 -42
- 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 -31
- 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 -22
- 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 -9
- 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/data/http.d.ts +0 -4
- 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/scenario.d.ts +0 -12
- package/dist/engines/node-vitest/runtime/data/types.d.ts +0 -412
- 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 -12
- 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 -70
- package/dist/environment/lifecycle.d.ts +0 -29
- package/dist/environment/maintenance.d.ts +0 -68
- package/dist/environment/planning-types.d.ts +0 -35
- package/dist/environment/readiness.d.ts +0 -12
- package/dist/environment/readiness.js +0 -1
- package/dist/environment/ready-environment.d.ts +0 -19
- package/dist/environment/run-environment.d.ts +0 -38
- package/dist/environment/run-stopping.d.ts +0 -13
- package/dist/environment/runtime-contexts.d.ts +0 -33
- package/dist/environment/runtime-manager.d.ts +0 -95
- 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 -18
- package/dist/environment/services.d.ts +0 -48
- package/dist/environment/state.d.ts +0 -18
- package/dist/environment/status-runtime.d.ts +0 -53
- package/dist/environment/template.d.ts +0 -180
- package/dist/evidence/artifact-config.d.ts +0 -14
- package/dist/evidence/artifact-validation.d.ts +0 -5
- 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/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 -22
- 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 -330
- 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 -27
- 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 -38
- package/dist/kernel/redaction.d.ts +0 -2
- package/dist/kernel/unknown-values.d.ts +0 -6
- 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 -12
- package/dist/process/files/paths.d.ts +0 -1
- 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 -11
- 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 -37
- 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/windows-cli-owner.d.ts +0 -2
- package/dist/process/windows-native.d.ts +0 -6
- 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 -15
- 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/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 -35
- 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 -84
- 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 -654
- /package/dist/engines/{k6/runtime/runtime-globals.js → api/profiles.js} +0 -0
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 +1 @@
|
|
|
1
|
-
import
|
|
1
|
+
import u from"node:fs";import{getLocalInfraDir as c,hasRemainingLocalArtifacts as i}from"./state.js";import{withUserHostLocalPostgresActivity as l}from"./activity-gate.js";import{requireProductLifecycleFenceProductDir as f}from"../process/lifecycle-fence.js";export async function cleanupOrphanedLocalInfrastructure(e,r={}){const n=c(e);return o(e,n)?l(e,()=>cleanupOrphanedLocalInfrastructureUnlocked(e),r):s()}export async function cleanupOrphanedLocalInfrastructureUnlocked(e){const r=c(e);if(!o(e,r))return s();const{hasActiveProductRuntime:n,cleanupLocalPostgresDockerResources:t}=await import("./cleanup.js");if(n(e))return s();const a=await t({productDir:e,dryRun:!1,stopIdle:!0});return i(e)||u.rmSync(r,{recursive:!0,force:!0}),a}export async function cleanupTerminalRunLocalInfrastructure(e,r,n={}){const t=f(e),a=c(t);return o(t,a)?l(t,async()=>{if(f(e),!o(t,a))return s();const{cleanupTerminalRunLocalPostgresDockerResources:p,assertTerminalRunDatabaseCleanupAuthority:m}=await import("./cleanup.js"),L=await p(e,r,{stopIdle:!0});return i(t)||(m(e,r),u.rmSync(a,{recursive:!0,force:!0})),L},n):s()}export function hasLocalDatabaseState(e){return o(e,c(e))}function o(e,r){return u.existsSync(r)||i(e)}function s(){return{kept:[],removed:[],skipped:[],stopped:[],targets:[]}}
|
|
@@ -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 G,buildDockerResourceLabels as v,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";const le="/var/lib/postgresql/data",fe="/var/lib/postgresql",A=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 T=s?.Config?.Image||c||null;if(s&&!isReusableLocalPostgresContainer(s,{image:a,maxConnections:_,volumeMountPath:f,volumeName:d})&&(await stopAndRemoveContainer(s.Id,t,r),T&&T!==a&&(await be(e,d,r),d=x(e,C())),s=null),s)s.State?.Running||(await k(["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 k(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 k(["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 I(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:ye(e.HostConfig),Id:g(e.Id),Mounts:Pe(e.Mounts),NetworkSettings:ke(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 I(o,t)}async function ge(e,n,r={}){const o=y(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=y(e,o,r);await k(["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=y(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[G];return typeof r=="string"&&r.length>0&&typeof o=="string"&&o.length>0&&Object.entries(n).every(([t,i])=>t===z||t===G||e[t]===A(i))}function we(e,n){const r=Object.entries(e).sort(([t],[i])=>t.localeCompare(i)),o=Object.entries(n).map(([t,i])=>[t,A(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=y(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=A(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 v(e,{generation:r,kind:"postgres-container",name:n,scope:"local-postgres",cachePolicy:"product-cache"})}function y(e,n,r){return v(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 ye(e){if(e===null)return null;if(m(e))return{NetworkMode:g(e.NetworkMode)}}function Pe(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 ke(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 m(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function P(e){return Array.isArray(e)}function Se(e){if(e===null)return null;if(P(e))return e.filter(n=>typeof n=="string")}async function k(e,n,r,o){const t=await L(e,n,r,o);if(t.exitCode===0)return t;throw I(n,t)}function I(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))}}}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{recordWork as l}from"../kernel/work-metrics.js";import o from"fs";import s from"path";import{createRequire as
|
|
2
|
-
`))}export async function appendSourceSchemaCacheToHash(e,r,t=null){if(!(r.testkit.database?.sourceSchema||null))return;const a=t?.cachePath||await getSourceSchemaCachePath(r),n=t?.active&&t.cacheKey?t.cacheKey:s.relative(getSourceSchemaRootDir(r),s.dirname(a));if(e.update(`source-schema-key:${n}`),e.update(`source-schema-cache:${s.relative(r.productDir,a)}`),!o.existsSync(a)){e.update(":missing");return}const i=o.statSync(a);e.update(`:${i.size.toString()}:${i.mtimeMs.toString()}`);const
|
|
3
|
-
`}function
|
|
1
|
+
import{recordWork as l}from"../kernel/work-metrics.js";import o from"fs";import s from"path";import{createRequire as z}from"node:module";import{collectRepoState as J,summarizeRepoStateForMetadata as K}from"../repo/state.js";import{findDatabasePlaceholders as V,resolveExecutionEnvValues as X}from"../steps/index.js";import{sourceSchemaEnvNames as F}from"./source-schema-env.js";import{dumpPostgresSchemaToFile as G,captureTemplateSnapshotText as Z,resolvePgDumpClientImage as Q,runTemplateStep as Y,sanitizeSnapshotText as ee}from"./template-steps.js";import{getSourceSchemaRefreshLockPath as re,withSourceSchemaRefreshLock as te}from"./source-refresh-lock.js";import{resolveSourceSchemaDumpUrl as ae}from"./source-url.js";import{makeTempDir as ce,removeTempDir as ne}from"../process/files/temp-dir.js";import{isNullableString as se,isUnknownRecord as f}from"../kernel/unknown-values.js";import{requireContainedTestkitStateDirectory as _,resolveContainedTestkitStateDirectory as D}from"../process/runs/product-state.js";const $=z(import.meta.url);let P;function ie(){if(P)return P;const e=$.resolve("diff/package.json"),t=JSON.parse(o.readFileSync(e,"utf8"))?.exports?.["."]?.import?.default;if(typeof t!="string"||!t.startsWith("./"))throw new Error("The diff package must declare a local ESM import entry");const c=s.dirname(e),a=s.resolve(c,t),n=s.relative(c,a);if(!n||n===".."||n.startsWith(`..${s.sep}`)||s.isAbsolute(n))throw new Error("The diff package ESM import entry must stay within its package directory");return P=$(a)}const M="db",U="source-schemas",b="source-schema.sql",g="source-schema.meta.json",oe="index.json",N=20,y=String;export function getSourceSchemaRootDir(e){return D(e.productDir,M,e.name,U)}export async function getSourceSchemaCachePath(e,r={}){return(await resolveSourceSchemaCacheLocation(e,r)).cachePath}export async function resolveSourceSchemaCacheLocation(e,r={}){const t=r.repoState||await J(e.productDir,{fingerprints:e.testkit?.fingerprints}),c=getSourceSchemaRootDir(e),a=D(e.productDir,M,e.name,U,...t.cacheKey.split("/").map(w));return{rootDir:c,cacheDir:a,cachePath:s.join(a,b),metadataPath:s.join(a,g),indexPath:s.join(c,oe),cacheKey:t.cacheKey,cacheKind:t.kind,repoState:t}}export async function resolveSourceSchemaState(e,r={}){const t=e.testkit.database?.sourceSchema||null;if(!t)return{active:!1};const c=await resolveSourceSchemaCacheLocation(e,r),a=c.cachePath,n=o.existsSync(a);n&&R(a);const i=r.env||process.env,h=F(e);Ee(e,h);const u=X(e,[...h,"CI"],i),m=h.find(W=>{const A=u[W];return typeof A=="string"&&A.trim().length>0})||null,S=m?u[m]:void 0,p=typeof S=="string"?S.trim():null;return t.kind==="env"||!!p||n?{active:!0,cachePath:a,metadataPath:c.metadataPath,indexPath:c.indexPath,rootDir:c.rootDir,cacheDir:c.cacheDir,cacheKey:c.cacheKey,cacheKind:c.cacheKind,repoState:c.repoState,envName:m,sourceUrl:p,sourceSchema:t,cacheExists:n,refreshed:!1,ci:u.CI==="true",unavailableMode:xe(t.unavailable,i)}:{active:!1,cachePath:a}}export async function prepareSourceSchemaCache(e,r={}){r.signal?.throwIfAborted();const t=await resolveSourceSchemaState(e,r);if(!t.active)return t;if(H(t)){if(pe(t))try{const a=await j(e,t,r),n={...t,refreshed:!0,cacheExists:!0,refreshInfo:a};return v(e,n,{refreshed:!0}),n}catch(a){r.signal?.throwIfAborted();const n=he(e,t,a,r);if(n)return n;throw a}return v(e,t),r.setupRegistry?.recordCached({config:e,stage:"source-schema",kind:"database-source-schema",summary:"source schema cache hit"}),t}if(t.cacheExists&&t.unavailableMode==="warn-cache")return v(e,t),r.setupRegistry?.recordCached({config:e,stage:"source-schema",kind:"database-source-schema",summary:"source unavailable; using cached source schema"}),t;const c=F(e).join(" or ");throw new Error(`Source schema database URL is unavailable for service "${e.name}". Set ${c}.`)}function he(e,r,t,c={}){if(r.unavailableMode!=="warn-cache")return null;const a=ue(e,r);if(!a)return null;const n={...r,...a,cacheExists:!0,refreshed:!1,refreshError:Re(t)};return c.setupRegistry?.recordCached({config:e,stage:"source-schema",kind:"database-source-schema",summary:`source unavailable; using cached source schema: ${s.relative(e.productDir,a.cachePath)}`}),n}function ue(e,r){const t=L(e,r,{cachePath:r.cachePath,metadataPath:r.metadataPath,cacheKey:r.cacheKey,cacheKind:r.cacheKind});if(t)return t;const c=O(r.indexPath);c.sort((a,n)=>Date.parse(n.lastUsedAt||n.refreshedAt||"0")-Date.parse(a.lastUsedAt||a.refreshedAt||"0"));for(const a of c){if(!a.cachePath)continue;const n=s.resolve(e.productDir,a.cachePath),i=L(e,r,{cachePath:n,metadataPath:s.join(s.dirname(n),g),cacheKey:a.cacheKey,cacheKind:a.kind||"unknown"});if(i)return i}return null}function L(e,r,t){const c=s.resolve(t.cachePath);if(s.basename(c)!==b)return null;const a=_(e.productDir,s.dirname(c),`Service "${e.name}" source schema cache directory`),n=s.join(a,b),i=s.resolve(r.rootDir);if(n!==i&&!n.startsWith(`${i}${s.sep}`)||!o.existsSync(n))return null;const h=o.lstatSync(n);if(h.isSymbolicLink()||!h.isFile())throw new Error(`Refusing unsafe source schema cache file at ${n}`);return{cachePath:n,metadataPath:s.join(a,g),cacheDir:a,cacheKey:t.cacheKey,cacheKind:t.cacheKind,indexPath:r.indexPath,rootDir:r.rootDir}}export async function forceRefreshSourceSchemaCache(e,r,t={}){const c=await resolveSourceSchemaState(e,t);if(!c.active||!H(c)){const i=r?.active?r.envName:null,h=r?.active&&r.sourceSchema.kind==="env"?r.sourceSchema.env:null,u=i||h||"source schema env";throw new Error(`Cannot refresh source schema for service "${e.name}" because ${u} is unavailable.`)}const a=await j(e,c,t),n={...c,refreshed:!0,cacheExists:!0,refreshInfo:a};return v(e,n,{refreshed:!0}),n}export async function applySourceSchemaCache(e,r,t,c={}){if(!t?.active)return!1;if(!o.existsSync(t.cachePath))throw new Error(`Missing source schema cache for service "${e.name}": ${t.cachePath}`);const a={kind:"sql-file",path:s.relative(e.productDir,t.cachePath).split(s.sep).join("/"),cwd:null,inputs:[]};return l("schema.sourceReplays"),await Y(e,"source-schema",a,0,r,c),!0}export async function verifyLocalSchemaMatchesSource(e,r,t,c={}){if(!t?.active)return{status:"disabled"};if(!t.sourceSchema.verify||c.skipSchemaSourceVerify)return c.setupRegistry?.recordCached({config:e,stage:"source-schema:verify",kind:"database-source-schema",summary:"source schema verification skipped"}),{status:"skipped"};we(e);const a=fe(t.cachePath);l("schema.verificationDumps");const n=await Z(e,r,{image:await c.resolveLocalSnapshotImage?.(),ownedContainer:c.ownedContainer||null,reporter:c.reporter||null,signal:c.signal});return l("schema.comparisons"),l("schema.bytesCompared",Buffer.byteLength(a)+Buffer.byteLength(n)),n===a?{status:"matched"}:{status:"mismatch",diagnostics:await Pe(e,t,a,n)}}export function createSourceSchemaMismatchError(e,r,t){const c=[`Local schema replay does not match source schema for service "${e.name}".`,`Source cache: ${s.relative(e.productDir,r.cachePath)}`];return t?.status==="mismatch"&&c.push(`Diff: ${s.relative(e.productDir,t.diagnostics.diffPath)}`),new Error(c.join(`
|
|
2
|
+
`))}export async function appendSourceSchemaCacheToHash(e,r,t=null){if(!(r.testkit.database?.sourceSchema||null))return;const a=t?.cachePath||await getSourceSchemaCachePath(r),n=t?.active&&t.cacheKey?t.cacheKey:s.relative(getSourceSchemaRootDir(r),s.dirname(a));if(e.update(`source-schema-key:${n}`),e.update(`source-schema-cache:${s.relative(r.productDir,a)}`),!o.existsSync(a)){e.update(":missing");return}const i=o.statSync(a);e.update(`:${i.size.toString()}:${i.mtimeMs.toString()}`);const h=o.readFileSync(a);l("schema.cacheHashFilesRead"),l("schema.cacheHashBytesRead",h.length),e.update(h)}async function j(e,r,t={}){const c=t.setupRegistry,a=c?.start({config:e,stage:"source-schema:refresh",kind:"database-source-schema",summary:`source schema refresh: ${s.relative(e.productDir,r.cachePath)}`});try{const n=await te(re(r.cachePath),async({waited:h,requestedAt:u})=>{const m=de(r,u);if(m)return{reusedExistingRefresh:!0,waitedForLock:h,metadata:m};const S=await me(e,r.sourceUrl,r.cachePath,{reporter:t.reporter||null,logRecord:a?._logRecord||null,env:t.env||process.env,signal:t.signal}),p=le(e,r,S);return ye(r.metadataPath,p),{reusedExistingRefresh:!1,waitedForLock:h,metadata:p}},{signal:t.signal}),i=a&&c?c.finish(a,{status:"passed",summary:`source schema refresh: ${s.relative(e.productDir,r.cachePath)}`}):null;return i&&t.reporter?.setupOperationFinished?.(i),n}catch(n){const i=a?c?.finish(a,{status:"failed",summary:`source schema refresh: ${s.relative(e.productDir,r.cachePath)}`,error:Ce(n)}):null;throw i&&t.reporter?.setupOperationFinished?.(i),n}}async function me(e,r,t,c={}){const a=ae(r);o.mkdirSync(s.dirname(t),{recursive:!0});const n=ce(".source-schema-",{parentDir:s.dirname(t)}),i=s.join(n,s.basename(t));try{const h=await Q(e,a.dumpUrl,{env:c.env||process.env,pgApplicationName:x(e.name),signal:c.signal});return l("schema.sourceDumps"),await G(e,i,a.dumpUrl,{...c,image:h,pgApplicationName:x(e.name)}),o.renameSync(i,t),{resolution:a,pgDump:{args:["--schema-only","--no-owner","--no-privileges"],applicationName:x(e.name),image:h}}}finally{ne(n)}}function le(e,r,t){return{refreshedAt:new Date().toISOString(),serviceName:e.name,envName:r.envName,cacheKey:r.cacheKey,cacheKind:r.cacheKind,cachePath:s.relative(e.productDir,r.cachePath),repo:K(r.repoState),sourceUrl:t.resolution.metadata,pgDump:t.pgDump}}function de(e,r){if(!o.existsSync(e.cachePath))return null;const t=k(e.metadataPath);if(!Fe(t))return null;const c=Date.parse(t.refreshedAt);return!Number.isFinite(c)||c<r?null:t}function x(e){return`testkit:schema-source:${e.replace(/[^a-zA-Z0-9._:-]+/g,"-").slice(0,24)||"service"}`}function fe(e){const r=o.readFileSync(e);return l("schema.cacheFilesRead"),l("schema.cacheBytesRead",r.length),ee(r.toString("utf8"))}function pe(e){if(!e.cacheExists)return!0;if(e.sourceSchema.refresh.mode==="auto")return e.ci;if(e.sourceSchema.refresh.mode==="ttl"){const r=C(e.metadataPath),t=r?Date.parse(r):0;return!Number.isFinite(t)||t<=0?!0:Date.now()-t>=e.sourceSchema.refresh.ttlSeconds*1e3}return!0}function v(e,r,t={}){if(!r?.active)return;const c=new Date().toISOString(),a=O(r.indexPath).filter(h=>h.cacheKey!==r.cacheKey),n=C(r.metadataPath);a.push({cacheKey:r.cacheKey,kind:r.cacheKind,commitSha:r.repoState.commitSha||null,branch:r.repoState.branch||null,detached:r.repoState.detached,dirty:r.repoState.dirty,dirtyFingerprint:r.repoState.dirtyFingerprint||null,contentFingerprint:r.repoState.contentFingerprint||null,remoteUrl:r.repoState.remoteUrl||null,repository:r.repoState.repository,cachePath:s.relative(e.productDir,r.cachePath),refreshedAt:t.refreshed?c:n,lastUsedAt:c});const i=Se(e,r,a);E(r.indexPath,{version:1,entries:i})}function Se(e,r,t){if(t.length<=N)return t;const c=[...t].sort((n,i)=>Date.parse(i.lastUsedAt||"0")-Date.parse(n.lastUsedAt||"0")),a=new Set(c.slice(0,N).map(n=>n.cacheKey));a.add(r.cacheKey);for(const n of t){if(a.has(n.cacheKey))continue;const i=n.cachePath?s.join(e.productDir,n.cachePath):null,h=i?s.resolve(i):null,u=s.resolve(r.rootDir);if(!i||!h||h!==u&&!h.startsWith(`${u}${s.sep}`))continue;const m=_(e.productDir,s.dirname(i),`Service "${e.name}" pruned source schema cache directory`);o.rmSync(m,{recursive:!0,force:!0})}return t.filter(n=>a.has(n.cacheKey))}function ye(e,r){E(e,r)}function E(e,r){o.mkdirSync(s.dirname(e),{recursive:!0}),R(e),o.writeFileSync(e,ke(r))}function k(e){if(!e||!o.existsSync(e))return null;R(e);try{return JSON.parse(o.readFileSync(e,"utf8"))}catch{return null}}function I(e){return D(e.productDir,"results","schema")}function R(e){if(!o.existsSync(e))return;const r=o.lstatSync(e);if(r.isSymbolicLink()||!r.isFile())throw new Error(`Refusing unsafe source schema state file at ${e}`)}function ve(e){return`${w(e.runtimeLabel||e.name)}__${w(e.name)}`}function we(e){const r=I(e);if(!o.existsSync(r))return;const t=w(e.name);for(const c of o.readdirSync(r))De(c,t)&&o.rmSync(s.join(r,c),{force:!0})}function De(e,r){for(const t of["__source-schema.sql","__local-replay-schema.sql","__schema.diff","__schema.meta.json"])if(e.endsWith(t))return e.slice(0,-t.length).endsWith(`__${r}`);return!1}async function Pe(e,r,t,c){const a=I(e);o.mkdirSync(a,{recursive:!0});const n=ve(e),i=s.join(a,`${n}__source-schema.sql`),h=s.join(a,`${n}__local-replay-schema.sql`),u=s.join(a,`${n}__schema.diff`),m=s.join(a,`${n}__schema.meta.json`);return o.writeFileSync(i,t),o.writeFileSync(h,c),o.writeFileSync(u,await ge(i,h)),E(m,be(e,r)),{sourceCachePath:r.cachePath,sourcePath:i,localPath:h,diffPath:u,metaPath:m}}function be(e,r){const t=C(r.metadataPath);return{version:1,createdAt:new Date().toISOString(),serviceName:e.name,runtimeLabel:e.runtimeLabel||e.name,sourceSchema:{envName:r.envName||null,cacheKey:r.cacheKey||null,cacheKind:r.cacheKind||null,cachePath:s.relative(e.productDir,r.cachePath),refreshedAt:t},repo:K(r.repoState)}}function ge(e,r){return new Promise(t=>{t(buildUnifiedDiffText(o.readFileSync(e,"utf8"),o.readFileSync(r,"utf8"),e,r))})}export function buildUnifiedDiffText(e,r,t="source",c="target"){if(e===r)return"";const{createTwoFilesPatch:a}=ie();return a(t,c,y(e),y(r),"","",{context:3})}function xe(e,r){return e==="fail"||e==="warn-cache"?e:r.CI==="true"?"fail":"warn-cache"}function Ee(e,r){const t={...e.testkit.serviceEnv,...e.testkit.local?.env};for(const c of r){const a=t[c];if(typeof a!="string")continue;const n=V(a).find(i=>(i.serviceName??e.name)===e.name);if(n)throw new Error(`Service "${e.name}" env ${c} uses ${n.placeholder}, which is the database the source schema verifies. Point it at another service's database or an external database URL.`)}}function w(e){return e.trim().toLowerCase().replace(/[^a-z0-9._-]+/g,"-").replace(/^-+|-+$/g,"")||"schema"}function O(e){const r=k(e);return!f(r)||!Array.isArray(r.entries)?[]:r.entries.filter(Ae)}function C(e){const r=k(e);return f(r)&&typeof r.refreshedAt=="string"?r.refreshedAt:null}function ke(e){return`${y(JSON.stringify(e,null,2))}
|
|
3
|
+
`}function Re(e){const r=T(e,"message");return!!r?r:y(e)}function Ce(e){const r=T(e,"message");return!!r?r:e}function T(e,r){if(!(e===null||typeof e!="object"&&typeof e!="function"))return Reflect.get(e,r)}function H(e){return!!e.sourceUrl}function Ae(e){return!f(e)||typeof e.cacheKey!="string"||e.cacheKey.length===0?!1:d(e.branch)&&d(e.cachePath)&&d(e.commitSha)&&d(e.contentFingerprint)&&B(e.detached)&&B(e.dirty)&&d(e.dirtyFingerprint)&&d(e.kind)&&d(e.lastUsedAt)&&d(e.refreshedAt)&&d(e.remoteUrl)&&Ke(e.repository)}function Ke(e){return e==null||f(e)&&typeof e.host=="string"&&typeof e.owner=="string"&&typeof e.name=="string"}function Fe(e){return f(e)&&typeof e.refreshedAt=="string"&&(e.sourceUrl===void 0||_e(e.sourceUrl))}function _e(e){return f(e)&&typeof e.originalHost=="string"&&q(e.originalClassification)&&typeof e.resolvedHost=="string"&&q(e.resolvedClassification)&&typeof e.rewritten=="boolean"}function q(e){return e==="neon-pooler"||e==="neon-direct"||e==="unsupported-pooler"||e==="unknown-direct"}function d(e){return e===void 0||se(e)}function B(e){return e===void 0||typeof e=="boolean"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export function sourceSchemaEnvNames(e){const n=e.testkit.database?.sourceSchema;return n?.kind==="env"?[n.env]:a(e.name)}export function sourceSchemaCredentialEnvNames(e){return[...new Set([...a(e.name),...sourceSchemaEnvNames(e)])]}function a(e){return[`TESTKIT_${e.toUpperCase().replace(/[^A-Z0-9]+/g,"_")}_SCHEMA_SOURCE_DATABASE_URL`,"TESTKIT_SCHEMA_SOURCE_DATABASE_URL"]}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{errorMessage as
|
|
2
|
-
`));return
|
|
1
|
+
import{errorMessage as l}from"../kernel/error-serialization.js";const s=".neon.tech",i="-pooler.",c=/(^|[-.])(pgbouncer|pooler)([-.]|$)/i;export function resolveSourceSchemaDumpUrl(o){const e=d(o),r=classifyDatabaseHost(e.hostname);if(r.kind==="neon-pooler"){const n=new URL(e.url.href);return n.hostname=e.hostname.replace(i,"."),a({original:e,resolved:n,originalClassification:r,resolvedClassification:classifyDatabaseHost(n.hostname),rewritten:!0,notice:"Source schema URL uses Neon pooler; Testkit is using the direct endpoint for pg_dump."})}if(r.kind==="unsupported-pooler")throw new Error(["Refusing to run pg_dump through a pooled source database URL.",`Host: ${e.hostname}`,"Configure a direct source database URL."].join(`
|
|
2
|
+
`));return a({original:e,resolved:e.url,originalClassification:r,resolvedClassification:r,rewritten:!1,notice:null})}export function classifyDatabaseHost(o){const e=o.toLowerCase();return e.endsWith(s)&&e.includes(i)?{kind:"neon-pooler",host:e}:e.endsWith(s)?{kind:"neon-direct",host:e}:c.test(e)?{kind:"unsupported-pooler",host:e}:{kind:"unknown-direct",host:e}}export function assertPgDumpArgsAreSourceSafe(o){for(let e=0;e<o.length;e+=1){const r=o[e];if(r==="-j"||r==="--jobs"||r.startsWith("--jobs="))throw new Error("Source schema pg_dump must not use parallel jobs.")}}function d(o){try{const e=new URL(o);if(!e.hostname)throw new Error("missing host");return{url:e,hostname:e.hostname.toLowerCase()}}catch(e){throw new Error(`Invalid source database URL: ${l(e)}`,{cause:e})}}function a({original:o,resolved:e,originalClassification:r,resolvedClassification:n,rewritten:t,notice:u}){return{originalUrl:o.url.href,dumpUrl:e.href,rewritten:t,notice:u,metadata:{originalHost:o.hostname,originalClassification:r.kind,resolvedHost:e.hostname.toLowerCase(),resolvedClassification:n.kind,rewritten:t}}}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import m from"fs";import e from"path";import{buildProductIdentity as
|
|
1
|
+
import m from"fs";import e from"path";import{buildProductIdentity as a}from"./docker.js";const p=String;export function writeLocalInfraState(o,t){const n=a(e.resolve(o,"..","..",".."));writeStateValue(e.join(o,"database_backend"),""),writeStateValue(e.join(o,"container_name"),t.containerName),writeStateValue(e.join(o,"container_id"),t.containerId),t.volumeName&&writeStateValue(e.join(o,"volume_name"),t.volumeName),t.volumeMountPath&&writeStateValue(e.join(o,"volume_mount_path"),t.volumeMountPath),writeStateValue(e.join(o,"ownership_product_id"),n.id),writeStateValue(e.join(o,"image"),t.image),writeStateValue(e.join(o,"user"),t.user),writeStateValue(e.join(o,"password"),t.password),writeStateValue(e.join(o,"max_connections"),String(t.maxConnections)),writeStateValue(e.join(o,"host"),t.host),writeStateValue(e.join(o,"port"),String(t.port))}export function writeCacheState(o,t,n,u){writeStateValue(e.join(o,"database_backend"),""),writeStateValue(e.join(o,"template_database_name"),n),writeStateValue(e.join(o,"template_fingerprint"),u),writeStateValue(e.join(o,"container_name"),t.containerName),t.volumeName&&writeStateValue(e.join(o,"volume_name"),t.volumeName),t.volumeMountPath&&writeStateValue(e.join(o,"volume_mount_path"),t.volumeMountPath)}export function beginTemplateCacheRebuild(o,t){m.rmSync(e.join(o,"template_fingerprint"),{force:!0}),writeStateValue(e.join(o,"template_database_name"),t)}export function writeStateValue(o,t){m.writeFileSync(o,p(t??""),{mode:384}),m.chmodSync(o,384)}
|
package/dist/database/state.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import
|
|
1
|
+
import f from"fs";import o from"path";import{resolveContainedTestkitStateDirectory as u}from"../process/runs/product-state.js";const k=new Set(["_bundles","_config","_dbcache","_execution","_graphs","_infra","_locks","_runs","_template-steps","_toolchains","_typecheck","engines","history","lint","results"]);export function getLocalInfraDir(t){return u(t,"_infra","local-postgres")}export function getLocalLocksDir(t){return u(t,"_locks","local-postgres")}export function getLocalServiceCacheDir(t,e){return u(t,"_dbcache",e)}export function listOwnedDatabaseStateDirs(t){const e=u(t),n=[],r=getLocalInfraDir(t);f.existsSync(r)&&n.push({dir:r,kind:"infra"});const d=o.join(e,"_dbcache");for(const i of l(d))n.push({dir:i,kind:"cache"});const a=o.join(e,"_graphs");for(const i of l(a))for(const c of l(o.join(i,"runtimes")))for(const _ of l(o.join(c,"services")))n.push({dir:_,kind:"runtime"});for(const i of p(e)){if(k.has(i.name)||!i.isDirectory())continue;const c=o.join(e,i.name);h(o.join(c,"database_backend"))&&n.push({dir:c,kind:"legacy"})}return n}export function hasRemainingLocalArtifacts(t){return listOwnedDatabaseStateDirs(t).map(({dir:e,kind:n})=>{const r=h(o.join(e,"database_backend"));return n!=="infra"&&(n==="cache"||r)}).some(Boolean)}export function readOwnedStateValues(t,e){const n=new Set;for(const{dir:r}of listOwnedDatabaseStateDirs(t))for(const d of e){const a=readStateValue(o.join(r,d));a&&n.add(a)}return n}function l(t){return p(t).flatMap(e=>{const n=o.join(t,e.name);if(e.isSymbolicLink())throw s(n,"symlink");if(!e.isDirectory()&&!e.isFile())throw s(n,"special node");return e.isDirectory()?[n]:[]})}function p(t){const e=m(t);if(!e)return[];if(!e.isDirectory())throw s(t,"non-directory node");return f.readdirSync(t,{withFileTypes:!0}).sort((n,r)=>n.name.localeCompare(r.name))}function h(t){const e=m(t);if(e&&!e.isFile())throw s(t,"non-file node");return!!e}function m(t){const e=f.lstatSync(t,{throwIfNoEntry:!1});if(e?.isSymbolicLink())throw s(t,"symlink");return e}export function readStateValue(t){return h(t)?f.readFileSync(t,"utf8").trim():null}function s(t,e){return new Error(`Refusing unsafe Testkit database state ${e} at ${t}`)}
|
package/dist/database/steps.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import O from"node:fs";import U from"node:path";import{runDockerPsql as P}from"./postgres/index.js";import{isUnknownRecord as S}from"../kernel/unknown-values.js";import{requireObjectRecord as R}from"../evidence/read-fields.js";const w=String;export async function verifySeed(r={}){const e=f(r),t=l(e.table,"verifySeed.args.table"),n=j(e.where),s=z(e.minRows??1,"verifySeed.args.minRows"),o=g(r),u=`select count(*)::int from ${t}${n?` where ${n}`:""}`,i=Number(await q(o,u,r));if(!Number.isInteger(i)||i<s)throw new Error(`Expected at least ${String(s)} row(s) in ${t}, found ${String(i||0)}`)}export async function verifyRows(r={}){const e=f(r),t=Array.isArray(e.checks)?e.checks:[e],n=g(r),s=[];for(const[o,u]of t.entries()){const i=`verifyRows.args.checks[${String(o)}]`,a=S(u)?u:{},m=l(a.table,`${i}.table`),d=j(a.where),c=z(a.minRows??1,`${i}.minRows`),$=a.severity==="warn"?"warn":"error",A=`select count(*)::int from ${m}${d?` where ${d}`:""}`,y=Number(await q(n,A,r));if(!Number.isInteger(y)||y<c){const N=`Expected at least ${String(c)} row(s) in ${m}, found ${String(y||0)}`;$==="warn"?J(r,{code:"database_verify_rows_min_rows",message:N,table:m,where:d||null,expectedMinRows:c,actualRows:Number.isInteger(y)?y:0}):s.push(N)}}if(s.length>0)throw new Error(`Database row verification failed:
|
|
2
2
|
${s.map(o=>`- ${o}`).join(`
|
|
3
|
-
`)}`)}export async function queryScalar(r={}){const e=f(r),t=g(r),n=
|
|
4
|
-
`)),Z(r)&&console.warn(t.message)}function Z(r){const e=r.emitWarningsToStderr;return e==null?!0:!["0","false","no","off"].includes(
|
|
3
|
+
`)}`)}export async function queryScalar(r={}){const e=f(r),t=g(r),n=v(e.statement??e.query,"queryScalar.args.statement");return q(t,n,r)}export async function runSql(r={}){const e=f(r),t=L(e.statements??e.statement,"runSql.args.statements"),n=g(r);for(const s of t){const o=await b(n,s,r);o.stdout&&process.stdout.write(o.stdout),o.stderr&&process.stderr.write(o.stderr)}}export async function seedRows(r={}){const e=f(r),t=l(e.table,"seedRows.args.table"),n=D(e.rows,"seedRows.args.rows"),s=g(r);if(e.truncate===!0&&await b(s,`truncate table ${t}`,r),n.length===0)return;const o=I(e.columns,n),u=o.map(a=>l(a,`seedRows.args.columns.${a}`)),i=n.map(a=>`(${o.map(m=>E(a[m],r)).join(", ")})`);await b(s,`insert into ${t} (${u.join(", ")}) values ${i.join(", ")}`,r)}export async function materializePostgresBinding(r={}){const e=f(r),t=l(e.table,"materializePostgresBinding.args.table"),n=l(e.keyColumn||"slug","materializePostgresBinding.args.keyColumn"),s=T(e.key,"materializePostgresBinding.args.key"),o=R(e.values,"materializePostgresBinding.args.values"),u=g(r),i=Object.keys(o);if(i.length===0)throw new Error("materializePostgresBinding.args.values must contain at least one column");const a=i.map($=>`${l($,`materializePostgresBinding.args.values.${$}`)} = ${E(o[$],r)}`),m=`with updated as (update ${t} set ${a.join(", ")} where ${n} = ${h(s)} returning 1) select count(*)::int from updated`,d=await b(u,m,r,{scalar:!0}),c=Number((d.stdout||"").trim()||"0");if(c!==1)throw new Error(`Expected to materialize 1 ${t} row for ${n}=${String(s)}, updated ${String(c||0)}`)}export async function updateRows(r={}){const e=f(r),t=l(e.table,"updateRows.args.table"),n=B(e.where,"updateRows.args.where"),s=R(e.values??e.set,"updateRows.args.values"),o=g(r),u=e.expectRows==null?null:z(e.expectRows,"updateRows.args.expectRows"),i=Object.keys(s).map(c=>`${l(c,`updateRows.args.values.${c}`)} = ${E(s[c],r)}`);if(i.length===0)throw new Error("updateRows.args.values must contain at least one column");const a=`with updated as (update ${t} set ${i.join(", ")} where ${W(n,r)} returning 1) select count(*)::int from updated`,m=await b(o,a,r,{scalar:!0}),d=Number((m.stdout||"").trim()||"0");if(u!==null&&d!==u)throw new Error(`Expected to update ${String(u)} ${t} row(s), updated ${String(d||0)}`)}function f(r){return S(r.args)?r.args:{}}function g(r){const e=(r.databaseUrl||r.env?.DATABASE_URL||"").trim();if(!e)throw new Error("Database template step requires DATABASE_URL");return e}async function q(r,e,t){return((await b(r,e,t,{scalar:!0})).stdout||"").trim()}async function b(r,e,t,n={}){const s={args:[r,"-v","ON_ERROR_STOP=1","-X",...n.scalar?["-q","-t","-A"]:[],"-c",e],cwd:t.cwd||t.productDir||process.cwd(),env:t.env||process.env,databaseUrl:r,ownedContainer:t.ownedContainer||null},o=await P(s);if(o.exitCode!==0)throw new Error(o.stderr||`psql failed with exit code ${String(o.exitCode)}`);return o}function L(r,e){const t=Array.isArray(r)?r:r==null?[]:[r];if(t.length===0)throw new Error(`${e} must contain at least one SQL statement`);return t.map((n,s)=>v(n,`${e}[${String(s)}]`))}function v(r,e){const t=w(r||"").trim();if(!t||/;\s*\S/.test(t))throw new Error(`${e} must be one SQL statement`);return t}function D(r,e){if(!Array.isArray(r))throw new Error(`${e} must be an array`);return r.map((n,s)=>R(n,`${e}[${String(s)}]`))}function I(r,e){if(r!=null){if(!Array.isArray(r)||r.length===0)throw new Error("seedRows.args.columns must be a non-empty array");return r.map(n=>String(n))}return[...new Set(e.flatMap(t=>Object.keys(t)))].sort((t,n)=>t.localeCompare(n))}function B(r,e){const t=R(r,e);if(Object.keys(t).length===0)throw new Error(`${e} must contain at least one column`);return t}function W(r,e){return Object.entries(r).map(([t,n])=>`${l(t,`where.${t}`)} = ${E(n,e)}`).join(" and ")}function l(r,e){const t=w(r||"").trim();if(!/^[A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*)?$/.test(t))throw new Error(`${e} must be a SQL identifier or schema-qualified identifier`);return t}function j(r){if(r==null||r==="")return"";const e=w(r).trim();if(!e||/;|--|\/\*/.test(e))throw new Error("verifySeed.args.where must be a single SQL predicate without comments or semicolons");return e}function z(r,e){const t=Number(r);if(!Number.isInteger(t)||t<0)throw new Error(`${e} must be a non-negative integer`);return t}function T(r,e){if(r==null||w(r).length===0)throw new Error(`${e} is required`);return r}function h(r){return r===null?"null":typeof r=="number"&&Number.isFinite(r)?String(r):typeof r=="boolean"?r?"true":"false":`'${w(r).replaceAll("'","''")}'`}function E(r,e){if(!_(r))return h(r);switch(r.kind){case"now":return"now()";case"env":return h(p(e.env||process.env,r.name));case"json":return`${h(JSON.stringify(k(r.value,e)))}::jsonb`;case"postgres-connection-from-env":return`${h(JSON.stringify(C(e.env||process.env,r.prefix)))}::jsonb`;default:throw new Error(`Unsupported database value descriptor "${r.kind}"`)}}function _(r){return S(r)&&typeof r.kind=="string"}function k(r,e){if(Array.isArray(r))return r.map(n=>k(n,e));if(!S(r))return r;if(_(r)){if(r.kind==="env")return p(e.env||process.env,r.name);if(r.kind==="postgres-connection-from-env")return C(e.env||process.env,r.prefix)}return Object.fromEntries(Object.entries(r).map(([t,n])=>[t,k(n,e)]))}function C(r,e){const t=w(e||"").trim();if(!t)throw new Error("postgresConnectionFromEnv requires a non-empty prefix");return{host:p(r,`${t}_HOST`),port:F(r,`${t}_PORT`),database:p(r,`${t}_NAME`),username:p(r,`${t}_USER`),password:p(r,`${t}_PASSWORD`),ssl:Q(r[`${t}_SSL`])}}function p(r,e){const t=String(e),n=r?.[t];if(n==null||n.trim().length===0)throw new Error(`${t} environment variable is required`);return n.trim()}function F(r,e){const t=Number.parseInt(p(r,e),10);if(!Number.isInteger(t)||t<=0)throw new Error(`${e} must be a positive integer`);return t}function Q(r){return r?["1","true","yes","on"].includes(r.trim().toLowerCase()):!1}function J(r,e){const t={severity:"warn",source:"database.verifyRows",serviceName:r.serviceName||null,runtimeId:r.runtimeId||null,timestamp:new Date().toISOString(),...e};typeof r.recordWarning=="function"&&r.recordWarning(t);const n=r.warningFile;n&&(O.mkdirSync(U.dirname(n),{recursive:!0}),O.appendFileSync(n,JSON.stringify(t)+`
|
|
4
|
+
`)),Z(r)&&console.warn(t.message)}function Z(r){const e=r.emitWarningsToStderr;return e==null?!0:!["0","false","no","off"].includes(w(e).trim().toLowerCase())}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export const DEFAULT_FILE_TIMEOUT_SECONDS=60,FILE_TIMEOUT_INTERVAL_SECONDS=.25,FILE_TIMEOUT_COOPERATIVE_ERROR_RESERVE_MAX_MS=2e3,FILE_TIMEOUT_COOPERATIVE_ERROR_RESERVE_MIN_MS=250,FILE_TIMEOUT_COOPERATIVE_ERROR_RESERVE_RATIO=.2,
|
|
1
|
+
import{isNonNullObject as i}from"../kernel/unknown-values.js";export const DEFAULT_FILE_TIMEOUT_SECONDS=60,FILE_TIMEOUT_INTERVAL_SECONDS=.25,FILE_TIMEOUT_COOPERATIVE_ERROR_RESERVE_MAX_MS=2e3,FILE_TIMEOUT_COOPERATIVE_ERROR_RESERVE_MIN_MS=250,FILE_TIMEOUT_COOPERATIVE_ERROR_RESERVE_RATIO=.2,INTERNAL_FILE_TIMEOUT_SECONDS_ENV="TESTKIT_INTERNAL_FILE_TIMEOUT_SECONDS",INTERNAL_FILE_DEADLINE_MS_ENV="TESTKIT_INTERNAL_FILE_DEADLINE_MS";export function parseFileTimeoutOption(e){return o(e,"--file-timeout-seconds")}export function normalizeFileTimeoutSeconds(e,t="execution.fileTimeoutSeconds"){return normalizePositiveInteger(e,t)}export function normalizeWaitIntervalSeconds(e){if(e==null)return FILE_TIMEOUT_INTERVAL_SECONDS;const t=Number(e);if(!Number.isFinite(t)||t<=0)throw new Error("waitFor intervalSeconds must be a positive number.");return t}export function readFileTimeoutBudget(e){if(!i(e))throw new Error("Runtime file timeout budget is missing.");return{fileTimeoutSeconds:normalizeFileTimeoutSeconds(e[INTERNAL_FILE_TIMEOUT_SECONDS_ENV],INTERNAL_FILE_TIMEOUT_SECONDS_ENV),deadlineMs:n(e[INTERNAL_FILE_DEADLINE_MS_ENV],INTERNAL_FILE_DEADLINE_MS_ENV)}}export function remainingFileTimeoutMs(e,t=Date.now()){return Math.max(0,e.deadlineMs-t)}export function remainingFileTimeoutSeconds(e,t=Date.now()){return remainingFileTimeoutMs(e,t)/1e3}export function remainingCooperativeFileTimeoutMs(e,t=Date.now()){return Math.max(0,remainingFileTimeoutMs(e,t)-cooperativeFileTimeoutErrorReserveMs(e.fileTimeoutSeconds))}export function cooperativeFileTimeoutErrorReserveMs(e){return Math.min(FILE_TIMEOUT_COOPERATIVE_ERROR_RESERVE_MAX_MS,Math.max(FILE_TIMEOUT_COOPERATIVE_ERROR_RESERVE_MIN_MS,Math.floor(e*1e3*FILE_TIMEOUT_COOPERATIVE_ERROR_RESERVE_RATIO)))}export function formatWaitForTimeoutError(e,t){return`Timed out waiting for ${String(e||"condition").trim()||"condition"} before the ${String(t)}s test file timeout`}function o(e,t){const r=Number.parseInt(String(e),10);if(!Number.isInteger(r)||r<=0)throw new Error(`Invalid ${t} value "${String(e)}". Expected a positive integer.`);return r}export function normalizePositiveInteger(e,t){const r=Number(e);if(!Number.isInteger(r)||r<=0)throw new Error(`${t} must be a positive integer.`);return r}function n(e,t){const r=Number(e);if(!Number.isInteger(r)||r<=0)throw new Error(`${t} must be a positive integer timestamp.`);return r}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export const ENGINE_IDS=Object.freeze(["
|
|
1
|
+
export const ENGINE_IDS=Object.freeze(["node-vitest","playwright"]),PUBLIC_TEST_TYPES=Object.freeze(["ui","contract","data"]),RUNNER_PROFILE_IDS=Object.freeze(["node-vitest-data","node-vitest-contract","node-vitest","playwright-ui"]);const n=new Set(ENGINE_IDS),o=new Set(PUBLIC_TEST_TYPES),r=new Set(RUNNER_PROFILE_IDS);export function isEngineId(t){return typeof t=="string"&&n.has(t)}export function isTypedTestFile(t){return PUBLIC_TEST_TYPES.some(e=>t.endsWith(`.${e}.testkit.ts`))}export function isPublicTestType(t){return typeof t=="string"&&o.has(t)}export function isRunnerProfileId(t){return typeof t=="string"&&r.has(t)}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{redactSensitiveText as i}from"../kernel/redaction.js";const g=new Set(["passed","succeeded","success","ready","cached"]),h=new Set(["running","pending","queued","starting","applying","cancelling"]),y=new Set(["skipped","skip","noop","no-op","not_applicable","not-applicable","cancelled","canceled","interrupted"]),A=new Set(["failed","error"]);export function normalizeOperationStatus(n){return S(n||"unknown").trim().toLowerCase().replaceAll(" ","_")}export function operationStatusKind(n){const e=normalizeOperationStatus(n);return g.has(e)?"passed":h.has(e)?"running":y.has(e)?"skipped":A.has(e)?"failed":"unknown"}export function operationStatusLabel(n,{runningLabel:e="RUN"}={}){const t=operationStatusKind(n);return t==="passed"?"PASS":t==="running"?e:t==="skipped"?"SKIP":"FAIL"}export function isNeutralOperationStatus(n){return operationStatusKind(n)==="skipped"}export function isSuccessfulOperationStatus(n){return operationStatusKind(n)==="passed"}export function formatOperationSummary(n={}){const e=
|
|
1
|
+
import{redactSensitiveText as i}from"../kernel/redaction.js";import{isUnknownRecord as l}from"../kernel/unknown-values.js";const g=new Set(["passed","succeeded","success","ready","cached"]),h=new Set(["running","pending","queued","starting","applying","cancelling"]),y=new Set(["skipped","skip","noop","no-op","not_applicable","not-applicable","cancelled","canceled","interrupted"]),A=new Set(["failed","error"]);export function normalizeOperationStatus(n){return S(n||"unknown").trim().toLowerCase().replaceAll(" ","_")}export function operationStatusKind(n){const e=normalizeOperationStatus(n);return g.has(e)?"passed":h.has(e)?"running":y.has(e)?"skipped":A.has(e)?"failed":"unknown"}export function operationStatusLabel(n,{runningLabel:e="RUN"}={}){const t=operationStatusKind(n);return t==="passed"?"PASS":t==="running"?e:t==="skipped"?"SKIP":"FAIL"}export function isNeutralOperationStatus(n){return operationStatusKind(n)==="skipped"}export function isSuccessfulOperationStatus(n){return operationStatusKind(n)==="passed"}export function formatOperationSummary(n={}){const e=d(n),t=d(e.metadata),r=[];e.message?r.push(i(e.message)):e.summary?r.push(i(e.summary)):e.error&&r.push(i(e.error));const a=o(t.phase||e.phase),c=o(t.action||e.action),s=o(t.resource||e.resource),u=o(t.decision),p=o(t.reason),f=k(e.logRefs);if(!r.length&&(a||c||s)){const m=[a,c].filter(Boolean).join(".");r.push(m+(s?` ${s}`:""))}return u&&r.push(`decision=${i(u)}`),p&&r.push(`reason=${i(p)}`),f&&r.push(`log=${f}`),r.filter(Boolean).join(" \xB7 ")}function k(n=[]){if(!Array.isArray(n))return"";const e=n.find(l);if(!e)return"";const t=o(e.service||e.serviceName),r=o(e.category||e.kind),a=o(e.step||e.name),c=o(e.state),s=[t,[r,a].filter(Boolean).join("."),c].filter(Boolean).join(" ");return s?i(s):""}function d(n){return l(n)?n:{}}function o(n){return n?S(n):""}const S=String;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const RETIRED_TEST_TYPES=Object.freeze(["int","e2e","dal","load"].map(e=>({testType:e,release:"0.5.0",migration:"remove-k6"}))),RETIRED_ENGINES=Object.freeze([{engine:"k6",release:"0.5.0",migration:"remove-k6"}]);export function retiredEngineMessage(e){const t=RETIRED_ENGINES.find(n=>n.engine===e);return t===void 0?null:`Testkit ${t.release} removed the ${t.engine} engine. Migrate with docs/release-notes.md#${t.migration}`}export function rejectRetiredTestType(e,t,n=RETIRED_TEST_TYPES){const r=n.find(i=>i.testType===e);if(r!==void 0)throw new Error(`${t}: Testkit ${r.release} removed the ${r.testType} test type. Migrate with docs/release-notes.md#${r.migration}.`)}export function rejectRetiredTestFile(e,t=RETIRED_TEST_TYPES){const n=/\.([^./]+)\.testkit\.ts$/u.exec(e)?.[1];n!==void 0&&rejectRetiredTestType(n,e,t)}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export const DEFAULT_RUNTIME_ISOLATION_POLICY="shared",RUNTIME_ISOLATION_POLICIES=Object.freeze(["shared","resetBefore"
|
|
1
|
+
export const DEFAULT_RUNTIME_ISOLATION_POLICY="shared",RUNTIME_ISOLATION_POLICIES=Object.freeze(["shared","resetBefore"]);const s=new Set(RUNTIME_ISOLATION_POLICIES),r={resetAfter:'is "resetAfter", which was removed. Set "resetBefore" on the file that needs a clean database instead.',exclusive:'is "exclusive", which was removed. Use "shared" and have each test create and filter by its own rows, or "resetBefore" for a test that changes a whole table.'};export function isRuntimeIsolationPolicy(e){return typeof e=="string"&&s.has(e)}export function normalizeRuntimeIsolationPolicy(e,t){if(isRuntimeIsolationPolicy(e))return e;throw typeof e=="string"&&Object.hasOwn(r,e)?new Error(`${t} ${r[e]}`):new Error(`${t} must be one of ${RUNTIME_ISOLATION_POLICIES.map(o=>JSON.stringify(o)).join(", ")}`)}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{RUN_TYPE_ORDER as
|
|
1
|
+
import{RUN_TYPE_ORDER as f,RUN_TYPES as h,isPublicTestType as y,publicTestTypeListText as l}from"./test-types.js";import"./identifiers.js";import{RETIRED_TEST_TYPES as a,rejectRetiredTestType as d}from"./retired-test-types.js";const m=String;export function normalizeTypeValues(e=[],n=a){const t=[];for(const p of e)if(p!=null)for(const i of m(p).split(",")){const o=i.trim();if(!o)continue;const c=o;if(d(c,`Type "${o}"`,n),!h.has(c))throw new Error(`Unknown type "${o}". Expected one of: ${l({includeAll:!0})}.`);t.push(c)}if(t.length===0)return["all"];const r=[...new Set(t)];if(r.includes("all")&&r.length>1)throw new Error('"--type all" cannot be combined with other types.');return r.sort((p,i)=>f.indexOf(p)-f.indexOf(i))}export function isAllTypeSelection(e=[]){return e.length===0||e.length===1&&e[0]==="all"}export function parseSuiteSelectors(e=[],n=a){const t=[];for(const r of e)if(r!=null)for(const p of m(r).split(",")){const i=p.trim();if(!i)continue;const o=i.indexOf(":");if(o<0){t.push({kind:"plain",name:i,raw:i});continue}const c=i.slice(0,o),s=i.slice(o+1).trim(),u=c.trim();if(d(u,`Suite selector "${i}"`,n),!y(u))throw new Error(`Unknown suite selector type "${c}". Expected one of: ${l()}.`);if(!s)throw new Error(`Invalid suite selector "${i}". Expected "<type>:<suite-name>".`);t.push({kind:"typed",testType:u,name:s,raw:`${u}:${s}`})}return t}export function suiteKey(e){return`${e.testType??""}:${e.name}`}export function matchesSelectedTypes(e,n){return isAllTypeSelection(n)||e!==void 0&&n.includes(e)}export function matchesSuiteSelectors(e,n,t=[]){return!t||t.length===0?!0:t.some(r=>(r.kind==="plain"||r.testType===e)&&r.name===n)}export function parseTypeOption(e,n=null){const t=[];if(n&&t.push(n),Array.isArray(e)){const r=e;t.push(...r)}else e&&t.push(e);return normalizeTypeValues(t)}export function parseSuiteOption(e){const n=Array.isArray(e)?e:e?[e]:[];return parseSuiteSelectors(n)}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export const TEST_OUTCOME_STATUS_ORDER=Object.freeze(["passed","failed","skipped","not_run","cancelled","interrupted"]);
|
|
1
|
+
export const TEST_OUTCOME_STATUS_ORDER=Object.freeze(["passed","failed","skipped","not_run","cancelled","interrupted"]);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{PUBLIC_TEST_TYPES as e,isPublicTestType as
|
|
1
|
+
import{PUBLIC_TEST_TYPES as e,isPublicTestType as i}from"./identifiers.js";export const TEST_TYPE_ORDER=e;const o=[...e,"all"];export const RUN_TYPE_ORDER=o,RUN_TYPES=new Set(RUN_TYPE_ORDER);const s={ui:"UI",contract:"Contract",data:"Data"},T=String;export function isPublicTestType(t){return i(n(t))}export function isRunType(t){return RUN_TYPES.has(n(t))}export function formatPublicTestType(t){const r=n(t);return isPublicTestType(r)?s[r]:T(t||"")}export function publicTestTypeList({includeAll:t=!1}={}){return t?[...o]:[...e]}export function publicTestTypeListText(t={}){return publicTestTypeList(t).join(", ")}function n(t){return T(t||"").trim()}export function formatTestTypeLabel(t){return formatPublicTestType(t)}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{requireNativeEvidenceJson as N}from"./native-report.js";import E from"node:crypto";import{constants as O}from"node:fs";import d from"node:fs/promises";import F from"node:path";import{readOnlyNoFollowOpenFlags as R,sameFileSnapshot as U,samePathAndHandleFileSnapshot as C}from"../../process/files/file-snapshot.js";const z=64*1024;export const evidenceFileAccess={lstat:async t=>d.lstat(t,{bigint:!0}),openReadOnly:async t=>d.open(t,R(process.platform,O)),platform:process.platform,readChunk:async(t,n)=>(await t.read(n,0,n.byteLength,null)).bytesRead,statHandle:async t=>t.stat({bigint:!0})};export function stableEvidenceFromBuffer(t){return{contents:t,sha256:E.createHash("sha256").update(t).digest("hex"),sizeBytes:t.byteLength}}export async function readStableJsonFile(t,n,e,r){let i;try{i=await readStableEvidenceFile(t,n,e,r)}catch(o){if(
|
|
1
|
+
import{requireNativeEvidenceJson as N}from"./native-report.js";import E from"node:crypto";import{constants as O}from"node:fs";import d from"node:fs/promises";import F from"node:path";import{readOnlyNoFollowOpenFlags as R,sameFileSnapshot as U,samePathAndHandleFileSnapshot as C}from"../../process/files/file-snapshot.js";const z=64*1024;export const evidenceFileAccess={lstat:async t=>d.lstat(t,{bigint:!0}),openReadOnly:async t=>d.open(t,R(process.platform,O)),platform:process.platform,readChunk:async(t,n)=>(await t.read(n,0,n.byteLength,null)).bytesRead,statHandle:async t=>t.stat({bigint:!0})};export function stableEvidenceFromBuffer(t){return{contents:t,sha256:E.createHash("sha256").update(t).digest("hex"),sizeBytes:t.byteLength}}export async function readStableJsonFile(t,n,e,r){let i;try{i=await readStableEvidenceFile(t,n,e,r)}catch(o){if(isMissingFileError(o))return{file:null,value:null};throw o}let a;try{a=JSON.parse(i.contents.toString("utf8"))}catch(o){throw new Error(`${e} is not valid JSON`,{cause:o})}return N(a,e),{file:i,value:a}}export async function readStableEvidenceFile(t,n,e,r,i){const a=i??await r.lstat(t);u(a,n,e);const o=await r.openReadOnly(t);try{const w=await r.statHandle(o);u(w,n,e),m(a,w,e,r.platform);const p=[],l=E.createHash("sha256");let h=0;const s=Buffer.allocUnsafe(z);for(;;){const f=await r.readChunk(o,s);if(I(f,s.byteLength,e),f===0)break;if(h+=f,h>n)throw new Error(`${e} exceeds its ${n.toString()} byte limit`);const g=Buffer.from(s.subarray(0,f));p.push(g),l.update(g)}const c=await r.statHandle(o),y=await r.lstat(t);if(u(c,n,e),u(y,n,e),S(w,c,e),S(a,y,e),m(y,c,e,r.platform),BigInt(h)!==w.size)throw new Error(`${e} changed while it was read`);return{contents:Buffer.concat(p,h),sha256:l.digest("hex"),sizeBytes:h}}finally{await o.close()}}export async function writeOwnedEvidenceFile(t,n,e){const r=F.join(t,n),i=F.join(t,`.${n}.${E.randomUUID()}.tmp`);let a=null;try{return a=await d.open(i,"wx",384),await a.writeFile(e),await a.sync(),await a.close(),a=null,await d.rename(i,r),process.platform!=="win32"&&await d.chmod(r,384),r}catch(o){throw a!==null&&await a.close().catch(()=>{}),await d.rm(i,{force:!0}).catch(()=>{}),o}}export async function snapshotStableEvidenceFile(t,n,e,r,i,a,o){const w=await r.lstat(t);u(w,n,e),S(i,w,e);const p=await r.openReadOnly(t),l=F.join(a,o),h=F.join(a,`.${o}.${E.randomUUID()}.tmp`);let s=null;try{const c=await r.statHandle(p);u(c,n,e),m(w,c,e,r.platform),s=await d.open(h,"wx",384);const y=E.createHash("sha256");let f=0;const g=Buffer.allocUnsafe(z);for(;;){const $=await r.readChunk(p,g);if(I($,g.byteLength,e),$===0)break;if(f+=$,f>n)throw new Error(`${e} exceeds its ${n.toString()} byte limit`);const k=g.subarray(0,$);await s.write(k),y.update(k)}const v=await r.statHandle(p),H=await r.lstat(t);if(u(v,n,e),u(H,n,e),S(c,v,e),S(w,H,e),m(H,v,e,r.platform),BigInt(f)!==c.size)throw new Error(`${e} changed while it was read`);return await s.sync(),await s.close(),s=null,await d.rename(h,l),process.platform!=="win32"&&await d.chmod(l,384),{filePath:l,identity:{sha256:y.digest("hex"),sizeBytes:f}}}catch(c){throw s!==null&&await s.close().catch(()=>{}),await d.rm(h,{force:!0}).catch(()=>{}),c}finally{await p.close()}}export function evidenceArtifact(t,n,e,r,i,a){return{contentType:e,kind:r,metadata:{sha256:n.sha256,sizeBytes:n.sizeBytes},name:i,path:t,summary:a}}function I(t,n,e){if(!Number.isSafeInteger(t)||t<0||t>n)throw new Error(`${e} returned an invalid read length`)}function u(t,n,e){if(!t.isFile()||t.isSymbolicLink())throw new Error(`${e} must be a regular non-symbolic-link file`);if(t.size<0n||t.size>BigInt(n))throw new Error(`${e} exceeds its ${n.toString()} byte limit`)}function S(t,n,e){if(!U(t,n))throw new Error(`${e} changed while it was read`)}function m(t,n,e,r){if(!C(t,n,r))throw new Error(`${e} changed while it was read`)}export function isMissingFileError(t){return t instanceof Error&&"code"in t&&t.code==="ENOENT"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import o from"node:fs";import{fileURLToPath as i}from"node:url";import{currentPlatformTarget as a,platformTargetKey as n,SUPPORTED_PLATFORM_TARGETS as f,UnsupportedPlatformTargetError as p}from"../../process/host/platform.js";const
|
|
1
|
+
import o from"node:fs";import{fileURLToPath as i}from"node:url";import{currentPlatformTarget as a,platformTargetKey as n,SUPPORTED_PLATFORM_TARGETS as f,UnsupportedPlatformTargetError as p}from"../../process/host/platform.js";import{isUnknownRecord as s}from"../../kernel/unknown-values.js";const d=c();export function engineEvidenceIdentity(e){if(e.profile.engineId!==e.engineId)throw new TypeError(`Evidence profile ${e.profile.id} belongs to ${e.profile.engineId}, expected ${e.engineId}`);const t=e.target??a();if(!f.some(r=>n(r)===n(t)))throw new p(t.platform,t.arch,"engine evidence target");return Object.freeze({engineId:e.engineId,installationKind:e.installationKind,nodeVersion:process.versions.node,profileId:e.profile.id,target:Object.freeze({arch:t.arch,platform:t.platform}),testkitVersion:d})}function c(){const e=i(new URL("../../../package.json",import.meta.url));let t;try{t=JSON.parse(o.readFileSync(e,"utf8"))}catch(r){throw new TypeError(`Root Testkit package manifest could not be read from ${e}`,{cause:r})}if(!s(t)||t.name!=="@gfdelarue/testkit")throw new TypeError(`Root Testkit package manifest at ${e} has an invalid package identity`);if(typeof t.version!="string"||!/^(?:0|[1-9][0-9]*)\.(?:0|[1-9][0-9]*)\.(?:0|[1-9][0-9]*)(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/u.test(t.version))throw new TypeError(`Root Testkit package manifest at ${e} has an invalid version`);return t.version}
|