pipework 0.251.0 → 0.252.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/dist/core/config/load.d.ts +2 -0
- package/dist/core/config/load.d.ts.map +1 -1
- package/dist/core/config/load.js +1 -0
- package/dist/core/config/load.js.map +1 -1
- package/dist/core/config/namespace.d.ts +1 -0
- package/dist/core/config/namespace.d.ts.map +1 -1
- package/dist/core/config/schema.d.ts +2 -0
- package/dist/core/config/schema.d.ts.map +1 -1
- package/dist/core/config/schema.js +1 -0
- package/dist/core/config/schema.js.map +1 -1
- package/dist/core/surface/types.d.ts +1 -0
- package/dist/core/surface/types.d.ts.map +1 -1
- package/dist/data/db/namespace.d.ts +3 -0
- package/dist/data/db/namespace.d.ts.map +1 -1
- package/dist/data/db/namespace.js +3 -0
- package/dist/data/db/namespace.js.map +1 -1
- package/dist/data/db/pool.d.ts.map +1 -1
- package/dist/data/db/pool.js +6 -1
- package/dist/data/db/pool.js.map +1 -1
- package/dist/data/db/query-label.d.ts +5 -0
- package/dist/data/db/query-label.d.ts.map +1 -0
- package/dist/data/db/query-label.js +11 -0
- package/dist/data/db/query-label.js.map +1 -0
- package/dist/data/db/query-watch.d.ts +21 -0
- package/dist/data/db/query-watch.d.ts.map +1 -0
- package/dist/data/db/query-watch.js +90 -0
- package/dist/data/db/query-watch.js.map +1 -0
- package/dist/data/query/postgres-js/driver.js +1 -1
- package/dist/data/query/postgres-js/driver.js.map +1 -1
- package/dist/data/query/postgres-js/session.d.ts +5 -1
- package/dist/data/query/postgres-js/session.d.ts.map +1 -1
- package/dist/data/query/postgres-js/session.js +16 -11
- package/dist/data/query/postgres-js/session.js.map +1 -1
- package/dist/data/query/utils.d.ts +2 -0
- package/dist/data/query/utils.d.ts.map +1 -1
- package/dist/data/query/utils.js.map +1 -1
- package/dist/infra/credits/schema.d.ts +1 -1
- package/dist/test/plugin.d.ts.map +1 -1
- package/dist/test/plugin.js +10 -4
- package/dist/test/plugin.js.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 +854 -0
- package/src/infra/support/http.ts +229 -0
- package/src/infra/support/index.ts +7 -0
- package/src/infra/support/namespace.ts +12 -0
- package/src/infra/support/schema.ts +96 -0
- package/src/infra/webhook/inbound.ts +45 -0
- package/src/infra/webhook/index.ts +5 -0
- package/src/infra/webhook/namespace.ts +24 -0
- package/src/infra/webhook/outbound.ts +315 -0
- package/src/infra/webhook/sign.ts +32 -0
- package/src/infra/webhook/verify.ts +82 -0
- package/src/invariants/assert.ts +26 -0
- package/src/invariants/index.ts +1 -0
- package/src/lint/config.ts +56 -0
- package/src/lint/doctrine.ts +163 -0
- package/src/lint/include.ts +97 -0
- package/src/lint/index.ts +3 -0
- package/src/lint/preset.ts +10 -0
- package/src/lint/resolver.ts +218 -0
- package/src/lint/rules/no-reexports.ts +46 -0
- package/src/request/behavior/audited.ts +85 -0
- package/src/request/behavior/cached.ts +62 -0
- package/src/request/behavior/compose.ts +28 -0
- package/src/request/behavior/index.ts +5 -0
- package/src/request/behavior/namespace.ts +16 -0
- package/src/request/behavior/types.ts +26 -0
- package/src/request/behavior/versioned.ts +54 -0
- package/src/request/context/create.ts +103 -0
- package/src/request/context/guard.ts +49 -0
- package/src/request/context/index.ts +4 -0
- package/src/request/context/run-in-transaction.ts +200 -0
- package/src/request/context/store.ts +28 -0
- package/src/request/context/types.ts +50 -0
- package/src/request/di/builder.ts +535 -0
- package/src/request/di/index.ts +8 -0
- package/src/request/di/resolve.ts +271 -0
- package/src/request/di/types.ts +262 -0
- package/src/request/fitting/namespace.ts +7 -0
- package/src/request/fixture/batch.ts +71 -0
- package/src/request/fixture/builder.ts +26 -0
- package/src/request/fixture/errors.ts +10 -0
- package/src/request/fixture/index.ts +8 -0
- package/src/request/fixture/namespace.ts +23 -0
- package/src/request/fixture/paginate.ts +44 -0
- package/src/request/fixture/to-handlers.ts +260 -0
- package/src/request/fixture/types.ts +79 -0
- package/src/request/flow/act-as.ts +78 -0
- package/src/request/flow/namespace.ts +17 -0
- package/src/request/http/error-step.ts +42 -0
- package/src/request/http/file.ts +141 -0
- package/src/request/http/index.ts +4 -0
- package/src/request/http/middleware.ts +141 -0
- package/src/request/http/namespace.ts +5 -0
- package/src/request/http/rate-limit-classes.ts +170 -0
- package/src/request/http/response.ts +82 -0
- package/src/request/http/server.ts +916 -0
- package/src/request/http/sniff.ts +261 -0
- package/src/request/http/types.ts +250 -0
- package/src/request/http/upload.ts +284 -0
- package/src/request/internal/text.ts +6 -0
- package/src/request/openapi/generate.ts +205 -0
- package/src/request/openapi/index.ts +2 -0
- package/src/request/openapi/namespace.ts +7 -0
- package/src/test/auth.ts +13 -0
- package/src/test/auto-setup.ts +45 -0
- package/src/test/context.ts +91 -0
- package/src/test/crawl-fixture.ts +157 -0
- package/src/test/discover-fork.ts +17 -0
- package/src/test/fingerprint.ts +135 -0
- package/src/test/globals.d.ts +7 -0
- package/src/test/index.ts +19 -0
- package/src/test/namespace.ts +87 -0
- package/src/test/plugin.ts +189 -0
- package/src/test/progress.ts +185 -0
- package/src/test/reap-after-run.ts +28 -0
- package/src/test/reap.ts +286 -0
- package/src/test/reset.ts +253 -0
- package/src/test/savepoints.ts +172 -0
- package/src/test/setup.ts +1422 -0
- package/src/test/system-channel.ts +50 -0
- package/src/test/vitest-plugin.ts +142 -0
- package/src/test/vitest-workspace.ts +507 -0
- package/src/test/vitest.ts +333 -0
- package/src/test/watch.ts +105 -0
- package/src/trace/capture/bounded-serializer.ts +146 -0
- package/src/trace/cardinality/analyze.ts +173 -0
- package/src/trace/cardinality/branch-context.ts +214 -0
- package/src/trace/cardinality/check.ts +33 -0
- package/src/trace/cardinality/classify-edge.ts +64 -0
- package/src/trace/cardinality/structural-max.ts +107 -0
- package/src/trace/check/coverage-check.ts +188 -0
- package/src/trace/check/flow-check.ts +103 -0
- package/src/trace/check/flow-step-site.ts +234 -0
- package/src/trace/check/io-pattern.ts +116 -0
- package/src/trace/codemod/add-cardinality.ts +152 -0
- package/src/trace/codemod/run.ts +80 -0
- package/src/trace/entry/child-trace.ts +21 -0
- package/src/trace/entry/close-trace.ts +87 -0
- package/src/trace/entry/finalize-trace.ts +28 -0
- package/src/trace/entry/open-trace.ts +138 -0
- package/src/trace/entry/parent-trace.ts +20 -0
- package/src/trace/entry/parse-traceparent.ts +39 -0
- package/src/trace/entry/process-start.ts +38 -0
- package/src/trace/entry/trace-context.ts +26 -0
- package/src/trace/entry/tracing-enabled.ts +10 -0
- package/src/trace/external/record.ts +195 -0
- package/src/trace/external/report.ts +66 -0
- package/src/trace/namespace.ts +19 -0
- package/src/trace/partition/day.ts +47 -0
- package/src/trace/partition/index.ts +14 -0
- package/src/trace/partition/partition-name.ts +23 -0
- package/src/trace/partition/plan.ts +82 -0
- package/src/trace/partition/schedule.ts +26 -0
- package/src/trace/partition/startup-hook.ts +39 -0
- package/src/trace/partition/sync.ts +154 -0
- package/src/trace/partition/tables.ts +53 -0
- package/src/trace/read/tree.ts +83 -0
- package/src/trace/retain/copy-on-error.ts +76 -0
- package/src/trace/runtime/expire-stale-traces.ts +54 -0
- package/src/trace/runtime/flow-step.ts +147 -0
- package/src/trace/runtime/flush-steps.ts +31 -0
- package/src/trace/runtime/pending-writes.ts +54 -0
- package/src/trace/runtime/step-buffer.ts +298 -0
- package/src/trace/runtime/step-error.ts +21 -0
- package/src/trace/runtime/step-meta.ts +55 -0
- package/src/trace/runtime/thenable.ts +12 -0
- package/src/trace/runtime/truncate-trace.ts +62 -0
- package/src/trace/schema/trace-fields.ts +52 -0
- package/src/trace/schema/trace-retained.ts +22 -0
- package/src/trace/schema/trace-step-fields.ts +44 -0
- package/src/trace/schema/trace-step-retained.ts +25 -0
- package/src/trace/schema/trace-step.ts +18 -0
- package/src/trace/schema/trace.ts +12 -0
- package/src/trace/transformer/apply.ts +79 -0
- package/src/trace/transformer/build-meta-literal.ts +30 -0
- package/src/trace/transformer/find-flow-step-sites.ts +85 -0
- package/src/trace/transformer/inject-flow-step-import.ts +39 -0
- package/src/trace/transformer/transform-error.ts +14 -0
- package/src/trace/transformer/transform-flow-steps.ts +123 -0
- package/src/trace/transformer/wrap-function-declaration.ts +62 -0
- package/src/trace/transformer/wrap-variable-declaration.ts +67 -0
- package/src/workspace/affected.ts +110 -0
- package/src/workspace/changed-files.ts +163 -0
- package/src/workspace/check-deps.ts +213 -0
- package/src/workspace/checksum.ts +20 -0
- package/src/workspace/dependency-graph.ts +67 -0
- package/src/workspace/detect-pipework-copies.ts +93 -0
- package/src/workspace/discover.ts +206 -0
- package/src/workspace/generate.ts +401 -0
- package/src/workspace/index.ts +7 -0
- package/src/workspace/module-imports-pipework.ts +39 -0
- package/src/workspace/module-package-name.ts +12 -0
- package/src/workspace/scrub-git-env.ts +23 -0
- package/src/workspace/source-paths.ts +36 -0
- package/src/workspace/sync.ts +182 -0
- package/dist/REFERENCE.md +0 -1477
- package/dist/infra/access-links/rate-limit.d.ts +0 -10
- package/dist/infra/access-links/rate-limit.d.ts.map +0 -1
- package/dist/infra/access-links/rate-limit.js +0 -28
- package/dist/infra/access-links/rate-limit.js.map +0 -1
- package/dist/infra/email/sigv4.d.ts +0 -24
- package/dist/infra/email/sigv4.d.ts.map +0 -1
- package/dist/infra/email/sigv4.js +0 -55
- package/dist/infra/email/sigv4.js.map +0 -1
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
import type { Schema } from '../../data/schema/types.js'
|
|
2
|
+
import { DEFAULT_DB } from './types.js'
|
|
3
|
+
import type { Handler, HandlerMeta, CheckpointFn, BinaryUpload, MultipartUpload } from './types.js'
|
|
4
|
+
import type { DB } from '../../data/db/types.js'
|
|
5
|
+
import type { Flow } from '../context/types.js'
|
|
6
|
+
import type { Manifold } from '../../core/pipework.js'
|
|
7
|
+
import type { TenantConfig } from '../../auth/tenant/types.js'
|
|
8
|
+
import { executeInTransaction } from '../context/run-in-transaction.js'
|
|
9
|
+
import type { HttpRequest, HttpResponse } from '../http/types.js'
|
|
10
|
+
import { requireContext } from '../context/store.js'
|
|
11
|
+
import { verifyTransactionBinding } from '../context/guard.js'
|
|
12
|
+
import { UnauthorizedError, ValidationError } from '../../core/errors/index.js'
|
|
13
|
+
import { parse } from '../../data/schema/parse.js'
|
|
14
|
+
import { unboundDb } from '../../data/db/unbound.js'
|
|
15
|
+
import { createScopedDb, createTenantGuardDb } from '../../auth/tenant/scoped-db.js'
|
|
16
|
+
|
|
17
|
+
export interface ResolveOptions {
|
|
18
|
+
readonly request?: HttpRequest
|
|
19
|
+
readonly response?: HttpResponse
|
|
20
|
+
readonly rawBody?: Buffer
|
|
21
|
+
readonly upload?: BinaryUpload | MultipartUpload
|
|
22
|
+
/**
|
|
23
|
+
* The `checkpoint` primitive for a manual-transaction handler — a job or a
|
|
24
|
+
* route. Injected as the `checkpoint` dep; the top-level database deps are
|
|
25
|
+
* then NOT injected, since a non-transactional handle outside a checkpoint
|
|
26
|
+
* would be a footgun.
|
|
27
|
+
*/
|
|
28
|
+
readonly checkpoint?: CheckpointFn
|
|
29
|
+
/** A job's own signal, injected as the `signal` dep. */
|
|
30
|
+
readonly signal?: AbortSignal
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Resolves the database deps a handler declared with `.use()` into the deps
|
|
35
|
+
* record keyed as the handler sees them ('db' for the default database, the
|
|
36
|
+
* name otherwise). Each handle is the context's bound transaction when one is
|
|
37
|
+
* active (request/job/checkpoint), otherwise a pooled connection.
|
|
38
|
+
*
|
|
39
|
+
* `scoped` (default `true`) wraps each handle in the tenant scope/guard proxy —
|
|
40
|
+
* the safe builder surface used for request and auto-mode job deps. The
|
|
41
|
+
* manual-mode `checkpoint` primitive passes `scoped: false` to hand the handler
|
|
42
|
+
* the raw bound transaction: a checkpointed job owns its own boundaries and runs
|
|
43
|
+
* arbitrary SQL, with tenant isolation enforced by the RLS session vars the
|
|
44
|
+
* checkpoint sets (`SET LOCAL pipework.tenant_id`), exactly as `pipe.transaction`
|
|
45
|
+
* already exposes a raw `tx`. Shared by both paths so they cannot drift.
|
|
46
|
+
*/
|
|
47
|
+
export function resolveDatabaseDeps(
|
|
48
|
+
databases: readonly string[],
|
|
49
|
+
ctx: Flow,
|
|
50
|
+
options?: { readonly scoped?: boolean },
|
|
51
|
+
): Record<string, DB> {
|
|
52
|
+
const scoped = options?.scoped ?? true
|
|
53
|
+
const deps: Record<string, DB> = {}
|
|
54
|
+
for (const dbName of databases) {
|
|
55
|
+
const isDefault = dbName === DEFAULT_DB
|
|
56
|
+
const resolvedName = isDefault ? ctx.instance.config.defaultDatabaseName() : dbName
|
|
57
|
+
const depKey = isDefault ? 'db' : dbName
|
|
58
|
+
|
|
59
|
+
const dbConfig = ctx.instance.config.database(resolvedName)
|
|
60
|
+
const bound = ctx.transactions.get(resolvedName)
|
|
61
|
+
let rawDb: DB
|
|
62
|
+
if (bound) {
|
|
63
|
+
verifyTransactionBinding(ctx, bound, resolvedName)
|
|
64
|
+
rawDb = bound.drizzle
|
|
65
|
+
} else {
|
|
66
|
+
rawDb = unboundDb(ctx, resolvedName, dbConfig, 'A handler\'s database dependency')
|
|
67
|
+
}
|
|
68
|
+
if (!scoped) {
|
|
69
|
+
deps[depKey] = rawDb
|
|
70
|
+
} else {
|
|
71
|
+
deps[depKey] = ctx.tenant !== null ? createScopedDb(rawDb, ctx.tenant) : createTenantGuardDb(rawDb)
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return deps
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** A route's query as its handler gets it: parsed by its schema, or the raw value when it has none. Throws when it fails the schema. */
|
|
78
|
+
export function parseRouteQuery(meta: { readonly querySchema: Schema | null }, raw: unknown): unknown {
|
|
79
|
+
return meta.querySchema ? parse(meta.querySchema as Schema, raw, 'query') : raw
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** A route's params as its handler gets them: parsed by its schema, or the raw value when it has none. Throws when they fail the schema. */
|
|
83
|
+
export function parseRouteParams(meta: { readonly paramsSchema: Schema | null }, raw: unknown): unknown {
|
|
84
|
+
return meta.paramsSchema ? parse(meta.paramsSchema as Schema, raw, 'params') : raw
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* A route's params and query as its handler gets them, for a hook that reports on a request:
|
|
89
|
+
* the parsed value, the raw one when the route has no schema, null when it fails its schema
|
|
90
|
+
* or when `meta` is null (no route known).
|
|
91
|
+
*/
|
|
92
|
+
export function parseRouteInputs(
|
|
93
|
+
meta: HandlerMeta | null,
|
|
94
|
+
rawQuery: unknown,
|
|
95
|
+
rawParams: unknown,
|
|
96
|
+
): { readonly params: unknown; readonly query: unknown } {
|
|
97
|
+
if (meta === null) return { params: null, query: null }
|
|
98
|
+
const orNull = (read: () => unknown): unknown => { try { return read() } catch { return null } }
|
|
99
|
+
return { params: orNull(() => parseRouteParams(meta, rawParams)), query: orNull(() => parseRouteQuery(meta, rawQuery)) }
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export async function resolveAndExecute<TDeps extends Record<string, unknown>, TReturn>(
|
|
103
|
+
handler: Handler<TDeps, TReturn>,
|
|
104
|
+
rawInput?: unknown,
|
|
105
|
+
rawQuery?: unknown,
|
|
106
|
+
rawParams?: unknown,
|
|
107
|
+
options?: ResolveOptions,
|
|
108
|
+
): Promise<TReturn> {
|
|
109
|
+
const ctx = requireContext()
|
|
110
|
+
const deps: Record<string, unknown> = {}
|
|
111
|
+
|
|
112
|
+
// Manual-transaction handlers take no top-level database handle — they reach
|
|
113
|
+
// the database only inside checkpoint(), which binds a fresh tenant-scoped
|
|
114
|
+
// transaction. Inject that primitive in place of the db deps.
|
|
115
|
+
if (handler.meta.jobTransaction === 'manual') {
|
|
116
|
+
if (options?.checkpoint === undefined) {
|
|
117
|
+
throw new ValidationError(
|
|
118
|
+
'Handler declares .transaction("manual") but no checkpoint primitive was provided.\n\n' +
|
|
119
|
+
' checkpoint() is supplied by the surface that runs the handler — jobs.execute\n' +
|
|
120
|
+
' for a job, the HTTP server for a route. Reaching here without one means the\n' +
|
|
121
|
+
' handler was executed directly rather than through a surface.\n',
|
|
122
|
+
)
|
|
123
|
+
}
|
|
124
|
+
deps['checkpoint'] = options.checkpoint
|
|
125
|
+
} else {
|
|
126
|
+
Object.assign(deps, resolveDatabaseDeps(handler.meta.databases, ctx))
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// A job handler's type promises `signal`; one run outside executeJob gets a signal that never aborts.
|
|
130
|
+
if (options?.signal !== undefined) deps['signal'] = options.signal
|
|
131
|
+
else if (handler.meta.isJob) deps['signal'] = new AbortController().signal
|
|
132
|
+
|
|
133
|
+
if (handler.meta.requiresAuth) {
|
|
134
|
+
if (ctx.auth === null) {
|
|
135
|
+
throw new UnauthorizedError()
|
|
136
|
+
}
|
|
137
|
+
deps['auth'] = ctx.auth
|
|
138
|
+
} else if (handler.meta.authRequirement === 'optional') {
|
|
139
|
+
deps['auth'] = ctx.auth
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (handler.meta.inputSchema) {
|
|
143
|
+
deps['input'] = parse(handler.meta.inputSchema as Schema, rawInput, 'body')
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (handler.meta.querySchema) {
|
|
147
|
+
deps['query'] = parseRouteQuery(handler.meta, rawQuery)
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (handler.meta.paramsSchema) {
|
|
151
|
+
deps['params'] = parseRouteParams(handler.meta, rawParams)
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (handler.meta.wantsRequest) {
|
|
155
|
+
if (options?.request === undefined) {
|
|
156
|
+
throw new ValidationError(
|
|
157
|
+
'Handler declared .request() but no HTTP request is available.\n\n' +
|
|
158
|
+
' This handler requires an HTTP context. It cannot be invoked as a background job\n' +
|
|
159
|
+
' or via direct resolveAndExecute() without providing a request object.\n',
|
|
160
|
+
)
|
|
161
|
+
}
|
|
162
|
+
deps['request'] = options.request
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (handler.meta.wantsResponse) {
|
|
166
|
+
if (options?.response === undefined) {
|
|
167
|
+
throw new ValidationError(
|
|
168
|
+
'Handler declared .response() but no HTTP response is available.\n\n' +
|
|
169
|
+
' This handler requires an HTTP context. It cannot be invoked as a background job\n' +
|
|
170
|
+
' or via direct resolveAndExecute() without providing a response object.\n',
|
|
171
|
+
)
|
|
172
|
+
}
|
|
173
|
+
deps['response'] = options.response
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (handler.meta.wantsRawBody) {
|
|
177
|
+
if (options?.rawBody === undefined) {
|
|
178
|
+
throw new ValidationError(
|
|
179
|
+
'Handler declared .rawBody() but no raw body is available.\n\n' +
|
|
180
|
+
' Raw body capture requires an HTTP context with body content.\n' +
|
|
181
|
+
' This handler cannot be invoked as a background job or via GET requests\n' +
|
|
182
|
+
' without providing a raw body buffer.\n',
|
|
183
|
+
)
|
|
184
|
+
}
|
|
185
|
+
deps['rawBody'] = options.rawBody
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (handler.meta.upload) {
|
|
189
|
+
if (options?.upload === undefined) {
|
|
190
|
+
throw new ValidationError(
|
|
191
|
+
'Handler declared .upload() but no uploaded file is available.\n\n' +
|
|
192
|
+
' An upload is supplied by the HTTP server for a route. This handler cannot be\n' +
|
|
193
|
+
' invoked as a background job or via direct resolveAndExecute() without one.\n',
|
|
194
|
+
)
|
|
195
|
+
}
|
|
196
|
+
deps['upload'] = options.upload
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const result = await handler.execute(deps as TDeps)
|
|
200
|
+
|
|
201
|
+
if (handler.meta.outputSchemas.size > 0) {
|
|
202
|
+
if (isHandlerResponse(result)) {
|
|
203
|
+
const response = result as { status: number; body?: unknown }
|
|
204
|
+
const bodySchema = handler.meta.outputSchemas.get(response.status)
|
|
205
|
+
if (bodySchema) {
|
|
206
|
+
return { status: response.status, body: parse(bodySchema as Schema, response.body, 'response') } as TReturn
|
|
207
|
+
}
|
|
208
|
+
return result
|
|
209
|
+
}
|
|
210
|
+
const defaultSchema = handler.meta.outputSchemas.get(200)
|
|
211
|
+
if (defaultSchema) {
|
|
212
|
+
return parse(defaultSchema as Schema, result, 'response') as TReturn
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return result
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function isHandlerResponse(value: unknown): boolean {
|
|
220
|
+
return value !== null && typeof value === 'object' && 'status' in value && typeof (value as Record<string, unknown>)['status'] === 'number'
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* The databases a handler's checkpoints run against: what it declared with
|
|
226
|
+
* `.use()`, with the default-database placeholder resolved to its real name.
|
|
227
|
+
*/
|
|
228
|
+
export function resolveHandlerDatabases<TDeps extends Record<string, unknown>, TReturn>(
|
|
229
|
+
handler: Handler<TDeps, TReturn>,
|
|
230
|
+
instance: Manifold,
|
|
231
|
+
): string[] {
|
|
232
|
+
const names = new Set<string>()
|
|
233
|
+
for (const dbName of handler.meta.databases) {
|
|
234
|
+
names.add(dbName === DEFAULT_DB ? instance.config.defaultDatabaseName() : dbName)
|
|
235
|
+
}
|
|
236
|
+
return [...names]
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Builds the `checkpoint` primitive for one manual-transaction run.
|
|
241
|
+
*
|
|
242
|
+
* Every call opens a fresh top-level transaction on the handler's databases,
|
|
243
|
+
* re-establishing the context's session variables so database-side RLS sees the
|
|
244
|
+
* tenant, and commits it independently of every other checkpoint. The handler
|
|
245
|
+
* gets the raw bound transactions: it owns its own boundaries, and tenant
|
|
246
|
+
* isolation is enforced by the session vars rather than by a scoped wrapper.
|
|
247
|
+
*
|
|
248
|
+
* Shared by the job surface and the HTTP surface so the two cannot drift in
|
|
249
|
+
* what a checkpoint is.
|
|
250
|
+
*/
|
|
251
|
+
export function buildCheckpoint<TDeps extends Record<string, unknown>, TReturn>(
|
|
252
|
+
handler: Handler<TDeps, TReturn>,
|
|
253
|
+
ctx: Flow,
|
|
254
|
+
databases: readonly string[],
|
|
255
|
+
tenant: TenantConfig | null,
|
|
256
|
+
timeoutMs: number,
|
|
257
|
+
signal?: AbortSignal,
|
|
258
|
+
refuseWhenAborted?: AbortSignal,
|
|
259
|
+
): CheckpointFn {
|
|
260
|
+
return (fn) =>
|
|
261
|
+
refuseWhenAborted?.aborted === true
|
|
262
|
+
? Promise.reject(refuseWhenAborted.reason)
|
|
263
|
+
: executeInTransaction(
|
|
264
|
+
ctx,
|
|
265
|
+
databases,
|
|
266
|
+
tenant,
|
|
267
|
+
timeoutMs,
|
|
268
|
+
() => fn(resolveDatabaseDeps(handler.meta.databases, requireContext(), { scoped: false })),
|
|
269
|
+
signal,
|
|
270
|
+
)
|
|
271
|
+
}
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
import type { CorsConfig } from '../http/types.js'
|
|
2
|
+
import type { Schema } from '../../data/schema/types.js'
|
|
3
|
+
import type { DB } from '../../data/db/index.js'
|
|
4
|
+
import type { Readable } from 'node:stream'
|
|
5
|
+
import type { BackoffConfig } from '../../async/jobs/queue.js'
|
|
6
|
+
|
|
7
|
+
/** The default database name constant ('default') used when pipe() is called without a name argument. */
|
|
8
|
+
export const DEFAULT_DB = '__pipework_default__' as const
|
|
9
|
+
|
|
10
|
+
/** Auth requirement level for a handler — 'required', 'optional', or 'public'. */
|
|
11
|
+
export type AuthRequirement = 'required' | 'optional' | 'none'
|
|
12
|
+
|
|
13
|
+
/** Permission requirement attached to a handler — resource, action, scope. */
|
|
14
|
+
export interface PermissionMeta {
|
|
15
|
+
readonly resource: string
|
|
16
|
+
readonly action: string
|
|
17
|
+
readonly scope?: string
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Retry configuration for job handlers — maxAttempts, backoff strategy, delay. */
|
|
21
|
+
export interface JobRetryConfig {
|
|
22
|
+
readonly attempts: number
|
|
23
|
+
readonly backoff?: BackoffConfig
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* How a job's database transaction(s) are managed.
|
|
28
|
+
*
|
|
29
|
+
* - `'auto'` (default): the framework wraps the whole `.fit` run in one outer,
|
|
30
|
+
* tenant-scoped transaction — atomic / all-or-nothing.
|
|
31
|
+
* - `'manual'`: the framework propagates the job context but opens no outer
|
|
32
|
+
* transaction. The handler receives a `checkpoint(fn)` primitive and owns its
|
|
33
|
+
* own transaction boundaries, committing incrementally within a single warm
|
|
34
|
+
* run. The contract shifts from atomic to at-least-once / idempotent-on-resume.
|
|
35
|
+
*/
|
|
36
|
+
export type JobTransactionMode = 'auto' | 'manual'
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* The `checkpoint` primitive injected into a manual-transaction job's deps.
|
|
40
|
+
* Each call opens a fresh top-level, tenant-scoped transaction (re-establishing
|
|
41
|
+
* the tenant `SET LOCAL` session vars so database-side RLS sees the tenant),
|
|
42
|
+
* runs `fn` with the participating databases bound, and commits durably and
|
|
43
|
+
* independently of any other checkpoint. `TDb` is the record of databases the
|
|
44
|
+
* handler declared with `.use()`.
|
|
45
|
+
*/
|
|
46
|
+
export type Checkpoint<TDb> = <R>(fn: (dbs: TDb) => Promise<R>) => Promise<R>
|
|
47
|
+
|
|
48
|
+
/** The erased runtime shape of {@link Checkpoint}, used where the db-deps type is not tracked. */
|
|
49
|
+
export type CheckpointFn = <R>(fn: (dbs: Record<string, DB>) => Promise<R>) => Promise<R>
|
|
50
|
+
|
|
51
|
+
/** Metadata attached to a handler — route info, auth requirements, input/output schemas, database names. */
|
|
52
|
+
export interface HandlerMeta {
|
|
53
|
+
readonly databases: readonly string[]
|
|
54
|
+
readonly requiresAuth: boolean
|
|
55
|
+
readonly authRequirement: AuthRequirement
|
|
56
|
+
readonly inputSchema: Schema | null
|
|
57
|
+
readonly outputSchemas: ReadonlyMap<number, Schema>
|
|
58
|
+
readonly querySchema: Schema | null
|
|
59
|
+
readonly paramsSchema: Schema | null
|
|
60
|
+
readonly route: RouteMeta | null
|
|
61
|
+
/** This route's own CORS grant (replaces the server default); `false` closes it. */
|
|
62
|
+
readonly cors?: CorsConfig | false | null
|
|
63
|
+
/** The named rate limit class this route is in; `false` exempts it; null follows the auth requirement. */
|
|
64
|
+
readonly rateLimit?: string | false | null
|
|
65
|
+
readonly isJob: boolean
|
|
66
|
+
readonly jobType: string | null
|
|
67
|
+
readonly jobRetry: JobRetryConfig | null
|
|
68
|
+
readonly jobTimeout: number | null
|
|
69
|
+
readonly jobTransaction: JobTransactionMode
|
|
70
|
+
readonly permission: PermissionMeta | null
|
|
71
|
+
readonly wantsRequest: boolean
|
|
72
|
+
readonly wantsResponse: boolean
|
|
73
|
+
readonly wantsRawBody: boolean
|
|
74
|
+
readonly ignoresBody?: boolean
|
|
75
|
+
readonly upload?: UploadConfig | null
|
|
76
|
+
readonly file?: FileConfig | null
|
|
77
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
78
|
+
readonly onRefused?: RouteRefusedHook<any, any, any> | null
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** A route's file response, declared by `.file()`. */
|
|
82
|
+
export interface FileConfig {
|
|
83
|
+
/** Media types the route may answer with; `type/*` wildcards allowed. */
|
|
84
|
+
readonly mediaTypes: readonly string[]
|
|
85
|
+
/** Status of the file response (default 200). */
|
|
86
|
+
readonly status: number
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** What pipework tells an upload route's `onRefuse` hook about a file it refused. */
|
|
90
|
+
export interface UploadRefusal<TParams = unknown, TQuery = unknown> {
|
|
91
|
+
readonly status: 413 | 415
|
|
92
|
+
/** The error code in the response body. */
|
|
93
|
+
readonly code: string
|
|
94
|
+
/** Type found in the file's first bytes; null when none matched or the head was not read. */
|
|
95
|
+
readonly detectedType: string | null
|
|
96
|
+
/** Content-Type the sender declared for the file; '' when none. */
|
|
97
|
+
readonly declaredType: string
|
|
98
|
+
/** Lowercase filename extension; null without a filename or a dot. */
|
|
99
|
+
readonly extension: string | null
|
|
100
|
+
readonly filename: string | null
|
|
101
|
+
/** The request's Content-Length; null when chunked. */
|
|
102
|
+
readonly declaredLength: number | null
|
|
103
|
+
/** File bytes pipework had read when it refused. */
|
|
104
|
+
readonly bytesRead: number
|
|
105
|
+
readonly accepted: readonly string[]
|
|
106
|
+
/** The request's auth (null on a public route). */
|
|
107
|
+
readonly auth: unknown
|
|
108
|
+
readonly requestId: string
|
|
109
|
+
/**
|
|
110
|
+
* The route's params parsed by its schema exactly as the handler receives them
|
|
111
|
+
* (transforms and coercions applied); the raw params when the route declares no
|
|
112
|
+
* schema; null when they fail the schema (the request would be refused too, but
|
|
113
|
+
* a refusal of the file can win the race).
|
|
114
|
+
*/
|
|
115
|
+
readonly params: TParams | null
|
|
116
|
+
/** The route's query, on the same terms as `params`. */
|
|
117
|
+
readonly query: TQuery | null
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Runs once for each 413 or 415 pipework answers for a file, inside the
|
|
122
|
+
* request's context. A returned non-empty string replaces the error message;
|
|
123
|
+
* status and code stay. A throw is logged and the default message is sent.
|
|
124
|
+
*/
|
|
125
|
+
export type UploadRefuseHook<TParams = unknown, TQuery = unknown> = (refusal: UploadRefusal<TParams, TQuery>) => string | void | Promise<string | void>
|
|
126
|
+
|
|
127
|
+
/** What pipework tells a route's `onRefused` hook about a 4xx it is answering. */
|
|
128
|
+
export interface RouteRefusal<TAuth = unknown, TParams = unknown, TQuery = unknown> {
|
|
129
|
+
/** The response status, 400-499. */
|
|
130
|
+
readonly status: number
|
|
131
|
+
/** The error code in the response body. */
|
|
132
|
+
readonly code: string
|
|
133
|
+
readonly message: string
|
|
134
|
+
/** The body's `fields`; set only for VALIDATION_ERROR. */
|
|
135
|
+
readonly fields: Record<string, readonly string[]> | null
|
|
136
|
+
/** The body's `retryAfterSeconds` on a 429 that has one; else null. */
|
|
137
|
+
readonly retryAfterSeconds: number | null
|
|
138
|
+
/** The request's auth; null when auth itself refused or the request has no context. */
|
|
139
|
+
readonly auth: TAuth | null
|
|
140
|
+
/** The route's params as the handler would get them; null when they fail the schema. */
|
|
141
|
+
readonly params: TParams | null
|
|
142
|
+
/** The route's query, on the same terms as `params`. */
|
|
143
|
+
readonly query: TQuery | null
|
|
144
|
+
readonly request: { readonly headers: Record<string, string | undefined>; readonly ip: string; readonly ips: readonly string[] }
|
|
145
|
+
readonly requestId: string
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Runs once for each 4xx a route answers, inside the request's context. A returned
|
|
150
|
+
* plain object is merged into the response's `error`; `code`, `message`, `fields`
|
|
151
|
+
* and `details` always keep pipework's values.
|
|
152
|
+
*/
|
|
153
|
+
export type RouteRefusedHook<TAuth = unknown, TParams = unknown, TQuery = unknown> =
|
|
154
|
+
(refusal: RouteRefusal<TAuth, TParams, TQuery>) => Record<string, unknown> | void | Promise<Record<string, unknown> | void>
|
|
155
|
+
|
|
156
|
+
/** Upload whose whole request body is the file's bytes. */
|
|
157
|
+
export interface BinaryUploadConfig<TParams = unknown, TQuery = unknown> {
|
|
158
|
+
readonly kind: 'binary'
|
|
159
|
+
/** Accepted media types, matched against the type detected from the first bytes; `type/*` wildcards allowed, and a star on each side of the slash for any type. Others get 415. */
|
|
160
|
+
readonly mediaTypes: readonly string[]
|
|
161
|
+
/** Largest accepted body in bytes. Over it gets 413. */
|
|
162
|
+
readonly maxBytes: number
|
|
163
|
+
readonly onRefuse?: UploadRefuseHook<TParams, TQuery>
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/** Upload whose file is one part of a `multipart/form-data` body. */
|
|
167
|
+
export interface MultipartUploadConfig<TParams = unknown, TQuery = unknown> {
|
|
168
|
+
readonly kind: 'multipart'
|
|
169
|
+
/** Name of the form field carrying the file. */
|
|
170
|
+
readonly file: string
|
|
171
|
+
/** Accepted media types of the file, matched against the type detected from its first bytes; `type/*` wildcards allowed, and a star on each side of the slash for any type. Others get 415. */
|
|
172
|
+
readonly mediaTypes: readonly string[]
|
|
173
|
+
/** Largest accepted file in bytes. Over it gets 413. */
|
|
174
|
+
readonly maxBytes: number
|
|
175
|
+
/** Text field names accepted ahead of the file part; any other field gets 422. */
|
|
176
|
+
readonly fields?: readonly string[]
|
|
177
|
+
readonly onRefuse?: UploadRefuseHook<TParams, TQuery>
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export type UploadConfig = BinaryUploadConfig | MultipartUploadConfig
|
|
181
|
+
|
|
182
|
+
/** The type fields shared by both upload deps. */
|
|
183
|
+
interface UploadTypes {
|
|
184
|
+
/** The type the file is treated as, and what `mediaTypes` was matched against. */
|
|
185
|
+
readonly mediaType: string
|
|
186
|
+
/** Type found in the first bytes; null when no signature matched. */
|
|
187
|
+
readonly detectedType: string | null
|
|
188
|
+
/** Content-Type the sender declared for the file; '' when none. */
|
|
189
|
+
readonly declaredType: string
|
|
190
|
+
/** Lowercase filename extension; null when there is no filename or no dot. */
|
|
191
|
+
readonly extension: string | null
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/** The `upload` dep of a binary upload route. */
|
|
195
|
+
export interface BinaryUpload extends UploadTypes {
|
|
196
|
+
readonly stream: Readable
|
|
197
|
+
/** Declared Content-Length, or null for a chunked body. */
|
|
198
|
+
readonly length: number | null
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/** The `upload` dep of a multipart upload route. */
|
|
202
|
+
export interface MultipartUpload extends UploadTypes {
|
|
203
|
+
readonly stream: Readable
|
|
204
|
+
readonly filename: string | null
|
|
205
|
+
/** Text fields that came before the file part. */
|
|
206
|
+
readonly fields: Readonly<Record<string, string>>
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* What a route screen is shown: the request line and headers, plus the declared
|
|
211
|
+
* body size. The body has not been read — nothing here forces it to be buffered.
|
|
212
|
+
*/
|
|
213
|
+
export interface ScreenedRequest {
|
|
214
|
+
readonly method: string
|
|
215
|
+
readonly url: string
|
|
216
|
+
readonly headers: Readonly<Record<string, string | undefined>>
|
|
217
|
+
/** Parsed `content-length`, or null when the header is absent or unparseable. */
|
|
218
|
+
readonly contentLength: number | null
|
|
219
|
+
/** The route's params parsed by its schema as the handler gets them; the raw params with no schema; null when they fail it. */
|
|
220
|
+
readonly params: unknown
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/** A screen's refusal — the status and named reason sent instead of reading the body. */
|
|
224
|
+
export interface ScreenRefusal {
|
|
225
|
+
readonly status: number
|
|
226
|
+
readonly reason: string
|
|
227
|
+
/** Extra fields merged into the response body alongside `reason`. */
|
|
228
|
+
readonly body?: Readonly<Record<string, unknown>>
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Runs before the body is parsed and before the body limit applies. Return a
|
|
233
|
+
* refusal to answer the request there and then; return nothing to let it through.
|
|
234
|
+
*/
|
|
235
|
+
export type RouteScreen = (request: ScreenedRequest) => ScreenRefusal | null | undefined | void | Promise<ScreenRefusal | null | undefined | void>
|
|
236
|
+
|
|
237
|
+
/** HTTP route metadata — method, path, and registered middleware. */
|
|
238
|
+
export interface RouteMeta {
|
|
239
|
+
readonly method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'
|
|
240
|
+
readonly path: string
|
|
241
|
+
/** Per-route max request body size in bytes — overrides the server-level bodyLimit for this route. */
|
|
242
|
+
readonly bodyLimit?: number
|
|
243
|
+
/** Pre-parse screen — inspects headers before the body is read, and may refuse. */
|
|
244
|
+
readonly screen?: RouteScreen
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/** HTTP method literal type — 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'. */
|
|
248
|
+
export type HttpMethod = RouteMeta['method']
|
|
249
|
+
|
|
250
|
+
/** A resolved handler produced by fitting().fit() — carries metadata, dependencies, and the handler function. */
|
|
251
|
+
export interface Handler<TDeps extends Record<string, unknown>, TReturn> {
|
|
252
|
+
execute(deps: TDeps): Promise<TReturn>
|
|
253
|
+
readonly meta: HandlerMeta
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/** Extracts the dependency type from a Handler — InferDeps<typeof myHandler> gives you the deps object shape. */
|
|
257
|
+
export type InferDeps<H> = H extends Handler<infer D, unknown> ? D : never
|
|
258
|
+
/** Extracts the return type from a Handler — InferReturn<typeof myHandler> gives you the resolved return type. */
|
|
259
|
+
export type InferReturn<H> = H extends Handler<Record<string, unknown>, infer R> ? R : never
|
|
260
|
+
|
|
261
|
+
/** Map of database names to their resolved DB instances for a handler. */
|
|
262
|
+
export type DatabaseDeps<N extends string> = Record<N, DB>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { createBuilder } from '../di/builder.js'
|
|
2
|
+
|
|
3
|
+
/** DI handler builder — chain .use(), .auth(), .input(), .route(), .fit() to build type-safe handlers with automatic dependency injection. */
|
|
4
|
+
export const fitting = Object.assign(
|
|
5
|
+
() => createBuilder(),
|
|
6
|
+
{},
|
|
7
|
+
)
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { DB } from '../../data/db/index.js'
|
|
2
|
+
import type { PermissionMeta } from '../di/types.js'
|
|
3
|
+
import { ValidationError } from '../../core/errors/index.js'
|
|
4
|
+
|
|
5
|
+
export interface BatchTargets {
|
|
6
|
+
readonly ids?: string[]
|
|
7
|
+
readonly filters?: Record<string, unknown>
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface BatchAction<TAuth> {
|
|
11
|
+
readonly permission?: PermissionMeta
|
|
12
|
+
readonly preview?: (db: DB, auth: TAuth, tenant: string | null, targets: BatchTargets) => Promise<unknown>
|
|
13
|
+
readonly execute: (db: DB, auth: TAuth, tenant: string | null, targets: BatchTargets) => Promise<unknown>
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface BatchRequest {
|
|
17
|
+
readonly action: string
|
|
18
|
+
readonly targets: BatchTargets
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function validateBatchRequest(body: unknown, validActions: readonly string[]): BatchRequest {
|
|
22
|
+
if (body === null || typeof body !== 'object') {
|
|
23
|
+
throw new ValidationError('Batch request body must be an object')
|
|
24
|
+
}
|
|
25
|
+
const obj = body as Record<string, unknown>
|
|
26
|
+
|
|
27
|
+
const action = obj['action']
|
|
28
|
+
if (typeof action !== 'string' || action.length === 0) {
|
|
29
|
+
throw new ValidationError({ action: ['action is required and must be a non-empty string'] })
|
|
30
|
+
}
|
|
31
|
+
if (!validActions.includes(action)) {
|
|
32
|
+
throw new ValidationError({
|
|
33
|
+
action: [`Unknown batch action "${action}". Valid actions: ${validActions.join(', ')}`],
|
|
34
|
+
})
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const targets = obj['targets']
|
|
38
|
+
if (targets === null || targets === undefined || typeof targets !== 'object') {
|
|
39
|
+
throw new ValidationError({ targets: ['targets is required and must be an object'] })
|
|
40
|
+
}
|
|
41
|
+
const t = targets as Record<string, unknown>
|
|
42
|
+
|
|
43
|
+
const hasIds = Array.isArray(t['ids'])
|
|
44
|
+
const hasFilters = t['filters'] !== undefined && t['filters'] !== null && typeof t['filters'] === 'object'
|
|
45
|
+
|
|
46
|
+
if (!hasIds && !hasFilters) {
|
|
47
|
+
throw new ValidationError({ targets: ['targets must contain exactly one of "ids" or "filters"'] })
|
|
48
|
+
}
|
|
49
|
+
if (hasIds && hasFilters) {
|
|
50
|
+
throw new ValidationError({ targets: ['targets must contain exactly one of "ids" or "filters", not both'] })
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (hasIds) {
|
|
54
|
+
const ids = t['ids'] as unknown[]
|
|
55
|
+
if (ids.length === 0) {
|
|
56
|
+
throw new ValidationError({ 'targets.ids': ['ids array must not be empty'] })
|
|
57
|
+
}
|
|
58
|
+
for (const id of ids) {
|
|
59
|
+
if (typeof id !== 'string') {
|
|
60
|
+
throw new ValidationError({ 'targets.ids': ['all ids must be strings'] })
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return {
|
|
66
|
+
action,
|
|
67
|
+
targets: hasIds
|
|
68
|
+
? { ids: t['ids'] as string[] }
|
|
69
|
+
: { filters: t['filters'] as Record<string, unknown> },
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { ResourceOperations, Resource, ResourceMeta, PaginationConfig } from './types.js'
|
|
2
|
+
import type { AuthRequirement, PermissionMeta } from '../di/types.js'
|
|
3
|
+
|
|
4
|
+
interface ResourceBuilderState {
|
|
5
|
+
database: string
|
|
6
|
+
authRequirement: AuthRequirement
|
|
7
|
+
permission: PermissionMeta | null
|
|
8
|
+
pagination: PaginationConfig | null
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function buildResource<TAuth>(
|
|
12
|
+
name: string,
|
|
13
|
+
operations: ResourceOperations<TAuth>,
|
|
14
|
+
state: ResourceBuilderState,
|
|
15
|
+
): Resource<TAuth> {
|
|
16
|
+
const meta: ResourceMeta = {
|
|
17
|
+
name,
|
|
18
|
+
database: state.database,
|
|
19
|
+
authRequirement: state.authRequirement,
|
|
20
|
+
permission: state.permission,
|
|
21
|
+
pagination: state.pagination,
|
|
22
|
+
}
|
|
23
|
+
return { meta, operations }
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export { type ResourceBuilderState }
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { PipeworkError } from '../../core/errors/base.js'
|
|
2
|
+
|
|
3
|
+
export class MethodNotAllowedError extends PipeworkError {
|
|
4
|
+
readonly code = 'METHOD_NOT_ALLOWED' as const
|
|
5
|
+
readonly httpStatus = 405 as const
|
|
6
|
+
|
|
7
|
+
constructor(method: string, resource: string) {
|
|
8
|
+
super(`${method} not supported on ${resource}`)
|
|
9
|
+
}
|
|
10
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export type { Resource, ResourceOperations, ResourceMeta, ResourceId, ResourceListOp, ResourceListPaginatedOp, ResourceListCursorOp, ResourceGetOp, ResourceCreateOp, ResourceUpdateOp, ResourceDeleteOp, PaginationConfig, PaginationParams, PaginatedResponse } from './types.js'
|
|
2
|
+
export { buildResource } from './builder.js'
|
|
3
|
+
export { resourceToHandlers } from './to-handlers.js'
|
|
4
|
+
export { MethodNotAllowedError } from './errors.js'
|
|
5
|
+
export { encodeCursor, decodeCursor, buildPage } from './paginate.js'
|
|
6
|
+
export type { PageResult, CursorPaginationParams } from './paginate.js'
|
|
7
|
+
export { validateBatchRequest } from './batch.js'
|
|
8
|
+
export type { BatchAction, BatchTargets, BatchRequest } from './batch.js'
|