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,157 @@
|
|
|
1
|
+
import { createServer, type IncomingHttpHeaders, type IncomingMessage, type ServerResponse } from 'node:http'
|
|
2
|
+
import { connect as netConnect, type AddressInfo, type Socket } from 'node:net'
|
|
3
|
+
import { readFile, stat } from 'node:fs/promises'
|
|
4
|
+
import { extname, join, normalize, sep } from 'node:path'
|
|
5
|
+
import type { FetchTarget } from '../infra/fetch/index.js'
|
|
6
|
+
|
|
7
|
+
const PUBLIC_ADDRESS = '93.184.216.34'
|
|
8
|
+
|
|
9
|
+
const TYPES: Record<string, string> = {
|
|
10
|
+
'.html': 'text/html', '.htm': 'text/html', '.xhtml': 'application/xhtml+xml', '.txt': 'text/plain',
|
|
11
|
+
'.pdf': 'application/pdf', '.xml': 'application/xml', '.css': 'text/css', '.js': 'text/javascript',
|
|
12
|
+
'.json': 'application/json', '.png': 'image/png', '.jpg': 'image/jpeg',
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** What one path of a fixture site does instead of serving a file. */
|
|
16
|
+
export interface CrawlFixtureRoute {
|
|
17
|
+
/** Answer with a redirect to this URL (status 302 unless `status` is given). */
|
|
18
|
+
readonly redirect?: string
|
|
19
|
+
/** Answer with this status and no body (with `redirect`: the redirect's status). */
|
|
20
|
+
readonly status?: number
|
|
21
|
+
/** Wait this long before answering. */
|
|
22
|
+
readonly delayMs?: number
|
|
23
|
+
/** Answer 200 with this many generated bytes, streamed and never on disk, without a Content-Length. */
|
|
24
|
+
readonly bytes?: number
|
|
25
|
+
/** Content type of the generated bytes; default `application/pdf`. */
|
|
26
|
+
readonly contentType?: string
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface CrawlFixtureOptions {
|
|
30
|
+
/** The directory served as every host's site: `/` is `index.html`, `/robots.txt` is the file of that name. Files carry an ETag and Last-Modified, and an `If-None-Match` that matches is answered 304. */
|
|
31
|
+
readonly root: string
|
|
32
|
+
/** The names that resolve, each to a fake public address (or the address given); any other name does not resolve. */
|
|
33
|
+
readonly hosts: readonly string[] | Readonly<Record<string, string>>
|
|
34
|
+
/** Per-path behaviour, keyed by the URL's path (`/big.pdf`), or by host and path (`example.test/`) for one host only; the host form wins. */
|
|
35
|
+
readonly routes?: Readonly<Record<string, CrawlFixtureRoute>>
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** One request the fixture server received. */
|
|
39
|
+
export interface CrawlFixtureRequest {
|
|
40
|
+
readonly method: string
|
|
41
|
+
/** The Host header without the port. */
|
|
42
|
+
readonly host: string
|
|
43
|
+
/** Path and query. */
|
|
44
|
+
readonly path: string
|
|
45
|
+
/** `Date.now()` when the request arrived. */
|
|
46
|
+
readonly at: number
|
|
47
|
+
readonly headers: IncomingHttpHeaders
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface CrawlFixtureSite {
|
|
51
|
+
/** Pass as the `network` option of `crawl.create` (or `untrustedFetch.get`'s `resolve` and `connect`). */
|
|
52
|
+
readonly network: {
|
|
53
|
+
resolve: (hostname: string) => Promise<string[]>
|
|
54
|
+
connect: (target: FetchTarget) => Socket
|
|
55
|
+
}
|
|
56
|
+
/** Every request received, oldest first. */
|
|
57
|
+
readonly requests: CrawlFixtureRequest[]
|
|
58
|
+
close(): Promise<void>
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function safePath(root: string, urlPath: string): string | null {
|
|
62
|
+
let decoded: string
|
|
63
|
+
try { decoded = decodeURIComponent(urlPath) } catch { return null }
|
|
64
|
+
const full = normalize(join(root, decoded))
|
|
65
|
+
const base = normalize(root)
|
|
66
|
+
return full === base || full.startsWith(base.endsWith(sep) ? base : base + sep) ? full : null
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async function serveFile(root: string, pathname: string, req: IncomingMessage, res: ServerResponse): Promise<void> {
|
|
70
|
+
let file = safePath(root, pathname)
|
|
71
|
+
if (file === null) { res.statusCode = 400; res.end(); return }
|
|
72
|
+
try {
|
|
73
|
+
if ((await stat(file)).isDirectory()) file = join(file, 'index.html')
|
|
74
|
+
const info = await stat(file)
|
|
75
|
+
const etag = `"${info.size.toString(16)}-${Math.floor(info.mtimeMs).toString(16)}"`
|
|
76
|
+
res.setHeader('etag', etag)
|
|
77
|
+
res.setHeader('last-modified', info.mtime.toUTCString())
|
|
78
|
+
if (req.headers['if-none-match'] === etag) { res.statusCode = 304; res.end(); return }
|
|
79
|
+
const body = await readFile(file)
|
|
80
|
+
res.setHeader('content-type', TYPES[extname(file).toLowerCase()] ?? 'application/octet-stream')
|
|
81
|
+
res.end(body)
|
|
82
|
+
} catch {
|
|
83
|
+
res.statusCode = 404
|
|
84
|
+
res.setHeader('content-type', 'text/plain')
|
|
85
|
+
res.end('not found')
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function streamBytes(res: ServerResponse, total: number, contentType: string): void {
|
|
90
|
+
res.setHeader('content-type', contentType)
|
|
91
|
+
const chunk = Buffer.alloc(64 * 1024, 0x25)
|
|
92
|
+
let sent = 0
|
|
93
|
+
let closed = false
|
|
94
|
+
res.on('close', () => { closed = true })
|
|
95
|
+
const pump = (): void => {
|
|
96
|
+
while (!closed && sent < total) {
|
|
97
|
+
const piece = sent + chunk.length <= total ? chunk : chunk.subarray(0, total - sent)
|
|
98
|
+
sent += piece.length
|
|
99
|
+
if (!res.write(piece)) { res.once('drain', pump); return }
|
|
100
|
+
}
|
|
101
|
+
if (!closed) res.end()
|
|
102
|
+
}
|
|
103
|
+
pump()
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Serves a directory as a website on a local server, for testing a crawl with no outside fetch. `network` makes the
|
|
108
|
+
* hosts in `hosts` resolve to a fake public address and sends their connections to the local server, so the real
|
|
109
|
+
* address rules still run: a host not in `hosts` (such as `127.0.0.1`) is refused as it would be live. Use plain
|
|
110
|
+
* `http://<host>/` URLs; the port is the fixture's. Every request is recorded in `requests`.
|
|
111
|
+
*/
|
|
112
|
+
export async function crawlFixtureSite(options: CrawlFixtureOptions): Promise<CrawlFixtureSite> {
|
|
113
|
+
const addresses: Record<string, string> = Array.isArray(options.hosts)
|
|
114
|
+
? Object.fromEntries((options.hosts as readonly string[]).map(h => [h, PUBLIC_ADDRESS]))
|
|
115
|
+
: { ...(options.hosts as Record<string, string>) }
|
|
116
|
+
const requests: CrawlFixtureRequest[] = []
|
|
117
|
+
const sockets = new Set<Socket>()
|
|
118
|
+
const server = createServer((req, res) => {
|
|
119
|
+
const url = new URL(req.url ?? '/', 'http://fixture.invalid')
|
|
120
|
+
const host = (req.headers.host ?? '').replace(/:\d+$/, '')
|
|
121
|
+
requests.push({ method: req.method ?? 'GET', host, path: url.pathname + url.search, at: Date.now(), headers: req.headers })
|
|
122
|
+
const route = options.routes?.[`${host}${url.pathname}`] ?? options.routes?.[url.pathname]
|
|
123
|
+
const respond = async (): Promise<void> => {
|
|
124
|
+
if (route?.redirect !== undefined) {
|
|
125
|
+
res.statusCode = route.status ?? 302
|
|
126
|
+
res.setHeader('location', route.redirect)
|
|
127
|
+
res.end()
|
|
128
|
+
} else if (route?.bytes !== undefined) {
|
|
129
|
+
streamBytes(res, route.bytes, route.contentType ?? 'application/pdf')
|
|
130
|
+
} else if (route?.status !== undefined) {
|
|
131
|
+
res.statusCode = route.status
|
|
132
|
+
res.end()
|
|
133
|
+
} else {
|
|
134
|
+
await serveFile(options.root, url.pathname, req, res)
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
if (route?.delayMs !== undefined) setTimeout(() => { void respond() }, route.delayMs)
|
|
138
|
+
else void respond()
|
|
139
|
+
})
|
|
140
|
+
server.on('connection', s => { sockets.add(s); s.on('close', () => sockets.delete(s)) })
|
|
141
|
+
await new Promise<void>(resolve => server.listen(0, '127.0.0.1', resolve))
|
|
142
|
+
const port = (server.address() as AddressInfo).port
|
|
143
|
+
return {
|
|
144
|
+
network: {
|
|
145
|
+
resolve: (hostname) => {
|
|
146
|
+
const found = addresses[hostname]
|
|
147
|
+
return found === undefined ? Promise.reject(new Error('ENOTFOUND')) : Promise.resolve([found])
|
|
148
|
+
},
|
|
149
|
+
connect: () => netConnect(port, '127.0.0.1'),
|
|
150
|
+
},
|
|
151
|
+
requests,
|
|
152
|
+
close: async () => {
|
|
153
|
+
for (const s of sockets) s.destroy()
|
|
154
|
+
await new Promise<void>(resolve => server.close(() => resolve()))
|
|
155
|
+
},
|
|
156
|
+
}
|
|
157
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Manifold } from '../core/pipework.js'
|
|
2
|
+
import { discoverInstance } from '../core/config/discover.js'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Load this test fork's manifold and consume `PIPEWORK_CONFIG`.
|
|
6
|
+
*
|
|
7
|
+
* The override has exactly one reader: this fork's discovery. It is unset once
|
|
8
|
+
* that read succeeds, so nothing a test spawns — another package's `pipework
|
|
9
|
+
* migrate`, a CLI under test — inherits it and migrates the wrong package
|
|
10
|
+
* against the wrong manifold. Discovery failing leaves it set: the variable is
|
|
11
|
+
* part of the diagnosis.
|
|
12
|
+
*/
|
|
13
|
+
export async function discoverForTestFork(): Promise<Manifold> {
|
|
14
|
+
const instance = await discoverInstance()
|
|
15
|
+
delete process.env['PIPEWORK_CONFIG']
|
|
16
|
+
return instance
|
|
17
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto'
|
|
2
|
+
import { readdirSync, readFileSync } from 'node:fs'
|
|
3
|
+
import { join, relative, sep } from 'node:path'
|
|
4
|
+
import type { Blueprint } from '../core/config/schema.js'
|
|
5
|
+
import type { ResolvedDatabase } from '../core/config/index.js'
|
|
6
|
+
import { VERSION } from '../core/runtime/broker.js'
|
|
7
|
+
|
|
8
|
+
// The template is keyed by its CONTENT, not by the run that happened to build
|
|
9
|
+
// it. Every input that changes what ends up inside a template database is
|
|
10
|
+
// folded into one hash, and that hash becomes the template's name — so a
|
|
11
|
+
// migration edit does not invalidate a template, it simply asks for a
|
|
12
|
+
// different one, and the stale one is reaped because nobody asks for it again.
|
|
13
|
+
//
|
|
14
|
+
// Content keying is what lets a template outlive the process that built it:
|
|
15
|
+
// a vitest run with `pool: 'forks'` + `isolate: true` gives every test file a
|
|
16
|
+
// fresh process, and a per-process key (pid, run nonce) meant every one of them
|
|
17
|
+
// replayed the whole migration stack. Issue #340.
|
|
18
|
+
|
|
19
|
+
/** Bump when pipework changes the SHAPE of what it puts in a template. */
|
|
20
|
+
const TEMPLATE_FORMAT_VERSION = 2
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Everything that determines a template's contents. Serialized and hashed;
|
|
24
|
+
* anything omitted here is something two runs may disagree about while still
|
|
25
|
+
* sharing a template, so omissions are the way stale reuse happens.
|
|
26
|
+
*/
|
|
27
|
+
export interface TemplateInputs {
|
|
28
|
+
/** Which configured database this template stands in for. */
|
|
29
|
+
readonly databaseName: string
|
|
30
|
+
/**
|
|
31
|
+
* The role that builds — and therefore owns — the template.
|
|
32
|
+
*
|
|
33
|
+
* `CREATE DATABASE ... WITH TEMPLATE` requires ownership of the template, so
|
|
34
|
+
* a template is only shareable among processes connecting as the same role.
|
|
35
|
+
* Keying on the owner means a second role builds its own rather than failing
|
|
36
|
+
* to clone, or worse, trying to drop a database it does not own.
|
|
37
|
+
*/
|
|
38
|
+
readonly owner: string
|
|
39
|
+
/**
|
|
40
|
+
* The role the template's migrations run as, when `testOwnerUrl` names one.
|
|
41
|
+
*
|
|
42
|
+
* It decides who OWNS every migrated object, which decides what a
|
|
43
|
+
* `SECURITY DEFINER` function made by a migration can read. Omitted entirely
|
|
44
|
+
* when unconfigured, so a template built without the field keeps its name.
|
|
45
|
+
*/
|
|
46
|
+
readonly migrationOwner?: string
|
|
47
|
+
readonly extensions: readonly string[]
|
|
48
|
+
readonly migrationTable: string
|
|
49
|
+
/** Hash of the migrations folder's contents, or `null` when unmigrated. */
|
|
50
|
+
readonly migrations: string | null
|
|
51
|
+
/** Framework-table provisioning depends on these blueprint sections. */
|
|
52
|
+
readonly trace: unknown
|
|
53
|
+
readonly audit: unknown
|
|
54
|
+
/** Framework tables ride the default database only. */
|
|
55
|
+
readonly isDefaultDatabase: boolean
|
|
56
|
+
/** Trace/audit day partitions are provisioned relative to the current day. */
|
|
57
|
+
readonly partitionDay: string
|
|
58
|
+
/** Internal definitions and emitted DDL travel with the pipework version. */
|
|
59
|
+
readonly pipeworkVersion: string
|
|
60
|
+
readonly formatVersion: number
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Hash a migrations folder's contents: every file's path relative to the folder
|
|
65
|
+
* and its bytes, in a stable order. Path names are included so a rename is a
|
|
66
|
+
* change; a missing or unreadable folder hashes as absent.
|
|
67
|
+
*/
|
|
68
|
+
export function hashMigrationsFolder(folder: string): string | null {
|
|
69
|
+
const files: string[] = []
|
|
70
|
+
const walk = (dir: string): void => {
|
|
71
|
+
let entries
|
|
72
|
+
try {
|
|
73
|
+
entries = readdirSync(dir, { withFileTypes: true })
|
|
74
|
+
} catch {
|
|
75
|
+
return
|
|
76
|
+
}
|
|
77
|
+
for (const entry of entries.sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0))) {
|
|
78
|
+
const full = join(dir, entry.name)
|
|
79
|
+
if (entry.isDirectory()) walk(full)
|
|
80
|
+
else if (entry.isFile()) files.push(full)
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
walk(folder)
|
|
84
|
+
if (files.length === 0) return null
|
|
85
|
+
|
|
86
|
+
const hash = createHash('sha256')
|
|
87
|
+
for (const file of files) {
|
|
88
|
+
// Normalize the separator so the same tree hashes alike on any platform.
|
|
89
|
+
hash.update(relative(folder, file).split(sep).join('/'))
|
|
90
|
+
hash.update('\0')
|
|
91
|
+
hash.update(readFileSync(file))
|
|
92
|
+
hash.update('\0')
|
|
93
|
+
}
|
|
94
|
+
return hash.digest('hex')
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Collect the content inputs for one configured database's template.
|
|
99
|
+
*
|
|
100
|
+
* `partitionDay` is the current UTC date. Trace and audit partitions are
|
|
101
|
+
* created per day with a lead window, so a template built yesterday is missing
|
|
102
|
+
* today's partitions — keying on the day makes that invalidation automatic
|
|
103
|
+
* instead of a stale-data bug that only shows up after midnight.
|
|
104
|
+
*/
|
|
105
|
+
export function templateInputs(
|
|
106
|
+
databaseName: string,
|
|
107
|
+
dbConfig: ResolvedDatabase,
|
|
108
|
+
blueprint: Blueprint,
|
|
109
|
+
isDefaultDatabase: boolean,
|
|
110
|
+
today: Date,
|
|
111
|
+
): TemplateInputs {
|
|
112
|
+
return {
|
|
113
|
+
databaseName,
|
|
114
|
+
owner: new URL(dbConfig.url).username,
|
|
115
|
+
...(dbConfig.testOwnerUrl !== undefined ? { migrationOwner: new URL(dbConfig.testOwnerUrl).username } : {}),
|
|
116
|
+
extensions: [...dbConfig.extensions].sort(),
|
|
117
|
+
migrationTable: dbConfig.migrationTable,
|
|
118
|
+
migrations: dbConfig.migrations === undefined ? null : hashMigrationsFolder(dbConfig.migrations),
|
|
119
|
+
trace: blueprint.trace ?? null,
|
|
120
|
+
audit: blueprint.audit ?? null,
|
|
121
|
+
isDefaultDatabase,
|
|
122
|
+
partitionDay: today.toISOString().slice(0, 10),
|
|
123
|
+
pipeworkVersion: VERSION,
|
|
124
|
+
formatVersion: TEMPLATE_FORMAT_VERSION,
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* The template's content fingerprint: 16 lowercase hex chars. Deliberately
|
|
130
|
+
* wider than the 12-char per-run nonce so a template name can never be mistaken
|
|
131
|
+
* for a run-owned database name.
|
|
132
|
+
*/
|
|
133
|
+
export function templateFingerprint(inputs: TemplateInputs): string {
|
|
134
|
+
return createHash('sha256').update(JSON.stringify(inputs)).digest('hex').slice(0, 16)
|
|
135
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export { setupTestDatabases, teardownTestDatabases, getTestDatabase, getTestDatabases, type TestDatabase, type SetupOptions } from './setup.js'
|
|
2
|
+
export { flushPendingDrops, TEST_DROP_LOG_PREFIX, TEST_REAP_LOG_PREFIX } from './setup.js'
|
|
3
|
+
export {
|
|
4
|
+
SetupStalled,
|
|
5
|
+
logSlowPhases,
|
|
6
|
+
SETUP_PROGRESS_LOG_PREFIX,
|
|
7
|
+
type SetupPhase,
|
|
8
|
+
type SetupProgress,
|
|
9
|
+
type SetupProgressListener,
|
|
10
|
+
} from './progress.js'
|
|
11
|
+
export { armTestWatch, TEST_WATCH_LOG_PREFIX, type TestWatchTarget } from './watch.js'
|
|
12
|
+
export { beginTestIsolation, endTestIsolation, type TestContext, type TestDatabaseConnections } from './plugin.js'
|
|
13
|
+
export { setupPipeworkTests, useTestDb, useSetupDb, useTestInstance, useTestClient, type UseTestClientOptions, closeSetupConnections, SetupConnectionCloseError, teardownAfterAll, withFlow, type WithFlowOptions } from './vitest.js'
|
|
14
|
+
export { mockAuth, asTenant } from './auth.js'
|
|
15
|
+
export { withContext, withJobContext, type WithContextOptions } from './context.js'
|
|
16
|
+
export { pipework as vitestPlugin, type PipeworkVitestOptions } from './vitest-plugin.js'
|
|
17
|
+
export { resolveVitestConfig, resolveVitestModules } from './vitest-workspace.js'
|
|
18
|
+
export { tap, type ManagedConnection, type ConnectOptions } from '../data/db/pool.js'
|
|
19
|
+
export { crawlFixtureSite, type CrawlFixtureOptions, type CrawlFixtureRoute, type CrawlFixtureRequest, type CrawlFixtureSite } from './crawl-fixture.js'
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs'
|
|
2
|
+
import { join } from 'node:path'
|
|
3
|
+
|
|
4
|
+
// The clone-per-test database namespace, scoped to ONE project.
|
|
5
|
+
//
|
|
6
|
+
// pipework is consumed by many projects that may share a Postgres cluster. A
|
|
7
|
+
// hardcoded `pipework_test_` / `pipework_app_` prefix made every consumer's
|
|
8
|
+
// per-run test databases AND runtime roles collide in a single namespace: one
|
|
9
|
+
// project's reaper dropped another's databases, clones ended up owned by a
|
|
10
|
+
// foreign role, and `CREATE` on the cloned `public` schema was denied because
|
|
11
|
+
// the connecting role was not the owner. Scoping both prefixes per project
|
|
12
|
+
// isolates them — and isolates the reaper, so it only ever touches its own.
|
|
13
|
+
//
|
|
14
|
+
// Resolution order:
|
|
15
|
+
// 1. PIPEWORK_TEST_NAMESPACE — explicit override (CI, or a shared cluster
|
|
16
|
+
// whose default namespace is already polluted by legacy runs).
|
|
17
|
+
// 2. the consumer's package.json `name` — the natural per-project identifier.
|
|
18
|
+
// 3. `pipework` — fallback when neither is available.
|
|
19
|
+
|
|
20
|
+
let cached: string | null = null
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Pure namespace resolution: pick the first non-empty source, then sanitize to
|
|
24
|
+
* a valid Postgres identifier prefix. Exported for direct testing — the impure
|
|
25
|
+
* {@link projectNamespace} wraps it with the actual env + package.json sources.
|
|
26
|
+
*/
|
|
27
|
+
export function resolveNamespace(envOverride: string | undefined, pkgName: string | undefined): string {
|
|
28
|
+
const raw = envOverride !== undefined && envOverride.trim() !== '' ? envOverride
|
|
29
|
+
: pkgName !== undefined && pkgName.trim() !== '' ? pkgName
|
|
30
|
+
: 'pipework'
|
|
31
|
+
// Postgres identifiers are lowercase, alphanumeric + underscore, and must
|
|
32
|
+
// start with a letter. Collapse every other run of characters to one
|
|
33
|
+
// underscore so `@scope/name` and `scope-name` map to distinct, stable,
|
|
34
|
+
// legal tokens; prefix `p_` if the result would not start with a letter.
|
|
35
|
+
const token = raw.toLowerCase().replace(/[^a-z0-9]+/g, '_').replace(/^_+|_+$/g, '')
|
|
36
|
+
return /^[a-z]/.test(token) ? token : `p_${token}`
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** This project's namespace token, resolved once per process. */
|
|
40
|
+
export function projectNamespace(): string {
|
|
41
|
+
if (cached !== null) return cached
|
|
42
|
+
let pkgName: string | undefined
|
|
43
|
+
try {
|
|
44
|
+
pkgName = (JSON.parse(readFileSync(join(process.cwd(), 'package.json'), 'utf-8')) as { name?: string }).name
|
|
45
|
+
} catch {
|
|
46
|
+
// No readable package.json (e.g. a bare test harness) — fall back below.
|
|
47
|
+
}
|
|
48
|
+
cached = resolveNamespace(process.env['PIPEWORK_TEST_NAMESPACE'], pkgName)
|
|
49
|
+
return cached
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Prefix for this project's per-run test databases: `<project>_test_`. */
|
|
53
|
+
export function testDbPrefix(): string {
|
|
54
|
+
return `${projectNamespace()}_test_`
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Prefix for this project's per-run runtime (app) roles: `<project>_app_`. */
|
|
58
|
+
export function appRolePrefix(): string {
|
|
59
|
+
return `${projectNamespace()}_app_`
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Prefix for this project's shared, content-keyed templates: `<project>_tmpl_`.
|
|
64
|
+
*
|
|
65
|
+
* Templates sit in their own prefix rather than under `testDbPrefix()` because
|
|
66
|
+
* they have a different lifecycle and a different reaping rule: a per-run clone
|
|
67
|
+
* dies with its run, a template deliberately outlives every run that uses it
|
|
68
|
+
* and is dropped only once no run wants it. Separate prefixes keep the reaper's
|
|
69
|
+
* two passes from having to tell the names apart by shape. Issue #340.
|
|
70
|
+
*/
|
|
71
|
+
export function templateDbPrefix(): string {
|
|
72
|
+
return `${projectNamespace()}_tmpl_`
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* `LIKE` pattern matching every per-run test database of THIS project, with the
|
|
77
|
+
* prefix underscores escaped so they match literally. The reaper uses it to
|
|
78
|
+
* scope its sweep to its own namespace and never another project's databases.
|
|
79
|
+
*/
|
|
80
|
+
export function testDbLikePattern(): string {
|
|
81
|
+
return `${testDbPrefix().replace(/_/g, '\\_')}%`
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** `LIKE` pattern matching every shared template of THIS project. */
|
|
85
|
+
export function templateDbLikePattern(): string {
|
|
86
|
+
return `${templateDbPrefix().replace(/_/g, '\\_')}%`
|
|
87
|
+
}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import postgres from 'postgres'
|
|
2
|
+
import { drizzle, type PostgresJsDatabase } from '../data/query/postgres-js/index.js'
|
|
3
|
+
import { sql } from '../data/query/index.js'
|
|
4
|
+
import type { Manifold } from '../core/pipework.js'
|
|
5
|
+
import { getTestDatabase, type TestDatabase } from './setup.js'
|
|
6
|
+
import { createTestContext as createCtx } from '../request/context/create.js'
|
|
7
|
+
import { runInContext } from '../request/context/store.js'
|
|
8
|
+
import type { Flow } from '../request/context/types.js'
|
|
9
|
+
import { randomUUID } from 'node:crypto'
|
|
10
|
+
import { createDebugFn, isObserverOnly } from '../data/db/instrumentation.js'
|
|
11
|
+
import { createStatementWatch } from '../data/db/query-watch.js'
|
|
12
|
+
import { markObservedConnection } from '../data/query/postgres-js/driver-error-boundary.js'
|
|
13
|
+
import type { ResolvedObservability } from '../core/config/load.js'
|
|
14
|
+
import { collapseTransactionsToSavepoints } from './savepoints.js'
|
|
15
|
+
import { splitLargeInserts } from '../data/db/insert-split.js'
|
|
16
|
+
import { settleTraceWritesBounded } from '../trace/runtime/pending-writes.js'
|
|
17
|
+
import { createSystemHandle } from './system-channel.js'
|
|
18
|
+
import { armTestWatch } from './watch.js'
|
|
19
|
+
|
|
20
|
+
export interface TestDatabaseConnections {
|
|
21
|
+
[name: string]: PostgresJsDatabase
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface TestContext {
|
|
25
|
+
id: string
|
|
26
|
+
db: TestDatabaseConnections
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface ActiveTestConnection {
|
|
30
|
+
client: postgres.Sql
|
|
31
|
+
closeWatch?: () => Promise<void>
|
|
32
|
+
drizzle: PostgresJsDatabase
|
|
33
|
+
/** Same connection and transaction, statements sent with the test-only cross-tenant policy open. Set when the database has `rls`. */
|
|
34
|
+
system?: PostgresJsDatabase
|
|
35
|
+
/** A handle for one named door on the same connection; set when the database has `rls`. */
|
|
36
|
+
door?: (name: string) => PostgresJsDatabase
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const contextConnections = new Map<string, Map<string, ActiveTestConnection>>()
|
|
40
|
+
const contextWatches = new Map<string, () => void>()
|
|
41
|
+
|
|
42
|
+
/** The slice of vitest's `task` the watch needs: `fullName` and the resolved `timeout`. */
|
|
43
|
+
export interface WatchedTask {
|
|
44
|
+
fullName: string
|
|
45
|
+
timeout: number
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export async function beginTestIsolation(instance: Manifold, task?: WatchedTask): Promise<TestContext> {
|
|
49
|
+
const dbConnections: TestDatabaseConnections = {}
|
|
50
|
+
const contextId = randomUUID()
|
|
51
|
+
const connections = new Map<string, ActiveTestConnection>()
|
|
52
|
+
|
|
53
|
+
for (const [name] of instance.config.databases()) {
|
|
54
|
+
const testDb = getTestDatabase(name)
|
|
55
|
+
if (!testDb) {
|
|
56
|
+
throw new Error(`[pipework] Test database not set up for "${name}". Did setupTestDatabases() run?`)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const conn = await createIsolatedConnection(
|
|
60
|
+
testDb, connections, name, instance.config.observability, instance.config.database(name).rls !== undefined,
|
|
61
|
+
)
|
|
62
|
+
dbConnections[name] = conn
|
|
63
|
+
|
|
64
|
+
instance.pool.override(name, {
|
|
65
|
+
name,
|
|
66
|
+
client: connections.get(name)!.client,
|
|
67
|
+
drizzle: conn,
|
|
68
|
+
...(connections.get(name)!.system !== undefined ? { system: connections.get(name)!.system! } : {}),
|
|
69
|
+
...(connections.get(name)!.door !== undefined ? { door: connections.get(name)!.door! } : {}),
|
|
70
|
+
connectionIds() {
|
|
71
|
+
return Promise.reject(new Error(
|
|
72
|
+
'[pipework] connectionIds() is not available inside a test isolation context: the test connection is one transaction-pinned connection.\n' +
|
|
73
|
+
' Fix: call connectionIds() on a pool opened with instance.pool.getOrCreate() or tap(), outside test isolation.',
|
|
74
|
+
))
|
|
75
|
+
},
|
|
76
|
+
async close() { /* test harness manages lifecycle */ },
|
|
77
|
+
})
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
contextConnections.set(contextId, connections)
|
|
81
|
+
if (task !== undefined) {
|
|
82
|
+
const databases = [...connections.keys()].flatMap(n => {
|
|
83
|
+
const d = getTestDatabase(n)
|
|
84
|
+
return d ? [{ database: d.database, url: d.url }] : []
|
|
85
|
+
})
|
|
86
|
+
contextWatches.set(contextId, armTestWatch({ name: task.fullName, timeoutMs: task.timeout, databases }))
|
|
87
|
+
}
|
|
88
|
+
instance.isolation.markBegin()
|
|
89
|
+
return { id: contextId, db: dbConnections }
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export async function endTestIsolation(instance: Manifold, ctx: TestContext): Promise<void> {
|
|
93
|
+
contextWatches.get(ctx.id)?.()
|
|
94
|
+
contextWatches.delete(ctx.id)
|
|
95
|
+
await settleTraceWritesBounded()
|
|
96
|
+
instance.pool.clearOverrides()
|
|
97
|
+
|
|
98
|
+
const connections = contextConnections.get(ctx.id)
|
|
99
|
+
if (!connections) {
|
|
100
|
+
instance.isolation.markEnd()
|
|
101
|
+
return
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
for (const [, conn] of connections) {
|
|
105
|
+
try {
|
|
106
|
+
await conn.drizzle.execute(sql.raw('ROLLBACK'))
|
|
107
|
+
} catch {
|
|
108
|
+
// Transaction may already be aborted
|
|
109
|
+
}
|
|
110
|
+
try {
|
|
111
|
+
await conn.client.end()
|
|
112
|
+
} catch {
|
|
113
|
+
// Connection may already be closed
|
|
114
|
+
}
|
|
115
|
+
await conn.closeWatch?.().catch(() => {})
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
contextConnections.delete(ctx.id)
|
|
119
|
+
instance.isolation.markEnd()
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async function createIsolatedConnection(
|
|
123
|
+
testDb: TestDatabase,
|
|
124
|
+
connections: Map<string, ActiveTestConnection>,
|
|
125
|
+
name: string,
|
|
126
|
+
observability: ResolvedObservability,
|
|
127
|
+
withSystem: boolean,
|
|
128
|
+
): Promise<PostgresJsDatabase> {
|
|
129
|
+
const debug = createDebugFn(observability)
|
|
130
|
+
const client = postgres(testDb.url, { max: 1, onnotice: () => {}, ...(debug ? { debug } : {}) })
|
|
131
|
+
if (isObserverOnly(observability)) markObservedConnection(client)
|
|
132
|
+
const watch = createStatementWatch({ database: name, url: testDb.url, slowQueryMs: observability.slowQueryMs, inFlightQueryMs: observability.inFlightQueryMs })
|
|
133
|
+
const db = drizzle(client, { statementWatch: watch.watch })
|
|
134
|
+
|
|
135
|
+
await db.execute(sql.raw('BEGIN'))
|
|
136
|
+
const join = collapseTransactionsToSavepoints(db, client)
|
|
137
|
+
splitLargeInserts(db)
|
|
138
|
+
let system: PostgresJsDatabase | undefined
|
|
139
|
+
let door: ((name: string) => PostgresJsDatabase) | undefined
|
|
140
|
+
if (withSystem) {
|
|
141
|
+
system = createSystemHandle(client)
|
|
142
|
+
join(system)
|
|
143
|
+
door = (doorName) => {
|
|
144
|
+
const handle = createSystemHandle(client, doorName)
|
|
145
|
+
join(handle)
|
|
146
|
+
return handle
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
connections.set(name, { client, closeWatch: watch.close, drizzle: db, ...(system !== undefined ? { system } : {}), ...(door !== undefined ? { door } : {}) })
|
|
151
|
+
return db
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export function createTestContext(instance: Manifold, testConnections: TestContext, auth?: unknown, tenant?: string | null): Flow {
|
|
155
|
+
const ctx = createCtx(instance, randomUUID(), { auth, tenant })
|
|
156
|
+
for (const [name, drizzleDb] of Object.entries(testConnections.db)) {
|
|
157
|
+
ctx.transactions.set(name, { drizzle: drizzleDb, nonce: ctx._nonce, tenant: ctx.tenant })
|
|
158
|
+
}
|
|
159
|
+
return ctx
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export async function propagateTestSessionVars(
|
|
163
|
+
instance: Manifold,
|
|
164
|
+
testConnections: TestContext,
|
|
165
|
+
tenant: string,
|
|
166
|
+
): Promise<void> {
|
|
167
|
+
for (const [name, db] of Object.entries(testConnections.db)) {
|
|
168
|
+
const dbConfig = instance.config.database(name)
|
|
169
|
+
if (dbConfig.rls !== undefined) {
|
|
170
|
+
await db.execute(sql`SELECT set_config(${dbConfig.rls.sessionVar}, ${tenant}, true)`)
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export function createSetupConnection(
|
|
176
|
+
testDb: TestDatabase,
|
|
177
|
+
observability?: ResolvedObservability,
|
|
178
|
+
): { drizzle: PostgresJsDatabase; close: () => Promise<void> } {
|
|
179
|
+
const debug = observability ? createDebugFn(observability) : undefined
|
|
180
|
+
const client = postgres(testDb.url, { max: 1, onnotice: () => {}, ...(debug ? { debug } : {}) })
|
|
181
|
+
if (observability && isObserverOnly(observability)) markObservedConnection(client)
|
|
182
|
+
const watch = observability
|
|
183
|
+
? createStatementWatch({ database: 'setup', url: testDb.url, slowQueryMs: observability.slowQueryMs, inFlightQueryMs: observability.inFlightQueryMs })
|
|
184
|
+
: undefined
|
|
185
|
+
const db = splitLargeInserts(drizzle(client, watch ? { statementWatch: watch.watch } : {}))
|
|
186
|
+
return { drizzle: db, close: async () => { await watch?.close(); await client.end() } }
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export { runInContext }
|