pipework 0.252.0 → 0.252.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +5 -0
- package/package.json +4 -3
- package/src/async/jobs/abortable-delay.ts +18 -0
- package/src/async/jobs/claims.ts +52 -0
- package/src/async/jobs/cron.ts +179 -0
- package/src/async/jobs/errors.ts +56 -0
- package/src/async/jobs/exclusion.ts +117 -0
- package/src/async/jobs/execute.ts +219 -0
- package/src/async/jobs/index.ts +10 -0
- package/src/async/jobs/listener.ts +135 -0
- package/src/async/jobs/namespace.ts +20 -0
- package/src/async/jobs/queue-door.ts +44 -0
- package/src/async/jobs/queue.ts +1012 -0
- package/src/async/jobs/run-script.ts +67 -0
- package/src/async/jobs/scheduler.ts +218 -0
- package/src/async/jobs/schedules-table.ts +37 -0
- package/src/async/jobs/serialize-error.ts +48 -0
- package/src/async/jobs/table.ts +191 -0
- package/src/async/pipeline/define.ts +53 -0
- package/src/async/pipeline/errors.ts +35 -0
- package/src/async/pipeline/execute.ts +53 -0
- package/src/async/pipeline/index.ts +13 -0
- package/src/async/pipeline/namespace.ts +12 -0
- package/src/async/pipeline/resume.ts +67 -0
- package/src/async/pipeline/run-steps.ts +98 -0
- package/src/async/pipeline/types.ts +57 -0
- package/src/async/state-machine/define.ts +64 -0
- package/src/async/state-machine/errors.ts +29 -0
- package/src/async/state-machine/index.ts +3 -0
- package/src/async/state-machine/namespace.ts +12 -0
- package/src/async/state-machine/transition.ts +90 -0
- package/src/async/state-machine/types.ts +37 -0
- package/src/async/steps/errors.ts +31 -0
- package/src/async/steps/graph.ts +1214 -0
- package/src/async/steps/hash.ts +40 -0
- package/src/async/steps/index.ts +7 -0
- package/src/async/steps/namespace.ts +7 -0
- package/src/async/steps/table.ts +97 -0
- package/src/async/steps/types.ts +318 -0
- package/src/audit/chain/index.ts +9 -0
- package/src/audit/chain/install.ts +113 -0
- package/src/audit/chain/retire.ts +52 -0
- package/src/audit/chain/verify.ts +257 -0
- package/src/audit/query/cursor.ts +37 -0
- package/src/audit/query/index.ts +9 -0
- package/src/audit/query/query.ts +223 -0
- package/src/audit/schema/audit-record-fields.ts +68 -0
- package/src/audit/schema/audit-record.ts +23 -0
- package/src/auth/auth/audit.ts +31 -0
- package/src/auth/auth/chain.ts +84 -0
- package/src/auth/auth/cookie.ts +136 -0
- package/src/auth/auth/email-links.ts +311 -0
- package/src/auth/auth/guard.ts +128 -0
- package/src/auth/auth/index.ts +18 -0
- package/src/auth/auth/multi-org.ts +337 -0
- package/src/auth/auth/namespace.ts +34 -0
- package/src/auth/auth/parse-expiry.ts +13 -0
- package/src/auth/auth/passwords.ts +110 -0
- package/src/auth/auth/schema.ts +110 -0
- package/src/auth/auth/sessions.ts +565 -0
- package/src/auth/auth/sign-in.ts +156 -0
- package/src/auth/auth/signing-keys.ts +69 -0
- package/src/auth/auth/types.ts +26 -0
- package/src/auth/auth/uuid.ts +10 -0
- package/src/auth/rbac/cache.ts +55 -0
- package/src/auth/rbac/check.ts +30 -0
- package/src/auth/rbac/create.ts +35 -0
- package/src/auth/rbac/index.ts +5 -0
- package/src/auth/rbac/middleware.ts +20 -0
- package/src/auth/rbac/namespace.ts +15 -0
- package/src/auth/rbac/resolve.ts +31 -0
- package/src/auth/rbac/schema.sql +31 -0
- package/src/auth/rbac/seed.ts +67 -0
- package/src/auth/rbac/tables.ts +32 -0
- package/src/auth/rbac/types.ts +65 -0
- package/src/auth/tenant/extract.ts +33 -0
- package/src/auth/tenant/in-tenant.ts +49 -0
- package/src/auth/tenant/index.ts +7 -0
- package/src/auth/tenant/namespace.ts +23 -0
- package/src/auth/tenant/propagate.ts +20 -0
- package/src/auth/tenant/rls.ts +120 -0
- package/src/auth/tenant/scope.ts +33 -0
- package/src/auth/tenant/scoped-db.ts +581 -0
- package/src/auth/tenant/types.ts +14 -0
- package/src/auth/tenant/validate.ts +27 -0
- package/src/cli/commands/add.ts +158 -0
- package/src/cli/commands/alerts.ts +98 -0
- package/src/cli/commands/check.ts +1418 -0
- package/src/cli/commands/codemod.ts +78 -0
- package/src/cli/commands/db.ts +356 -0
- package/src/cli/commands/dev.ts +101 -0
- package/src/cli/commands/generate.ts +75 -0
- package/src/cli/commands/init.ts +36 -0
- package/src/cli/commands/install.ts +28 -0
- package/src/cli/commands/lint.ts +62 -0
- package/src/cli/commands/migrate.ts +132 -0
- package/src/cli/commands/openapi.ts +63 -0
- package/src/cli/commands/remove.ts +72 -0
- package/src/cli/commands/run.ts +50 -0
- package/src/cli/commands/scope.ts +54 -0
- package/src/cli/commands/serve.ts +68 -0
- package/src/cli/commands/shard.ts +31 -0
- package/src/cli/commands/shared/config-pipework-version.ts +28 -0
- package/src/cli/commands/shared/find-config.ts +22 -0
- package/src/cli/commands/shared/load-config.ts +35 -0
- package/src/cli/commands/shared/pnpm-install.ts +95 -0
- package/src/cli/commands/shared/report-removed.ts +8 -0
- package/src/cli/commands/sync.ts +32 -0
- package/src/cli/commands/test.ts +307 -0
- package/src/cli/commands/upgrade.ts +48 -0
- package/src/cli/dev-bootstrap.ts +24 -0
- package/src/cli/errors.ts +10 -0
- package/src/cli/index.ts +303 -0
- package/src/cli/output.ts +17 -0
- package/src/cli/port.ts +15 -0
- package/src/cli/surface-kinds.ts +20 -0
- package/src/client/index.ts +4 -0
- package/src/client/poll.ts +130 -0
- package/src/client/report-fault.ts +119 -0
- package/src/core/config/app-pool.ts +19 -0
- package/src/core/config/aws-env.ts +29 -0
- package/src/core/config/base-dir.ts +24 -0
- package/src/core/config/discover.ts +64 -0
- package/src/core/config/env-resolve.ts +87 -0
- package/src/core/config/env-types.ts +116 -0
- package/src/core/config/env.ts +55 -0
- package/src/core/config/errors.ts +9 -0
- package/src/core/config/import-config.ts +40 -0
- package/src/core/config/index.ts +8 -0
- package/src/core/config/load.ts +417 -0
- package/src/core/config/namespace.ts +14 -0
- package/src/core/config/resolve.ts +195 -0
- package/src/core/config/schema.ts +488 -0
- package/src/core/config/statement-observer.ts +48 -0
- package/src/core/config/ts-inline-hooks.ts +75 -0
- package/src/core/config/ts-register.ts +4 -0
- package/src/core/config/ts-resolve-hooks.ts +101 -0
- package/src/core/errors/base.ts +49 -0
- package/src/core/errors/http.ts +146 -0
- package/src/core/errors/index.ts +2 -0
- package/src/core/errors/namespace.ts +31 -0
- package/src/core/lifecycle/drain.ts +33 -0
- package/src/core/lifecycle/health.ts +72 -0
- package/src/core/lifecycle/index.ts +4 -0
- package/src/core/lifecycle/namespace.ts +7 -0
- package/src/core/lifecycle/startup.ts +102 -0
- package/src/core/lifecycle/types.ts +36 -0
- package/src/core/logging/context.ts +60 -0
- package/src/core/logging/error-chain.ts +68 -0
- package/src/core/logging/index.ts +5 -0
- package/src/core/logging/logger.ts +65 -0
- package/src/core/logging/namespace.ts +16 -0
- package/src/core/logging/proxy.ts +22 -0
- package/src/core/logging/redact-match.ts +24 -0
- package/src/core/logging/types.ts +47 -0
- package/src/core/pipework.ts +566 -0
- package/src/core/runtime/broker.ts +69 -0
- package/src/core/surface/http.ts +133 -0
- package/src/core/surface/index.ts +11 -0
- package/src/core/surface/job-timeout.ts +40 -0
- package/src/core/surface/namespace.ts +13 -0
- package/src/core/surface/script.ts +33 -0
- package/src/core/surface/start.ts +142 -0
- package/src/core/surface/types.ts +225 -0
- package/src/core/surface/validate.ts +88 -0
- package/src/core/surface/worker.ts +528 -0
- package/src/core/validation/connectivity.ts +49 -0
- package/src/core/validation/domain.ts +99 -0
- package/src/core/validation/index.ts +5 -0
- package/src/core/validation/queue-tables.ts +312 -0
- package/src/core/validation/rls-role.ts +138 -0
- package/src/core/validation/startup.ts +51 -0
- package/src/core/validation/surfaces.ts +65 -0
- package/src/core/validation/types.ts +25 -0
- package/src/data/db/advisory-lock.ts +55 -0
- package/src/data/db/aggregates.ts +172 -0
- package/src/data/db/array-elements.ts +111 -0
- package/src/data/db/bulk-set.ts +53 -0
- package/src/data/db/col/index.ts +14 -0
- package/src/data/db/composed.ts +53 -0
- package/src/data/db/db.ts +276 -0
- package/src/data/db/excluded.ts +16 -0
- package/src/data/db/expressions.ts +473 -0
- package/src/data/db/filter.ts +90 -0
- package/src/data/db/function-source.ts +114 -0
- package/src/data/db/identifiers.ts +16 -0
- package/src/data/db/idx/index.ts +9 -0
- package/src/data/db/in-list.ts +49 -0
- package/src/data/db/index.ts +9 -0
- package/src/data/db/insert-split.ts +137 -0
- package/src/data/db/instrumentation.ts +82 -0
- package/src/data/db/maintenance.ts +28 -0
- package/src/data/db/namespace.ts +107 -0
- package/src/data/db/owner-labels.ts +9 -0
- package/src/data/db/pool.ts +187 -0
- package/src/data/db/query-label.ts +13 -0
- package/src/data/db/query-watch.ts +116 -0
- package/src/data/db/raw-client-warning.ts +37 -0
- package/src/data/db/readonly.ts +23 -0
- package/src/data/db/reservation.ts +151 -0
- package/src/data/db/schema-features.ts +14 -0
- package/src/data/db/serializable.ts +83 -0
- package/src/data/db/sql.ts +1 -0
- package/src/data/db/timeouts.ts +76 -0
- package/src/data/db/types.ts +18 -0
- package/src/data/db/unbound.ts +33 -0
- package/src/data/db/values.ts +29 -0
- package/src/data/domain/brand.ts +15 -0
- package/src/data/domain/build-factory.ts +193 -0
- package/src/data/domain/build-schema.ts +161 -0
- package/src/data/domain/build-table.ts +355 -0
- package/src/data/domain/define.ts +239 -0
- package/src/data/domain/external-registry.ts +34 -0
- package/src/data/domain/external.ts +77 -0
- package/src/data/domain/field.ts +279 -0
- package/src/data/domain/index.ts +13 -0
- package/src/data/domain/naming.ts +6 -0
- package/src/data/domain/projections.ts +57 -0
- package/src/data/domain/trace-safe-projection.ts +31 -0
- package/src/data/domain/traits.ts +108 -0
- package/src/data/domain/types.ts +289 -0
- package/src/data/jsonb/composed.ts +18 -0
- package/src/data/jsonb/index.ts +1 -0
- package/src/data/jsonb/namespace.ts +30 -0
- package/src/data/jsonb/query.ts +86 -0
- package/src/data/migrate/append-only-generate.ts +47 -0
- package/src/data/migrate/applied.ts +47 -0
- package/src/data/migrate/apply.ts +309 -0
- package/src/data/migrate/check.ts +119 -0
- package/src/data/migrate/diff.ts +357 -0
- package/src/data/migrate/door-generate.ts +130 -0
- package/src/data/migrate/framework-rls.ts +58 -0
- package/src/data/migrate/generate.ts +201 -0
- package/src/data/migrate/history.ts +215 -0
- package/src/data/migrate/index.ts +9 -0
- package/src/data/migrate/internal-definitions.ts +175 -0
- package/src/data/migrate/post-process.ts +73 -0
- package/src/data/migrate/pragma.ts +73 -0
- package/src/data/migrate/provision-framework.ts +147 -0
- package/src/data/migrate/rls-generate.ts +61 -0
- package/src/data/migrate/run.ts +124 -0
- package/src/data/migrate/safety.ts +167 -0
- package/src/data/migrate/snapshot.ts +368 -0
- package/src/data/migrate/sql-emit.ts +392 -0
- package/src/data/migrate/statements.ts +312 -0
- package/src/data/query/alias.ts +126 -0
- package/src/data/query/batch.ts +8 -0
- package/src/data/query/cache/core/cache.ts +78 -0
- package/src/data/query/cache/core/index.ts +1 -0
- package/src/data/query/cache/core/types.ts +29 -0
- package/src/data/query/cache/readme.md +230 -0
- package/src/data/query/casing.ts +76 -0
- package/src/data/query/collation.ts +11 -0
- package/src/data/query/column-builder.ts +370 -0
- package/src/data/query/column.ts +150 -0
- package/src/data/query/entity.ts +42 -0
- package/src/data/query/errors.ts +33 -0
- package/src/data/query/index.ts +14 -0
- package/src/data/query/logger.ts +47 -0
- package/src/data/query/operations.ts +45 -0
- package/src/data/query/pg-core/alias.ts +12 -0
- package/src/data/query/pg-core/checks.ts +32 -0
- package/src/data/query/pg-core/columns/all.ts +71 -0
- package/src/data/query/pg-core/columns/bigint.ts +110 -0
- package/src/data/query/pg-core/columns/bigserial.ts +128 -0
- package/src/data/query/pg-core/columns/boolean.ts +43 -0
- package/src/data/query/pg-core/columns/char.ts +85 -0
- package/src/data/query/pg-core/columns/cidr.ts +43 -0
- package/src/data/query/pg-core/columns/common.ts +366 -0
- package/src/data/query/pg-core/columns/custom.ts +232 -0
- package/src/data/query/pg-core/columns/date.common.ts +15 -0
- package/src/data/query/pg-core/columns/date.ts +112 -0
- package/src/data/query/pg-core/columns/double-precision.ts +55 -0
- package/src/data/query/pg-core/columns/enum.ts +201 -0
- package/src/data/query/pg-core/columns/index.ts +35 -0
- package/src/data/query/pg-core/columns/inet.ts +43 -0
- package/src/data/query/pg-core/columns/int.common.ts +57 -0
- package/src/data/query/pg-core/columns/integer.ts +53 -0
- package/src/data/query/pg-core/columns/interval.ts +83 -0
- package/src/data/query/pg-core/columns/json.ts +58 -0
- package/src/data/query/pg-core/columns/jsonb.ts +56 -0
- package/src/data/query/pg-core/columns/line.ts +117 -0
- package/src/data/query/pg-core/columns/macaddr.ts +43 -0
- package/src/data/query/pg-core/columns/macaddr8.ts +43 -0
- package/src/data/query/pg-core/columns/numeric.ts +227 -0
- package/src/data/query/pg-core/columns/point.ts +127 -0
- package/src/data/query/pg-core/columns/postgis_extension/geometry.ts +119 -0
- package/src/data/query/pg-core/columns/postgis_extension/utils.ts +47 -0
- package/src/data/query/pg-core/columns/real.ts +58 -0
- package/src/data/query/pg-core/columns/serial.ts +55 -0
- package/src/data/query/pg-core/columns/smallint.ts +53 -0
- package/src/data/query/pg-core/columns/smallserial.ts +60 -0
- package/src/data/query/pg-core/columns/text.ts +67 -0
- package/src/data/query/pg-core/columns/time.ts +72 -0
- package/src/data/query/pg-core/columns/timestamp.ts +163 -0
- package/src/data/query/pg-core/columns/uuid.ts +51 -0
- package/src/data/query/pg-core/columns/varchar.ts +89 -0
- package/src/data/query/pg-core/columns/vector_extension/bit.ts +68 -0
- package/src/data/query/pg-core/columns/vector_extension/halfvec.ts +80 -0
- package/src/data/query/pg-core/columns/vector_extension/sparsevec.ts +67 -0
- package/src/data/query/pg-core/columns/vector_extension/vector.ts +80 -0
- package/src/data/query/pg-core/db.ts +674 -0
- package/src/data/query/pg-core/dialect.ts +1404 -0
- package/src/data/query/pg-core/expressions.ts +25 -0
- package/src/data/query/pg-core/foreign-keys.ts +130 -0
- package/src/data/query/pg-core/index.ts +21 -0
- package/src/data/query/pg-core/indexes.ts +276 -0
- package/src/data/query/pg-core/policies.ts +56 -0
- package/src/data/query/pg-core/primary-keys.ts +62 -0
- package/src/data/query/pg-core/query-builders/count.ts +86 -0
- package/src/data/query/pg-core/query-builders/delete.ts +293 -0
- package/src/data/query/pg-core/query-builders/index.ts +7 -0
- package/src/data/query/pg-core/query-builders/insert.ts +452 -0
- package/src/data/query/pg-core/query-builders/query-builder.ts +123 -0
- package/src/data/query/pg-core/query-builders/query.ts +157 -0
- package/src/data/query/pg-core/query-builders/raw.ts +49 -0
- package/src/data/query/pg-core/query-builders/refresh-materialized-view.ts +108 -0
- package/src/data/query/pg-core/query-builders/select.ts +1346 -0
- package/src/data/query/pg-core/query-builders/select.types.ts +437 -0
- package/src/data/query/pg-core/query-builders/update.ts +626 -0
- package/src/data/query/pg-core/roles.ts +41 -0
- package/src/data/query/pg-core/schema.ts +72 -0
- package/src/data/query/pg-core/sequence.ts +41 -0
- package/src/data/query/pg-core/session.ts +292 -0
- package/src/data/query/pg-core/subquery.ts +46 -0
- package/src/data/query/pg-core/table.ts +252 -0
- package/src/data/query/pg-core/unique-constraint.ts +73 -0
- package/src/data/query/pg-core/utils/array.ts +95 -0
- package/src/data/query/pg-core/utils/index.ts +1 -0
- package/src/data/query/pg-core/utils.ts +104 -0
- package/src/data/query/pg-core/view-base.ts +14 -0
- package/src/data/query/pg-core/view-common.ts +1 -0
- package/src/data/query/pg-core/view.ts +435 -0
- package/src/data/query/pg-core/with-builder.ts +41 -0
- package/src/data/query/postgres-js/README.md +46 -0
- package/src/data/query/postgres-js/array-parser.ts +45 -0
- package/src/data/query/postgres-js/driver-error-boundary.ts +149 -0
- package/src/data/query/postgres-js/driver.ts +163 -0
- package/src/data/query/postgres-js/index.ts +2 -0
- package/src/data/query/postgres-js/session.ts +234 -0
- package/src/data/query/primary-key.ts +11 -0
- package/src/data/query/query-builders/query-builder.ts +19 -0
- package/src/data/query/query-builders/select.types.ts +175 -0
- package/src/data/query/query-promise.ts +35 -0
- package/src/data/query/relations.ts +725 -0
- package/src/data/query/runnable-query.ts +12 -0
- package/src/data/query/selection-proxy.ts +121 -0
- package/src/data/query/session.ts +8 -0
- package/src/data/query/sql/expressions/conditions.ts +741 -0
- package/src/data/query/sql/expressions/index.ts +2 -0
- package/src/data/query/sql/expressions/select.ts +45 -0
- package/src/data/query/sql/functions/aggregate.ts +129 -0
- package/src/data/query/sql/functions/index.ts +2 -0
- package/src/data/query/sql/functions/vector.ts +178 -0
- package/src/data/query/sql/index.ts +3 -0
- package/src/data/query/sql/sql.ts +794 -0
- package/src/data/query/subquery.ts +53 -0
- package/src/data/query/table.ts +208 -0
- package/src/data/query/table.utils.ts +2 -0
- package/src/data/query/tracing-utils.ts +3 -0
- package/src/data/query/tracing.ts +11 -0
- package/src/data/query/utils.ts +357 -0
- package/src/data/query/version.ts +4 -0
- package/src/data/query/view-common.ts +1 -0
- package/src/data/schema/branded.ts +13 -0
- package/src/data/schema/generated/coerce.ts +5 -0
- package/src/data/schema/generated/combinator.ts +6 -0
- package/src/data/schema/generated/encoding.ts +5 -0
- package/src/data/schema/generated/error.ts +6 -0
- package/src/data/schema/generated/format.ts +5 -0
- package/src/data/schema/generated/type-constructor.ts +10 -0
- package/src/data/schema/generated/type.ts +6 -0
- package/src/data/schema/index.ts +6 -0
- package/src/data/schema/json-schema.ts +33 -0
- package/src/data/schema/namespace.ts +55 -0
- package/src/data/schema/parse.ts +39 -0
- package/src/data/schema/types.ts +39 -0
- package/src/data/temporal/append-only.ts +134 -0
- package/src/data/temporal/close.ts +27 -0
- package/src/data/temporal/columns.ts +10 -0
- package/src/data/temporal/definition-queries.ts +70 -0
- package/src/data/temporal/errors.ts +10 -0
- package/src/data/temporal/get-current.ts +22 -0
- package/src/data/temporal/index.ts +9 -0
- package/src/data/temporal/namespace.ts +36 -0
- package/src/data/temporal/query.ts +25 -0
- package/src/data/temporal/resolve.ts +17 -0
- package/src/data/temporal/revise.ts +67 -0
- package/src/data/temporal/types.ts +22 -0
- package/src/data/valve/artifact.ts +33 -0
- package/src/data/valve/contract-diff.ts +21 -0
- package/src/data/valve/cross.ts +57 -0
- package/src/data/valve/crossing-ledger.ts +50 -0
- package/src/data/valve/decision.ts +44 -0
- package/src/data/valve/durable-crossing.ts +52 -0
- package/src/data/valve/execute-crossing.ts +77 -0
- package/src/data/valve/faucet.ts +56 -0
- package/src/data/valve/gate.ts +114 -0
- package/src/data/valve/graph.ts +77 -0
- package/src/data/valve/index.ts +53 -0
- package/src/data/valve/namespace.ts +57 -0
- package/src/data/valve/registry.ts +50 -0
- package/src/data/valve/schema/valve-crossing-fields.ts +23 -0
- package/src/data/valve/schema/valve-crossing.ts +9 -0
- package/src/data/valve/supply.ts +154 -0
- package/src/data/valve/types.ts +143 -0
- package/src/data/vector/columns.ts +27 -0
- package/src/data/vector/index.ts +4 -0
- package/src/data/vector/namespace.ts +33 -0
- package/src/data/vector/query.ts +85 -0
- package/src/data/vector/validate.ts +16 -0
- package/src/data/write/audit/actor.ts +15 -0
- package/src/data/write/audit/audit-columns.ts +18 -0
- package/src/data/write/audit/audit-select.ts +111 -0
- package/src/data/write/audit/bound-transaction.ts +14 -0
- package/src/data/write/audit/cte-compile.ts +32 -0
- package/src/data/write/audit/decode-returning.ts +47 -0
- package/src/data/write/audit/final-select.ts +43 -0
- package/src/data/write/audit/infra-audit.ts +36 -0
- package/src/data/write/audit/primary-key.ts +65 -0
- package/src/data/write/audit/projection.ts +41 -0
- package/src/data/write/audit/read-touches.ts +86 -0
- package/src/data/write/audit/touch-match.ts +78 -0
- package/src/data/write/audit/verb-delete.ts +62 -0
- package/src/data/write/audit/verb-insert.ts +36 -0
- package/src/data/write/audit/verb-select.ts +47 -0
- package/src/data/write/audit/verb-update.ts +60 -0
- package/src/data/write/audit/verb-upsert.ts +104 -0
- package/src/data/write/conflict.ts +43 -0
- package/src/data/write/delete.ts +38 -0
- package/src/data/write/execute.ts +243 -0
- package/src/data/write/index.ts +11 -0
- package/src/data/write/insert.ts +63 -0
- package/src/data/write/state.ts +43 -0
- package/src/data/write/update.ts +54 -0
- package/src/data/write/upsert.ts +58 -0
- package/src/data/write/values.ts +19 -0
- package/src/index.ts +230 -0
- package/src/infra/access-links/code.ts +70 -0
- package/src/infra/access-links/handle.ts +892 -0
- package/src/infra/access-links/index.ts +9 -0
- package/src/infra/access-links/namespace.ts +30 -0
- package/src/infra/access-links/person-check.ts +177 -0
- package/src/infra/access-links/schema.ts +71 -0
- package/src/infra/alerts/async-sources.ts +231 -0
- package/src/infra/alerts/audit.ts +36 -0
- package/src/infra/alerts/beat.ts +123 -0
- package/src/infra/alerts/client-faults.ts +111 -0
- package/src/infra/alerts/faults.ts +156 -0
- package/src/infra/alerts/handle.ts +778 -0
- package/src/infra/alerts/http.ts +192 -0
- package/src/infra/alerts/index.ts +12 -0
- package/src/infra/alerts/infra-sources.ts +180 -0
- package/src/infra/alerts/namespace.ts +81 -0
- package/src/infra/alerts/redact.ts +21 -0
- package/src/infra/alerts/request-faults.ts +41 -0
- package/src/infra/alerts/schema.ts +124 -0
- package/src/infra/alerts/sinks.ts +125 -0
- package/src/infra/alerts/source-options.ts +26 -0
- package/src/infra/alerts/sources.ts +503 -0
- package/src/infra/alerts/subsystem-sources.ts +137 -0
- package/src/infra/audit/emit.ts +87 -0
- package/src/infra/audit/index.ts +2 -0
- package/src/infra/audit/namespace.ts +22 -0
- package/src/infra/audit/schema.sql +35 -0
- package/src/infra/audit/types.ts +18 -0
- package/src/infra/aws/credentials.ts +147 -0
- package/src/infra/aws/sigv4.ts +140 -0
- package/src/infra/cache/create.ts +58 -0
- package/src/infra/cache/entry.ts +81 -0
- package/src/infra/cache/index.ts +4 -0
- package/src/infra/cache/namespace.ts +10 -0
- package/src/infra/cache/tenant.ts +93 -0
- package/src/infra/cache/types.ts +24 -0
- package/src/infra/crawl/audit.ts +35 -0
- package/src/infra/crawl/errors.ts +25 -0
- package/src/infra/crawl/handle.ts +693 -0
- package/src/infra/crawl/index.ts +9 -0
- package/src/infra/crawl/links.ts +160 -0
- package/src/infra/crawl/namespace.ts +36 -0
- package/src/infra/crawl/robots.ts +100 -0
- package/src/infra/crawl/schema.ts +89 -0
- package/src/infra/credit-line/handle.ts +883 -0
- package/src/infra/credit-line/index.ts +6 -0
- package/src/infra/credit-line/namespace.ts +7 -0
- package/src/infra/credit-line/schema.ts +96 -0
- package/src/infra/credits/handle.ts +706 -0
- package/src/infra/credits/index.ts +5 -0
- package/src/infra/credits/namespace.ts +7 -0
- package/src/infra/credits/schema.ts +83 -0
- package/src/infra/email/audit.ts +36 -0
- package/src/infra/email/inbound.ts +622 -0
- package/src/infra/email/index.ts +11 -0
- package/src/infra/email/mail.ts +517 -0
- package/src/infra/email/mime.ts +447 -0
- package/src/infra/email/namespace.ts +22 -0
- package/src/infra/email/providers.ts +111 -0
- package/src/infra/email/schema.ts +96 -0
- package/src/infra/email/sns.ts +200 -0
- package/src/infra/email/unsubscribe.ts +44 -0
- package/src/infra/fetch/address.ts +88 -0
- package/src/infra/fetch/connect-retry.ts +87 -0
- package/src/infra/fetch/handle.ts +329 -0
- package/src/infra/fetch/index.ts +7 -0
- package/src/infra/fetch/namespace.ts +70 -0
- package/src/infra/idempotency/index.ts +3 -0
- package/src/infra/idempotency/middleware.ts +87 -0
- package/src/infra/idempotency/store.ts +92 -0
- package/src/infra/idempotency/types.ts +30 -0
- package/src/infra/join-requests/handle.ts +312 -0
- package/src/infra/join-requests/index.ts +2 -0
- package/src/infra/join-requests/namespace.ts +7 -0
- package/src/infra/join-requests/schema.ts +33 -0
- package/src/infra/outbox/core.ts +165 -0
- package/src/infra/quota/errors.ts +32 -0
- package/src/infra/quota/handle.ts +215 -0
- package/src/infra/quota/index.ts +5 -0
- package/src/infra/quota/namespace.ts +7 -0
- package/src/infra/quota/schema.ts +15 -0
- package/src/infra/storage/backend.ts +34 -0
- package/src/infra/storage/handle.ts +513 -0
- package/src/infra/storage/index.ts +9 -0
- package/src/infra/storage/local.ts +104 -0
- package/src/infra/storage/namespace.ts +13 -0
- package/src/infra/storage/s3.ts +257 -0
- package/src/infra/storage/schema.ts +24 -0
- package/src/infra/support/handle.ts +854 -0
- package/src/infra/support/http.ts +229 -0
- package/src/infra/support/index.ts +7 -0
- package/src/infra/support/namespace.ts +12 -0
- package/src/infra/support/schema.ts +96 -0
- package/src/infra/webhook/inbound.ts +45 -0
- package/src/infra/webhook/index.ts +5 -0
- package/src/infra/webhook/namespace.ts +24 -0
- package/src/infra/webhook/outbound.ts +315 -0
- package/src/infra/webhook/sign.ts +32 -0
- package/src/infra/webhook/verify.ts +82 -0
- package/src/invariants/assert.ts +26 -0
- package/src/invariants/index.ts +1 -0
- package/src/lint/config.ts +56 -0
- package/src/lint/doctrine.ts +163 -0
- package/src/lint/include.ts +97 -0
- package/src/lint/index.ts +3 -0
- package/src/lint/preset.ts +10 -0
- package/src/lint/resolver.ts +218 -0
- package/src/lint/rules/no-reexports.ts +46 -0
- package/src/request/behavior/audited.ts +85 -0
- package/src/request/behavior/cached.ts +62 -0
- package/src/request/behavior/compose.ts +28 -0
- package/src/request/behavior/index.ts +5 -0
- package/src/request/behavior/namespace.ts +16 -0
- package/src/request/behavior/types.ts +26 -0
- package/src/request/behavior/versioned.ts +54 -0
- package/src/request/context/create.ts +103 -0
- package/src/request/context/guard.ts +49 -0
- package/src/request/context/index.ts +4 -0
- package/src/request/context/run-in-transaction.ts +200 -0
- package/src/request/context/store.ts +28 -0
- package/src/request/context/types.ts +50 -0
- package/src/request/di/builder.ts +535 -0
- package/src/request/di/index.ts +8 -0
- package/src/request/di/resolve.ts +271 -0
- package/src/request/di/types.ts +262 -0
- package/src/request/fitting/namespace.ts +7 -0
- package/src/request/fixture/batch.ts +71 -0
- package/src/request/fixture/builder.ts +26 -0
- package/src/request/fixture/errors.ts +10 -0
- package/src/request/fixture/index.ts +8 -0
- package/src/request/fixture/namespace.ts +23 -0
- package/src/request/fixture/paginate.ts +44 -0
- package/src/request/fixture/to-handlers.ts +260 -0
- package/src/request/fixture/types.ts +79 -0
- package/src/request/flow/act-as.ts +78 -0
- package/src/request/flow/namespace.ts +17 -0
- package/src/request/http/error-step.ts +42 -0
- package/src/request/http/file.ts +141 -0
- package/src/request/http/index.ts +4 -0
- package/src/request/http/middleware.ts +141 -0
- package/src/request/http/namespace.ts +5 -0
- package/src/request/http/rate-limit-classes.ts +170 -0
- package/src/request/http/response.ts +82 -0
- package/src/request/http/server.ts +916 -0
- package/src/request/http/sniff.ts +261 -0
- package/src/request/http/types.ts +250 -0
- package/src/request/http/upload.ts +284 -0
- package/src/request/internal/text.ts +6 -0
- package/src/request/openapi/generate.ts +205 -0
- package/src/request/openapi/index.ts +2 -0
- package/src/request/openapi/namespace.ts +7 -0
- package/src/test/auth.ts +13 -0
- package/src/test/auto-setup.ts +45 -0
- package/src/test/context.ts +91 -0
- package/src/test/crawl-fixture.ts +157 -0
- package/src/test/discover-fork.ts +17 -0
- package/src/test/fingerprint.ts +135 -0
- package/src/test/globals.d.ts +7 -0
- package/src/test/index.ts +19 -0
- package/src/test/namespace.ts +87 -0
- package/src/test/plugin.ts +189 -0
- package/src/test/progress.ts +185 -0
- package/src/test/reap-after-run.ts +28 -0
- package/src/test/reap.ts +286 -0
- package/src/test/reset.ts +253 -0
- package/src/test/savepoints.ts +172 -0
- package/src/test/setup.ts +1422 -0
- package/src/test/system-channel.ts +50 -0
- package/src/test/vitest-plugin.ts +142 -0
- package/src/test/vitest-workspace.ts +507 -0
- package/src/test/vitest.ts +333 -0
- package/src/test/watch.ts +105 -0
- package/src/trace/capture/bounded-serializer.ts +146 -0
- package/src/trace/cardinality/analyze.ts +173 -0
- package/src/trace/cardinality/branch-context.ts +214 -0
- package/src/trace/cardinality/check.ts +33 -0
- package/src/trace/cardinality/classify-edge.ts +64 -0
- package/src/trace/cardinality/structural-max.ts +107 -0
- package/src/trace/check/coverage-check.ts +188 -0
- package/src/trace/check/flow-check.ts +103 -0
- package/src/trace/check/flow-step-site.ts +234 -0
- package/src/trace/check/io-pattern.ts +116 -0
- package/src/trace/codemod/add-cardinality.ts +152 -0
- package/src/trace/codemod/run.ts +80 -0
- package/src/trace/entry/child-trace.ts +21 -0
- package/src/trace/entry/close-trace.ts +87 -0
- package/src/trace/entry/finalize-trace.ts +28 -0
- package/src/trace/entry/open-trace.ts +138 -0
- package/src/trace/entry/parent-trace.ts +20 -0
- package/src/trace/entry/parse-traceparent.ts +39 -0
- package/src/trace/entry/process-start.ts +38 -0
- package/src/trace/entry/trace-context.ts +26 -0
- package/src/trace/entry/tracing-enabled.ts +10 -0
- package/src/trace/external/record.ts +195 -0
- package/src/trace/external/report.ts +66 -0
- package/src/trace/namespace.ts +19 -0
- package/src/trace/partition/day.ts +47 -0
- package/src/trace/partition/index.ts +14 -0
- package/src/trace/partition/partition-name.ts +23 -0
- package/src/trace/partition/plan.ts +82 -0
- package/src/trace/partition/schedule.ts +26 -0
- package/src/trace/partition/startup-hook.ts +39 -0
- package/src/trace/partition/sync.ts +154 -0
- package/src/trace/partition/tables.ts +53 -0
- package/src/trace/read/tree.ts +83 -0
- package/src/trace/retain/copy-on-error.ts +76 -0
- package/src/trace/runtime/expire-stale-traces.ts +54 -0
- package/src/trace/runtime/flow-step.ts +147 -0
- package/src/trace/runtime/flush-steps.ts +31 -0
- package/src/trace/runtime/pending-writes.ts +54 -0
- package/src/trace/runtime/step-buffer.ts +298 -0
- package/src/trace/runtime/step-error.ts +21 -0
- package/src/trace/runtime/step-meta.ts +55 -0
- package/src/trace/runtime/thenable.ts +12 -0
- package/src/trace/runtime/truncate-trace.ts +62 -0
- package/src/trace/schema/trace-fields.ts +52 -0
- package/src/trace/schema/trace-retained.ts +22 -0
- package/src/trace/schema/trace-step-fields.ts +44 -0
- package/src/trace/schema/trace-step-retained.ts +25 -0
- package/src/trace/schema/trace-step.ts +18 -0
- package/src/trace/schema/trace.ts +12 -0
- package/src/trace/transformer/apply.ts +79 -0
- package/src/trace/transformer/build-meta-literal.ts +30 -0
- package/src/trace/transformer/find-flow-step-sites.ts +85 -0
- package/src/trace/transformer/inject-flow-step-import.ts +39 -0
- package/src/trace/transformer/transform-error.ts +14 -0
- package/src/trace/transformer/transform-flow-steps.ts +123 -0
- package/src/trace/transformer/wrap-function-declaration.ts +62 -0
- package/src/trace/transformer/wrap-variable-declaration.ts +67 -0
- package/src/workspace/affected.ts +110 -0
- package/src/workspace/changed-files.ts +163 -0
- package/src/workspace/check-deps.ts +213 -0
- package/src/workspace/checksum.ts +20 -0
- package/src/workspace/dependency-graph.ts +67 -0
- package/src/workspace/detect-pipework-copies.ts +93 -0
- package/src/workspace/discover.ts +206 -0
- package/src/workspace/generate.ts +401 -0
- package/src/workspace/index.ts +7 -0
- package/src/workspace/module-imports-pipework.ts +39 -0
- package/src/workspace/module-package-name.ts +12 -0
- package/src/workspace/scrub-git-env.ts +23 -0
- package/src/workspace/source-paths.ts +36 -0
- package/src/workspace/sync.ts +182 -0
- package/dist/REFERENCE.md +0 -1477
- package/dist/infra/access-links/rate-limit.d.ts +0 -10
- package/dist/infra/access-links/rate-limit.d.ts.map +0 -1
- package/dist/infra/access-links/rate-limit.js +0 -28
- package/dist/infra/access-links/rate-limit.js.map +0 -1
- package/dist/infra/email/sigv4.d.ts +0 -24
- package/dist/infra/email/sigv4.d.ts.map +0 -1
- package/dist/infra/email/sigv4.js +0 -55
- package/dist/infra/email/sigv4.js.map +0 -1
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import { sql } from '../../data/db/sql.js'
|
|
2
|
+
import type { DB } from '../../data/db/index.js'
|
|
3
|
+
import { getContext } from '../../request/context/store.js'
|
|
4
|
+
import { getBaseLogger } from '../../core/logging/index.js'
|
|
5
|
+
import { recordFault, faultMessage } from '../alerts/faults.js'
|
|
6
|
+
import type { ManagedConnection } from '../../data/db/pool.js'
|
|
7
|
+
import type { AcceptReadonly } from '../../data/db/readonly.js'
|
|
8
|
+
import { QuotaExhaustedError } from './errors.js'
|
|
9
|
+
import { retryUnsent, type ConnectRetryOptions } from '../fetch/connect-retry.js'
|
|
10
|
+
|
|
11
|
+
/** Anything with a case-insensitive `get` — a fetch `Headers` satisfies it. */
|
|
12
|
+
export type QuotaHeaders = { get(name: string): string | null }
|
|
13
|
+
export type QuotaHeaderInput = QuotaHeaders | AcceptReadonly<Record<string, string | string[] | undefined>>
|
|
14
|
+
|
|
15
|
+
export interface QuotaBudget {
|
|
16
|
+
readonly remaining: number
|
|
17
|
+
readonly resetAt: Date
|
|
18
|
+
readonly limit?: number
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface QuotaConfig<C extends string> {
|
|
22
|
+
/** Remaining count each class keeps in reserve for others: a call of class c is refused while remaining <= reserve[c]. Classes absent here have reserve 0 (refused only at zero). */
|
|
23
|
+
readonly reserve?: Partial<Record<C, number>>
|
|
24
|
+
/** Reads the budget from a response's headers; default reads GitHub-style headers. Return null when the response carries none. */
|
|
25
|
+
readonly read?: (headers: QuotaHeaders) => QuotaBudget | null
|
|
26
|
+
/** Database name; default the instance's default database. */
|
|
27
|
+
readonly database?: string
|
|
28
|
+
/** A standalone connection (from `tap()`) for a process outside the manifold; the caller owns its lifecycle. With it, no managed context is needed and `database` is not used. */
|
|
29
|
+
readonly connection?: ManagedConnection
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface QuotaState {
|
|
33
|
+
readonly remaining: number
|
|
34
|
+
readonly limit: number | null
|
|
35
|
+
readonly resetAt: Date
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface QuotaCallOptions<R> {
|
|
39
|
+
/** Where to read the response headers; default `result.headers` when it has a `get`. */
|
|
40
|
+
readonly headers?: (result: R) => QuotaHeaderInput | null
|
|
41
|
+
/**
|
|
42
|
+
* Sends again when the connection could not be made (see `connectRetry`). One budget unit is taken for the call:
|
|
43
|
+
* a connect fault never reached the upstream, so the tries cost nothing more. `true` uses the default waits.
|
|
44
|
+
*/
|
|
45
|
+
readonly retryUnsent?: true | ConnectRetryOptions
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface Quota<C extends string> {
|
|
49
|
+
/** Takes one unit of the upstream's budget for this class, sends, and records the budget from the response headers. */
|
|
50
|
+
call<R>(target: { key: string; class: C }, send: () => Promise<R>, opts?: QuotaCallOptions<R>): Promise<R>
|
|
51
|
+
/** Records the budget from headers of a call sent through the caller's own client. */
|
|
52
|
+
record(key: string, headers: QuotaHeaderInput): Promise<void>
|
|
53
|
+
/** The recorded budget; null when there is none or its window has passed. */
|
|
54
|
+
state(key: string): Promise<QuotaState | null>
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function intHeader(headers: QuotaHeaders, name: string): number | null {
|
|
58
|
+
const raw = headers.get(name)
|
|
59
|
+
if (raw === null) return null
|
|
60
|
+
const n = Number(raw.trim())
|
|
61
|
+
return raw.trim() !== '' && Number.isFinite(n) ? Math.trunc(n) : null
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function defaultRead(headers: QuotaHeaders): QuotaBudget | null {
|
|
65
|
+
const remaining = intHeader(headers, 'x-ratelimit-remaining')
|
|
66
|
+
const reset = intHeader(headers, 'x-ratelimit-reset')
|
|
67
|
+
if (remaining === null || reset === null) return null
|
|
68
|
+
const limit = intHeader(headers, 'x-ratelimit-limit')
|
|
69
|
+
return { remaining, resetAt: new Date(reset * 1000), ...(limit !== null ? { limit } : {}) }
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function toHeaders(input: QuotaHeaderInput): QuotaHeaders {
|
|
73
|
+
if (typeof (input as QuotaHeaders).get === 'function') return input as QuotaHeaders
|
|
74
|
+
const map = new Map<string, string>()
|
|
75
|
+
for (const [name, value] of Object.entries(input as Record<string, string | readonly string[] | undefined>)) {
|
|
76
|
+
if (value === undefined) continue
|
|
77
|
+
map.set(name.toLowerCase(), typeof value === 'string' ? value : value.join(', '))
|
|
78
|
+
}
|
|
79
|
+
return { get: (name) => map.get(name.toLowerCase()) ?? null }
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function headersOf(value: unknown): QuotaHeaderInput | null {
|
|
83
|
+
if (typeof value !== 'object' || value === null) return null
|
|
84
|
+
const headers = (value as { headers?: unknown }).headers
|
|
85
|
+
if (typeof headers !== 'object' || headers === null) return null
|
|
86
|
+
return headers as QuotaHeaderInput
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function resolveDb(database: string | undefined): DB {
|
|
90
|
+
const ctx = getContext()
|
|
91
|
+
if (ctx === undefined) {
|
|
92
|
+
throw new Error(
|
|
93
|
+
'[pipework] quota needs a managed context to reach the database. ' +
|
|
94
|
+
'Call it inside a request handler, a job, or flow.run.',
|
|
95
|
+
)
|
|
96
|
+
}
|
|
97
|
+
const name = database ?? ctx.instance.config.defaultDatabaseName()
|
|
98
|
+
return ctx.instance.pool.getOrCreate(ctx.instance.config.database(name)).drizzle
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const iso = (d: Date): string => d.toISOString()
|
|
102
|
+
|
|
103
|
+
function logRecordFailure(key: string, err: unknown): void {
|
|
104
|
+
getBaseLogger().error({ err, key }, 'quota could not record the budget from a response')
|
|
105
|
+
void recordFault('quota-record', faultMessage(err), { key })
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export function createQuota<C extends string>(config: QuotaConfig<C> = {}): Quota<C> {
|
|
109
|
+
if (config.connection !== undefined && config.database !== undefined) {
|
|
110
|
+
throw new Error('[pipework] quota.create takes connection or database, not both.')
|
|
111
|
+
}
|
|
112
|
+
const read = config.read ?? defaultRead
|
|
113
|
+
const db = (): DB => config.connection !== undefined ? config.connection.drizzle : resolveDb(config.database)
|
|
114
|
+
|
|
115
|
+
async function record(key: string, input: QuotaHeaderInput): Promise<void> {
|
|
116
|
+
const headers = toHeaders(input)
|
|
117
|
+
const budget = read(headers)
|
|
118
|
+
const retryAfter = intHeader(headers, 'retry-after')
|
|
119
|
+
if (budget === null && retryAfter === null) return
|
|
120
|
+
const conn = db()
|
|
121
|
+
if (budget !== null) {
|
|
122
|
+
// Same window: lowest remaining. Different window: a live response (reset in the future) replaces the row; a past reset is ignored.
|
|
123
|
+
const limit = budget.limit ?? null
|
|
124
|
+
await conn.execute(sql`
|
|
125
|
+
INSERT INTO outbound_quota (key, remaining, "limit", reset_at, updated_at)
|
|
126
|
+
VALUES (${key}, ${budget.remaining}, ${limit}, ${iso(budget.resetAt)}::timestamptz, clock_timestamp())
|
|
127
|
+
ON CONFLICT (key) DO UPDATE SET
|
|
128
|
+
remaining = CASE
|
|
129
|
+
WHEN date_trunc('second', outbound_quota.reset_at) = date_trunc('second', EXCLUDED.reset_at)
|
|
130
|
+
THEN LEAST(outbound_quota.remaining, EXCLUDED.remaining)
|
|
131
|
+
WHEN EXCLUDED.reset_at > now() THEN EXCLUDED.remaining
|
|
132
|
+
ELSE outbound_quota.remaining END,
|
|
133
|
+
"limit" = CASE
|
|
134
|
+
WHEN date_trunc('second', outbound_quota.reset_at) = date_trunc('second', EXCLUDED.reset_at)
|
|
135
|
+
OR EXCLUDED.reset_at > now()
|
|
136
|
+
THEN COALESCE(EXCLUDED."limit", outbound_quota."limit")
|
|
137
|
+
ELSE outbound_quota."limit" END,
|
|
138
|
+
reset_at = CASE
|
|
139
|
+
WHEN date_trunc('second', outbound_quota.reset_at) = date_trunc('second', EXCLUDED.reset_at)
|
|
140
|
+
THEN outbound_quota.reset_at
|
|
141
|
+
WHEN EXCLUDED.reset_at > now() THEN EXCLUDED.reset_at
|
|
142
|
+
ELSE outbound_quota.reset_at END,
|
|
143
|
+
updated_at = clock_timestamp()`)
|
|
144
|
+
}
|
|
145
|
+
if (retryAfter !== null) {
|
|
146
|
+
await conn.execute(sql`
|
|
147
|
+
INSERT INTO outbound_quota (key, remaining, "limit", reset_at, updated_at)
|
|
148
|
+
VALUES (${key}, 0, NULL, now() + make_interval(secs => ${Math.max(0, retryAfter)}), clock_timestamp())
|
|
149
|
+
ON CONFLICT (key) DO UPDATE SET
|
|
150
|
+
remaining = 0,
|
|
151
|
+
reset_at = GREATEST(outbound_quota.reset_at, now() + make_interval(secs => ${Math.max(0, retryAfter)})),
|
|
152
|
+
updated_at = clock_timestamp()`)
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
async function state(key: string): Promise<QuotaState | null> {
|
|
157
|
+
const rows = await db().execute(sql`
|
|
158
|
+
SELECT remaining, "limit", reset_at FROM outbound_quota WHERE key = ${key} AND reset_at > now()`)
|
|
159
|
+
const row = rows[0]
|
|
160
|
+
if (row === undefined) return null
|
|
161
|
+
return {
|
|
162
|
+
remaining: Number(row['remaining']),
|
|
163
|
+
limit: row['limit'] === null ? null : Number(row['limit']),
|
|
164
|
+
resetAt: new Date(row['reset_at'] as string | Date),
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
async function take(key: string, cls: string): Promise<void> {
|
|
169
|
+
const conn = db()
|
|
170
|
+
const reserve = config.reserve?.[cls as C] ?? 0
|
|
171
|
+
const taken = await conn.execute(sql`
|
|
172
|
+
UPDATE outbound_quota SET remaining = remaining - 1, updated_at = clock_timestamp()
|
|
173
|
+
WHERE key = ${key} AND reset_at > now() AND remaining > ${reserve}
|
|
174
|
+
RETURNING remaining`)
|
|
175
|
+
if (taken.length > 0) return
|
|
176
|
+
const rows = await conn.execute(sql`
|
|
177
|
+
UPDATE outbound_quota SET refused_at = clock_timestamp()
|
|
178
|
+
WHERE key = ${key} AND reset_at > now()
|
|
179
|
+
RETURNING remaining, reset_at`)
|
|
180
|
+
const row = rows[0]
|
|
181
|
+
if (row === undefined) return // unknown budget: send optimistically
|
|
182
|
+
throw new QuotaExhaustedError({
|
|
183
|
+
key, class: cls, reserve,
|
|
184
|
+
remaining: Number(row['remaining']),
|
|
185
|
+
resetAt: new Date(row['reset_at'] as string | Date),
|
|
186
|
+
})
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return {
|
|
190
|
+
record,
|
|
191
|
+
state,
|
|
192
|
+
async call<R>(target: { key: string; class: C }, send: () => Promise<R>, opts: QuotaCallOptions<R> = {}): Promise<R> {
|
|
193
|
+
await take(target.key, target.class)
|
|
194
|
+
let result: R
|
|
195
|
+
try {
|
|
196
|
+
const o = opts.retryUnsent
|
|
197
|
+
result = o === undefined ? await send() : await retryUnsent(send, o === true ? {} : o)
|
|
198
|
+
} catch (err) {
|
|
199
|
+
const e = err as { response?: unknown } | null
|
|
200
|
+
const found = headersOf(e?.response) ?? headersOf(err)
|
|
201
|
+
if (found !== null) await record(target.key, found).catch((recordErr: unknown) => { logRecordFailure(target.key, recordErr) })
|
|
202
|
+
throw err
|
|
203
|
+
}
|
|
204
|
+
const found = opts.headers !== undefined
|
|
205
|
+
? opts.headers(result)
|
|
206
|
+
: (() => {
|
|
207
|
+
const h = headersOf(result)
|
|
208
|
+
return h !== null && typeof (h as QuotaHeaders).get === 'function' ? h : null
|
|
209
|
+
})()
|
|
210
|
+
// The call was sent: a failure to record must not reject it, or a retry would send it again.
|
|
211
|
+
if (found !== null) await record(target.key, found).catch((recordErr: unknown) => { logRecordFailure(target.key, recordErr) })
|
|
212
|
+
return result
|
|
213
|
+
},
|
|
214
|
+
}
|
|
215
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { createQuota } from './handle.js'
|
|
2
|
+
|
|
3
|
+
/** Quota namespace — callers of one rate-limited upstream share its budget by class, read from the upstream's headers and kept in Postgres. */
|
|
4
|
+
export const quota = {
|
|
5
|
+
/** Creates a quota handle. */
|
|
6
|
+
create: createQuota,
|
|
7
|
+
} as const
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// One row per upstream key: what the upstream last said was left, and when it resets.
|
|
2
|
+
// Not tenant-scoped — one budget per upstream across the whole app.
|
|
3
|
+
|
|
4
|
+
import { define } from '../../data/domain/index.js'
|
|
5
|
+
import { field } from '../../data/domain/field.js'
|
|
6
|
+
|
|
7
|
+
export const outboundQuota = define('outbound_quota', {
|
|
8
|
+
key: field.text().primaryKey(),
|
|
9
|
+
remaining: field.integer(),
|
|
10
|
+
limit: field.integer().nullable(),
|
|
11
|
+
resetAt: field.timestamp(),
|
|
12
|
+
updatedAt: field.timestamp().default('now'),
|
|
13
|
+
/** When a call was last refused against this row; it counts while it is not older than `updatedAt`. */
|
|
14
|
+
refusedAt: field.timestamp().nullable(),
|
|
15
|
+
})
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { Readable } from 'node:stream'
|
|
2
|
+
|
|
3
|
+
/** One stored object as a listing reports it. `key` is relative to the backend's prefix. */
|
|
4
|
+
export interface BackendObject {
|
|
5
|
+
readonly key: string
|
|
6
|
+
readonly lastModified: Date
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface BackendSignedUrlOptions {
|
|
10
|
+
readonly expiresInSeconds: number
|
|
11
|
+
readonly disposition: 'attachment' | 'inline'
|
|
12
|
+
readonly filename?: string
|
|
13
|
+
readonly mediaType: string
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Where the bytes of stored files live. `storage.s3(...)` and `storage.local(...)`
|
|
18
|
+
* make one; keys are the relative `tenants/<tenantId>/...` names the handle builds.
|
|
19
|
+
*/
|
|
20
|
+
export interface StorageBackend {
|
|
21
|
+
readonly name: 's3' | 'local'
|
|
22
|
+
/** Streams `body` to `key`, replacing any object there. A stream error leaves nothing at `key`. */
|
|
23
|
+
put(key: string, body: AsyncIterable<Buffer | Uint8Array>): Promise<void>
|
|
24
|
+
exists(key: string): Promise<boolean>
|
|
25
|
+
copy(from: string, to: string): Promise<void>
|
|
26
|
+
/** Removes an object; absent is not an error. */
|
|
27
|
+
remove(key: string): Promise<void>
|
|
28
|
+
/** Streams an object's bytes. */
|
|
29
|
+
read(key: string): Promise<Readable>
|
|
30
|
+
/** Every object whose key starts with `prefix`, in key order. */
|
|
31
|
+
list(prefix: string): AsyncIterable<BackendObject>
|
|
32
|
+
/** A time-limited URL that serves the object to a client with no credentials; throws where the backend cannot. */
|
|
33
|
+
signedUrl(key: string, options: BackendSignedUrlOptions): Promise<string>
|
|
34
|
+
}
|