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,284 @@
|
|
|
1
|
+
import { Readable, Transform } from 'node:stream'
|
|
2
|
+
import type { FastifyRequest } from 'fastify'
|
|
3
|
+
import type { UploadConfig, BinaryUpload, MultipartUpload } from '../di/types.js'
|
|
4
|
+
import { extensionOf, resolveMediaType, SNIFF_BYTES } from './sniff.js'
|
|
5
|
+
import { isPipeworkError, MalformedMultipartError, PayloadTooLargeError, UnsupportedMediaTypeError, ValidationError } from '../../core/errors/index.js'
|
|
6
|
+
|
|
7
|
+
/** Largest accepted multipart text field, in bytes. */
|
|
8
|
+
export const MULTIPART_FIELD_LIMIT = 64 * 1024
|
|
9
|
+
const MULTIPART_OVERHEAD = 16 * 1024
|
|
10
|
+
|
|
11
|
+
/** The body size Fastify may let through: the file plus multipart framing and fields. */
|
|
12
|
+
export function uploadBodyLimit(config: UploadConfig): number {
|
|
13
|
+
if (config.kind === 'binary') return config.maxBytes
|
|
14
|
+
return config.maxBytes + MULTIPART_OVERHEAD + (config.fields?.length ?? 0) * MULTIPART_FIELD_LIMIT
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** `type/subtype` of a Content-Type value, lowercased and without parameters. */
|
|
18
|
+
export function mediaTypeOf(value: string | undefined): string {
|
|
19
|
+
if (value === undefined) return ''
|
|
20
|
+
return (value.split(';')[0] ?? '').trim().toLowerCase()
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** True when `mediaType` matches an allowed entry: exactly, by `type/*`, or by a star on each side of the slash (any non-empty type). */
|
|
24
|
+
export function mediaTypeAllowed(allowed: readonly string[], mediaType: string): boolean {
|
|
25
|
+
if (mediaType === '') return false
|
|
26
|
+
for (const entry of allowed) {
|
|
27
|
+
const a = entry.trim().toLowerCase()
|
|
28
|
+
if (a === mediaType || a === '*/*') return true
|
|
29
|
+
if (a.endsWith('/*') && mediaType.startsWith(a.slice(0, -1))) return true
|
|
30
|
+
}
|
|
31
|
+
return false
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const pipeworkRefusals = new WeakSet<object>()
|
|
35
|
+
|
|
36
|
+
function refusal<T extends Error>(error: T): T {
|
|
37
|
+
pipeworkRefusals.add(error)
|
|
38
|
+
return error
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** True for a 413 or 415 pipework raised for a file, as opposed to one a handler threw. */
|
|
42
|
+
export function isUploadRefusal(error: unknown): error is PayloadTooLargeError | UnsupportedMediaTypeError {
|
|
43
|
+
return typeof error === 'object' && error !== null && pipeworkRefusals.has(error)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** What pipework knows of the file at the moment it refuses it. */
|
|
47
|
+
export interface UploadTrack {
|
|
48
|
+
declaredType: string
|
|
49
|
+
extension: string | null
|
|
50
|
+
filename: string | null
|
|
51
|
+
detectedType: string | null
|
|
52
|
+
bytesRead: number
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function newUploadTrack(): UploadTrack {
|
|
56
|
+
return { declaredType: '', extension: null, filename: null, detectedType: null, bytesRead: 0 }
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Refuses a multipart route's request that is not `multipart/form-data`, from
|
|
61
|
+
* its headers alone. A protocol error: no file is involved. Binary routes have
|
|
62
|
+
* no header check; their type is settled from the bytes.
|
|
63
|
+
*/
|
|
64
|
+
export function checkUploadHeaders(config: UploadConfig, headers: Record<string, string | string[] | undefined>): UnsupportedMediaTypeError | null {
|
|
65
|
+
if (config.kind === 'binary') return null
|
|
66
|
+
const contentType = headers['content-type']
|
|
67
|
+
const mediaType = mediaTypeOf(Array.isArray(contentType) ? contentType[0] : contentType)
|
|
68
|
+
if (mediaType === 'multipart/form-data') return null
|
|
69
|
+
return new UnsupportedMediaTypeError(
|
|
70
|
+
mediaType === ''
|
|
71
|
+
? 'This route needs a Content-Type; accepted: multipart/form-data'
|
|
72
|
+
: `Media type ${mediaType} is not accepted; accepted: multipart/form-data`,
|
|
73
|
+
{ accepted: ['multipart/form-data'] },
|
|
74
|
+
)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** 413 when the request declares a length over what the route may receive. */
|
|
78
|
+
function checkDeclaredLength(config: UploadConfig, headers: Record<string, string | string[] | undefined>): PayloadTooLargeError | null {
|
|
79
|
+
const declared = Number(headers['content-length'])
|
|
80
|
+
if (headers['content-length'] !== undefined && Number.isFinite(declared) && declared > uploadBodyLimit(config)) {
|
|
81
|
+
return refusal(new PayloadTooLargeError(`Request body is ${declared} bytes; the limit is ${config.maxBytes}`, { maxBytes: config.maxBytes }))
|
|
82
|
+
}
|
|
83
|
+
return null
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Passes `source` through, erroring with a 413 once more than `maxBytes` have
|
|
88
|
+
* passed. A failure of `source` becomes the failure of the returned stream.
|
|
89
|
+
*/
|
|
90
|
+
export function limitStream(
|
|
91
|
+
source: Readable,
|
|
92
|
+
maxBytes: number,
|
|
93
|
+
mapError: (e: unknown) => Error = (e) => e as Error,
|
|
94
|
+
onUnfinished?: () => Error,
|
|
95
|
+
track?: UploadTrack,
|
|
96
|
+
): Readable {
|
|
97
|
+
let seen = 0
|
|
98
|
+
const counter = new Transform({
|
|
99
|
+
transform(chunk: Buffer, _enc, cb) {
|
|
100
|
+
seen += chunk.length
|
|
101
|
+
if (track !== undefined) track.bytesRead = seen
|
|
102
|
+
if (seen > maxBytes) {
|
|
103
|
+
source.unpipe(counter)
|
|
104
|
+
cb(refusal(new PayloadTooLargeError(`Upload is over the limit of ${maxBytes} bytes`, { maxBytes })))
|
|
105
|
+
return
|
|
106
|
+
}
|
|
107
|
+
cb(null, chunk)
|
|
108
|
+
},
|
|
109
|
+
})
|
|
110
|
+
source.on('error', (e) => counter.destroy(mapError(e)))
|
|
111
|
+
if (onUnfinished !== undefined) {
|
|
112
|
+
// The parser may have failed and destroyed the part before this stream
|
|
113
|
+
// was attached, so its 'error' and 'close' are already past.
|
|
114
|
+
const unfinished = (): void => {
|
|
115
|
+
if (!source.readableEnded && !counter.destroyed) counter.destroy(onUnfinished())
|
|
116
|
+
}
|
|
117
|
+
if (source.destroyed) unfinished()
|
|
118
|
+
else source.on('close', unfinished)
|
|
119
|
+
}
|
|
120
|
+
source.pipe(counter)
|
|
121
|
+
return counter
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function toUploadError(e: unknown): Error {
|
|
125
|
+
const code = (e as { code?: unknown } | null)?.code
|
|
126
|
+
if (code === 'FST_REQ_FILE_TOO_LARGE') return refusal(new PayloadTooLargeError('Uploaded file is over the size limit'))
|
|
127
|
+
return asMalformedMultipart(e)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** A parser failure becomes a 400; errors that already carry a status are kept. */
|
|
131
|
+
function asMalformedMultipart(e: unknown): Error {
|
|
132
|
+
if (isPipeworkError(e)) return e
|
|
133
|
+
const status = (e as { statusCode?: unknown } | null)?.statusCode
|
|
134
|
+
if (typeof status === 'number' && status >= 400 && status < 500) return e as Error
|
|
135
|
+
const message = e instanceof Error ? e.message : String(e)
|
|
136
|
+
return new MalformedMultipartError(undefined, { cause: message })
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/** Lowercase text after the last dot of a filename; null without a filename or a dot. */
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Reads up to SNIFF_BYTES from `source`, then returns them with a stream that
|
|
143
|
+
* yields them again followed by the rest of `source`, byte for byte. Nothing
|
|
144
|
+
* further is buffered. A failure of `source` while reading the head is thrown.
|
|
145
|
+
*/
|
|
146
|
+
async function withHead(source: Readable): Promise<{ head: Buffer; stream: Readable }> {
|
|
147
|
+
const it = source[Symbol.asyncIterator]()
|
|
148
|
+
const chunks: Buffer[] = []
|
|
149
|
+
let length = 0
|
|
150
|
+
let ended = false
|
|
151
|
+
while (length < SNIFF_BYTES) {
|
|
152
|
+
const next = await it.next() as IteratorResult<Buffer>
|
|
153
|
+
if (next.done === true) { ended = true; break }
|
|
154
|
+
chunks.push(next.value)
|
|
155
|
+
length += next.value.length
|
|
156
|
+
}
|
|
157
|
+
const all = Buffer.concat(chunks)
|
|
158
|
+
async function* rejoin(): AsyncGenerator<Buffer> {
|
|
159
|
+
try {
|
|
160
|
+
if (all.length > 0) yield all
|
|
161
|
+
while (!ended) {
|
|
162
|
+
const next = await it.next() as IteratorResult<Buffer>
|
|
163
|
+
if (next.done === true) return
|
|
164
|
+
yield next.value
|
|
165
|
+
}
|
|
166
|
+
} finally {
|
|
167
|
+
source.destroy()
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
const stream = Readable.from(rejoin(), { objectMode: false })
|
|
171
|
+
stream.on('close', () => source.destroy())
|
|
172
|
+
return { head: all.subarray(0, SNIFF_BYTES), stream }
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
interface SniffedFile {
|
|
176
|
+
readonly stream: Readable
|
|
177
|
+
readonly mediaType: string
|
|
178
|
+
readonly detectedType: string | null
|
|
179
|
+
readonly declaredType: string
|
|
180
|
+
readonly extension: string | null
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/** Reads the head of `limited`, settles the file's type and refuses a type outside the route's list. */
|
|
184
|
+
async function sniffFile(
|
|
185
|
+
config: UploadConfig,
|
|
186
|
+
limited: Readable,
|
|
187
|
+
declaredType: string,
|
|
188
|
+
filename: string | null,
|
|
189
|
+
track: UploadTrack,
|
|
190
|
+
): Promise<SniffedFile> {
|
|
191
|
+
const extension = extensionOf(filename)
|
|
192
|
+
track.declaredType = declaredType
|
|
193
|
+
track.filename = filename
|
|
194
|
+
track.extension = extension
|
|
195
|
+
const { head, stream } = await withHead(limited)
|
|
196
|
+
const { detectedType, mediaType } = resolveMediaType({ head, declared: declaredType, extension })
|
|
197
|
+
track.detectedType = detectedType
|
|
198
|
+
if (!mediaTypeAllowed(config.mediaTypes, mediaType)) {
|
|
199
|
+
stream.destroy()
|
|
200
|
+
throw refusal(new UnsupportedMediaTypeError(
|
|
201
|
+
`File media type is not accepted (detected ${detectedType ?? 'none'}, declared ${declaredType === '' ? 'none' : declaredType}); accepted: ${config.mediaTypes.join(', ')}`,
|
|
202
|
+
{ accepted: config.mediaTypes, detectedType, declaredType },
|
|
203
|
+
))
|
|
204
|
+
}
|
|
205
|
+
return { stream, mediaType, detectedType, declaredType, extension }
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Starts an upload: refuses a declared length over the limit, builds the size-
|
|
210
|
+
* limited file stream, reads its first bytes to settle the media type, and
|
|
211
|
+
* refuses a type outside the route's list. `track` follows what is learned.
|
|
212
|
+
*/
|
|
213
|
+
export async function startUpload(req: FastifyRequest, config: UploadConfig, track: UploadTrack): Promise<BinaryUpload | MultipartUpload> {
|
|
214
|
+
if (config.kind === 'binary') {
|
|
215
|
+
track.declaredType = mediaTypeOf(req.headers['content-type'])
|
|
216
|
+
const tooLarge = checkDeclaredLength(config, req.headers)
|
|
217
|
+
if (tooLarge !== null) throw tooLarge
|
|
218
|
+
const body = req.body
|
|
219
|
+
const source = body instanceof Readable ? body : Readable.from([])
|
|
220
|
+
const declared = req.headers['content-length']
|
|
221
|
+
const length = declared === undefined ? null : Number(declared)
|
|
222
|
+
const file = await sniffFile(config, limitStream(source, config.maxBytes, undefined, undefined, track), track.declaredType, null, track)
|
|
223
|
+
return { ...file, length: length !== null && Number.isFinite(length) ? length : null }
|
|
224
|
+
}
|
|
225
|
+
const tooLarge = checkDeclaredLength(config, req.headers)
|
|
226
|
+
if (tooLarge !== null) throw tooLarge
|
|
227
|
+
try {
|
|
228
|
+
return await readMultipart(req, config, track)
|
|
229
|
+
} catch (e) {
|
|
230
|
+
throw asMalformedMultipart(e)
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Reads a multipart body up to its file part: the text fields before it, then
|
|
236
|
+
* the file as a size-limited stream. Nothing after the file part is read.
|
|
237
|
+
*/
|
|
238
|
+
async function readMultipart(
|
|
239
|
+
req: FastifyRequest,
|
|
240
|
+
config: Extract<UploadConfig, { kind: 'multipart' }>,
|
|
241
|
+
track: UploadTrack,
|
|
242
|
+
): Promise<MultipartUpload> {
|
|
243
|
+
const allowedFields = new Set(config.fields ?? [])
|
|
244
|
+
const fields: Record<string, string> = {}
|
|
245
|
+
for await (const part of req.parts()) {
|
|
246
|
+
if (part.type === 'field') {
|
|
247
|
+
if (!allowedFields.has(part.fieldname)) {
|
|
248
|
+
throw new ValidationError({ [part.fieldname]: ['Unexpected field'] }, `Unexpected form field "${part.fieldname}"`)
|
|
249
|
+
}
|
|
250
|
+
if (part.valueTruncated) {
|
|
251
|
+
throw new PayloadTooLargeError(`Form field "${part.fieldname}" is over ${MULTIPART_FIELD_LIMIT} bytes`)
|
|
252
|
+
}
|
|
253
|
+
fields[part.fieldname] = String(part.value)
|
|
254
|
+
continue
|
|
255
|
+
}
|
|
256
|
+
if (part.fieldname !== config.file) {
|
|
257
|
+
part.file.resume()
|
|
258
|
+
throw new ValidationError({ [part.fieldname]: ['Unexpected file field'] }, `Unexpected file field "${part.fieldname}"; the file goes in "${config.file}"`)
|
|
259
|
+
}
|
|
260
|
+
const filename = part.filename === '' ? null : part.filename
|
|
261
|
+
const limited = limitStream(part.file, config.maxBytes, toUploadError, () => new MalformedMultipartError(undefined, { cause: 'The body ended before the file part was closed' }), track)
|
|
262
|
+
try {
|
|
263
|
+
const file = await sniffFile(config, limited, mediaTypeOf(part.mimetype), filename, track)
|
|
264
|
+
return { ...file, filename, fields }
|
|
265
|
+
} catch (e) {
|
|
266
|
+
part.file.resume()
|
|
267
|
+
throw e
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
throw new ValidationError({ [config.file]: ['Required'] }, `The form has no file part named "${config.file}"`)
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Ends an upload after its handler: a stream not read to the end is destroyed.
|
|
275
|
+
* Returns true when the body was left incomplete, so the caller closes the
|
|
276
|
+
* connection rather than reading the rest.
|
|
277
|
+
*/
|
|
278
|
+
export function settleUpload(stream: Readable | undefined): boolean {
|
|
279
|
+
if (stream === undefined) return false
|
|
280
|
+
if (stream.readableEnded) return false
|
|
281
|
+
stream.on('error', () => undefined)
|
|
282
|
+
stream.destroy()
|
|
283
|
+
return true
|
|
284
|
+
}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import type { RouteRegistration } from '../http/types.js'
|
|
2
|
+
import { toJsonSchema } from '../../data/schema/json-schema.js'
|
|
3
|
+
|
|
4
|
+
export interface OpenAPIInfo {
|
|
5
|
+
title: string
|
|
6
|
+
version: string
|
|
7
|
+
description?: string
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface OpenAPIDocument {
|
|
11
|
+
openapi: '3.0.3'
|
|
12
|
+
info: OpenAPIInfo
|
|
13
|
+
paths: Record<string, Record<string, unknown>>
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** The body of a validation 422: `{ error: { code, message, fields? } }`. */
|
|
17
|
+
export const VALIDATION_ERROR_SCHEMA = {
|
|
18
|
+
type: 'object',
|
|
19
|
+
properties: {
|
|
20
|
+
error: {
|
|
21
|
+
type: 'object',
|
|
22
|
+
properties: {
|
|
23
|
+
code: { type: 'string' },
|
|
24
|
+
message: { type: 'string' },
|
|
25
|
+
fields: { type: 'object', additionalProperties: { type: 'array', items: { type: 'string' } } },
|
|
26
|
+
},
|
|
27
|
+
required: ['code', 'message'],
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
required: ['error'],
|
|
31
|
+
} as const
|
|
32
|
+
|
|
33
|
+
export const TOO_MANY_REQUESTS_SCHEMA = {
|
|
34
|
+
type: 'object',
|
|
35
|
+
properties: {
|
|
36
|
+
error: {
|
|
37
|
+
type: 'object',
|
|
38
|
+
properties: {
|
|
39
|
+
code: { type: 'string' },
|
|
40
|
+
message: { type: 'string' },
|
|
41
|
+
retryAfterSeconds: { type: 'integer', minimum: 0 },
|
|
42
|
+
},
|
|
43
|
+
required: ['code', 'message', 'retryAfterSeconds'],
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
required: ['error'],
|
|
47
|
+
} as const
|
|
48
|
+
|
|
49
|
+
export function generateOpenAPI(
|
|
50
|
+
info: OpenAPIInfo,
|
|
51
|
+
routes: readonly RouteRegistration[],
|
|
52
|
+
options?: { rateLimited?: boolean },
|
|
53
|
+
): OpenAPIDocument {
|
|
54
|
+
const paths: Record<string, Record<string, unknown>> = {}
|
|
55
|
+
|
|
56
|
+
for (const route of routes) {
|
|
57
|
+
const { method, path, handler } = route
|
|
58
|
+
const meta = handler.meta
|
|
59
|
+
|
|
60
|
+
const openApiPath = path.replace(/:(\w+)/g, '{$1}')
|
|
61
|
+
|
|
62
|
+
let pathItem = paths[openApiPath]
|
|
63
|
+
if (!pathItem) {
|
|
64
|
+
pathItem = {}
|
|
65
|
+
paths[openApiPath] = pathItem
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const operation: Record<string, unknown> = {}
|
|
69
|
+
|
|
70
|
+
const parameters: Record<string, unknown>[] = []
|
|
71
|
+
|
|
72
|
+
if (meta.paramsSchema) {
|
|
73
|
+
const schema = toJsonSchema(meta.paramsSchema)
|
|
74
|
+
if ('properties' in schema) {
|
|
75
|
+
const props = schema['properties'] as Record<string, unknown>
|
|
76
|
+
for (const [name, propSchema] of Object.entries(props)) {
|
|
77
|
+
parameters.push({
|
|
78
|
+
name,
|
|
79
|
+
in: 'path',
|
|
80
|
+
required: true,
|
|
81
|
+
schema: propSchema,
|
|
82
|
+
})
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (meta.querySchema) {
|
|
88
|
+
const schema = toJsonSchema(meta.querySchema)
|
|
89
|
+
if ('properties' in schema) {
|
|
90
|
+
const props = schema['properties'] as Record<string, unknown>
|
|
91
|
+
const requiredFields = Array.isArray(schema['required']) ? schema['required'] as string[] : []
|
|
92
|
+
for (const [name, propSchema] of Object.entries(props)) {
|
|
93
|
+
parameters.push({
|
|
94
|
+
name,
|
|
95
|
+
in: 'query',
|
|
96
|
+
required: requiredFields.includes(name),
|
|
97
|
+
schema: propSchema,
|
|
98
|
+
})
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (parameters.length > 0) {
|
|
104
|
+
operation['parameters'] = parameters
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (meta.inputSchema) {
|
|
108
|
+
operation['requestBody'] = {
|
|
109
|
+
// Mirrors the runtime: a request with no body reaches the input as undefined.
|
|
110
|
+
required: !meta.inputSchema.safeParse(undefined).success,
|
|
111
|
+
content: {
|
|
112
|
+
'application/json': {
|
|
113
|
+
schema: toJsonSchema(meta.inputSchema),
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (meta.upload) {
|
|
120
|
+
const binary = { type: 'string', format: 'binary' }
|
|
121
|
+
if (meta.upload.kind === 'binary') {
|
|
122
|
+
operation['requestBody'] = {
|
|
123
|
+
required: true,
|
|
124
|
+
content: Object.fromEntries(meta.upload.mediaTypes.map((type) => [type, { schema: binary }])),
|
|
125
|
+
}
|
|
126
|
+
} else {
|
|
127
|
+
const properties: Record<string, unknown> = { [meta.upload.file]: binary }
|
|
128
|
+
for (const field of meta.upload.fields ?? []) properties[field] = { type: 'string' }
|
|
129
|
+
operation['requestBody'] = {
|
|
130
|
+
required: true,
|
|
131
|
+
content: {
|
|
132
|
+
'multipart/form-data': {
|
|
133
|
+
schema: { type: 'object', properties, required: [meta.upload.file] },
|
|
134
|
+
encoding: { [meta.upload.file]: { contentType: meta.upload.mediaTypes.join(', ') } },
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const responses: Record<string, unknown> = {}
|
|
142
|
+
if (meta.outputSchemas.size > 0) {
|
|
143
|
+
for (const [status, outputSchema] of meta.outputSchemas) {
|
|
144
|
+
responses[String(status)] = {
|
|
145
|
+
description: status === 200 ? 'Successful response' : `${status} response`,
|
|
146
|
+
content: {
|
|
147
|
+
'application/json': {
|
|
148
|
+
schema: toJsonSchema(outputSchema),
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
if (meta.file) {
|
|
155
|
+
responses[String(meta.file.status)] = {
|
|
156
|
+
description: meta.file.status === 200 ? 'Successful response' : `${meta.file.status} response`,
|
|
157
|
+
content: Object.fromEntries(meta.file.mediaTypes.map((type) => [type, { schema: { type: 'string', format: 'binary' } }])),
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
if (Object.keys(responses).length === 0) {
|
|
161
|
+
responses['200'] = { description: 'Successful response' }
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (meta.inputSchema || meta.querySchema || meta.paramsSchema) {
|
|
165
|
+
const declared = meta.outputSchemas.get(422)
|
|
166
|
+
responses['422'] = {
|
|
167
|
+
description: declared === undefined ? 'Validation failed' : '422 response',
|
|
168
|
+
content: {
|
|
169
|
+
'application/json': {
|
|
170
|
+
schema: declared === undefined
|
|
171
|
+
? VALIDATION_ERROR_SCHEMA
|
|
172
|
+
: { oneOf: [toJsonSchema(declared), VALIDATION_ERROR_SCHEMA] },
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (typeof meta.rateLimit === 'string' || (options?.rateLimited === true && meta.rateLimit !== false)) {
|
|
179
|
+
const declared = meta.outputSchemas.get(429)
|
|
180
|
+
responses['429'] = {
|
|
181
|
+
description: 'Too many requests',
|
|
182
|
+
content: {
|
|
183
|
+
'application/json': {
|
|
184
|
+
schema: declared === undefined
|
|
185
|
+
? TOO_MANY_REQUESTS_SCHEMA
|
|
186
|
+
: { oneOf: [toJsonSchema(declared), TOO_MANY_REQUESTS_SCHEMA] },
|
|
187
|
+
},
|
|
188
|
+
},
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (meta.requiresAuth) {
|
|
193
|
+
responses['401'] = { description: 'Unauthorized' }
|
|
194
|
+
}
|
|
195
|
+
if (meta.permission) {
|
|
196
|
+
responses['403'] = { description: 'Forbidden' }
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
operation['responses'] = responses
|
|
200
|
+
|
|
201
|
+
pathItem[method.toLowerCase()] = operation
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return { openapi: '3.0.3', info, paths }
|
|
205
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { generateOpenAPI } from './generate.js'
|
|
2
|
+
|
|
3
|
+
/** OpenAPI spec generation namespace. */
|
|
4
|
+
export const openapi = {
|
|
5
|
+
/** Generates an OpenAPI 3.1 document from route registrations — schemas derived from fitting() input/output. */
|
|
6
|
+
generate: generateOpenAPI,
|
|
7
|
+
}
|
package/src/test/auth.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { BaseAuth } from '../auth/auth/types.js'
|
|
2
|
+
|
|
3
|
+
export function mockAuth<TAuth extends BaseAuth>(overrides: Partial<TAuth> & { userId?: string; tenantId?: string } = {}): TAuth {
|
|
4
|
+
return {
|
|
5
|
+
userId: 'test-user',
|
|
6
|
+
tenantId: 'test-tenant',
|
|
7
|
+
...overrides,
|
|
8
|
+
} as TAuth
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function asTenant<TAuth extends BaseAuth>(tenantId: string, overrides: Partial<TAuth> = {}): TAuth {
|
|
12
|
+
return mockAuth<TAuth>({ tenantId, ...overrides } as Partial<TAuth> & { userId?: string; tenantId?: string })
|
|
13
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { beforeAll, afterAll, beforeEach, afterEach } from 'vitest'
|
|
2
|
+
import { loadEnvFiles } from '../core/config/env.js'
|
|
3
|
+
import { discoverForTestFork } from './discover-fork.js'
|
|
4
|
+
import { setupTestDatabases } from './setup.js'
|
|
5
|
+
import { beginTestIsolation, endTestIsolation, type TestContext } from './plugin.js'
|
|
6
|
+
import { _setTestState, teardownAfterAll, useTestDb } from './vitest.js'
|
|
7
|
+
import { logSlowPhases, SETUP_HOOK_TIMEOUT_MS } from './progress.js'
|
|
8
|
+
import { TEARDOWN_HOOK_TIMEOUT_MS } from '../trace/runtime/pending-writes.js'
|
|
9
|
+
|
|
10
|
+
loadEnvFiles()
|
|
11
|
+
|
|
12
|
+
let _testContext: TestContext | null = null
|
|
13
|
+
|
|
14
|
+
const instance = await discoverForTestFork()
|
|
15
|
+
|
|
16
|
+
_setTestState(instance)
|
|
17
|
+
|
|
18
|
+
const dbNames = instance.config.databaseNames()
|
|
19
|
+
if (dbNames.length === 1) {
|
|
20
|
+
;(globalThis as Record<string, unknown>)['db'] = useTestDb()
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Setup narrates: any phase that holds long enough to explain a late hook gets
|
|
24
|
+
// a line, and setup fails by name when a phase stops advancing — never merely
|
|
25
|
+
// because a fixed wall clock elapsed while it queued behind peer forks (#380).
|
|
26
|
+
beforeAll(async () => {
|
|
27
|
+
await setupTestDatabases(instance, { onProgress: logSlowPhases() })
|
|
28
|
+
}, SETUP_HOOK_TIMEOUT_MS)
|
|
29
|
+
|
|
30
|
+
afterAll(async () => {
|
|
31
|
+
await teardownAfterAll(instance)
|
|
32
|
+
}, TEARDOWN_HOOK_TIMEOUT_MS)
|
|
33
|
+
|
|
34
|
+
beforeEach(async ({ task }) => {
|
|
35
|
+
_testContext = await beginTestIsolation(instance, task)
|
|
36
|
+
_setTestState(instance, _testContext)
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
afterEach(async () => {
|
|
40
|
+
if (_testContext) {
|
|
41
|
+
await endTestIsolation(instance, _testContext)
|
|
42
|
+
_testContext = null
|
|
43
|
+
_setTestState(instance, null)
|
|
44
|
+
}
|
|
45
|
+
}, TEARDOWN_HOOK_TIMEOUT_MS)
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import type { Manifold } from '../core/pipework.js'
|
|
2
|
+
import { runInContext } from '../request/context/store.js'
|
|
3
|
+
import { createRequestContext, createJobContext } from '../request/context/index.js'
|
|
4
|
+
import { isTracingEnabled } from '../trace/entry/tracing-enabled.js'
|
|
5
|
+
import { openTrace } from '../trace/entry/open-trace.js'
|
|
6
|
+
import { parentTraceRef } from '../trace/entry/parent-trace.js'
|
|
7
|
+
import { finalizeTrace } from '../trace/entry/finalize-trace.js'
|
|
8
|
+
import { closeTrace } from '../trace/entry/close-trace.js'
|
|
9
|
+
import { _isInTestIsolation } from './vitest.js'
|
|
10
|
+
|
|
11
|
+
export interface WithContextOptions {
|
|
12
|
+
instance: Manifold
|
|
13
|
+
auth?: unknown
|
|
14
|
+
tenant?: string | null
|
|
15
|
+
requestId?: string
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function withContext<T>(options: WithContextOptions, fn: () => T): T {
|
|
19
|
+
if (_isInTestIsolation()) {
|
|
20
|
+
throw new Error(
|
|
21
|
+
'[pipework] withContext() creates a fresh context that bypasses test isolation.\n' +
|
|
22
|
+
' Queries will not use the test transaction and will not roll back.\n\n' +
|
|
23
|
+
' Use withFlow() instead — it preserves the test transaction:\n\n' +
|
|
24
|
+
" import { withFlow, asTenant } from 'pipework/test'\n\n" +
|
|
25
|
+
" withFlow({ auth: asTenant('tenant-1'), tenant: 'tenant-1' }, async () => {\n" +
|
|
26
|
+
' // test transaction is preserved, rollback works\n' +
|
|
27
|
+
' })',
|
|
28
|
+
)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const trace = isTracingEnabled(options.instance)
|
|
32
|
+
? openTrace({
|
|
33
|
+
instance: options.instance,
|
|
34
|
+
entryKind: 'test',
|
|
35
|
+
parent: parentTraceRef(),
|
|
36
|
+
tenant: options.tenant ?? null,
|
|
37
|
+
auth: options.auth ?? null,
|
|
38
|
+
requestId: options.requestId ?? null,
|
|
39
|
+
sessionId: null,
|
|
40
|
+
})
|
|
41
|
+
: null
|
|
42
|
+
|
|
43
|
+
const ctx = createRequestContext({
|
|
44
|
+
instance: options.instance,
|
|
45
|
+
auth: options.auth ?? null,
|
|
46
|
+
tenant: options.tenant ?? null,
|
|
47
|
+
trace,
|
|
48
|
+
metadata: {
|
|
49
|
+
method: 'TEST',
|
|
50
|
+
path: '/test',
|
|
51
|
+
startedAt: Date.now(),
|
|
52
|
+
traceId: options.requestId ?? 'test-trace',
|
|
53
|
+
sessionId: null,
|
|
54
|
+
},
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
try {
|
|
58
|
+
return finalizeTrace(options.instance, trace, runInContext(ctx, fn))
|
|
59
|
+
} catch (err) {
|
|
60
|
+
void closeTrace(options.instance, trace, 'error')
|
|
61
|
+
throw err
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function withJobContext<T>(options: WithContextOptions, fn: () => T): T {
|
|
66
|
+
const trace = isTracingEnabled(options.instance)
|
|
67
|
+
? openTrace({
|
|
68
|
+
instance: options.instance,
|
|
69
|
+
entryKind: 'test',
|
|
70
|
+
parent: parentTraceRef(),
|
|
71
|
+
tenant: options.tenant ?? null,
|
|
72
|
+
auth: options.auth ?? null,
|
|
73
|
+
requestId: options.requestId ?? null,
|
|
74
|
+
sessionId: null,
|
|
75
|
+
})
|
|
76
|
+
: null
|
|
77
|
+
|
|
78
|
+
const ctx = createJobContext({
|
|
79
|
+
instance: options.instance,
|
|
80
|
+
auth: options.auth ?? null,
|
|
81
|
+
tenant: options.tenant ?? null,
|
|
82
|
+
trace,
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
try {
|
|
86
|
+
return finalizeTrace(options.instance, trace, runInContext(ctx, fn))
|
|
87
|
+
} catch (err) {
|
|
88
|
+
void closeTrace(options.instance, trace, 'error')
|
|
89
|
+
throw err
|
|
90
|
+
}
|
|
91
|
+
}
|