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,5 @@
|
|
|
1
|
+
// Auto-generated by scaffold-namespace — do not edit re-export lines
|
|
2
|
+
// Run: node scripts/scaffold-namespace.js generate manifests/zod--v4.json
|
|
3
|
+
// Manual symbols are not included — they live in separate files.
|
|
4
|
+
|
|
5
|
+
export { base64, base64url, cidrv4, cidrv6, cuid, cuid2, e164, email, emoji, float32, float64, guid, hex, hostname, httpUrl, int32, int64, ipv4, ipv6, jwt, ksuid, mac, nanoid, stringbool, stringFormat, uint32, uint64, ulid, url, uuid, uuidv4, uuidv6, uuidv7, xid } from 'zod/v4'
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// Auto-generated by scaffold-namespace — do not edit re-export lines
|
|
2
|
+
// Run: node scripts/scaffold-namespace.js generate manifests/zod--v4.json
|
|
3
|
+
// Manual symbols are not included — they live in separate files.
|
|
4
|
+
|
|
5
|
+
export type { function, null, undefined, void, enum } from 'zod/v4'
|
|
6
|
+
export { _function, any, array, bigint, boolean, custom, date, file, int, json, lazy, literal, map, nan, nativeEnum, never, number, object, promise, record, set, string, symbol, tuple, unknown } from 'zod/v4'
|
|
7
|
+
|
|
8
|
+
// Reserved words — runtime values exist but can't be re-exported as identifiers.
|
|
9
|
+
// Provide manually in the namespace file (e.g. `enum: z.enum`):
|
|
10
|
+
// enum
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// Auto-generated by scaffold-namespace — do not edit re-export lines
|
|
2
|
+
// Run: node scripts/scaffold-namespace.js generate manifests/zod--v4.json
|
|
3
|
+
// Manual symbols are not included — they live in separate files.
|
|
4
|
+
|
|
5
|
+
export type { BRAND, GlobalMeta, infer, Infer, input, IssueData, JSONType, NEVER, output, RefinementCtx, SafeExtendShape, Schema, TimePrecision, TypeOf, ZodAny, ZodArray, ZodBigInt, ZodBoolean, ZodCatch, ZodCodec, ZodCustom, ZodDate, ZodDefault, ZodDiscriminatedUnion, ZodEnum, ZodError, ZodErrorMap, ZodExactOptional, ZodFile, ZodFirstPartySchemaTypes, ZodFlattenedError, ZodFormattedError, ZodFunction, ZodIntersection, ZodIssue, ZodLazy, ZodLiteral, ZodMap, ZodNaN, ZodNever, ZodNonOptional, ZodNull, ZodNullable, ZodNumber, ZodObject, ZodOptional, ZodPipe, ZodPrefault, ZodPreprocess, ZodPromise, ZodRawShape, ZodReadonly, ZodRecord, ZodSafeParseError, ZodSafeParseResult, ZodSafeParseSuccess, ZodSchema, ZodSet, ZodString, ZodSuccess, ZodSymbol, ZodTransform, ZodTuple, ZodType, ZodTypeAny, ZodUndefined, ZodUnion, ZodUnknown, ZodVoid } from 'zod/v4'
|
|
6
|
+
export { ZodFirstPartyTypeKind, ZodIssueCode, ZodRealError } from 'zod/v4'
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { z } from 'zod/v4'
|
|
2
|
+
import type { ZodStandardJSONSchemaPayload } from 'zod/v4/core'
|
|
3
|
+
|
|
4
|
+
export type JsonSchema = ZodStandardJSONSchemaPayload<z.ZodType>
|
|
5
|
+
|
|
6
|
+
export function toJsonSchema(schema: z.ZodType): JsonSchema {
|
|
7
|
+
return collapseSingleRefAllOf(z.toJSONSchema(schema, { target: 'draft-07' })) as JsonSchema
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
// zod emits a lazy reference as `{ allOf: [{ $ref }] }`; fast-json-stringify recurses
|
|
11
|
+
// forever on that form when the target is recursive, and accepts the bare `$ref`.
|
|
12
|
+
function collapseSingleRefAllOf(node: unknown): unknown {
|
|
13
|
+
if (Array.isArray(node)) return node.map(collapseSingleRefAllOf)
|
|
14
|
+
if (node === null || typeof node !== 'object') return node
|
|
15
|
+
const source = node as Record<string, unknown>
|
|
16
|
+
const out: Record<string, unknown> = {}
|
|
17
|
+
for (const [key, value] of Object.entries(source)) {
|
|
18
|
+
if (key === 'allOf' && isSingleRef(value)) {
|
|
19
|
+
out['$ref'] = value[0]['$ref']
|
|
20
|
+
} else {
|
|
21
|
+
out[key] = collapseSingleRefAllOf(value)
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return out
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function isSingleRef(value: unknown): value is [{ $ref: unknown }] {
|
|
28
|
+
if (!Array.isArray(value) || value.length !== 1) return false
|
|
29
|
+
const member: unknown = value[0]
|
|
30
|
+
if (member === null || typeof member !== 'object' || Array.isArray(member)) return false
|
|
31
|
+
const keys = Object.keys(member)
|
|
32
|
+
return keys.length === 1 && keys[0] === '$ref'
|
|
33
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { z } from 'zod/v4'
|
|
2
|
+
import * as typeConstructors from './generated/type-constructor.js'
|
|
3
|
+
import * as combinators from './generated/combinator.js'
|
|
4
|
+
import * as formats from './generated/format.js'
|
|
5
|
+
import * as encoding from './generated/encoding.js'
|
|
6
|
+
import * as errors from './generated/error.js'
|
|
7
|
+
import { parse } from './parse.js'
|
|
8
|
+
import { tryParse } from './parse.js'
|
|
9
|
+
import { toJsonSchema } from './json-schema.js'
|
|
10
|
+
import { branded } from './branded.js'
|
|
11
|
+
|
|
12
|
+
// Database schema definition layers (moved from db namespace)
|
|
13
|
+
import * as col from '../db/col/index.js'
|
|
14
|
+
import * as idx from '../db/idx/index.js'
|
|
15
|
+
import * as schemaFeatures from '../db/schema-features.js'
|
|
16
|
+
|
|
17
|
+
export type { Schema, Infer, StringSchema, NumberSchema, BooleanSchema, EnumSchema, LiteralSchema, ArraySchema, OptionalSchema, NullableSchema, ObjectSchema, ObjectOps } from './types.js'
|
|
18
|
+
export type { JsonSchema } from './json-schema.js'
|
|
19
|
+
|
|
20
|
+
/** Validation sub-namespace — type constructors (string, number, object, array), combinators (union, intersection), formats (email, uuid), coerce, parse, tryParse, toJsonSchema, branded. */
|
|
21
|
+
const check = {
|
|
22
|
+
...typeConstructors,
|
|
23
|
+
...combinators,
|
|
24
|
+
...formats,
|
|
25
|
+
...encoding,
|
|
26
|
+
...errors,
|
|
27
|
+
/** Enum schema from a list of string literals. */
|
|
28
|
+
enum: z.enum,
|
|
29
|
+
/** Coercion sub-namespace — coerce.number(), coerce.boolean(), etc. */
|
|
30
|
+
coerce: z.coerce,
|
|
31
|
+
|
|
32
|
+
/** Validates data against a schema, throws ValidationError with actionable message on failure. */
|
|
33
|
+
parse,
|
|
34
|
+
/** Validates data, returns { success, data } or { success: false, error } without throwing. */
|
|
35
|
+
tryParse,
|
|
36
|
+
/** Converts a schema to JSON Schema for OpenAPI or external validation. */
|
|
37
|
+
toJsonSchema,
|
|
38
|
+
/** Creates a branded type — nominal typing for IDs, slugs, etc. */
|
|
39
|
+
branded,
|
|
40
|
+
} as const
|
|
41
|
+
|
|
42
|
+
/** Schema definition and validation namespace — wraps zod for validation, drizzle for table/column/index definitions. */
|
|
43
|
+
export const schema = {
|
|
44
|
+
/** Validation sub-namespace — type constructors, combinators, formats, coerce, parse, tryParse, toJsonSchema, branded. */
|
|
45
|
+
check,
|
|
46
|
+
|
|
47
|
+
// Table/view definition — table, view, materializedView, pgSchema, sequence, policy, role, tableCreator, withReplicas
|
|
48
|
+
...schemaFeatures,
|
|
49
|
+
|
|
50
|
+
/** Column type builders for table definitions — text, uuid, integer, timestamp, jsonb, boolean, varchar, etc. */
|
|
51
|
+
col,
|
|
52
|
+
|
|
53
|
+
/** Index and constraint builders — index, uniqueIndex, primaryKey, foreignKey, unique, check. */
|
|
54
|
+
idx,
|
|
55
|
+
} as const
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { z } from 'zod/v4'
|
|
2
|
+
import { ValidationError, type ValidationSource } from '../../core/errors/index.js'
|
|
3
|
+
|
|
4
|
+
export function parse<T extends z.ZodType>(schema: T, data: unknown, source?: ValidationSource): z.infer<T> {
|
|
5
|
+
const result = schema.safeParse(data)
|
|
6
|
+
if (result.success) return result.data
|
|
7
|
+
|
|
8
|
+
const fieldErrors: Record<string, string[]> = {}
|
|
9
|
+
for (const issue of result.error.issues) {
|
|
10
|
+
const path = issue.path.length > 0 ? issue.path.join('.') : '_root'
|
|
11
|
+
if (!fieldErrors[path]) {
|
|
12
|
+
fieldErrors[path] = []
|
|
13
|
+
}
|
|
14
|
+
fieldErrors[path].push(issue.message)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
throw source === undefined
|
|
18
|
+
? new ValidationError(fieldErrors)
|
|
19
|
+
: new ValidationError(fieldErrors, undefined, { source, paths: result.error.issues.map(i => ({ path: i.path.map(String), message: i.message })) })
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function tryParse<T extends z.ZodType>(
|
|
23
|
+
schema: T,
|
|
24
|
+
data: unknown,
|
|
25
|
+
): { success: true; data: z.infer<T> } | { success: false; error: ValidationError } {
|
|
26
|
+
const result = schema.safeParse(data)
|
|
27
|
+
if (result.success) return { success: true, data: result.data }
|
|
28
|
+
|
|
29
|
+
const fieldErrors: Record<string, string[]> = {}
|
|
30
|
+
for (const issue of result.error.issues) {
|
|
31
|
+
const path = issue.path.length > 0 ? issue.path.join('.') : '_root'
|
|
32
|
+
if (!fieldErrors[path]) {
|
|
33
|
+
fieldErrors[path] = []
|
|
34
|
+
}
|
|
35
|
+
fieldErrors[path].push(issue.message)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return { success: false, error: new ValidationError(fieldErrors) }
|
|
39
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { z } from 'zod/v4'
|
|
2
|
+
|
|
3
|
+
/** Any validation schema — the base type accepted by .input(), .query(), .params() in the fitting builder. */
|
|
4
|
+
export type Schema<T = unknown> = z.ZodType<T>
|
|
5
|
+
/** Extracts the TypeScript type from a Schema — Infer<typeof mySchema> gives you the validated type. */
|
|
6
|
+
export type Infer<T extends Schema> = z.infer<T>
|
|
7
|
+
|
|
8
|
+
type Shape = z.core.$ZodShape
|
|
9
|
+
|
|
10
|
+
/*
|
|
11
|
+
* The validator types check.string(), check.object(), ... return are zod's own, under pipework names.
|
|
12
|
+
* They are plain aliases: a new interface per kind (with its own optional/nullable/extend) made every
|
|
13
|
+
* comparison against Schema<T> recurse through fresh `this` types, which cost an app 5x the instantiations
|
|
14
|
+
* (#690). The zod classes are re-exported from the package root so declaration emit can name an exported
|
|
15
|
+
* validator in an app that does not depend on zod (#673).
|
|
16
|
+
*/
|
|
17
|
+
export type { ZodString, ZodNumber, ZodBoolean, ZodEnum, ZodLiteral, ZodArray, ZodOptional, ZodNullable, ZodObject, ZodType } from 'zod/v4'
|
|
18
|
+
export type { $strip, $ZodTypeInternals } from 'zod/v4/core'
|
|
19
|
+
|
|
20
|
+
/** A string validator — what check.string() returns. */
|
|
21
|
+
export type StringSchema = z.ZodString
|
|
22
|
+
/** A number validator — what check.number() returns. */
|
|
23
|
+
export type NumberSchema = z.ZodNumber
|
|
24
|
+
/** A boolean validator — what check.boolean() returns. */
|
|
25
|
+
export type BooleanSchema = z.ZodBoolean
|
|
26
|
+
/** An enum validator over string literals — what check.enum([...]) returns. */
|
|
27
|
+
export type EnumSchema<V extends string = string> = z.ZodEnum<{ [K in V]: K }>
|
|
28
|
+
/** A literal validator — what check.literal(v) returns. */
|
|
29
|
+
export type LiteralSchema<V extends string | number | boolean | null = string> = z.ZodLiteral<V>
|
|
30
|
+
/** An array validator — what check.array(item) returns. */
|
|
31
|
+
export type ArraySchema<Item extends z.core.SomeType = z.core.SomeType> = z.ZodArray<Item>
|
|
32
|
+
/** An optional validator — what check.optional(inner) and .optional() return. */
|
|
33
|
+
export type OptionalSchema<Inner extends z.core.SomeType = z.core.SomeType> = z.ZodOptional<Inner>
|
|
34
|
+
/** A nullable validator — what check.nullable(inner) and .nullable() return. */
|
|
35
|
+
export type NullableSchema<Inner extends z.core.SomeType = z.core.SomeType> = z.ZodNullable<Inner>
|
|
36
|
+
/** An object validator — what check.object({...}) returns; keeps .extend, .pick, .omit and .partial. For a shape with fields beyond the plain kinds, annotate with ObjectSchema<{ field: Schema<T> }>. */
|
|
37
|
+
export type ObjectSchema<S extends Shape = Shape> = z.ZodObject<S, z.core.$strip>
|
|
38
|
+
/** The object operations of ObjectSchema. */
|
|
39
|
+
export type ObjectOps<S extends Shape> = Pick<ObjectSchema<S>, 'extend' | 'pick' | 'omit' | 'partial'>
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import type { DB, SQL } from '../db/types.js'
|
|
2
|
+
import type { DefinedTable } from '../domain/types.js'
|
|
3
|
+
import { sql } from '../db/sql.js'
|
|
4
|
+
import { identifier } from '../db/expressions.js'
|
|
5
|
+
import { advisoryLock } from '../db/advisory-lock.js'
|
|
6
|
+
import { camelToSnake } from '../domain/naming.js'
|
|
7
|
+
import { appendOnlyScopeField } from '../domain/traits.js'
|
|
8
|
+
import { insert } from '../write/insert.js'
|
|
9
|
+
import { and, desc, lte } from '../query/index.js'
|
|
10
|
+
import { TemporalIntegrityError } from './errors.js'
|
|
11
|
+
|
|
12
|
+
// The definition's field generics erase at this boundary, as in `pipe.insert`.
|
|
13
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
14
|
+
type AnyDefinition = DefinedTable<any>
|
|
15
|
+
|
|
16
|
+
export interface SetVersionOptions {
|
|
17
|
+
/** The transaction the write runs in. It must be the context's bound transaction (`pipe.transaction`), because the insert goes through `pipe.insert`. */
|
|
18
|
+
readonly tx: DB
|
|
19
|
+
readonly definition: AnyDefinition
|
|
20
|
+
/** The version's own fields, plus the scope field when the definition has a tenant field. */
|
|
21
|
+
readonly data: Record<string, unknown>
|
|
22
|
+
/** When the version comes into force. Defaults to the moment it is set; never earlier than the transaction's now(). */
|
|
23
|
+
readonly effectiveFrom?: Date
|
|
24
|
+
readonly userId?: string
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const MANAGED_FIELDS = ['effectiveFrom', 'version', 'setBy'] as const
|
|
28
|
+
|
|
29
|
+
function requireAppendOnly(definition: AnyDefinition, fnName: string): void {
|
|
30
|
+
if (!definition.$traits.includes('appendOnlyVersions')) {
|
|
31
|
+
throw new TemporalIntegrityError(
|
|
32
|
+
`temporal.${fnName}() requires an append-only-versions definition.\n\n` +
|
|
33
|
+
` "${definition.$name}" does not have the .appendOnlyVersions() trait.\n\n` +
|
|
34
|
+
` Add .appendOnlyVersions() to your definition:\n` +
|
|
35
|
+
` pipe.define('${definition.$name}', { ... }).appendOnlyVersions()\n`,
|
|
36
|
+
)
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export async function setVersion(
|
|
41
|
+
options: SetVersionOptions,
|
|
42
|
+
): Promise<Record<string, unknown>> {
|
|
43
|
+
const { tx, definition, data } = options
|
|
44
|
+
requireAppendOnly(definition, 'setVersion')
|
|
45
|
+
const table = definition.$name
|
|
46
|
+
|
|
47
|
+
for (const key of MANAGED_FIELDS) {
|
|
48
|
+
if (key in data) {
|
|
49
|
+
throw new TemporalIntegrityError(
|
|
50
|
+
`temporal.setVersion() sets "${key}" itself; remove it from data for "${table}".`,
|
|
51
|
+
{ table, field: key },
|
|
52
|
+
)
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const scopeField = appendOnlyScopeField(definition)
|
|
57
|
+
let scopeCondition: SQL = sql`TRUE`
|
|
58
|
+
const lockKeys = [`pipework.versions:${table}`]
|
|
59
|
+
if (scopeField !== undefined) {
|
|
60
|
+
const scopeValue = data[scopeField]
|
|
61
|
+
if (scopeValue === undefined || scopeValue === null) {
|
|
62
|
+
throw new TemporalIntegrityError(
|
|
63
|
+
`temporal.setVersion() on "${table}" needs data.${scopeField}: versions are numbered per ${scopeField}.`,
|
|
64
|
+
{ table, field: scopeField },
|
|
65
|
+
)
|
|
66
|
+
}
|
|
67
|
+
scopeCondition = sql`${identifier(camelToSnake(scopeField))} = ${scopeValue}`
|
|
68
|
+
lockKeys.push(String(scopeValue))
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
await advisoryLock(tx, ...lockKeys)
|
|
72
|
+
|
|
73
|
+
const latestRows = await tx.execute(
|
|
74
|
+
sql`SELECT version, effective_from FROM ${identifier(table)} WHERE ${scopeCondition} ORDER BY version DESC LIMIT 1`,
|
|
75
|
+
)
|
|
76
|
+
const latest = latestRows[0]
|
|
77
|
+
|
|
78
|
+
let effectiveFrom: Date | SQL
|
|
79
|
+
if (options.effectiveFrom === undefined) {
|
|
80
|
+
// Read after the lock, so it is later than every version already committed
|
|
81
|
+
// in scope; the transaction's now() may predate the latest of them.
|
|
82
|
+
effectiveFrom = sql`clock_timestamp()`
|
|
83
|
+
} else {
|
|
84
|
+
const iso = options.effectiveFrom.toISOString()
|
|
85
|
+
const nowRows = await tx.execute(sql`SELECT ${iso}::timestamptz < now() AS before_now, now() AS now`)
|
|
86
|
+
if (nowRows[0]!['before_now'] === true) {
|
|
87
|
+
throw new TemporalIntegrityError(
|
|
88
|
+
`Cannot set a version of "${table}" effective ${iso}: it is earlier than now (${toIso(nowRows[0]!['now'])}). ` +
|
|
89
|
+
`A version cannot be in force before the moment it is set.`,
|
|
90
|
+
{ table, effectiveFrom: iso },
|
|
91
|
+
)
|
|
92
|
+
}
|
|
93
|
+
if (latest !== undefined) {
|
|
94
|
+
const cmp = await tx.execute(sql`SELECT ${iso}::timestamptz <= ${latest['effective_from'] as Date}::timestamptz AS not_after`)
|
|
95
|
+
if (cmp[0]!['not_after'] === true) {
|
|
96
|
+
throw new TemporalIntegrityError(
|
|
97
|
+
`Cannot set a version of "${table}" effective ${iso}: it is not after the latest version's effectiveFrom ` +
|
|
98
|
+
`(${toIso(latest['effective_from'])}, version ${String(latest['version'])}).`,
|
|
99
|
+
{ table, effectiveFrom: iso },
|
|
100
|
+
)
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
effectiveFrom = options.effectiveFrom
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const version = latest === undefined ? 1 : Number(latest['version']) + 1
|
|
107
|
+
const rows = await insert(definition as never).values({
|
|
108
|
+
...data,
|
|
109
|
+
effectiveFrom,
|
|
110
|
+
version,
|
|
111
|
+
setBy: options.userId ?? null,
|
|
112
|
+
} as never).returning()
|
|
113
|
+
return (rows as Record<string, unknown>[])[0]!
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export async function versionAt(
|
|
117
|
+
db: DB,
|
|
118
|
+
definition: AnyDefinition,
|
|
119
|
+
t: Date,
|
|
120
|
+
where?: SQL,
|
|
121
|
+
): Promise<Record<string, unknown> | undefined> {
|
|
122
|
+
requireAppendOnly(definition, 'versionAt')
|
|
123
|
+
const columns = definition as unknown as { effectiveFrom: never }
|
|
124
|
+
const inForce = lte(columns.effectiveFrom, t)
|
|
125
|
+
const condition = where !== undefined ? and(inForce, where) : inForce
|
|
126
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Drizzle select chain requires concrete table generics
|
|
127
|
+
const rows: Record<string, unknown>[] = await (db as any)
|
|
128
|
+
.select().from(definition.table()).where(condition).orderBy(desc(columns.effectiveFrom)).limit(1)
|
|
129
|
+
return rows[0]
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function toIso(value: unknown): string {
|
|
133
|
+
return value instanceof Date ? value.toISOString() : String(value)
|
|
134
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { sql } from '../db/sql.js'
|
|
2
|
+
import { identifier, now } from '../db/expressions.js'
|
|
3
|
+
import type { TemporalOptions } from './types.js'
|
|
4
|
+
import { resolveTableName, assertAtMostOneActive } from './resolve.js'
|
|
5
|
+
|
|
6
|
+
export async function close(options: TemporalOptions): Promise<number> {
|
|
7
|
+
const { tx, where } = options
|
|
8
|
+
const table = resolveTableName(options.table)
|
|
9
|
+
|
|
10
|
+
const rows = await tx.execute(
|
|
11
|
+
sql`SELECT id FROM ${identifier(table)} WHERE ${where} AND effective_to IS NULL FOR UPDATE`,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
if (rows.length === 0) {
|
|
15
|
+
return 0
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
assertAtMostOneActive(rows, table)
|
|
19
|
+
|
|
20
|
+
const currentId = rows[0]!['id'] as string
|
|
21
|
+
|
|
22
|
+
await tx.execute(
|
|
23
|
+
sql`UPDATE ${identifier(table)} SET effective_to = ${now()} WHERE id = ${currentId}::uuid`,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
return 1
|
|
27
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { timestamp, integer, text } from '../db/col/index.js'
|
|
2
|
+
|
|
3
|
+
export function temporalColumns() {
|
|
4
|
+
return {
|
|
5
|
+
effectiveFrom: timestamp('effective_from', { withTimezone: true }).notNull().defaultNow(),
|
|
6
|
+
effectiveTo: timestamp('effective_to', { withTimezone: true }),
|
|
7
|
+
version: integer('version').notNull().default(1),
|
|
8
|
+
createdBy: text('created_by'),
|
|
9
|
+
}
|
|
10
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { DB, SQL } from '../db/types.js'
|
|
2
|
+
import type { DefinedTable, FieldRecord } from '../domain/types.js'
|
|
3
|
+
import type { ReviseOptions, ReviseResult } from './types.js'
|
|
4
|
+
import { atTime } from './query.js'
|
|
5
|
+
import { revise } from './revise.js'
|
|
6
|
+
import { TemporalIntegrityError } from './errors.js'
|
|
7
|
+
import { and } from '../query/index.js'
|
|
8
|
+
|
|
9
|
+
export interface DefinitionReviseOptions<TFields extends FieldRecord = FieldRecord> {
|
|
10
|
+
readonly tx: DB
|
|
11
|
+
readonly definition: DefinedTable<TFields>
|
|
12
|
+
readonly where: SQL
|
|
13
|
+
readonly data: Record<string, unknown>
|
|
14
|
+
readonly userId?: string
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function requireEffectiveDated<TFields extends FieldRecord>(definition: DefinedTable<TFields>, fnName: string): void {
|
|
18
|
+
if (!definition.$traits.includes('effectiveDated')) {
|
|
19
|
+
throw new TemporalIntegrityError(
|
|
20
|
+
`temporal.${fnName}() requires an effective-dated definition.\n\n` +
|
|
21
|
+
` "${definition.$name}" does not have the .effectiveDated() trait.\n\n` +
|
|
22
|
+
` Add .effectiveDated() to your definition:\n` +
|
|
23
|
+
` pipe.define('${definition.$name}', { ... }).effectiveDated()\n`,
|
|
24
|
+
)
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export async function asOf<TFields extends FieldRecord>(
|
|
29
|
+
db: DB,
|
|
30
|
+
definition: DefinedTable<TFields>,
|
|
31
|
+
date: Date,
|
|
32
|
+
where?: SQL,
|
|
33
|
+
): Promise<Record<string, unknown>[]> {
|
|
34
|
+
requireEffectiveDated(definition, 'asOf')
|
|
35
|
+
const table = definition.table()
|
|
36
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- atTime expects column accessors typed by HasTemporalColumns
|
|
37
|
+
const temporal = atTime(table as any, date)
|
|
38
|
+
const condition = where !== undefined ? and(temporal, where) : temporal
|
|
39
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Drizzle select chain requires concrete table generics
|
|
40
|
+
return await (db as any).select().from(table).where(condition)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export async function allVersions<TFields extends FieldRecord>(
|
|
44
|
+
db: DB,
|
|
45
|
+
definition: DefinedTable<TFields>,
|
|
46
|
+
where?: SQL,
|
|
47
|
+
): Promise<Record<string, unknown>[]> {
|
|
48
|
+
requireEffectiveDated(definition, 'allVersions')
|
|
49
|
+
const table = definition.table()
|
|
50
|
+
if (where !== undefined) {
|
|
51
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Drizzle select chain requires concrete table generics
|
|
52
|
+
return await (db as any).select().from(table).where(where)
|
|
53
|
+
}
|
|
54
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Drizzle select chain requires concrete table generics
|
|
55
|
+
return await (db as any).select().from(table)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export async function effectiveUpdate<TFields extends FieldRecord>(options: DefinitionReviseOptions<TFields>): Promise<ReviseResult> {
|
|
59
|
+
requireEffectiveDated(options.definition, 'effectiveUpdate')
|
|
60
|
+
const reviseOptions: ReviseOptions = {
|
|
61
|
+
tx: options.tx,
|
|
62
|
+
table: options.definition.$name,
|
|
63
|
+
where: options.where,
|
|
64
|
+
data: options.data,
|
|
65
|
+
}
|
|
66
|
+
if (options.userId !== undefined) {
|
|
67
|
+
return revise({ ...reviseOptions, userId: options.userId })
|
|
68
|
+
}
|
|
69
|
+
return revise(reviseOptions)
|
|
70
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { PipeworkError } from '../../core/errors/base.js'
|
|
2
|
+
|
|
3
|
+
export class TemporalIntegrityError extends PipeworkError {
|
|
4
|
+
readonly code = 'TEMPORAL_INTEGRITY' as const
|
|
5
|
+
readonly httpStatus = 409 as const
|
|
6
|
+
|
|
7
|
+
constructor(message: string, details: Record<string, unknown> = {}) {
|
|
8
|
+
super(message, details)
|
|
9
|
+
}
|
|
10
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { sql } from '../db/sql.js'
|
|
2
|
+
import { identifier } from '../db/expressions.js'
|
|
3
|
+
import type { TemporalOptions, GetCurrentResult } from './types.js'
|
|
4
|
+
import { resolveTableName, assertAtMostOneActive } from './resolve.js'
|
|
5
|
+
|
|
6
|
+
export async function getCurrent(options: TemporalOptions): Promise<GetCurrentResult | null> {
|
|
7
|
+
const { tx, where } = options
|
|
8
|
+
const table = resolveTableName(options.table)
|
|
9
|
+
|
|
10
|
+
const rows = await tx.execute(
|
|
11
|
+
sql`SELECT id::text, version FROM ${identifier(table)} WHERE ${where} AND effective_to IS NULL`,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
if (rows.length === 0) {
|
|
15
|
+
return null
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
assertAtMostOneActive(rows, table)
|
|
19
|
+
|
|
20
|
+
const row = rows[0]!
|
|
21
|
+
return { id: row['id'] as string, version: row['version'] as number }
|
|
22
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { temporalColumns } from './columns.js'
|
|
2
|
+
export { atTime, current, between } from './query.js'
|
|
3
|
+
export { revise } from './revise.js'
|
|
4
|
+
export { close } from './close.js'
|
|
5
|
+
export { getCurrent } from './get-current.js'
|
|
6
|
+
export { TemporalIntegrityError } from './errors.js'
|
|
7
|
+
export { asOf, allVersions, effectiveUpdate, type DefinitionReviseOptions } from './definition-queries.js'
|
|
8
|
+
export type { TemporalOptions, ReviseOptions, ReviseResult, GetCurrentResult } from './types.js'
|
|
9
|
+
export { setVersion, versionAt, type SetVersionOptions } from './append-only.js'
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { temporalColumns } from './columns.js'
|
|
2
|
+
import { revise } from './revise.js'
|
|
3
|
+
import { close } from './close.js'
|
|
4
|
+
import { getCurrent } from './get-current.js'
|
|
5
|
+
import { atTime, between } from './query.js'
|
|
6
|
+
import { TemporalIntegrityError } from './errors.js'
|
|
7
|
+
import { setVersion, versionAt } from './append-only.js'
|
|
8
|
+
import { asOf, allVersions, effectiveUpdate } from './definition-queries.js'
|
|
9
|
+
|
|
10
|
+
/** Temporal (bitemporal) data namespace — version history with effective date ranges. */
|
|
11
|
+
export const temporal = {
|
|
12
|
+
/** Returns the standard temporal column definitions (effectiveFrom, effectiveTo, version, createdBy) for table schemas. */
|
|
13
|
+
columns: temporalColumns,
|
|
14
|
+
/** Creates a new version of a temporal record, closing the previous version's effectiveTo. */
|
|
15
|
+
revise,
|
|
16
|
+
/** Closes a temporal record by setting effectiveTo to now. Returns number of rows affected. */
|
|
17
|
+
close,
|
|
18
|
+
/** Fetches the currently-effective version of a temporal record (effectiveTo IS NULL). */
|
|
19
|
+
current: getCurrent,
|
|
20
|
+
/** Returns a SQL condition for records effective at a specific point in time. */
|
|
21
|
+
atTime,
|
|
22
|
+
/** Returns a SQL condition for records overlapping a date range. */
|
|
23
|
+
between,
|
|
24
|
+
/** Thrown when a temporal operation would violate version continuity. */
|
|
25
|
+
TemporalIntegrityError,
|
|
26
|
+
/** Query records effective at a specific date, using a domain definition with .effectiveDated() trait. */
|
|
27
|
+
asOf,
|
|
28
|
+
/** Query all versions of temporal records (no date filter). */
|
|
29
|
+
allVersions,
|
|
30
|
+
/** Close the current version and create a new one with updated data. */
|
|
31
|
+
effectiveUpdate,
|
|
32
|
+
/** Append the next version of an append-only-versions definition: serialised per scope, numbered, never back-dated, audited. */
|
|
33
|
+
setVersion,
|
|
34
|
+
/** The version in force at a moment: the row with the greatest effectiveFrom at or before it. */
|
|
35
|
+
versionAt,
|
|
36
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { and, lte, gt, isNull, or } from '../query/index.js'
|
|
2
|
+
import type { SQL, Column } from '../db/types.js'
|
|
3
|
+
|
|
4
|
+
interface HasTemporalColumns {
|
|
5
|
+
effectiveFrom: Column
|
|
6
|
+
effectiveTo: Column
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function atTime<T extends HasTemporalColumns>(table: T, asOf: Date): SQL {
|
|
10
|
+
return and(
|
|
11
|
+
lte(table.effectiveFrom, asOf),
|
|
12
|
+
or(isNull(table.effectiveTo), gt(table.effectiveTo, asOf)),
|
|
13
|
+
)!
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function current<T extends HasTemporalColumns>(table: T): SQL {
|
|
17
|
+
return isNull(table.effectiveTo)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function between<T extends HasTemporalColumns>(table: T, from: Date, to: Date): SQL {
|
|
21
|
+
return and(
|
|
22
|
+
lte(table.effectiveFrom, to),
|
|
23
|
+
or(isNull(table.effectiveTo), gt(table.effectiveTo, from)),
|
|
24
|
+
)!
|
|
25
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { getTableName } from '../query/index.js'
|
|
2
|
+
import type { Table } from '../db/types.js'
|
|
3
|
+
import { TemporalIntegrityError } from './errors.js'
|
|
4
|
+
|
|
5
|
+
export function resolveTableName(table: string | Table): string {
|
|
6
|
+
return typeof table === 'string' ? table : getTableName(table)
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function assertAtMostOneActive(rows: readonly unknown[], table: string): void {
|
|
10
|
+
if (rows.length > 1) {
|
|
11
|
+
throw new TemporalIntegrityError(
|
|
12
|
+
`Multiple open versions detected in "${table}" — data integrity violation. ` +
|
|
13
|
+
`Expected at most one row with effective_to IS NULL, found ${rows.length}.`,
|
|
14
|
+
{ table, count: rows.length },
|
|
15
|
+
)
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { sql } from '../db/sql.js'
|
|
2
|
+
import { identifier, now } from '../db/expressions.js'
|
|
3
|
+
import type { ReviseOptions, ReviseResult } from './types.js'
|
|
4
|
+
import { TemporalIntegrityError } from './errors.js'
|
|
5
|
+
import { assertValidIdentifier } from '../db/identifiers.js'
|
|
6
|
+
import { resolveTableName, assertAtMostOneActive } from './resolve.js'
|
|
7
|
+
|
|
8
|
+
export async function revise(options: ReviseOptions): Promise<ReviseResult> {
|
|
9
|
+
const { tx, where, data, userId } = options
|
|
10
|
+
const table = resolveTableName(options.table)
|
|
11
|
+
|
|
12
|
+
const current = await tx.execute(
|
|
13
|
+
sql`SELECT id, version FROM ${identifier(table)} WHERE ${where} AND effective_to IS NULL FOR UPDATE`,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
if (current.length === 0) {
|
|
17
|
+
throw new TemporalIntegrityError(
|
|
18
|
+
`No current version found. Cannot revise a record in "${table}" — ` +
|
|
19
|
+
`ensure the entity exists and has an open version (effective_to IS NULL).`,
|
|
20
|
+
{ table },
|
|
21
|
+
)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
assertAtMostOneActive(current, table)
|
|
25
|
+
|
|
26
|
+
const currentRow = current[0]!
|
|
27
|
+
const currentId = currentRow['id'] as string
|
|
28
|
+
const currentVersion = currentRow['version'] as number
|
|
29
|
+
|
|
30
|
+
await tx.execute(
|
|
31
|
+
sql`UPDATE ${identifier(table)} SET effective_to = ${now()} WHERE id = ${currentId}::uuid`,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
const columns = Object.keys(data)
|
|
35
|
+
const values = Object.values(data)
|
|
36
|
+
|
|
37
|
+
for (const col of columns) {
|
|
38
|
+
assertValidIdentifier(col, 'column')
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const allColumns = [...columns, 'effective_from', 'effective_to', 'version', 'created_by']
|
|
42
|
+
const colIdentifiers = allColumns.map(c => identifier(c))
|
|
43
|
+
|
|
44
|
+
const serializedValues = values.map((v) => {
|
|
45
|
+
if (v === null || v === undefined) return null
|
|
46
|
+
if (typeof v === 'object') return JSON.stringify(v)
|
|
47
|
+
return v
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
const valueParts = [
|
|
51
|
+
...serializedValues.map((v) => sql`${v ?? null}`),
|
|
52
|
+
now(),
|
|
53
|
+
sql`NULL`,
|
|
54
|
+
sql`${currentVersion + 1}`,
|
|
55
|
+
sql`${userId ?? null}`,
|
|
56
|
+
]
|
|
57
|
+
|
|
58
|
+
const valuesList = sql.join(valueParts, sql`, `)
|
|
59
|
+
|
|
60
|
+
assertValidIdentifier(table, 'table')
|
|
61
|
+
const result = await tx.execute(
|
|
62
|
+
sql`INSERT INTO ${identifier(table)} (${sql.join(colIdentifiers, sql`, `)}) VALUES (${valuesList}) RETURNING id::text, version`,
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
const row = result[0]!
|
|
66
|
+
return { id: row['id'] as string, version: row['version'] as number }
|
|
67
|
+
}
|