pipework 0.252.0 → 0.253.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -0
- package/dist/infra/support/handle.d.ts +8 -1
- package/dist/infra/support/handle.d.ts.map +1 -1
- package/dist/infra/support/handle.js +9 -2
- package/dist/infra/support/handle.js.map +1 -1
- package/dist/infra/support/namespace.d.ts.map +1 -1
- package/package.json +4 -3
- package/src/async/jobs/abortable-delay.ts +18 -0
- package/src/async/jobs/claims.ts +52 -0
- package/src/async/jobs/cron.ts +179 -0
- package/src/async/jobs/errors.ts +56 -0
- package/src/async/jobs/exclusion.ts +117 -0
- package/src/async/jobs/execute.ts +219 -0
- package/src/async/jobs/index.ts +10 -0
- package/src/async/jobs/listener.ts +135 -0
- package/src/async/jobs/namespace.ts +20 -0
- package/src/async/jobs/queue-door.ts +44 -0
- package/src/async/jobs/queue.ts +1012 -0
- package/src/async/jobs/run-script.ts +67 -0
- package/src/async/jobs/scheduler.ts +218 -0
- package/src/async/jobs/schedules-table.ts +37 -0
- package/src/async/jobs/serialize-error.ts +48 -0
- package/src/async/jobs/table.ts +191 -0
- package/src/async/pipeline/define.ts +53 -0
- package/src/async/pipeline/errors.ts +35 -0
- package/src/async/pipeline/execute.ts +53 -0
- package/src/async/pipeline/index.ts +13 -0
- package/src/async/pipeline/namespace.ts +12 -0
- package/src/async/pipeline/resume.ts +67 -0
- package/src/async/pipeline/run-steps.ts +98 -0
- package/src/async/pipeline/types.ts +57 -0
- package/src/async/state-machine/define.ts +64 -0
- package/src/async/state-machine/errors.ts +29 -0
- package/src/async/state-machine/index.ts +3 -0
- package/src/async/state-machine/namespace.ts +12 -0
- package/src/async/state-machine/transition.ts +90 -0
- package/src/async/state-machine/types.ts +37 -0
- package/src/async/steps/errors.ts +31 -0
- package/src/async/steps/graph.ts +1214 -0
- package/src/async/steps/hash.ts +40 -0
- package/src/async/steps/index.ts +7 -0
- package/src/async/steps/namespace.ts +7 -0
- package/src/async/steps/table.ts +97 -0
- package/src/async/steps/types.ts +318 -0
- package/src/audit/chain/index.ts +9 -0
- package/src/audit/chain/install.ts +113 -0
- package/src/audit/chain/retire.ts +52 -0
- package/src/audit/chain/verify.ts +257 -0
- package/src/audit/query/cursor.ts +37 -0
- package/src/audit/query/index.ts +9 -0
- package/src/audit/query/query.ts +223 -0
- package/src/audit/schema/audit-record-fields.ts +68 -0
- package/src/audit/schema/audit-record.ts +23 -0
- package/src/auth/auth/audit.ts +31 -0
- package/src/auth/auth/chain.ts +84 -0
- package/src/auth/auth/cookie.ts +136 -0
- package/src/auth/auth/email-links.ts +311 -0
- package/src/auth/auth/guard.ts +128 -0
- package/src/auth/auth/index.ts +18 -0
- package/src/auth/auth/multi-org.ts +337 -0
- package/src/auth/auth/namespace.ts +34 -0
- package/src/auth/auth/parse-expiry.ts +13 -0
- package/src/auth/auth/passwords.ts +110 -0
- package/src/auth/auth/schema.ts +110 -0
- package/src/auth/auth/sessions.ts +565 -0
- package/src/auth/auth/sign-in.ts +156 -0
- package/src/auth/auth/signing-keys.ts +69 -0
- package/src/auth/auth/types.ts +26 -0
- package/src/auth/auth/uuid.ts +10 -0
- package/src/auth/rbac/cache.ts +55 -0
- package/src/auth/rbac/check.ts +30 -0
- package/src/auth/rbac/create.ts +35 -0
- package/src/auth/rbac/index.ts +5 -0
- package/src/auth/rbac/middleware.ts +20 -0
- package/src/auth/rbac/namespace.ts +15 -0
- package/src/auth/rbac/resolve.ts +31 -0
- package/src/auth/rbac/schema.sql +31 -0
- package/src/auth/rbac/seed.ts +67 -0
- package/src/auth/rbac/tables.ts +32 -0
- package/src/auth/rbac/types.ts +65 -0
- package/src/auth/tenant/extract.ts +33 -0
- package/src/auth/tenant/in-tenant.ts +49 -0
- package/src/auth/tenant/index.ts +7 -0
- package/src/auth/tenant/namespace.ts +23 -0
- package/src/auth/tenant/propagate.ts +20 -0
- package/src/auth/tenant/rls.ts +120 -0
- package/src/auth/tenant/scope.ts +33 -0
- package/src/auth/tenant/scoped-db.ts +581 -0
- package/src/auth/tenant/types.ts +14 -0
- package/src/auth/tenant/validate.ts +27 -0
- package/src/cli/commands/add.ts +158 -0
- package/src/cli/commands/alerts.ts +98 -0
- package/src/cli/commands/check.ts +1418 -0
- package/src/cli/commands/codemod.ts +78 -0
- package/src/cli/commands/db.ts +356 -0
- package/src/cli/commands/dev.ts +101 -0
- package/src/cli/commands/generate.ts +75 -0
- package/src/cli/commands/init.ts +36 -0
- package/src/cli/commands/install.ts +28 -0
- package/src/cli/commands/lint.ts +62 -0
- package/src/cli/commands/migrate.ts +132 -0
- package/src/cli/commands/openapi.ts +63 -0
- package/src/cli/commands/remove.ts +72 -0
- package/src/cli/commands/run.ts +50 -0
- package/src/cli/commands/scope.ts +54 -0
- package/src/cli/commands/serve.ts +68 -0
- package/src/cli/commands/shard.ts +31 -0
- package/src/cli/commands/shared/config-pipework-version.ts +28 -0
- package/src/cli/commands/shared/find-config.ts +22 -0
- package/src/cli/commands/shared/load-config.ts +35 -0
- package/src/cli/commands/shared/pnpm-install.ts +95 -0
- package/src/cli/commands/shared/report-removed.ts +8 -0
- package/src/cli/commands/sync.ts +32 -0
- package/src/cli/commands/test.ts +307 -0
- package/src/cli/commands/upgrade.ts +48 -0
- package/src/cli/dev-bootstrap.ts +24 -0
- package/src/cli/errors.ts +10 -0
- package/src/cli/index.ts +303 -0
- package/src/cli/output.ts +17 -0
- package/src/cli/port.ts +15 -0
- package/src/cli/surface-kinds.ts +20 -0
- package/src/client/index.ts +4 -0
- package/src/client/poll.ts +130 -0
- package/src/client/report-fault.ts +119 -0
- package/src/core/config/app-pool.ts +19 -0
- package/src/core/config/aws-env.ts +29 -0
- package/src/core/config/base-dir.ts +24 -0
- package/src/core/config/discover.ts +64 -0
- package/src/core/config/env-resolve.ts +87 -0
- package/src/core/config/env-types.ts +116 -0
- package/src/core/config/env.ts +55 -0
- package/src/core/config/errors.ts +9 -0
- package/src/core/config/import-config.ts +40 -0
- package/src/core/config/index.ts +8 -0
- package/src/core/config/load.ts +417 -0
- package/src/core/config/namespace.ts +14 -0
- package/src/core/config/resolve.ts +195 -0
- package/src/core/config/schema.ts +488 -0
- package/src/core/config/statement-observer.ts +48 -0
- package/src/core/config/ts-inline-hooks.ts +75 -0
- package/src/core/config/ts-register.ts +4 -0
- package/src/core/config/ts-resolve-hooks.ts +101 -0
- package/src/core/errors/base.ts +49 -0
- package/src/core/errors/http.ts +146 -0
- package/src/core/errors/index.ts +2 -0
- package/src/core/errors/namespace.ts +31 -0
- package/src/core/lifecycle/drain.ts +33 -0
- package/src/core/lifecycle/health.ts +72 -0
- package/src/core/lifecycle/index.ts +4 -0
- package/src/core/lifecycle/namespace.ts +7 -0
- package/src/core/lifecycle/startup.ts +102 -0
- package/src/core/lifecycle/types.ts +36 -0
- package/src/core/logging/context.ts +60 -0
- package/src/core/logging/error-chain.ts +68 -0
- package/src/core/logging/index.ts +5 -0
- package/src/core/logging/logger.ts +65 -0
- package/src/core/logging/namespace.ts +16 -0
- package/src/core/logging/proxy.ts +22 -0
- package/src/core/logging/redact-match.ts +24 -0
- package/src/core/logging/types.ts +47 -0
- package/src/core/pipework.ts +566 -0
- package/src/core/runtime/broker.ts +69 -0
- package/src/core/surface/http.ts +133 -0
- package/src/core/surface/index.ts +11 -0
- package/src/core/surface/job-timeout.ts +40 -0
- package/src/core/surface/namespace.ts +13 -0
- package/src/core/surface/script.ts +33 -0
- package/src/core/surface/start.ts +142 -0
- package/src/core/surface/types.ts +225 -0
- package/src/core/surface/validate.ts +88 -0
- package/src/core/surface/worker.ts +528 -0
- package/src/core/validation/connectivity.ts +49 -0
- package/src/core/validation/domain.ts +99 -0
- package/src/core/validation/index.ts +5 -0
- package/src/core/validation/queue-tables.ts +312 -0
- package/src/core/validation/rls-role.ts +138 -0
- package/src/core/validation/startup.ts +51 -0
- package/src/core/validation/surfaces.ts +65 -0
- package/src/core/validation/types.ts +25 -0
- package/src/data/db/advisory-lock.ts +55 -0
- package/src/data/db/aggregates.ts +172 -0
- package/src/data/db/array-elements.ts +111 -0
- package/src/data/db/bulk-set.ts +53 -0
- package/src/data/db/col/index.ts +14 -0
- package/src/data/db/composed.ts +53 -0
- package/src/data/db/db.ts +276 -0
- package/src/data/db/excluded.ts +16 -0
- package/src/data/db/expressions.ts +473 -0
- package/src/data/db/filter.ts +90 -0
- package/src/data/db/function-source.ts +114 -0
- package/src/data/db/identifiers.ts +16 -0
- package/src/data/db/idx/index.ts +9 -0
- package/src/data/db/in-list.ts +49 -0
- package/src/data/db/index.ts +9 -0
- package/src/data/db/insert-split.ts +137 -0
- package/src/data/db/instrumentation.ts +82 -0
- package/src/data/db/maintenance.ts +28 -0
- package/src/data/db/namespace.ts +107 -0
- package/src/data/db/owner-labels.ts +9 -0
- package/src/data/db/pool.ts +187 -0
- package/src/data/db/query-label.ts +13 -0
- package/src/data/db/query-watch.ts +116 -0
- package/src/data/db/raw-client-warning.ts +37 -0
- package/src/data/db/readonly.ts +23 -0
- package/src/data/db/reservation.ts +151 -0
- package/src/data/db/schema-features.ts +14 -0
- package/src/data/db/serializable.ts +83 -0
- package/src/data/db/sql.ts +1 -0
- package/src/data/db/timeouts.ts +76 -0
- package/src/data/db/types.ts +18 -0
- package/src/data/db/unbound.ts +33 -0
- package/src/data/db/values.ts +29 -0
- package/src/data/domain/brand.ts +15 -0
- package/src/data/domain/build-factory.ts +193 -0
- package/src/data/domain/build-schema.ts +161 -0
- package/src/data/domain/build-table.ts +355 -0
- package/src/data/domain/define.ts +239 -0
- package/src/data/domain/external-registry.ts +34 -0
- package/src/data/domain/external.ts +77 -0
- package/src/data/domain/field.ts +279 -0
- package/src/data/domain/index.ts +13 -0
- package/src/data/domain/naming.ts +6 -0
- package/src/data/domain/projections.ts +57 -0
- package/src/data/domain/trace-safe-projection.ts +31 -0
- package/src/data/domain/traits.ts +108 -0
- package/src/data/domain/types.ts +289 -0
- package/src/data/jsonb/composed.ts +18 -0
- package/src/data/jsonb/index.ts +1 -0
- package/src/data/jsonb/namespace.ts +30 -0
- package/src/data/jsonb/query.ts +86 -0
- package/src/data/migrate/append-only-generate.ts +47 -0
- package/src/data/migrate/applied.ts +47 -0
- package/src/data/migrate/apply.ts +309 -0
- package/src/data/migrate/check.ts +119 -0
- package/src/data/migrate/diff.ts +357 -0
- package/src/data/migrate/door-generate.ts +130 -0
- package/src/data/migrate/framework-rls.ts +58 -0
- package/src/data/migrate/generate.ts +201 -0
- package/src/data/migrate/history.ts +215 -0
- package/src/data/migrate/index.ts +9 -0
- package/src/data/migrate/internal-definitions.ts +175 -0
- package/src/data/migrate/post-process.ts +73 -0
- package/src/data/migrate/pragma.ts +73 -0
- package/src/data/migrate/provision-framework.ts +147 -0
- package/src/data/migrate/rls-generate.ts +61 -0
- package/src/data/migrate/run.ts +124 -0
- package/src/data/migrate/safety.ts +167 -0
- package/src/data/migrate/snapshot.ts +368 -0
- package/src/data/migrate/sql-emit.ts +392 -0
- package/src/data/migrate/statements.ts +312 -0
- package/src/data/query/alias.ts +126 -0
- package/src/data/query/batch.ts +8 -0
- package/src/data/query/cache/core/cache.ts +78 -0
- package/src/data/query/cache/core/index.ts +1 -0
- package/src/data/query/cache/core/types.ts +29 -0
- package/src/data/query/cache/readme.md +230 -0
- package/src/data/query/casing.ts +76 -0
- package/src/data/query/collation.ts +11 -0
- package/src/data/query/column-builder.ts +370 -0
- package/src/data/query/column.ts +150 -0
- package/src/data/query/entity.ts +42 -0
- package/src/data/query/errors.ts +33 -0
- package/src/data/query/index.ts +14 -0
- package/src/data/query/logger.ts +47 -0
- package/src/data/query/operations.ts +45 -0
- package/src/data/query/pg-core/alias.ts +12 -0
- package/src/data/query/pg-core/checks.ts +32 -0
- package/src/data/query/pg-core/columns/all.ts +71 -0
- package/src/data/query/pg-core/columns/bigint.ts +110 -0
- package/src/data/query/pg-core/columns/bigserial.ts +128 -0
- package/src/data/query/pg-core/columns/boolean.ts +43 -0
- package/src/data/query/pg-core/columns/char.ts +85 -0
- package/src/data/query/pg-core/columns/cidr.ts +43 -0
- package/src/data/query/pg-core/columns/common.ts +366 -0
- package/src/data/query/pg-core/columns/custom.ts +232 -0
- package/src/data/query/pg-core/columns/date.common.ts +15 -0
- package/src/data/query/pg-core/columns/date.ts +112 -0
- package/src/data/query/pg-core/columns/double-precision.ts +55 -0
- package/src/data/query/pg-core/columns/enum.ts +201 -0
- package/src/data/query/pg-core/columns/index.ts +35 -0
- package/src/data/query/pg-core/columns/inet.ts +43 -0
- package/src/data/query/pg-core/columns/int.common.ts +57 -0
- package/src/data/query/pg-core/columns/integer.ts +53 -0
- package/src/data/query/pg-core/columns/interval.ts +83 -0
- package/src/data/query/pg-core/columns/json.ts +58 -0
- package/src/data/query/pg-core/columns/jsonb.ts +56 -0
- package/src/data/query/pg-core/columns/line.ts +117 -0
- package/src/data/query/pg-core/columns/macaddr.ts +43 -0
- package/src/data/query/pg-core/columns/macaddr8.ts +43 -0
- package/src/data/query/pg-core/columns/numeric.ts +227 -0
- package/src/data/query/pg-core/columns/point.ts +127 -0
- package/src/data/query/pg-core/columns/postgis_extension/geometry.ts +119 -0
- package/src/data/query/pg-core/columns/postgis_extension/utils.ts +47 -0
- package/src/data/query/pg-core/columns/real.ts +58 -0
- package/src/data/query/pg-core/columns/serial.ts +55 -0
- package/src/data/query/pg-core/columns/smallint.ts +53 -0
- package/src/data/query/pg-core/columns/smallserial.ts +60 -0
- package/src/data/query/pg-core/columns/text.ts +67 -0
- package/src/data/query/pg-core/columns/time.ts +72 -0
- package/src/data/query/pg-core/columns/timestamp.ts +163 -0
- package/src/data/query/pg-core/columns/uuid.ts +51 -0
- package/src/data/query/pg-core/columns/varchar.ts +89 -0
- package/src/data/query/pg-core/columns/vector_extension/bit.ts +68 -0
- package/src/data/query/pg-core/columns/vector_extension/halfvec.ts +80 -0
- package/src/data/query/pg-core/columns/vector_extension/sparsevec.ts +67 -0
- package/src/data/query/pg-core/columns/vector_extension/vector.ts +80 -0
- package/src/data/query/pg-core/db.ts +674 -0
- package/src/data/query/pg-core/dialect.ts +1404 -0
- package/src/data/query/pg-core/expressions.ts +25 -0
- package/src/data/query/pg-core/foreign-keys.ts +130 -0
- package/src/data/query/pg-core/index.ts +21 -0
- package/src/data/query/pg-core/indexes.ts +276 -0
- package/src/data/query/pg-core/policies.ts +56 -0
- package/src/data/query/pg-core/primary-keys.ts +62 -0
- package/src/data/query/pg-core/query-builders/count.ts +86 -0
- package/src/data/query/pg-core/query-builders/delete.ts +293 -0
- package/src/data/query/pg-core/query-builders/index.ts +7 -0
- package/src/data/query/pg-core/query-builders/insert.ts +452 -0
- package/src/data/query/pg-core/query-builders/query-builder.ts +123 -0
- package/src/data/query/pg-core/query-builders/query.ts +157 -0
- package/src/data/query/pg-core/query-builders/raw.ts +49 -0
- package/src/data/query/pg-core/query-builders/refresh-materialized-view.ts +108 -0
- package/src/data/query/pg-core/query-builders/select.ts +1346 -0
- package/src/data/query/pg-core/query-builders/select.types.ts +437 -0
- package/src/data/query/pg-core/query-builders/update.ts +626 -0
- package/src/data/query/pg-core/roles.ts +41 -0
- package/src/data/query/pg-core/schema.ts +72 -0
- package/src/data/query/pg-core/sequence.ts +41 -0
- package/src/data/query/pg-core/session.ts +292 -0
- package/src/data/query/pg-core/subquery.ts +46 -0
- package/src/data/query/pg-core/table.ts +252 -0
- package/src/data/query/pg-core/unique-constraint.ts +73 -0
- package/src/data/query/pg-core/utils/array.ts +95 -0
- package/src/data/query/pg-core/utils/index.ts +1 -0
- package/src/data/query/pg-core/utils.ts +104 -0
- package/src/data/query/pg-core/view-base.ts +14 -0
- package/src/data/query/pg-core/view-common.ts +1 -0
- package/src/data/query/pg-core/view.ts +435 -0
- package/src/data/query/pg-core/with-builder.ts +41 -0
- package/src/data/query/postgres-js/README.md +46 -0
- package/src/data/query/postgres-js/array-parser.ts +45 -0
- package/src/data/query/postgres-js/driver-error-boundary.ts +149 -0
- package/src/data/query/postgres-js/driver.ts +163 -0
- package/src/data/query/postgres-js/index.ts +2 -0
- package/src/data/query/postgres-js/session.ts +234 -0
- package/src/data/query/primary-key.ts +11 -0
- package/src/data/query/query-builders/query-builder.ts +19 -0
- package/src/data/query/query-builders/select.types.ts +175 -0
- package/src/data/query/query-promise.ts +35 -0
- package/src/data/query/relations.ts +725 -0
- package/src/data/query/runnable-query.ts +12 -0
- package/src/data/query/selection-proxy.ts +121 -0
- package/src/data/query/session.ts +8 -0
- package/src/data/query/sql/expressions/conditions.ts +741 -0
- package/src/data/query/sql/expressions/index.ts +2 -0
- package/src/data/query/sql/expressions/select.ts +45 -0
- package/src/data/query/sql/functions/aggregate.ts +129 -0
- package/src/data/query/sql/functions/index.ts +2 -0
- package/src/data/query/sql/functions/vector.ts +178 -0
- package/src/data/query/sql/index.ts +3 -0
- package/src/data/query/sql/sql.ts +794 -0
- package/src/data/query/subquery.ts +53 -0
- package/src/data/query/table.ts +208 -0
- package/src/data/query/table.utils.ts +2 -0
- package/src/data/query/tracing-utils.ts +3 -0
- package/src/data/query/tracing.ts +11 -0
- package/src/data/query/utils.ts +357 -0
- package/src/data/query/version.ts +4 -0
- package/src/data/query/view-common.ts +1 -0
- package/src/data/schema/branded.ts +13 -0
- package/src/data/schema/generated/coerce.ts +5 -0
- package/src/data/schema/generated/combinator.ts +6 -0
- package/src/data/schema/generated/encoding.ts +5 -0
- package/src/data/schema/generated/error.ts +6 -0
- package/src/data/schema/generated/format.ts +5 -0
- package/src/data/schema/generated/type-constructor.ts +10 -0
- package/src/data/schema/generated/type.ts +6 -0
- package/src/data/schema/index.ts +6 -0
- package/src/data/schema/json-schema.ts +33 -0
- package/src/data/schema/namespace.ts +55 -0
- package/src/data/schema/parse.ts +39 -0
- package/src/data/schema/types.ts +39 -0
- package/src/data/temporal/append-only.ts +134 -0
- package/src/data/temporal/close.ts +27 -0
- package/src/data/temporal/columns.ts +10 -0
- package/src/data/temporal/definition-queries.ts +70 -0
- package/src/data/temporal/errors.ts +10 -0
- package/src/data/temporal/get-current.ts +22 -0
- package/src/data/temporal/index.ts +9 -0
- package/src/data/temporal/namespace.ts +36 -0
- package/src/data/temporal/query.ts +25 -0
- package/src/data/temporal/resolve.ts +17 -0
- package/src/data/temporal/revise.ts +67 -0
- package/src/data/temporal/types.ts +22 -0
- package/src/data/valve/artifact.ts +33 -0
- package/src/data/valve/contract-diff.ts +21 -0
- package/src/data/valve/cross.ts +57 -0
- package/src/data/valve/crossing-ledger.ts +50 -0
- package/src/data/valve/decision.ts +44 -0
- package/src/data/valve/durable-crossing.ts +52 -0
- package/src/data/valve/execute-crossing.ts +77 -0
- package/src/data/valve/faucet.ts +56 -0
- package/src/data/valve/gate.ts +114 -0
- package/src/data/valve/graph.ts +77 -0
- package/src/data/valve/index.ts +53 -0
- package/src/data/valve/namespace.ts +57 -0
- package/src/data/valve/registry.ts +50 -0
- package/src/data/valve/schema/valve-crossing-fields.ts +23 -0
- package/src/data/valve/schema/valve-crossing.ts +9 -0
- package/src/data/valve/supply.ts +154 -0
- package/src/data/valve/types.ts +143 -0
- package/src/data/vector/columns.ts +27 -0
- package/src/data/vector/index.ts +4 -0
- package/src/data/vector/namespace.ts +33 -0
- package/src/data/vector/query.ts +85 -0
- package/src/data/vector/validate.ts +16 -0
- package/src/data/write/audit/actor.ts +15 -0
- package/src/data/write/audit/audit-columns.ts +18 -0
- package/src/data/write/audit/audit-select.ts +111 -0
- package/src/data/write/audit/bound-transaction.ts +14 -0
- package/src/data/write/audit/cte-compile.ts +32 -0
- package/src/data/write/audit/decode-returning.ts +47 -0
- package/src/data/write/audit/final-select.ts +43 -0
- package/src/data/write/audit/infra-audit.ts +36 -0
- package/src/data/write/audit/primary-key.ts +65 -0
- package/src/data/write/audit/projection.ts +41 -0
- package/src/data/write/audit/read-touches.ts +86 -0
- package/src/data/write/audit/touch-match.ts +78 -0
- package/src/data/write/audit/verb-delete.ts +62 -0
- package/src/data/write/audit/verb-insert.ts +36 -0
- package/src/data/write/audit/verb-select.ts +47 -0
- package/src/data/write/audit/verb-update.ts +60 -0
- package/src/data/write/audit/verb-upsert.ts +104 -0
- package/src/data/write/conflict.ts +43 -0
- package/src/data/write/delete.ts +38 -0
- package/src/data/write/execute.ts +243 -0
- package/src/data/write/index.ts +11 -0
- package/src/data/write/insert.ts +63 -0
- package/src/data/write/state.ts +43 -0
- package/src/data/write/update.ts +54 -0
- package/src/data/write/upsert.ts +58 -0
- package/src/data/write/values.ts +19 -0
- package/src/index.ts +230 -0
- package/src/infra/access-links/code.ts +70 -0
- package/src/infra/access-links/handle.ts +892 -0
- package/src/infra/access-links/index.ts +9 -0
- package/src/infra/access-links/namespace.ts +30 -0
- package/src/infra/access-links/person-check.ts +177 -0
- package/src/infra/access-links/schema.ts +71 -0
- package/src/infra/alerts/async-sources.ts +231 -0
- package/src/infra/alerts/audit.ts +36 -0
- package/src/infra/alerts/beat.ts +123 -0
- package/src/infra/alerts/client-faults.ts +111 -0
- package/src/infra/alerts/faults.ts +156 -0
- package/src/infra/alerts/handle.ts +778 -0
- package/src/infra/alerts/http.ts +192 -0
- package/src/infra/alerts/index.ts +12 -0
- package/src/infra/alerts/infra-sources.ts +180 -0
- package/src/infra/alerts/namespace.ts +81 -0
- package/src/infra/alerts/redact.ts +21 -0
- package/src/infra/alerts/request-faults.ts +41 -0
- package/src/infra/alerts/schema.ts +124 -0
- package/src/infra/alerts/sinks.ts +125 -0
- package/src/infra/alerts/source-options.ts +26 -0
- package/src/infra/alerts/sources.ts +503 -0
- package/src/infra/alerts/subsystem-sources.ts +137 -0
- package/src/infra/audit/emit.ts +87 -0
- package/src/infra/audit/index.ts +2 -0
- package/src/infra/audit/namespace.ts +22 -0
- package/src/infra/audit/schema.sql +35 -0
- package/src/infra/audit/types.ts +18 -0
- package/src/infra/aws/credentials.ts +147 -0
- package/src/infra/aws/sigv4.ts +140 -0
- package/src/infra/cache/create.ts +58 -0
- package/src/infra/cache/entry.ts +81 -0
- package/src/infra/cache/index.ts +4 -0
- package/src/infra/cache/namespace.ts +10 -0
- package/src/infra/cache/tenant.ts +93 -0
- package/src/infra/cache/types.ts +24 -0
- package/src/infra/crawl/audit.ts +35 -0
- package/src/infra/crawl/errors.ts +25 -0
- package/src/infra/crawl/handle.ts +693 -0
- package/src/infra/crawl/index.ts +9 -0
- package/src/infra/crawl/links.ts +160 -0
- package/src/infra/crawl/namespace.ts +36 -0
- package/src/infra/crawl/robots.ts +100 -0
- package/src/infra/crawl/schema.ts +89 -0
- package/src/infra/credit-line/handle.ts +883 -0
- package/src/infra/credit-line/index.ts +6 -0
- package/src/infra/credit-line/namespace.ts +7 -0
- package/src/infra/credit-line/schema.ts +96 -0
- package/src/infra/credits/handle.ts +706 -0
- package/src/infra/credits/index.ts +5 -0
- package/src/infra/credits/namespace.ts +7 -0
- package/src/infra/credits/schema.ts +83 -0
- package/src/infra/email/audit.ts +36 -0
- package/src/infra/email/inbound.ts +622 -0
- package/src/infra/email/index.ts +11 -0
- package/src/infra/email/mail.ts +517 -0
- package/src/infra/email/mime.ts +447 -0
- package/src/infra/email/namespace.ts +22 -0
- package/src/infra/email/providers.ts +111 -0
- package/src/infra/email/schema.ts +96 -0
- package/src/infra/email/sns.ts +200 -0
- package/src/infra/email/unsubscribe.ts +44 -0
- package/src/infra/fetch/address.ts +88 -0
- package/src/infra/fetch/connect-retry.ts +87 -0
- package/src/infra/fetch/handle.ts +329 -0
- package/src/infra/fetch/index.ts +7 -0
- package/src/infra/fetch/namespace.ts +70 -0
- package/src/infra/idempotency/index.ts +3 -0
- package/src/infra/idempotency/middleware.ts +87 -0
- package/src/infra/idempotency/store.ts +92 -0
- package/src/infra/idempotency/types.ts +30 -0
- package/src/infra/join-requests/handle.ts +312 -0
- package/src/infra/join-requests/index.ts +2 -0
- package/src/infra/join-requests/namespace.ts +7 -0
- package/src/infra/join-requests/schema.ts +33 -0
- package/src/infra/outbox/core.ts +165 -0
- package/src/infra/quota/errors.ts +32 -0
- package/src/infra/quota/handle.ts +215 -0
- package/src/infra/quota/index.ts +5 -0
- package/src/infra/quota/namespace.ts +7 -0
- package/src/infra/quota/schema.ts +15 -0
- package/src/infra/storage/backend.ts +34 -0
- package/src/infra/storage/handle.ts +513 -0
- package/src/infra/storage/index.ts +9 -0
- package/src/infra/storage/local.ts +104 -0
- package/src/infra/storage/namespace.ts +13 -0
- package/src/infra/storage/s3.ts +257 -0
- package/src/infra/storage/schema.ts +24 -0
- package/src/infra/support/handle.ts +869 -0
- package/src/infra/support/http.ts +229 -0
- package/src/infra/support/index.ts +7 -0
- package/src/infra/support/namespace.ts +12 -0
- package/src/infra/support/schema.ts +96 -0
- package/src/infra/webhook/inbound.ts +45 -0
- package/src/infra/webhook/index.ts +5 -0
- package/src/infra/webhook/namespace.ts +24 -0
- package/src/infra/webhook/outbound.ts +315 -0
- package/src/infra/webhook/sign.ts +32 -0
- package/src/infra/webhook/verify.ts +82 -0
- package/src/invariants/assert.ts +26 -0
- package/src/invariants/index.ts +1 -0
- package/src/lint/config.ts +56 -0
- package/src/lint/doctrine.ts +163 -0
- package/src/lint/include.ts +97 -0
- package/src/lint/index.ts +3 -0
- package/src/lint/preset.ts +10 -0
- package/src/lint/resolver.ts +218 -0
- package/src/lint/rules/no-reexports.ts +46 -0
- package/src/request/behavior/audited.ts +85 -0
- package/src/request/behavior/cached.ts +62 -0
- package/src/request/behavior/compose.ts +28 -0
- package/src/request/behavior/index.ts +5 -0
- package/src/request/behavior/namespace.ts +16 -0
- package/src/request/behavior/types.ts +26 -0
- package/src/request/behavior/versioned.ts +54 -0
- package/src/request/context/create.ts +103 -0
- package/src/request/context/guard.ts +49 -0
- package/src/request/context/index.ts +4 -0
- package/src/request/context/run-in-transaction.ts +200 -0
- package/src/request/context/store.ts +28 -0
- package/src/request/context/types.ts +50 -0
- package/src/request/di/builder.ts +535 -0
- package/src/request/di/index.ts +8 -0
- package/src/request/di/resolve.ts +271 -0
- package/src/request/di/types.ts +262 -0
- package/src/request/fitting/namespace.ts +7 -0
- package/src/request/fixture/batch.ts +71 -0
- package/src/request/fixture/builder.ts +26 -0
- package/src/request/fixture/errors.ts +10 -0
- package/src/request/fixture/index.ts +8 -0
- package/src/request/fixture/namespace.ts +23 -0
- package/src/request/fixture/paginate.ts +44 -0
- package/src/request/fixture/to-handlers.ts +260 -0
- package/src/request/fixture/types.ts +79 -0
- package/src/request/flow/act-as.ts +78 -0
- package/src/request/flow/namespace.ts +17 -0
- package/src/request/http/error-step.ts +42 -0
- package/src/request/http/file.ts +141 -0
- package/src/request/http/index.ts +4 -0
- package/src/request/http/middleware.ts +141 -0
- package/src/request/http/namespace.ts +5 -0
- package/src/request/http/rate-limit-classes.ts +170 -0
- package/src/request/http/response.ts +82 -0
- package/src/request/http/server.ts +916 -0
- package/src/request/http/sniff.ts +261 -0
- package/src/request/http/types.ts +250 -0
- package/src/request/http/upload.ts +284 -0
- package/src/request/internal/text.ts +6 -0
- package/src/request/openapi/generate.ts +205 -0
- package/src/request/openapi/index.ts +2 -0
- package/src/request/openapi/namespace.ts +7 -0
- package/src/test/auth.ts +13 -0
- package/src/test/auto-setup.ts +45 -0
- package/src/test/context.ts +91 -0
- package/src/test/crawl-fixture.ts +157 -0
- package/src/test/discover-fork.ts +17 -0
- package/src/test/fingerprint.ts +135 -0
- package/src/test/globals.d.ts +7 -0
- package/src/test/index.ts +19 -0
- package/src/test/namespace.ts +87 -0
- package/src/test/plugin.ts +189 -0
- package/src/test/progress.ts +185 -0
- package/src/test/reap-after-run.ts +28 -0
- package/src/test/reap.ts +286 -0
- package/src/test/reset.ts +253 -0
- package/src/test/savepoints.ts +172 -0
- package/src/test/setup.ts +1422 -0
- package/src/test/system-channel.ts +50 -0
- package/src/test/vitest-plugin.ts +142 -0
- package/src/test/vitest-workspace.ts +507 -0
- package/src/test/vitest.ts +333 -0
- package/src/test/watch.ts +105 -0
- package/src/trace/capture/bounded-serializer.ts +146 -0
- package/src/trace/cardinality/analyze.ts +173 -0
- package/src/trace/cardinality/branch-context.ts +214 -0
- package/src/trace/cardinality/check.ts +33 -0
- package/src/trace/cardinality/classify-edge.ts +64 -0
- package/src/trace/cardinality/structural-max.ts +107 -0
- package/src/trace/check/coverage-check.ts +188 -0
- package/src/trace/check/flow-check.ts +103 -0
- package/src/trace/check/flow-step-site.ts +234 -0
- package/src/trace/check/io-pattern.ts +116 -0
- package/src/trace/codemod/add-cardinality.ts +152 -0
- package/src/trace/codemod/run.ts +80 -0
- package/src/trace/entry/child-trace.ts +21 -0
- package/src/trace/entry/close-trace.ts +87 -0
- package/src/trace/entry/finalize-trace.ts +28 -0
- package/src/trace/entry/open-trace.ts +138 -0
- package/src/trace/entry/parent-trace.ts +20 -0
- package/src/trace/entry/parse-traceparent.ts +39 -0
- package/src/trace/entry/process-start.ts +38 -0
- package/src/trace/entry/trace-context.ts +26 -0
- package/src/trace/entry/tracing-enabled.ts +10 -0
- package/src/trace/external/record.ts +195 -0
- package/src/trace/external/report.ts +66 -0
- package/src/trace/namespace.ts +19 -0
- package/src/trace/partition/day.ts +47 -0
- package/src/trace/partition/index.ts +14 -0
- package/src/trace/partition/partition-name.ts +23 -0
- package/src/trace/partition/plan.ts +82 -0
- package/src/trace/partition/schedule.ts +26 -0
- package/src/trace/partition/startup-hook.ts +39 -0
- package/src/trace/partition/sync.ts +154 -0
- package/src/trace/partition/tables.ts +53 -0
- package/src/trace/read/tree.ts +83 -0
- package/src/trace/retain/copy-on-error.ts +76 -0
- package/src/trace/runtime/expire-stale-traces.ts +54 -0
- package/src/trace/runtime/flow-step.ts +147 -0
- package/src/trace/runtime/flush-steps.ts +31 -0
- package/src/trace/runtime/pending-writes.ts +54 -0
- package/src/trace/runtime/step-buffer.ts +298 -0
- package/src/trace/runtime/step-error.ts +21 -0
- package/src/trace/runtime/step-meta.ts +55 -0
- package/src/trace/runtime/thenable.ts +12 -0
- package/src/trace/runtime/truncate-trace.ts +62 -0
- package/src/trace/schema/trace-fields.ts +52 -0
- package/src/trace/schema/trace-retained.ts +22 -0
- package/src/trace/schema/trace-step-fields.ts +44 -0
- package/src/trace/schema/trace-step-retained.ts +25 -0
- package/src/trace/schema/trace-step.ts +18 -0
- package/src/trace/schema/trace.ts +12 -0
- package/src/trace/transformer/apply.ts +79 -0
- package/src/trace/transformer/build-meta-literal.ts +30 -0
- package/src/trace/transformer/find-flow-step-sites.ts +85 -0
- package/src/trace/transformer/inject-flow-step-import.ts +39 -0
- package/src/trace/transformer/transform-error.ts +14 -0
- package/src/trace/transformer/transform-flow-steps.ts +123 -0
- package/src/trace/transformer/wrap-function-declaration.ts +62 -0
- package/src/trace/transformer/wrap-variable-declaration.ts +67 -0
- package/src/workspace/affected.ts +110 -0
- package/src/workspace/changed-files.ts +163 -0
- package/src/workspace/check-deps.ts +213 -0
- package/src/workspace/checksum.ts +20 -0
- package/src/workspace/dependency-graph.ts +67 -0
- package/src/workspace/detect-pipework-copies.ts +93 -0
- package/src/workspace/discover.ts +206 -0
- package/src/workspace/generate.ts +401 -0
- package/src/workspace/index.ts +7 -0
- package/src/workspace/module-imports-pipework.ts +39 -0
- package/src/workspace/module-package-name.ts +12 -0
- package/src/workspace/scrub-git-env.ts +23 -0
- package/src/workspace/source-paths.ts +36 -0
- package/src/workspace/sync.ts +182 -0
- package/dist/REFERENCE.md +0 -1477
- package/dist/infra/access-links/rate-limit.d.ts +0 -10
- package/dist/infra/access-links/rate-limit.d.ts.map +0 -1
- package/dist/infra/access-links/rate-limit.js +0 -28
- package/dist/infra/access-links/rate-limit.js.map +0 -1
- package/dist/infra/email/sigv4.d.ts +0 -24
- package/dist/infra/email/sigv4.d.ts.map +0 -1
- package/dist/infra/email/sigv4.js +0 -55
- package/dist/infra/email/sigv4.js.map +0 -1
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export type { AuthStrategy, AuthRequest, AuthContext, BaseAuth } from './types.js'
|
|
2
|
+
export { runAuthChain } from './chain.js'
|
|
3
|
+
export type { AuthEnricher, TenantResolver, AuthChainConfig, AuthChainResult } from './chain.js'
|
|
4
|
+
export { createSessions } from './sessions.js'
|
|
5
|
+
export type { SessionAuth, SessionStrategyOptions } from './sessions.js'
|
|
6
|
+
export type { Sessions, SessionConfig, TokenPair, TokenPayload, HttpTokenResult, HttpRequest, HttpResponse } from './sessions.js'
|
|
7
|
+
export type { CookieConfig, ResolvedCookieConfig } from './cookie.js'
|
|
8
|
+
export { resolveCookieConfig, buildSetCookieHeader, buildClearCookieHeader, parseCookieHeader } from './cookie.js'
|
|
9
|
+
export { createMultiOrgSessions } from './multi-org.js'
|
|
10
|
+
export type { MultiOrgConfig, MultiOrgSessions, ResolveLoginResult, AuthenticatedResult, OrgSelectResult, OrgMembership, OrgInfo, OrgMembershipDetail } from './multi-org.js'
|
|
11
|
+
export { createPasswords } from './passwords.js'
|
|
12
|
+
export type { Passwords, PasswordsConfig, ScryptParams, VerifyResult } from './passwords.js'
|
|
13
|
+
export { createSignIn } from './sign-in.js'
|
|
14
|
+
export type { SignIn, SignInConfig, SignInAttempt, SignInResult } from './sign-in.js'
|
|
15
|
+
export { createGuard } from './guard.js'
|
|
16
|
+
export type { Guard, GuardConfig, GuardKeys, GuardTake, GuardStatus } from './guard.js'
|
|
17
|
+
export { createEmailLinks } from './email-links.js'
|
|
18
|
+
export type { EmailLinks, EmailLinksConfig, EmailLinkPurpose, EmailLinkPurposeConfig, EmailLinkState, EmailLinkCheck, EmailLinkRefusal, RequestLimit } from './email-links.js'
|
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
import { SignJWT, jwtVerify } from 'jose'
|
|
2
|
+
import { parseExpiry } from './parse-expiry.js'
|
|
3
|
+
import { sql } from '../../data/db/sql.js'
|
|
4
|
+
import type { DB } from '../../data/db/index.js'
|
|
5
|
+
import { ForbiddenError, UnauthorizedError, ConflictError, NotFoundError } from '../../core/errors/index.js'
|
|
6
|
+
import { assertValidIdentifier } from '../../data/db/identifiers.js'
|
|
7
|
+
import { assertUuid } from './uuid.js'
|
|
8
|
+
import { checkSigningKeys, importSigningKey } from './signing-keys.js'
|
|
9
|
+
import type { Sessions, HttpResponse, HttpRequest, HttpTokenResult } from './sessions.js'
|
|
10
|
+
import { refuseScopedHandle } from '../tenant/scoped-db.js'
|
|
11
|
+
|
|
12
|
+
/** Configuration for multi-org sessions — base sessions, signing keys, org-select token TTL. */
|
|
13
|
+
export interface MultiOrgConfig {
|
|
14
|
+
readonly sessions: Sessions
|
|
15
|
+
readonly signing: { algorithm: 'ES256'; privateKey: string; publicKey: string }
|
|
16
|
+
readonly issuer: string
|
|
17
|
+
readonly audience: string
|
|
18
|
+
readonly orgSelectToken?: { readonly expiresIn?: string }
|
|
19
|
+
/**
|
|
20
|
+
* Membership table name. Default `pipework_user_orgs`, whose definition ships with the `orgs: {}`
|
|
21
|
+
* blueprint flag. A custom name means the app owns that table's DDL (org_id uuid, same columns).
|
|
22
|
+
*/
|
|
23
|
+
readonly table?: string
|
|
24
|
+
readonly resolveOrgNames?: (db: DB, orgIds: string[]) => Promise<Map<string, string>>
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** A user's membership in an organization — userId, orgId, role. */
|
|
28
|
+
export interface OrgMembership {
|
|
29
|
+
readonly id: string
|
|
30
|
+
readonly orgId: string
|
|
31
|
+
readonly role: string | null
|
|
32
|
+
readonly joinedAt: Date
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Basic organization info — id and display name. */
|
|
36
|
+
export interface OrgInfo {
|
|
37
|
+
readonly id: string
|
|
38
|
+
readonly name: string
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Organization membership with display name — extends OrgMembership with org info. */
|
|
42
|
+
export interface OrgMembershipDetail {
|
|
43
|
+
readonly id: string
|
|
44
|
+
readonly name: string
|
|
45
|
+
readonly role: string | null
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Result when user has exactly one org — direct authentication, no org selection needed. */
|
|
49
|
+
export interface AuthenticatedResult {
|
|
50
|
+
readonly type: 'authenticated'
|
|
51
|
+
readonly accessToken: string
|
|
52
|
+
readonly expiresIn: number
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Result when user has multiple orgs — returns a session token for org selection. */
|
|
56
|
+
export interface OrgSelectResult {
|
|
57
|
+
readonly type: 'org_select'
|
|
58
|
+
readonly sessionToken: string
|
|
59
|
+
readonly orgs: readonly OrgInfo[]
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Union result from resolveLogin() — either AuthenticatedResult or OrgSelectResult. */
|
|
63
|
+
export type ResolveLoginResult = AuthenticatedResult | OrgSelectResult
|
|
64
|
+
|
|
65
|
+
/** Multi-org session manager — login resolution, org selection, switching, membership CRUD. */
|
|
66
|
+
export interface MultiOrgSessions {
|
|
67
|
+
resolveLogin(
|
|
68
|
+
response: HttpResponse,
|
|
69
|
+
db: DB,
|
|
70
|
+
user: { id: string },
|
|
71
|
+
): Promise<ResolveLoginResult>
|
|
72
|
+
|
|
73
|
+
selectOrg(
|
|
74
|
+
response: HttpResponse,
|
|
75
|
+
db: DB,
|
|
76
|
+
sessionToken: string,
|
|
77
|
+
orgId: string,
|
|
78
|
+
): Promise<HttpTokenResult>
|
|
79
|
+
|
|
80
|
+
switchOrg(
|
|
81
|
+
response: HttpResponse,
|
|
82
|
+
db: DB,
|
|
83
|
+
request: Pick<HttpRequest, 'headers'> & { readonly method?: string },
|
|
84
|
+
orgId: string,
|
|
85
|
+
userId: string,
|
|
86
|
+
): Promise<HttpTokenResult>
|
|
87
|
+
|
|
88
|
+
listOrgs(
|
|
89
|
+
db: DB,
|
|
90
|
+
userId: string,
|
|
91
|
+
): Promise<{ orgs: readonly OrgMembershipDetail[] }>
|
|
92
|
+
|
|
93
|
+
addMember(
|
|
94
|
+
db: DB,
|
|
95
|
+
params: { userId: string; orgId: string; role?: string; addedBy?: string },
|
|
96
|
+
): Promise<{ id: string }>
|
|
97
|
+
|
|
98
|
+
removeMember(
|
|
99
|
+
db: DB,
|
|
100
|
+
params: { userId: string; orgId: string },
|
|
101
|
+
): Promise<void>
|
|
102
|
+
|
|
103
|
+
checkMembership(
|
|
104
|
+
db: DB,
|
|
105
|
+
userId: string,
|
|
106
|
+
orgId: string,
|
|
107
|
+
): Promise<OrgMembership | null>
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Creates a multi-org session manager with org selection flow and membership management.
|
|
112
|
+
* The default table comes from blueprint `orgs: {}`; org ids must be uuids.
|
|
113
|
+
*/
|
|
114
|
+
export function createMultiOrgSessions(config: MultiOrgConfig): MultiOrgSessions {
|
|
115
|
+
const tableName = config.table ?? 'pipework_user_orgs'
|
|
116
|
+
assertValidIdentifier(tableName, 'multi-org membership table')
|
|
117
|
+
const t = sql.raw(`"${tableName}"`)
|
|
118
|
+
|
|
119
|
+
checkSigningKeys(config.signing, 'createMultiOrgSessions')
|
|
120
|
+
|
|
121
|
+
const orgSelectAudience = `${config.audience}:org-select`
|
|
122
|
+
const orgSelectExpiresIn = config.orgSelectToken?.expiresIn ?? '10m'
|
|
123
|
+
|
|
124
|
+
let cachedPrivateKey: CryptoKey | null = null
|
|
125
|
+
let cachedPublicKey: CryptoKey | null = null
|
|
126
|
+
|
|
127
|
+
async function getPrivateKey(): Promise<CryptoKey> {
|
|
128
|
+
if (cachedPrivateKey === null) {
|
|
129
|
+
cachedPrivateKey = await importSigningKey('private', config.signing, 'createMultiOrgSessions')
|
|
130
|
+
}
|
|
131
|
+
return cachedPrivateKey
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async function getPublicKey(): Promise<CryptoKey> {
|
|
135
|
+
if (cachedPublicKey === null) {
|
|
136
|
+
cachedPublicKey = await importSigningKey('public', config.signing, 'createMultiOrgSessions')
|
|
137
|
+
}
|
|
138
|
+
return cachedPublicKey
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
async function signOrgSelectToken(userId: string): Promise<string> {
|
|
143
|
+
const key = await getPrivateKey()
|
|
144
|
+
const expiresInSeconds = parseExpiry(orgSelectExpiresIn)
|
|
145
|
+
return new SignJWT({ purpose: 'org_select' })
|
|
146
|
+
.setProtectedHeader({ alg: config.signing.algorithm })
|
|
147
|
+
.setSubject(userId)
|
|
148
|
+
.setIssuer(config.issuer)
|
|
149
|
+
.setAudience(orgSelectAudience)
|
|
150
|
+
.setIssuedAt()
|
|
151
|
+
.setExpirationTime(`${expiresInSeconds}s`)
|
|
152
|
+
.sign(key)
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
async function verifyOrgSelectToken(token: string): Promise<string> {
|
|
156
|
+
const key = await getPublicKey()
|
|
157
|
+
try {
|
|
158
|
+
const { payload } = await jwtVerify(token, key, {
|
|
159
|
+
algorithms: [config.signing.algorithm],
|
|
160
|
+
issuer: config.issuer,
|
|
161
|
+
audience: orgSelectAudience,
|
|
162
|
+
})
|
|
163
|
+
if (payload['purpose'] !== 'org_select') {
|
|
164
|
+
throw new Error('wrong purpose')
|
|
165
|
+
}
|
|
166
|
+
return payload.sub!
|
|
167
|
+
} catch {
|
|
168
|
+
throw new UnauthorizedError(
|
|
169
|
+
'Invalid or expired organization selection token. Please log in again.',
|
|
170
|
+
)
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
async function requireMembership(db: DB, userId: string, orgId: string): Promise<void> {
|
|
175
|
+
assertUuid(orgId, 'orgId')
|
|
176
|
+
const rows = await db.execute(
|
|
177
|
+
sql`SELECT 1 FROM ${t} WHERE user_id = ${userId} AND org_id = ${orgId}`,
|
|
178
|
+
)
|
|
179
|
+
if (rows.length === 0) {
|
|
180
|
+
throw new ForbiddenError('You are not a member of this organization.')
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
async function resolveLogin(
|
|
185
|
+
response: HttpResponse,
|
|
186
|
+
db: DB,
|
|
187
|
+
user: { id: string },
|
|
188
|
+
): Promise<ResolveLoginResult> {
|
|
189
|
+
const rows = await db.execute(
|
|
190
|
+
sql`SELECT org_id, role FROM ${t} WHERE user_id = ${user.id} ORDER BY joined_at`,
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
if (rows.length === 0) {
|
|
194
|
+
throw new ForbiddenError(
|
|
195
|
+
'User has no organization memberships. Contact your administrator to be added to an organization.',
|
|
196
|
+
)
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (rows.length === 1) {
|
|
200
|
+
const orgId = rows[0]!['org_id'] as string
|
|
201
|
+
const result = await config.sessions.issueTokensHttp(response, db, { id: user.id, tenantId: orgId })
|
|
202
|
+
return { type: 'authenticated', accessToken: result.accessToken, expiresIn: result.expiresIn }
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const orgIds = rows.map(r => r['org_id'] as string)
|
|
206
|
+
let orgNames: Map<string, string> | null = null
|
|
207
|
+
if (config.resolveOrgNames !== undefined) {
|
|
208
|
+
orgNames = await config.resolveOrgNames(db, orgIds)
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const orgs: OrgInfo[] = orgIds.map(id => ({
|
|
212
|
+
id,
|
|
213
|
+
name: orgNames?.get(id) ?? id,
|
|
214
|
+
}))
|
|
215
|
+
|
|
216
|
+
const sessionToken = await signOrgSelectToken(user.id)
|
|
217
|
+
return { type: 'org_select', sessionToken, orgs }
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
async function selectOrg(
|
|
221
|
+
response: HttpResponse,
|
|
222
|
+
db: DB,
|
|
223
|
+
sessionToken: string,
|
|
224
|
+
orgId: string,
|
|
225
|
+
): Promise<HttpTokenResult> {
|
|
226
|
+
const userId = await verifyOrgSelectToken(sessionToken)
|
|
227
|
+
await requireMembership(db, userId, orgId)
|
|
228
|
+
return config.sessions.issueTokensHttp(response, db, { id: userId, tenantId: orgId })
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
async function switchOrg(
|
|
232
|
+
response: HttpResponse,
|
|
233
|
+
db: DB,
|
|
234
|
+
request: Pick<HttpRequest, 'headers'> & { readonly method?: string },
|
|
235
|
+
orgId: string,
|
|
236
|
+
userId: string,
|
|
237
|
+
): Promise<HttpTokenResult> {
|
|
238
|
+
config.sessions.assertSameSiteCookie(request)
|
|
239
|
+
await requireMembership(db, userId, orgId)
|
|
240
|
+
await config.sessions.revokeRefreshCookie(db, request)
|
|
241
|
+
return config.sessions.issueTokensHttp(response, db, { id: userId, tenantId: orgId })
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
async function listOrgs(
|
|
245
|
+
db: DB,
|
|
246
|
+
userId: string,
|
|
247
|
+
): Promise<{ orgs: readonly OrgMembershipDetail[] }> {
|
|
248
|
+
const rows = await db.execute(
|
|
249
|
+
sql`SELECT org_id, role FROM ${t} WHERE user_id = ${userId} ORDER BY joined_at`,
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
const orgIds = rows.map(r => r['org_id'] as string)
|
|
253
|
+
let orgNames: Map<string, string> | null = null
|
|
254
|
+
if (config.resolveOrgNames !== undefined && orgIds.length > 0) {
|
|
255
|
+
orgNames = await config.resolveOrgNames(db, orgIds)
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const orgs: OrgMembershipDetail[] = rows.map(r => {
|
|
259
|
+
const orgId = r['org_id'] as string
|
|
260
|
+
return {
|
|
261
|
+
id: orgId,
|
|
262
|
+
name: orgNames?.get(orgId) ?? orgId,
|
|
263
|
+
role: (r['role'] as string | null) ?? null,
|
|
264
|
+
}
|
|
265
|
+
})
|
|
266
|
+
|
|
267
|
+
return { orgs }
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
async function addMember(
|
|
271
|
+
db: DB,
|
|
272
|
+
params: { userId: string; orgId: string; role?: string; addedBy?: string },
|
|
273
|
+
): Promise<{ id: string }> {
|
|
274
|
+
assertUuid(params.orgId, 'orgId')
|
|
275
|
+
const role = params.role ?? null
|
|
276
|
+
const addedBy = params.addedBy ?? null
|
|
277
|
+
const rows = await db.execute(
|
|
278
|
+
sql`INSERT INTO ${t} (user_id, org_id, role, added_by)
|
|
279
|
+
VALUES (${params.userId}, ${params.orgId}, ${role}, ${addedBy})
|
|
280
|
+
ON CONFLICT (user_id, org_id) DO NOTHING
|
|
281
|
+
RETURNING id`,
|
|
282
|
+
)
|
|
283
|
+
|
|
284
|
+
if (rows.length === 0) {
|
|
285
|
+
throw new ConflictError('User is already a member of this organization.')
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
return { id: rows[0]!['id'] as string }
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
async function removeMember(
|
|
292
|
+
db: DB,
|
|
293
|
+
params: { userId: string; orgId: string },
|
|
294
|
+
): Promise<void> {
|
|
295
|
+
assertUuid(params.orgId, 'orgId')
|
|
296
|
+
const rows = await db.execute(
|
|
297
|
+
sql`DELETE FROM ${t} WHERE user_id = ${params.userId} AND org_id = ${params.orgId} RETURNING id`,
|
|
298
|
+
)
|
|
299
|
+
|
|
300
|
+
if (rows.length === 0) {
|
|
301
|
+
throw new NotFoundError('Membership')
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
await config.sessions.bumpVersion(db, params.userId)
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
async function checkMembershipFn(
|
|
308
|
+
db: DB,
|
|
309
|
+
userId: string,
|
|
310
|
+
orgId: string,
|
|
311
|
+
): Promise<OrgMembership | null> {
|
|
312
|
+
assertUuid(orgId, 'orgId')
|
|
313
|
+
const rows = await db.execute(
|
|
314
|
+
sql`SELECT id, org_id, role, joined_at FROM ${t} WHERE user_id = ${userId} AND org_id = ${orgId}`,
|
|
315
|
+
)
|
|
316
|
+
|
|
317
|
+
if (rows.length === 0) return null
|
|
318
|
+
|
|
319
|
+
const row = rows[0]!
|
|
320
|
+
return {
|
|
321
|
+
id: row['id'] as string,
|
|
322
|
+
orgId: row['org_id'] as string,
|
|
323
|
+
role: (row['role'] as string | null) ?? null,
|
|
324
|
+
joinedAt: new Date(row['joined_at'] as string),
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
return refuseScopedHandle({
|
|
329
|
+
resolveLogin,
|
|
330
|
+
selectOrg,
|
|
331
|
+
switchOrg,
|
|
332
|
+
listOrgs,
|
|
333
|
+
addMember,
|
|
334
|
+
removeMember,
|
|
335
|
+
checkMembership: checkMembershipFn,
|
|
336
|
+
}, 'multiOrg', 'a user\'s memberships span tenants')
|
|
337
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { createSessions } from './sessions.js'
|
|
2
|
+
import { createMultiOrgSessions } from './multi-org.js'
|
|
3
|
+
import { createPasswords } from './passwords.js'
|
|
4
|
+
import { createSignIn } from './sign-in.js'
|
|
5
|
+
import { createGuard } from './guard.js'
|
|
6
|
+
import { createEmailLinks } from './email-links.js'
|
|
7
|
+
import { runAuthChain } from './chain.js'
|
|
8
|
+
import { resolveCookieConfig, parseCookieHeader, buildSetCookieHeader, buildClearCookieHeader } from './cookie.js'
|
|
9
|
+
|
|
10
|
+
/** Authentication namespace — session management, multi-org support, auth chain resolution. */
|
|
11
|
+
export const auth = {
|
|
12
|
+
/** Creates a Sessions manager with JWT access/refresh tokens, rotation, and reuse detection. */
|
|
13
|
+
createSessions,
|
|
14
|
+
/** Creates multi-org session management — org selection, switching, membership CRUD. */
|
|
15
|
+
createMultiOrg: createMultiOrgSessions,
|
|
16
|
+
/** Hashes and verifies passwords with scrypt; reports when a stored hash needs rehashing. */
|
|
17
|
+
passwords: createPasswords,
|
|
18
|
+
/** Guarded password sign-in that slows, then refuses, repeated failures per account and per client. */
|
|
19
|
+
signIn: createSignIn,
|
|
20
|
+
/** Guards wrong guesses at a secret the app checks itself: counted per key set before the check, reset on a right answer, refused with retryAfterSeconds. */
|
|
21
|
+
guard: createGuard,
|
|
22
|
+
/** Emailed single-use links that prove control of an address: password reset and address verification; the token is never stored. */
|
|
23
|
+
emailLinks: createEmailLinks,
|
|
24
|
+
/** Executes an auth chain (strategy pipeline) to resolve auth + tenant from a request. */
|
|
25
|
+
runChain: runAuthChain,
|
|
26
|
+
/** Resolves CookieConfig with defaults for secure cookie-based token delivery. */
|
|
27
|
+
resolveCookie: resolveCookieConfig,
|
|
28
|
+
/** Parses a Cookie header string into key-value pairs. */
|
|
29
|
+
parseCookie: parseCookieHeader,
|
|
30
|
+
/** Builds a Set-Cookie header string from name, value, and CookieConfig. */
|
|
31
|
+
buildSetCookie: buildSetCookieHeader,
|
|
32
|
+
/** Builds a Set-Cookie header that clears (expires) a cookie. */
|
|
33
|
+
buildClearCookie: buildClearCookieHeader,
|
|
34
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export function parseExpiry(exp: string): number {
|
|
2
|
+
const match = exp.match(/^(\d+)([smhd])$/)
|
|
3
|
+
if (!match) throw new Error(`Invalid expiry format: ${exp}`)
|
|
4
|
+
const value = parseInt(match[1]!, 10)
|
|
5
|
+
const unit = match[2]
|
|
6
|
+
switch (unit) {
|
|
7
|
+
case 's': return value
|
|
8
|
+
case 'm': return value * 60
|
|
9
|
+
case 'h': return value * 3600
|
|
10
|
+
case 'd': return value * 86400
|
|
11
|
+
default: return value
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { scrypt, randomBytes, timingSafeEqual } from 'node:crypto'
|
|
2
|
+
import { ValidationError } from '../../core/errors/index.js'
|
|
3
|
+
import { getBaseLogger } from '../../core/logging/index.js'
|
|
4
|
+
|
|
5
|
+
export interface ScryptParams {
|
|
6
|
+
/** log2 of the CPU/memory cost N. Default 15. */
|
|
7
|
+
readonly ln: number
|
|
8
|
+
/** Block size. Default 8. */
|
|
9
|
+
readonly r: number
|
|
10
|
+
/** Parallelism. Default 3. */
|
|
11
|
+
readonly p: number
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface PasswordsConfig {
|
|
15
|
+
readonly scrypt?: Partial<ScryptParams>
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface VerifyResult {
|
|
19
|
+
readonly ok: boolean
|
|
20
|
+
/** True when the password matched but the stored hash used other parameters than the configured ones. */
|
|
21
|
+
readonly needsRehash: boolean
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface Passwords {
|
|
25
|
+
/** Returns a PHC string `$scrypt$ln=15,r=8,p=3$<salt>$<hash>` for the password. */
|
|
26
|
+
hash(password: string): Promise<string>
|
|
27
|
+
/** Checks a password against a stored hash in constant time. A malformed stored hash is `ok: false`. */
|
|
28
|
+
verify(stored: string, password: string): Promise<VerifyResult>
|
|
29
|
+
/** Spends the cost of a verify against a fixed dummy hash, for an account that does not exist. */
|
|
30
|
+
verifyNone(password: string): Promise<{ ok: false }>
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const DEFAULTS: ScryptParams = { ln: 15, r: 8, p: 3 }
|
|
34
|
+
const KEY_LENGTH = 32
|
|
35
|
+
const SALT_LENGTH = 16
|
|
36
|
+
const MAX_PASSWORD_BYTES = 4096
|
|
37
|
+
const STORED_RE = /^\$scrypt\$ln=(\d+),r=(\d+),p=(\d+)\$([A-Za-z0-9+/]+)\$([A-Za-z0-9+/]+)$/
|
|
38
|
+
|
|
39
|
+
function derive(password: string, salt: Buffer, params: ScryptParams, length: number): Promise<Buffer> {
|
|
40
|
+
const N = 2 ** params.ln
|
|
41
|
+
return new Promise((resolve, reject) => {
|
|
42
|
+
scrypt(password, salt, length, { N, r: params.r, p: params.p, maxmem: 256 * N * params.r + (1 << 20) }, (err, key) => {
|
|
43
|
+
if (err !== null) reject(err)
|
|
44
|
+
else resolve(key)
|
|
45
|
+
})
|
|
46
|
+
})
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function normalize(password: string): string {
|
|
50
|
+
const normalized = password.normalize('NFC')
|
|
51
|
+
if (Buffer.byteLength(normalized, 'utf8') > MAX_PASSWORD_BYTES) {
|
|
52
|
+
throw new ValidationError({ password: [`must be at most ${MAX_PASSWORD_BYTES} bytes`] }, 'Password is too long.')
|
|
53
|
+
}
|
|
54
|
+
return normalized
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function validParams(p: ScryptParams): boolean {
|
|
58
|
+
return Number.isInteger(p.ln) && p.ln >= 1 && p.ln <= 22
|
|
59
|
+
&& Number.isInteger(p.r) && p.r >= 1 && p.r <= 32
|
|
60
|
+
&& Number.isInteger(p.p) && p.p >= 1 && p.p <= 16
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Creates the password hasher. Passwords are NFC-normalized and hashed with
|
|
65
|
+
* Node's scrypt (no dependency); there are no composition rules, and anything up
|
|
66
|
+
* to 4096 bytes of UTF-8 is accepted, so long passphrases and pasted passwords
|
|
67
|
+
* work. The hash records its own parameters, so raising `scrypt` later leaves old
|
|
68
|
+
* hashes verifiable and `verify` reports `needsRehash` for them.
|
|
69
|
+
*
|
|
70
|
+
* `verify` never throws on a bad stored string (it returns `ok: false` and logs),
|
|
71
|
+
* and `verifyNone` costs the same as a wrong password, so an unknown account is
|
|
72
|
+
* not distinguishable by timing.
|
|
73
|
+
*/
|
|
74
|
+
export function createPasswords(config: PasswordsConfig = {}): Passwords {
|
|
75
|
+
const params: ScryptParams = { ...DEFAULTS, ...config.scrypt }
|
|
76
|
+
if (!validParams(params)) {
|
|
77
|
+
throw new ValidationError('auth.passwords scrypt params must satisfy 1<=ln<=22, 1<=r<=32, 1<=p<=16.')
|
|
78
|
+
}
|
|
79
|
+
const encode = (b: Buffer): string => b.toString('base64').replace(/=+$/, '')
|
|
80
|
+
let dummy: Promise<string> | undefined
|
|
81
|
+
|
|
82
|
+
async function hash(password: string): Promise<string> {
|
|
83
|
+
const salt = randomBytes(SALT_LENGTH)
|
|
84
|
+
const key = await derive(normalize(password), salt, params, KEY_LENGTH)
|
|
85
|
+
return `$scrypt$ln=${params.ln},r=${params.r},p=${params.p}$${encode(salt)}$${encode(key)}`
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async function verify(stored: string, password: string): Promise<VerifyResult> {
|
|
89
|
+
const m = STORED_RE.exec(stored)
|
|
90
|
+
const storedParams = m === null ? null : { ln: Number(m[1]), r: Number(m[2]), p: Number(m[3]) }
|
|
91
|
+
if (m === null || storedParams === null || !validParams(storedParams)) {
|
|
92
|
+
getBaseLogger().error({ event: 'password hash malformed' }, 'stored password hash is malformed')
|
|
93
|
+
return { ok: false, needsRehash: false }
|
|
94
|
+
}
|
|
95
|
+
const salt = Buffer.from(m[4]!, 'base64')
|
|
96
|
+
const expected = Buffer.from(m[5]!, 'base64')
|
|
97
|
+
const key = await derive(normalize(password), salt, storedParams, expected.length)
|
|
98
|
+
const ok = key.length === expected.length && timingSafeEqual(key, expected)
|
|
99
|
+
const needsRehash = ok && (storedParams.ln !== params.ln || storedParams.r !== params.r || storedParams.p !== params.p)
|
|
100
|
+
return { ok, needsRehash }
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async function verifyNone(password: string): Promise<{ ok: false }> {
|
|
104
|
+
dummy ??= hash(randomBytes(16).toString('hex'))
|
|
105
|
+
await verify(await dummy, password)
|
|
106
|
+
return { ok: false }
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return { hash, verify, verifyNone }
|
|
110
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
// Definitions for the tables behind `auth.createSessions` and
|
|
2
|
+
// `auth.createMultiOrg`. Enabled with `sessions: {}` / `orgs: {}` in the
|
|
3
|
+
// blueprint; `pipework generate` then emits them into the default database's
|
|
4
|
+
// migrations. Emitted as IF NOT EXISTS so apps that created these tables by
|
|
5
|
+
// hand from the former .sql files migrate cleanly.
|
|
6
|
+
//
|
|
7
|
+
// `user_id` is text everywhere: its format is the app's. `tenant_id` and
|
|
8
|
+
// `org_id` are uuid so they can be the tenant id, but deliberately NOT
|
|
9
|
+
// `.tenant()`: a refresh token is looked up by its hash before any tenant is
|
|
10
|
+
// known, and a user's org list is read across orgs at sign-in, so tenant RLS on
|
|
11
|
+
// either table would hide the rows the lookup needs.
|
|
12
|
+
//
|
|
13
|
+
// A custom `createMultiOrg({ table })` name is outside these definitions: the
|
|
14
|
+
// app owns that table's DDL.
|
|
15
|
+
|
|
16
|
+
import { define } from '../../data/domain/index.js'
|
|
17
|
+
import { field } from '../../data/domain/field.js'
|
|
18
|
+
import { sql } from '../../data/db/sql.js'
|
|
19
|
+
import { index, unique } from '../../data/db/idx/index.js'
|
|
20
|
+
|
|
21
|
+
export const AUTH_TABLE_NAMES: ReadonlySet<string> = new Set([
|
|
22
|
+
'pipework_refresh_tokens', 'pipework_auth_versions', 'pipework_user_orgs', 'pipework_signin_attempts', 'pipework_guard_attempts',
|
|
23
|
+
'pipework_email_links', 'pipework_email_link_requests',
|
|
24
|
+
])
|
|
25
|
+
|
|
26
|
+
export const refreshTokens = define('pipework_refresh_tokens', {
|
|
27
|
+
id: field.uuid().defaultRandom().primaryKey(),
|
|
28
|
+
userId: field.text(),
|
|
29
|
+
tenantId: field.uuid(),
|
|
30
|
+
tokenHash: field.text(),
|
|
31
|
+
familyId: field.uuid(),
|
|
32
|
+
revokedAt: field.timestamp().nullable(),
|
|
33
|
+
createdAt: field.timestamp().default('now'),
|
|
34
|
+
expiresAt: field.timestamp(),
|
|
35
|
+
}, {
|
|
36
|
+
indexes: (t) => ({
|
|
37
|
+
byHash: index('idx_pipework_refresh_tokens_hash').on(t['tokenHash']),
|
|
38
|
+
byUser: index('idx_pipework_refresh_tokens_user').on(t['userId']).where(sql`revoked_at IS NULL`),
|
|
39
|
+
byFamily: index('idx_pipework_refresh_tokens_family').on(t['familyId']),
|
|
40
|
+
}),
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
export const authVersions = define('pipework_auth_versions', {
|
|
44
|
+
userId: field.text().primaryKey(),
|
|
45
|
+
version: field.integer().default(1),
|
|
46
|
+
updatedAt: field.timestamp().default('now'),
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
export const userOrgs = define('pipework_user_orgs', {
|
|
50
|
+
id: field.uuid().defaultRandom().primaryKey(),
|
|
51
|
+
userId: field.text(),
|
|
52
|
+
orgId: field.uuid(),
|
|
53
|
+
role: field.text().nullable(),
|
|
54
|
+
joinedAt: field.timestamp().default('now'),
|
|
55
|
+
addedBy: field.text().nullable(),
|
|
56
|
+
}, {
|
|
57
|
+
indexes: (t) => ({
|
|
58
|
+
membership: unique('pipework_user_orgs_user_id_org_id_key').on(t['userId'], t['orgId']),
|
|
59
|
+
byUser: index('idx_pipework_user_orgs_user').on(t['userId']),
|
|
60
|
+
byOrg: index('idx_pipework_user_orgs_org').on(t['orgId']),
|
|
61
|
+
}),
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
// Behind `auth.signIn`. Not tenant-scoped: a sign-in happens before any tenant
|
|
65
|
+
// is known. `key` is 'account:<sha256 of the account>' or 'client:<address>'.
|
|
66
|
+
export const signinAttempts = define('pipework_signin_attempts', {
|
|
67
|
+
key: field.text().primaryKey(),
|
|
68
|
+
failures: field.integer().default(0),
|
|
69
|
+
firstFailureAt: field.timestamp().nullable(),
|
|
70
|
+
lastFailureAt: field.timestamp().nullable(),
|
|
71
|
+
lockedUntil: field.timestamp().nullable(),
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
// Behind `auth.guard`. Same shape as signinAttempts; `key` is
|
|
75
|
+
// '<guard name>:<sha256 of the sorted key entries>'. Not tenant-scoped.
|
|
76
|
+
export const guardAttempts = define('pipework_guard_attempts', {
|
|
77
|
+
key: field.text().primaryKey(),
|
|
78
|
+
failures: field.integer().default(0),
|
|
79
|
+
firstFailureAt: field.timestamp().nullable(),
|
|
80
|
+
lastFailureAt: field.timestamp().nullable(),
|
|
81
|
+
lockedUntil: field.timestamp().nullable(),
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
// Behind `auth.emailLinks`. Not tenant-scoped: a reset is asked for before any
|
|
85
|
+
// tenant is known. The link's token is never stored: `token_hash` is its sha256,
|
|
86
|
+
// and the token itself is an HMAC (keyed by the app's secret) of `id` and `nonce`.
|
|
87
|
+
export const emailLinks = define('pipework_email_links', {
|
|
88
|
+
id: field.uuid().primaryKey(),
|
|
89
|
+
purpose: field.text(),
|
|
90
|
+
userId: field.text(),
|
|
91
|
+
address: field.text(),
|
|
92
|
+
tokenHash: field.text(),
|
|
93
|
+
nonce: field.text(),
|
|
94
|
+
createdAt: field.timestamp().default('now'),
|
|
95
|
+
expiresAt: field.timestamp(),
|
|
96
|
+
usedAt: field.timestamp().nullable(),
|
|
97
|
+
endedAt: field.timestamp().nullable(),
|
|
98
|
+
}, {
|
|
99
|
+
indexes: (t) => ({
|
|
100
|
+
byHash: unique('pipework_email_links_token_hash_key').on(t['tokenHash']),
|
|
101
|
+
byUser: index('idx_pipework_email_links_user').on(t['userId'], t['purpose']),
|
|
102
|
+
}),
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
// Request counts behind `auth.emailLinks`; `key` is 'address:<sha256 of the address>' or 'client:<address>'.
|
|
106
|
+
export const emailLinkRequests = define('pipework_email_link_requests', {
|
|
107
|
+
key: field.text().primaryKey(),
|
|
108
|
+
count: field.integer().default(0),
|
|
109
|
+
windowStart: field.timestamp().default('now'),
|
|
110
|
+
})
|