pipework 0.252.0 → 0.252.1
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/CHANGELOG.md +5 -0
- package/package.json +4 -3
- package/src/async/jobs/abortable-delay.ts +18 -0
- package/src/async/jobs/claims.ts +52 -0
- package/src/async/jobs/cron.ts +179 -0
- package/src/async/jobs/errors.ts +56 -0
- package/src/async/jobs/exclusion.ts +117 -0
- package/src/async/jobs/execute.ts +219 -0
- package/src/async/jobs/index.ts +10 -0
- package/src/async/jobs/listener.ts +135 -0
- package/src/async/jobs/namespace.ts +20 -0
- package/src/async/jobs/queue-door.ts +44 -0
- package/src/async/jobs/queue.ts +1012 -0
- package/src/async/jobs/run-script.ts +67 -0
- package/src/async/jobs/scheduler.ts +218 -0
- package/src/async/jobs/schedules-table.ts +37 -0
- package/src/async/jobs/serialize-error.ts +48 -0
- package/src/async/jobs/table.ts +191 -0
- package/src/async/pipeline/define.ts +53 -0
- package/src/async/pipeline/errors.ts +35 -0
- package/src/async/pipeline/execute.ts +53 -0
- package/src/async/pipeline/index.ts +13 -0
- package/src/async/pipeline/namespace.ts +12 -0
- package/src/async/pipeline/resume.ts +67 -0
- package/src/async/pipeline/run-steps.ts +98 -0
- package/src/async/pipeline/types.ts +57 -0
- package/src/async/state-machine/define.ts +64 -0
- package/src/async/state-machine/errors.ts +29 -0
- package/src/async/state-machine/index.ts +3 -0
- package/src/async/state-machine/namespace.ts +12 -0
- package/src/async/state-machine/transition.ts +90 -0
- package/src/async/state-machine/types.ts +37 -0
- package/src/async/steps/errors.ts +31 -0
- package/src/async/steps/graph.ts +1214 -0
- package/src/async/steps/hash.ts +40 -0
- package/src/async/steps/index.ts +7 -0
- package/src/async/steps/namespace.ts +7 -0
- package/src/async/steps/table.ts +97 -0
- package/src/async/steps/types.ts +318 -0
- package/src/audit/chain/index.ts +9 -0
- package/src/audit/chain/install.ts +113 -0
- package/src/audit/chain/retire.ts +52 -0
- package/src/audit/chain/verify.ts +257 -0
- package/src/audit/query/cursor.ts +37 -0
- package/src/audit/query/index.ts +9 -0
- package/src/audit/query/query.ts +223 -0
- package/src/audit/schema/audit-record-fields.ts +68 -0
- package/src/audit/schema/audit-record.ts +23 -0
- package/src/auth/auth/audit.ts +31 -0
- package/src/auth/auth/chain.ts +84 -0
- package/src/auth/auth/cookie.ts +136 -0
- package/src/auth/auth/email-links.ts +311 -0
- package/src/auth/auth/guard.ts +128 -0
- package/src/auth/auth/index.ts +18 -0
- package/src/auth/auth/multi-org.ts +337 -0
- package/src/auth/auth/namespace.ts +34 -0
- package/src/auth/auth/parse-expiry.ts +13 -0
- package/src/auth/auth/passwords.ts +110 -0
- package/src/auth/auth/schema.ts +110 -0
- package/src/auth/auth/sessions.ts +565 -0
- package/src/auth/auth/sign-in.ts +156 -0
- package/src/auth/auth/signing-keys.ts +69 -0
- package/src/auth/auth/types.ts +26 -0
- package/src/auth/auth/uuid.ts +10 -0
- package/src/auth/rbac/cache.ts +55 -0
- package/src/auth/rbac/check.ts +30 -0
- package/src/auth/rbac/create.ts +35 -0
- package/src/auth/rbac/index.ts +5 -0
- package/src/auth/rbac/middleware.ts +20 -0
- package/src/auth/rbac/namespace.ts +15 -0
- package/src/auth/rbac/resolve.ts +31 -0
- package/src/auth/rbac/schema.sql +31 -0
- package/src/auth/rbac/seed.ts +67 -0
- package/src/auth/rbac/tables.ts +32 -0
- package/src/auth/rbac/types.ts +65 -0
- package/src/auth/tenant/extract.ts +33 -0
- package/src/auth/tenant/in-tenant.ts +49 -0
- package/src/auth/tenant/index.ts +7 -0
- package/src/auth/tenant/namespace.ts +23 -0
- package/src/auth/tenant/propagate.ts +20 -0
- package/src/auth/tenant/rls.ts +120 -0
- package/src/auth/tenant/scope.ts +33 -0
- package/src/auth/tenant/scoped-db.ts +581 -0
- package/src/auth/tenant/types.ts +14 -0
- package/src/auth/tenant/validate.ts +27 -0
- package/src/cli/commands/add.ts +158 -0
- package/src/cli/commands/alerts.ts +98 -0
- package/src/cli/commands/check.ts +1418 -0
- package/src/cli/commands/codemod.ts +78 -0
- package/src/cli/commands/db.ts +356 -0
- package/src/cli/commands/dev.ts +101 -0
- package/src/cli/commands/generate.ts +75 -0
- package/src/cli/commands/init.ts +36 -0
- package/src/cli/commands/install.ts +28 -0
- package/src/cli/commands/lint.ts +62 -0
- package/src/cli/commands/migrate.ts +132 -0
- package/src/cli/commands/openapi.ts +63 -0
- package/src/cli/commands/remove.ts +72 -0
- package/src/cli/commands/run.ts +50 -0
- package/src/cli/commands/scope.ts +54 -0
- package/src/cli/commands/serve.ts +68 -0
- package/src/cli/commands/shard.ts +31 -0
- package/src/cli/commands/shared/config-pipework-version.ts +28 -0
- package/src/cli/commands/shared/find-config.ts +22 -0
- package/src/cli/commands/shared/load-config.ts +35 -0
- package/src/cli/commands/shared/pnpm-install.ts +95 -0
- package/src/cli/commands/shared/report-removed.ts +8 -0
- package/src/cli/commands/sync.ts +32 -0
- package/src/cli/commands/test.ts +307 -0
- package/src/cli/commands/upgrade.ts +48 -0
- package/src/cli/dev-bootstrap.ts +24 -0
- package/src/cli/errors.ts +10 -0
- package/src/cli/index.ts +303 -0
- package/src/cli/output.ts +17 -0
- package/src/cli/port.ts +15 -0
- package/src/cli/surface-kinds.ts +20 -0
- package/src/client/index.ts +4 -0
- package/src/client/poll.ts +130 -0
- package/src/client/report-fault.ts +119 -0
- package/src/core/config/app-pool.ts +19 -0
- package/src/core/config/aws-env.ts +29 -0
- package/src/core/config/base-dir.ts +24 -0
- package/src/core/config/discover.ts +64 -0
- package/src/core/config/env-resolve.ts +87 -0
- package/src/core/config/env-types.ts +116 -0
- package/src/core/config/env.ts +55 -0
- package/src/core/config/errors.ts +9 -0
- package/src/core/config/import-config.ts +40 -0
- package/src/core/config/index.ts +8 -0
- package/src/core/config/load.ts +417 -0
- package/src/core/config/namespace.ts +14 -0
- package/src/core/config/resolve.ts +195 -0
- package/src/core/config/schema.ts +488 -0
- package/src/core/config/statement-observer.ts +48 -0
- package/src/core/config/ts-inline-hooks.ts +75 -0
- package/src/core/config/ts-register.ts +4 -0
- package/src/core/config/ts-resolve-hooks.ts +101 -0
- package/src/core/errors/base.ts +49 -0
- package/src/core/errors/http.ts +146 -0
- package/src/core/errors/index.ts +2 -0
- package/src/core/errors/namespace.ts +31 -0
- package/src/core/lifecycle/drain.ts +33 -0
- package/src/core/lifecycle/health.ts +72 -0
- package/src/core/lifecycle/index.ts +4 -0
- package/src/core/lifecycle/namespace.ts +7 -0
- package/src/core/lifecycle/startup.ts +102 -0
- package/src/core/lifecycle/types.ts +36 -0
- package/src/core/logging/context.ts +60 -0
- package/src/core/logging/error-chain.ts +68 -0
- package/src/core/logging/index.ts +5 -0
- package/src/core/logging/logger.ts +65 -0
- package/src/core/logging/namespace.ts +16 -0
- package/src/core/logging/proxy.ts +22 -0
- package/src/core/logging/redact-match.ts +24 -0
- package/src/core/logging/types.ts +47 -0
- package/src/core/pipework.ts +566 -0
- package/src/core/runtime/broker.ts +69 -0
- package/src/core/surface/http.ts +133 -0
- package/src/core/surface/index.ts +11 -0
- package/src/core/surface/job-timeout.ts +40 -0
- package/src/core/surface/namespace.ts +13 -0
- package/src/core/surface/script.ts +33 -0
- package/src/core/surface/start.ts +142 -0
- package/src/core/surface/types.ts +225 -0
- package/src/core/surface/validate.ts +88 -0
- package/src/core/surface/worker.ts +528 -0
- package/src/core/validation/connectivity.ts +49 -0
- package/src/core/validation/domain.ts +99 -0
- package/src/core/validation/index.ts +5 -0
- package/src/core/validation/queue-tables.ts +312 -0
- package/src/core/validation/rls-role.ts +138 -0
- package/src/core/validation/startup.ts +51 -0
- package/src/core/validation/surfaces.ts +65 -0
- package/src/core/validation/types.ts +25 -0
- package/src/data/db/advisory-lock.ts +55 -0
- package/src/data/db/aggregates.ts +172 -0
- package/src/data/db/array-elements.ts +111 -0
- package/src/data/db/bulk-set.ts +53 -0
- package/src/data/db/col/index.ts +14 -0
- package/src/data/db/composed.ts +53 -0
- package/src/data/db/db.ts +276 -0
- package/src/data/db/excluded.ts +16 -0
- package/src/data/db/expressions.ts +473 -0
- package/src/data/db/filter.ts +90 -0
- package/src/data/db/function-source.ts +114 -0
- package/src/data/db/identifiers.ts +16 -0
- package/src/data/db/idx/index.ts +9 -0
- package/src/data/db/in-list.ts +49 -0
- package/src/data/db/index.ts +9 -0
- package/src/data/db/insert-split.ts +137 -0
- package/src/data/db/instrumentation.ts +82 -0
- package/src/data/db/maintenance.ts +28 -0
- package/src/data/db/namespace.ts +107 -0
- package/src/data/db/owner-labels.ts +9 -0
- package/src/data/db/pool.ts +187 -0
- package/src/data/db/query-label.ts +13 -0
- package/src/data/db/query-watch.ts +116 -0
- package/src/data/db/raw-client-warning.ts +37 -0
- package/src/data/db/readonly.ts +23 -0
- package/src/data/db/reservation.ts +151 -0
- package/src/data/db/schema-features.ts +14 -0
- package/src/data/db/serializable.ts +83 -0
- package/src/data/db/sql.ts +1 -0
- package/src/data/db/timeouts.ts +76 -0
- package/src/data/db/types.ts +18 -0
- package/src/data/db/unbound.ts +33 -0
- package/src/data/db/values.ts +29 -0
- package/src/data/domain/brand.ts +15 -0
- package/src/data/domain/build-factory.ts +193 -0
- package/src/data/domain/build-schema.ts +161 -0
- package/src/data/domain/build-table.ts +355 -0
- package/src/data/domain/define.ts +239 -0
- package/src/data/domain/external-registry.ts +34 -0
- package/src/data/domain/external.ts +77 -0
- package/src/data/domain/field.ts +279 -0
- package/src/data/domain/index.ts +13 -0
- package/src/data/domain/naming.ts +6 -0
- package/src/data/domain/projections.ts +57 -0
- package/src/data/domain/trace-safe-projection.ts +31 -0
- package/src/data/domain/traits.ts +108 -0
- package/src/data/domain/types.ts +289 -0
- package/src/data/jsonb/composed.ts +18 -0
- package/src/data/jsonb/index.ts +1 -0
- package/src/data/jsonb/namespace.ts +30 -0
- package/src/data/jsonb/query.ts +86 -0
- package/src/data/migrate/append-only-generate.ts +47 -0
- package/src/data/migrate/applied.ts +47 -0
- package/src/data/migrate/apply.ts +309 -0
- package/src/data/migrate/check.ts +119 -0
- package/src/data/migrate/diff.ts +357 -0
- package/src/data/migrate/door-generate.ts +130 -0
- package/src/data/migrate/framework-rls.ts +58 -0
- package/src/data/migrate/generate.ts +201 -0
- package/src/data/migrate/history.ts +215 -0
- package/src/data/migrate/index.ts +9 -0
- package/src/data/migrate/internal-definitions.ts +175 -0
- package/src/data/migrate/post-process.ts +73 -0
- package/src/data/migrate/pragma.ts +73 -0
- package/src/data/migrate/provision-framework.ts +147 -0
- package/src/data/migrate/rls-generate.ts +61 -0
- package/src/data/migrate/run.ts +124 -0
- package/src/data/migrate/safety.ts +167 -0
- package/src/data/migrate/snapshot.ts +368 -0
- package/src/data/migrate/sql-emit.ts +392 -0
- package/src/data/migrate/statements.ts +312 -0
- package/src/data/query/alias.ts +126 -0
- package/src/data/query/batch.ts +8 -0
- package/src/data/query/cache/core/cache.ts +78 -0
- package/src/data/query/cache/core/index.ts +1 -0
- package/src/data/query/cache/core/types.ts +29 -0
- package/src/data/query/cache/readme.md +230 -0
- package/src/data/query/casing.ts +76 -0
- package/src/data/query/collation.ts +11 -0
- package/src/data/query/column-builder.ts +370 -0
- package/src/data/query/column.ts +150 -0
- package/src/data/query/entity.ts +42 -0
- package/src/data/query/errors.ts +33 -0
- package/src/data/query/index.ts +14 -0
- package/src/data/query/logger.ts +47 -0
- package/src/data/query/operations.ts +45 -0
- package/src/data/query/pg-core/alias.ts +12 -0
- package/src/data/query/pg-core/checks.ts +32 -0
- package/src/data/query/pg-core/columns/all.ts +71 -0
- package/src/data/query/pg-core/columns/bigint.ts +110 -0
- package/src/data/query/pg-core/columns/bigserial.ts +128 -0
- package/src/data/query/pg-core/columns/boolean.ts +43 -0
- package/src/data/query/pg-core/columns/char.ts +85 -0
- package/src/data/query/pg-core/columns/cidr.ts +43 -0
- package/src/data/query/pg-core/columns/common.ts +366 -0
- package/src/data/query/pg-core/columns/custom.ts +232 -0
- package/src/data/query/pg-core/columns/date.common.ts +15 -0
- package/src/data/query/pg-core/columns/date.ts +112 -0
- package/src/data/query/pg-core/columns/double-precision.ts +55 -0
- package/src/data/query/pg-core/columns/enum.ts +201 -0
- package/src/data/query/pg-core/columns/index.ts +35 -0
- package/src/data/query/pg-core/columns/inet.ts +43 -0
- package/src/data/query/pg-core/columns/int.common.ts +57 -0
- package/src/data/query/pg-core/columns/integer.ts +53 -0
- package/src/data/query/pg-core/columns/interval.ts +83 -0
- package/src/data/query/pg-core/columns/json.ts +58 -0
- package/src/data/query/pg-core/columns/jsonb.ts +56 -0
- package/src/data/query/pg-core/columns/line.ts +117 -0
- package/src/data/query/pg-core/columns/macaddr.ts +43 -0
- package/src/data/query/pg-core/columns/macaddr8.ts +43 -0
- package/src/data/query/pg-core/columns/numeric.ts +227 -0
- package/src/data/query/pg-core/columns/point.ts +127 -0
- package/src/data/query/pg-core/columns/postgis_extension/geometry.ts +119 -0
- package/src/data/query/pg-core/columns/postgis_extension/utils.ts +47 -0
- package/src/data/query/pg-core/columns/real.ts +58 -0
- package/src/data/query/pg-core/columns/serial.ts +55 -0
- package/src/data/query/pg-core/columns/smallint.ts +53 -0
- package/src/data/query/pg-core/columns/smallserial.ts +60 -0
- package/src/data/query/pg-core/columns/text.ts +67 -0
- package/src/data/query/pg-core/columns/time.ts +72 -0
- package/src/data/query/pg-core/columns/timestamp.ts +163 -0
- package/src/data/query/pg-core/columns/uuid.ts +51 -0
- package/src/data/query/pg-core/columns/varchar.ts +89 -0
- package/src/data/query/pg-core/columns/vector_extension/bit.ts +68 -0
- package/src/data/query/pg-core/columns/vector_extension/halfvec.ts +80 -0
- package/src/data/query/pg-core/columns/vector_extension/sparsevec.ts +67 -0
- package/src/data/query/pg-core/columns/vector_extension/vector.ts +80 -0
- package/src/data/query/pg-core/db.ts +674 -0
- package/src/data/query/pg-core/dialect.ts +1404 -0
- package/src/data/query/pg-core/expressions.ts +25 -0
- package/src/data/query/pg-core/foreign-keys.ts +130 -0
- package/src/data/query/pg-core/index.ts +21 -0
- package/src/data/query/pg-core/indexes.ts +276 -0
- package/src/data/query/pg-core/policies.ts +56 -0
- package/src/data/query/pg-core/primary-keys.ts +62 -0
- package/src/data/query/pg-core/query-builders/count.ts +86 -0
- package/src/data/query/pg-core/query-builders/delete.ts +293 -0
- package/src/data/query/pg-core/query-builders/index.ts +7 -0
- package/src/data/query/pg-core/query-builders/insert.ts +452 -0
- package/src/data/query/pg-core/query-builders/query-builder.ts +123 -0
- package/src/data/query/pg-core/query-builders/query.ts +157 -0
- package/src/data/query/pg-core/query-builders/raw.ts +49 -0
- package/src/data/query/pg-core/query-builders/refresh-materialized-view.ts +108 -0
- package/src/data/query/pg-core/query-builders/select.ts +1346 -0
- package/src/data/query/pg-core/query-builders/select.types.ts +437 -0
- package/src/data/query/pg-core/query-builders/update.ts +626 -0
- package/src/data/query/pg-core/roles.ts +41 -0
- package/src/data/query/pg-core/schema.ts +72 -0
- package/src/data/query/pg-core/sequence.ts +41 -0
- package/src/data/query/pg-core/session.ts +292 -0
- package/src/data/query/pg-core/subquery.ts +46 -0
- package/src/data/query/pg-core/table.ts +252 -0
- package/src/data/query/pg-core/unique-constraint.ts +73 -0
- package/src/data/query/pg-core/utils/array.ts +95 -0
- package/src/data/query/pg-core/utils/index.ts +1 -0
- package/src/data/query/pg-core/utils.ts +104 -0
- package/src/data/query/pg-core/view-base.ts +14 -0
- package/src/data/query/pg-core/view-common.ts +1 -0
- package/src/data/query/pg-core/view.ts +435 -0
- package/src/data/query/pg-core/with-builder.ts +41 -0
- package/src/data/query/postgres-js/README.md +46 -0
- package/src/data/query/postgres-js/array-parser.ts +45 -0
- package/src/data/query/postgres-js/driver-error-boundary.ts +149 -0
- package/src/data/query/postgres-js/driver.ts +163 -0
- package/src/data/query/postgres-js/index.ts +2 -0
- package/src/data/query/postgres-js/session.ts +234 -0
- package/src/data/query/primary-key.ts +11 -0
- package/src/data/query/query-builders/query-builder.ts +19 -0
- package/src/data/query/query-builders/select.types.ts +175 -0
- package/src/data/query/query-promise.ts +35 -0
- package/src/data/query/relations.ts +725 -0
- package/src/data/query/runnable-query.ts +12 -0
- package/src/data/query/selection-proxy.ts +121 -0
- package/src/data/query/session.ts +8 -0
- package/src/data/query/sql/expressions/conditions.ts +741 -0
- package/src/data/query/sql/expressions/index.ts +2 -0
- package/src/data/query/sql/expressions/select.ts +45 -0
- package/src/data/query/sql/functions/aggregate.ts +129 -0
- package/src/data/query/sql/functions/index.ts +2 -0
- package/src/data/query/sql/functions/vector.ts +178 -0
- package/src/data/query/sql/index.ts +3 -0
- package/src/data/query/sql/sql.ts +794 -0
- package/src/data/query/subquery.ts +53 -0
- package/src/data/query/table.ts +208 -0
- package/src/data/query/table.utils.ts +2 -0
- package/src/data/query/tracing-utils.ts +3 -0
- package/src/data/query/tracing.ts +11 -0
- package/src/data/query/utils.ts +357 -0
- package/src/data/query/version.ts +4 -0
- package/src/data/query/view-common.ts +1 -0
- package/src/data/schema/branded.ts +13 -0
- package/src/data/schema/generated/coerce.ts +5 -0
- package/src/data/schema/generated/combinator.ts +6 -0
- package/src/data/schema/generated/encoding.ts +5 -0
- package/src/data/schema/generated/error.ts +6 -0
- package/src/data/schema/generated/format.ts +5 -0
- package/src/data/schema/generated/type-constructor.ts +10 -0
- package/src/data/schema/generated/type.ts +6 -0
- package/src/data/schema/index.ts +6 -0
- package/src/data/schema/json-schema.ts +33 -0
- package/src/data/schema/namespace.ts +55 -0
- package/src/data/schema/parse.ts +39 -0
- package/src/data/schema/types.ts +39 -0
- package/src/data/temporal/append-only.ts +134 -0
- package/src/data/temporal/close.ts +27 -0
- package/src/data/temporal/columns.ts +10 -0
- package/src/data/temporal/definition-queries.ts +70 -0
- package/src/data/temporal/errors.ts +10 -0
- package/src/data/temporal/get-current.ts +22 -0
- package/src/data/temporal/index.ts +9 -0
- package/src/data/temporal/namespace.ts +36 -0
- package/src/data/temporal/query.ts +25 -0
- package/src/data/temporal/resolve.ts +17 -0
- package/src/data/temporal/revise.ts +67 -0
- package/src/data/temporal/types.ts +22 -0
- package/src/data/valve/artifact.ts +33 -0
- package/src/data/valve/contract-diff.ts +21 -0
- package/src/data/valve/cross.ts +57 -0
- package/src/data/valve/crossing-ledger.ts +50 -0
- package/src/data/valve/decision.ts +44 -0
- package/src/data/valve/durable-crossing.ts +52 -0
- package/src/data/valve/execute-crossing.ts +77 -0
- package/src/data/valve/faucet.ts +56 -0
- package/src/data/valve/gate.ts +114 -0
- package/src/data/valve/graph.ts +77 -0
- package/src/data/valve/index.ts +53 -0
- package/src/data/valve/namespace.ts +57 -0
- package/src/data/valve/registry.ts +50 -0
- package/src/data/valve/schema/valve-crossing-fields.ts +23 -0
- package/src/data/valve/schema/valve-crossing.ts +9 -0
- package/src/data/valve/supply.ts +154 -0
- package/src/data/valve/types.ts +143 -0
- package/src/data/vector/columns.ts +27 -0
- package/src/data/vector/index.ts +4 -0
- package/src/data/vector/namespace.ts +33 -0
- package/src/data/vector/query.ts +85 -0
- package/src/data/vector/validate.ts +16 -0
- package/src/data/write/audit/actor.ts +15 -0
- package/src/data/write/audit/audit-columns.ts +18 -0
- package/src/data/write/audit/audit-select.ts +111 -0
- package/src/data/write/audit/bound-transaction.ts +14 -0
- package/src/data/write/audit/cte-compile.ts +32 -0
- package/src/data/write/audit/decode-returning.ts +47 -0
- package/src/data/write/audit/final-select.ts +43 -0
- package/src/data/write/audit/infra-audit.ts +36 -0
- package/src/data/write/audit/primary-key.ts +65 -0
- package/src/data/write/audit/projection.ts +41 -0
- package/src/data/write/audit/read-touches.ts +86 -0
- package/src/data/write/audit/touch-match.ts +78 -0
- package/src/data/write/audit/verb-delete.ts +62 -0
- package/src/data/write/audit/verb-insert.ts +36 -0
- package/src/data/write/audit/verb-select.ts +47 -0
- package/src/data/write/audit/verb-update.ts +60 -0
- package/src/data/write/audit/verb-upsert.ts +104 -0
- package/src/data/write/conflict.ts +43 -0
- package/src/data/write/delete.ts +38 -0
- package/src/data/write/execute.ts +243 -0
- package/src/data/write/index.ts +11 -0
- package/src/data/write/insert.ts +63 -0
- package/src/data/write/state.ts +43 -0
- package/src/data/write/update.ts +54 -0
- package/src/data/write/upsert.ts +58 -0
- package/src/data/write/values.ts +19 -0
- package/src/index.ts +230 -0
- package/src/infra/access-links/code.ts +70 -0
- package/src/infra/access-links/handle.ts +892 -0
- package/src/infra/access-links/index.ts +9 -0
- package/src/infra/access-links/namespace.ts +30 -0
- package/src/infra/access-links/person-check.ts +177 -0
- package/src/infra/access-links/schema.ts +71 -0
- package/src/infra/alerts/async-sources.ts +231 -0
- package/src/infra/alerts/audit.ts +36 -0
- package/src/infra/alerts/beat.ts +123 -0
- package/src/infra/alerts/client-faults.ts +111 -0
- package/src/infra/alerts/faults.ts +156 -0
- package/src/infra/alerts/handle.ts +778 -0
- package/src/infra/alerts/http.ts +192 -0
- package/src/infra/alerts/index.ts +12 -0
- package/src/infra/alerts/infra-sources.ts +180 -0
- package/src/infra/alerts/namespace.ts +81 -0
- package/src/infra/alerts/redact.ts +21 -0
- package/src/infra/alerts/request-faults.ts +41 -0
- package/src/infra/alerts/schema.ts +124 -0
- package/src/infra/alerts/sinks.ts +125 -0
- package/src/infra/alerts/source-options.ts +26 -0
- package/src/infra/alerts/sources.ts +503 -0
- package/src/infra/alerts/subsystem-sources.ts +137 -0
- package/src/infra/audit/emit.ts +87 -0
- package/src/infra/audit/index.ts +2 -0
- package/src/infra/audit/namespace.ts +22 -0
- package/src/infra/audit/schema.sql +35 -0
- package/src/infra/audit/types.ts +18 -0
- package/src/infra/aws/credentials.ts +147 -0
- package/src/infra/aws/sigv4.ts +140 -0
- package/src/infra/cache/create.ts +58 -0
- package/src/infra/cache/entry.ts +81 -0
- package/src/infra/cache/index.ts +4 -0
- package/src/infra/cache/namespace.ts +10 -0
- package/src/infra/cache/tenant.ts +93 -0
- package/src/infra/cache/types.ts +24 -0
- package/src/infra/crawl/audit.ts +35 -0
- package/src/infra/crawl/errors.ts +25 -0
- package/src/infra/crawl/handle.ts +693 -0
- package/src/infra/crawl/index.ts +9 -0
- package/src/infra/crawl/links.ts +160 -0
- package/src/infra/crawl/namespace.ts +36 -0
- package/src/infra/crawl/robots.ts +100 -0
- package/src/infra/crawl/schema.ts +89 -0
- package/src/infra/credit-line/handle.ts +883 -0
- package/src/infra/credit-line/index.ts +6 -0
- package/src/infra/credit-line/namespace.ts +7 -0
- package/src/infra/credit-line/schema.ts +96 -0
- package/src/infra/credits/handle.ts +706 -0
- package/src/infra/credits/index.ts +5 -0
- package/src/infra/credits/namespace.ts +7 -0
- package/src/infra/credits/schema.ts +83 -0
- package/src/infra/email/audit.ts +36 -0
- package/src/infra/email/inbound.ts +622 -0
- package/src/infra/email/index.ts +11 -0
- package/src/infra/email/mail.ts +517 -0
- package/src/infra/email/mime.ts +447 -0
- package/src/infra/email/namespace.ts +22 -0
- package/src/infra/email/providers.ts +111 -0
- package/src/infra/email/schema.ts +96 -0
- package/src/infra/email/sns.ts +200 -0
- package/src/infra/email/unsubscribe.ts +44 -0
- package/src/infra/fetch/address.ts +88 -0
- package/src/infra/fetch/connect-retry.ts +87 -0
- package/src/infra/fetch/handle.ts +329 -0
- package/src/infra/fetch/index.ts +7 -0
- package/src/infra/fetch/namespace.ts +70 -0
- package/src/infra/idempotency/index.ts +3 -0
- package/src/infra/idempotency/middleware.ts +87 -0
- package/src/infra/idempotency/store.ts +92 -0
- package/src/infra/idempotency/types.ts +30 -0
- package/src/infra/join-requests/handle.ts +312 -0
- package/src/infra/join-requests/index.ts +2 -0
- package/src/infra/join-requests/namespace.ts +7 -0
- package/src/infra/join-requests/schema.ts +33 -0
- package/src/infra/outbox/core.ts +165 -0
- package/src/infra/quota/errors.ts +32 -0
- package/src/infra/quota/handle.ts +215 -0
- package/src/infra/quota/index.ts +5 -0
- package/src/infra/quota/namespace.ts +7 -0
- package/src/infra/quota/schema.ts +15 -0
- package/src/infra/storage/backend.ts +34 -0
- package/src/infra/storage/handle.ts +513 -0
- package/src/infra/storage/index.ts +9 -0
- package/src/infra/storage/local.ts +104 -0
- package/src/infra/storage/namespace.ts +13 -0
- package/src/infra/storage/s3.ts +257 -0
- package/src/infra/storage/schema.ts +24 -0
- package/src/infra/support/handle.ts +854 -0
- package/src/infra/support/http.ts +229 -0
- package/src/infra/support/index.ts +7 -0
- package/src/infra/support/namespace.ts +12 -0
- package/src/infra/support/schema.ts +96 -0
- package/src/infra/webhook/inbound.ts +45 -0
- package/src/infra/webhook/index.ts +5 -0
- package/src/infra/webhook/namespace.ts +24 -0
- package/src/infra/webhook/outbound.ts +315 -0
- package/src/infra/webhook/sign.ts +32 -0
- package/src/infra/webhook/verify.ts +82 -0
- package/src/invariants/assert.ts +26 -0
- package/src/invariants/index.ts +1 -0
- package/src/lint/config.ts +56 -0
- package/src/lint/doctrine.ts +163 -0
- package/src/lint/include.ts +97 -0
- package/src/lint/index.ts +3 -0
- package/src/lint/preset.ts +10 -0
- package/src/lint/resolver.ts +218 -0
- package/src/lint/rules/no-reexports.ts +46 -0
- package/src/request/behavior/audited.ts +85 -0
- package/src/request/behavior/cached.ts +62 -0
- package/src/request/behavior/compose.ts +28 -0
- package/src/request/behavior/index.ts +5 -0
- package/src/request/behavior/namespace.ts +16 -0
- package/src/request/behavior/types.ts +26 -0
- package/src/request/behavior/versioned.ts +54 -0
- package/src/request/context/create.ts +103 -0
- package/src/request/context/guard.ts +49 -0
- package/src/request/context/index.ts +4 -0
- package/src/request/context/run-in-transaction.ts +200 -0
- package/src/request/context/store.ts +28 -0
- package/src/request/context/types.ts +50 -0
- package/src/request/di/builder.ts +535 -0
- package/src/request/di/index.ts +8 -0
- package/src/request/di/resolve.ts +271 -0
- package/src/request/di/types.ts +262 -0
- package/src/request/fitting/namespace.ts +7 -0
- package/src/request/fixture/batch.ts +71 -0
- package/src/request/fixture/builder.ts +26 -0
- package/src/request/fixture/errors.ts +10 -0
- package/src/request/fixture/index.ts +8 -0
- package/src/request/fixture/namespace.ts +23 -0
- package/src/request/fixture/paginate.ts +44 -0
- package/src/request/fixture/to-handlers.ts +260 -0
- package/src/request/fixture/types.ts +79 -0
- package/src/request/flow/act-as.ts +78 -0
- package/src/request/flow/namespace.ts +17 -0
- package/src/request/http/error-step.ts +42 -0
- package/src/request/http/file.ts +141 -0
- package/src/request/http/index.ts +4 -0
- package/src/request/http/middleware.ts +141 -0
- package/src/request/http/namespace.ts +5 -0
- package/src/request/http/rate-limit-classes.ts +170 -0
- package/src/request/http/response.ts +82 -0
- package/src/request/http/server.ts +916 -0
- package/src/request/http/sniff.ts +261 -0
- package/src/request/http/types.ts +250 -0
- package/src/request/http/upload.ts +284 -0
- package/src/request/internal/text.ts +6 -0
- package/src/request/openapi/generate.ts +205 -0
- package/src/request/openapi/index.ts +2 -0
- package/src/request/openapi/namespace.ts +7 -0
- package/src/test/auth.ts +13 -0
- package/src/test/auto-setup.ts +45 -0
- package/src/test/context.ts +91 -0
- package/src/test/crawl-fixture.ts +157 -0
- package/src/test/discover-fork.ts +17 -0
- package/src/test/fingerprint.ts +135 -0
- package/src/test/globals.d.ts +7 -0
- package/src/test/index.ts +19 -0
- package/src/test/namespace.ts +87 -0
- package/src/test/plugin.ts +189 -0
- package/src/test/progress.ts +185 -0
- package/src/test/reap-after-run.ts +28 -0
- package/src/test/reap.ts +286 -0
- package/src/test/reset.ts +253 -0
- package/src/test/savepoints.ts +172 -0
- package/src/test/setup.ts +1422 -0
- package/src/test/system-channel.ts +50 -0
- package/src/test/vitest-plugin.ts +142 -0
- package/src/test/vitest-workspace.ts +507 -0
- package/src/test/vitest.ts +333 -0
- package/src/test/watch.ts +105 -0
- package/src/trace/capture/bounded-serializer.ts +146 -0
- package/src/trace/cardinality/analyze.ts +173 -0
- package/src/trace/cardinality/branch-context.ts +214 -0
- package/src/trace/cardinality/check.ts +33 -0
- package/src/trace/cardinality/classify-edge.ts +64 -0
- package/src/trace/cardinality/structural-max.ts +107 -0
- package/src/trace/check/coverage-check.ts +188 -0
- package/src/trace/check/flow-check.ts +103 -0
- package/src/trace/check/flow-step-site.ts +234 -0
- package/src/trace/check/io-pattern.ts +116 -0
- package/src/trace/codemod/add-cardinality.ts +152 -0
- package/src/trace/codemod/run.ts +80 -0
- package/src/trace/entry/child-trace.ts +21 -0
- package/src/trace/entry/close-trace.ts +87 -0
- package/src/trace/entry/finalize-trace.ts +28 -0
- package/src/trace/entry/open-trace.ts +138 -0
- package/src/trace/entry/parent-trace.ts +20 -0
- package/src/trace/entry/parse-traceparent.ts +39 -0
- package/src/trace/entry/process-start.ts +38 -0
- package/src/trace/entry/trace-context.ts +26 -0
- package/src/trace/entry/tracing-enabled.ts +10 -0
- package/src/trace/external/record.ts +195 -0
- package/src/trace/external/report.ts +66 -0
- package/src/trace/namespace.ts +19 -0
- package/src/trace/partition/day.ts +47 -0
- package/src/trace/partition/index.ts +14 -0
- package/src/trace/partition/partition-name.ts +23 -0
- package/src/trace/partition/plan.ts +82 -0
- package/src/trace/partition/schedule.ts +26 -0
- package/src/trace/partition/startup-hook.ts +39 -0
- package/src/trace/partition/sync.ts +154 -0
- package/src/trace/partition/tables.ts +53 -0
- package/src/trace/read/tree.ts +83 -0
- package/src/trace/retain/copy-on-error.ts +76 -0
- package/src/trace/runtime/expire-stale-traces.ts +54 -0
- package/src/trace/runtime/flow-step.ts +147 -0
- package/src/trace/runtime/flush-steps.ts +31 -0
- package/src/trace/runtime/pending-writes.ts +54 -0
- package/src/trace/runtime/step-buffer.ts +298 -0
- package/src/trace/runtime/step-error.ts +21 -0
- package/src/trace/runtime/step-meta.ts +55 -0
- package/src/trace/runtime/thenable.ts +12 -0
- package/src/trace/runtime/truncate-trace.ts +62 -0
- package/src/trace/schema/trace-fields.ts +52 -0
- package/src/trace/schema/trace-retained.ts +22 -0
- package/src/trace/schema/trace-step-fields.ts +44 -0
- package/src/trace/schema/trace-step-retained.ts +25 -0
- package/src/trace/schema/trace-step.ts +18 -0
- package/src/trace/schema/trace.ts +12 -0
- package/src/trace/transformer/apply.ts +79 -0
- package/src/trace/transformer/build-meta-literal.ts +30 -0
- package/src/trace/transformer/find-flow-step-sites.ts +85 -0
- package/src/trace/transformer/inject-flow-step-import.ts +39 -0
- package/src/trace/transformer/transform-error.ts +14 -0
- package/src/trace/transformer/transform-flow-steps.ts +123 -0
- package/src/trace/transformer/wrap-function-declaration.ts +62 -0
- package/src/trace/transformer/wrap-variable-declaration.ts +67 -0
- package/src/workspace/affected.ts +110 -0
- package/src/workspace/changed-files.ts +163 -0
- package/src/workspace/check-deps.ts +213 -0
- package/src/workspace/checksum.ts +20 -0
- package/src/workspace/dependency-graph.ts +67 -0
- package/src/workspace/detect-pipework-copies.ts +93 -0
- package/src/workspace/discover.ts +206 -0
- package/src/workspace/generate.ts +401 -0
- package/src/workspace/index.ts +7 -0
- package/src/workspace/module-imports-pipework.ts +39 -0
- package/src/workspace/module-package-name.ts +12 -0
- package/src/workspace/scrub-git-env.ts +23 -0
- package/src/workspace/source-paths.ts +36 -0
- package/src/workspace/sync.ts +182 -0
- package/dist/REFERENCE.md +0 -1477
- package/dist/infra/access-links/rate-limit.d.ts +0 -10
- package/dist/infra/access-links/rate-limit.d.ts.map +0 -1
- package/dist/infra/access-links/rate-limit.js +0 -28
- package/dist/infra/access-links/rate-limit.js.map +0 -1
- package/dist/infra/email/sigv4.d.ts +0 -24
- package/dist/infra/email/sigv4.d.ts.map +0 -1
- package/dist/infra/email/sigv4.js +0 -55
- package/dist/infra/email/sigv4.js.map +0 -1
|
@@ -0,0 +1,417 @@
|
|
|
1
|
+
import { resolve } from 'node:path'
|
|
2
|
+
import { trunkConfigSchema, type Blueprint, type Environment, type DatabaseConfig, type IsolationStrategy } from './schema.js'
|
|
3
|
+
import { resolveEnvironment, resolveEnvironmentStrict, resolveDbUrl, resolveTestOwnerUrl } from './resolve.js'
|
|
4
|
+
import { ConfigError } from './errors.js'
|
|
5
|
+
import { resolveEnvVars } from './env-resolve.js'
|
|
6
|
+
import { consumeConfigBaseDir } from './base-dir.js'
|
|
7
|
+
import type { StatementObserver } from './statement-observer.js'
|
|
8
|
+
|
|
9
|
+
export interface ResolvedObservability {
|
|
10
|
+
readonly slowQueryMs: number
|
|
11
|
+
/** Interval at which a still-running statement is reported; undefined = off. */
|
|
12
|
+
readonly inFlightQueryMs?: number | undefined
|
|
13
|
+
readonly logAllQueries: boolean
|
|
14
|
+
readonly poolMetricsInterval: number
|
|
15
|
+
/** Caller-supplied per-statement observer; undefined when not configured. */
|
|
16
|
+
readonly onStatement: StatementObserver | undefined
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface ResolvedTrace {
|
|
20
|
+
readonly hotRetentionDays: number
|
|
21
|
+
readonly errorRetentionDays: number
|
|
22
|
+
readonly partitionLeadDays: number
|
|
23
|
+
readonly safetyFactor: number
|
|
24
|
+
/** Unset → the runtime step cap is open (truncation never fires). Set → cap is `structuralMax × safetyFactor`. */
|
|
25
|
+
readonly structuralMax: number | undefined
|
|
26
|
+
readonly captureInputs: boolean
|
|
27
|
+
/** Per-trace limit on steps recorded from outside the Node process via `trace.record`; not part of the structural cap. */
|
|
28
|
+
readonly maxExternalSteps: number
|
|
29
|
+
/**
|
|
30
|
+
* Consumer-extension I/O registry (Q9). Keys are lexical call patterns —
|
|
31
|
+
* either a bare identifier (`"fetch"`) or a single member access
|
|
32
|
+
* (`"cache.get"`) — and values declare whether the call is a read or a
|
|
33
|
+
* write. The Tier-2 coverage check unions this with the stock pipework
|
|
34
|
+
* registry (`pipe.*` writes, `pipe(...)`-chain reads).
|
|
35
|
+
*/
|
|
36
|
+
readonly io: Readonly<Record<string, 'read' | 'write'>>
|
|
37
|
+
/**
|
|
38
|
+
* Maximum audit rows emitted per write+audit CTE statement (Q8 / Phase 6c).
|
|
39
|
+
* Bulk inserts whose `values.length × touches.length` would exceed this are
|
|
40
|
+
* split into chunks executed sequentially inside a transaction (or savepoint
|
|
41
|
+
* when already nested). Engineering knob: defaults to 5000, never zero.
|
|
42
|
+
*/
|
|
43
|
+
readonly auditChunkSize: number
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface ResolvedAudit {
|
|
47
|
+
readonly retentionDays: number | 'keep'
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface ResolvedConfig {
|
|
51
|
+
readonly raw: Blueprint
|
|
52
|
+
/** The environment this process runs as: Blueprint `environment`, else `PIPEWORK_ENV`, else `NODE_ENV` (test/production), else development. Read it here rather than from `process.env`. */
|
|
53
|
+
readonly environment: Environment
|
|
54
|
+
/** The release read from `release.env`; null when not configured, unset or empty. */
|
|
55
|
+
readonly release: string | null
|
|
56
|
+
readonly observability: ResolvedObservability
|
|
57
|
+
readonly trace: ResolvedTrace
|
|
58
|
+
readonly audit: ResolvedAudit
|
|
59
|
+
readonly env: Readonly<Record<string, unknown>>
|
|
60
|
+
/** Declared cross-tenant read doors, by name, each with its database resolved. */
|
|
61
|
+
readonly doors: Readonly<Record<string, ResolvedDoor>>
|
|
62
|
+
/** Doors pipework declares for its own substrates, only where the default database has `rls.platformRole`. */
|
|
63
|
+
readonly frameworkDoors: Readonly<Record<string, ResolvedDoor>>
|
|
64
|
+
database(name?: string): ResolvedDatabase
|
|
65
|
+
databases(): ReadonlyMap<string, ResolvedDatabase>
|
|
66
|
+
databaseNames(): readonly string[]
|
|
67
|
+
defaultDatabaseName(): string
|
|
68
|
+
isolationStrategy(dbName: string): IsolationStrategy
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export interface ResolvedDoor {
|
|
72
|
+
readonly name: string
|
|
73
|
+
/** SQL table name. */
|
|
74
|
+
readonly table: string
|
|
75
|
+
/** The database the table lives in — the default database when the door names none. */
|
|
76
|
+
readonly database: string
|
|
77
|
+
readonly reason: string
|
|
78
|
+
/** A door pipework declares for its own work (named `pipework.*`); not in config. */
|
|
79
|
+
readonly framework?: true
|
|
80
|
+
/** Operations a framework door opens; an app door is SELECT-only. */
|
|
81
|
+
readonly ops?: readonly ('select' | 'update' | 'delete')[]
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export interface ResolvedDatabase {
|
|
85
|
+
readonly name: string
|
|
86
|
+
readonly url: string
|
|
87
|
+
readonly appUrl?: string
|
|
88
|
+
/** Platform mode only: the connection `pipe.system()` and `pipe.door()` run on, as `rls.platformRole`. */
|
|
89
|
+
readonly systemUrl?: string
|
|
90
|
+
/** Test mode only: the connection whose role migrates the template. See `testOwnerUrl` in the config schema. */
|
|
91
|
+
readonly testOwnerUrl?: string
|
|
92
|
+
/** Directory relative `migrations`/`schema` paths resolve against — the config file's directory (process.cwd() for directly-constructed configs). */
|
|
93
|
+
readonly baseDir: string
|
|
94
|
+
/** Glob patterns stay as authored; consumers glob them with `cwd: baseDir`. */
|
|
95
|
+
readonly schema: string | string[] | undefined
|
|
96
|
+
/** Absolute — resolved against baseDir at load time, so cwd never matters (D1). */
|
|
97
|
+
readonly migrations: string | undefined
|
|
98
|
+
readonly extensions: readonly string[]
|
|
99
|
+
readonly migrationTable: string
|
|
100
|
+
readonly pool: { max: number; idleTimeout: number; readonly publishReservation?: boolean; readonly settings?: Readonly<Record<string, string>> }
|
|
101
|
+
readonly rls?: { readonly sessionVar: string; readonly force: boolean; readonly platformRole?: string }
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function loadConfig(raw: unknown): ResolvedConfig {
|
|
105
|
+
// Consume before validation so a parse failure cannot leave the slot set for
|
|
106
|
+
// an unrelated later load.
|
|
107
|
+
const baseDir = consumeConfigBaseDir() ?? process.cwd()
|
|
108
|
+
const parsed = trunkConfigSchema.safeParse(raw)
|
|
109
|
+
if (!parsed.success) {
|
|
110
|
+
const issues = parsed.error.issues.map(i => ` - ${i.path.join('.')}: ${i.message}`).join('\n')
|
|
111
|
+
throw new ConfigError(
|
|
112
|
+
`Invalid pipework.config.ts:\n${issues}`,
|
|
113
|
+
'Check your pipework.config.ts against the expected schema.',
|
|
114
|
+
)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const config = parsed.data
|
|
118
|
+
const environment = config.environment
|
|
119
|
+
?? (config.requireEnvironment === true ? resolveEnvironmentStrict() : resolveEnvironment())
|
|
120
|
+
return buildResolved(config, environment, baseDir)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function stringifySettings(settings: Readonly<Record<string, string | number | boolean>>): Readonly<Record<string, string>> {
|
|
124
|
+
const out: Record<string, string> = {}
|
|
125
|
+
for (const [k, v] of Object.entries(settings)) out[k] = typeof v === 'boolean' ? (v ? 'on' : 'off') : String(v)
|
|
126
|
+
return out
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function resolveRelease(def: Blueprint['release'], environment: Environment): string | null {
|
|
130
|
+
if (def === undefined) return null
|
|
131
|
+
const value = process.env[def.env]
|
|
132
|
+
if (value !== undefined && value !== '') return value
|
|
133
|
+
if (def.requiredIn?.includes(environment) === true) {
|
|
134
|
+
throw new ConfigError(
|
|
135
|
+
`Environment variable ${def.env} is not set, but the release is required in environment "${environment}".`,
|
|
136
|
+
`Set ${def.env} to this deploy's release (for example ${def.env}=2026.10.3), or remove "${environment}" from release.requiredIn.`,
|
|
137
|
+
)
|
|
138
|
+
}
|
|
139
|
+
return null
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function buildResolved(config: Blueprint, environment: Environment, baseDir: string): ResolvedConfig {
|
|
143
|
+
const dbMap = new Map<string, ResolvedDatabase>()
|
|
144
|
+
|
|
145
|
+
// Defined here, not inline in the accessor, so the app-pair carve-out below
|
|
146
|
+
// and `isolationStrategy()` cannot answer the same question differently.
|
|
147
|
+
const isolationStrategyFor = (dbName: string): IsolationStrategy =>
|
|
148
|
+
config.test?.databases?.[dbName]?.isolation ?? config.test?.isolation ?? 'transaction'
|
|
149
|
+
|
|
150
|
+
// URL resolution is attempted for EVERY database here, but a failure is not
|
|
151
|
+
// thrown until a URL is first accessed. Two recorded consumer defects drive
|
|
152
|
+
// this shape: refusing at load meant runs that never open a connection
|
|
153
|
+
// (type-only test legs, `check`) still demanded DB env; and throwing on the
|
|
154
|
+
// first failure surfaced missing variables one per run. So failures are
|
|
155
|
+
// collected across all databases and the aggregate is thrown lazily, naming
|
|
156
|
+
// everything that must be fixed at once.
|
|
157
|
+
const urlIssues: ConfigError[] = []
|
|
158
|
+
const collect = (fn: () => string): string | undefined => {
|
|
159
|
+
try {
|
|
160
|
+
return fn()
|
|
161
|
+
} catch (err) {
|
|
162
|
+
if (err instanceof ConfigError) {
|
|
163
|
+
urlIssues.push(err)
|
|
164
|
+
return undefined
|
|
165
|
+
}
|
|
166
|
+
throw err
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
const refuseUrls = (): never => {
|
|
170
|
+
const details = urlIssues
|
|
171
|
+
.map(e => ` - ${e.message.replace(/^\[pipework\] /, '').trimEnd().split('\n').join('\n ')}`)
|
|
172
|
+
.join('\n')
|
|
173
|
+
throw new ConfigError(
|
|
174
|
+
`Database URL resolution failed (${String(urlIssues.length)} problem${urlIssues.length === 1 ? '' : 's'}):\n\n${details}`,
|
|
175
|
+
'Fix every item above — they are all reported in this one run.',
|
|
176
|
+
)
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
for (const [name, dbConfig] of Object.entries(config.databases)) {
|
|
180
|
+
const url = collect(() => resolveDbUrl(dbConfig.url, dbConfig.testUrl, environment, name))
|
|
181
|
+
const appUrl = dbConfig.appUrl !== undefined
|
|
182
|
+
? collect(() => resolveDbUrl(dbConfig.appUrl!, dbConfig.appTestUrl, environment, name, { strategy: isolationStrategyFor(name) }))
|
|
183
|
+
: undefined
|
|
184
|
+
const systemUrl = dbConfig.systemUrl !== undefined
|
|
185
|
+
? collect(() => resolveDbUrl(dbConfig.systemUrl!, dbConfig.systemTestUrl, environment, name, { strategy: isolationStrategyFor(name), role: 'system' }))
|
|
186
|
+
: undefined
|
|
187
|
+
// Outside test mode the field is inert — production already migrates as
|
|
188
|
+
// `url`'s role — so it is neither resolved nor exposed. `{ resolved }`
|
|
189
|
+
// rather than a bare string so a failed resolution is still a property
|
|
190
|
+
// that refuses on access, not a property that quietly went missing.
|
|
191
|
+
const testOwner = dbConfig.testOwnerUrl !== undefined && environment === 'test'
|
|
192
|
+
? { resolved: collect(() => resolveTestOwnerUrl(dbConfig.testOwnerUrl!, url, name)) }
|
|
193
|
+
: undefined
|
|
194
|
+
dbMap.set(name, resolveDatabase(name, dbConfig, url, appUrl, systemUrl, testOwner, baseDir, refuseUrls))
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const declaredNames = [...dbMap.keys()]
|
|
198
|
+
|
|
199
|
+
const defaultDbName = config.defaultDatabase
|
|
200
|
+
if (defaultDbName !== undefined && !dbMap.has(defaultDbName)) {
|
|
201
|
+
const suggestion = closestMatch(defaultDbName, declaredNames)
|
|
202
|
+
throw new ConfigError(
|
|
203
|
+
`defaultDatabase "${defaultDbName}" is not a declared database.`,
|
|
204
|
+
suggestion !== null
|
|
205
|
+
? `Did you mean "${suggestion}"? Declared databases: ${declaredNames.join(', ')}`
|
|
206
|
+
: `Declared databases: ${declaredNames.join(', ')}`,
|
|
207
|
+
)
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// The database resolved by a no-name db()/pipe(): the sole database, or the
|
|
211
|
+
// explicitly-configured defaultDatabase. Undefined only when multiple are
|
|
212
|
+
// declared with no default — genuine ambiguity, which both no-name resolvers
|
|
213
|
+
// surface as a ConfigError.
|
|
214
|
+
const resolveDefaultName = (): string | undefined => {
|
|
215
|
+
if (declaredNames.length === 1) return declaredNames[0]!
|
|
216
|
+
if (defaultDbName !== undefined) return defaultDbName
|
|
217
|
+
return undefined
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const doors: Record<string, ResolvedDoor> = {}
|
|
221
|
+
for (const [doorName, door] of Object.entries(config.doors ?? {})) {
|
|
222
|
+
// The schema refused a door with no resolvable database.
|
|
223
|
+
doors[doorName] = Object.freeze({ name: doorName, table: door.table, database: door.database ?? resolveDefaultName()!, reason: door.reason })
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const frameworkDoors: Record<string, ResolvedDoor> = {}
|
|
227
|
+
const defaultName = resolveDefaultName()
|
|
228
|
+
if (config.storage !== undefined && defaultName !== undefined && dbMap.get(defaultName)?.rls?.platformRole !== undefined) {
|
|
229
|
+
frameworkDoors['pipework.storage_sweep'] = Object.freeze({
|
|
230
|
+
name: 'pipework.storage_sweep', table: 'storage_file', database: defaultName,
|
|
231
|
+
reason: 'the storage sweep expires files and finds objects no file references, across tenants',
|
|
232
|
+
framework: true as const, ops: ['select', 'delete'] as const,
|
|
233
|
+
})
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const release = resolveRelease(config.release, environment)
|
|
237
|
+
|
|
238
|
+
const env = Object.freeze(resolveEnvVars((config.env ?? {}) as import('./env-types.js').EnvDefs, environment))
|
|
239
|
+
|
|
240
|
+
return {
|
|
241
|
+
raw: config,
|
|
242
|
+
environment,
|
|
243
|
+
release,
|
|
244
|
+
observability: {
|
|
245
|
+
slowQueryMs: config.observability?.slowQueryMs ?? 500,
|
|
246
|
+
inFlightQueryMs: config.observability?.inFlightQueryMs,
|
|
247
|
+
logAllQueries: config.observability?.logAllQueries ?? false,
|
|
248
|
+
poolMetricsInterval: config.observability?.poolMetricsInterval ?? 0,
|
|
249
|
+
onStatement: config.observability?.onStatement,
|
|
250
|
+
},
|
|
251
|
+
trace: {
|
|
252
|
+
hotRetentionDays: config.trace?.hotRetentionDays ?? 7,
|
|
253
|
+
errorRetentionDays: config.trace?.errorRetentionDays ?? 30,
|
|
254
|
+
partitionLeadDays: config.trace?.partitionLeadDays ?? 2,
|
|
255
|
+
safetyFactor: config.trace?.safetyFactor ?? 4,
|
|
256
|
+
structuralMax: config.trace?.structuralMax,
|
|
257
|
+
captureInputs: config.trace?.captureInputs ?? true,
|
|
258
|
+
maxExternalSteps: config.trace?.maxExternalSteps ?? 1000,
|
|
259
|
+
io: Object.freeze({ ...(config.trace?.io ?? {}) }),
|
|
260
|
+
auditChunkSize: config.trace?.auditChunkSize ?? 5000,
|
|
261
|
+
},
|
|
262
|
+
audit: {
|
|
263
|
+
retentionDays: config.audit?.retentionDays ?? 365,
|
|
264
|
+
},
|
|
265
|
+
env,
|
|
266
|
+
doors: Object.freeze(doors),
|
|
267
|
+
frameworkDoors: Object.freeze(frameworkDoors),
|
|
268
|
+
|
|
269
|
+
database(name?: string): ResolvedDatabase {
|
|
270
|
+
if (name === undefined) {
|
|
271
|
+
const resolved = resolveDefaultName()
|
|
272
|
+
if (resolved !== undefined) {
|
|
273
|
+
return dbMap.get(resolved)!
|
|
274
|
+
}
|
|
275
|
+
throw new ConfigError(
|
|
276
|
+
'Multiple databases configured — you must specify which one.',
|
|
277
|
+
`Declared databases: ${declaredNames.join(', ')}\n\n Use db("${declaredNames[0]}") to specify which database, or set defaultDatabase in pipework.config.ts.`,
|
|
278
|
+
)
|
|
279
|
+
}
|
|
280
|
+
const db = dbMap.get(name)
|
|
281
|
+
if (!db) {
|
|
282
|
+
const suggestion = closestMatch(name, declaredNames)
|
|
283
|
+
const hint = suggestion !== null
|
|
284
|
+
? `Did you mean "${suggestion}"? Declared databases: ${declaredNames.join(', ')}`
|
|
285
|
+
: `Declared databases: ${declaredNames.join(', ')}`
|
|
286
|
+
throw new ConfigError(`Unknown database "${name}".`, hint)
|
|
287
|
+
}
|
|
288
|
+
return db
|
|
289
|
+
},
|
|
290
|
+
|
|
291
|
+
databases(): ReadonlyMap<string, ResolvedDatabase> {
|
|
292
|
+
return dbMap
|
|
293
|
+
},
|
|
294
|
+
|
|
295
|
+
databaseNames(): readonly string[] {
|
|
296
|
+
return declaredNames
|
|
297
|
+
},
|
|
298
|
+
|
|
299
|
+
defaultDatabaseName(): string {
|
|
300
|
+
const resolved = resolveDefaultName()
|
|
301
|
+
if (resolved !== undefined) {
|
|
302
|
+
return resolved
|
|
303
|
+
}
|
|
304
|
+
throw new ConfigError(
|
|
305
|
+
'Multiple databases configured — you must specify which one.',
|
|
306
|
+
`Declared databases: ${declaredNames.join(', ')}\n\n Set defaultDatabase in pipework.config.ts to pick one.`,
|
|
307
|
+
)
|
|
308
|
+
},
|
|
309
|
+
|
|
310
|
+
isolationStrategy(dbName: string): IsolationStrategy {
|
|
311
|
+
return isolationStrategyFor(dbName)
|
|
312
|
+
},
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
function resolveDatabase(
|
|
317
|
+
name: string,
|
|
318
|
+
config: DatabaseConfig,
|
|
319
|
+
url: string | undefined,
|
|
320
|
+
appUrl: string | undefined,
|
|
321
|
+
systemUrl: string | undefined,
|
|
322
|
+
testOwner: { resolved: string | undefined } | undefined,
|
|
323
|
+
baseDir: string,
|
|
324
|
+
refuseUrls: () => never,
|
|
325
|
+
): ResolvedDatabase {
|
|
326
|
+
const resolved = {
|
|
327
|
+
name,
|
|
328
|
+
baseDir,
|
|
329
|
+
schema: config.schema,
|
|
330
|
+
// Absolutized here — the single place — so every consumer (generate,
|
|
331
|
+
// migrate safety gate, test template provisioning) is cwd-independent.
|
|
332
|
+
// resolve(base, abs) === abs, so already-absolute consumer paths stay valid.
|
|
333
|
+
migrations: config.migrations !== undefined ? resolve(baseDir, config.migrations) : undefined,
|
|
334
|
+
extensions: config.extensions ?? [],
|
|
335
|
+
migrationTable: config.migrationTable ?? `__pipework_migrations_${name}`,
|
|
336
|
+
pool: {
|
|
337
|
+
max: config.pool?.max ?? 10,
|
|
338
|
+
idleTimeout: config.pool?.idleTimeout ?? 30,
|
|
339
|
+
...(config.pool?.publishReservation === true ? { publishReservation: true } : {}),
|
|
340
|
+
...(config.pool?.settings !== undefined ? { settings: stringifySettings(config.pool.settings) } : {}),
|
|
341
|
+
},
|
|
342
|
+
...(config.rls !== undefined
|
|
343
|
+
? { rls: { sessionVar: config.rls.sessionVar, force: config.rls.force, ...(config.rls.platformRole !== undefined ? { platformRole: config.rls.platformRole } : {}) } }
|
|
344
|
+
: {}),
|
|
345
|
+
}
|
|
346
|
+
// Lazy URL properties, attached with defineProperty rather than object
|
|
347
|
+
// literal getters: a getter in a spread source is evaluated BY the spread,
|
|
348
|
+
// which would turn the deferred refusal back into a load-time one. A URL
|
|
349
|
+
// that failed to resolve throws the aggregate refusal on first access, so
|
|
350
|
+
// config metadata (names, migrations, pool sizing) stays usable by code
|
|
351
|
+
// paths that never dial a connection.
|
|
352
|
+
Object.defineProperty(resolved, 'url', {
|
|
353
|
+
enumerable: true,
|
|
354
|
+
get: () => url ?? refuseUrls(),
|
|
355
|
+
})
|
|
356
|
+
if (config.appUrl !== undefined) {
|
|
357
|
+
Object.defineProperty(resolved, 'appUrl', {
|
|
358
|
+
enumerable: true,
|
|
359
|
+
get: () => appUrl ?? refuseUrls(),
|
|
360
|
+
})
|
|
361
|
+
}
|
|
362
|
+
if (config.systemUrl !== undefined) {
|
|
363
|
+
Object.defineProperty(resolved, 'systemUrl', {
|
|
364
|
+
enumerable: true,
|
|
365
|
+
get: () => systemUrl ?? refuseUrls(),
|
|
366
|
+
})
|
|
367
|
+
}
|
|
368
|
+
// Only in test mode does this property exist at all: outside it the field is
|
|
369
|
+
// configured but meaningless, and an absent property is what every consumer
|
|
370
|
+
// already reads as "migrate as the test login".
|
|
371
|
+
if (testOwner !== undefined) {
|
|
372
|
+
Object.defineProperty(resolved, 'testOwnerUrl', {
|
|
373
|
+
enumerable: true,
|
|
374
|
+
get: () => testOwner.resolved ?? refuseUrls(),
|
|
375
|
+
})
|
|
376
|
+
}
|
|
377
|
+
return resolved as unknown as ResolvedDatabase
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
function closestMatch(input: string, candidates: string[]): string | null {
|
|
381
|
+
if (candidates.length === 0) return null
|
|
382
|
+
|
|
383
|
+
let best: string | null = null
|
|
384
|
+
let bestDistance = Infinity
|
|
385
|
+
|
|
386
|
+
for (const candidate of candidates) {
|
|
387
|
+
const d = levenshtein(input.toLowerCase(), candidate.toLowerCase())
|
|
388
|
+
if (d < bestDistance && d <= Math.max(input.length, candidate.length) * 0.6) {
|
|
389
|
+
bestDistance = d
|
|
390
|
+
best = candidate
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
return best
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
function levenshtein(a: string, b: string): number {
|
|
398
|
+
const m = a.length
|
|
399
|
+
const n = b.length
|
|
400
|
+
const dp: number[][] = Array.from({ length: m + 1 }, () => Array.from({ length: n + 1 }, () => 0))
|
|
401
|
+
|
|
402
|
+
for (let i = 0; i <= m; i++) dp[i]![0] = i
|
|
403
|
+
for (let j = 0; j <= n; j++) dp[0]![j] = j
|
|
404
|
+
|
|
405
|
+
for (let i = 1; i <= m; i++) {
|
|
406
|
+
for (let j = 1; j <= n; j++) {
|
|
407
|
+
const cost = a[i - 1] === b[j - 1] ? 0 : 1
|
|
408
|
+
dp[i]![j] = Math.min(
|
|
409
|
+
dp[i - 1]![j]! + 1,
|
|
410
|
+
dp[i]![j - 1]! + 1,
|
|
411
|
+
dp[i - 1]![j - 1]! + cost,
|
|
412
|
+
)
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
return dp[m]![n]!
|
|
417
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { loadConfig, trunkConfigSchema, ConfigError } from './index.js'
|
|
2
|
+
import { discoverInstance } from './discover.js'
|
|
3
|
+
|
|
4
|
+
/** Configuration namespace — loads and resolves Blueprint configs, discovers pipework.config.ts. */
|
|
5
|
+
export const config = {
|
|
6
|
+
/** Parses and resolves a raw Blueprint into a ResolvedConfig with environment detection. */
|
|
7
|
+
load: loadConfig,
|
|
8
|
+
/** Walks up from cwd to find pipework.config.ts, imports it, and returns the Manifold. */
|
|
9
|
+
discover: discoverInstance,
|
|
10
|
+
/** The zod schema for Blueprint validation — useful for custom config tooling. */
|
|
11
|
+
configSchema: trunkConfigSchema,
|
|
12
|
+
/** Thrown for configuration problems — includes a suggestion for how to fix. */
|
|
13
|
+
ConfigError,
|
|
14
|
+
}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import type { Environment, IsolationStrategy } from './schema.js'
|
|
2
|
+
import { ConfigError } from './errors.js'
|
|
3
|
+
import { appPoolCreated } from './app-pool.js'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Passed when resolving the app-role pair (`appUrl`/`appTestUrl`), which carries
|
|
7
|
+
* the transaction-isolation carve-out on value-identity. Absent when resolving
|
|
8
|
+
* the owner pair (`url`/`testUrl`), which has no carve-out: that connection is
|
|
9
|
+
* dialed under every strategy.
|
|
10
|
+
*/
|
|
11
|
+
export interface AppRolePair {
|
|
12
|
+
/** This database's resolved test-isolation strategy. */
|
|
13
|
+
readonly strategy: IsolationStrategy
|
|
14
|
+
/** Which connection's fields the pair names in refusals; default `app`. */
|
|
15
|
+
readonly role?: 'app' | 'system'
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const ENVIRONMENTS: readonly string[] = ['test', 'development', 'production']
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Strict resolution for Blueprint `requireEnvironment`: PIPEWORK_ENV, then
|
|
22
|
+
* NODE_ENV, each of which must name a known environment. Unset or unknown
|
|
23
|
+
* is an error rather than development.
|
|
24
|
+
*/
|
|
25
|
+
export function resolveEnvironmentStrict(): Environment {
|
|
26
|
+
const hint = 'Set PIPEWORK_ENV or NODE_ENV to development, test or production.'
|
|
27
|
+
const explicit = process.env['PIPEWORK_ENV']
|
|
28
|
+
if (explicit !== undefined && explicit !== '') {
|
|
29
|
+
if (ENVIRONMENTS.includes(explicit)) return explicit as Environment
|
|
30
|
+
throw new ConfigError(
|
|
31
|
+
`PIPEWORK_ENV is ${JSON.stringify(explicit)}, which is not an environment, and requireEnvironment is set.`,
|
|
32
|
+
hint,
|
|
33
|
+
)
|
|
34
|
+
}
|
|
35
|
+
const nodeEnv = process.env['NODE_ENV']
|
|
36
|
+
if (nodeEnv !== undefined && nodeEnv !== '') {
|
|
37
|
+
if (ENVIRONMENTS.includes(nodeEnv)) return nodeEnv as Environment
|
|
38
|
+
throw new ConfigError(
|
|
39
|
+
`NODE_ENV is ${JSON.stringify(nodeEnv)}, which is not an environment, and requireEnvironment is set.`,
|
|
40
|
+
hint,
|
|
41
|
+
)
|
|
42
|
+
}
|
|
43
|
+
throw new ConfigError(
|
|
44
|
+
'Neither PIPEWORK_ENV nor NODE_ENV is set, and requireEnvironment is set.',
|
|
45
|
+
hint,
|
|
46
|
+
)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function resolveEnvironment(): Environment {
|
|
50
|
+
const explicit = process.env['PIPEWORK_ENV']
|
|
51
|
+
if (explicit === 'test' || explicit === 'development' || explicit === 'production') {
|
|
52
|
+
return explicit
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const nodeEnv = process.env['NODE_ENV']
|
|
56
|
+
if (nodeEnv === 'test') return 'test'
|
|
57
|
+
if (nodeEnv === 'production') return 'production'
|
|
58
|
+
|
|
59
|
+
return 'development'
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function resolveDbUrl(
|
|
63
|
+
envVarName: string,
|
|
64
|
+
testEnvVarName: string | undefined,
|
|
65
|
+
environment: Environment,
|
|
66
|
+
dbName?: string,
|
|
67
|
+
appRolePair?: AppRolePair,
|
|
68
|
+
): string {
|
|
69
|
+
if (environment === 'test') {
|
|
70
|
+
// The pair being resolved, so every refusal below names the fields the
|
|
71
|
+
// reader actually has to edit rather than always saying url/testUrl.
|
|
72
|
+
const urlField = appRolePair === undefined ? 'url' : appRolePair.role === 'system' ? 'systemUrl' : 'appUrl'
|
|
73
|
+
const testUrlField = appRolePair === undefined ? 'testUrl' : appRolePair.role === 'system' ? 'systemTestUrl' : 'appTestUrl'
|
|
74
|
+
|
|
75
|
+
if (testEnvVarName === undefined) {
|
|
76
|
+
throw new ConfigError(
|
|
77
|
+
`Database "${dbName ?? envVarName}" has no ${testUrlField} configured, but environment is "test".`,
|
|
78
|
+
`Add ${testUrlField} to your database config:\n\n` +
|
|
79
|
+
` databases: {\n` +
|
|
80
|
+
` ${dbName ?? 'app'}: {\n` +
|
|
81
|
+
` ${urlField}: '${envVarName}',\n` +
|
|
82
|
+
` ${testUrlField}: '${envVarName}_TEST', // <-- add this\n` +
|
|
83
|
+
` },\n` +
|
|
84
|
+
` }\n\n` +
|
|
85
|
+
`Then set ${envVarName}_TEST in your environment.`,
|
|
86
|
+
)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// D4a (subseam audit 2026-07-16): a testUrl that resolves to the real
|
|
90
|
+
// database means "tests" run against production — the harness
|
|
91
|
+
// creates/drops databases and rolls back transactions on what it assumes
|
|
92
|
+
// is a dedicated instance, and RLS-inertness goes unnoticed. Name-identity
|
|
93
|
+
// first: both fields reading one variable is a config-shape defect,
|
|
94
|
+
// regardless of what the variable holds.
|
|
95
|
+
if (testEnvVarName === envVarName) {
|
|
96
|
+
throw new ConfigError(
|
|
97
|
+
`Database "${dbName ?? envVarName}" uses the same environment variable "${envVarName}" for ${urlField} and ${testUrlField} — test isolation is defeated.`,
|
|
98
|
+
`${testUrlField} must be its own variable pointing at a separate database:\n\n` +
|
|
99
|
+
` databases: {\n` +
|
|
100
|
+
` ${dbName ?? 'app'}: {\n` +
|
|
101
|
+
` ${urlField}: '${envVarName}',\n` +
|
|
102
|
+
` ${testUrlField}: '${envVarName}_TEST', // <-- its own variable\n` +
|
|
103
|
+
` },\n` +
|
|
104
|
+
` }\n\n` +
|
|
105
|
+
`Then set ${envVarName}_TEST to a separate database's connection string.`,
|
|
106
|
+
)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const value = process.env[testEnvVarName]
|
|
110
|
+
if (value !== undefined && value !== '') {
|
|
111
|
+
const urlValue = process.env[envVarName]
|
|
112
|
+
// 0.17.1: value-identity on the app-role pair is a false positive under a
|
|
113
|
+
// strategy that never creates the app-role pool — the value satisfies
|
|
114
|
+
// config resolution and is then never dialed by it. The refusal stands
|
|
115
|
+
// wherever the pool IS created, and for the owner pair in every mode.
|
|
116
|
+
// Name-identity above is NOT carved out: one env var serving both fields
|
|
117
|
+
// carries no intent and cannot be told from a typo.
|
|
118
|
+
const inert = appRolePair !== undefined && !appPoolCreated(appRolePair.strategy)
|
|
119
|
+
if (!inert && urlValue !== undefined && urlValue !== '' && urlValue === value) {
|
|
120
|
+
throw new ConfigError(
|
|
121
|
+
`Database "${dbName ?? envVarName}": "${testEnvVarName}" and "${envVarName}" hold the identical connection string — test isolation is defeated.`,
|
|
122
|
+
`Tests would run against the same database "${envVarName}" points at. ` +
|
|
123
|
+
`Point ${testEnvVarName} at a separate database.` +
|
|
124
|
+
(appRolePair !== undefined
|
|
125
|
+
? `\n\nIdentical app-role values are accepted only under "transaction" isolation, ` +
|
|
126
|
+
`where the harness overrides every role onto one rolled-back connection and the ` +
|
|
127
|
+
`app-role pool is never created. This database resolves to "${appRolePair.strategy}" ` +
|
|
128
|
+
`isolation, which creates it — so ${testEnvVarName} is genuinely dialed here.`
|
|
129
|
+
: ''),
|
|
130
|
+
)
|
|
131
|
+
}
|
|
132
|
+
return value
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
throw new ConfigError(
|
|
136
|
+
`Missing test database URL: environment variable "${testEnvVarName}" is not set.`,
|
|
137
|
+
`Set ${testEnvVarName} in .env.test (or .env.test.local for a machine-local value) or in your environment.`,
|
|
138
|
+
)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const value = process.env[envVarName]
|
|
142
|
+
if (value !== undefined && value !== '') return value
|
|
143
|
+
|
|
144
|
+
throw new ConfigError(
|
|
145
|
+
`Missing database URL: environment variable "${envVarName}" is not set.`,
|
|
146
|
+
`Set ${envVarName} in your .env file or environment.`,
|
|
147
|
+
)
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Resolve `testOwnerUrl`: the connection whose ROLE migrates the test template.
|
|
153
|
+
*
|
|
154
|
+
* Outside test mode there is no such thing — production migrates as `url`'s
|
|
155
|
+
* role already — so this is only ever called with `environment === 'test'`.
|
|
156
|
+
*
|
|
157
|
+
* No identity refusal against `url` here, and deliberately so: the field names
|
|
158
|
+
* the variable holding the owner's credentials, and a venue that exports one
|
|
159
|
+
* `DATABASE_URL` per role set legitimately points this at the same variable
|
|
160
|
+
* name `url` uses. What that variable holds in test mode is the test cluster's
|
|
161
|
+
* owner. The isolation refusals stay where they belong, on `url`/`testUrl`,
|
|
162
|
+
* which is the connection tests actually run on.
|
|
163
|
+
*/
|
|
164
|
+
export function resolveTestOwnerUrl(
|
|
165
|
+
envVarName: string,
|
|
166
|
+
testUrl: string | undefined,
|
|
167
|
+
dbName: string,
|
|
168
|
+
): string {
|
|
169
|
+
const value = process.env[envVarName]
|
|
170
|
+
if (value === undefined || value === '') {
|
|
171
|
+
throw new ConfigError(
|
|
172
|
+
`Database "${dbName}": testOwnerUrl names "${envVarName}", which is not set.`,
|
|
173
|
+
`Set ${envVarName} to a connection string for the role that should migrate the test ` +
|
|
174
|
+
`template, or remove testOwnerUrl to migrate as the test login.`,
|
|
175
|
+
)
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// The template is CREATEd on testUrl's server and then migrated on this one.
|
|
179
|
+
// Two different servers means the migrations land in a database nobody
|
|
180
|
+
// clones, and the template gets cloned empty — a silent, bewildering failure
|
|
181
|
+
// that is cheap to refuse here.
|
|
182
|
+
if (testUrl !== undefined) {
|
|
183
|
+
const owner = new URL(value)
|
|
184
|
+
const test = new URL(testUrl)
|
|
185
|
+
if (owner.host !== test.host) {
|
|
186
|
+
throw new ConfigError(
|
|
187
|
+
`Database "${dbName}": testOwnerUrl ("${envVarName}") points at ${owner.host}, but the test database is on ${test.host}.`,
|
|
188
|
+
`The template is created on the test server and migrated as the owner, so both must ` +
|
|
189
|
+
`be the same server. Point ${envVarName} at ${test.host}.`,
|
|
190
|
+
)
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return value
|
|
195
|
+
}
|