pipework 0.252.0 → 0.253.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -0
- package/dist/infra/support/handle.d.ts +8 -1
- package/dist/infra/support/handle.d.ts.map +1 -1
- package/dist/infra/support/handle.js +9 -2
- package/dist/infra/support/handle.js.map +1 -1
- package/dist/infra/support/namespace.d.ts.map +1 -1
- 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 +869 -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,333 @@
|
|
|
1
|
+
import { beforeAll, afterAll, beforeEach, afterEach } from 'vitest'
|
|
2
|
+
import type { DB } from '../data/db/index.js'
|
|
3
|
+
import type { Blueprint } from '../core/config/index.js'
|
|
4
|
+
import type { Manifold } from '../core/pipework.js'
|
|
5
|
+
import type { SurfaceContext, TestClient } from '../core/surface/types.js'
|
|
6
|
+
import { createManifold } from '../core/pipework.js'
|
|
7
|
+
import { SETUP_HOOK_TIMEOUT_MS } from './progress.js'
|
|
8
|
+
import { setupTestDatabases, teardownTestDatabases, getTestDatabase } from './setup.js'
|
|
9
|
+
import { settleTraceWritesBounded, TEARDOWN_HOOK_TIMEOUT_MS } from '../trace/runtime/pending-writes.js'
|
|
10
|
+
import { beginTestIsolation, endTestIsolation, createTestContext, createSetupConnection, propagateTestSessionVars, runInContext, type TestContext } from './plugin.js'
|
|
11
|
+
import { describeError } from '../core/logging/error-chain.js'
|
|
12
|
+
|
|
13
|
+
export { type TestContext, type TestDatabaseConnections } from './plugin.js'
|
|
14
|
+
|
|
15
|
+
let _testContext: TestContext | null = null
|
|
16
|
+
let _instance: Manifold | null = null
|
|
17
|
+
|
|
18
|
+
const _setupConnections = new Map<string, { drizzle: DB; close: () => Promise<void> }>()
|
|
19
|
+
|
|
20
|
+
export function _setTestState(instance: Manifold, testContext?: TestContext | null): void {
|
|
21
|
+
_instance = instance
|
|
22
|
+
if (testContext !== undefined) {
|
|
23
|
+
_testContext = testContext
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function setupPipeworkTests(configOrInstance: Blueprint | Manifold): Manifold {
|
|
28
|
+
const instance = isInstance(configOrInstance) ? configOrInstance : createManifold(configOrInstance)
|
|
29
|
+
_instance = instance
|
|
30
|
+
|
|
31
|
+
beforeAll(async () => {
|
|
32
|
+
await setupTestDatabases(instance)
|
|
33
|
+
}, SETUP_HOOK_TIMEOUT_MS)
|
|
34
|
+
|
|
35
|
+
afterAll(async () => {
|
|
36
|
+
await teardownAfterAll(instance)
|
|
37
|
+
}, TEARDOWN_HOOK_TIMEOUT_MS)
|
|
38
|
+
|
|
39
|
+
beforeEach(async ({ task }) => {
|
|
40
|
+
_testContext = await beginTestIsolation(instance, task)
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
afterEach(async () => {
|
|
44
|
+
if (_testContext) {
|
|
45
|
+
await endTestIsolation(instance, _testContext)
|
|
46
|
+
_testContext = null
|
|
47
|
+
}
|
|
48
|
+
}, TEARDOWN_HOOK_TIMEOUT_MS)
|
|
49
|
+
|
|
50
|
+
return instance
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function useTestDb(name?: string): DB {
|
|
54
|
+
const resolvedName = name
|
|
55
|
+
|
|
56
|
+
function resolve(): DB {
|
|
57
|
+
if (!_testContext || !_instance) {
|
|
58
|
+
throw new Error(
|
|
59
|
+
'[pipework] No test context available.\n\n' +
|
|
60
|
+
' useTestDb() only works inside test bodies (it, test) and beforeEach/afterEach.\n' +
|
|
61
|
+
' For beforeAll/afterAll fixture setup, use useSetupDb() instead.\n'
|
|
62
|
+
)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const dbName = resolvedName ?? _instance.config.defaultDatabaseName()
|
|
66
|
+
const db = _testContext.db[dbName]
|
|
67
|
+
if (!db) {
|
|
68
|
+
const available = Object.keys(_testContext.db).join(', ')
|
|
69
|
+
throw new Error(
|
|
70
|
+
`[pipework] No test database "${dbName}". Available: ${available}`,
|
|
71
|
+
)
|
|
72
|
+
}
|
|
73
|
+
return db
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return new Proxy({} as DB, {
|
|
77
|
+
get(_target, prop, receiver) {
|
|
78
|
+
const db = resolve()
|
|
79
|
+
const value: unknown = Reflect.get(db, prop, receiver)
|
|
80
|
+
return typeof value === 'function' ? (value as Function).bind(db) : value
|
|
81
|
+
},
|
|
82
|
+
has(_target, prop) {
|
|
83
|
+
return prop in resolve()
|
|
84
|
+
},
|
|
85
|
+
})
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function useSetupDb(name?: string): DB {
|
|
89
|
+
const resolvedName = name
|
|
90
|
+
|
|
91
|
+
function resolve(): DB {
|
|
92
|
+
if (!_instance) {
|
|
93
|
+
throw new Error(
|
|
94
|
+
'[pipework] No test instance available.\n\n' +
|
|
95
|
+
' Add to your vitest.config.ts:\n' +
|
|
96
|
+
' test: { setupFiles: [\'pipework/test/setup\'] }\n'
|
|
97
|
+
)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const dbName = resolvedName ?? _instance.config.defaultDatabaseName()
|
|
101
|
+
|
|
102
|
+
const existing = _setupConnections.get(dbName)
|
|
103
|
+
if (existing) return existing.drizzle
|
|
104
|
+
|
|
105
|
+
const testDb = getTestDatabase(dbName)
|
|
106
|
+
if (!testDb) {
|
|
107
|
+
throw new Error(
|
|
108
|
+
`[pipework] Test database "${dbName}" is not set up yet.\n\n` +
|
|
109
|
+
' useSetupDb() requires setupTestDatabases() to have completed.\n' +
|
|
110
|
+
' Make sure your beforeAll that seeds data runs after the pipework setup.\n'
|
|
111
|
+
)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const conn = createSetupConnection(testDb)
|
|
115
|
+
_setupConnections.set(dbName, conn)
|
|
116
|
+
return conn.drizzle
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return new Proxy({} as DB, {
|
|
120
|
+
get(_target, prop, receiver) {
|
|
121
|
+
const db = resolve()
|
|
122
|
+
const value: unknown = Reflect.get(db, prop, receiver)
|
|
123
|
+
return typeof value === 'function' ? (value as Function).bind(db) : value
|
|
124
|
+
},
|
|
125
|
+
has(_target, prop) {
|
|
126
|
+
return prop in resolve()
|
|
127
|
+
},
|
|
128
|
+
})
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Register a setup connection directly. Internal: the only caller outside this module
|
|
133
|
+
* is the test that proves a close failure is named rather than swallowed (#395), which
|
|
134
|
+
* needs a connection whose `close` rejects.
|
|
135
|
+
*/
|
|
136
|
+
export function _registerSetupConnection(database: string, conn: { drizzle: DB; close: () => Promise<void> }): void {
|
|
137
|
+
_setupConnections.set(database, conn)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Close every connection `useSetupDb()` has opened, and forget them.
|
|
142
|
+
*
|
|
143
|
+
* The harness closes these in `setupPipeworkTests`' own `afterAll`. Under
|
|
144
|
+
* vitest's `sequence.hooks: 'stack'` that hook runs *after* a consumer's
|
|
145
|
+
* setupFile `afterAll`, so a teardown-time observer — one sampling
|
|
146
|
+
* `pg_stat_activity` to name leaked backends, say — sees every setup
|
|
147
|
+
* connection still open and cannot tell them from a real leak. Call this
|
|
148
|
+
* first, then sample: what remains is a true survivor set.
|
|
149
|
+
*
|
|
150
|
+
* Idempotent, and safe to call before the harness's own teardown — that
|
|
151
|
+
* teardown still closes the pool and drops the databases afterwards. A later
|
|
152
|
+
* `useSetupDb()` opens a fresh connection.
|
|
153
|
+
*/
|
|
154
|
+
export async function closeSetupConnections(): Promise<void> {
|
|
155
|
+
const failures: Array<{ database: string; cause: unknown }> = []
|
|
156
|
+
|
|
157
|
+
for (const [database, conn] of _setupConnections) {
|
|
158
|
+
try {
|
|
159
|
+
await conn.close()
|
|
160
|
+
} catch (cause: unknown) {
|
|
161
|
+
failures.push({ database, cause })
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
// Cleared either way: a connection that would not close must not be handed to a
|
|
165
|
+
// later useSetupDb(). What must not happen is clearing it *quietly* — the backend
|
|
166
|
+
// stays held, and the only later evidence is a foreign pid in pg_stat_activity or
|
|
167
|
+
// a setup hook that times out, both of which read as a test or box fault (#395).
|
|
168
|
+
_setupConnections.clear()
|
|
169
|
+
|
|
170
|
+
if (failures.length > 0) throw new SetupConnectionCloseError(failures)
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* A setup connection refused to close.
|
|
175
|
+
*
|
|
176
|
+
* Names every database whose connection would not close and the underlying error,
|
|
177
|
+
* because the backend is still held: the alternative is a leak with no record, found
|
|
178
|
+
* later as an unexplained pid or a hook that never finishes.
|
|
179
|
+
*/
|
|
180
|
+
export class SetupConnectionCloseError extends Error {
|
|
181
|
+
readonly failures: ReadonlyArray<{ database: string; cause: unknown }>
|
|
182
|
+
|
|
183
|
+
constructor(failures: ReadonlyArray<{ database: string; cause: unknown }>) {
|
|
184
|
+
const named = failures.map(f => ` ${f.database}: ${messageOf(f.cause)}`).join('\n')
|
|
185
|
+
super(
|
|
186
|
+
`[pipework] ${String(failures.length)} setup connection${failures.length === 1 ? '' : 's'} would not close.\n\n` +
|
|
187
|
+
`${named}\n\n` +
|
|
188
|
+
' Each backend above is still held by this test process. It is dropped from\n' +
|
|
189
|
+
' pipework\'s registry regardless, so a later useSetupDb() opens a fresh one —\n' +
|
|
190
|
+
' but the old backend holds its locks until the process exits.\n',
|
|
191
|
+
)
|
|
192
|
+
this.name = 'SetupConnectionCloseError'
|
|
193
|
+
this.failures = failures
|
|
194
|
+
if (failures.length === 1) this.cause = failures[0]!.cause
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* The harness's own `afterAll`: close setup connections, the pool, the databases.
|
|
200
|
+
*
|
|
201
|
+
* A setup connection that will not close must not cost the pool and the databases
|
|
202
|
+
* their teardown — that would trade one leak for many. Every step runs, and the close
|
|
203
|
+
* failure is raised at the end so the file fails by name (#395).
|
|
204
|
+
*/
|
|
205
|
+
export async function teardownAfterAll(instance: Manifold): Promise<void> {
|
|
206
|
+
await settleTraceWritesBounded()
|
|
207
|
+
let closeFailure: unknown
|
|
208
|
+
try {
|
|
209
|
+
await closeSetupConnections()
|
|
210
|
+
} catch (err: unknown) {
|
|
211
|
+
closeFailure = err
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
await instance.pool.closeAll()
|
|
215
|
+
await teardownTestDatabases()
|
|
216
|
+
|
|
217
|
+
if (closeFailure !== undefined) throw closeFailure
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function messageOf(cause: unknown): string {
|
|
221
|
+
return describeError(cause).message
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
export function _isInTestIsolation(): boolean {
|
|
225
|
+
return _testContext !== null
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* The manifold the test harness set up: one connection per database with an
|
|
230
|
+
* open transaction, bound into the context for every database name. Nothing a
|
|
231
|
+
* handler writes under it is ever committed, so a test on this instance cannot
|
|
232
|
+
* observe commit boundaries in either direction — transaction-per-request on or
|
|
233
|
+
* off reads the same. A request, a job, `pipe.transaction()`, a `'manual'`
|
|
234
|
+
* checkpoint or any other framework transaction is a savepoint of the test's
|
|
235
|
+
* transaction (isolation-level options are ignored there): the transaction id
|
|
236
|
+
* stays the same, and tenant and other transaction-local settings, and the role,
|
|
237
|
+
* are back to their earlier values once it ends. For commit boundaries, build the venue from `discoverInstance()` and
|
|
238
|
+
* `createServer()` on the package entry instead.
|
|
239
|
+
*/
|
|
240
|
+
export function useTestInstance(): Manifold {
|
|
241
|
+
if (!_instance) {
|
|
242
|
+
throw new Error(
|
|
243
|
+
'[pipework] No test instance available.\n\n' +
|
|
244
|
+
' Add to your vitest.config.ts:\n' +
|
|
245
|
+
' test: { setupFiles: [\'pipework/test/setup\'] }\n'
|
|
246
|
+
)
|
|
247
|
+
}
|
|
248
|
+
return _instance
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
export interface UseTestClientOptions {
|
|
252
|
+
/** App setup (services, sinks) that runs before the client is made. */
|
|
253
|
+
before?: () => void | Promise<void>
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* A test client made in this file's `beforeAll` and closed in its `afterAll`
|
|
258
|
+
* only if it was made, so a failed setup reports only its own error. Replaces
|
|
259
|
+
* the `let client; beforeAll(...); afterAll(() => client.close())` pattern.
|
|
260
|
+
*
|
|
261
|
+
* const client = useTestClient(api, useTestInstance, {
|
|
262
|
+
* before: async () => { await startSinks() },
|
|
263
|
+
* })
|
|
264
|
+
* it('answers', async () => { expect((await client.inject({ url: '/x' })).statusCode).toBe(200) })
|
|
265
|
+
*
|
|
266
|
+
* `inject` throws a `[pipework]` error if the client was not made or is closed.
|
|
267
|
+
*/
|
|
268
|
+
export function useTestClient(
|
|
269
|
+
surface: { test(context: SurfaceContext): Promise<TestClient> },
|
|
270
|
+
context: () => SurfaceContext = useTestInstance,
|
|
271
|
+
options: UseTestClientOptions = {},
|
|
272
|
+
): TestClient {
|
|
273
|
+
let made: TestClient | undefined
|
|
274
|
+
let closed = false
|
|
275
|
+
|
|
276
|
+
beforeAll(async () => {
|
|
277
|
+
await options.before?.()
|
|
278
|
+
made = await surface.test(context())
|
|
279
|
+
})
|
|
280
|
+
|
|
281
|
+
const close = async (): Promise<void> => {
|
|
282
|
+
if (!made || closed) return
|
|
283
|
+
closed = true
|
|
284
|
+
await made.close()
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
afterAll(close)
|
|
288
|
+
|
|
289
|
+
return {
|
|
290
|
+
async inject(opts) {
|
|
291
|
+
if (closed) {
|
|
292
|
+
throw new Error('[pipework] The test client is closed: inject() ran after close() or afterAll.')
|
|
293
|
+
}
|
|
294
|
+
if (!made) {
|
|
295
|
+
throw new Error('[pipework] The test client was not made: setup failed or the call ran before beforeAll. See the setup error above.')
|
|
296
|
+
}
|
|
297
|
+
return await made.inject(opts)
|
|
298
|
+
},
|
|
299
|
+
close,
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
export interface WithFlowOptions {
|
|
304
|
+
auth?: unknown
|
|
305
|
+
tenant?: string | null
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
export function withFlow<T>(fn: () => T): Promise<Awaited<T>>
|
|
309
|
+
export function withFlow<T>(options: WithFlowOptions, fn: () => T): Promise<Awaited<T>>
|
|
310
|
+
export async function withFlow<T>(fnOrOptions: (() => T) | WithFlowOptions, maybeFn?: () => T): Promise<Awaited<T>> {
|
|
311
|
+
if (!_testContext || !_instance) {
|
|
312
|
+
throw new Error(
|
|
313
|
+
'[pipework] No test context available.\n\n' +
|
|
314
|
+
' Add to your vitest.config.ts:\n' +
|
|
315
|
+
' test: { setupFiles: [\'pipework/test/setup\'] }\n'
|
|
316
|
+
)
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
const options = typeof fnOrOptions === 'function' ? {} : fnOrOptions
|
|
320
|
+
const fn = typeof fnOrOptions === 'function' ? fnOrOptions : maybeFn!
|
|
321
|
+
|
|
322
|
+
const ctx = createTestContext(_instance, _testContext, options.auth, options.tenant)
|
|
323
|
+
|
|
324
|
+
if (options.tenant != null) {
|
|
325
|
+
await propagateTestSessionVars(_instance, _testContext, options.tenant)
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
return await runInContext(ctx, fn) as Awaited<T>
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
function isInstance(v: Blueprint | Manifold): v is Manifold {
|
|
332
|
+
return 'pool' in v && 'config' in v
|
|
333
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises'
|
|
2
|
+
import postgres from 'postgres'
|
|
3
|
+
import { getAdminUrl } from './reap.js'
|
|
4
|
+
|
|
5
|
+
/** Prefix for the one stderr line written when a test is 2/3 of the way to its timeout. */
|
|
6
|
+
export const TEST_WATCH_LOG_PREFIX = 'pipework:test-watch'
|
|
7
|
+
|
|
8
|
+
const QUERY_MAX_CHARS = 200
|
|
9
|
+
const WATCH_FRACTION = 2 / 3
|
|
10
|
+
const WATCH_QUERY_TIMEOUT_MS = 5_000
|
|
11
|
+
|
|
12
|
+
export interface TestWatchTarget {
|
|
13
|
+
/** The test's full name, suites included. */
|
|
14
|
+
name: string
|
|
15
|
+
/** The effective timeout of the test in ms; 0 or less disables the watch. */
|
|
16
|
+
timeoutMs: number
|
|
17
|
+
/** Per-test databases whose backends are reported, with a URL to reach their server. */
|
|
18
|
+
databases: readonly { database: string; url: string }[]
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface BackendRow {
|
|
22
|
+
datname: string
|
|
23
|
+
pid: number
|
|
24
|
+
state: string | null
|
|
25
|
+
wait_event_type: string | null
|
|
26
|
+
wait_event: string | null
|
|
27
|
+
state_s: string | null
|
|
28
|
+
blocked_by: string | null
|
|
29
|
+
query: string | null
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Arms a timer at 2/3 of the test's timeout. On fire it samples pg_stat_activity
|
|
34
|
+
* over its own short-lived connection (the test's is busy) plus the host's load
|
|
35
|
+
* and pressure files, and writes one line. Returns the disarm function; a test
|
|
36
|
+
* that ends first prints nothing. Nothing here may fail or delay the test.
|
|
37
|
+
*/
|
|
38
|
+
export function armTestWatch(
|
|
39
|
+
target: TestWatchTarget,
|
|
40
|
+
write: (line: string) => void = line => console.warn(line),
|
|
41
|
+
): () => void {
|
|
42
|
+
if (!(target.timeoutMs > 0)) return () => {}
|
|
43
|
+
const timer = setTimeout(() => {
|
|
44
|
+
void sampleLine(target).then(write, () => {})
|
|
45
|
+
}, Math.floor(target.timeoutMs * WATCH_FRACTION))
|
|
46
|
+
timer.unref()
|
|
47
|
+
return () => clearTimeout(timer)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async function sampleLine(target: TestWatchTarget): Promise<string> {
|
|
51
|
+
const parts: string[] = [`${TEST_WATCH_LOG_PREFIX} test=${JSON.stringify(target.name)} timeout_ms=${String(target.timeoutMs)}`]
|
|
52
|
+
const urls = new Map<string, string>()
|
|
53
|
+
for (const d of target.databases) urls.set(d.database, d.url)
|
|
54
|
+
const byServer = new Map<string, string[]>()
|
|
55
|
+
for (const [database, url] of urls) {
|
|
56
|
+
const admin = getAdminUrl(url)
|
|
57
|
+
byServer.set(admin, [...(byServer.get(admin) ?? []), database])
|
|
58
|
+
}
|
|
59
|
+
for (const [adminUrl, names] of byServer) {
|
|
60
|
+
try {
|
|
61
|
+
parts.push(...(await backends(adminUrl, names)))
|
|
62
|
+
} catch (error) {
|
|
63
|
+
parts.push(`watch-error=${oneLine(error instanceof Error ? error.message : String(error), 120)}`)
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
for (const [label, path] of [['loadavg', '/proc/loadavg'], ['pressure_io', '/proc/pressure/io'], ['pressure_cpu', '/proc/pressure/cpu']] as const) {
|
|
67
|
+
try {
|
|
68
|
+
parts.push(`${label}=${JSON.stringify(oneLine((await readFile(path, 'utf8')).trim(), 200))}`)
|
|
69
|
+
} catch {
|
|
70
|
+
// absent (non-Linux, or no PSI): skip silently
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return parts.join(' ')
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async function backends(adminUrl: string, databases: string[]): Promise<string[]> {
|
|
77
|
+
const client = postgres(adminUrl, { max: 1, onnotice: () => {}, connect_timeout: 5 })
|
|
78
|
+
try {
|
|
79
|
+
const rows = await Promise.race([
|
|
80
|
+
client<BackendRow[]>`
|
|
81
|
+
SELECT datname, pid, state, wait_event_type, wait_event,
|
|
82
|
+
round(extract(epoch FROM (now() - state_change))::numeric, 1)::text AS state_s,
|
|
83
|
+
pg_blocking_pids(pid)::text AS blocked_by, query
|
|
84
|
+
FROM pg_stat_activity
|
|
85
|
+
WHERE datname = ANY(${databases}) AND pid <> pg_backend_pid()
|
|
86
|
+
ORDER BY datname, pid`,
|
|
87
|
+
new Promise<never>((_, reject) => {
|
|
88
|
+
setTimeout(() => reject(new Error('pg_stat_activity query timed out')), WATCH_QUERY_TIMEOUT_MS).unref()
|
|
89
|
+
}),
|
|
90
|
+
])
|
|
91
|
+
if (rows.length === 0) return [`backends(${databases.join(',')})=none`]
|
|
92
|
+
return rows.map(r =>
|
|
93
|
+
`backend[db=${r.datname} pid=${String(r.pid)} state=${r.state ?? '-'} ` +
|
|
94
|
+
`wait=${r.wait_event_type ?? '-'}/${r.wait_event ?? '-'} in_state_s=${r.state_s ?? '-'} ` +
|
|
95
|
+
`blocked_by=${r.blocked_by ?? '{}'} query=${JSON.stringify(oneLine(r.query ?? '', QUERY_MAX_CHARS))}]`,
|
|
96
|
+
)
|
|
97
|
+
} finally {
|
|
98
|
+
void client.end({ timeout: 1 }).catch(() => {})
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function oneLine(text: string, max: number): string {
|
|
103
|
+
const flat = text.replace(/\s+/g, ' ')
|
|
104
|
+
return flat.length > max ? `${flat.slice(0, max)}...` : flat
|
|
105
|
+
}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
// The bounded serializer is the last safety net on trace input capture: it
|
|
2
|
+
// turns an arbitrary captured value into something JSON-safe, size-bounded, and
|
|
3
|
+
// redacted before it lands in `trace_step.captured_inputs`.
|
|
4
|
+
//
|
|
5
|
+
// "Safe" means it never throws and never blows up the trace row: depth, fan-out
|
|
6
|
+
// and string length are all capped, cycles are broken, and non-JSON primitives
|
|
7
|
+
// (bigint, NaN, Date, functions) are rendered to stable markers or strings.
|
|
8
|
+
// "Redacted" means it runs every key-path through the same redact matcher the
|
|
9
|
+
// logger uses, so a path censored in logs is censored in traces too.
|
|
10
|
+
|
|
11
|
+
import { compileRedactPaths, type RedactMatcher } from '../../core/logging/redact-match.js'
|
|
12
|
+
import { DEFAULT_REDACT_PATHS } from '../../core/logging/index.js'
|
|
13
|
+
|
|
14
|
+
export interface TraceSerializeOptions {
|
|
15
|
+
/** Nested objects/arrays deeper than this are replaced with a marker. */
|
|
16
|
+
readonly maxDepth?: number
|
|
17
|
+
/** Object keys / array items kept per level; the excess is summarized. */
|
|
18
|
+
readonly maxEntries?: number
|
|
19
|
+
/** Strings longer than this are truncated with a marker. */
|
|
20
|
+
readonly maxStringLength?: number
|
|
21
|
+
/** Key-path matcher; matching values are replaced with `[Redacted]`. */
|
|
22
|
+
readonly redact?: RedactMatcher
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
interface ResolvedOptions {
|
|
26
|
+
readonly maxDepth: number
|
|
27
|
+
readonly maxEntries: number
|
|
28
|
+
readonly maxStringLength: number
|
|
29
|
+
readonly redact: RedactMatcher
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const DEFAULTS = {
|
|
33
|
+
maxDepth: 6,
|
|
34
|
+
maxEntries: 64,
|
|
35
|
+
maxStringLength: 1024,
|
|
36
|
+
redact: compileRedactPaths(DEFAULT_REDACT_PATHS),
|
|
37
|
+
} as const
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Serialize a captured value into a JSON-safe, bounded, redacted form.
|
|
41
|
+
*
|
|
42
|
+
* The result contains only `null`, booleans, finite numbers, strings, plain
|
|
43
|
+
* objects and arrays — safe to write straight into a `jsonb` column. Anything
|
|
44
|
+
* that would break those guarantees is replaced with a `[…]` marker rather than
|
|
45
|
+
* dropped silently, so a reader can tell capture hit a bound.
|
|
46
|
+
*/
|
|
47
|
+
export function serializeForTrace(value: unknown, options: TraceSerializeOptions = {}): unknown {
|
|
48
|
+
const resolved: ResolvedOptions = {
|
|
49
|
+
maxDepth: options.maxDepth ?? DEFAULTS.maxDepth,
|
|
50
|
+
maxEntries: options.maxEntries ?? DEFAULTS.maxEntries,
|
|
51
|
+
maxStringLength: options.maxStringLength ?? DEFAULTS.maxStringLength,
|
|
52
|
+
redact: options.redact ?? DEFAULTS.redact,
|
|
53
|
+
}
|
|
54
|
+
return walk(value, 0, [], new WeakSet<object>(), resolved)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function walk(
|
|
58
|
+
value: unknown,
|
|
59
|
+
depth: number,
|
|
60
|
+
keyPath: readonly string[],
|
|
61
|
+
seen: WeakSet<object>,
|
|
62
|
+
opts: ResolvedOptions,
|
|
63
|
+
): unknown {
|
|
64
|
+
if (value === null) return null
|
|
65
|
+
|
|
66
|
+
switch (typeof value) {
|
|
67
|
+
case 'string':
|
|
68
|
+
return boundString(value, opts.maxStringLength)
|
|
69
|
+
case 'number':
|
|
70
|
+
return Number.isFinite(value) ? value : String(value)
|
|
71
|
+
case 'boolean':
|
|
72
|
+
return value
|
|
73
|
+
case 'bigint':
|
|
74
|
+
return value.toString()
|
|
75
|
+
case 'undefined':
|
|
76
|
+
return undefined
|
|
77
|
+
case 'function':
|
|
78
|
+
return '[Function]'
|
|
79
|
+
case 'symbol':
|
|
80
|
+
return value.toString()
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (value instanceof Date) return value.toISOString()
|
|
84
|
+
|
|
85
|
+
if (depth >= opts.maxDepth) return '[Pruned: max depth]'
|
|
86
|
+
if (seen.has(value)) return '[Circular]'
|
|
87
|
+
seen.add(value)
|
|
88
|
+
const result = Array.isArray(value)
|
|
89
|
+
? walkArray(value, depth, keyPath, seen, opts)
|
|
90
|
+
: walkObject(value as Record<string, unknown>, depth, keyPath, seen, opts)
|
|
91
|
+
seen.delete(value)
|
|
92
|
+
return result
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function walkArray(
|
|
96
|
+
value: readonly unknown[],
|
|
97
|
+
depth: number,
|
|
98
|
+
keyPath: readonly string[],
|
|
99
|
+
seen: WeakSet<object>,
|
|
100
|
+
opts: ResolvedOptions,
|
|
101
|
+
): unknown[] {
|
|
102
|
+
const out: unknown[] = []
|
|
103
|
+
for (const item of value.slice(0, opts.maxEntries)) {
|
|
104
|
+
// Arrays are transparent to the redact key-path: an item carries its
|
|
105
|
+
// parent's path, not an index segment. This makes redaction match by
|
|
106
|
+
// object-key nesting and, where it differs from pino, errs toward
|
|
107
|
+
// redacting more — the safe direction for a capture net.
|
|
108
|
+
const serialized = walk(item, depth + 1, keyPath, seen, opts)
|
|
109
|
+
// JSON arrays cannot carry holes; an undefined slot becomes null.
|
|
110
|
+
out.push(serialized === undefined ? null : serialized)
|
|
111
|
+
}
|
|
112
|
+
if (value.length > opts.maxEntries) {
|
|
113
|
+
out.push(`[Pruned: ${value.length - opts.maxEntries} more items]`)
|
|
114
|
+
}
|
|
115
|
+
return out
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function walkObject(
|
|
119
|
+
value: Record<string, unknown>,
|
|
120
|
+
depth: number,
|
|
121
|
+
keyPath: readonly string[],
|
|
122
|
+
seen: WeakSet<object>,
|
|
123
|
+
opts: ResolvedOptions,
|
|
124
|
+
): Record<string, unknown> {
|
|
125
|
+
const out: Record<string, unknown> = {}
|
|
126
|
+
const entries = Object.entries(value)
|
|
127
|
+
for (const [key, raw] of entries.slice(0, opts.maxEntries)) {
|
|
128
|
+
const path = [...keyPath, key]
|
|
129
|
+
if (opts.redact(path)) {
|
|
130
|
+
out[key] = '[Redacted]'
|
|
131
|
+
continue
|
|
132
|
+
}
|
|
133
|
+
const serialized = walk(raw, depth + 1, path, seen, opts)
|
|
134
|
+
// An undefined property is absent in JSON; drop it rather than emit a hole.
|
|
135
|
+
if (serialized !== undefined) out[key] = serialized
|
|
136
|
+
}
|
|
137
|
+
if (entries.length > opts.maxEntries) {
|
|
138
|
+
out['[Pruned]'] = `${entries.length - opts.maxEntries} more keys`
|
|
139
|
+
}
|
|
140
|
+
return out
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function boundString(value: string, max: number): string {
|
|
144
|
+
if (value.length <= max) return value
|
|
145
|
+
return `${value.slice(0, max)}[Truncated: ${value.length - max} chars]`
|
|
146
|
+
}
|