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,185 @@
|
|
|
1
|
+
// Test setup is one awaited call from a vitest `beforeAll`. When forty forks
|
|
2
|
+
// start at once, a healthy setup can spend a minute queued behind its peers —
|
|
3
|
+
// and the consumer's hook timeout fires with nothing to say but the number of
|
|
4
|
+
// milliseconds it waited. So setup narrates: every step it reaches is reported
|
|
5
|
+
// to the caller, and the bound it enforces itself is on PHASE ADVANCE, never on
|
|
6
|
+
// total elapsed time. A queued setup that keeps advancing finishes late and
|
|
7
|
+
// green; a genuinely stuck one fails naming the phase it reached. Issue #380.
|
|
8
|
+
|
|
9
|
+
/** Steps `setupTestDatabases` passes through, in the order it reaches them. */
|
|
10
|
+
export type SetupPhase =
|
|
11
|
+
| 'rls-probe'
|
|
12
|
+
| 'run-prepare'
|
|
13
|
+
| 'run-reap'
|
|
14
|
+
| 'template-pin'
|
|
15
|
+
| 'template-wait'
|
|
16
|
+
| 'template-build'
|
|
17
|
+
| 'template-migrate'
|
|
18
|
+
| 'template-framework'
|
|
19
|
+
| 'template-partitions'
|
|
20
|
+
| 'template-grants'
|
|
21
|
+
| 'template-ready'
|
|
22
|
+
| 'queue-check'
|
|
23
|
+
| 'clone-start'
|
|
24
|
+
| 'clone-done'
|
|
25
|
+
| 'ready'
|
|
26
|
+
|
|
27
|
+
export interface SetupProgress {
|
|
28
|
+
readonly phase: SetupPhase
|
|
29
|
+
/** The configured database this step is about, or null for whole-run steps. */
|
|
30
|
+
readonly database: string | null
|
|
31
|
+
/** Free-form detail — a migration tag, a poll count. Never parsed. */
|
|
32
|
+
readonly detail?: string
|
|
33
|
+
/** Milliseconds since setup started. */
|
|
34
|
+
readonly elapsedMs: number
|
|
35
|
+
/** The phase this one displaced, and how long it held. Null on the first. */
|
|
36
|
+
readonly previous: { readonly phase: SetupPhase, readonly database: string | null, readonly heldMs: number } | null
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export type SetupProgressListener = (progress: SetupProgress) => void
|
|
40
|
+
|
|
41
|
+
/** Setup made no progress for the configured bound. Names the last phase reached. */
|
|
42
|
+
export class SetupStalled extends Error {
|
|
43
|
+
constructor(
|
|
44
|
+
readonly phase: SetupPhase,
|
|
45
|
+
readonly database: string | null,
|
|
46
|
+
readonly stalledMs: number,
|
|
47
|
+
readonly elapsedMs: number,
|
|
48
|
+
) {
|
|
49
|
+
const where = database === null ? phase : `${phase} (database "${database}")`
|
|
50
|
+
super(
|
|
51
|
+
`[pipework] Test setup failed:\n\n` +
|
|
52
|
+
` No progress for ${(stalledMs / 1000).toFixed(1)}s.\n\n` +
|
|
53
|
+
` last phase reached: ${where}\n` +
|
|
54
|
+
` total elapsed: ${(elapsedMs / 1000).toFixed(1)}s\n\n` +
|
|
55
|
+
` This bound is on phase advance, not on total time — setup queued behind\n` +
|
|
56
|
+
` other processes keeps advancing and does not trip it. Something at the\n` +
|
|
57
|
+
` phase above is stuck. Raise the bound with test.setupStallMs in\n` +
|
|
58
|
+
` pipework.config.ts if the step is legitimately this slow.\n`,
|
|
59
|
+
)
|
|
60
|
+
this.name = 'SetupStalled'
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Records the phase setup is in and when it got there. The watchdog reads it;
|
|
66
|
+
* the listener is told about every advance.
|
|
67
|
+
*/
|
|
68
|
+
export class PhaseTracker {
|
|
69
|
+
phase: SetupPhase = 'run-prepare'
|
|
70
|
+
database: string | null = null
|
|
71
|
+
private started = false
|
|
72
|
+
private at: number
|
|
73
|
+
private readonly start: number
|
|
74
|
+
|
|
75
|
+
constructor(
|
|
76
|
+
private readonly listener: SetupProgressListener = () => {},
|
|
77
|
+
private readonly clock: () => number = () => Date.now(),
|
|
78
|
+
) {
|
|
79
|
+
this.start = this.clock()
|
|
80
|
+
this.at = this.start
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** How long the current phase has held. */
|
|
84
|
+
idleMs(): number {
|
|
85
|
+
return this.clock() - this.at
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
elapsedMs(): number {
|
|
89
|
+
return this.clock() - this.start
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
reached(phase: SetupPhase, database: string | null = null, detail?: string): void {
|
|
93
|
+
const now = this.clock()
|
|
94
|
+
const previous = this.started
|
|
95
|
+
? { phase: this.phase, database: this.database, heldMs: now - this.at }
|
|
96
|
+
: null
|
|
97
|
+
this.started = true
|
|
98
|
+
this.phase = phase
|
|
99
|
+
this.database = database
|
|
100
|
+
this.at = now
|
|
101
|
+
this.listener({
|
|
102
|
+
phase,
|
|
103
|
+
database,
|
|
104
|
+
...(detail === undefined ? {} : { detail }),
|
|
105
|
+
elapsedMs: now - this.start,
|
|
106
|
+
previous,
|
|
107
|
+
})
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export const DEFAULT_SETUP_STALL_MS = 120_000
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Timeout for the `beforeAll` that runs setup: setTimeout's ceiling, so vitest's
|
|
115
|
+
* hookTimeout never bounds a setup that is still advancing. Only the stall
|
|
116
|
+
* watchdog fails setup.
|
|
117
|
+
*/
|
|
118
|
+
export const SETUP_HOOK_TIMEOUT_MS = 2 ** 31 - 1
|
|
119
|
+
|
|
120
|
+
export interface StallWatchOptions {
|
|
121
|
+
stallMs?: number
|
|
122
|
+
/** How often the watchdog looks. Defaults to a tenth of the bound. */
|
|
123
|
+
pollMs?: number
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Run `work`, failing it if `tracker` reports no phase advance for `stallMs`.
|
|
128
|
+
*
|
|
129
|
+
* The watchdog only ever REJECTS earlier than `work` would settle — it cannot
|
|
130
|
+
* make a completed setup fail, and its timer is unref'd so it never holds the
|
|
131
|
+
* process open.
|
|
132
|
+
*/
|
|
133
|
+
export async function withStallWatchdog<T>(
|
|
134
|
+
work: () => Promise<T>,
|
|
135
|
+
tracker: PhaseTracker,
|
|
136
|
+
options: StallWatchOptions = {},
|
|
137
|
+
): Promise<T> {
|
|
138
|
+
const stallMs = options.stallMs ?? DEFAULT_SETUP_STALL_MS
|
|
139
|
+
const pollMs = options.pollMs ?? Math.max(50, Math.floor(stallMs / 10))
|
|
140
|
+
|
|
141
|
+
let timer: ReturnType<typeof setInterval> | undefined
|
|
142
|
+
const stalled = new Promise<never>((_resolve, reject) => {
|
|
143
|
+
timer = setInterval(() => {
|
|
144
|
+
const idle = tracker.idleMs()
|
|
145
|
+
if (idle >= stallMs) {
|
|
146
|
+
reject(new SetupStalled(tracker.phase, tracker.database, idle, tracker.elapsedMs()))
|
|
147
|
+
}
|
|
148
|
+
}, pollMs)
|
|
149
|
+
timer.unref()
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
try {
|
|
153
|
+
return await Promise.race([work(), stalled])
|
|
154
|
+
} finally {
|
|
155
|
+
if (timer !== undefined) clearInterval(timer)
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/** Prefix for the stderr lines `auto-setup` writes about slow phases. */
|
|
160
|
+
export const SETUP_PROGRESS_LOG_PREFIX = 'pipework:test-setup'
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* A phase that held this long is worth a line. Below it setup is silent: a
|
|
164
|
+
* healthy run must not spray forty lines per test file.
|
|
165
|
+
*/
|
|
166
|
+
export const SLOW_PHASE_MS = 10_000
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* The listener `auto-setup` installs. Silent while setup is quick, one
|
|
170
|
+
* key=value line per phase that took long enough to explain a late hook.
|
|
171
|
+
*/
|
|
172
|
+
export function logSlowPhases(
|
|
173
|
+
write: (line: string) => void = message => console.warn(message),
|
|
174
|
+
slowMs: number = SLOW_PHASE_MS,
|
|
175
|
+
): SetupProgressListener {
|
|
176
|
+
return progress => {
|
|
177
|
+
const previous = progress.previous
|
|
178
|
+
if (previous === null || previous.heldMs < slowMs) return
|
|
179
|
+
const db = previous.database === null ? '' : ` db=${previous.database}`
|
|
180
|
+
write(
|
|
181
|
+
`${SETUP_PROGRESS_LOG_PREFIX} slow phase=${previous.phase}${db} ` +
|
|
182
|
+
`held_ms=${previous.heldMs} next=${progress.phase} elapsed_ms=${progress.elapsedMs}`,
|
|
183
|
+
)
|
|
184
|
+
}
|
|
185
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { loadEnvFiles } from '../core/config/env.js'
|
|
2
|
+
import { discoverInstance } from '../core/config/discover.js'
|
|
3
|
+
import { reapConfiguredTestDatabases } from './reap.js'
|
|
4
|
+
import { TEST_REAP_LOG_PREFIX } from './setup.js'
|
|
5
|
+
|
|
6
|
+
// Vitest globalSetup, added by the pipework plugin. `teardown` runs in the
|
|
7
|
+
// parent after every worker has stopped, so the clones a SIGTERMed fork left
|
|
8
|
+
// queued are orphans by then: their run locks died with the workers. Clones
|
|
9
|
+
// only (templates outlive runs), and only those whose run lock is free, so a
|
|
10
|
+
// concurrent run's live clones are never touched. Best effort, never fails a run.
|
|
11
|
+
export function setup(): void {}
|
|
12
|
+
|
|
13
|
+
export async function teardown(): Promise<void> {
|
|
14
|
+
try {
|
|
15
|
+
loadEnvFiles()
|
|
16
|
+
const instance = await discoverInstance()
|
|
17
|
+
try {
|
|
18
|
+
const { droppedDatabases } = await reapConfiguredTestDatabases(instance, { clonesOnly: true })
|
|
19
|
+
if (droppedDatabases.length > 0) {
|
|
20
|
+
process.stderr.write(`${TEST_REAP_LOG_PREFIX} dropped ${String(droppedDatabases.length)} leftover clone(s): ${droppedDatabases.join(', ')}\n`)
|
|
21
|
+
}
|
|
22
|
+
} finally {
|
|
23
|
+
await instance.pool.closeAll()
|
|
24
|
+
}
|
|
25
|
+
} catch {
|
|
26
|
+
// Best effort — the next run's reaper drops what this missed.
|
|
27
|
+
}
|
|
28
|
+
}
|
package/src/test/reap.ts
ADDED
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto'
|
|
2
|
+
import postgres from 'postgres'
|
|
3
|
+
import type { Manifold } from '../core/pipework.js'
|
|
4
|
+
import { quoteIdentifier } from '../data/db/identifiers.js'
|
|
5
|
+
import {
|
|
6
|
+
testDbPrefix,
|
|
7
|
+
appRolePrefix,
|
|
8
|
+
testDbLikePattern,
|
|
9
|
+
templateDbPrefix,
|
|
10
|
+
templateDbLikePattern,
|
|
11
|
+
} from './namespace.js'
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
// The run nonce every name `testDbName` writes ends with: 12 random hex chars.
|
|
15
|
+
// Carrying one is the reaper's evidence that it created the database, and the nonce
|
|
16
|
+
// rebuilds the owning run's advisory-lock key. Deliberately not the fuller
|
|
17
|
+
// `_<seq>_<pid>_<nonce>` tail: an orphan left by an older pipework whose names had
|
|
18
|
+
// no clone sequence would then never be reaped by any later version.
|
|
19
|
+
//
|
|
20
|
+
// The residual limit, stated rather than hidden: a foreign database inside this
|
|
21
|
+
// project's prefix whose name happens to end in exactly 12 hex characters is
|
|
22
|
+
// indistinguishable from a clone and is still eligible. Nothing in the name can
|
|
23
|
+
// separate them; `PIPEWORK_TEST_NAMESPACE` is the way out of a shared prefix.
|
|
24
|
+
const CLONE_SUFFIX = /_([0-9a-f]{12})$/
|
|
25
|
+
|
|
26
|
+
export interface ReapOptions {
|
|
27
|
+
/**
|
|
28
|
+
* Called before each candidate database is handled, `index` counting 1..`total`
|
|
29
|
+
* across the clones and the stale templates together, so a hung drop is named.
|
|
30
|
+
*/
|
|
31
|
+
onReap?: (datname: string, index: number, total: number) => void
|
|
32
|
+
/** Leave shared templates alone: only per-run clones are candidates. */
|
|
33
|
+
clonesOnly?: boolean
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface ReapResult {
|
|
37
|
+
readonly droppedDatabases: readonly string[]
|
|
38
|
+
/**
|
|
39
|
+
* Databases inside this project's prefix that the reaper did NOT create and so
|
|
40
|
+
* will not drop. Reported rather than dropped: sharing a prefix is not evidence
|
|
41
|
+
* of ownership, and a name pipework never wrote belongs to somebody else (#391).
|
|
42
|
+
*/
|
|
43
|
+
readonly skippedDatabases: readonly string[]
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Advisory-lock key identifying a test run. A live run holds this lock on a
|
|
48
|
+
* dedicated connection for its entire lifetime; Postgres releases it
|
|
49
|
+
* automatically when that connection ends by any means (clean exit, crash,
|
|
50
|
+
* SIGKILL, reboot). The reaper reads "lock is free" as "owning run is dead".
|
|
51
|
+
*/
|
|
52
|
+
export function runLockKey(nonce: string): bigint {
|
|
53
|
+
return BigInt('0x' + nonce)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Advisory-lock keys for a shared template, derived from its content
|
|
58
|
+
* fingerprint. Both are salted hashes truncated to 60 bits, which keeps them
|
|
59
|
+
* inside a signed `bigint` and makes an accidental collision with a 48-bit run
|
|
60
|
+
* lock key or with each other a non-event.
|
|
61
|
+
*
|
|
62
|
+
* Two keys, because the two things being coordinated are different:
|
|
63
|
+
*
|
|
64
|
+
* - the USE key is taken in SHARE mode by every process that intends to clone
|
|
65
|
+
* the template, for that process's whole life, and in EXCLUSIVE mode by the
|
|
66
|
+
* reaper. That is what stops a reaper from dropping a template between
|
|
67
|
+
* another process's readiness check and its `CREATE DATABASE ... WITH
|
|
68
|
+
* TEMPLATE`: the reaper simply cannot get the lock while anyone holds it.
|
|
69
|
+
* - the BUILD key is taken in EXCLUSIVE mode only while building, so that N
|
|
70
|
+
* processes starting at once produce one template and one migration replay
|
|
71
|
+
* rather than N. It is separate because advisory locks do not upgrade from
|
|
72
|
+
* share to exclusive, and a builder must keep its own pin while it builds.
|
|
73
|
+
*
|
|
74
|
+
* Issue #340.
|
|
75
|
+
*/
|
|
76
|
+
function templateLockKey(salt: string, fingerprint: string): bigint {
|
|
77
|
+
return BigInt('0x' + createHash('sha256').update(`${salt}:${fingerprint}`).digest('hex').slice(0, 15))
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function templateUseKey(fingerprint: string): bigint {
|
|
81
|
+
return templateLockKey('pipework-template-use', fingerprint)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function templateBuildKey(fingerprint: string): bigint {
|
|
85
|
+
return templateLockKey('pipework-template-build', fingerprint)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** Admin (`/postgres`) connection URL for a database URL. */
|
|
89
|
+
export function getAdminUrl(databaseUrl: string): string {
|
|
90
|
+
const url = new URL(databaseUrl)
|
|
91
|
+
url.pathname = '/postgres'
|
|
92
|
+
return url.toString()
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function pairedRoleName(datname: string): string {
|
|
96
|
+
return appRolePrefix() + datname.slice(testDbPrefix().length)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** A shared template's app role is content-keyed the same way the template is. */
|
|
100
|
+
function pairedTemplateRoleName(datname: string): string {
|
|
101
|
+
return appRolePrefix() + datname.slice(templateDbPrefix().length)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** Content fingerprint trailing a shared template's name: 16 lowercase hex. */
|
|
105
|
+
const TEMPLATE_FINGERPRINT_SUFFIX = /_([0-9a-f]{16})$/
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Drop test databases left behind by runs that never reached teardown
|
|
109
|
+
* (Ctrl-C, timeout, crash). A database is reaped only when ALL THREE hold:
|
|
110
|
+
*
|
|
111
|
+
* - the reaper created it — its name carries the 12-hex run nonce every name
|
|
112
|
+
* `testDbName` writes. Sharing the project prefix is not evidence of
|
|
113
|
+
* ownership: a cluster carrying `<project>_test_<slot>` control databases
|
|
114
|
+
* lost all of them at once to a prefix-only sweep (#391). Anything else in
|
|
115
|
+
* the prefix is reported in `skippedDatabases` and left alone;
|
|
116
|
+
* - its run's advisory lock is free — the owning run's process is gone; and
|
|
117
|
+
* - `numbackends = 0` — no connections remain, so reaping cannot pull a
|
|
118
|
+
* database out from under workers orphaned by a SIGKILL'd parent.
|
|
119
|
+
*
|
|
120
|
+
* All three only ever make the reaper more conservative, so it can never drop a
|
|
121
|
+
* database belonging to a live or still-provisioning run, or to anyone else.
|
|
122
|
+
*/
|
|
123
|
+
export async function reapOrphanTestDatabases(
|
|
124
|
+
admin: postgres.Sql,
|
|
125
|
+
liveTemplates: ReadonlySet<string> = new Set(),
|
|
126
|
+
options: ReapOptions = {},
|
|
127
|
+
): Promise<ReapResult> {
|
|
128
|
+
const rows = await admin<{ datname: string, numbackends: number }[]>`
|
|
129
|
+
SELECT datname, numbackends
|
|
130
|
+
FROM pg_stat_database
|
|
131
|
+
WHERE datname LIKE ${testDbLikePattern()}
|
|
132
|
+
`
|
|
133
|
+
const templateRows = options.clonesOnly === true ? [] : await staleTemplateCandidates(admin)
|
|
134
|
+
const total = rows.length + templateRows.length
|
|
135
|
+
const { onReap } = options
|
|
136
|
+
const droppedDatabases: string[] = []
|
|
137
|
+
const skippedDatabases: string[] = []
|
|
138
|
+
for (const [i, { datname, numbackends }] of rows.entries()) {
|
|
139
|
+
onReap?.(datname, i + 1, total)
|
|
140
|
+
// A name in this prefix that pipework did not write. It could be anything —
|
|
141
|
+
// a control database, a hand-made fixture, another tool's namespace that
|
|
142
|
+
// happens to collide. Dropping it because the prefix matched is what #391
|
|
143
|
+
// reported; say what was left alone instead.
|
|
144
|
+
const nonce = CLONE_SUFFIX.exec(datname)?.[1]
|
|
145
|
+
if (nonce === undefined) {
|
|
146
|
+
skippedDatabases.push(datname)
|
|
147
|
+
continue
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (numbackends > 0) continue
|
|
151
|
+
|
|
152
|
+
const key = runLockKey(nonce)
|
|
153
|
+
if (!(await tryAcquire(admin, key))) continue
|
|
154
|
+
|
|
155
|
+
try {
|
|
156
|
+
if (await dropDatabase(admin, datname)) {
|
|
157
|
+
droppedDatabases.push(datname)
|
|
158
|
+
await dropRole(admin, pairedRoleName(datname))
|
|
159
|
+
}
|
|
160
|
+
} finally {
|
|
161
|
+
await admin`SELECT pg_advisory_unlock(${key.toString()}::bigint)`
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
const templates = await reapStaleTemplates(admin, liveTemplates, templateRows, (datname, i) => onReap?.(datname, rows.length + i, total))
|
|
165
|
+
droppedDatabases.push(...templates.droppedDatabases)
|
|
166
|
+
skippedDatabases.push(...templates.skippedDatabases)
|
|
167
|
+
return { droppedDatabases, skippedDatabases }
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Drop shared templates nobody wants any more.
|
|
172
|
+
*
|
|
173
|
+
* A template is deliberately long-lived — it survives the run that built it, so
|
|
174
|
+
* the next run clones instead of replaying migrations (#340) — which means
|
|
175
|
+
* "left behind by a dead run" is no longer a reason to drop one. The two
|
|
176
|
+
* reasons that remain:
|
|
177
|
+
*
|
|
178
|
+
* - it is not in `liveTemplates`, the set of content keys the calling run
|
|
179
|
+
* actually intends to use. Editing a migration changes the key, so the
|
|
180
|
+
* template built from the old migrations is named something nobody will ask
|
|
181
|
+
* for again. That is what makes invalidation automatic rather than manual.
|
|
182
|
+
* - nobody holds its use lock, in any mode. Every process that intends to
|
|
183
|
+
* clone a template holds that lock in SHARE mode for its whole life, so
|
|
184
|
+
* failing to take it EXCLUSIVE means no live process is cloning from it —
|
|
185
|
+
* including no process sitting between a readiness check and its `CREATE
|
|
186
|
+
* DATABASE ... WITH TEMPLATE`.
|
|
187
|
+
*
|
|
188
|
+
* `numbackends = 0` is required as well, for the same conservatism as clones.
|
|
189
|
+
*
|
|
190
|
+
* An empty `liveTemplates` — what `pipework test --reap` passes — therefore
|
|
191
|
+
* means "drop every template not currently in use", which is the right reading
|
|
192
|
+
* of an explicit cleanup command.
|
|
193
|
+
*/
|
|
194
|
+
async function reapStaleTemplates(
|
|
195
|
+
admin: postgres.Sql,
|
|
196
|
+
liveTemplates: ReadonlySet<string>,
|
|
197
|
+
rows: readonly { datname: string, numbackends: number }[],
|
|
198
|
+
onEach: (datname: string, index: number) => void,
|
|
199
|
+
): Promise<ReapResult> {
|
|
200
|
+
const droppedDatabases: string[] = []
|
|
201
|
+
const skippedDatabases: string[] = []
|
|
202
|
+
for (const [i, { datname, numbackends }] of rows.entries()) {
|
|
203
|
+
onEach(datname, i + 1)
|
|
204
|
+
const fingerprint = TEMPLATE_FINGERPRINT_SUFFIX.exec(datname)?.[1]
|
|
205
|
+
// A name in the template namespace without a fingerprint was not written by
|
|
206
|
+
// this scheme. Leave it alone rather than guess at its owner — and say so.
|
|
207
|
+
if (fingerprint === undefined) {
|
|
208
|
+
skippedDatabases.push(datname)
|
|
209
|
+
continue
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (numbackends > 0) continue
|
|
213
|
+
if (liveTemplates.has(datname)) continue
|
|
214
|
+
|
|
215
|
+
const key = templateUseKey(fingerprint)
|
|
216
|
+
if (!(await tryAcquire(admin, key))) continue
|
|
217
|
+
|
|
218
|
+
try {
|
|
219
|
+
if (await dropDatabase(admin, datname)) {
|
|
220
|
+
droppedDatabases.push(datname)
|
|
221
|
+
await dropRole(admin, pairedTemplateRoleName(datname))
|
|
222
|
+
}
|
|
223
|
+
} finally {
|
|
224
|
+
await admin`SELECT pg_advisory_unlock(${key.toString()}::bigint)`
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
return { droppedDatabases, skippedDatabases }
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
async function staleTemplateCandidates(admin: postgres.Sql): Promise<{ datname: string, numbackends: number }[]> {
|
|
231
|
+
return [...await admin<{ datname: string, numbackends: number }[]>`
|
|
232
|
+
SELECT datname, numbackends
|
|
233
|
+
FROM pg_stat_database
|
|
234
|
+
WHERE datname LIKE ${templateDbLikePattern()}
|
|
235
|
+
`]
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Reap orphan test databases across every Postgres server referenced by the
|
|
240
|
+
* manifold's database config. Used by `pipework test --reap`.
|
|
241
|
+
*/
|
|
242
|
+
export async function reapConfiguredTestDatabases(instance: Manifold, options: ReapOptions = {}): Promise<ReapResult> {
|
|
243
|
+
const adminUrls = new Set<string>()
|
|
244
|
+
for (const [, dbConfig] of instance.config.databases()) {
|
|
245
|
+
adminUrls.add(getAdminUrl(dbConfig.url))
|
|
246
|
+
}
|
|
247
|
+
const droppedDatabases: string[] = []
|
|
248
|
+
const skippedDatabases: string[] = []
|
|
249
|
+
for (const adminUrl of adminUrls) {
|
|
250
|
+
const client = postgres(adminUrl, { max: 1, onnotice: () => {} })
|
|
251
|
+
try {
|
|
252
|
+
const result = await reapOrphanTestDatabases(client, new Set(), options)
|
|
253
|
+
droppedDatabases.push(...result.droppedDatabases)
|
|
254
|
+
skippedDatabases.push(...result.skippedDatabases)
|
|
255
|
+
} finally {
|
|
256
|
+
await client.end()
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
return { droppedDatabases, skippedDatabases }
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
async function tryAcquire(admin: postgres.Sql, key: bigint): Promise<boolean> {
|
|
263
|
+
const rows = await admin<{ locked: boolean }[]>`
|
|
264
|
+
SELECT pg_try_advisory_lock(${key.toString()}::bigint) AS locked
|
|
265
|
+
`
|
|
266
|
+
return rows[0]?.locked === true
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
async function dropDatabase(admin: postgres.Sql, datname: string): Promise<boolean> {
|
|
270
|
+
try {
|
|
271
|
+
await admin.unsafe(`DROP DATABASE IF EXISTS ${quoteIdentifier(datname, 'database')}`)
|
|
272
|
+
return true
|
|
273
|
+
} catch {
|
|
274
|
+
// A backend connected between the numbackends check and the DROP — leave
|
|
275
|
+
// this database for the next run's reaper.
|
|
276
|
+
return false
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
async function dropRole(admin: postgres.Sql, role: string): Promise<void> {
|
|
281
|
+
try {
|
|
282
|
+
await admin.unsafe(`DROP ROLE IF EXISTS ${quoteIdentifier(role, 'role')}`)
|
|
283
|
+
} catch {
|
|
284
|
+
// Role still owns objects in another database — best effort.
|
|
285
|
+
}
|
|
286
|
+
}
|