pipework 0.252.0 → 0.252.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +5 -0
- package/package.json +4 -3
- package/src/async/jobs/abortable-delay.ts +18 -0
- package/src/async/jobs/claims.ts +52 -0
- package/src/async/jobs/cron.ts +179 -0
- package/src/async/jobs/errors.ts +56 -0
- package/src/async/jobs/exclusion.ts +117 -0
- package/src/async/jobs/execute.ts +219 -0
- package/src/async/jobs/index.ts +10 -0
- package/src/async/jobs/listener.ts +135 -0
- package/src/async/jobs/namespace.ts +20 -0
- package/src/async/jobs/queue-door.ts +44 -0
- package/src/async/jobs/queue.ts +1012 -0
- package/src/async/jobs/run-script.ts +67 -0
- package/src/async/jobs/scheduler.ts +218 -0
- package/src/async/jobs/schedules-table.ts +37 -0
- package/src/async/jobs/serialize-error.ts +48 -0
- package/src/async/jobs/table.ts +191 -0
- package/src/async/pipeline/define.ts +53 -0
- package/src/async/pipeline/errors.ts +35 -0
- package/src/async/pipeline/execute.ts +53 -0
- package/src/async/pipeline/index.ts +13 -0
- package/src/async/pipeline/namespace.ts +12 -0
- package/src/async/pipeline/resume.ts +67 -0
- package/src/async/pipeline/run-steps.ts +98 -0
- package/src/async/pipeline/types.ts +57 -0
- package/src/async/state-machine/define.ts +64 -0
- package/src/async/state-machine/errors.ts +29 -0
- package/src/async/state-machine/index.ts +3 -0
- package/src/async/state-machine/namespace.ts +12 -0
- package/src/async/state-machine/transition.ts +90 -0
- package/src/async/state-machine/types.ts +37 -0
- package/src/async/steps/errors.ts +31 -0
- package/src/async/steps/graph.ts +1214 -0
- package/src/async/steps/hash.ts +40 -0
- package/src/async/steps/index.ts +7 -0
- package/src/async/steps/namespace.ts +7 -0
- package/src/async/steps/table.ts +97 -0
- package/src/async/steps/types.ts +318 -0
- package/src/audit/chain/index.ts +9 -0
- package/src/audit/chain/install.ts +113 -0
- package/src/audit/chain/retire.ts +52 -0
- package/src/audit/chain/verify.ts +257 -0
- package/src/audit/query/cursor.ts +37 -0
- package/src/audit/query/index.ts +9 -0
- package/src/audit/query/query.ts +223 -0
- package/src/audit/schema/audit-record-fields.ts +68 -0
- package/src/audit/schema/audit-record.ts +23 -0
- package/src/auth/auth/audit.ts +31 -0
- package/src/auth/auth/chain.ts +84 -0
- package/src/auth/auth/cookie.ts +136 -0
- package/src/auth/auth/email-links.ts +311 -0
- package/src/auth/auth/guard.ts +128 -0
- package/src/auth/auth/index.ts +18 -0
- package/src/auth/auth/multi-org.ts +337 -0
- package/src/auth/auth/namespace.ts +34 -0
- package/src/auth/auth/parse-expiry.ts +13 -0
- package/src/auth/auth/passwords.ts +110 -0
- package/src/auth/auth/schema.ts +110 -0
- package/src/auth/auth/sessions.ts +565 -0
- package/src/auth/auth/sign-in.ts +156 -0
- package/src/auth/auth/signing-keys.ts +69 -0
- package/src/auth/auth/types.ts +26 -0
- package/src/auth/auth/uuid.ts +10 -0
- package/src/auth/rbac/cache.ts +55 -0
- package/src/auth/rbac/check.ts +30 -0
- package/src/auth/rbac/create.ts +35 -0
- package/src/auth/rbac/index.ts +5 -0
- package/src/auth/rbac/middleware.ts +20 -0
- package/src/auth/rbac/namespace.ts +15 -0
- package/src/auth/rbac/resolve.ts +31 -0
- package/src/auth/rbac/schema.sql +31 -0
- package/src/auth/rbac/seed.ts +67 -0
- package/src/auth/rbac/tables.ts +32 -0
- package/src/auth/rbac/types.ts +65 -0
- package/src/auth/tenant/extract.ts +33 -0
- package/src/auth/tenant/in-tenant.ts +49 -0
- package/src/auth/tenant/index.ts +7 -0
- package/src/auth/tenant/namespace.ts +23 -0
- package/src/auth/tenant/propagate.ts +20 -0
- package/src/auth/tenant/rls.ts +120 -0
- package/src/auth/tenant/scope.ts +33 -0
- package/src/auth/tenant/scoped-db.ts +581 -0
- package/src/auth/tenant/types.ts +14 -0
- package/src/auth/tenant/validate.ts +27 -0
- package/src/cli/commands/add.ts +158 -0
- package/src/cli/commands/alerts.ts +98 -0
- package/src/cli/commands/check.ts +1418 -0
- package/src/cli/commands/codemod.ts +78 -0
- package/src/cli/commands/db.ts +356 -0
- package/src/cli/commands/dev.ts +101 -0
- package/src/cli/commands/generate.ts +75 -0
- package/src/cli/commands/init.ts +36 -0
- package/src/cli/commands/install.ts +28 -0
- package/src/cli/commands/lint.ts +62 -0
- package/src/cli/commands/migrate.ts +132 -0
- package/src/cli/commands/openapi.ts +63 -0
- package/src/cli/commands/remove.ts +72 -0
- package/src/cli/commands/run.ts +50 -0
- package/src/cli/commands/scope.ts +54 -0
- package/src/cli/commands/serve.ts +68 -0
- package/src/cli/commands/shard.ts +31 -0
- package/src/cli/commands/shared/config-pipework-version.ts +28 -0
- package/src/cli/commands/shared/find-config.ts +22 -0
- package/src/cli/commands/shared/load-config.ts +35 -0
- package/src/cli/commands/shared/pnpm-install.ts +95 -0
- package/src/cli/commands/shared/report-removed.ts +8 -0
- package/src/cli/commands/sync.ts +32 -0
- package/src/cli/commands/test.ts +307 -0
- package/src/cli/commands/upgrade.ts +48 -0
- package/src/cli/dev-bootstrap.ts +24 -0
- package/src/cli/errors.ts +10 -0
- package/src/cli/index.ts +303 -0
- package/src/cli/output.ts +17 -0
- package/src/cli/port.ts +15 -0
- package/src/cli/surface-kinds.ts +20 -0
- package/src/client/index.ts +4 -0
- package/src/client/poll.ts +130 -0
- package/src/client/report-fault.ts +119 -0
- package/src/core/config/app-pool.ts +19 -0
- package/src/core/config/aws-env.ts +29 -0
- package/src/core/config/base-dir.ts +24 -0
- package/src/core/config/discover.ts +64 -0
- package/src/core/config/env-resolve.ts +87 -0
- package/src/core/config/env-types.ts +116 -0
- package/src/core/config/env.ts +55 -0
- package/src/core/config/errors.ts +9 -0
- package/src/core/config/import-config.ts +40 -0
- package/src/core/config/index.ts +8 -0
- package/src/core/config/load.ts +417 -0
- package/src/core/config/namespace.ts +14 -0
- package/src/core/config/resolve.ts +195 -0
- package/src/core/config/schema.ts +488 -0
- package/src/core/config/statement-observer.ts +48 -0
- package/src/core/config/ts-inline-hooks.ts +75 -0
- package/src/core/config/ts-register.ts +4 -0
- package/src/core/config/ts-resolve-hooks.ts +101 -0
- package/src/core/errors/base.ts +49 -0
- package/src/core/errors/http.ts +146 -0
- package/src/core/errors/index.ts +2 -0
- package/src/core/errors/namespace.ts +31 -0
- package/src/core/lifecycle/drain.ts +33 -0
- package/src/core/lifecycle/health.ts +72 -0
- package/src/core/lifecycle/index.ts +4 -0
- package/src/core/lifecycle/namespace.ts +7 -0
- package/src/core/lifecycle/startup.ts +102 -0
- package/src/core/lifecycle/types.ts +36 -0
- package/src/core/logging/context.ts +60 -0
- package/src/core/logging/error-chain.ts +68 -0
- package/src/core/logging/index.ts +5 -0
- package/src/core/logging/logger.ts +65 -0
- package/src/core/logging/namespace.ts +16 -0
- package/src/core/logging/proxy.ts +22 -0
- package/src/core/logging/redact-match.ts +24 -0
- package/src/core/logging/types.ts +47 -0
- package/src/core/pipework.ts +566 -0
- package/src/core/runtime/broker.ts +69 -0
- package/src/core/surface/http.ts +133 -0
- package/src/core/surface/index.ts +11 -0
- package/src/core/surface/job-timeout.ts +40 -0
- package/src/core/surface/namespace.ts +13 -0
- package/src/core/surface/script.ts +33 -0
- package/src/core/surface/start.ts +142 -0
- package/src/core/surface/types.ts +225 -0
- package/src/core/surface/validate.ts +88 -0
- package/src/core/surface/worker.ts +528 -0
- package/src/core/validation/connectivity.ts +49 -0
- package/src/core/validation/domain.ts +99 -0
- package/src/core/validation/index.ts +5 -0
- package/src/core/validation/queue-tables.ts +312 -0
- package/src/core/validation/rls-role.ts +138 -0
- package/src/core/validation/startup.ts +51 -0
- package/src/core/validation/surfaces.ts +65 -0
- package/src/core/validation/types.ts +25 -0
- package/src/data/db/advisory-lock.ts +55 -0
- package/src/data/db/aggregates.ts +172 -0
- package/src/data/db/array-elements.ts +111 -0
- package/src/data/db/bulk-set.ts +53 -0
- package/src/data/db/col/index.ts +14 -0
- package/src/data/db/composed.ts +53 -0
- package/src/data/db/db.ts +276 -0
- package/src/data/db/excluded.ts +16 -0
- package/src/data/db/expressions.ts +473 -0
- package/src/data/db/filter.ts +90 -0
- package/src/data/db/function-source.ts +114 -0
- package/src/data/db/identifiers.ts +16 -0
- package/src/data/db/idx/index.ts +9 -0
- package/src/data/db/in-list.ts +49 -0
- package/src/data/db/index.ts +9 -0
- package/src/data/db/insert-split.ts +137 -0
- package/src/data/db/instrumentation.ts +82 -0
- package/src/data/db/maintenance.ts +28 -0
- package/src/data/db/namespace.ts +107 -0
- package/src/data/db/owner-labels.ts +9 -0
- package/src/data/db/pool.ts +187 -0
- package/src/data/db/query-label.ts +13 -0
- package/src/data/db/query-watch.ts +116 -0
- package/src/data/db/raw-client-warning.ts +37 -0
- package/src/data/db/readonly.ts +23 -0
- package/src/data/db/reservation.ts +151 -0
- package/src/data/db/schema-features.ts +14 -0
- package/src/data/db/serializable.ts +83 -0
- package/src/data/db/sql.ts +1 -0
- package/src/data/db/timeouts.ts +76 -0
- package/src/data/db/types.ts +18 -0
- package/src/data/db/unbound.ts +33 -0
- package/src/data/db/values.ts +29 -0
- package/src/data/domain/brand.ts +15 -0
- package/src/data/domain/build-factory.ts +193 -0
- package/src/data/domain/build-schema.ts +161 -0
- package/src/data/domain/build-table.ts +355 -0
- package/src/data/domain/define.ts +239 -0
- package/src/data/domain/external-registry.ts +34 -0
- package/src/data/domain/external.ts +77 -0
- package/src/data/domain/field.ts +279 -0
- package/src/data/domain/index.ts +13 -0
- package/src/data/domain/naming.ts +6 -0
- package/src/data/domain/projections.ts +57 -0
- package/src/data/domain/trace-safe-projection.ts +31 -0
- package/src/data/domain/traits.ts +108 -0
- package/src/data/domain/types.ts +289 -0
- package/src/data/jsonb/composed.ts +18 -0
- package/src/data/jsonb/index.ts +1 -0
- package/src/data/jsonb/namespace.ts +30 -0
- package/src/data/jsonb/query.ts +86 -0
- package/src/data/migrate/append-only-generate.ts +47 -0
- package/src/data/migrate/applied.ts +47 -0
- package/src/data/migrate/apply.ts +309 -0
- package/src/data/migrate/check.ts +119 -0
- package/src/data/migrate/diff.ts +357 -0
- package/src/data/migrate/door-generate.ts +130 -0
- package/src/data/migrate/framework-rls.ts +58 -0
- package/src/data/migrate/generate.ts +201 -0
- package/src/data/migrate/history.ts +215 -0
- package/src/data/migrate/index.ts +9 -0
- package/src/data/migrate/internal-definitions.ts +175 -0
- package/src/data/migrate/post-process.ts +73 -0
- package/src/data/migrate/pragma.ts +73 -0
- package/src/data/migrate/provision-framework.ts +147 -0
- package/src/data/migrate/rls-generate.ts +61 -0
- package/src/data/migrate/run.ts +124 -0
- package/src/data/migrate/safety.ts +167 -0
- package/src/data/migrate/snapshot.ts +368 -0
- package/src/data/migrate/sql-emit.ts +392 -0
- package/src/data/migrate/statements.ts +312 -0
- package/src/data/query/alias.ts +126 -0
- package/src/data/query/batch.ts +8 -0
- package/src/data/query/cache/core/cache.ts +78 -0
- package/src/data/query/cache/core/index.ts +1 -0
- package/src/data/query/cache/core/types.ts +29 -0
- package/src/data/query/cache/readme.md +230 -0
- package/src/data/query/casing.ts +76 -0
- package/src/data/query/collation.ts +11 -0
- package/src/data/query/column-builder.ts +370 -0
- package/src/data/query/column.ts +150 -0
- package/src/data/query/entity.ts +42 -0
- package/src/data/query/errors.ts +33 -0
- package/src/data/query/index.ts +14 -0
- package/src/data/query/logger.ts +47 -0
- package/src/data/query/operations.ts +45 -0
- package/src/data/query/pg-core/alias.ts +12 -0
- package/src/data/query/pg-core/checks.ts +32 -0
- package/src/data/query/pg-core/columns/all.ts +71 -0
- package/src/data/query/pg-core/columns/bigint.ts +110 -0
- package/src/data/query/pg-core/columns/bigserial.ts +128 -0
- package/src/data/query/pg-core/columns/boolean.ts +43 -0
- package/src/data/query/pg-core/columns/char.ts +85 -0
- package/src/data/query/pg-core/columns/cidr.ts +43 -0
- package/src/data/query/pg-core/columns/common.ts +366 -0
- package/src/data/query/pg-core/columns/custom.ts +232 -0
- package/src/data/query/pg-core/columns/date.common.ts +15 -0
- package/src/data/query/pg-core/columns/date.ts +112 -0
- package/src/data/query/pg-core/columns/double-precision.ts +55 -0
- package/src/data/query/pg-core/columns/enum.ts +201 -0
- package/src/data/query/pg-core/columns/index.ts +35 -0
- package/src/data/query/pg-core/columns/inet.ts +43 -0
- package/src/data/query/pg-core/columns/int.common.ts +57 -0
- package/src/data/query/pg-core/columns/integer.ts +53 -0
- package/src/data/query/pg-core/columns/interval.ts +83 -0
- package/src/data/query/pg-core/columns/json.ts +58 -0
- package/src/data/query/pg-core/columns/jsonb.ts +56 -0
- package/src/data/query/pg-core/columns/line.ts +117 -0
- package/src/data/query/pg-core/columns/macaddr.ts +43 -0
- package/src/data/query/pg-core/columns/macaddr8.ts +43 -0
- package/src/data/query/pg-core/columns/numeric.ts +227 -0
- package/src/data/query/pg-core/columns/point.ts +127 -0
- package/src/data/query/pg-core/columns/postgis_extension/geometry.ts +119 -0
- package/src/data/query/pg-core/columns/postgis_extension/utils.ts +47 -0
- package/src/data/query/pg-core/columns/real.ts +58 -0
- package/src/data/query/pg-core/columns/serial.ts +55 -0
- package/src/data/query/pg-core/columns/smallint.ts +53 -0
- package/src/data/query/pg-core/columns/smallserial.ts +60 -0
- package/src/data/query/pg-core/columns/text.ts +67 -0
- package/src/data/query/pg-core/columns/time.ts +72 -0
- package/src/data/query/pg-core/columns/timestamp.ts +163 -0
- package/src/data/query/pg-core/columns/uuid.ts +51 -0
- package/src/data/query/pg-core/columns/varchar.ts +89 -0
- package/src/data/query/pg-core/columns/vector_extension/bit.ts +68 -0
- package/src/data/query/pg-core/columns/vector_extension/halfvec.ts +80 -0
- package/src/data/query/pg-core/columns/vector_extension/sparsevec.ts +67 -0
- package/src/data/query/pg-core/columns/vector_extension/vector.ts +80 -0
- package/src/data/query/pg-core/db.ts +674 -0
- package/src/data/query/pg-core/dialect.ts +1404 -0
- package/src/data/query/pg-core/expressions.ts +25 -0
- package/src/data/query/pg-core/foreign-keys.ts +130 -0
- package/src/data/query/pg-core/index.ts +21 -0
- package/src/data/query/pg-core/indexes.ts +276 -0
- package/src/data/query/pg-core/policies.ts +56 -0
- package/src/data/query/pg-core/primary-keys.ts +62 -0
- package/src/data/query/pg-core/query-builders/count.ts +86 -0
- package/src/data/query/pg-core/query-builders/delete.ts +293 -0
- package/src/data/query/pg-core/query-builders/index.ts +7 -0
- package/src/data/query/pg-core/query-builders/insert.ts +452 -0
- package/src/data/query/pg-core/query-builders/query-builder.ts +123 -0
- package/src/data/query/pg-core/query-builders/query.ts +157 -0
- package/src/data/query/pg-core/query-builders/raw.ts +49 -0
- package/src/data/query/pg-core/query-builders/refresh-materialized-view.ts +108 -0
- package/src/data/query/pg-core/query-builders/select.ts +1346 -0
- package/src/data/query/pg-core/query-builders/select.types.ts +437 -0
- package/src/data/query/pg-core/query-builders/update.ts +626 -0
- package/src/data/query/pg-core/roles.ts +41 -0
- package/src/data/query/pg-core/schema.ts +72 -0
- package/src/data/query/pg-core/sequence.ts +41 -0
- package/src/data/query/pg-core/session.ts +292 -0
- package/src/data/query/pg-core/subquery.ts +46 -0
- package/src/data/query/pg-core/table.ts +252 -0
- package/src/data/query/pg-core/unique-constraint.ts +73 -0
- package/src/data/query/pg-core/utils/array.ts +95 -0
- package/src/data/query/pg-core/utils/index.ts +1 -0
- package/src/data/query/pg-core/utils.ts +104 -0
- package/src/data/query/pg-core/view-base.ts +14 -0
- package/src/data/query/pg-core/view-common.ts +1 -0
- package/src/data/query/pg-core/view.ts +435 -0
- package/src/data/query/pg-core/with-builder.ts +41 -0
- package/src/data/query/postgres-js/README.md +46 -0
- package/src/data/query/postgres-js/array-parser.ts +45 -0
- package/src/data/query/postgres-js/driver-error-boundary.ts +149 -0
- package/src/data/query/postgres-js/driver.ts +163 -0
- package/src/data/query/postgres-js/index.ts +2 -0
- package/src/data/query/postgres-js/session.ts +234 -0
- package/src/data/query/primary-key.ts +11 -0
- package/src/data/query/query-builders/query-builder.ts +19 -0
- package/src/data/query/query-builders/select.types.ts +175 -0
- package/src/data/query/query-promise.ts +35 -0
- package/src/data/query/relations.ts +725 -0
- package/src/data/query/runnable-query.ts +12 -0
- package/src/data/query/selection-proxy.ts +121 -0
- package/src/data/query/session.ts +8 -0
- package/src/data/query/sql/expressions/conditions.ts +741 -0
- package/src/data/query/sql/expressions/index.ts +2 -0
- package/src/data/query/sql/expressions/select.ts +45 -0
- package/src/data/query/sql/functions/aggregate.ts +129 -0
- package/src/data/query/sql/functions/index.ts +2 -0
- package/src/data/query/sql/functions/vector.ts +178 -0
- package/src/data/query/sql/index.ts +3 -0
- package/src/data/query/sql/sql.ts +794 -0
- package/src/data/query/subquery.ts +53 -0
- package/src/data/query/table.ts +208 -0
- package/src/data/query/table.utils.ts +2 -0
- package/src/data/query/tracing-utils.ts +3 -0
- package/src/data/query/tracing.ts +11 -0
- package/src/data/query/utils.ts +357 -0
- package/src/data/query/version.ts +4 -0
- package/src/data/query/view-common.ts +1 -0
- package/src/data/schema/branded.ts +13 -0
- package/src/data/schema/generated/coerce.ts +5 -0
- package/src/data/schema/generated/combinator.ts +6 -0
- package/src/data/schema/generated/encoding.ts +5 -0
- package/src/data/schema/generated/error.ts +6 -0
- package/src/data/schema/generated/format.ts +5 -0
- package/src/data/schema/generated/type-constructor.ts +10 -0
- package/src/data/schema/generated/type.ts +6 -0
- package/src/data/schema/index.ts +6 -0
- package/src/data/schema/json-schema.ts +33 -0
- package/src/data/schema/namespace.ts +55 -0
- package/src/data/schema/parse.ts +39 -0
- package/src/data/schema/types.ts +39 -0
- package/src/data/temporal/append-only.ts +134 -0
- package/src/data/temporal/close.ts +27 -0
- package/src/data/temporal/columns.ts +10 -0
- package/src/data/temporal/definition-queries.ts +70 -0
- package/src/data/temporal/errors.ts +10 -0
- package/src/data/temporal/get-current.ts +22 -0
- package/src/data/temporal/index.ts +9 -0
- package/src/data/temporal/namespace.ts +36 -0
- package/src/data/temporal/query.ts +25 -0
- package/src/data/temporal/resolve.ts +17 -0
- package/src/data/temporal/revise.ts +67 -0
- package/src/data/temporal/types.ts +22 -0
- package/src/data/valve/artifact.ts +33 -0
- package/src/data/valve/contract-diff.ts +21 -0
- package/src/data/valve/cross.ts +57 -0
- package/src/data/valve/crossing-ledger.ts +50 -0
- package/src/data/valve/decision.ts +44 -0
- package/src/data/valve/durable-crossing.ts +52 -0
- package/src/data/valve/execute-crossing.ts +77 -0
- package/src/data/valve/faucet.ts +56 -0
- package/src/data/valve/gate.ts +114 -0
- package/src/data/valve/graph.ts +77 -0
- package/src/data/valve/index.ts +53 -0
- package/src/data/valve/namespace.ts +57 -0
- package/src/data/valve/registry.ts +50 -0
- package/src/data/valve/schema/valve-crossing-fields.ts +23 -0
- package/src/data/valve/schema/valve-crossing.ts +9 -0
- package/src/data/valve/supply.ts +154 -0
- package/src/data/valve/types.ts +143 -0
- package/src/data/vector/columns.ts +27 -0
- package/src/data/vector/index.ts +4 -0
- package/src/data/vector/namespace.ts +33 -0
- package/src/data/vector/query.ts +85 -0
- package/src/data/vector/validate.ts +16 -0
- package/src/data/write/audit/actor.ts +15 -0
- package/src/data/write/audit/audit-columns.ts +18 -0
- package/src/data/write/audit/audit-select.ts +111 -0
- package/src/data/write/audit/bound-transaction.ts +14 -0
- package/src/data/write/audit/cte-compile.ts +32 -0
- package/src/data/write/audit/decode-returning.ts +47 -0
- package/src/data/write/audit/final-select.ts +43 -0
- package/src/data/write/audit/infra-audit.ts +36 -0
- package/src/data/write/audit/primary-key.ts +65 -0
- package/src/data/write/audit/projection.ts +41 -0
- package/src/data/write/audit/read-touches.ts +86 -0
- package/src/data/write/audit/touch-match.ts +78 -0
- package/src/data/write/audit/verb-delete.ts +62 -0
- package/src/data/write/audit/verb-insert.ts +36 -0
- package/src/data/write/audit/verb-select.ts +47 -0
- package/src/data/write/audit/verb-update.ts +60 -0
- package/src/data/write/audit/verb-upsert.ts +104 -0
- package/src/data/write/conflict.ts +43 -0
- package/src/data/write/delete.ts +38 -0
- package/src/data/write/execute.ts +243 -0
- package/src/data/write/index.ts +11 -0
- package/src/data/write/insert.ts +63 -0
- package/src/data/write/state.ts +43 -0
- package/src/data/write/update.ts +54 -0
- package/src/data/write/upsert.ts +58 -0
- package/src/data/write/values.ts +19 -0
- package/src/index.ts +230 -0
- package/src/infra/access-links/code.ts +70 -0
- package/src/infra/access-links/handle.ts +892 -0
- package/src/infra/access-links/index.ts +9 -0
- package/src/infra/access-links/namespace.ts +30 -0
- package/src/infra/access-links/person-check.ts +177 -0
- package/src/infra/access-links/schema.ts +71 -0
- package/src/infra/alerts/async-sources.ts +231 -0
- package/src/infra/alerts/audit.ts +36 -0
- package/src/infra/alerts/beat.ts +123 -0
- package/src/infra/alerts/client-faults.ts +111 -0
- package/src/infra/alerts/faults.ts +156 -0
- package/src/infra/alerts/handle.ts +778 -0
- package/src/infra/alerts/http.ts +192 -0
- package/src/infra/alerts/index.ts +12 -0
- package/src/infra/alerts/infra-sources.ts +180 -0
- package/src/infra/alerts/namespace.ts +81 -0
- package/src/infra/alerts/redact.ts +21 -0
- package/src/infra/alerts/request-faults.ts +41 -0
- package/src/infra/alerts/schema.ts +124 -0
- package/src/infra/alerts/sinks.ts +125 -0
- package/src/infra/alerts/source-options.ts +26 -0
- package/src/infra/alerts/sources.ts +503 -0
- package/src/infra/alerts/subsystem-sources.ts +137 -0
- package/src/infra/audit/emit.ts +87 -0
- package/src/infra/audit/index.ts +2 -0
- package/src/infra/audit/namespace.ts +22 -0
- package/src/infra/audit/schema.sql +35 -0
- package/src/infra/audit/types.ts +18 -0
- package/src/infra/aws/credentials.ts +147 -0
- package/src/infra/aws/sigv4.ts +140 -0
- package/src/infra/cache/create.ts +58 -0
- package/src/infra/cache/entry.ts +81 -0
- package/src/infra/cache/index.ts +4 -0
- package/src/infra/cache/namespace.ts +10 -0
- package/src/infra/cache/tenant.ts +93 -0
- package/src/infra/cache/types.ts +24 -0
- package/src/infra/crawl/audit.ts +35 -0
- package/src/infra/crawl/errors.ts +25 -0
- package/src/infra/crawl/handle.ts +693 -0
- package/src/infra/crawl/index.ts +9 -0
- package/src/infra/crawl/links.ts +160 -0
- package/src/infra/crawl/namespace.ts +36 -0
- package/src/infra/crawl/robots.ts +100 -0
- package/src/infra/crawl/schema.ts +89 -0
- package/src/infra/credit-line/handle.ts +883 -0
- package/src/infra/credit-line/index.ts +6 -0
- package/src/infra/credit-line/namespace.ts +7 -0
- package/src/infra/credit-line/schema.ts +96 -0
- package/src/infra/credits/handle.ts +706 -0
- package/src/infra/credits/index.ts +5 -0
- package/src/infra/credits/namespace.ts +7 -0
- package/src/infra/credits/schema.ts +83 -0
- package/src/infra/email/audit.ts +36 -0
- package/src/infra/email/inbound.ts +622 -0
- package/src/infra/email/index.ts +11 -0
- package/src/infra/email/mail.ts +517 -0
- package/src/infra/email/mime.ts +447 -0
- package/src/infra/email/namespace.ts +22 -0
- package/src/infra/email/providers.ts +111 -0
- package/src/infra/email/schema.ts +96 -0
- package/src/infra/email/sns.ts +200 -0
- package/src/infra/email/unsubscribe.ts +44 -0
- package/src/infra/fetch/address.ts +88 -0
- package/src/infra/fetch/connect-retry.ts +87 -0
- package/src/infra/fetch/handle.ts +329 -0
- package/src/infra/fetch/index.ts +7 -0
- package/src/infra/fetch/namespace.ts +70 -0
- package/src/infra/idempotency/index.ts +3 -0
- package/src/infra/idempotency/middleware.ts +87 -0
- package/src/infra/idempotency/store.ts +92 -0
- package/src/infra/idempotency/types.ts +30 -0
- package/src/infra/join-requests/handle.ts +312 -0
- package/src/infra/join-requests/index.ts +2 -0
- package/src/infra/join-requests/namespace.ts +7 -0
- package/src/infra/join-requests/schema.ts +33 -0
- package/src/infra/outbox/core.ts +165 -0
- package/src/infra/quota/errors.ts +32 -0
- package/src/infra/quota/handle.ts +215 -0
- package/src/infra/quota/index.ts +5 -0
- package/src/infra/quota/namespace.ts +7 -0
- package/src/infra/quota/schema.ts +15 -0
- package/src/infra/storage/backend.ts +34 -0
- package/src/infra/storage/handle.ts +513 -0
- package/src/infra/storage/index.ts +9 -0
- package/src/infra/storage/local.ts +104 -0
- package/src/infra/storage/namespace.ts +13 -0
- package/src/infra/storage/s3.ts +257 -0
- package/src/infra/storage/schema.ts +24 -0
- package/src/infra/support/handle.ts +854 -0
- package/src/infra/support/http.ts +229 -0
- package/src/infra/support/index.ts +7 -0
- package/src/infra/support/namespace.ts +12 -0
- package/src/infra/support/schema.ts +96 -0
- package/src/infra/webhook/inbound.ts +45 -0
- package/src/infra/webhook/index.ts +5 -0
- package/src/infra/webhook/namespace.ts +24 -0
- package/src/infra/webhook/outbound.ts +315 -0
- package/src/infra/webhook/sign.ts +32 -0
- package/src/infra/webhook/verify.ts +82 -0
- package/src/invariants/assert.ts +26 -0
- package/src/invariants/index.ts +1 -0
- package/src/lint/config.ts +56 -0
- package/src/lint/doctrine.ts +163 -0
- package/src/lint/include.ts +97 -0
- package/src/lint/index.ts +3 -0
- package/src/lint/preset.ts +10 -0
- package/src/lint/resolver.ts +218 -0
- package/src/lint/rules/no-reexports.ts +46 -0
- package/src/request/behavior/audited.ts +85 -0
- package/src/request/behavior/cached.ts +62 -0
- package/src/request/behavior/compose.ts +28 -0
- package/src/request/behavior/index.ts +5 -0
- package/src/request/behavior/namespace.ts +16 -0
- package/src/request/behavior/types.ts +26 -0
- package/src/request/behavior/versioned.ts +54 -0
- package/src/request/context/create.ts +103 -0
- package/src/request/context/guard.ts +49 -0
- package/src/request/context/index.ts +4 -0
- package/src/request/context/run-in-transaction.ts +200 -0
- package/src/request/context/store.ts +28 -0
- package/src/request/context/types.ts +50 -0
- package/src/request/di/builder.ts +535 -0
- package/src/request/di/index.ts +8 -0
- package/src/request/di/resolve.ts +271 -0
- package/src/request/di/types.ts +262 -0
- package/src/request/fitting/namespace.ts +7 -0
- package/src/request/fixture/batch.ts +71 -0
- package/src/request/fixture/builder.ts +26 -0
- package/src/request/fixture/errors.ts +10 -0
- package/src/request/fixture/index.ts +8 -0
- package/src/request/fixture/namespace.ts +23 -0
- package/src/request/fixture/paginate.ts +44 -0
- package/src/request/fixture/to-handlers.ts +260 -0
- package/src/request/fixture/types.ts +79 -0
- package/src/request/flow/act-as.ts +78 -0
- package/src/request/flow/namespace.ts +17 -0
- package/src/request/http/error-step.ts +42 -0
- package/src/request/http/file.ts +141 -0
- package/src/request/http/index.ts +4 -0
- package/src/request/http/middleware.ts +141 -0
- package/src/request/http/namespace.ts +5 -0
- package/src/request/http/rate-limit-classes.ts +170 -0
- package/src/request/http/response.ts +82 -0
- package/src/request/http/server.ts +916 -0
- package/src/request/http/sniff.ts +261 -0
- package/src/request/http/types.ts +250 -0
- package/src/request/http/upload.ts +284 -0
- package/src/request/internal/text.ts +6 -0
- package/src/request/openapi/generate.ts +205 -0
- package/src/request/openapi/index.ts +2 -0
- package/src/request/openapi/namespace.ts +7 -0
- package/src/test/auth.ts +13 -0
- package/src/test/auto-setup.ts +45 -0
- package/src/test/context.ts +91 -0
- package/src/test/crawl-fixture.ts +157 -0
- package/src/test/discover-fork.ts +17 -0
- package/src/test/fingerprint.ts +135 -0
- package/src/test/globals.d.ts +7 -0
- package/src/test/index.ts +19 -0
- package/src/test/namespace.ts +87 -0
- package/src/test/plugin.ts +189 -0
- package/src/test/progress.ts +185 -0
- package/src/test/reap-after-run.ts +28 -0
- package/src/test/reap.ts +286 -0
- package/src/test/reset.ts +253 -0
- package/src/test/savepoints.ts +172 -0
- package/src/test/setup.ts +1422 -0
- package/src/test/system-channel.ts +50 -0
- package/src/test/vitest-plugin.ts +142 -0
- package/src/test/vitest-workspace.ts +507 -0
- package/src/test/vitest.ts +333 -0
- package/src/test/watch.ts +105 -0
- package/src/trace/capture/bounded-serializer.ts +146 -0
- package/src/trace/cardinality/analyze.ts +173 -0
- package/src/trace/cardinality/branch-context.ts +214 -0
- package/src/trace/cardinality/check.ts +33 -0
- package/src/trace/cardinality/classify-edge.ts +64 -0
- package/src/trace/cardinality/structural-max.ts +107 -0
- package/src/trace/check/coverage-check.ts +188 -0
- package/src/trace/check/flow-check.ts +103 -0
- package/src/trace/check/flow-step-site.ts +234 -0
- package/src/trace/check/io-pattern.ts +116 -0
- package/src/trace/codemod/add-cardinality.ts +152 -0
- package/src/trace/codemod/run.ts +80 -0
- package/src/trace/entry/child-trace.ts +21 -0
- package/src/trace/entry/close-trace.ts +87 -0
- package/src/trace/entry/finalize-trace.ts +28 -0
- package/src/trace/entry/open-trace.ts +138 -0
- package/src/trace/entry/parent-trace.ts +20 -0
- package/src/trace/entry/parse-traceparent.ts +39 -0
- package/src/trace/entry/process-start.ts +38 -0
- package/src/trace/entry/trace-context.ts +26 -0
- package/src/trace/entry/tracing-enabled.ts +10 -0
- package/src/trace/external/record.ts +195 -0
- package/src/trace/external/report.ts +66 -0
- package/src/trace/namespace.ts +19 -0
- package/src/trace/partition/day.ts +47 -0
- package/src/trace/partition/index.ts +14 -0
- package/src/trace/partition/partition-name.ts +23 -0
- package/src/trace/partition/plan.ts +82 -0
- package/src/trace/partition/schedule.ts +26 -0
- package/src/trace/partition/startup-hook.ts +39 -0
- package/src/trace/partition/sync.ts +154 -0
- package/src/trace/partition/tables.ts +53 -0
- package/src/trace/read/tree.ts +83 -0
- package/src/trace/retain/copy-on-error.ts +76 -0
- package/src/trace/runtime/expire-stale-traces.ts +54 -0
- package/src/trace/runtime/flow-step.ts +147 -0
- package/src/trace/runtime/flush-steps.ts +31 -0
- package/src/trace/runtime/pending-writes.ts +54 -0
- package/src/trace/runtime/step-buffer.ts +298 -0
- package/src/trace/runtime/step-error.ts +21 -0
- package/src/trace/runtime/step-meta.ts +55 -0
- package/src/trace/runtime/thenable.ts +12 -0
- package/src/trace/runtime/truncate-trace.ts +62 -0
- package/src/trace/schema/trace-fields.ts +52 -0
- package/src/trace/schema/trace-retained.ts +22 -0
- package/src/trace/schema/trace-step-fields.ts +44 -0
- package/src/trace/schema/trace-step-retained.ts +25 -0
- package/src/trace/schema/trace-step.ts +18 -0
- package/src/trace/schema/trace.ts +12 -0
- package/src/trace/transformer/apply.ts +79 -0
- package/src/trace/transformer/build-meta-literal.ts +30 -0
- package/src/trace/transformer/find-flow-step-sites.ts +85 -0
- package/src/trace/transformer/inject-flow-step-import.ts +39 -0
- package/src/trace/transformer/transform-error.ts +14 -0
- package/src/trace/transformer/transform-flow-steps.ts +123 -0
- package/src/trace/transformer/wrap-function-declaration.ts +62 -0
- package/src/trace/transformer/wrap-variable-declaration.ts +67 -0
- package/src/workspace/affected.ts +110 -0
- package/src/workspace/changed-files.ts +163 -0
- package/src/workspace/check-deps.ts +213 -0
- package/src/workspace/checksum.ts +20 -0
- package/src/workspace/dependency-graph.ts +67 -0
- package/src/workspace/detect-pipework-copies.ts +93 -0
- package/src/workspace/discover.ts +206 -0
- package/src/workspace/generate.ts +401 -0
- package/src/workspace/index.ts +7 -0
- package/src/workspace/module-imports-pipework.ts +39 -0
- package/src/workspace/module-package-name.ts +12 -0
- package/src/workspace/scrub-git-env.ts +23 -0
- package/src/workspace/source-paths.ts +36 -0
- package/src/workspace/sync.ts +182 -0
- package/dist/REFERENCE.md +0 -1477
- package/dist/infra/access-links/rate-limit.d.ts +0 -10
- package/dist/infra/access-links/rate-limit.d.ts.map +0 -1
- package/dist/infra/access-links/rate-limit.js +0 -28
- package/dist/infra/access-links/rate-limit.js.map +0 -1
- package/dist/infra/email/sigv4.d.ts +0 -24
- package/dist/infra/email/sigv4.d.ts.map +0 -1
- package/dist/infra/email/sigv4.js +0 -55
- package/dist/infra/email/sigv4.js.map +0 -1
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
// `trace.traceparent`, `trace.env` and `trace.record`: carry the current trace
|
|
2
|
+
// to a process outside Node, stage the steps it reports back, and mark point events.
|
|
3
|
+
|
|
4
|
+
import { randomUUID } from 'node:crypto'
|
|
5
|
+
import { getContext } from '../../request/context/store.js'
|
|
6
|
+
import { getBaseLogger } from '../../core/logging/index.js'
|
|
7
|
+
import { recordFault, faultMessage } from '../../infra/alerts/faults.js'
|
|
8
|
+
import { emitReadTouches } from '../../data/write/audit/read-touches.js'
|
|
9
|
+
import { serializeForTrace } from '../capture/bounded-serializer.js'
|
|
10
|
+
import { projectStepError } from '../runtime/step-error.js'
|
|
11
|
+
import type { BegunStep } from '../runtime/step-buffer.js'
|
|
12
|
+
import {
|
|
13
|
+
externalStepNameSchema,
|
|
14
|
+
externalStepReportSchema,
|
|
15
|
+
parseReportTime,
|
|
16
|
+
type ExternalRecordResult,
|
|
17
|
+
type ExternalStepReport,
|
|
18
|
+
} from './report.js'
|
|
19
|
+
|
|
20
|
+
const NONE: ExternalRecordResult = { recorded: 0, invalid: 0, dropped: 0 }
|
|
21
|
+
|
|
22
|
+
/** W3C `traceparent` for the current trace and open step, or `null` outside a traced flow or once the trace is truncated. */
|
|
23
|
+
export function traceparent(): string | null {
|
|
24
|
+
const ctx = getContext()?.trace
|
|
25
|
+
if (ctx === undefined || ctx === null || ctx.buffer.truncated) return null
|
|
26
|
+
const traceHex = ctx.id.replaceAll('-', '')
|
|
27
|
+
const spanSource = (ctx.buffer.openStepId ?? ctx.id).replaceAll('-', '')
|
|
28
|
+
return `00-${traceHex}-${spanSource.slice(-16)}-01`
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** `{ TRACEPARENT }` for a child process's environment, or `{}` when there is no traceparent. */
|
|
32
|
+
export function env(): Record<string, string> {
|
|
33
|
+
const value = traceparent()
|
|
34
|
+
return value === null ? {} : { TRACEPARENT: value }
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Records steps reported by an outside process under the step open now (or as
|
|
39
|
+
* root-level steps when none is open). Synchronous and never throws.
|
|
40
|
+
*/
|
|
41
|
+
export function record(input: string | Iterable<ExternalStepReport>): ExternalRecordResult {
|
|
42
|
+
try {
|
|
43
|
+
return recordUnsafe(input)
|
|
44
|
+
} catch (err) {
|
|
45
|
+
getBaseLogger().error({ err }, 'trace.record failed; no external steps were recorded')
|
|
46
|
+
void recordFault('trace-record', faultMessage(err))
|
|
47
|
+
return NONE
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Records one zero-length step named `external:<name>` under the step open now,
|
|
53
|
+
* with an optional capture and, when `error` is given, status `error`. Outside a
|
|
54
|
+
* traced flow, or for an invalid name, it records nothing. Never throws.
|
|
55
|
+
*/
|
|
56
|
+
export function event(name: string, options?: { capture?: unknown; error?: unknown }): void {
|
|
57
|
+
try {
|
|
58
|
+
const ctx = getContext()?.trace
|
|
59
|
+
if (ctx === undefined || ctx === null) return
|
|
60
|
+
if (!externalStepNameSchema.safeParse(name).success) {
|
|
61
|
+
getBaseLogger().error({ name }, 'trace.event name is invalid; no event was recorded')
|
|
62
|
+
return
|
|
63
|
+
}
|
|
64
|
+
const now = new Date()
|
|
65
|
+
const failed = options !== undefined && 'error' in options && options.error !== undefined
|
|
66
|
+
ctx.buffer.stageExternal({
|
|
67
|
+
id: randomUUID(),
|
|
68
|
+
parentStepId: ctx.buffer.openStepId,
|
|
69
|
+
flowStepRef: `external:${name}`,
|
|
70
|
+
status: failed ? 'error' : 'ok',
|
|
71
|
+
startedAt: now,
|
|
72
|
+
completedAt: now,
|
|
73
|
+
durationMicros: 0n,
|
|
74
|
+
capturedInputs: options?.capture === undefined ? null : [serializeForTrace(options.capture)],
|
|
75
|
+
error: failed ? projectStepError(options.error) : null,
|
|
76
|
+
})
|
|
77
|
+
} catch (err) {
|
|
78
|
+
getBaseLogger().error({ err }, 'trace.event failed; no event was recorded')
|
|
79
|
+
void recordFault('trace-record', faultMessage(err))
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function recordUnsafe(input: string | Iterable<ExternalStepReport>): ExternalRecordResult {
|
|
84
|
+
const flow = getContext()
|
|
85
|
+
const ctx = flow?.trace
|
|
86
|
+
if (flow === undefined || ctx === undefined || ctx === null) return NONE
|
|
87
|
+
const buffer = ctx.buffer
|
|
88
|
+
|
|
89
|
+
let invalid = 0
|
|
90
|
+
const valid = new Map<string, ExternalStepReport>()
|
|
91
|
+
for (const candidate of candidates(input)) {
|
|
92
|
+
if (candidate === SKIP) continue
|
|
93
|
+
const parsed = candidate === BAD ? null : externalStepReportSchema.safeParse(candidate)
|
|
94
|
+
if (parsed === null || !parsed.success || valid.has(parsed.data.id)) {
|
|
95
|
+
invalid++
|
|
96
|
+
continue
|
|
97
|
+
}
|
|
98
|
+
valid.set(parsed.data.id, parsed.data)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// A parent chain that never reaches the calling step (a cycle) invalidates
|
|
102
|
+
// every report on it. Unknown or self parents end the chain at the caller.
|
|
103
|
+
const parentOf = (r: ExternalStepReport): ExternalStepReport | undefined =>
|
|
104
|
+
r.parent == null || r.parent === r.id ? undefined : valid.get(r.parent)
|
|
105
|
+
const cyclic: string[] = []
|
|
106
|
+
for (const r of valid.values()) {
|
|
107
|
+
const seen = new Set<string>()
|
|
108
|
+
for (let cur: ExternalStepReport | undefined = r; cur !== undefined; cur = parentOf(cur)) {
|
|
109
|
+
if (seen.has(cur.id)) { cyclic.push(r.id); break }
|
|
110
|
+
seen.add(cur.id)
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
for (const id of cyclic) valid.delete(id)
|
|
114
|
+
invalid += cyclic.length
|
|
115
|
+
|
|
116
|
+
const callerStepId = buffer.openStepId
|
|
117
|
+
const ordered = [...valid.values()]
|
|
118
|
+
.map(r => ({ r, start: parseReportTime(r.start)!, end: parseReportTime(r.end)! }))
|
|
119
|
+
.sort((a, b) => (a.start < b.start ? -1 : a.start > b.start ? 1 : 0))
|
|
120
|
+
|
|
121
|
+
const staged = new Map<string, string>()
|
|
122
|
+
const resolveParent = (r: ExternalStepReport): string | null => {
|
|
123
|
+
for (let p = parentOf(r); p !== undefined; p = parentOf(p)) {
|
|
124
|
+
const id = staged.get(p.id)
|
|
125
|
+
if (id !== undefined) return id
|
|
126
|
+
}
|
|
127
|
+
return callerStepId
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
let recorded = 0
|
|
131
|
+
let dropped = 0
|
|
132
|
+
const ids = new Map(ordered.map(o => [o.r.id, randomUUID()]))
|
|
133
|
+
for (const { r, start, end } of ordered) {
|
|
134
|
+
const id = ids.get(r.id)!
|
|
135
|
+
const parentStepId = resolveParent(r)
|
|
136
|
+
const row = buffer.stageExternal({
|
|
137
|
+
id,
|
|
138
|
+
parentStepId,
|
|
139
|
+
flowStepRef: `external:${r.name}`,
|
|
140
|
+
status: r.status,
|
|
141
|
+
startedAt: new Date(Number(start / 1000n)),
|
|
142
|
+
completedAt: new Date(Number(end / 1000n)),
|
|
143
|
+
durationMicros: end - start,
|
|
144
|
+
capturedInputs: r.capture === undefined ? null : [serializeForTrace(r.capture)],
|
|
145
|
+
error: r.status === 'error'
|
|
146
|
+
? {
|
|
147
|
+
name: r.error?.name ?? 'ExternalError',
|
|
148
|
+
message: r.error?.message ?? 'step reported error',
|
|
149
|
+
stack: r.error?.stack ?? null,
|
|
150
|
+
}
|
|
151
|
+
: null,
|
|
152
|
+
})
|
|
153
|
+
if (row === null) { dropped++; continue }
|
|
154
|
+
staged.set(r.id, id)
|
|
155
|
+
recorded++
|
|
156
|
+
if (r.status === 'ok' && r.touches !== undefined && r.touches.length > 0) {
|
|
157
|
+
const begun: BegunStep = {
|
|
158
|
+
seq: row.seq,
|
|
159
|
+
stepId: id,
|
|
160
|
+
parentStepId: row.parentStepId,
|
|
161
|
+
flowStepRef: row.flowStepRef,
|
|
162
|
+
startedAt: row.startedAt,
|
|
163
|
+
startHrTime: 0n,
|
|
164
|
+
capturedInputs: row.capturedInputs,
|
|
165
|
+
touches: r.touches.map(t => {
|
|
166
|
+
const at = t.indexOf(':')
|
|
167
|
+
return { entity: t.slice(0, at), verb: t.slice(at + 1) }
|
|
168
|
+
}),
|
|
169
|
+
wroteTouches: new Set(),
|
|
170
|
+
}
|
|
171
|
+
// Sync like a sync flow step: fire and forget; the emitter logs its own failures.
|
|
172
|
+
void emitReadTouches(flow, begun)
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return { recorded, invalid, dropped }
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const SKIP = Symbol('skip')
|
|
179
|
+
const BAD = Symbol('bad')
|
|
180
|
+
|
|
181
|
+
function* candidates(input: string | Iterable<ExternalStepReport>): Generator<unknown> {
|
|
182
|
+
if (typeof input !== 'string') {
|
|
183
|
+
yield* input
|
|
184
|
+
return
|
|
185
|
+
}
|
|
186
|
+
for (const raw of input.split('\n')) {
|
|
187
|
+
const line = raw.trim()
|
|
188
|
+
if (!line.startsWith('{')) { yield SKIP; continue }
|
|
189
|
+
try {
|
|
190
|
+
yield JSON.parse(line)
|
|
191
|
+
} catch {
|
|
192
|
+
yield BAD
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// The language-neutral report a process outside Node sends back: one JSON
|
|
2
|
+
// object per line. `parseReportTime` reads RFC 3339 timestamps to microsecond
|
|
3
|
+
// precision (a JS `Date` keeps only milliseconds).
|
|
4
|
+
|
|
5
|
+
import { z } from '../../data/schema/index.js'
|
|
6
|
+
|
|
7
|
+
const RFC3339 = /^(\d{4})-(\d{2})-(\d{2})[Tt](\d{2}):(\d{2}):(\d{2})(?:\.(\d+))?([Zz]|[+-]\d{2}:\d{2})$/
|
|
8
|
+
|
|
9
|
+
/** Microseconds since the Unix epoch for an RFC 3339 timestamp with offset, or `null` when malformed. */
|
|
10
|
+
export function parseReportTime(text: string): bigint | null {
|
|
11
|
+
const m = RFC3339.exec(text)
|
|
12
|
+
if (m === null) return null
|
|
13
|
+
const [year, month, day, hour, minute, second] = m.slice(1, 7).map(Number) as [number, number, number, number, number, number]
|
|
14
|
+
if (month < 1 || month > 12 || day < 1 || hour > 23 || minute > 59 || second > 60) return null
|
|
15
|
+
const probe = new Date(Date.UTC(year, month - 1, day))
|
|
16
|
+
if (probe.getUTCMonth() !== month - 1 || probe.getUTCDate() !== day) return null
|
|
17
|
+
let offsetSeconds = 0
|
|
18
|
+
const zone = m[8] as string
|
|
19
|
+
if (zone !== 'Z' && zone !== 'z') {
|
|
20
|
+
const oh = Number(zone.slice(1, 3))
|
|
21
|
+
const om = Number(zone.slice(4, 6))
|
|
22
|
+
if (oh > 23 || om > 59) return null
|
|
23
|
+
offsetSeconds = (oh * 3600 + om * 60) * (zone.startsWith('-') ? -1 : 1)
|
|
24
|
+
}
|
|
25
|
+
const wholeMs = Date.UTC(year, month - 1, day, hour, minute, second)
|
|
26
|
+
const fraction = Number((m[7] ?? '').padEnd(6, '0').slice(0, 6))
|
|
27
|
+
return (BigInt(wholeMs) / 1000n - BigInt(offsetSeconds)) * 1000000n + BigInt(fraction)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const timestamp = z.string().refine(v => parseReportTime(v) !== null, 'expected an RFC 3339 timestamp with offset')
|
|
31
|
+
|
|
32
|
+
/** The name rule for an external step and a point event. */
|
|
33
|
+
export const externalStepNameSchema = z.string().min(1).max(200)
|
|
34
|
+
|
|
35
|
+
export const externalStepReportSchema = z.object({
|
|
36
|
+
v: z.literal(1),
|
|
37
|
+
id: z.string().min(1).max(128),
|
|
38
|
+
parent: z.string().nullable().optional(),
|
|
39
|
+
name: externalStepNameSchema,
|
|
40
|
+
start: timestamp,
|
|
41
|
+
end: timestamp,
|
|
42
|
+
status: z.enum(['ok', 'error']),
|
|
43
|
+
error: z.object({
|
|
44
|
+
name: z.string().optional(),
|
|
45
|
+
message: z.string(),
|
|
46
|
+
stack: z.string().optional(),
|
|
47
|
+
}).optional(),
|
|
48
|
+
capture: z.unknown().optional(),
|
|
49
|
+
touches: z.array(z.string().regex(/^[A-Za-z_][A-Za-z0-9_.]*:[A-Za-z_][A-Za-z0-9_-]*$/)).optional(),
|
|
50
|
+
}).refine(
|
|
51
|
+
r => parseReportTime(r.end)! >= parseReportTime(r.start)!,
|
|
52
|
+
{ message: 'end is before start' },
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
/** One step reported by an outside process. */
|
|
56
|
+
export type ExternalStepReport = z.infer<typeof externalStepReportSchema>
|
|
57
|
+
|
|
58
|
+
/** What `trace.record` did with a report. */
|
|
59
|
+
export interface ExternalRecordResult {
|
|
60
|
+
/** Steps staged into the trace. */
|
|
61
|
+
readonly recorded: number
|
|
62
|
+
/** Reports skipped as malformed, duplicate, time-inverted or cyclic. */
|
|
63
|
+
readonly invalid: number
|
|
64
|
+
/** Valid reports not recorded: over `trace.maxExternalSteps`, or the trace is truncated. */
|
|
65
|
+
readonly dropped: number
|
|
66
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { env, event, record, traceparent } from './external/record.js'
|
|
2
|
+
import { tree } from './read/tree.js'
|
|
3
|
+
|
|
4
|
+
/** Trace namespace — hand the current trace to a process outside Node, record the steps it reports back, mark point events, and read a trace tree. */
|
|
5
|
+
export const trace = {
|
|
6
|
+
/** W3C `traceparent` for the current trace and open step; `null` outside a traced flow or when the trace is truncated. */
|
|
7
|
+
traceparent,
|
|
8
|
+
/** `{ TRACEPARENT }` for a child process's environment, or `{}` when there is no traceparent. */
|
|
9
|
+
env,
|
|
10
|
+
/** Records steps reported as JSON lines (or objects) by an outside process under the step open now. Synchronous; never throws. */
|
|
11
|
+
record,
|
|
12
|
+
/** Records one zero-length step `external:<name>` under the step open now, with an optional `capture` and `error`. A no-op outside a traced flow; never throws. */
|
|
13
|
+
event,
|
|
14
|
+
/**
|
|
15
|
+
* Reads every hot trace sharing `rootId` (a request's trace and the jobs it queued) with their steps. Runs on the
|
|
16
|
+
* given `db`, so tenant policies apply: only an unrestricted connection (table owner, tests) sees every tenant's rows.
|
|
17
|
+
*/
|
|
18
|
+
tree,
|
|
19
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// Day arithmetic for trace partitioning. Trace tables are RANGE-partitioned by
|
|
2
|
+
// a `date` column, one partition per calendar day, reckoned in UTC — the same
|
|
3
|
+
// frame the `date` column stores. All helpers take and return `YYYY-MM-DD`.
|
|
4
|
+
|
|
5
|
+
const DAY_PATTERN = /^\d{4}-\d{2}-\d{2}$/
|
|
6
|
+
const MS_PER_DAY = 86_400_000
|
|
7
|
+
|
|
8
|
+
/** The UTC calendar day of an instant, as `YYYY-MM-DD`. */
|
|
9
|
+
export function toDay(date: Date): string {
|
|
10
|
+
return date.toISOString().slice(0, 10)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function parseDay(day: string): number {
|
|
14
|
+
if (!DAY_PATTERN.test(day)) {
|
|
15
|
+
throw new Error(
|
|
16
|
+
`[pipework] Invalid day "${day}" — expected YYYY-MM-DD.\n\n` +
|
|
17
|
+
' Trace partition days are UTC calendar dates with no time component.\n'
|
|
18
|
+
)
|
|
19
|
+
}
|
|
20
|
+
const ms = Date.parse(`${day}T00:00:00.000Z`)
|
|
21
|
+
// Date.parse rolls overflow (`2026-02-30` → March 2) instead of failing, so a
|
|
22
|
+
// well-formed string that isn't a real calendar date round-trips to a
|
|
23
|
+
// different day. Reject those rather than silently shifting the partition.
|
|
24
|
+
if (Number.isNaN(ms) || new Date(ms).toISOString().slice(0, 10) !== day) {
|
|
25
|
+
throw new Error(
|
|
26
|
+
`[pipework] Invalid day "${day}" — well-formed but not a real date.\n\n` +
|
|
27
|
+
' Check the month and day are within range.\n'
|
|
28
|
+
)
|
|
29
|
+
}
|
|
30
|
+
return ms
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** The day `n` days after `day` (negative `n` moves backward). */
|
|
34
|
+
export function addDays(day: string, n: number): string {
|
|
35
|
+
return toDay(new Date(parseDay(day) + n * MS_PER_DAY))
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Every day in `[from, to]` inclusive, ascending. Empty when `from > to`. */
|
|
39
|
+
export function dayRange(from: string, to: string): string[] {
|
|
40
|
+
const start = parseDay(from)
|
|
41
|
+
const end = parseDay(to)
|
|
42
|
+
const days: string[] = []
|
|
43
|
+
for (let ms = start; ms <= end; ms += MS_PER_DAY) {
|
|
44
|
+
days.push(toDay(new Date(ms)))
|
|
45
|
+
}
|
|
46
|
+
return days
|
|
47
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export { syncTracePartitions } from './sync.js'
|
|
2
|
+
export type { PartitionSyncSummary } from './sync.js'
|
|
3
|
+
export { planPartitions } from './plan.js'
|
|
4
|
+
export type { TracePartitionConfig, DayPartition, PartitionPlan, PartitionPlanInput } from './plan.js'
|
|
5
|
+
export { PARTITIONED_TRACE_TABLES } from './tables.js'
|
|
6
|
+
export type { PartitionedTraceTable, RetentionClass } from './tables.js'
|
|
7
|
+
export { partitionName, parsePartitionDay } from './partition-name.js'
|
|
8
|
+
export { toDay, addDays, dayRange } from './day.js'
|
|
9
|
+
export { createTracePartitionStartupHook, TRACE_PARTITION_HOOK_NAME } from './startup-hook.js'
|
|
10
|
+
export {
|
|
11
|
+
scheduleTracePartitionMaintenance,
|
|
12
|
+
TRACE_PARTITION_MAINTENANCE_JOB,
|
|
13
|
+
TRACE_PARTITION_MAINTENANCE_CRON,
|
|
14
|
+
} from './schedule.js'
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// The naming convention that ties a day partition back to its parent table and
|
|
2
|
+
// calendar day. `trace` + `2026-05-14` ⇄ `trace_p20260514`. The `_p` separator
|
|
3
|
+
// plus a fixed 8-digit date keeps real table names (`trace_step`,
|
|
4
|
+
// `trace_retained`) from ever parsing as partitions of `trace`.
|
|
5
|
+
|
|
6
|
+
const DIGITS_PATTERN = /^\d{8}$/
|
|
7
|
+
|
|
8
|
+
/** The partition table name for `parent`'s slice covering `day` (`YYYY-MM-DD`). */
|
|
9
|
+
export function partitionName(parent: string, day: string): string {
|
|
10
|
+
return `${parent}_p${day.replace(/-/g, '')}`
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* The calendar day a partition name encodes, or `null` when `name` is not a day
|
|
15
|
+
* partition of `parent`. Used to tell which catalog children are ours to manage.
|
|
16
|
+
*/
|
|
17
|
+
export function parsePartitionDay(parent: string, name: string): string | null {
|
|
18
|
+
const prefix = `${parent}_p`
|
|
19
|
+
if (!name.startsWith(prefix)) return null
|
|
20
|
+
const digits = name.slice(prefix.length)
|
|
21
|
+
if (!DIGITS_PATTERN.test(digits)) return null
|
|
22
|
+
return `${digits.slice(0, 4)}-${digits.slice(4, 6)}-${digits.slice(6, 8)}`
|
|
23
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
// The pure core of partition maintenance: given the clock, the retention
|
|
2
|
+
// windows, and the partitions that currently exist, decide which day
|
|
3
|
+
// partitions to create and which to drop. No database access — every decision
|
|
4
|
+
// here is a function of its inputs, so the whole policy is unit-testable.
|
|
5
|
+
|
|
6
|
+
import { PARTITIONED_TRACE_TABLES, type RetentionClass } from './tables.js'
|
|
7
|
+
import { partitionName, parsePartitionDay } from './partition-name.js'
|
|
8
|
+
import { toDay, addDays, dayRange } from './day.js'
|
|
9
|
+
|
|
10
|
+
export interface TracePartitionConfig {
|
|
11
|
+
/** Days of hot partitions kept for `trace` / `trace_step`. */
|
|
12
|
+
readonly hotRetentionDays: number
|
|
13
|
+
/** Days of cold partitions kept for `trace_retained` / `trace_step_retained`. */
|
|
14
|
+
readonly errorRetentionDays: number
|
|
15
|
+
/** Days of audit partitions kept for `audit_record` — independent of trace storage. `null` keeps them all: none is dropped and none is created for the past. */
|
|
16
|
+
readonly auditRetentionDays: number | null
|
|
17
|
+
/** Days of partitions pre-created ahead of today so writes never race creation. */
|
|
18
|
+
readonly partitionLeadDays: number
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** A single day partition: which parent it belongs to, its name, and its day. */
|
|
22
|
+
export interface DayPartition {
|
|
23
|
+
readonly parent: string
|
|
24
|
+
readonly name: string
|
|
25
|
+
readonly day: string
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface PartitionPlan {
|
|
29
|
+
readonly create: readonly DayPartition[]
|
|
30
|
+
readonly drop: readonly DayPartition[]
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface PartitionPlanInput {
|
|
34
|
+
readonly now: Date
|
|
35
|
+
readonly config: TracePartitionConfig
|
|
36
|
+
/** Existing child partition names per parent table, as found in the catalog. */
|
|
37
|
+
readonly existing: ReadonlyMap<string, ReadonlySet<string>>
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Reconcile desired partitions against existing ones.
|
|
42
|
+
*
|
|
43
|
+
* For each table the desired set spans `[today - retentionDays, today + leadDays]`.
|
|
44
|
+
* `create` is every desired day with no partition yet. `drop` is every existing
|
|
45
|
+
* partition whose day has fallen before the retention cutoff — retention is the
|
|
46
|
+
* dropping of day partitions, never a DELETE. Catalog children that don't parse
|
|
47
|
+
* as our day partitions are left untouched.
|
|
48
|
+
*/
|
|
49
|
+
export function planPartitions(input: PartitionPlanInput): PartitionPlan {
|
|
50
|
+
const { config } = input
|
|
51
|
+
const today = toDay(input.now)
|
|
52
|
+
const create: DayPartition[] = []
|
|
53
|
+
const drop: DayPartition[] = []
|
|
54
|
+
|
|
55
|
+
for (const { parent, retention } of PARTITIONED_TRACE_TABLES) {
|
|
56
|
+
const retentionDays = retentionDaysFor(retention, config)
|
|
57
|
+
const keep = retentionDays === null
|
|
58
|
+
// Kept partitions are never dropped, and none is back-filled beyond yesterday.
|
|
59
|
+
const cutoff = addDays(today, keep ? -1 : -retentionDays)
|
|
60
|
+
const existing = input.existing.get(parent) ?? new Set<string>()
|
|
61
|
+
|
|
62
|
+
for (const day of dayRange(cutoff, addDays(today, config.partitionLeadDays))) {
|
|
63
|
+
const name = partitionName(parent, day)
|
|
64
|
+
if (!existing.has(name)) create.push({ parent, name, day })
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
for (const name of existing) {
|
|
68
|
+
const day = parsePartitionDay(parent, name)
|
|
69
|
+
if (!keep && day !== null && day < cutoff) drop.push({ parent, name, day })
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return { create, drop }
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function retentionDaysFor(retention: RetentionClass, config: TracePartitionConfig): number | null {
|
|
77
|
+
switch (retention) {
|
|
78
|
+
case 'hot': return config.hotRetentionDays
|
|
79
|
+
case 'cold': return config.errorRetentionDays
|
|
80
|
+
case 'audit': return config.auditRetentionDays
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// Registers trace maintenance as a recurring job on the cron scheduler. The
|
|
2
|
+
// startup hook covers the boot moment; this keeps things rolling forward for a
|
|
3
|
+
// long-lived process. The consumer's worker dispatch routes
|
|
4
|
+
// `TRACE_PARTITION_MAINTENANCE_JOB` to a body that runs both `syncTracePartitions`
|
|
5
|
+
// (rolls day partitions forward, drops retired ones) and `expireStaleTraces`
|
|
6
|
+
// (force-closes orphan traces past their hot retention window — Q7).
|
|
7
|
+
|
|
8
|
+
import type { DB } from '../../data/db/index.js'
|
|
9
|
+
|
|
10
|
+
export const TRACE_PARTITION_MAINTENANCE_JOB = 'pipework.trace.partition-maintenance'
|
|
11
|
+
|
|
12
|
+
/** Daily at 01:10 UTC — past the midnight day rollover, before business hours. */
|
|
13
|
+
export const TRACE_PARTITION_MAINTENANCE_CRON = '10 1 * * *'
|
|
14
|
+
|
|
15
|
+
/** The scheduler surface this helper needs — structural so it doesn't couple to the job queue. */
|
|
16
|
+
interface SchedulerLike {
|
|
17
|
+
schedule(db: DB, spec: { readonly name: string; readonly cron: string }): Promise<void>
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export async function scheduleTracePartitionMaintenance(
|
|
21
|
+
scheduler: SchedulerLike,
|
|
22
|
+
db: DB,
|
|
23
|
+
cron: string = TRACE_PARTITION_MAINTENANCE_CRON,
|
|
24
|
+
): Promise<void> {
|
|
25
|
+
await scheduler.schedule(db, { name: TRACE_PARTITION_MAINTENANCE_JOB, cron })
|
|
26
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// Trace maintenance as a startup hook. Running it at boot guarantees the
|
|
2
|
+
// near-term partitions exist before the app takes its first request — a fresh
|
|
3
|
+
// deploy on a new day must not write a trace into a partition that doesn't
|
|
4
|
+
// exist yet — and clears any orphan traces left open by a process that died
|
|
5
|
+
// without closing them (Q7 expiry). The cron schedule keeps both current after
|
|
6
|
+
// that.
|
|
7
|
+
|
|
8
|
+
import type { DB } from '../../data/db/index.js'
|
|
9
|
+
import type { StartupHook } from '../../core/lifecycle/types.js'
|
|
10
|
+
import { syncTracePartitions } from './sync.js'
|
|
11
|
+
import { expireStaleTraces } from '../runtime/expire-stale-traces.js'
|
|
12
|
+
import type { TracePartitionConfig } from './plan.js'
|
|
13
|
+
import { installAuditChain } from '../../audit/chain/install.js'
|
|
14
|
+
import { sql } from '../../data/db/sql.js'
|
|
15
|
+
|
|
16
|
+
export const TRACE_PARTITION_HOOK_NAME = 'trace-partition-maintenance'
|
|
17
|
+
|
|
18
|
+
export interface TracePartitionHookOptions {
|
|
19
|
+
/** Blueprint `audit.chain`: (re)install the audit hash chain before partitions are synced. */
|
|
20
|
+
readonly auditChain?: boolean
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function createTracePartitionStartupHook(
|
|
24
|
+
db: DB,
|
|
25
|
+
config: TracePartitionConfig,
|
|
26
|
+
options: TracePartitionHookOptions = {},
|
|
27
|
+
): StartupHook {
|
|
28
|
+
return {
|
|
29
|
+
name: TRACE_PARTITION_HOOK_NAME,
|
|
30
|
+
async execute(): Promise<void> {
|
|
31
|
+
if (options.auditChain === true) {
|
|
32
|
+
const present = await db.execute(sql`SELECT to_regclass('audit_record') IS NOT NULL AS present`)
|
|
33
|
+
if (present[0]?.['present'] === true) await installAuditChain(db)
|
|
34
|
+
}
|
|
35
|
+
await syncTracePartitions(db, config)
|
|
36
|
+
await expireStaleTraces(db)
|
|
37
|
+
},
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
// The impure half of partition maintenance: read the catalog for partitions
|
|
2
|
+
// that exist, ask the planner what should exist, and apply the difference.
|
|
3
|
+
// Creating and dropping day partitions is idempotent — `IF NOT EXISTS` /
|
|
4
|
+
// `IF EXISTS` make a re-run after a crash a no-op — so this is safe to run on
|
|
5
|
+
// every boot and on a cron without coordination.
|
|
6
|
+
|
|
7
|
+
import { sql } from '../../data/db/sql.js'
|
|
8
|
+
import type { DB } from '../../data/db/index.js'
|
|
9
|
+
import { quoteIdentifier } from '../../data/db/identifiers.js'
|
|
10
|
+
import { getBaseLogger } from '../../core/logging/index.js'
|
|
11
|
+
import { PARTITIONED_TRACE_TABLES, STEP_TABLE_PARENTS } from './tables.js'
|
|
12
|
+
import { planPartitions, type DayPartition, type TracePartitionConfig } from './plan.js'
|
|
13
|
+
import { addDays } from './day.js'
|
|
14
|
+
import { auditRecord } from '../../audit/schema/audit-record.js'
|
|
15
|
+
import { withMaintenance } from '../../data/db/maintenance.js'
|
|
16
|
+
import { retireAuditPartition, auditChainInstalled } from '../../audit/chain/retire.js'
|
|
17
|
+
|
|
18
|
+
export interface PartitionSyncOptions {
|
|
19
|
+
/** Called as each partition's CREATE starts; `index` is 1-based of `total` creations. */
|
|
20
|
+
readonly onPartition?: (name: string, index: number, total: number) => void
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface PartitionSyncSummary {
|
|
24
|
+
readonly created: readonly string[]
|
|
25
|
+
readonly dropped: readonly string[]
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Reconcile the trace tables' day partitions with the retention policy.
|
|
30
|
+
*
|
|
31
|
+
* `now` is injectable so the maintenance window is testable without mocking the
|
|
32
|
+
* clock. `options.onPartition` reports each creation as it starts, so a hung
|
|
33
|
+
* CREATE is named. Returns the names created and dropped so a job runner can
|
|
34
|
+
* record them.
|
|
35
|
+
*/
|
|
36
|
+
export async function syncTracePartitions(
|
|
37
|
+
db: DB,
|
|
38
|
+
config: TracePartitionConfig,
|
|
39
|
+
now: Date = new Date(),
|
|
40
|
+
options: PartitionSyncOptions = {},
|
|
41
|
+
): Promise<PartitionSyncSummary> {
|
|
42
|
+
const existingParents = await readExistingParents(db)
|
|
43
|
+
const existing = await readExistingPartitions(db)
|
|
44
|
+
const plan = planPartitions({ now, config, existing })
|
|
45
|
+
|
|
46
|
+
// A parent pipework never provisioned (e.g. `audit` disabled, so no
|
|
47
|
+
// `audit_record`) has no table to attach a partition to — `CREATE TABLE ...
|
|
48
|
+
// PARTITION OF` it would abort the whole pass. Drop those from the plan so
|
|
49
|
+
// the summary reports only partitions actually applied. Issue #313.
|
|
50
|
+
const toCreate = plan.create.filter(partition => existingParents.has(partition.parent))
|
|
51
|
+
|
|
52
|
+
for (const [i, partition] of toCreate.entries()) {
|
|
53
|
+
options.onPartition?.(partition.name, i + 1, toCreate.length)
|
|
54
|
+
const child = quoteIdentifier(partition.name, 'trace partition')
|
|
55
|
+
const parent = quoteIdentifier(partition.parent, 'trace table')
|
|
56
|
+
// addDays re-validates the day; the bound is a generated YYYY-MM-DD literal.
|
|
57
|
+
const upper = addDays(partition.day, 1)
|
|
58
|
+
await db.execute(sql.raw(
|
|
59
|
+
`CREATE TABLE IF NOT EXISTS ${child} PARTITION OF ${parent} ` +
|
|
60
|
+
`FOR VALUES FROM ('${partition.day}') TO ('${upper}')`,
|
|
61
|
+
))
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const chained = plan.drop.some(p => p.parent === auditRecord.$name) && await auditChainInstalled(db)
|
|
65
|
+
|
|
66
|
+
const traceParents = new Set(STEP_TABLE_PARENTS.values())
|
|
67
|
+
const stepDrops = new Map<string, DayPartition>()
|
|
68
|
+
for (const partition of plan.drop) {
|
|
69
|
+
if (STEP_TABLE_PARENTS.has(partition.parent)) stepDrops.set(`${partition.parent}:${partition.day}`, partition)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
for (const partition of plan.drop) {
|
|
73
|
+
const child = quoteIdentifier(partition.name, 'trace partition')
|
|
74
|
+
if (chained && partition.parent === auditRecord.$name) {
|
|
75
|
+
// Keep the dropped rows' hashes so what remains still links back to them.
|
|
76
|
+
// It reads every tenant's rows, so it runs as a maintenance pass.
|
|
77
|
+
await withMaintenance(db, tx => retireAuditPartition(tx, child))
|
|
78
|
+
continue
|
|
79
|
+
}
|
|
80
|
+
if (STEP_TABLE_PARENTS.has(partition.parent)) continue // dropped with its trace partition's day
|
|
81
|
+
if (traceParents.has(partition.parent)) {
|
|
82
|
+
// The step table's foreign key points at this partition: drop the day's
|
|
83
|
+
// step partition first, then detach the trace partition (Postgres will
|
|
84
|
+
// not drop a referenced partition while attached), all or nothing.
|
|
85
|
+
const stepParent = [...STEP_TABLE_PARENTS].find(([, traceParent]) => traceParent === partition.parent)![0]
|
|
86
|
+
const step = stepDrops.get(`${stepParent}:${partition.day}`)
|
|
87
|
+
const parent = quoteIdentifier(partition.parent, 'trace table')
|
|
88
|
+
await db.transaction(async tx => {
|
|
89
|
+
if (step !== undefined) {
|
|
90
|
+
await tx.execute(sql.raw(`DROP TABLE IF EXISTS ${quoteIdentifier(step.name, 'trace partition')}`))
|
|
91
|
+
stepDrops.delete(`${stepParent}:${partition.day}`)
|
|
92
|
+
}
|
|
93
|
+
await tx.execute(sql.raw(`ALTER TABLE ${parent} DETACH PARTITION ${child}`))
|
|
94
|
+
await tx.execute(sql.raw(`DROP TABLE IF EXISTS ${child}`))
|
|
95
|
+
})
|
|
96
|
+
continue
|
|
97
|
+
}
|
|
98
|
+
await db.execute(sql.raw(`DROP TABLE IF EXISTS ${child}`))
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// A step partition left over has no trace partition planned for its day.
|
|
102
|
+
for (const step of stepDrops.values()) {
|
|
103
|
+
await db.execute(sql.raw(`DROP TABLE IF EXISTS ${quoteIdentifier(step.name, 'trace partition')}`))
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const summary: PartitionSyncSummary = {
|
|
107
|
+
created: toCreate.map(p => p.name),
|
|
108
|
+
dropped: plan.drop.map(p => p.name),
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (summary.created.length > 0 || summary.dropped.length > 0) {
|
|
112
|
+
getBaseLogger().info(
|
|
113
|
+
{ created: summary.created, dropped: summary.dropped },
|
|
114
|
+
'Trace partition maintenance applied',
|
|
115
|
+
)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return summary
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Which of the partitioned trace parents actually exist as partitioned tables.
|
|
123
|
+
* A parent pipework never provisioned — `audit` disabled, or a non-default
|
|
124
|
+
* database — must be skipped: `CREATE TABLE ... PARTITION OF` a missing parent
|
|
125
|
+
* aborts the maintenance pass. Issue #313.
|
|
126
|
+
*/
|
|
127
|
+
async function readExistingParents(db: DB): Promise<Set<string>> {
|
|
128
|
+
const parents = PARTITIONED_TRACE_TABLES.map(t => t.parent)
|
|
129
|
+
const rows = await db.execute(sql`
|
|
130
|
+
SELECT relname FROM pg_class
|
|
131
|
+
WHERE relkind = 'p' AND relname ${sql.inList(parents)}
|
|
132
|
+
`)
|
|
133
|
+
return new Set(rows.map(row => row['relname'] as string))
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
async function readExistingPartitions(db: DB): Promise<Map<string, Set<string>>> {
|
|
137
|
+
const parents = PARTITIONED_TRACE_TABLES.map(t => t.parent)
|
|
138
|
+
const rows = await db.execute(sql`
|
|
139
|
+
SELECT p.relname AS parent, c.relname AS child
|
|
140
|
+
FROM pg_inherits i
|
|
141
|
+
JOIN pg_class c ON c.oid = i.inhrelid
|
|
142
|
+
JOIN pg_class p ON p.oid = i.inhparent
|
|
143
|
+
WHERE p.relname ${sql.inList(parents)}
|
|
144
|
+
`)
|
|
145
|
+
|
|
146
|
+
const byParent = new Map<string, Set<string>>()
|
|
147
|
+
for (const parent of parents) byParent.set(parent, new Set())
|
|
148
|
+
for (const row of rows) {
|
|
149
|
+
const parent = row['parent'] as string
|
|
150
|
+
const child = row['child'] as string
|
|
151
|
+
byParent.get(parent)?.add(child)
|
|
152
|
+
}
|
|
153
|
+
return byParent
|
|
154
|
+
}
|