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,97 @@
|
|
|
1
|
+
import { existsSync, readdirSync } from 'node:fs'
|
|
2
|
+
import { join, relative, resolve, sep } from 'node:path'
|
|
3
|
+
import { discoverWorkspacePackages } from '../workspace/discover.js'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* The file globs a module lints, relative to the module directory.
|
|
7
|
+
*
|
|
8
|
+
* `configured` — `lint.modules.<path>.include`, else `lint.include` — replaces the
|
|
9
|
+
* default outright: a module whose sources are not under `src` says so once. The
|
|
10
|
+
* default covers `src` plus the `src` of each workspace package under `dir`, so a
|
|
11
|
+
* nested workspace is not left matching nothing (#393).
|
|
12
|
+
*/
|
|
13
|
+
export function lintIncludeGlobs(dir: string, configured?: readonly string[]): string[] {
|
|
14
|
+
if (configured !== undefined && configured.length > 0) return [...configured]
|
|
15
|
+
|
|
16
|
+
const globs = ['src/**/*.ts', 'src/**/*.tsx']
|
|
17
|
+
for (const pkg of discoverWorkspacePackages(dir)) {
|
|
18
|
+
globs.push(pkg + '/src/**/*.ts', pkg + '/src/**/*.tsx')
|
|
19
|
+
}
|
|
20
|
+
return globs
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* The longest directory prefix of `glob` that contains no wildcard.
|
|
25
|
+
*
|
|
26
|
+
* `app/src/**\/*.ts` → `app/src`; `**\/*.ts` → `''` (the module root). That is the
|
|
27
|
+
* directory eslint is pointed at and the directory walked to find files the run
|
|
28
|
+
* should have covered.
|
|
29
|
+
*/
|
|
30
|
+
export function globRoot(glob: string): string {
|
|
31
|
+
const parts = glob.split('/')
|
|
32
|
+
const wildcard = parts.findIndex(p => p.includes('*') || p.includes('?'))
|
|
33
|
+
return (wildcard === -1 ? parts.slice(0, -1) : parts.slice(0, wildcard)).join('/')
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Match a path (relative, `/`-separated) against one glob.
|
|
38
|
+
*
|
|
39
|
+
* `**` crosses directory separators, `*` and `?` do not. Enough for the `files`
|
|
40
|
+
* patterns eslint itself accepts; anything richer belongs in eslint's config, not here.
|
|
41
|
+
*/
|
|
42
|
+
export function matchesGlob(path: string, glob: string): boolean {
|
|
43
|
+
return globToRegExp(glob).test(path)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function globToRegExp(glob: string): RegExp {
|
|
47
|
+
let out = '^'
|
|
48
|
+
for (let i = 0; i < glob.length; i++) {
|
|
49
|
+
const ch = glob[i]!
|
|
50
|
+
if (ch === '*') {
|
|
51
|
+
if (glob[i + 1] === '*') {
|
|
52
|
+
// `**/` may match zero segments, so the separator is part of the optional group.
|
|
53
|
+
i++
|
|
54
|
+
if (glob[i + 1] === '/') i++
|
|
55
|
+
out += '(?:[^/]*(?:/|$))*'
|
|
56
|
+
} else {
|
|
57
|
+
out += '[^/]*'
|
|
58
|
+
}
|
|
59
|
+
} else if (ch === '?') {
|
|
60
|
+
out += '[^/]'
|
|
61
|
+
} else {
|
|
62
|
+
out += ch.replace(/[.+^${}()|[\]\\]/g, '\\$&')
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return new RegExp(out + '$')
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Every file under `dir` that the module's globs say should be linted.
|
|
70
|
+
*
|
|
71
|
+
* Walked from each glob's static root so the cost tracks the configured scope, not the
|
|
72
|
+
* whole module. `node_modules` and `dist` are never descended — eslint ignores both.
|
|
73
|
+
*/
|
|
74
|
+
export function filesMatchingGlobs(dir: string, globs: readonly string[]): string[] {
|
|
75
|
+
const found = new Set<string>()
|
|
76
|
+
for (const glob of globs) {
|
|
77
|
+
const root = globRoot(glob)
|
|
78
|
+
const rootDir = root === '' ? dir : join(dir, root)
|
|
79
|
+
if (!existsSync(rootDir)) continue
|
|
80
|
+
for (const file of walk(rootDir, dir)) {
|
|
81
|
+
if (matchesGlob(file, glob)) found.add(file)
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return [...found].sort()
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function walk(dir: string, base: string, out: string[] = []): string[] {
|
|
88
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
89
|
+
if (entry.isDirectory()) {
|
|
90
|
+
if (entry.name === 'node_modules' || entry.name === 'dist') continue
|
|
91
|
+
walk(join(dir, entry.name), base, out)
|
|
92
|
+
} else {
|
|
93
|
+
out.push(relative(resolve(base), resolve(dir, entry.name)).split(sep).join('/'))
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return out
|
|
97
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { resolveLintConfig } from './resolver.js'
|
|
2
|
+
|
|
3
|
+
// `pipework check` lints each module directory with `--config <this file>` (see
|
|
4
|
+
// lintDirectory in src/cli/commands/check.ts). Resolving here — rather than a
|
|
5
|
+
// static createLintConfig() — makes that per-module pass honour pipework.config.ts:
|
|
6
|
+
// `lint.extra` applies everywhere, and `lint.modules` applies to the module being
|
|
7
|
+
// linted (resolveLintConfig keys off the eslint process cwd, i.e. the module dir).
|
|
8
|
+
// With `--config`, eslint's base path IS the cwd, so cwd is also the right root for
|
|
9
|
+
// the file globs here — unlike the generated eslint.config.js, which passes its own dir.
|
|
10
|
+
export default await resolveLintConfig()
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import { dirname, join, relative, sep } from 'node:path'
|
|
2
|
+
import type { Manifold } from '../core/pipework.js'
|
|
3
|
+
import type { LintModuleConfig } from '../core/config/schema.js'
|
|
4
|
+
import { findConfig, registerTsResolveHooks } from '../core/config/discover.js'
|
|
5
|
+
import { importConfig } from '../core/config/import-config.js'
|
|
6
|
+
import { loadEnvFiles } from '../core/config/env.js'
|
|
7
|
+
import { expandGlob } from '../workspace/discover.js'
|
|
8
|
+
import { lintIncludeGlobs } from './include.js'
|
|
9
|
+
import { createLintConfig, type LintOptions } from './config.js'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Build the full eslint flat-config array from pipework.config.ts.
|
|
13
|
+
*
|
|
14
|
+
* The generated `eslint.config.js` delegates here, passing its own directory as `cwd` —
|
|
15
|
+
* eslint resolves flat-config globs against that directory, not the process cwd.
|
|
16
|
+
* Everything that would normally live in `eslint.config.js` — extra plugins, rules, additional
|
|
17
|
+
* flat-config blocks — flows through `pipework.config.ts` so pipework remains the single source
|
|
18
|
+
* of truth and the generated file stays byte-stable for `pipework check`.
|
|
19
|
+
*
|
|
20
|
+
* Shape:
|
|
21
|
+
* - `lint.extra.plugins` → registered in pipework's main lint block; rules under
|
|
22
|
+
* `lint.extra.rules` (or `pipework`'s built-in rules) can reference them as `name/rule`.
|
|
23
|
+
* - `lint.extra.rules` → merged into pipework's main block (can override defaults).
|
|
24
|
+
* - `lint.extra.configs` → appended as additional flat-config entries after pipework's block.
|
|
25
|
+
* - `lint.include` (or a module's own `include`) → the block's `files`. Unset, the globs
|
|
26
|
+
* are derived from the workspace rooted at `base`: a glob for `src` plus one per
|
|
27
|
+
* workspace package under it, nested workspaces included. Without either, a package
|
|
28
|
+
* whose sources are not under `base/src` matches no block, eslint reports "no matching
|
|
29
|
+
* configuration", and `pipework check` still prints Lint ✓ (#392, #393).
|
|
30
|
+
* - `lint.modules` → per-module overrides, applied per file whatever the directory eslint
|
|
31
|
+
* runs from. When `cwd` resolves inside a declared module (the per-module `pipework check`
|
|
32
|
+
* pass, `pipework lint`), that module's `rules` and `configs` are layered on top of
|
|
33
|
+
* `lint.extra`. For a module below `cwd` (the generated root `eslint.config.js`), its
|
|
34
|
+
* `rules` and `configs` become config entries whose `files` and `ignores` are prefixed
|
|
35
|
+
* with the module's path, so plain `eslint <file>` agrees with `pipework check`.
|
|
36
|
+
*/
|
|
37
|
+
export async function resolveLintConfig(cwd?: string): Promise<unknown[]> {
|
|
38
|
+
const start = cwd ?? process.cwd()
|
|
39
|
+
const configPath = findConfig(start)
|
|
40
|
+
|
|
41
|
+
if (configPath === null) {
|
|
42
|
+
return createLintConfig({ include: lintIncludeGlobs(start) })
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const configDir = dirname(configPath)
|
|
46
|
+
loadEnvFiles(configDir)
|
|
47
|
+
registerTsResolveHooks()
|
|
48
|
+
|
|
49
|
+
let manifold: Manifold
|
|
50
|
+
try {
|
|
51
|
+
manifold = await importConfig(configPath)
|
|
52
|
+
} catch (err: unknown) {
|
|
53
|
+
const message = err instanceof Error ? err.message : String(err)
|
|
54
|
+
throw new Error(
|
|
55
|
+
`[pipework] Failed to load pipework.config.ts for eslint config resolution.\n\n` +
|
|
56
|
+
` ${message}\n\n` +
|
|
57
|
+
' The eslint config resolver imports your config to read `lint.extra`.\n' +
|
|
58
|
+
' It does not connect to any database.\n',
|
|
59
|
+
)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const lint = manifold.config.raw.lint
|
|
63
|
+
if (lint === undefined) {
|
|
64
|
+
return createLintConfig({ include: lintIncludeGlobs(start) })
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const extra = lint.extra
|
|
68
|
+
const moduleOverride = resolveModuleOverride(lint.modules, configDir, start)
|
|
69
|
+
|
|
70
|
+
const extraRules = { ...extra?.rules, ...moduleOverride.rules }
|
|
71
|
+
const extraConfigs = [...(extra?.configs ?? []), ...moduleOverride.configs]
|
|
72
|
+
|
|
73
|
+
const below = resolveBelowOverrides(lint, configDir, start)
|
|
74
|
+
|
|
75
|
+
const options: LintOptions = {
|
|
76
|
+
include: unique([...lintIncludeGlobs(start, moduleOverride.include ?? lint.include), ...below.include]),
|
|
77
|
+
...(extra?.plugins !== undefined ? { extraPlugins: extra.plugins } : {}),
|
|
78
|
+
...(Object.keys(extraRules).length > 0 ? { extraRules } : {}),
|
|
79
|
+
...(extraConfigs.length > 0 || below.configs.length > 0 ? { extraConfigs: [...extraConfigs, ...below.configs] } : {}),
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return createLintConfig(options)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function unique(globs: readonly string[]): string[] {
|
|
86
|
+
return [...new Set(globs)]
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Every `lint.modules` entry as `{ path, config }`, glob keys expanded the same way as the
|
|
91
|
+
* top-level `modules` map. Glob keys come first so that when a glob expands to the same
|
|
92
|
+
* path as an explicit key, the explicit entry lands later; the callers' sort by path length
|
|
93
|
+
* is stable, so the explicit entry stays last and wins — an explicit module entry refines a
|
|
94
|
+
* glob match regardless of declaration order.
|
|
95
|
+
*/
|
|
96
|
+
function moduleEntries(
|
|
97
|
+
modules: Record<string, LintModuleConfig> | undefined,
|
|
98
|
+
configDir: string,
|
|
99
|
+
): Array<{ path: string; config: LintModuleConfig }> {
|
|
100
|
+
const entries: Array<{ path: string; config: LintModuleConfig }> = []
|
|
101
|
+
if (modules === undefined) return entries
|
|
102
|
+
for (const [key, config] of Object.entries(modules)) {
|
|
103
|
+
if (key.includes('*')) {
|
|
104
|
+
for (const path of expandGlob(key, configDir)) entries.push({ path, config })
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
for (const [key, config] of Object.entries(modules)) {
|
|
108
|
+
if (!key.includes('*')) entries.push({ path: key, config })
|
|
109
|
+
}
|
|
110
|
+
return entries
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function prefixGlob(prefix: string, glob: string): string {
|
|
114
|
+
const negated = glob.startsWith('!')
|
|
115
|
+
const bare = (negated ? glob.slice(1) : glob).replace(/^\.\//, '')
|
|
116
|
+
return (negated ? '!' : '') + prefix + '/' + bare
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// A flat-config `files` item is a glob or an array of globs that must all match.
|
|
120
|
+
function prefixFiles(prefix: string, files: unknown): unknown {
|
|
121
|
+
if (typeof files === 'string') return prefixGlob(prefix, files)
|
|
122
|
+
if (Array.isArray(files)) return files.map(f => prefixFiles(prefix, f))
|
|
123
|
+
return files
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Re-root one module-relative flat-config entry at `prefix`. An entry with no `files`
|
|
128
|
+
* applied to the whole module, so it takes the module's include globs; an entry with only
|
|
129
|
+
* `ignores` is a global ignore and just has its patterns prefixed.
|
|
130
|
+
*/
|
|
131
|
+
function prefixConfig(prefix: string, config: unknown, moduleFiles: readonly string[]): unknown {
|
|
132
|
+
if (typeof config !== 'object' || config === null || Array.isArray(config)) return config
|
|
133
|
+
const entry = { ...(config as Record<string, unknown>) }
|
|
134
|
+
const ignoresOnly = entry['files'] === undefined && entry['ignores'] !== undefined &&
|
|
135
|
+
Object.keys(entry).every(k => k === 'ignores' || k === 'name')
|
|
136
|
+
if (entry['files'] !== undefined) {
|
|
137
|
+
entry['files'] = prefixFiles(prefix, entry['files'])
|
|
138
|
+
} else if (!ignoresOnly) {
|
|
139
|
+
entry['files'] = moduleFiles.map(g => prefixGlob(prefix, g))
|
|
140
|
+
}
|
|
141
|
+
if (Array.isArray(entry['ignores'])) {
|
|
142
|
+
entry['ignores'] = entry['ignores'].map((g: unknown) => typeof g === 'string' ? prefixGlob(prefix, g) : g)
|
|
143
|
+
}
|
|
144
|
+
return entry
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Overrides of modules strictly below `start`, as config entries scoped to each module's
|
|
149
|
+
* files, plus the module's include globs re-rooted at `start` so the main block reaches them.
|
|
150
|
+
*/
|
|
151
|
+
function resolveBelowOverrides(
|
|
152
|
+
lint: { include?: readonly string[] | undefined; modules?: Record<string, LintModuleConfig> | undefined },
|
|
153
|
+
configDir: string,
|
|
154
|
+
start: string,
|
|
155
|
+
): { configs: unknown[]; include: string[] } {
|
|
156
|
+
const configs: unknown[] = []
|
|
157
|
+
const include: string[] = []
|
|
158
|
+
const below = moduleEntries(lint.modules, configDir)
|
|
159
|
+
.map(e => ({ ...e, prefix: relative(start, join(configDir, e.path)).split(sep).join('/') }))
|
|
160
|
+
.filter(e => e.prefix !== '' && e.prefix !== '..' && !e.prefix.startsWith('../') && !e.prefix.startsWith('/'))
|
|
161
|
+
.sort((a, b) => a.prefix.length - b.prefix.length)
|
|
162
|
+
|
|
163
|
+
for (const { path, config, prefix } of below) {
|
|
164
|
+
const moduleFiles = lintIncludeGlobs(join(configDir, path), config.include ?? lint.include)
|
|
165
|
+
include.push(...moduleFiles.map(g => prefixGlob(prefix, g)))
|
|
166
|
+
if (config.rules !== undefined && Object.keys(config.rules).length > 0) {
|
|
167
|
+
configs.push({ files: moduleFiles.map(g => prefixGlob(prefix, g)), rules: config.rules })
|
|
168
|
+
}
|
|
169
|
+
for (const c of config.configs ?? []) configs.push(prefixConfig(prefix, c, moduleFiles))
|
|
170
|
+
}
|
|
171
|
+
return { configs, include }
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Collect the `lint.modules` rules and configs that apply to `start`.
|
|
176
|
+
*
|
|
177
|
+
* `start` is the directory eslint runs in — a module directory for a per-module
|
|
178
|
+
* `pipework check` pass, or the repo root for the generated root `eslint.config.js`
|
|
179
|
+
* (where modules below it are handled by `resolveBelowOverrides`).
|
|
180
|
+
* An override applies when its module path (glob keys expanded the same way as the
|
|
181
|
+
* top-level `modules` map) contains `start`. Least-specific module first, so a deeper
|
|
182
|
+
* module's rules win, and a glob match is refined by an explicit module entry.
|
|
183
|
+
*/
|
|
184
|
+
function resolveModuleOverride(
|
|
185
|
+
modules: Record<string, LintModuleConfig> | undefined,
|
|
186
|
+
configDir: string,
|
|
187
|
+
start: string,
|
|
188
|
+
): { rules: Record<string, unknown>; configs: readonly unknown[]; include?: readonly string[] } {
|
|
189
|
+
if (modules === undefined) {
|
|
190
|
+
return { rules: {}, configs: [] }
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// `findConfig` only walks upward, so `configDir` is always an ancestor of
|
|
194
|
+
// `start`; an empty `rel` means `start` *is* the repo root (the generated
|
|
195
|
+
// root eslint.config.js) — no module override applies there.
|
|
196
|
+
const rel = relative(configDir, start)
|
|
197
|
+
if (rel === '') {
|
|
198
|
+
return { rules: {}, configs: [] }
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const entries = moduleEntries(modules, configDir)
|
|
202
|
+
|
|
203
|
+
const matching = entries
|
|
204
|
+
.filter(e => rel === e.path || rel.startsWith(e.path + '/'))
|
|
205
|
+
.sort((a, b) => a.path.length - b.path.length)
|
|
206
|
+
|
|
207
|
+
let rules: Record<string, unknown> = {}
|
|
208
|
+
let configs: readonly unknown[] = []
|
|
209
|
+
let include: readonly string[] | undefined
|
|
210
|
+
for (const { config } of matching) {
|
|
211
|
+
rules = { ...rules, ...config.rules }
|
|
212
|
+
configs = [...configs, ...(config.configs ?? [])]
|
|
213
|
+
// Replaced, not merged: `include` names the module's sources outright, so the
|
|
214
|
+
// deepest module that declares it wins rather than accumulating a union.
|
|
215
|
+
if (config.include !== undefined) include = config.include
|
|
216
|
+
}
|
|
217
|
+
return { rules, configs, ...(include !== undefined ? { include } : {}) }
|
|
218
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { Rule } from 'eslint'
|
|
2
|
+
|
|
3
|
+
function isTypeOnly(node: Record<string, unknown>): boolean {
|
|
4
|
+
return (node as Record<string, unknown>)['exportKind'] === 'type'
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export const noReexports: Rule.RuleModule = {
|
|
8
|
+
meta: {
|
|
9
|
+
type: 'problem',
|
|
10
|
+
docs: {
|
|
11
|
+
description: 'Disallow re-exporting from other modules. Each module should export its own declarations.',
|
|
12
|
+
},
|
|
13
|
+
messages: {
|
|
14
|
+
noReexport:
|
|
15
|
+
'Do not re-export from "{{source}}". Import into the consuming module directly.\n' +
|
|
16
|
+
'Re-exports create hidden coupling and make it harder to trace where a symbol is defined.',
|
|
17
|
+
noBarrelExport:
|
|
18
|
+
'Do not use `export * from "{{source}}"`. Barrel files obscure the dependency graph.\n' +
|
|
19
|
+
'Import from the source module directly.',
|
|
20
|
+
},
|
|
21
|
+
schema: [],
|
|
22
|
+
},
|
|
23
|
+
|
|
24
|
+
create(context) {
|
|
25
|
+
return {
|
|
26
|
+
ExportAllDeclaration(node) {
|
|
27
|
+
if (isTypeOnly(node as unknown as Record<string, unknown>)) return
|
|
28
|
+
context.report({
|
|
29
|
+
node,
|
|
30
|
+
messageId: 'noBarrelExport',
|
|
31
|
+
data: { source: String(node.source.value) },
|
|
32
|
+
})
|
|
33
|
+
},
|
|
34
|
+
|
|
35
|
+
ExportNamedDeclaration(node) {
|
|
36
|
+
if (node.source === null || node.source === undefined) return
|
|
37
|
+
if (isTypeOnly(node as unknown as Record<string, unknown>)) return
|
|
38
|
+
context.report({
|
|
39
|
+
node,
|
|
40
|
+
messageId: 'noReexport',
|
|
41
|
+
data: { source: String(node.source.value) },
|
|
42
|
+
})
|
|
43
|
+
},
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import type { DB } from '../../data/db/index.js'
|
|
2
|
+
import type { ResourceOperations, ResourceId } from '../fixture/types.js'
|
|
3
|
+
import type { AuditedBehaviorConfig } from './types.js'
|
|
4
|
+
import type { Audit, AuditEmitOptions } from '../../infra/audit/types.js'
|
|
5
|
+
import { singularize } from '../internal/text.js'
|
|
6
|
+
|
|
7
|
+
export function applyAudited<TAuth>(
|
|
8
|
+
ops: ResourceOperations<TAuth>,
|
|
9
|
+
config: AuditedBehaviorConfig | Audit,
|
|
10
|
+
resourceName: string,
|
|
11
|
+
): ResourceOperations<TAuth> {
|
|
12
|
+
const audit: Audit = 'emit' in config ? config : config.audit
|
|
13
|
+
const isConfig = !('emit' in config)
|
|
14
|
+
const entityType = isConfig && (config as AuditedBehaviorConfig).entityType !== undefined
|
|
15
|
+
? (config as AuditedBehaviorConfig).entityType!
|
|
16
|
+
: singularize(resourceName)
|
|
17
|
+
const prefix = isConfig && (config as AuditedBehaviorConfig).actionPrefix !== undefined
|
|
18
|
+
? (config as AuditedBehaviorConfig).actionPrefix!
|
|
19
|
+
: entityType
|
|
20
|
+
|
|
21
|
+
const result: Record<string, unknown> = { ...ops }
|
|
22
|
+
|
|
23
|
+
if (ops.create !== undefined) {
|
|
24
|
+
const original = ops.create
|
|
25
|
+
result['create'] = {
|
|
26
|
+
input: original.input,
|
|
27
|
+
handler: async (db: DB, auth: TAuth, tenant: string, input: unknown) => {
|
|
28
|
+
const created = await original.handler(db, auth, tenant, input)
|
|
29
|
+
const opts: AuditEmitOptions = {
|
|
30
|
+
actionType: `${prefix}.created`,
|
|
31
|
+
entityType,
|
|
32
|
+
entityId: extractEntityId(created),
|
|
33
|
+
after: created,
|
|
34
|
+
}
|
|
35
|
+
await audit.emit(opts)
|
|
36
|
+
return created
|
|
37
|
+
},
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (ops.update !== undefined) {
|
|
42
|
+
const original = ops.update
|
|
43
|
+
result['update'] = {
|
|
44
|
+
input: original.input,
|
|
45
|
+
handler: async (db: DB, auth: TAuth, tenant: string, id: ResourceId, input: unknown) => {
|
|
46
|
+
const before = ops.get !== undefined ? await ops.get(db, auth, tenant, id) : null
|
|
47
|
+
const updated = await original.handler(db, auth, tenant, id, input)
|
|
48
|
+
const opts: AuditEmitOptions = {
|
|
49
|
+
actionType: `${prefix}.updated`,
|
|
50
|
+
entityType,
|
|
51
|
+
entityId: id,
|
|
52
|
+
before: before ?? undefined,
|
|
53
|
+
after: updated,
|
|
54
|
+
}
|
|
55
|
+
await audit.emit(opts)
|
|
56
|
+
return updated
|
|
57
|
+
},
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (ops.delete !== undefined) {
|
|
62
|
+
const original = ops.delete
|
|
63
|
+
result['delete'] = async (db: DB, auth: TAuth, tenant: string, id: ResourceId) => {
|
|
64
|
+
const before = ops.get !== undefined ? await ops.get(db, auth, tenant, id) : null
|
|
65
|
+
await original(db, auth, tenant, id)
|
|
66
|
+
const opts: AuditEmitOptions = {
|
|
67
|
+
actionType: `${prefix}.deleted`,
|
|
68
|
+
entityType,
|
|
69
|
+
entityId: id,
|
|
70
|
+
before: before ?? undefined,
|
|
71
|
+
}
|
|
72
|
+
await audit.emit(opts)
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return result as ResourceOperations<TAuth>
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function extractEntityId(result: unknown): string {
|
|
80
|
+
if (result !== null && typeof result === 'object' && 'id' in result) {
|
|
81
|
+
return String((result as { id: unknown }).id)
|
|
82
|
+
}
|
|
83
|
+
return 'unknown'
|
|
84
|
+
}
|
|
85
|
+
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { DB } from '../../data/db/index.js'
|
|
2
|
+
import type { ResourceOperations, ResourceId } from '../fixture/types.js'
|
|
3
|
+
import type { InvalidationBehaviorConfig } from './types.js'
|
|
4
|
+
import { createCache } from '../../infra/cache/create.js'
|
|
5
|
+
import type { Cache } from '../../infra/cache/types.js'
|
|
6
|
+
|
|
7
|
+
export function withInvalidation<TAuth>(
|
|
8
|
+
ops: ResourceOperations<TAuth>,
|
|
9
|
+
config: InvalidationBehaviorConfig,
|
|
10
|
+
resourceName: string,
|
|
11
|
+
): ResourceOperations<TAuth> & { readonly cache: Cache<unknown[]> } {
|
|
12
|
+
const listCache = createCache<unknown[]>({
|
|
13
|
+
name: `resource:${resourceName}:list`,
|
|
14
|
+
database: config.database,
|
|
15
|
+
ttlMs: config.ttlMs,
|
|
16
|
+
load: () => Promise.resolve([]),
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
const result: Record<string, unknown> = { ...ops, cache: listCache }
|
|
20
|
+
|
|
21
|
+
if (ops.list !== undefined) {
|
|
22
|
+
const original = ops.list
|
|
23
|
+
result['list'] = async (db: DB, auth: TAuth, tenant: string) => {
|
|
24
|
+
const data = await original(db, auth, tenant)
|
|
25
|
+
return data
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (ops.create !== undefined) {
|
|
30
|
+
const original = ops.create
|
|
31
|
+
result['create'] = {
|
|
32
|
+
input: original.input,
|
|
33
|
+
handler: async (db: DB, auth: TAuth, tenant: string, input: unknown) => {
|
|
34
|
+
const created = await original.handler(db, auth, tenant, input)
|
|
35
|
+
listCache.invalidate()
|
|
36
|
+
return created
|
|
37
|
+
},
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (ops.update !== undefined) {
|
|
42
|
+
const original = ops.update
|
|
43
|
+
result['update'] = {
|
|
44
|
+
input: original.input,
|
|
45
|
+
handler: async (db: DB, auth: TAuth, tenant: string, id: ResourceId, input: unknown) => {
|
|
46
|
+
const updated = await original.handler(db, auth, tenant, id, input)
|
|
47
|
+
listCache.invalidate()
|
|
48
|
+
return updated
|
|
49
|
+
},
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (ops.delete !== undefined) {
|
|
54
|
+
const original = ops.delete
|
|
55
|
+
result['delete'] = async (db: DB, _auth: TAuth, _tenant: string, id: ResourceId) => {
|
|
56
|
+
await original(db, _auth, _tenant, id)
|
|
57
|
+
listCache.invalidate()
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return result as unknown as ResourceOperations<TAuth> & { readonly cache: Cache<unknown[]> }
|
|
62
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { ResourceOperations } from '../fixture/types.js'
|
|
2
|
+
import type { BehaviorConfig } from './types.js'
|
|
3
|
+
import { applyVersioned } from './versioned.js'
|
|
4
|
+
import { applyAudited } from './audited.js'
|
|
5
|
+
import { withInvalidation } from './cached.js'
|
|
6
|
+
|
|
7
|
+
export function withBehaviors<TAuth>(
|
|
8
|
+
ops: ResourceOperations<TAuth>,
|
|
9
|
+
config: BehaviorConfig,
|
|
10
|
+
resourceName?: string,
|
|
11
|
+
): ResourceOperations<TAuth> {
|
|
12
|
+
const name = resourceName ?? 'resource'
|
|
13
|
+
let decorated = ops
|
|
14
|
+
|
|
15
|
+
if (config.versioned !== undefined) {
|
|
16
|
+
decorated = applyVersioned(decorated, config.versioned, name)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (config.audited !== undefined) {
|
|
20
|
+
decorated = applyAudited(decorated, config.audited, name)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (config.cached !== undefined) {
|
|
24
|
+
decorated = withInvalidation(decorated, config.cached, name)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return decorated
|
|
28
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { withBehaviors } from './compose.js'
|
|
2
|
+
export { applyVersioned } from './versioned.js'
|
|
3
|
+
export { applyAudited } from './audited.js'
|
|
4
|
+
export { withInvalidation } from './cached.js'
|
|
5
|
+
export type { BehaviorConfig, VersionedBehaviorConfig, AuditedBehaviorConfig, InvalidationBehaviorConfig } from './types.js'
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { withBehaviors } from './compose.js'
|
|
2
|
+
import { applyVersioned } from './versioned.js'
|
|
3
|
+
import { applyAudited } from './audited.js'
|
|
4
|
+
import { withInvalidation } from './cached.js'
|
|
5
|
+
|
|
6
|
+
/** Resource behavior decorators — compose versioning, audit trails, and cache invalidation onto CRUD operations. */
|
|
7
|
+
export const behavior = {
|
|
8
|
+
/** Applies multiple behaviors to a ResourceOperations set in order. */
|
|
9
|
+
compose: withBehaviors,
|
|
10
|
+
/** Adds optimistic concurrency control via a version column — rejects stale updates. */
|
|
11
|
+
versioned: applyVersioned,
|
|
12
|
+
/** Wraps operations to emit audit records on create/update/delete. */
|
|
13
|
+
audited: applyAudited,
|
|
14
|
+
/** Wraps operations to invalidate a cache on mutations. Returns ops with an attached .cache handle. */
|
|
15
|
+
invalidate: withInvalidation,
|
|
16
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { Audit } from '../../infra/audit/types.js'
|
|
2
|
+
import type { SQL, DB } from '../../data/db/types.js'
|
|
3
|
+
|
|
4
|
+
export interface VersionedBehaviorConfig {
|
|
5
|
+
readonly table: string
|
|
6
|
+
readonly whereFromId?: (id: string) => SQL
|
|
7
|
+
readonly softDelete?: boolean
|
|
8
|
+
readonly beforeRevise?: (db: DB, auth: unknown, tenant: string, id: string, input: unknown) => Promise<void>
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface AuditedBehaviorConfig {
|
|
12
|
+
readonly audit: Audit
|
|
13
|
+
readonly entityType?: string
|
|
14
|
+
readonly actionPrefix?: string
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface InvalidationBehaviorConfig {
|
|
18
|
+
readonly ttlMs: number
|
|
19
|
+
readonly database: string
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface BehaviorConfig {
|
|
23
|
+
readonly versioned?: VersionedBehaviorConfig
|
|
24
|
+
readonly audited?: AuditedBehaviorConfig | Audit
|
|
25
|
+
readonly cached?: InvalidationBehaviorConfig
|
|
26
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { DB } from '../../data/db/index.js'
|
|
2
|
+
import type { ResourceOperations, ResourceId } from '../fixture/types.js'
|
|
3
|
+
import type { VersionedBehaviorConfig } from './types.js'
|
|
4
|
+
import type { ReviseOptions } from '../../data/temporal/types.js'
|
|
5
|
+
import { sql } from '../../data/db/sql.js'
|
|
6
|
+
import { revise } from '../../data/temporal/revise.js'
|
|
7
|
+
|
|
8
|
+
export function applyVersioned<TAuth>(
|
|
9
|
+
ops: ResourceOperations<TAuth>,
|
|
10
|
+
config: VersionedBehaviorConfig,
|
|
11
|
+
_resourceName: string,
|
|
12
|
+
): ResourceOperations<TAuth> {
|
|
13
|
+
const softDelete = config.softDelete !== false
|
|
14
|
+
|
|
15
|
+
const buildWhere = config.whereFromId
|
|
16
|
+
?? ((id: string) => sql`id = ${id}::uuid`)
|
|
17
|
+
|
|
18
|
+
const result: Record<string, unknown> = { ...ops }
|
|
19
|
+
|
|
20
|
+
if (ops.update !== undefined) {
|
|
21
|
+
const originalUpdate = ops.update
|
|
22
|
+
result['update'] = {
|
|
23
|
+
input: originalUpdate.input,
|
|
24
|
+
handler: async (db: DB, auth: TAuth, _tenant: string, id: ResourceId, input: unknown) => {
|
|
25
|
+
return (db as DB).transaction(async (tx) => {
|
|
26
|
+
await config.beforeRevise?.(db, auth, _tenant, id, input)
|
|
27
|
+
const userId = extractUserId(auth)
|
|
28
|
+
const opts: ReviseOptions = userId !== undefined
|
|
29
|
+
? { tx, table: config.table, where: sql`${buildWhere(id)} AND effective_to IS NULL`, data: input as Record<string, unknown>, userId }
|
|
30
|
+
: { tx, table: config.table, where: sql`${buildWhere(id)} AND effective_to IS NULL`, data: input as Record<string, unknown> }
|
|
31
|
+
return revise(opts)
|
|
32
|
+
})
|
|
33
|
+
},
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (ops.delete !== undefined && softDelete) {
|
|
38
|
+
result['delete'] = async (db: DB, _auth: TAuth, _tenant: string, id: ResourceId) => {
|
|
39
|
+
await db.execute(
|
|
40
|
+
sql`UPDATE ${sql.identifier(config.table)} SET effective_to = now() WHERE ${buildWhere(id)} AND effective_to IS NULL`,
|
|
41
|
+
)
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return result as ResourceOperations<TAuth>
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function extractUserId(auth: unknown): string | undefined {
|
|
49
|
+
if (auth !== null && typeof auth === 'object' && 'userId' in auth) {
|
|
50
|
+
const userId = (auth as { userId: unknown }).userId
|
|
51
|
+
if (typeof userId === 'string') return userId
|
|
52
|
+
}
|
|
53
|
+
return undefined
|
|
54
|
+
}
|