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,47 @@
|
|
|
1
|
+
import { sql } from '../query/index.js'
|
|
2
|
+
import type { DB } from '../db/index.js'
|
|
3
|
+
import { quoteIdentifier } from '../db/identifiers.js'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* What the tracker table says has been applied, in whichever vocabulary the
|
|
7
|
+
* table currently speaks:
|
|
8
|
+
*
|
|
9
|
+
* - `tags`: the upgraded shape — applied = the exact set of recorded migration
|
|
10
|
+
* tags (see `applyMigrations`, which owns the membership semantics and the
|
|
11
|
+
* legacy-table upgrade).
|
|
12
|
+
* - `legacy-threshold`: a not-yet-upgraded table (no `tag` column) can only
|
|
13
|
+
* express the old high-water mark, `max(created_at)`. It is read for exactly
|
|
14
|
+
* one purpose: scoping the pre-apply SAFETY check to pending migrations, so
|
|
15
|
+
* a fully-current database with a destructive op in its applied history can
|
|
16
|
+
* still run a clean no-op `migrate`. `applyMigrations` upgrades the table on
|
|
17
|
+
* its next run, after which this variant never appears again.
|
|
18
|
+
*
|
|
19
|
+
* `null` — the table does not exist; nothing has been applied.
|
|
20
|
+
*/
|
|
21
|
+
export type AppliedState =
|
|
22
|
+
| { kind: 'tags'; tags: ReadonlySet<string> }
|
|
23
|
+
| { kind: 'legacy-threshold'; threshold: number }
|
|
24
|
+
|
|
25
|
+
export async function readAppliedState(db: DB, migrationsTable: string): Promise<AppliedState | null> {
|
|
26
|
+
const quoted = quoteIdentifier(migrationsTable, 'migration table')
|
|
27
|
+
|
|
28
|
+
const [reg] = await db.execute(sql`SELECT to_regclass(${quoted}) AS reg`)
|
|
29
|
+
if (reg === undefined || reg['reg'] === null) return null
|
|
30
|
+
|
|
31
|
+
const [tagColumn] = await db.execute(
|
|
32
|
+
sql`SELECT 1 AS present FROM pg_attribute
|
|
33
|
+
WHERE attrelid = to_regclass(${quoted}) AND attname = 'tag' AND NOT attisdropped`,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
if (tagColumn === undefined) {
|
|
37
|
+
const [row] = await db.execute(sql.raw(`SELECT max(created_at) AS last_applied FROM ${quoted}`))
|
|
38
|
+
const lastApplied = row?.['last_applied']
|
|
39
|
+
return {
|
|
40
|
+
kind: 'legacy-threshold',
|
|
41
|
+
threshold: lastApplied === null || lastApplied === undefined ? -1 : Number(lastApplied),
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const rows = await db.execute(sql.raw(`SELECT tag FROM ${quoted} WHERE tag IS NOT NULL`))
|
|
46
|
+
return { kind: 'tags', tags: new Set(rows.map((row) => String(row['tag']))) }
|
|
47
|
+
}
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs'
|
|
2
|
+
import { createHash } from 'node:crypto'
|
|
3
|
+
import postgres from 'postgres'
|
|
4
|
+
import { scanStatements } from './statements.js'
|
|
5
|
+
import { stripPragmaLines } from './pragma.js'
|
|
6
|
+
import { listMigrations } from './history.js'
|
|
7
|
+
|
|
8
|
+
const NO_TRANSACTION_PRAGMA = '-- pipework:no-transaction'
|
|
9
|
+
|
|
10
|
+
export interface ApplyOptions {
|
|
11
|
+
migrationsFolder: string
|
|
12
|
+
migrationsTable: string
|
|
13
|
+
connectionUrl: string
|
|
14
|
+
stripConcurrently?: boolean
|
|
15
|
+
/**
|
|
16
|
+
* Rewrite `CREATE TABLE` to `CREATE UNLOGGED TABLE`. A test database is
|
|
17
|
+
* dropped or rolled back and never needs crash durability, so skipping the
|
|
18
|
+
* WAL for its tables is free speed with no semantic change. Issue #325.
|
|
19
|
+
*/
|
|
20
|
+
unloggedTables?: boolean
|
|
21
|
+
/**
|
|
22
|
+
* Called after each migration file is applied and tracked. Replaying a stack
|
|
23
|
+
* is the longest single step of test setup; without this it is one silent
|
|
24
|
+
* await and a caller watching for progress sees none. Issue #380.
|
|
25
|
+
*/
|
|
26
|
+
onMigration?: (tag: string, index: number, total: number) => void
|
|
27
|
+
/**
|
|
28
|
+
* Called after each statement of a migration file lands (inside the file's
|
|
29
|
+
* transaction, when it has one), with the 1-based statement and the file's
|
|
30
|
+
* statement count, then the 1-based position of the file among those being
|
|
31
|
+
* applied and their count. A long file is otherwise silent until it commits.
|
|
32
|
+
* Issue #871.
|
|
33
|
+
*/
|
|
34
|
+
onStatement?: (tag: string, statement: number, statements: number, index: number, total: number) => void
|
|
35
|
+
/**
|
|
36
|
+
* Called as each step before the first file finishes (tracker table, legacy
|
|
37
|
+
* tag recovery per file, the unique index, the applied read), with a detail
|
|
38
|
+
* naming it. Issue #871.
|
|
39
|
+
*/
|
|
40
|
+
onPrepare?: (detail: string) => void
|
|
41
|
+
/**
|
|
42
|
+
* Called when a no-transaction migration resumes a half-applied file, with
|
|
43
|
+
* the statement it starts from (1-based) and how many the file holds.
|
|
44
|
+
* Issue #378.
|
|
45
|
+
*/
|
|
46
|
+
onResume?: (tag: string, fromStatement: number, total: number) => void
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface Migration {
|
|
50
|
+
tag: string
|
|
51
|
+
hash: string
|
|
52
|
+
/** Hash of the file with `expect-*` pragma lines removed; also accepted as the recorded one. */
|
|
53
|
+
pragmaFreeHash?: string
|
|
54
|
+
folderMillis: number
|
|
55
|
+
statements: string[]
|
|
56
|
+
noTransaction: boolean
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export async function applyMigrations(options: ApplyOptions): Promise<number> {
|
|
60
|
+
const migrations = readMigrations(options)
|
|
61
|
+
if (migrations.length === 0) return 0
|
|
62
|
+
|
|
63
|
+
const client = postgres(options.connectionUrl, { max: 1, onnotice: () => {} })
|
|
64
|
+
|
|
65
|
+
try {
|
|
66
|
+
const table = quoteIdent(options.migrationsTable)
|
|
67
|
+
const prepared = (detail: string): void => options.onPrepare?.(detail)
|
|
68
|
+
|
|
69
|
+
// "Applied" is MEMBERSHIP — this exact migration (by tag, verified by
|
|
70
|
+
// content hash) has a tracker row — never a timestamp high-water mark.
|
|
71
|
+
// A threshold silently skips any unapplied migration dated earlier than
|
|
72
|
+
// the newest applied one: a branch merged late, or a second manifold
|
|
73
|
+
// sharing this tracker table whose whole journal predates the first
|
|
74
|
+
// manifold's newest entry (a real production incident for both).
|
|
75
|
+
await client.unsafe(`
|
|
76
|
+
CREATE TABLE IF NOT EXISTS ${table} (
|
|
77
|
+
id SERIAL PRIMARY KEY,
|
|
78
|
+
hash text NOT NULL,
|
|
79
|
+
created_at bigint,
|
|
80
|
+
tag text
|
|
81
|
+
)
|
|
82
|
+
`)
|
|
83
|
+
prepared('tracker table')
|
|
84
|
+
// Deployed databases predate the tag column: add it, then recover each
|
|
85
|
+
// legacy row's identity by matching its recorded content hash against the
|
|
86
|
+
// journal's files (min(id) claims one row per tag, so a duplicated hash
|
|
87
|
+
// can't double-claim). Rows matching no current journal entry keep a NULL
|
|
88
|
+
// tag and never participate in membership.
|
|
89
|
+
await client.unsafe(`ALTER TABLE ${table} ADD COLUMN IF NOT EXISTS tag text`)
|
|
90
|
+
for (const [i, migration] of migrations.entries()) {
|
|
91
|
+
await client.unsafe(
|
|
92
|
+
`UPDATE ${table} SET tag = $1
|
|
93
|
+
WHERE id = (SELECT min(id) FROM ${table} WHERE hash = $2 AND tag IS NULL)`,
|
|
94
|
+
[migration.tag, migration.hash],
|
|
95
|
+
)
|
|
96
|
+
prepared(`tags ${i + 1}/${migrations.length}`)
|
|
97
|
+
}
|
|
98
|
+
await client.unsafe(
|
|
99
|
+
`CREATE UNIQUE INDEX IF NOT EXISTS ${quoteIdent(`${options.migrationsTable}_tag_key`)} ON ${table} (tag)`,
|
|
100
|
+
)
|
|
101
|
+
prepared('tag index')
|
|
102
|
+
|
|
103
|
+
const rows = await client.unsafe<{ tag: string | null; hash: string }[]>(
|
|
104
|
+
`SELECT tag, hash FROM ${table} WHERE tag IS NOT NULL`,
|
|
105
|
+
)
|
|
106
|
+
const appliedHashByTag = new Map(rows.map((row) => [row.tag!, row.hash]))
|
|
107
|
+
prepared('applied read')
|
|
108
|
+
|
|
109
|
+
// A no-transaction file has no rollback: interrupted between statements it
|
|
110
|
+
// leaves the database half-changed with no tracker row, and the rerun
|
|
111
|
+
// replays from the top and dies on the first already-applied statement.
|
|
112
|
+
// Nor can the file be edited to add guards — its content is immutable
|
|
113
|
+
// history. So each statement's completion is recorded as it lands, and a
|
|
114
|
+
// rerun starts from the first one not recorded. The table exists only for
|
|
115
|
+
// databases that actually have a no-transaction migration. Issue #378.
|
|
116
|
+
const progress = quoteIdent(progressTableName(options.migrationsTable))
|
|
117
|
+
const hasNoTransaction = migrations.some((migration) => migration.noTransaction)
|
|
118
|
+
const progressByTag = new Map<string, { hash: string; done: number }>()
|
|
119
|
+
if (hasNoTransaction) {
|
|
120
|
+
await client.unsafe(`
|
|
121
|
+
CREATE TABLE IF NOT EXISTS ${progress} (
|
|
122
|
+
tag text PRIMARY KEY,
|
|
123
|
+
hash text NOT NULL,
|
|
124
|
+
statements_done integer NOT NULL,
|
|
125
|
+
updated_at timestamptz NOT NULL DEFAULT now()
|
|
126
|
+
)
|
|
127
|
+
`)
|
|
128
|
+
const partial = await client.unsafe<{ tag: string; hash: string; statements_done: number }[]>(
|
|
129
|
+
`SELECT tag, hash, statements_done FROM ${progress}`,
|
|
130
|
+
)
|
|
131
|
+
for (const row of partial) {
|
|
132
|
+
progressByTag.set(row.tag, { hash: row.hash, done: row.statements_done })
|
|
133
|
+
}
|
|
134
|
+
prepared('progress table')
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
let applied = 0
|
|
138
|
+
|
|
139
|
+
for (const migration of migrations) {
|
|
140
|
+
const appliedHash = appliedHashByTag.get(migration.tag)
|
|
141
|
+
if (appliedHash !== undefined) {
|
|
142
|
+
if (!hashMatches(appliedHash, migration)) {
|
|
143
|
+
throw new Error(
|
|
144
|
+
`Migration "${migration.tag}" is recorded as applied in ${options.migrationsTable}, but its ` +
|
|
145
|
+
`file content has changed (recorded sha256 ${appliedHash.slice(0, 12)}…, file is now ` +
|
|
146
|
+
`${migration.hash.slice(0, 12)}…). Applied migrations are immutable history — put schema ` +
|
|
147
|
+
`changes in a NEW migration (\`pipework generate\`) instead of editing an applied one (adding "-- pipework:expect-*" lines is allowed). If two ` +
|
|
148
|
+
`manifolds share this database and both declare a migration tagged "${migration.tag}", that is ` +
|
|
149
|
+
`a schema-level collision no tracker can reconcile — give them separate databases or distinct ` +
|
|
150
|
+
`migrationTable names.`,
|
|
151
|
+
)
|
|
152
|
+
}
|
|
153
|
+
continue
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (migration.noTransaction) {
|
|
157
|
+
const from = resumePoint(migration, progressByTag.get(migration.tag), options.migrationsTable)
|
|
158
|
+
if (from > 0) options.onResume?.(migration.tag, from + 1, migration.statements.length)
|
|
159
|
+
|
|
160
|
+
for (let i = from; i < migration.statements.length; i++) {
|
|
161
|
+
await client.unsafe(migration.statements[i]!)
|
|
162
|
+
// Recorded AFTER the statement it describes: marking first would let
|
|
163
|
+
// a death between the mark and the statement skip work silently,
|
|
164
|
+
// which is worse than replaying one statement. The window that
|
|
165
|
+
// remains is exactly one statement wide — the one whose mark had not
|
|
166
|
+
// landed yet — instead of the whole file.
|
|
167
|
+
await client.unsafe(
|
|
168
|
+
`INSERT INTO ${progress} (tag, hash, statements_done) VALUES ($1, $2, $3)
|
|
169
|
+
ON CONFLICT (tag) DO UPDATE SET statements_done = EXCLUDED.statements_done, updated_at = now()`,
|
|
170
|
+
[migration.tag, migration.hash, i + 1],
|
|
171
|
+
)
|
|
172
|
+
options.onStatement?.(migration.tag, i + 1, migration.statements.length, applied + 1, migrations.length)
|
|
173
|
+
}
|
|
174
|
+
await client.unsafe(
|
|
175
|
+
`INSERT INTO ${table} ("hash", "created_at", "tag") VALUES ($1, $2, $3)`,
|
|
176
|
+
[migration.hash, migration.folderMillis, migration.tag],
|
|
177
|
+
)
|
|
178
|
+
await client.unsafe(`DELETE FROM ${progress} WHERE tag = $1`, [migration.tag])
|
|
179
|
+
} else {
|
|
180
|
+
await client.begin(async (tx) => {
|
|
181
|
+
for (const [i, stmt] of migration.statements.entries()) {
|
|
182
|
+
await tx.unsafe(stmt)
|
|
183
|
+
options.onStatement?.(migration.tag, i + 1, migration.statements.length, applied + 1, migrations.length)
|
|
184
|
+
}
|
|
185
|
+
await tx.unsafe(
|
|
186
|
+
`INSERT INTO ${table} ("hash", "created_at", "tag") VALUES ($1, $2, $3)`,
|
|
187
|
+
[migration.hash, migration.folderMillis, migration.tag],
|
|
188
|
+
)
|
|
189
|
+
})
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
applied++
|
|
193
|
+
options.onMigration?.(migration.tag, applied, migrations.length)
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return applied
|
|
197
|
+
} finally {
|
|
198
|
+
await client.end()
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
export function readMigrations(options: ApplyOptions): Migration[] {
|
|
203
|
+
const migrations: Migration[] = []
|
|
204
|
+
|
|
205
|
+
for (const entry of listMigrations(options.migrationsFolder)) {
|
|
206
|
+
const filePath = `${options.migrationsFolder}/${entry.tag}.sql`
|
|
207
|
+
let raw = readFileSync(filePath, 'utf-8')
|
|
208
|
+
const noTransaction = raw.includes(NO_TRANSACTION_PRAGMA)
|
|
209
|
+
|
|
210
|
+
if (options.stripConcurrently === true) {
|
|
211
|
+
raw = raw.replace(/\bCONCURRENTLY\s+/gi, '')
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (options.unloggedTables === true) {
|
|
215
|
+
raw = makeTablesUnlogged(raw)
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const statements = parseStatements(raw)
|
|
219
|
+
|
|
220
|
+
const fileText = readFileSync(filePath, 'utf-8')
|
|
221
|
+
migrations.push({
|
|
222
|
+
tag: entry.tag,
|
|
223
|
+
hash: sha256(fileText),
|
|
224
|
+
pragmaFreeHash: sha256(stripPragmaLines(fileText)),
|
|
225
|
+
folderMillis: entry.when,
|
|
226
|
+
statements,
|
|
227
|
+
noTransaction,
|
|
228
|
+
})
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
return migrations
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function sha256(text: string): string {
|
|
235
|
+
return createHash('sha256').update(text).digest('hex')
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* True when a recorded hash is this migration's: the raw file hash, or the
|
|
240
|
+
* hash with `expect-*` pragma lines removed (those lines may be added to an
|
|
241
|
+
* applied file; nothing else may change).
|
|
242
|
+
*/
|
|
243
|
+
function hashMatches(recorded: string, migration: Migration): boolean {
|
|
244
|
+
return recorded === migration.hash || recorded === migration.pragmaFreeHash
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Split migration SQL into individual statements, honoring drizzle's
|
|
249
|
+
* `--> statement-breakpoint` as an explicit separator.
|
|
250
|
+
*
|
|
251
|
+
* The scanning rules live in `statements.ts`, shared with the safety gate so
|
|
252
|
+
* the runner and the gate cannot disagree about where a statement begins —
|
|
253
|
+
* they did, and the gate was the one that was wrong (#334).
|
|
254
|
+
*/
|
|
255
|
+
export function parseStatements(raw: string): string[] {
|
|
256
|
+
return scanStatements(raw, { breakpointSeparator: true })
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/** Companion to the tracker table, holding half-applied no-transaction files. */
|
|
260
|
+
export function progressTableName(migrationsTable: string): string {
|
|
261
|
+
return `${migrationsTable}_progress`
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* The statement index a half-applied no-transaction file resumes from. Zero
|
|
266
|
+
* when there is no record of one.
|
|
267
|
+
*
|
|
268
|
+
* A recorded partial whose hash no longer matches the file is refused rather
|
|
269
|
+
* than resumed: the mark counts statements in the file as it was, and the file
|
|
270
|
+
* on disk is now a different one, so no index into it means anything.
|
|
271
|
+
*/
|
|
272
|
+
export function resumePoint(
|
|
273
|
+
migration: Migration,
|
|
274
|
+
recorded: { hash: string; done: number } | undefined,
|
|
275
|
+
migrationsTable: string,
|
|
276
|
+
): number {
|
|
277
|
+
if (recorded === undefined) return 0
|
|
278
|
+
if (!hashMatches(recorded.hash, migration)) {
|
|
279
|
+
throw new Error(
|
|
280
|
+
`Migration "${migration.tag}" is half applied — ${recorded.done} of its statements ran before the ` +
|
|
281
|
+
`runner stopped, recorded in ${progressTableName(migrationsTable)} — and its file content has ` +
|
|
282
|
+
`changed since (recorded sha256 ${recorded.hash.slice(0, 12)}…, file is now ` +
|
|
283
|
+
`${migration.hash.slice(0, 12)}…). The recorded count indexes into the file as it was, so it ` +
|
|
284
|
+
`cannot be carried into the new one. Restore the file to the content that was running, let the ` +
|
|
285
|
+
`resume finish, and put the change in a NEW migration; or, if the half-applied statements have ` +
|
|
286
|
+
`been undone by hand, delete the "${migration.tag}" row from ` +
|
|
287
|
+
`${progressTableName(migrationsTable)} to start the file over.`,
|
|
288
|
+
)
|
|
289
|
+
}
|
|
290
|
+
// A count past the end means every statement landed and only the tracker row
|
|
291
|
+
// was missed: nothing left to run, and the caller writes the row.
|
|
292
|
+
return Math.min(recorded.done, migration.statements.length)
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
function quoteIdent(name: string): string {
|
|
296
|
+
return `"${name.replace(/"/g, '""')}"`
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Rewrite every `CREATE TABLE` (including the `IF NOT EXISTS` form) to
|
|
301
|
+
* `CREATE UNLOGGED TABLE`. Used only when applying schema to a test database —
|
|
302
|
+
* a test database is dropped or rolled back, so the WAL its tables would
|
|
303
|
+
* generate is pure overhead. `CREATE UNLOGGED TABLE` is otherwise identical
|
|
304
|
+
* SQL: same columns, constraints, RLS, isolation. `TEMPORARY` and
|
|
305
|
+
* already-`UNLOGGED` tables are left alone. Issue #325.
|
|
306
|
+
*/
|
|
307
|
+
export function makeTablesUnlogged(sql: string): string {
|
|
308
|
+
return sql.replace(/\bCREATE\s+TABLE\b/gi, 'CREATE UNLOGGED TABLE')
|
|
309
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { readFileSync, existsSync } from 'node:fs'
|
|
2
|
+
import { join } from 'node:path'
|
|
3
|
+
import { analyzeSQL, formatHazards, type MigrationHazard, type Severity } from './safety.js'
|
|
4
|
+
import { hasPragma, PRAGMA_FOR, type PragmaKind } from './pragma.js'
|
|
5
|
+
import type { AppliedState } from './applied.js'
|
|
6
|
+
import { listMigrations, type MigrationEntry } from './history.js'
|
|
7
|
+
|
|
8
|
+
export interface FileHazards {
|
|
9
|
+
file: string
|
|
10
|
+
hazards: MigrationHazard[]
|
|
11
|
+
/** Hazard classes this file raises but does not declare a pragma for. */
|
|
12
|
+
undeclared: PragmaKind[]
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface MigrationCheckResult {
|
|
16
|
+
safe: boolean
|
|
17
|
+
hasDestructive: boolean
|
|
18
|
+
hasLocking: boolean
|
|
19
|
+
hasDynamic: boolean
|
|
20
|
+
/** True when every hazard raised is declared by its own migration. */
|
|
21
|
+
declared: boolean
|
|
22
|
+
fileHazards: FileHazards[]
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Scans pending migrations for destructive and locking operations.
|
|
27
|
+
*
|
|
28
|
+
* `applied` is what the tracker table records as already applied (see
|
|
29
|
+
* `readAppliedState`); applied entries are skipped — their hazards are
|
|
30
|
+
* history, not a decision the operator still gets to make. Membership is by
|
|
31
|
+
* tag on an upgraded tracker, or by the old `when`-timestamp high-water mark
|
|
32
|
+
* on a tracker `applyMigrations` has not upgraded yet. Omitting `applied`
|
|
33
|
+
* (or passing null — no tracker table) scans every entry, the correct
|
|
34
|
+
* behaviour when nothing is known to be applied.
|
|
35
|
+
*/
|
|
36
|
+
export function checkMigrations(migrationsFolder: string, applied?: AppliedState | null): MigrationCheckResult {
|
|
37
|
+
const entries = listMigrations(migrationsFolder)
|
|
38
|
+
const fileHazards: FileHazards[] = []
|
|
39
|
+
let hasDestructive = false
|
|
40
|
+
let hasLocking = false
|
|
41
|
+
let hasDynamic = false
|
|
42
|
+
let declared = true
|
|
43
|
+
|
|
44
|
+
const isApplied = (entry: MigrationEntry): boolean => {
|
|
45
|
+
if (applied === undefined || applied === null) return false
|
|
46
|
+
if (applied.kind === 'tags') return applied.tags.has(entry.tag)
|
|
47
|
+
return entry.when <= applied.threshold
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
for (const entry of entries) {
|
|
51
|
+
if (isApplied(entry)) continue
|
|
52
|
+
|
|
53
|
+
const sqlPath = join(migrationsFolder, `${entry.tag}.sql`)
|
|
54
|
+
if (!existsSync(sqlPath)) continue
|
|
55
|
+
|
|
56
|
+
const sql = readFileSync(sqlPath, 'utf-8')
|
|
57
|
+
const hazards = analyzeSQL(sql)
|
|
58
|
+
|
|
59
|
+
if (hazards.length > 0) {
|
|
60
|
+
// A hazard is authorized by a pragma in the migration that raises it —
|
|
61
|
+
// never by a flag on the deploy command, which would authorize this
|
|
62
|
+
// migration and every migration after it. Checked per file, per class.
|
|
63
|
+
const undeclared: PragmaKind[] = []
|
|
64
|
+
for (const kind of ['destructive', 'locking', 'dynamic'] as const) {
|
|
65
|
+
if (!hazards.some(h => h.severity === kind)) continue
|
|
66
|
+
if (kind === 'destructive') hasDestructive = true
|
|
67
|
+
if (kind === 'locking') hasLocking = true
|
|
68
|
+
if (kind === 'dynamic') hasDynamic = true
|
|
69
|
+
if (!hasPragma(sql, kind)) {
|
|
70
|
+
undeclared.push(kind)
|
|
71
|
+
declared = false
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
fileHazards.push({ file: `${entry.tag}.sql`, hazards, undeclared })
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return {
|
|
79
|
+
safe: !hasDestructive && !hasLocking && !hasDynamic,
|
|
80
|
+
hasDestructive,
|
|
81
|
+
hasLocking,
|
|
82
|
+
hasDynamic,
|
|
83
|
+
declared,
|
|
84
|
+
fileHazards,
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function formatCheckResult(result: MigrationCheckResult): string {
|
|
89
|
+
if (result.declared) return ''
|
|
90
|
+
|
|
91
|
+
const lines: string[] = []
|
|
92
|
+
const undeclaredFiles = result.fileHazards.filter(f => f.undeclared.length > 0)
|
|
93
|
+
const n = undeclaredFiles.length
|
|
94
|
+
|
|
95
|
+
lines.push(
|
|
96
|
+
`Migration safety check: ${n} migration${n > 1 ? 's' : ''} raise${n > 1 ? '' : 's'} ` +
|
|
97
|
+
`hazards that ${n > 1 ? 'their own files do' : 'its own file does'} not declare.\n`,
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
for (const { file, hazards } of undeclaredFiles) {
|
|
101
|
+
lines.push(formatHazards(hazards, file))
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
lines.push('')
|
|
105
|
+
lines.push('Authorization belongs in the migration, not the deploy command.')
|
|
106
|
+
lines.push('Add the matching line to the top of each file above and commit it,')
|
|
107
|
+
lines.push('so the approval is reviewed in the diff and recorded in history:')
|
|
108
|
+
lines.push('')
|
|
109
|
+
|
|
110
|
+
for (const { file, undeclared } of undeclaredFiles) {
|
|
111
|
+
for (const kind of undeclared) {
|
|
112
|
+
lines.push(` ${file}: -- ${PRAGMA_FOR[kind]}`)
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return lines.join('\n')
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export type { MigrationHazard, Severity }
|