pipework 0.252.0 → 0.253.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -0
- package/dist/infra/support/handle.d.ts +8 -1
- package/dist/infra/support/handle.d.ts.map +1 -1
- package/dist/infra/support/handle.js +9 -2
- package/dist/infra/support/handle.js.map +1 -1
- package/dist/infra/support/namespace.d.ts.map +1 -1
- package/package.json +4 -3
- package/src/async/jobs/abortable-delay.ts +18 -0
- package/src/async/jobs/claims.ts +52 -0
- package/src/async/jobs/cron.ts +179 -0
- package/src/async/jobs/errors.ts +56 -0
- package/src/async/jobs/exclusion.ts +117 -0
- package/src/async/jobs/execute.ts +219 -0
- package/src/async/jobs/index.ts +10 -0
- package/src/async/jobs/listener.ts +135 -0
- package/src/async/jobs/namespace.ts +20 -0
- package/src/async/jobs/queue-door.ts +44 -0
- package/src/async/jobs/queue.ts +1012 -0
- package/src/async/jobs/run-script.ts +67 -0
- package/src/async/jobs/scheduler.ts +218 -0
- package/src/async/jobs/schedules-table.ts +37 -0
- package/src/async/jobs/serialize-error.ts +48 -0
- package/src/async/jobs/table.ts +191 -0
- package/src/async/pipeline/define.ts +53 -0
- package/src/async/pipeline/errors.ts +35 -0
- package/src/async/pipeline/execute.ts +53 -0
- package/src/async/pipeline/index.ts +13 -0
- package/src/async/pipeline/namespace.ts +12 -0
- package/src/async/pipeline/resume.ts +67 -0
- package/src/async/pipeline/run-steps.ts +98 -0
- package/src/async/pipeline/types.ts +57 -0
- package/src/async/state-machine/define.ts +64 -0
- package/src/async/state-machine/errors.ts +29 -0
- package/src/async/state-machine/index.ts +3 -0
- package/src/async/state-machine/namespace.ts +12 -0
- package/src/async/state-machine/transition.ts +90 -0
- package/src/async/state-machine/types.ts +37 -0
- package/src/async/steps/errors.ts +31 -0
- package/src/async/steps/graph.ts +1214 -0
- package/src/async/steps/hash.ts +40 -0
- package/src/async/steps/index.ts +7 -0
- package/src/async/steps/namespace.ts +7 -0
- package/src/async/steps/table.ts +97 -0
- package/src/async/steps/types.ts +318 -0
- package/src/audit/chain/index.ts +9 -0
- package/src/audit/chain/install.ts +113 -0
- package/src/audit/chain/retire.ts +52 -0
- package/src/audit/chain/verify.ts +257 -0
- package/src/audit/query/cursor.ts +37 -0
- package/src/audit/query/index.ts +9 -0
- package/src/audit/query/query.ts +223 -0
- package/src/audit/schema/audit-record-fields.ts +68 -0
- package/src/audit/schema/audit-record.ts +23 -0
- package/src/auth/auth/audit.ts +31 -0
- package/src/auth/auth/chain.ts +84 -0
- package/src/auth/auth/cookie.ts +136 -0
- package/src/auth/auth/email-links.ts +311 -0
- package/src/auth/auth/guard.ts +128 -0
- package/src/auth/auth/index.ts +18 -0
- package/src/auth/auth/multi-org.ts +337 -0
- package/src/auth/auth/namespace.ts +34 -0
- package/src/auth/auth/parse-expiry.ts +13 -0
- package/src/auth/auth/passwords.ts +110 -0
- package/src/auth/auth/schema.ts +110 -0
- package/src/auth/auth/sessions.ts +565 -0
- package/src/auth/auth/sign-in.ts +156 -0
- package/src/auth/auth/signing-keys.ts +69 -0
- package/src/auth/auth/types.ts +26 -0
- package/src/auth/auth/uuid.ts +10 -0
- package/src/auth/rbac/cache.ts +55 -0
- package/src/auth/rbac/check.ts +30 -0
- package/src/auth/rbac/create.ts +35 -0
- package/src/auth/rbac/index.ts +5 -0
- package/src/auth/rbac/middleware.ts +20 -0
- package/src/auth/rbac/namespace.ts +15 -0
- package/src/auth/rbac/resolve.ts +31 -0
- package/src/auth/rbac/schema.sql +31 -0
- package/src/auth/rbac/seed.ts +67 -0
- package/src/auth/rbac/tables.ts +32 -0
- package/src/auth/rbac/types.ts +65 -0
- package/src/auth/tenant/extract.ts +33 -0
- package/src/auth/tenant/in-tenant.ts +49 -0
- package/src/auth/tenant/index.ts +7 -0
- package/src/auth/tenant/namespace.ts +23 -0
- package/src/auth/tenant/propagate.ts +20 -0
- package/src/auth/tenant/rls.ts +120 -0
- package/src/auth/tenant/scope.ts +33 -0
- package/src/auth/tenant/scoped-db.ts +581 -0
- package/src/auth/tenant/types.ts +14 -0
- package/src/auth/tenant/validate.ts +27 -0
- package/src/cli/commands/add.ts +158 -0
- package/src/cli/commands/alerts.ts +98 -0
- package/src/cli/commands/check.ts +1418 -0
- package/src/cli/commands/codemod.ts +78 -0
- package/src/cli/commands/db.ts +356 -0
- package/src/cli/commands/dev.ts +101 -0
- package/src/cli/commands/generate.ts +75 -0
- package/src/cli/commands/init.ts +36 -0
- package/src/cli/commands/install.ts +28 -0
- package/src/cli/commands/lint.ts +62 -0
- package/src/cli/commands/migrate.ts +132 -0
- package/src/cli/commands/openapi.ts +63 -0
- package/src/cli/commands/remove.ts +72 -0
- package/src/cli/commands/run.ts +50 -0
- package/src/cli/commands/scope.ts +54 -0
- package/src/cli/commands/serve.ts +68 -0
- package/src/cli/commands/shard.ts +31 -0
- package/src/cli/commands/shared/config-pipework-version.ts +28 -0
- package/src/cli/commands/shared/find-config.ts +22 -0
- package/src/cli/commands/shared/load-config.ts +35 -0
- package/src/cli/commands/shared/pnpm-install.ts +95 -0
- package/src/cli/commands/shared/report-removed.ts +8 -0
- package/src/cli/commands/sync.ts +32 -0
- package/src/cli/commands/test.ts +307 -0
- package/src/cli/commands/upgrade.ts +48 -0
- package/src/cli/dev-bootstrap.ts +24 -0
- package/src/cli/errors.ts +10 -0
- package/src/cli/index.ts +303 -0
- package/src/cli/output.ts +17 -0
- package/src/cli/port.ts +15 -0
- package/src/cli/surface-kinds.ts +20 -0
- package/src/client/index.ts +4 -0
- package/src/client/poll.ts +130 -0
- package/src/client/report-fault.ts +119 -0
- package/src/core/config/app-pool.ts +19 -0
- package/src/core/config/aws-env.ts +29 -0
- package/src/core/config/base-dir.ts +24 -0
- package/src/core/config/discover.ts +64 -0
- package/src/core/config/env-resolve.ts +87 -0
- package/src/core/config/env-types.ts +116 -0
- package/src/core/config/env.ts +55 -0
- package/src/core/config/errors.ts +9 -0
- package/src/core/config/import-config.ts +40 -0
- package/src/core/config/index.ts +8 -0
- package/src/core/config/load.ts +417 -0
- package/src/core/config/namespace.ts +14 -0
- package/src/core/config/resolve.ts +195 -0
- package/src/core/config/schema.ts +488 -0
- package/src/core/config/statement-observer.ts +48 -0
- package/src/core/config/ts-inline-hooks.ts +75 -0
- package/src/core/config/ts-register.ts +4 -0
- package/src/core/config/ts-resolve-hooks.ts +101 -0
- package/src/core/errors/base.ts +49 -0
- package/src/core/errors/http.ts +146 -0
- package/src/core/errors/index.ts +2 -0
- package/src/core/errors/namespace.ts +31 -0
- package/src/core/lifecycle/drain.ts +33 -0
- package/src/core/lifecycle/health.ts +72 -0
- package/src/core/lifecycle/index.ts +4 -0
- package/src/core/lifecycle/namespace.ts +7 -0
- package/src/core/lifecycle/startup.ts +102 -0
- package/src/core/lifecycle/types.ts +36 -0
- package/src/core/logging/context.ts +60 -0
- package/src/core/logging/error-chain.ts +68 -0
- package/src/core/logging/index.ts +5 -0
- package/src/core/logging/logger.ts +65 -0
- package/src/core/logging/namespace.ts +16 -0
- package/src/core/logging/proxy.ts +22 -0
- package/src/core/logging/redact-match.ts +24 -0
- package/src/core/logging/types.ts +47 -0
- package/src/core/pipework.ts +566 -0
- package/src/core/runtime/broker.ts +69 -0
- package/src/core/surface/http.ts +133 -0
- package/src/core/surface/index.ts +11 -0
- package/src/core/surface/job-timeout.ts +40 -0
- package/src/core/surface/namespace.ts +13 -0
- package/src/core/surface/script.ts +33 -0
- package/src/core/surface/start.ts +142 -0
- package/src/core/surface/types.ts +225 -0
- package/src/core/surface/validate.ts +88 -0
- package/src/core/surface/worker.ts +528 -0
- package/src/core/validation/connectivity.ts +49 -0
- package/src/core/validation/domain.ts +99 -0
- package/src/core/validation/index.ts +5 -0
- package/src/core/validation/queue-tables.ts +312 -0
- package/src/core/validation/rls-role.ts +138 -0
- package/src/core/validation/startup.ts +51 -0
- package/src/core/validation/surfaces.ts +65 -0
- package/src/core/validation/types.ts +25 -0
- package/src/data/db/advisory-lock.ts +55 -0
- package/src/data/db/aggregates.ts +172 -0
- package/src/data/db/array-elements.ts +111 -0
- package/src/data/db/bulk-set.ts +53 -0
- package/src/data/db/col/index.ts +14 -0
- package/src/data/db/composed.ts +53 -0
- package/src/data/db/db.ts +276 -0
- package/src/data/db/excluded.ts +16 -0
- package/src/data/db/expressions.ts +473 -0
- package/src/data/db/filter.ts +90 -0
- package/src/data/db/function-source.ts +114 -0
- package/src/data/db/identifiers.ts +16 -0
- package/src/data/db/idx/index.ts +9 -0
- package/src/data/db/in-list.ts +49 -0
- package/src/data/db/index.ts +9 -0
- package/src/data/db/insert-split.ts +137 -0
- package/src/data/db/instrumentation.ts +82 -0
- package/src/data/db/maintenance.ts +28 -0
- package/src/data/db/namespace.ts +107 -0
- package/src/data/db/owner-labels.ts +9 -0
- package/src/data/db/pool.ts +187 -0
- package/src/data/db/query-label.ts +13 -0
- package/src/data/db/query-watch.ts +116 -0
- package/src/data/db/raw-client-warning.ts +37 -0
- package/src/data/db/readonly.ts +23 -0
- package/src/data/db/reservation.ts +151 -0
- package/src/data/db/schema-features.ts +14 -0
- package/src/data/db/serializable.ts +83 -0
- package/src/data/db/sql.ts +1 -0
- package/src/data/db/timeouts.ts +76 -0
- package/src/data/db/types.ts +18 -0
- package/src/data/db/unbound.ts +33 -0
- package/src/data/db/values.ts +29 -0
- package/src/data/domain/brand.ts +15 -0
- package/src/data/domain/build-factory.ts +193 -0
- package/src/data/domain/build-schema.ts +161 -0
- package/src/data/domain/build-table.ts +355 -0
- package/src/data/domain/define.ts +239 -0
- package/src/data/domain/external-registry.ts +34 -0
- package/src/data/domain/external.ts +77 -0
- package/src/data/domain/field.ts +279 -0
- package/src/data/domain/index.ts +13 -0
- package/src/data/domain/naming.ts +6 -0
- package/src/data/domain/projections.ts +57 -0
- package/src/data/domain/trace-safe-projection.ts +31 -0
- package/src/data/domain/traits.ts +108 -0
- package/src/data/domain/types.ts +289 -0
- package/src/data/jsonb/composed.ts +18 -0
- package/src/data/jsonb/index.ts +1 -0
- package/src/data/jsonb/namespace.ts +30 -0
- package/src/data/jsonb/query.ts +86 -0
- package/src/data/migrate/append-only-generate.ts +47 -0
- package/src/data/migrate/applied.ts +47 -0
- package/src/data/migrate/apply.ts +309 -0
- package/src/data/migrate/check.ts +119 -0
- package/src/data/migrate/diff.ts +357 -0
- package/src/data/migrate/door-generate.ts +130 -0
- package/src/data/migrate/framework-rls.ts +58 -0
- package/src/data/migrate/generate.ts +201 -0
- package/src/data/migrate/history.ts +215 -0
- package/src/data/migrate/index.ts +9 -0
- package/src/data/migrate/internal-definitions.ts +175 -0
- package/src/data/migrate/post-process.ts +73 -0
- package/src/data/migrate/pragma.ts +73 -0
- package/src/data/migrate/provision-framework.ts +147 -0
- package/src/data/migrate/rls-generate.ts +61 -0
- package/src/data/migrate/run.ts +124 -0
- package/src/data/migrate/safety.ts +167 -0
- package/src/data/migrate/snapshot.ts +368 -0
- package/src/data/migrate/sql-emit.ts +392 -0
- package/src/data/migrate/statements.ts +312 -0
- package/src/data/query/alias.ts +126 -0
- package/src/data/query/batch.ts +8 -0
- package/src/data/query/cache/core/cache.ts +78 -0
- package/src/data/query/cache/core/index.ts +1 -0
- package/src/data/query/cache/core/types.ts +29 -0
- package/src/data/query/cache/readme.md +230 -0
- package/src/data/query/casing.ts +76 -0
- package/src/data/query/collation.ts +11 -0
- package/src/data/query/column-builder.ts +370 -0
- package/src/data/query/column.ts +150 -0
- package/src/data/query/entity.ts +42 -0
- package/src/data/query/errors.ts +33 -0
- package/src/data/query/index.ts +14 -0
- package/src/data/query/logger.ts +47 -0
- package/src/data/query/operations.ts +45 -0
- package/src/data/query/pg-core/alias.ts +12 -0
- package/src/data/query/pg-core/checks.ts +32 -0
- package/src/data/query/pg-core/columns/all.ts +71 -0
- package/src/data/query/pg-core/columns/bigint.ts +110 -0
- package/src/data/query/pg-core/columns/bigserial.ts +128 -0
- package/src/data/query/pg-core/columns/boolean.ts +43 -0
- package/src/data/query/pg-core/columns/char.ts +85 -0
- package/src/data/query/pg-core/columns/cidr.ts +43 -0
- package/src/data/query/pg-core/columns/common.ts +366 -0
- package/src/data/query/pg-core/columns/custom.ts +232 -0
- package/src/data/query/pg-core/columns/date.common.ts +15 -0
- package/src/data/query/pg-core/columns/date.ts +112 -0
- package/src/data/query/pg-core/columns/double-precision.ts +55 -0
- package/src/data/query/pg-core/columns/enum.ts +201 -0
- package/src/data/query/pg-core/columns/index.ts +35 -0
- package/src/data/query/pg-core/columns/inet.ts +43 -0
- package/src/data/query/pg-core/columns/int.common.ts +57 -0
- package/src/data/query/pg-core/columns/integer.ts +53 -0
- package/src/data/query/pg-core/columns/interval.ts +83 -0
- package/src/data/query/pg-core/columns/json.ts +58 -0
- package/src/data/query/pg-core/columns/jsonb.ts +56 -0
- package/src/data/query/pg-core/columns/line.ts +117 -0
- package/src/data/query/pg-core/columns/macaddr.ts +43 -0
- package/src/data/query/pg-core/columns/macaddr8.ts +43 -0
- package/src/data/query/pg-core/columns/numeric.ts +227 -0
- package/src/data/query/pg-core/columns/point.ts +127 -0
- package/src/data/query/pg-core/columns/postgis_extension/geometry.ts +119 -0
- package/src/data/query/pg-core/columns/postgis_extension/utils.ts +47 -0
- package/src/data/query/pg-core/columns/real.ts +58 -0
- package/src/data/query/pg-core/columns/serial.ts +55 -0
- package/src/data/query/pg-core/columns/smallint.ts +53 -0
- package/src/data/query/pg-core/columns/smallserial.ts +60 -0
- package/src/data/query/pg-core/columns/text.ts +67 -0
- package/src/data/query/pg-core/columns/time.ts +72 -0
- package/src/data/query/pg-core/columns/timestamp.ts +163 -0
- package/src/data/query/pg-core/columns/uuid.ts +51 -0
- package/src/data/query/pg-core/columns/varchar.ts +89 -0
- package/src/data/query/pg-core/columns/vector_extension/bit.ts +68 -0
- package/src/data/query/pg-core/columns/vector_extension/halfvec.ts +80 -0
- package/src/data/query/pg-core/columns/vector_extension/sparsevec.ts +67 -0
- package/src/data/query/pg-core/columns/vector_extension/vector.ts +80 -0
- package/src/data/query/pg-core/db.ts +674 -0
- package/src/data/query/pg-core/dialect.ts +1404 -0
- package/src/data/query/pg-core/expressions.ts +25 -0
- package/src/data/query/pg-core/foreign-keys.ts +130 -0
- package/src/data/query/pg-core/index.ts +21 -0
- package/src/data/query/pg-core/indexes.ts +276 -0
- package/src/data/query/pg-core/policies.ts +56 -0
- package/src/data/query/pg-core/primary-keys.ts +62 -0
- package/src/data/query/pg-core/query-builders/count.ts +86 -0
- package/src/data/query/pg-core/query-builders/delete.ts +293 -0
- package/src/data/query/pg-core/query-builders/index.ts +7 -0
- package/src/data/query/pg-core/query-builders/insert.ts +452 -0
- package/src/data/query/pg-core/query-builders/query-builder.ts +123 -0
- package/src/data/query/pg-core/query-builders/query.ts +157 -0
- package/src/data/query/pg-core/query-builders/raw.ts +49 -0
- package/src/data/query/pg-core/query-builders/refresh-materialized-view.ts +108 -0
- package/src/data/query/pg-core/query-builders/select.ts +1346 -0
- package/src/data/query/pg-core/query-builders/select.types.ts +437 -0
- package/src/data/query/pg-core/query-builders/update.ts +626 -0
- package/src/data/query/pg-core/roles.ts +41 -0
- package/src/data/query/pg-core/schema.ts +72 -0
- package/src/data/query/pg-core/sequence.ts +41 -0
- package/src/data/query/pg-core/session.ts +292 -0
- package/src/data/query/pg-core/subquery.ts +46 -0
- package/src/data/query/pg-core/table.ts +252 -0
- package/src/data/query/pg-core/unique-constraint.ts +73 -0
- package/src/data/query/pg-core/utils/array.ts +95 -0
- package/src/data/query/pg-core/utils/index.ts +1 -0
- package/src/data/query/pg-core/utils.ts +104 -0
- package/src/data/query/pg-core/view-base.ts +14 -0
- package/src/data/query/pg-core/view-common.ts +1 -0
- package/src/data/query/pg-core/view.ts +435 -0
- package/src/data/query/pg-core/with-builder.ts +41 -0
- package/src/data/query/postgres-js/README.md +46 -0
- package/src/data/query/postgres-js/array-parser.ts +45 -0
- package/src/data/query/postgres-js/driver-error-boundary.ts +149 -0
- package/src/data/query/postgres-js/driver.ts +163 -0
- package/src/data/query/postgres-js/index.ts +2 -0
- package/src/data/query/postgres-js/session.ts +234 -0
- package/src/data/query/primary-key.ts +11 -0
- package/src/data/query/query-builders/query-builder.ts +19 -0
- package/src/data/query/query-builders/select.types.ts +175 -0
- package/src/data/query/query-promise.ts +35 -0
- package/src/data/query/relations.ts +725 -0
- package/src/data/query/runnable-query.ts +12 -0
- package/src/data/query/selection-proxy.ts +121 -0
- package/src/data/query/session.ts +8 -0
- package/src/data/query/sql/expressions/conditions.ts +741 -0
- package/src/data/query/sql/expressions/index.ts +2 -0
- package/src/data/query/sql/expressions/select.ts +45 -0
- package/src/data/query/sql/functions/aggregate.ts +129 -0
- package/src/data/query/sql/functions/index.ts +2 -0
- package/src/data/query/sql/functions/vector.ts +178 -0
- package/src/data/query/sql/index.ts +3 -0
- package/src/data/query/sql/sql.ts +794 -0
- package/src/data/query/subquery.ts +53 -0
- package/src/data/query/table.ts +208 -0
- package/src/data/query/table.utils.ts +2 -0
- package/src/data/query/tracing-utils.ts +3 -0
- package/src/data/query/tracing.ts +11 -0
- package/src/data/query/utils.ts +357 -0
- package/src/data/query/version.ts +4 -0
- package/src/data/query/view-common.ts +1 -0
- package/src/data/schema/branded.ts +13 -0
- package/src/data/schema/generated/coerce.ts +5 -0
- package/src/data/schema/generated/combinator.ts +6 -0
- package/src/data/schema/generated/encoding.ts +5 -0
- package/src/data/schema/generated/error.ts +6 -0
- package/src/data/schema/generated/format.ts +5 -0
- package/src/data/schema/generated/type-constructor.ts +10 -0
- package/src/data/schema/generated/type.ts +6 -0
- package/src/data/schema/index.ts +6 -0
- package/src/data/schema/json-schema.ts +33 -0
- package/src/data/schema/namespace.ts +55 -0
- package/src/data/schema/parse.ts +39 -0
- package/src/data/schema/types.ts +39 -0
- package/src/data/temporal/append-only.ts +134 -0
- package/src/data/temporal/close.ts +27 -0
- package/src/data/temporal/columns.ts +10 -0
- package/src/data/temporal/definition-queries.ts +70 -0
- package/src/data/temporal/errors.ts +10 -0
- package/src/data/temporal/get-current.ts +22 -0
- package/src/data/temporal/index.ts +9 -0
- package/src/data/temporal/namespace.ts +36 -0
- package/src/data/temporal/query.ts +25 -0
- package/src/data/temporal/resolve.ts +17 -0
- package/src/data/temporal/revise.ts +67 -0
- package/src/data/temporal/types.ts +22 -0
- package/src/data/valve/artifact.ts +33 -0
- package/src/data/valve/contract-diff.ts +21 -0
- package/src/data/valve/cross.ts +57 -0
- package/src/data/valve/crossing-ledger.ts +50 -0
- package/src/data/valve/decision.ts +44 -0
- package/src/data/valve/durable-crossing.ts +52 -0
- package/src/data/valve/execute-crossing.ts +77 -0
- package/src/data/valve/faucet.ts +56 -0
- package/src/data/valve/gate.ts +114 -0
- package/src/data/valve/graph.ts +77 -0
- package/src/data/valve/index.ts +53 -0
- package/src/data/valve/namespace.ts +57 -0
- package/src/data/valve/registry.ts +50 -0
- package/src/data/valve/schema/valve-crossing-fields.ts +23 -0
- package/src/data/valve/schema/valve-crossing.ts +9 -0
- package/src/data/valve/supply.ts +154 -0
- package/src/data/valve/types.ts +143 -0
- package/src/data/vector/columns.ts +27 -0
- package/src/data/vector/index.ts +4 -0
- package/src/data/vector/namespace.ts +33 -0
- package/src/data/vector/query.ts +85 -0
- package/src/data/vector/validate.ts +16 -0
- package/src/data/write/audit/actor.ts +15 -0
- package/src/data/write/audit/audit-columns.ts +18 -0
- package/src/data/write/audit/audit-select.ts +111 -0
- package/src/data/write/audit/bound-transaction.ts +14 -0
- package/src/data/write/audit/cte-compile.ts +32 -0
- package/src/data/write/audit/decode-returning.ts +47 -0
- package/src/data/write/audit/final-select.ts +43 -0
- package/src/data/write/audit/infra-audit.ts +36 -0
- package/src/data/write/audit/primary-key.ts +65 -0
- package/src/data/write/audit/projection.ts +41 -0
- package/src/data/write/audit/read-touches.ts +86 -0
- package/src/data/write/audit/touch-match.ts +78 -0
- package/src/data/write/audit/verb-delete.ts +62 -0
- package/src/data/write/audit/verb-insert.ts +36 -0
- package/src/data/write/audit/verb-select.ts +47 -0
- package/src/data/write/audit/verb-update.ts +60 -0
- package/src/data/write/audit/verb-upsert.ts +104 -0
- package/src/data/write/conflict.ts +43 -0
- package/src/data/write/delete.ts +38 -0
- package/src/data/write/execute.ts +243 -0
- package/src/data/write/index.ts +11 -0
- package/src/data/write/insert.ts +63 -0
- package/src/data/write/state.ts +43 -0
- package/src/data/write/update.ts +54 -0
- package/src/data/write/upsert.ts +58 -0
- package/src/data/write/values.ts +19 -0
- package/src/index.ts +230 -0
- package/src/infra/access-links/code.ts +70 -0
- package/src/infra/access-links/handle.ts +892 -0
- package/src/infra/access-links/index.ts +9 -0
- package/src/infra/access-links/namespace.ts +30 -0
- package/src/infra/access-links/person-check.ts +177 -0
- package/src/infra/access-links/schema.ts +71 -0
- package/src/infra/alerts/async-sources.ts +231 -0
- package/src/infra/alerts/audit.ts +36 -0
- package/src/infra/alerts/beat.ts +123 -0
- package/src/infra/alerts/client-faults.ts +111 -0
- package/src/infra/alerts/faults.ts +156 -0
- package/src/infra/alerts/handle.ts +778 -0
- package/src/infra/alerts/http.ts +192 -0
- package/src/infra/alerts/index.ts +12 -0
- package/src/infra/alerts/infra-sources.ts +180 -0
- package/src/infra/alerts/namespace.ts +81 -0
- package/src/infra/alerts/redact.ts +21 -0
- package/src/infra/alerts/request-faults.ts +41 -0
- package/src/infra/alerts/schema.ts +124 -0
- package/src/infra/alerts/sinks.ts +125 -0
- package/src/infra/alerts/source-options.ts +26 -0
- package/src/infra/alerts/sources.ts +503 -0
- package/src/infra/alerts/subsystem-sources.ts +137 -0
- package/src/infra/audit/emit.ts +87 -0
- package/src/infra/audit/index.ts +2 -0
- package/src/infra/audit/namespace.ts +22 -0
- package/src/infra/audit/schema.sql +35 -0
- package/src/infra/audit/types.ts +18 -0
- package/src/infra/aws/credentials.ts +147 -0
- package/src/infra/aws/sigv4.ts +140 -0
- package/src/infra/cache/create.ts +58 -0
- package/src/infra/cache/entry.ts +81 -0
- package/src/infra/cache/index.ts +4 -0
- package/src/infra/cache/namespace.ts +10 -0
- package/src/infra/cache/tenant.ts +93 -0
- package/src/infra/cache/types.ts +24 -0
- package/src/infra/crawl/audit.ts +35 -0
- package/src/infra/crawl/errors.ts +25 -0
- package/src/infra/crawl/handle.ts +693 -0
- package/src/infra/crawl/index.ts +9 -0
- package/src/infra/crawl/links.ts +160 -0
- package/src/infra/crawl/namespace.ts +36 -0
- package/src/infra/crawl/robots.ts +100 -0
- package/src/infra/crawl/schema.ts +89 -0
- package/src/infra/credit-line/handle.ts +883 -0
- package/src/infra/credit-line/index.ts +6 -0
- package/src/infra/credit-line/namespace.ts +7 -0
- package/src/infra/credit-line/schema.ts +96 -0
- package/src/infra/credits/handle.ts +706 -0
- package/src/infra/credits/index.ts +5 -0
- package/src/infra/credits/namespace.ts +7 -0
- package/src/infra/credits/schema.ts +83 -0
- package/src/infra/email/audit.ts +36 -0
- package/src/infra/email/inbound.ts +622 -0
- package/src/infra/email/index.ts +11 -0
- package/src/infra/email/mail.ts +517 -0
- package/src/infra/email/mime.ts +447 -0
- package/src/infra/email/namespace.ts +22 -0
- package/src/infra/email/providers.ts +111 -0
- package/src/infra/email/schema.ts +96 -0
- package/src/infra/email/sns.ts +200 -0
- package/src/infra/email/unsubscribe.ts +44 -0
- package/src/infra/fetch/address.ts +88 -0
- package/src/infra/fetch/connect-retry.ts +87 -0
- package/src/infra/fetch/handle.ts +329 -0
- package/src/infra/fetch/index.ts +7 -0
- package/src/infra/fetch/namespace.ts +70 -0
- package/src/infra/idempotency/index.ts +3 -0
- package/src/infra/idempotency/middleware.ts +87 -0
- package/src/infra/idempotency/store.ts +92 -0
- package/src/infra/idempotency/types.ts +30 -0
- package/src/infra/join-requests/handle.ts +312 -0
- package/src/infra/join-requests/index.ts +2 -0
- package/src/infra/join-requests/namespace.ts +7 -0
- package/src/infra/join-requests/schema.ts +33 -0
- package/src/infra/outbox/core.ts +165 -0
- package/src/infra/quota/errors.ts +32 -0
- package/src/infra/quota/handle.ts +215 -0
- package/src/infra/quota/index.ts +5 -0
- package/src/infra/quota/namespace.ts +7 -0
- package/src/infra/quota/schema.ts +15 -0
- package/src/infra/storage/backend.ts +34 -0
- package/src/infra/storage/handle.ts +513 -0
- package/src/infra/storage/index.ts +9 -0
- package/src/infra/storage/local.ts +104 -0
- package/src/infra/storage/namespace.ts +13 -0
- package/src/infra/storage/s3.ts +257 -0
- package/src/infra/storage/schema.ts +24 -0
- package/src/infra/support/handle.ts +869 -0
- package/src/infra/support/http.ts +229 -0
- package/src/infra/support/index.ts +7 -0
- package/src/infra/support/namespace.ts +12 -0
- package/src/infra/support/schema.ts +96 -0
- package/src/infra/webhook/inbound.ts +45 -0
- package/src/infra/webhook/index.ts +5 -0
- package/src/infra/webhook/namespace.ts +24 -0
- package/src/infra/webhook/outbound.ts +315 -0
- package/src/infra/webhook/sign.ts +32 -0
- package/src/infra/webhook/verify.ts +82 -0
- package/src/invariants/assert.ts +26 -0
- package/src/invariants/index.ts +1 -0
- package/src/lint/config.ts +56 -0
- package/src/lint/doctrine.ts +163 -0
- package/src/lint/include.ts +97 -0
- package/src/lint/index.ts +3 -0
- package/src/lint/preset.ts +10 -0
- package/src/lint/resolver.ts +218 -0
- package/src/lint/rules/no-reexports.ts +46 -0
- package/src/request/behavior/audited.ts +85 -0
- package/src/request/behavior/cached.ts +62 -0
- package/src/request/behavior/compose.ts +28 -0
- package/src/request/behavior/index.ts +5 -0
- package/src/request/behavior/namespace.ts +16 -0
- package/src/request/behavior/types.ts +26 -0
- package/src/request/behavior/versioned.ts +54 -0
- package/src/request/context/create.ts +103 -0
- package/src/request/context/guard.ts +49 -0
- package/src/request/context/index.ts +4 -0
- package/src/request/context/run-in-transaction.ts +200 -0
- package/src/request/context/store.ts +28 -0
- package/src/request/context/types.ts +50 -0
- package/src/request/di/builder.ts +535 -0
- package/src/request/di/index.ts +8 -0
- package/src/request/di/resolve.ts +271 -0
- package/src/request/di/types.ts +262 -0
- package/src/request/fitting/namespace.ts +7 -0
- package/src/request/fixture/batch.ts +71 -0
- package/src/request/fixture/builder.ts +26 -0
- package/src/request/fixture/errors.ts +10 -0
- package/src/request/fixture/index.ts +8 -0
- package/src/request/fixture/namespace.ts +23 -0
- package/src/request/fixture/paginate.ts +44 -0
- package/src/request/fixture/to-handlers.ts +260 -0
- package/src/request/fixture/types.ts +79 -0
- package/src/request/flow/act-as.ts +78 -0
- package/src/request/flow/namespace.ts +17 -0
- package/src/request/http/error-step.ts +42 -0
- package/src/request/http/file.ts +141 -0
- package/src/request/http/index.ts +4 -0
- package/src/request/http/middleware.ts +141 -0
- package/src/request/http/namespace.ts +5 -0
- package/src/request/http/rate-limit-classes.ts +170 -0
- package/src/request/http/response.ts +82 -0
- package/src/request/http/server.ts +916 -0
- package/src/request/http/sniff.ts +261 -0
- package/src/request/http/types.ts +250 -0
- package/src/request/http/upload.ts +284 -0
- package/src/request/internal/text.ts +6 -0
- package/src/request/openapi/generate.ts +205 -0
- package/src/request/openapi/index.ts +2 -0
- package/src/request/openapi/namespace.ts +7 -0
- package/src/test/auth.ts +13 -0
- package/src/test/auto-setup.ts +45 -0
- package/src/test/context.ts +91 -0
- package/src/test/crawl-fixture.ts +157 -0
- package/src/test/discover-fork.ts +17 -0
- package/src/test/fingerprint.ts +135 -0
- package/src/test/globals.d.ts +7 -0
- package/src/test/index.ts +19 -0
- package/src/test/namespace.ts +87 -0
- package/src/test/plugin.ts +189 -0
- package/src/test/progress.ts +185 -0
- package/src/test/reap-after-run.ts +28 -0
- package/src/test/reap.ts +286 -0
- package/src/test/reset.ts +253 -0
- package/src/test/savepoints.ts +172 -0
- package/src/test/setup.ts +1422 -0
- package/src/test/system-channel.ts +50 -0
- package/src/test/vitest-plugin.ts +142 -0
- package/src/test/vitest-workspace.ts +507 -0
- package/src/test/vitest.ts +333 -0
- package/src/test/watch.ts +105 -0
- package/src/trace/capture/bounded-serializer.ts +146 -0
- package/src/trace/cardinality/analyze.ts +173 -0
- package/src/trace/cardinality/branch-context.ts +214 -0
- package/src/trace/cardinality/check.ts +33 -0
- package/src/trace/cardinality/classify-edge.ts +64 -0
- package/src/trace/cardinality/structural-max.ts +107 -0
- package/src/trace/check/coverage-check.ts +188 -0
- package/src/trace/check/flow-check.ts +103 -0
- package/src/trace/check/flow-step-site.ts +234 -0
- package/src/trace/check/io-pattern.ts +116 -0
- package/src/trace/codemod/add-cardinality.ts +152 -0
- package/src/trace/codemod/run.ts +80 -0
- package/src/trace/entry/child-trace.ts +21 -0
- package/src/trace/entry/close-trace.ts +87 -0
- package/src/trace/entry/finalize-trace.ts +28 -0
- package/src/trace/entry/open-trace.ts +138 -0
- package/src/trace/entry/parent-trace.ts +20 -0
- package/src/trace/entry/parse-traceparent.ts +39 -0
- package/src/trace/entry/process-start.ts +38 -0
- package/src/trace/entry/trace-context.ts +26 -0
- package/src/trace/entry/tracing-enabled.ts +10 -0
- package/src/trace/external/record.ts +195 -0
- package/src/trace/external/report.ts +66 -0
- package/src/trace/namespace.ts +19 -0
- package/src/trace/partition/day.ts +47 -0
- package/src/trace/partition/index.ts +14 -0
- package/src/trace/partition/partition-name.ts +23 -0
- package/src/trace/partition/plan.ts +82 -0
- package/src/trace/partition/schedule.ts +26 -0
- package/src/trace/partition/startup-hook.ts +39 -0
- package/src/trace/partition/sync.ts +154 -0
- package/src/trace/partition/tables.ts +53 -0
- package/src/trace/read/tree.ts +83 -0
- package/src/trace/retain/copy-on-error.ts +76 -0
- package/src/trace/runtime/expire-stale-traces.ts +54 -0
- package/src/trace/runtime/flow-step.ts +147 -0
- package/src/trace/runtime/flush-steps.ts +31 -0
- package/src/trace/runtime/pending-writes.ts +54 -0
- package/src/trace/runtime/step-buffer.ts +298 -0
- package/src/trace/runtime/step-error.ts +21 -0
- package/src/trace/runtime/step-meta.ts +55 -0
- package/src/trace/runtime/thenable.ts +12 -0
- package/src/trace/runtime/truncate-trace.ts +62 -0
- package/src/trace/schema/trace-fields.ts +52 -0
- package/src/trace/schema/trace-retained.ts +22 -0
- package/src/trace/schema/trace-step-fields.ts +44 -0
- package/src/trace/schema/trace-step-retained.ts +25 -0
- package/src/trace/schema/trace-step.ts +18 -0
- package/src/trace/schema/trace.ts +12 -0
- package/src/trace/transformer/apply.ts +79 -0
- package/src/trace/transformer/build-meta-literal.ts +30 -0
- package/src/trace/transformer/find-flow-step-sites.ts +85 -0
- package/src/trace/transformer/inject-flow-step-import.ts +39 -0
- package/src/trace/transformer/transform-error.ts +14 -0
- package/src/trace/transformer/transform-flow-steps.ts +123 -0
- package/src/trace/transformer/wrap-function-declaration.ts +62 -0
- package/src/trace/transformer/wrap-variable-declaration.ts +67 -0
- package/src/workspace/affected.ts +110 -0
- package/src/workspace/changed-files.ts +163 -0
- package/src/workspace/check-deps.ts +213 -0
- package/src/workspace/checksum.ts +20 -0
- package/src/workspace/dependency-graph.ts +67 -0
- package/src/workspace/detect-pipework-copies.ts +93 -0
- package/src/workspace/discover.ts +206 -0
- package/src/workspace/generate.ts +401 -0
- package/src/workspace/index.ts +7 -0
- package/src/workspace/module-imports-pipework.ts +39 -0
- package/src/workspace/module-package-name.ts +12 -0
- package/src/workspace/scrub-git-env.ts +23 -0
- package/src/workspace/source-paths.ts +36 -0
- package/src/workspace/sync.ts +182 -0
- package/dist/REFERENCE.md +0 -1477
- package/dist/infra/access-links/rate-limit.d.ts +0 -10
- package/dist/infra/access-links/rate-limit.d.ts.map +0 -1
- package/dist/infra/access-links/rate-limit.js +0 -28
- package/dist/infra/access-links/rate-limit.js.map +0 -1
- package/dist/infra/email/sigv4.d.ts +0 -24
- package/dist/infra/email/sigv4.d.ts.map +0 -1
- package/dist/infra/email/sigv4.js +0 -55
- package/dist/infra/email/sigv4.js.map +0 -1
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs'
|
|
2
|
+
import { readFile } from 'node:fs/promises'
|
|
3
|
+
import { fileURLToPath } from 'node:url'
|
|
4
|
+
import { stripTypeScriptTypes } from 'node:module'
|
|
5
|
+
|
|
6
|
+
// ESM loader hooks — run on a separate thread, so this file must be
|
|
7
|
+
// self-contained: no static imports from the rest of pipework. The one
|
|
8
|
+
// exception is `load`, which dynamically imports the flow-step transformer
|
|
9
|
+
// (`../../trace/transformer/apply.js`) only for a file that mentions `@flow_step`.
|
|
10
|
+
//
|
|
11
|
+
// `resolve` rewrites a relative specifier to the TS file that actually backs
|
|
12
|
+
// it (.js→.ts, and extensionless bundler-style imports); `load` fully transpiles
|
|
13
|
+
// that TS — parameter properties, enums, namespaces and all — which
|
|
14
|
+
// `--experimental-strip-types` cannot do. Together they let `pipework dev` and
|
|
15
|
+
// config discovery run a consumer's real TypeScript source unbuilt.
|
|
16
|
+
//
|
|
17
|
+
// Mirrors the inline hook in `registerTsResolveHooks()` (discover.ts) for
|
|
18
|
+
// `resolve` and for TS stripping. They stay separate on purpose: that one
|
|
19
|
+
// registers a self-contained `data:` URL so it works during pipework's own
|
|
20
|
+
// source-mode runs where this compiled `.js` file does not exist yet, and it
|
|
21
|
+
// skips the `@flow_step` transform because config discovery has no steps; this
|
|
22
|
+
// one is a real file loaded via `--import` into child processes (`pipework
|
|
23
|
+
// dev`/`serve`), so it can reach the compiled transformer by relative URL.
|
|
24
|
+
// Keep the `resolve` and strip logic of BOTH hooks in sync.
|
|
25
|
+
//
|
|
26
|
+
// The `.ts` candidate must exist on disk before we rewrite. `nextResolve` does
|
|
27
|
+
// not touch the filesystem for file: URLs, so a missing file is not caught by a
|
|
28
|
+
// try/catch — it surfaces later as a misleading "Cannot find module foo.ts" at
|
|
29
|
+
// load time, even when foo.js sits right there (issue #292).
|
|
30
|
+
export function resolve(
|
|
31
|
+
specifier: string,
|
|
32
|
+
context: { parentURL?: string },
|
|
33
|
+
nextResolve: (specifier: string, context: { parentURL?: string }) => { url: string },
|
|
34
|
+
): { url: string } {
|
|
35
|
+
const parent = context.parentURL ?? ''
|
|
36
|
+
const isLocal = (specifier.startsWith('./') || specifier.startsWith('../'))
|
|
37
|
+
&& !parent.includes('/node_modules/') && !parent.includes('/dist/')
|
|
38
|
+
if (isLocal) {
|
|
39
|
+
for (const candidate of tsCandidates(specifier)) {
|
|
40
|
+
let candidatePath: string | null = null
|
|
41
|
+
try {
|
|
42
|
+
const url = new URL(candidate, parent)
|
|
43
|
+
if (url.protocol === 'file:') candidatePath = fileURLToPath(url)
|
|
44
|
+
} catch {
|
|
45
|
+
// candidate did not form a valid URL — try the next one.
|
|
46
|
+
}
|
|
47
|
+
if (candidatePath !== null && existsSync(candidatePath)) {
|
|
48
|
+
return nextResolve(candidate, context)
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return nextResolve(specifier, context)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Ordered resolution candidates for a relative specifier authored in TS source.
|
|
56
|
+
// - `./foo.js` → `./foo.ts` (NodeNext style: import the emit name)
|
|
57
|
+
// - `./foo` → .ts, .js, /index.ts, /index.js (bundler-style extensionless)
|
|
58
|
+
// - `./foo.ts` → [] (already explicit — let the host resolve)
|
|
59
|
+
// The caller probes each in order and rewrites to the first that exists on disk.
|
|
60
|
+
function tsCandidates(specifier: string): string[] {
|
|
61
|
+
const jsMatch = specifier.match(/\.([cm]?)js$/)
|
|
62
|
+
if (jsMatch !== null) {
|
|
63
|
+
return [specifier.slice(0, -jsMatch[0].length) + '.' + (jsMatch[1] ?? '') + 'ts']
|
|
64
|
+
}
|
|
65
|
+
if (/\.[cm]?tsx?$/.test(specifier)) return []
|
|
66
|
+
return [specifier + '.ts', specifier + '.js', specifier + '/index.ts', specifier + '/index.js']
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
interface LoadResult {
|
|
70
|
+
format: string
|
|
71
|
+
source?: string | ArrayBuffer | NodeJS.TypedArray
|
|
72
|
+
shortCircuit?: boolean
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Installed by ts-register.js, which marks globalThis[Symbol.for('pipework.tsHooks')]
|
|
76
|
+
// so discovery's inline hook (ts-inline-hooks.ts) is not registered over it.
|
|
77
|
+
// `--experimental-strip-types` only erases annotations — it cannot emit the
|
|
78
|
+
// runtime code that parameter properties, enums, or namespaces require, and it
|
|
79
|
+
// rejects them outright (ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX). This `load` hook
|
|
80
|
+
// does the full transform via Node's bundled `stripTypeScriptTypes` (amaro)
|
|
81
|
+
// instead, so consumer TS using any of those features loads cleanly. Only
|
|
82
|
+
// consumer source is transformed — pipework's own published code is compiled
|
|
83
|
+
// `.js` and node_modules is left to the host loader.
|
|
84
|
+
export async function load(
|
|
85
|
+
url: string,
|
|
86
|
+
context: { format?: string | null | undefined },
|
|
87
|
+
nextLoad: (url: string, context: { format?: string | null | undefined }) => LoadResult | Promise<LoadResult>,
|
|
88
|
+
): Promise<LoadResult> {
|
|
89
|
+
if (url.endsWith('.ts') && !url.includes('/node_modules/')) {
|
|
90
|
+
let source = await readFile(fileURLToPath(url), 'utf8')
|
|
91
|
+
if (source.includes('@flow_step')) {
|
|
92
|
+
const { applyFlowStepTransform } = await import('../../trace/transformer/apply.js')
|
|
93
|
+
const applied = applyFlowStepTransform(source, fileURLToPath(url))
|
|
94
|
+
if (applied !== null) source = applied.code
|
|
95
|
+
}
|
|
96
|
+
const code = stripTypeScriptTypes(source, { mode: 'transform', sourceMap: true, sourceUrl: url })
|
|
97
|
+
return { format: 'module', source: code, shortCircuit: true }
|
|
98
|
+
}
|
|
99
|
+
return nextLoad(url, context)
|
|
100
|
+
}
|
|
101
|
+
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// Cross-realm brand. The HTTP error mapper must recognize a PipeworkError thrown by a
|
|
2
|
+
// shared leaf library that resolved a DIFFERENT physical copy of pipework — where the
|
|
3
|
+
// two PipeworkError class objects are distinct and `instanceof` is false. A global
|
|
4
|
+
// Symbol survives the realm boundary (the same pattern drizzle uses for entityKind),
|
|
5
|
+
// so identity is keyed on the brand, never on the constructor.
|
|
6
|
+
// Stryker disable next-line StringLiteral: static module-init constant; ignoreStatic makes the mutant
|
|
7
|
+
// unreachable. Behavior is covered by tests/errors/brand.test.ts (cross-realm brand identity).
|
|
8
|
+
const PIPEWORK_ERROR = Symbol.for('pipework.error')
|
|
9
|
+
|
|
10
|
+
export abstract class PipeworkError extends Error {
|
|
11
|
+
abstract readonly code: string
|
|
12
|
+
abstract readonly httpStatus: number
|
|
13
|
+
readonly details: Record<string, unknown>
|
|
14
|
+
|
|
15
|
+
constructor(message: string, details: Record<string, unknown> = {}) {
|
|
16
|
+
super(message)
|
|
17
|
+
this.name = this.constructor.name
|
|
18
|
+
this.details = details
|
|
19
|
+
Object.defineProperty(this, PIPEWORK_ERROR, { value: true, enumerable: false })
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// The brand is the identity across physical copies of pipework: a branded error is an
|
|
23
|
+
// instance of the class its prototype chain names, whichever copy constructed it.
|
|
24
|
+
static override [Symbol.hasInstance](value: unknown): boolean {
|
|
25
|
+
if (Function.prototype[Symbol.hasInstance].call(this, value)) return true
|
|
26
|
+
if (!isPipeworkError(value)) return false
|
|
27
|
+
if (this === PipeworkError) return true
|
|
28
|
+
for (let proto: unknown = Object.getPrototypeOf(value); proto !== null; proto = Object.getPrototypeOf(proto)) {
|
|
29
|
+
if ((proto as { constructor?: { name?: unknown } }).constructor?.name === this.name) return true
|
|
30
|
+
}
|
|
31
|
+
return false
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* True for any PipeworkError, including one thrown across a module-realm boundary
|
|
37
|
+
* (a shared library that resolved its own copy of pipework). Use this instead of
|
|
38
|
+
* `instanceof PipeworkError` anywhere an error may cross such a boundary — `instanceof`
|
|
39
|
+
* silently fails there, mapping a 403/401 to a generic 500.
|
|
40
|
+
*/
|
|
41
|
+
export function isPipeworkError(error: unknown): error is PipeworkError {
|
|
42
|
+
return (
|
|
43
|
+
typeof error === 'object' &&
|
|
44
|
+
error !== null &&
|
|
45
|
+
(error as Record<symbol, unknown>)[PIPEWORK_ERROR] === true &&
|
|
46
|
+
typeof (error as { httpStatus?: unknown }).httpStatus === 'number' &&
|
|
47
|
+
typeof (error as { code?: unknown }).code === 'string'
|
|
48
|
+
)
|
|
49
|
+
}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { PipeworkError } from './base.js'
|
|
2
|
+
|
|
3
|
+
export class NotFoundError extends PipeworkError {
|
|
4
|
+
readonly code = 'NOT_FOUND' as const
|
|
5
|
+
readonly httpStatus = 404 as const
|
|
6
|
+
|
|
7
|
+
constructor(resource: string, details: Record<string, unknown> = {}) {
|
|
8
|
+
super(`${resource} not found`, details)
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export class UnauthorizedError extends PipeworkError {
|
|
13
|
+
readonly code = 'UNAUTHORIZED' as const
|
|
14
|
+
readonly httpStatus = 401 as const
|
|
15
|
+
|
|
16
|
+
constructor(message: string = 'Authentication required', details: Record<string, unknown> = {}) {
|
|
17
|
+
super(message, details)
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export class ForbiddenError extends PipeworkError {
|
|
22
|
+
readonly code = 'FORBIDDEN' as const
|
|
23
|
+
readonly httpStatus = 403 as const
|
|
24
|
+
|
|
25
|
+
constructor(message: string = 'Insufficient permissions', details: Record<string, unknown> = {}) {
|
|
26
|
+
super(message, details)
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export class CrossSiteRequestError extends PipeworkError {
|
|
31
|
+
readonly code = 'CROSS_SITE_REQUEST' as const
|
|
32
|
+
readonly httpStatus = 403 as const
|
|
33
|
+
|
|
34
|
+
constructor(
|
|
35
|
+
message: string = 'A request authenticated by a cookie must come from the app\'s own site.',
|
|
36
|
+
details: Record<string, unknown> = {},
|
|
37
|
+
) {
|
|
38
|
+
super(message, details)
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Where the failing input came from: the request parts a route validates, or the handler's own response. */
|
|
43
|
+
export type ValidationSource = 'body' | 'query' | 'params' | 'headers' | 'response'
|
|
44
|
+
|
|
45
|
+
/** One failed check: the path to the field (empty for the root) and the schema's message. Never the input value. */
|
|
46
|
+
export interface ValidationFieldIssue {
|
|
47
|
+
readonly source: ValidationSource | null
|
|
48
|
+
readonly path: readonly string[]
|
|
49
|
+
readonly message: string
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export class ValidationError extends PipeworkError {
|
|
53
|
+
readonly code = 'VALIDATION_ERROR' as const
|
|
54
|
+
readonly httpStatus = 422 as const
|
|
55
|
+
readonly fieldErrors: Readonly<Record<string, readonly string[]>>
|
|
56
|
+
/** The part of the request that failed, when a schema check threw this; null for an error raised by hand. */
|
|
57
|
+
readonly source: ValidationSource | null
|
|
58
|
+
readonly issues: readonly ValidationFieldIssue[]
|
|
59
|
+
|
|
60
|
+
constructor(message: string)
|
|
61
|
+
constructor(fieldErrors: Record<string, readonly string[]>, message?: string, detail?: { source: ValidationSource; paths: ReadonlyArray<{ path: readonly string[]; message: string }> })
|
|
62
|
+
constructor(fieldErrorsOrMessage: Record<string, readonly string[]> | string, message?: string, detail?: { source: ValidationSource; paths: ReadonlyArray<{ path: readonly string[]; message: string }> }) {
|
|
63
|
+
if (typeof fieldErrorsOrMessage === 'string') {
|
|
64
|
+
super(fieldErrorsOrMessage, { fieldErrors: {} })
|
|
65
|
+
this.fieldErrors = {}
|
|
66
|
+
this.source = null
|
|
67
|
+
this.issues = []
|
|
68
|
+
} else {
|
|
69
|
+
const msg = message ?? 'Invalid input'
|
|
70
|
+
super(msg, { fieldErrors: fieldErrorsOrMessage })
|
|
71
|
+
this.fieldErrors = fieldErrorsOrMessage
|
|
72
|
+
this.source = detail?.source ?? null
|
|
73
|
+
this.issues = detail !== undefined
|
|
74
|
+
? detail.paths.map(p => ({ source: detail.source, path: p.path, message: p.message }))
|
|
75
|
+
: Object.entries(fieldErrorsOrMessage).flatMap(([key, messages]) =>
|
|
76
|
+
messages.map(m => ({ source: null, path: key === '_root' ? [] : key.split('.'), message: m })))
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export class TooManyRequestsError extends PipeworkError {
|
|
82
|
+
readonly code = 'TOO_MANY_REQUESTS' as const
|
|
83
|
+
readonly httpStatus = 429 as const
|
|
84
|
+
readonly retryAfterSeconds: number
|
|
85
|
+
|
|
86
|
+
constructor(retryAfterSeconds: number, message: string = 'Too many attempts. Try again later.') {
|
|
87
|
+
super(message, { retryAfterSeconds })
|
|
88
|
+
this.retryAfterSeconds = retryAfterSeconds
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export class ConflictError extends PipeworkError {
|
|
93
|
+
readonly code = 'CONFLICT' as const
|
|
94
|
+
readonly httpStatus = 409 as const
|
|
95
|
+
|
|
96
|
+
constructor(message: string, details: Record<string, unknown> = {}) {
|
|
97
|
+
super(message, details)
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
export class PayloadTooLargeError extends PipeworkError {
|
|
103
|
+
readonly code = 'PAYLOAD_TOO_LARGE' as const
|
|
104
|
+
readonly httpStatus = 413 as const
|
|
105
|
+
|
|
106
|
+
constructor(message: string = 'Request body too large', details: Record<string, unknown> = {}) {
|
|
107
|
+
super(message, details)
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export class UnsupportedMediaTypeError extends PipeworkError {
|
|
112
|
+
readonly code = 'UNSUPPORTED_MEDIA_TYPE' as const
|
|
113
|
+
readonly httpStatus = 415 as const
|
|
114
|
+
|
|
115
|
+
constructor(message: string = 'Unsupported media type', details: Record<string, unknown> = {}) {
|
|
116
|
+
super(message, details)
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export class MalformedMultipartError extends PipeworkError {
|
|
121
|
+
readonly code = 'MALFORMED_MULTIPART' as const
|
|
122
|
+
readonly httpStatus = 400 as const
|
|
123
|
+
|
|
124
|
+
constructor(message: string = 'The multipart body is malformed or ends before its closing boundary', details: Record<string, unknown> = {}) {
|
|
125
|
+
super(message, details)
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export class AuthConfigurationError extends PipeworkError {
|
|
130
|
+
readonly code = 'AUTH_CONFIGURATION' as const
|
|
131
|
+
readonly httpStatus = 500 as const
|
|
132
|
+
|
|
133
|
+
constructor(message: string, details: Record<string, unknown> = {}) {
|
|
134
|
+
super(message, details)
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export class AuthUnavailableError extends PipeworkError {
|
|
139
|
+
readonly code = 'AUTH_UNAVAILABLE' as const
|
|
140
|
+
readonly httpStatus = 503 as const
|
|
141
|
+
|
|
142
|
+
constructor(message: string, options: { cause?: unknown; details?: Record<string, unknown> } = {}) {
|
|
143
|
+
super(message, options.details ?? {})
|
|
144
|
+
if (options.cause !== undefined) this.cause = options.cause
|
|
145
|
+
}
|
|
146
|
+
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export { PipeworkError, isPipeworkError } from './base.js'
|
|
2
|
+
export { NotFoundError, UnauthorizedError, ForbiddenError, CrossSiteRequestError, AuthConfigurationError, AuthUnavailableError, ValidationError, type ValidationSource, type ValidationFieldIssue, ConflictError, TooManyRequestsError, PayloadTooLargeError, UnsupportedMediaTypeError, MalformedMultipartError } from './http.js'
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { PipeworkError, NotFoundError, UnauthorizedError, ForbiddenError, CrossSiteRequestError, AuthConfigurationError, AuthUnavailableError, ValidationError, ConflictError, TooManyRequestsError, PayloadTooLargeError, UnsupportedMediaTypeError, MalformedMultipartError } from './index.js'
|
|
2
|
+
|
|
3
|
+
/** Standard error classes — all extend PipeworkError with structured error codes and actionable messages. */
|
|
4
|
+
export const errors = {
|
|
5
|
+
/** Base error class with code, statusCode, and suggestion fields. */
|
|
6
|
+
Base: PipeworkError,
|
|
7
|
+
/** 404 — resource not found. */
|
|
8
|
+
NotFound: NotFoundError,
|
|
9
|
+
/** 401 — missing or invalid authentication. */
|
|
10
|
+
Unauthorized: UnauthorizedError,
|
|
11
|
+
/** 403 — authenticated but insufficient permissions. */
|
|
12
|
+
Forbidden: ForbiddenError,
|
|
13
|
+
/** 403 `CROSS_SITE_REQUEST` — a cookie-authenticated request that did not come from the app's own site. */
|
|
14
|
+
CrossSiteRequest: CrossSiteRequestError,
|
|
15
|
+
/** 500 `AUTH_CONFIGURATION` — the auth setup itself is faulty (a signing key that will not load); never a refusal of the caller. */
|
|
16
|
+
AuthConfiguration: AuthConfigurationError,
|
|
17
|
+
/** 503 `AUTH_UNAVAILABLE` — authentication could not be decided (its database failed); never read as signed out. */
|
|
18
|
+
AuthUnavailable: AuthUnavailableError,
|
|
19
|
+
/** 422 — input validation failed, carries field-level details. */
|
|
20
|
+
Validation: ValidationError,
|
|
21
|
+
/** 409 — resource state conflict (duplicate, version mismatch). */
|
|
22
|
+
Conflict: ConflictError,
|
|
23
|
+
/** 429 — too many attempts; carries `retryAfterSeconds`, sent in both the `Retry-After` header and the body's `error.retryAfterSeconds`. */
|
|
24
|
+
TooManyRequests: TooManyRequestsError,
|
|
25
|
+
/** 413 — request body over the route's size limit. */
|
|
26
|
+
PayloadTooLarge: PayloadTooLargeError,
|
|
27
|
+
/** 415 — request body media type not accepted by the route. */
|
|
28
|
+
UnsupportedMediaType: UnsupportedMediaTypeError,
|
|
29
|
+
/** 400 `MALFORMED_MULTIPART` — a multipart body that cannot be parsed or ends before its closing boundary. */
|
|
30
|
+
MalformedMultipart: MalformedMultipartError,
|
|
31
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { FastifyInstance } from 'fastify'
|
|
2
|
+
|
|
3
|
+
export interface DrainState {
|
|
4
|
+
readonly inFlight: () => number
|
|
5
|
+
readonly waitForDrain: (timeoutMs: number) => Promise<void>
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function setupDrain(app: FastifyInstance): DrainState {
|
|
9
|
+
let count = 0
|
|
10
|
+
|
|
11
|
+
app.addHook('onRequest', () => { count++; return Promise.resolve() })
|
|
12
|
+
app.addHook('onResponse', () => { count--; return Promise.resolve() })
|
|
13
|
+
|
|
14
|
+
return {
|
|
15
|
+
inFlight: () => count,
|
|
16
|
+
waitForDrain: (timeoutMs: number) => new Promise<void>((resolve, reject) => {
|
|
17
|
+
if (count === 0) return resolve()
|
|
18
|
+
|
|
19
|
+
const deadline = setTimeout(() => {
|
|
20
|
+
clearInterval(poll)
|
|
21
|
+
reject(new Error(`[pipework] Drain timeout: ${count} requests still in-flight after ${timeoutMs}ms`))
|
|
22
|
+
}, timeoutMs)
|
|
23
|
+
|
|
24
|
+
const poll = setInterval(() => {
|
|
25
|
+
if (count === 0) {
|
|
26
|
+
clearTimeout(deadline)
|
|
27
|
+
clearInterval(poll)
|
|
28
|
+
resolve()
|
|
29
|
+
}
|
|
30
|
+
}, 50)
|
|
31
|
+
}),
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { FastifyInstance } from 'fastify'
|
|
2
|
+
import type { Manifold } from '../pipework.js'
|
|
3
|
+
import type { HealthCheck, HealthCheckResult, HealthStatus } from './types.js'
|
|
4
|
+
import { sql } from '../../data/db/sql.js'
|
|
5
|
+
|
|
6
|
+
const startedAt = Date.now()
|
|
7
|
+
|
|
8
|
+
export function registerHealthRoutes(
|
|
9
|
+
app: FastifyInstance,
|
|
10
|
+
instance: Manifold,
|
|
11
|
+
checks: readonly HealthCheck[],
|
|
12
|
+
paths: { health: string; ready: string },
|
|
13
|
+
): void {
|
|
14
|
+
app.get(paths.health, async (_req, reply) => {
|
|
15
|
+
return reply.status(200).send({ status: 'healthy', release: instance.release, uptime: Date.now() - startedAt })
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
app.get(paths.ready, async (_req, reply) => {
|
|
19
|
+
const results: Record<string, HealthCheckResult> = {}
|
|
20
|
+
let overall: HealthStatus['status'] = 'healthy'
|
|
21
|
+
|
|
22
|
+
const dbChecks = await checkDatabases(instance)
|
|
23
|
+
for (const [name, result] of Object.entries(dbChecks)) {
|
|
24
|
+
results[name] = result
|
|
25
|
+
if (result.status === 'unhealthy') overall = 'unhealthy'
|
|
26
|
+
else if (result.status === 'degraded' && overall !== 'unhealthy') overall = 'degraded'
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
for (const check of checks) {
|
|
30
|
+
const start = Date.now()
|
|
31
|
+
try {
|
|
32
|
+
const result = await check.check()
|
|
33
|
+
results[check.name] = { ...result, latencyMs: result.latencyMs ?? (Date.now() - start) }
|
|
34
|
+
if (result.status === 'unhealthy') overall = 'unhealthy'
|
|
35
|
+
else if (result.status === 'degraded' && overall !== 'unhealthy') overall = 'degraded'
|
|
36
|
+
} catch (err) {
|
|
37
|
+
results[check.name] = {
|
|
38
|
+
status: 'unhealthy',
|
|
39
|
+
message: err instanceof Error ? err.message : 'check failed',
|
|
40
|
+
latencyMs: Date.now() - start,
|
|
41
|
+
}
|
|
42
|
+
overall = 'unhealthy'
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const status: HealthStatus = { status: overall, release: instance.release, uptime: Date.now() - startedAt, checks: results }
|
|
47
|
+
return reply.status(overall === 'unhealthy' ? 503 : 200).send(status)
|
|
48
|
+
})
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async function checkDatabases(instance: Manifold): Promise<Record<string, HealthCheckResult>> {
|
|
52
|
+
const results: Record<string, HealthCheckResult> = {}
|
|
53
|
+
const dbNames = instance.config.databaseNames()
|
|
54
|
+
|
|
55
|
+
for (const name of dbNames) {
|
|
56
|
+
const start = Date.now()
|
|
57
|
+
try {
|
|
58
|
+
const dbConfig = instance.config.database(name)
|
|
59
|
+
const conn = instance.pool.getOrCreate(dbConfig)
|
|
60
|
+
await conn.drizzle.execute(sql`SELECT 1`)
|
|
61
|
+
results[`db:${name}`] = { status: 'healthy', latencyMs: Date.now() - start }
|
|
62
|
+
} catch (err) {
|
|
63
|
+
results[`db:${name}`] = {
|
|
64
|
+
status: 'unhealthy',
|
|
65
|
+
message: err instanceof Error ? err.message : 'connection failed',
|
|
66
|
+
latencyMs: Date.now() - start,
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return results
|
|
72
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { orchestrateStartup, type StartupOptions } from './startup.js'
|
|
2
|
+
export { registerHealthRoutes } from './health.js'
|
|
3
|
+
export { setupDrain, type DrainState } from './drain.js'
|
|
4
|
+
export type { LifecycleConfig, StartupHook, ShutdownHook, HealthCheck, HealthCheckResult, HealthStatus } from './types.js'
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { orchestrateStartup } from './startup.js'
|
|
2
|
+
|
|
3
|
+
/** Application lifecycle namespace — startup orchestration with hooks and timeouts. */
|
|
4
|
+
export const lifecycle = {
|
|
5
|
+
/** Runs pre-start hooks (DB readiness checks, migrations, etc.) with a configurable timeout. The hooks run in one trace that names the process; the trace-partition hook runs first, outside it. */
|
|
6
|
+
orchestrate: orchestrateStartup,
|
|
7
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto'
|
|
2
|
+
import { sql } from '../../data/db/sql.js'
|
|
3
|
+
import type { Manifold } from '../pipework.js'
|
|
4
|
+
import type { StartupHook } from './types.js'
|
|
5
|
+
import { getBaseLogger } from '../logging/index.js'
|
|
6
|
+
import { runScript } from '../../async/jobs/run-script.js'
|
|
7
|
+
import { getContext } from '../../request/context/store.js'
|
|
8
|
+
import { TRACE_PARTITION_HOOK_NAME } from '../../trace/partition/startup-hook.js'
|
|
9
|
+
import { projectStepError } from '../../trace/runtime/step-error.js'
|
|
10
|
+
import { serializeForTrace } from '../../trace/capture/bounded-serializer.js'
|
|
11
|
+
|
|
12
|
+
export interface StartupOptions {
|
|
13
|
+
readonly hooks?: readonly StartupHook[]
|
|
14
|
+
readonly timeoutMs?: number
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export async function orchestrateStartup(instance: Manifold, options: StartupOptions = {}): Promise<void> {
|
|
18
|
+
const logger = getBaseLogger()
|
|
19
|
+
const timeoutMs = options.timeoutMs ?? 30000
|
|
20
|
+
|
|
21
|
+
const doStartupWork = async () => {
|
|
22
|
+
await verifyDatabases(instance, logger)
|
|
23
|
+
|
|
24
|
+
if (options.hooks) {
|
|
25
|
+
// The partition hook creates today's trace partition, so it runs first and outside the trace.
|
|
26
|
+
const partition = options.hooks.filter(h => h.name === TRACE_PARTITION_HOOK_NAME)
|
|
27
|
+
const rest = options.hooks.filter(h => h.name !== TRACE_PARTITION_HOOK_NAME)
|
|
28
|
+
for (const hook of partition) await runHook(hook)
|
|
29
|
+
if (rest.length > 0) {
|
|
30
|
+
await runScript(instance, async () => {
|
|
31
|
+
for (const hook of rest) await runTracedHook(hook)
|
|
32
|
+
}, { jobType: 'start', process: { phase: 'preStart', surfaces: instance.surfaceNames() } })
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const runHook = async (hook: StartupHook) => {
|
|
38
|
+
logger.info({ hook: hook.name }, 'Running pre-start hook')
|
|
39
|
+
await hook.execute()
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const runTracedHook = async (hook: StartupHook) => {
|
|
43
|
+
const startedAt = new Date()
|
|
44
|
+
const t0 = process.hrtime.bigint()
|
|
45
|
+
const stage = (error: unknown): void => {
|
|
46
|
+
const trace = getContext()?.trace
|
|
47
|
+
if (trace === undefined || trace === null) return
|
|
48
|
+
trace.buffer.stageExternal({
|
|
49
|
+
id: randomUUID(),
|
|
50
|
+
parentStepId: null,
|
|
51
|
+
flowStepRef: 'external:pipework.start.hook',
|
|
52
|
+
status: error === undefined ? 'ok' : 'error',
|
|
53
|
+
startedAt,
|
|
54
|
+
completedAt: new Date(),
|
|
55
|
+
durationMicros: (process.hrtime.bigint() - t0) / 1000n,
|
|
56
|
+
capturedInputs: [serializeForTrace({ hook: hook.name })],
|
|
57
|
+
error: error === undefined ? null : projectStepError(error),
|
|
58
|
+
})
|
|
59
|
+
}
|
|
60
|
+
try {
|
|
61
|
+
await runHook(hook)
|
|
62
|
+
} catch (err) {
|
|
63
|
+
stage(err)
|
|
64
|
+
throw err
|
|
65
|
+
}
|
|
66
|
+
stage(undefined)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const timeoutPromise = new Promise<never>((_, reject) => {
|
|
70
|
+
const timer = setTimeout(() => {
|
|
71
|
+
reject(new Error(
|
|
72
|
+
`[pipework] Startup timed out after ${timeoutMs}ms.\n\n` +
|
|
73
|
+
' This usually means a database is unreachable or a pre-start hook is hanging.\n' +
|
|
74
|
+
' Check your DATABASE_URL values and network connectivity.\n'
|
|
75
|
+
))
|
|
76
|
+
}, timeoutMs)
|
|
77
|
+
timer.unref()
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
await Promise.race([doStartupWork(), timeoutPromise])
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async function verifyDatabases(instance: Manifold, logger: { info(obj: Record<string, unknown>, msg?: string): void }): Promise<void> {
|
|
84
|
+
const dbNames = instance.config.databaseNames()
|
|
85
|
+
|
|
86
|
+
for (const name of dbNames) {
|
|
87
|
+
const dbConfig = instance.config.database(name)
|
|
88
|
+
const conn = instance.pool.getOrCreate(dbConfig)
|
|
89
|
+
try {
|
|
90
|
+
await conn.drizzle.execute(sql`SELECT 1`)
|
|
91
|
+
logger.info({ database: name }, 'Database connectivity verified')
|
|
92
|
+
} catch (err) {
|
|
93
|
+
const message = err instanceof Error ? err.message : String(err)
|
|
94
|
+
throw new Error(
|
|
95
|
+
`[pipework] Cannot connect to database "${name}".\n\n` +
|
|
96
|
+
` URL: ${dbConfig.url.replace(/\/\/[^@]+@/, '//***@')}\n` +
|
|
97
|
+
` Error: ${message}\n\n` +
|
|
98
|
+
' Check that the database is running and the URL is correct.\n'
|
|
99
|
+
)
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export interface LifecycleConfig {
|
|
2
|
+
readonly preStart?: readonly StartupHook[]
|
|
3
|
+
readonly preShutdown?: readonly ShutdownHook[]
|
|
4
|
+
readonly healthChecks?: readonly HealthCheck[]
|
|
5
|
+
readonly drainTimeoutMs?: number
|
|
6
|
+
readonly startupTimeoutMs?: number
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface StartupHook {
|
|
10
|
+
readonly name: string
|
|
11
|
+
execute(): Promise<void>
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface ShutdownHook {
|
|
15
|
+
readonly name: string
|
|
16
|
+
execute(): Promise<void>
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface HealthCheck {
|
|
20
|
+
readonly name: string
|
|
21
|
+
check(): Promise<HealthCheckResult>
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface HealthCheckResult {
|
|
25
|
+
readonly status: 'healthy' | 'degraded' | 'unhealthy'
|
|
26
|
+
readonly message?: string
|
|
27
|
+
readonly latencyMs?: number
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface HealthStatus {
|
|
31
|
+
readonly status: 'healthy' | 'degraded' | 'unhealthy'
|
|
32
|
+
/** The release this process runs (`manifold.release`); null when none is configured or set. */
|
|
33
|
+
readonly release: string | null
|
|
34
|
+
readonly uptime: number
|
|
35
|
+
readonly checks: Record<string, HealthCheckResult>
|
|
36
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { getContext } from '../../request/context/store.js'
|
|
2
|
+
import { getLoggingConfig } from './logger.js'
|
|
3
|
+
import type { ContextField } from './types.js'
|
|
4
|
+
|
|
5
|
+
export interface CorrelationFields {
|
|
6
|
+
requestId?: string
|
|
7
|
+
tenantId?: string
|
|
8
|
+
userId?: string
|
|
9
|
+
sessionId?: string
|
|
10
|
+
traceId?: string
|
|
11
|
+
jobType?: string
|
|
12
|
+
[key: string]: string | undefined
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function getCorrelationFields(): CorrelationFields {
|
|
16
|
+
const ctx = getContext()
|
|
17
|
+
if (!ctx) return {}
|
|
18
|
+
|
|
19
|
+
const config = getLoggingConfig()
|
|
20
|
+
const enabled = config?.contextFields
|
|
21
|
+
|
|
22
|
+
const fields: CorrelationFields = {}
|
|
23
|
+
|
|
24
|
+
if (isEnabled('requestId', enabled) && ctx.requestId !== '') {
|
|
25
|
+
fields.requestId = ctx.requestId
|
|
26
|
+
}
|
|
27
|
+
if (isEnabled('tenantId', enabled) && ctx.tenant !== null) {
|
|
28
|
+
fields.tenantId = ctx.tenant
|
|
29
|
+
}
|
|
30
|
+
if (isEnabled('userId', enabled) && ctx.auth !== null && typeof ctx.auth === 'object' && 'userId' in ctx.auth) {
|
|
31
|
+
const userId = (ctx.auth as { userId: unknown }).userId
|
|
32
|
+
if (typeof userId === 'string') fields.userId = userId
|
|
33
|
+
}
|
|
34
|
+
if (isEnabled('sessionId', enabled) && ctx.metadata?.sessionId != null) {
|
|
35
|
+
fields.sessionId = ctx.metadata.sessionId
|
|
36
|
+
}
|
|
37
|
+
if (isEnabled('traceId', enabled) && ctx.metadata?.traceId != null) {
|
|
38
|
+
fields.traceId = ctx.metadata.traceId
|
|
39
|
+
}
|
|
40
|
+
if (ctx.jobType !== null) {
|
|
41
|
+
fields.jobType = ctx.jobType
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (config?.authFields != null && ctx.auth !== null && typeof ctx.auth === 'object') {
|
|
45
|
+
const auth = ctx.auth as Record<string, unknown>
|
|
46
|
+
for (const field of config.authFields) {
|
|
47
|
+
const value = auth[field]
|
|
48
|
+
if (typeof value === 'string') {
|
|
49
|
+
fields[field] = value
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return fields
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function isEnabled(field: ContextField, enabled: readonly ContextField[] | undefined): boolean {
|
|
58
|
+
if (enabled === undefined) return true
|
|
59
|
+
return enabled.includes(field)
|
|
60
|
+
}
|