pipework 0.8.2 → 0.8.4
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/async/jobs/execute.d.ts +2 -0
- package/dist/async/jobs/execute.d.ts.map +1 -1
- package/dist/async/jobs/execute.js +17 -2
- package/dist/async/jobs/execute.js.map +1 -1
- package/dist/audit/query/cursor.d.ts +7 -0
- package/dist/audit/query/cursor.d.ts.map +1 -0
- package/dist/audit/query/cursor.js +25 -0
- package/dist/audit/query/cursor.js.map +1 -0
- package/dist/audit/query/index.d.ts +5 -0
- package/dist/audit/query/index.d.ts.map +1 -0
- package/dist/audit/query/index.js +3 -0
- package/dist/audit/query/index.js.map +1 -0
- package/dist/audit/query/query.d.ts +49 -0
- package/dist/audit/query/query.d.ts.map +1 -0
- package/dist/audit/query/query.js +119 -0
- package/dist/audit/query/query.js.map +1 -0
- package/dist/audit/schema/audit-record-fields.d.ts +15 -0
- package/dist/audit/schema/audit-record-fields.d.ts.map +1 -0
- package/dist/audit/schema/audit-record-fields.js +50 -0
- package/dist/audit/schema/audit-record-fields.js.map +1 -0
- package/dist/audit/schema/audit-record.d.ts +15 -0
- package/dist/audit/schema/audit-record.d.ts.map +1 -0
- package/dist/audit/schema/audit-record.js +12 -0
- package/dist/audit/schema/audit-record.js.map +1 -0
- package/dist/auth/tenant/scope.js +1 -1
- package/dist/auth/tenant/scope.js.map +1 -1
- package/dist/auth/tenant/scoped-db.d.ts.map +1 -1
- package/dist/auth/tenant/scoped-db.js +1 -2
- package/dist/auth/tenant/scoped-db.js.map +1 -1
- package/dist/cli/commands/check.d.ts +4 -0
- package/dist/cli/commands/check.d.ts.map +1 -1
- package/dist/cli/commands/check.js +181 -14
- package/dist/cli/commands/check.js.map +1 -1
- package/dist/cli/commands/codemod.d.ts +6 -0
- package/dist/cli/commands/codemod.d.ts.map +1 -0
- package/dist/cli/commands/codemod.js +63 -0
- package/dist/cli/commands/codemod.js.map +1 -0
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +9 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/core/config/load.d.ts +23 -0
- package/dist/core/config/load.d.ts.map +1 -1
- package/dist/core/config/load.js +8 -0
- package/dist/core/config/load.js.map +1 -1
- package/dist/core/config/namespace.d.ts +19 -0
- package/dist/core/config/namespace.d.ts.map +1 -1
- package/dist/core/config/schema.d.ts +41 -0
- package/dist/core/config/schema.d.ts.map +1 -1
- package/dist/core/config/schema.js +27 -0
- package/dist/core/config/schema.js.map +1 -1
- package/dist/core/logging/logger.d.ts +3 -1
- package/dist/core/logging/logger.d.ts.map +1 -1
- package/dist/core/logging/logger.js +2 -2
- package/dist/core/logging/logger.js.map +1 -1
- package/dist/core/pipework.d.ts +2 -2
- package/dist/core/pipework.d.ts.map +1 -1
- package/dist/data/db/bulk-set.js +1 -1
- package/dist/data/db/bulk-set.js.map +1 -1
- package/dist/data/db/composed.js +1 -1
- package/dist/data/db/composed.js.map +1 -1
- package/dist/data/db/db.d.ts +10 -0
- package/dist/data/db/db.d.ts.map +1 -1
- package/dist/data/db/db.js +12 -0
- package/dist/data/db/db.js.map +1 -1
- package/dist/data/db/filter.d.ts +42 -43
- package/dist/data/db/filter.d.ts.map +1 -1
- package/dist/data/db/filter.js +7 -6
- package/dist/data/db/filter.js.map +1 -1
- package/dist/data/db/namespace.d.ts +90 -82
- package/dist/data/db/namespace.d.ts.map +1 -1
- package/dist/data/db/namespace.js +21 -5
- package/dist/data/db/namespace.js.map +1 -1
- package/dist/data/migrate/post-process.d.ts +2 -1
- package/dist/data/migrate/post-process.d.ts.map +1 -1
- package/dist/data/migrate/post-process.js +2 -1
- package/dist/data/migrate/post-process.js.map +1 -1
- package/dist/data/temporal/definition-queries.js +1 -1
- package/dist/data/temporal/definition-queries.js.map +1 -1
- package/dist/data/temporal/query.js +1 -1
- package/dist/data/temporal/query.js.map +1 -1
- package/dist/data/temporal/resolve.js +1 -1
- package/dist/data/temporal/resolve.js.map +1 -1
- package/dist/data/write/audit/actor.d.ts +3 -0
- package/dist/data/write/audit/actor.d.ts.map +1 -0
- package/dist/data/write/audit/actor.js +15 -0
- package/dist/data/write/audit/actor.js.map +1 -0
- package/dist/data/write/audit/audit-columns.d.ts +6 -0
- package/dist/data/write/audit/audit-columns.d.ts.map +1 -0
- package/dist/data/write/audit/audit-columns.js +15 -0
- package/dist/data/write/audit/audit-columns.js.map +1 -0
- package/dist/data/write/audit/audit-select.d.ts +28 -0
- package/dist/data/write/audit/audit-select.d.ts.map +1 -0
- package/dist/data/write/audit/audit-select.js +49 -0
- package/dist/data/write/audit/audit-select.js.map +1 -0
- package/dist/data/write/audit/cte-compile.d.ts +7 -0
- package/dist/data/write/audit/cte-compile.d.ts.map +1 -0
- package/dist/data/write/audit/cte-compile.js +23 -0
- package/dist/data/write/audit/cte-compile.js.map +1 -0
- package/dist/data/write/audit/final-select.d.ts +4 -0
- package/dist/data/write/audit/final-select.d.ts.map +1 -0
- package/dist/data/write/audit/final-select.js +28 -0
- package/dist/data/write/audit/final-select.js.map +1 -0
- package/dist/data/write/audit/primary-key.d.ts +7 -0
- package/dist/data/write/audit/primary-key.d.ts.map +1 -0
- package/dist/data/write/audit/primary-key.js +32 -0
- package/dist/data/write/audit/primary-key.js.map +1 -0
- package/dist/data/write/audit/projection.d.ts +11 -0
- package/dist/data/write/audit/projection.d.ts.map +1 -0
- package/dist/data/write/audit/projection.js +37 -0
- package/dist/data/write/audit/projection.js.map +1 -0
- package/dist/data/write/audit/read-touches.d.ts +10 -0
- package/dist/data/write/audit/read-touches.d.ts.map +1 -0
- package/dist/data/write/audit/read-touches.js +63 -0
- package/dist/data/write/audit/read-touches.js.map +1 -0
- package/dist/data/write/audit/verb-delete.d.ts +7 -0
- package/dist/data/write/audit/verb-delete.d.ts.map +1 -0
- package/dist/data/write/audit/verb-delete.js +53 -0
- package/dist/data/write/audit/verb-delete.js.map +1 -0
- package/dist/data/write/audit/verb-insert.d.ts +7 -0
- package/dist/data/write/audit/verb-insert.d.ts.map +1 -0
- package/dist/data/write/audit/verb-insert.js +28 -0
- package/dist/data/write/audit/verb-insert.js.map +1 -0
- package/dist/data/write/audit/verb-update.d.ts +7 -0
- package/dist/data/write/audit/verb-update.d.ts.map +1 -0
- package/dist/data/write/audit/verb-update.js +52 -0
- package/dist/data/write/audit/verb-update.js.map +1 -0
- package/dist/data/write/audit/verb-upsert.d.ts +7 -0
- package/dist/data/write/audit/verb-upsert.d.ts.map +1 -0
- package/dist/data/write/audit/verb-upsert.js +81 -0
- package/dist/data/write/audit/verb-upsert.js.map +1 -0
- package/dist/data/write/delete.d.ts +15 -0
- package/dist/data/write/delete.d.ts.map +1 -0
- package/dist/data/write/delete.js +25 -0
- package/dist/data/write/delete.js.map +1 -0
- package/dist/data/write/execute.d.ts +3 -0
- package/dist/data/write/execute.d.ts.map +1 -0
- package/dist/data/write/execute.js +162 -0
- package/dist/data/write/execute.js.map +1 -0
- package/dist/data/write/index.d.ts +6 -0
- package/dist/data/write/index.d.ts.map +1 -0
- package/dist/data/write/index.js +10 -0
- package/dist/data/write/index.js.map +1 -0
- package/dist/data/write/insert.d.ts +18 -0
- package/dist/data/write/insert.d.ts.map +1 -0
- package/dist/data/write/insert.js +29 -0
- package/dist/data/write/insert.js.map +1 -0
- package/dist/data/write/state.d.ts +29 -0
- package/dist/data/write/state.d.ts.map +1 -0
- package/dist/data/write/state.js +9 -0
- package/dist/data/write/state.js.map +1 -0
- package/dist/data/write/update.d.ts +17 -0
- package/dist/data/write/update.d.ts.map +1 -0
- package/dist/data/write/update.js +39 -0
- package/dist/data/write/update.js.map +1 -0
- package/dist/data/write/upsert.d.ts +21 -0
- package/dist/data/write/upsert.d.ts.map +1 -0
- package/dist/data/write/upsert.js +42 -0
- package/dist/data/write/upsert.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/infra/audit/namespace.d.ts +3 -0
- package/dist/infra/audit/namespace.d.ts.map +1 -1
- package/dist/infra/audit/namespace.js +3 -0
- package/dist/infra/audit/namespace.js.map +1 -1
- package/dist/lint/config.d.ts +4 -2
- package/dist/lint/config.d.ts.map +1 -1
- package/dist/lint/config.js +9 -5
- package/dist/lint/config.js.map +1 -1
- package/dist/lint/index.d.ts +2 -1
- package/dist/lint/index.d.ts.map +1 -1
- package/dist/lint/index.js +1 -0
- package/dist/lint/index.js.map +1 -1
- package/dist/lint/resolver.d.ts +16 -0
- package/dist/lint/resolver.d.ts.map +1 -0
- package/dist/lint/resolver.js +61 -0
- package/dist/lint/resolver.js.map +1 -0
- package/dist/request/context/create.d.ts +4 -0
- package/dist/request/context/create.d.ts.map +1 -1
- package/dist/request/context/create.js +3 -0
- package/dist/request/context/create.js.map +1 -1
- package/dist/request/context/types.d.ts +3 -0
- package/dist/request/context/types.d.ts.map +1 -1
- package/dist/request/http/middleware.d.ts.map +1 -1
- package/dist/request/http/middleware.js +30 -0
- package/dist/request/http/middleware.js.map +1 -1
- package/dist/request/http/server.d.ts.map +1 -1
- package/dist/request/http/server.js +6 -1
- package/dist/request/http/server.js.map +1 -1
- package/dist/test/context.d.ts.map +1 -1
- package/dist/test/context.js +43 -2
- package/dist/test/context.js.map +1 -1
- package/dist/test/index.d.ts +1 -1
- package/dist/test/index.d.ts.map +1 -1
- package/dist/test/index.js +1 -1
- package/dist/test/index.js.map +1 -1
- package/dist/test/vitest-workspace.d.ts +15 -2
- package/dist/test/vitest-workspace.d.ts.map +1 -1
- package/dist/test/vitest-workspace.js +60 -7
- package/dist/test/vitest-workspace.js.map +1 -1
- package/dist/trace/cardinality/analyze.d.ts +26 -0
- package/dist/trace/cardinality/analyze.d.ts.map +1 -0
- package/dist/trace/cardinality/analyze.js +129 -0
- package/dist/trace/cardinality/analyze.js.map +1 -0
- package/dist/trace/cardinality/branch-context.d.ts +13 -0
- package/dist/trace/cardinality/branch-context.d.ts.map +1 -0
- package/dist/trace/cardinality/branch-context.js +182 -0
- package/dist/trace/cardinality/branch-context.js.map +1 -0
- package/dist/trace/cardinality/check.d.ts +4 -0
- package/dist/trace/cardinality/check.d.ts.map +1 -0
- package/dist/trace/cardinality/check.js +29 -0
- package/dist/trace/cardinality/check.js.map +1 -0
- package/dist/trace/cardinality/classify-edge.d.ts +31 -0
- package/dist/trace/cardinality/classify-edge.d.ts.map +1 -0
- package/dist/trace/cardinality/classify-edge.js +27 -0
- package/dist/trace/cardinality/classify-edge.js.map +1 -0
- package/dist/trace/cardinality/structural-max.d.ts +14 -0
- package/dist/trace/cardinality/structural-max.d.ts.map +1 -0
- package/dist/trace/cardinality/structural-max.js +83 -0
- package/dist/trace/cardinality/structural-max.js.map +1 -0
- package/dist/trace/check/coverage-check.d.ts +20 -0
- package/dist/trace/check/coverage-check.d.ts.map +1 -0
- package/dist/trace/check/coverage-check.js +146 -0
- package/dist/trace/check/coverage-check.js.map +1 -0
- package/dist/trace/check/flow-check.d.ts +13 -0
- package/dist/trace/check/flow-check.d.ts.map +1 -0
- package/dist/trace/check/flow-check.js +82 -0
- package/dist/trace/check/flow-check.js.map +1 -0
- package/dist/trace/check/flow-step-site.d.ts +62 -0
- package/dist/trace/check/flow-step-site.d.ts.map +1 -0
- package/dist/trace/check/flow-step-site.js +142 -0
- package/dist/trace/check/flow-step-site.js.map +1 -0
- package/dist/trace/check/io-pattern.d.ts +14 -0
- package/dist/trace/check/io-pattern.d.ts.map +1 -0
- package/dist/trace/check/io-pattern.js +113 -0
- package/dist/trace/check/io-pattern.js.map +1 -0
- package/dist/trace/codemod/add-cardinality.d.ts +38 -0
- package/dist/trace/codemod/add-cardinality.d.ts.map +1 -0
- package/dist/trace/codemod/add-cardinality.js +106 -0
- package/dist/trace/codemod/add-cardinality.js.map +1 -0
- package/dist/trace/codemod/run.d.ts +25 -0
- package/dist/trace/codemod/run.d.ts.map +1 -0
- package/dist/trace/codemod/run.js +47 -0
- package/dist/trace/codemod/run.js.map +1 -0
- package/dist/trace/entry/close-trace.d.ts +7 -0
- package/dist/trace/entry/close-trace.d.ts.map +1 -0
- package/dist/trace/entry/close-trace.js +54 -0
- package/dist/trace/entry/close-trace.js.map +1 -0
- package/dist/trace/entry/finalize-trace.d.ts +5 -0
- package/dist/trace/entry/finalize-trace.d.ts.map +1 -0
- package/dist/trace/entry/finalize-trace.js +23 -0
- package/dist/trace/entry/finalize-trace.js.map +1 -0
- package/dist/trace/entry/open-trace.d.ts +26 -0
- package/dist/trace/entry/open-trace.d.ts.map +1 -0
- package/dist/trace/entry/open-trace.js +75 -0
- package/dist/trace/entry/open-trace.js.map +1 -0
- package/dist/trace/entry/parent-trace.d.ts +4 -0
- package/dist/trace/entry/parent-trace.d.ts.map +1 -0
- package/dist/trace/entry/parent-trace.js +10 -0
- package/dist/trace/entry/parent-trace.js.map +1 -0
- package/dist/trace/entry/parse-traceparent.d.ts +8 -0
- package/dist/trace/entry/parse-traceparent.d.ts.map +1 -0
- package/dist/trace/entry/parse-traceparent.js +37 -0
- package/dist/trace/entry/parse-traceparent.js.map +1 -0
- package/dist/trace/entry/trace-context.d.ts +19 -0
- package/dist/trace/entry/trace-context.d.ts.map +1 -0
- package/dist/trace/entry/trace-context.js +5 -0
- package/dist/trace/entry/trace-context.js.map +1 -0
- package/dist/trace/entry/tracing-enabled.d.ts +4 -0
- package/dist/trace/entry/tracing-enabled.d.ts.map +1 -0
- package/dist/trace/entry/tracing-enabled.js +8 -0
- package/dist/trace/entry/tracing-enabled.js.map +1 -0
- package/dist/trace/partition/plan.d.ts +2 -0
- package/dist/trace/partition/plan.d.ts.map +1 -1
- package/dist/trace/partition/plan.js +8 -1
- package/dist/trace/partition/plan.js.map +1 -1
- package/dist/trace/partition/schedule.d.ts.map +1 -1
- package/dist/trace/partition/schedule.js +6 -4
- package/dist/trace/partition/schedule.js.map +1 -1
- package/dist/trace/partition/startup-hook.d.ts.map +1 -1
- package/dist/trace/partition/startup-hook.js +6 -2
- package/dist/trace/partition/startup-hook.js.map +1 -1
- package/dist/trace/partition/tables.d.ts +1 -1
- package/dist/trace/partition/tables.d.ts.map +1 -1
- package/dist/trace/partition/tables.js +13 -5
- package/dist/trace/partition/tables.js.map +1 -1
- package/dist/trace/runtime/expire-stale-traces.d.ts +10 -0
- package/dist/trace/runtime/expire-stale-traces.d.ts.map +1 -0
- package/dist/trace/runtime/expire-stale-traces.js +36 -0
- package/dist/trace/runtime/expire-stale-traces.js.map +1 -0
- package/dist/trace/runtime/flow-step.d.ts +6 -0
- package/dist/trace/runtime/flow-step.d.ts.map +1 -0
- package/dist/trace/runtime/flow-step.js +122 -0
- package/dist/trace/runtime/flow-step.js.map +1 -0
- package/dist/trace/runtime/flush-steps.d.ts +5 -0
- package/dist/trace/runtime/flush-steps.d.ts.map +1 -0
- package/dist/trace/runtime/flush-steps.js +23 -0
- package/dist/trace/runtime/flush-steps.js.map +1 -0
- package/dist/trace/runtime/step-buffer.d.ts +139 -0
- package/dist/trace/runtime/step-buffer.d.ts.map +1 -0
- package/dist/trace/runtime/step-buffer.js +140 -0
- package/dist/trace/runtime/step-buffer.js.map +1 -0
- package/dist/trace/runtime/step-error.d.ts +8 -0
- package/dist/trace/runtime/step-error.d.ts.map +1 -0
- package/dist/trace/runtime/step-error.js +13 -0
- package/dist/trace/runtime/step-error.js.map +1 -0
- package/dist/trace/runtime/step-meta.d.ts +48 -0
- package/dist/trace/runtime/step-meta.d.ts.map +1 -0
- package/dist/trace/runtime/step-meta.js +6 -0
- package/dist/trace/runtime/step-meta.js.map +1 -0
- package/dist/trace/runtime/thenable.d.ts +3 -0
- package/dist/trace/runtime/thenable.d.ts.map +1 -0
- package/dist/trace/runtime/thenable.js +12 -0
- package/dist/trace/runtime/thenable.js.map +1 -0
- package/dist/trace/runtime/truncate-trace.d.ts +5 -0
- package/dist/trace/runtime/truncate-trace.d.ts.map +1 -0
- package/dist/trace/runtime/truncate-trace.js +44 -0
- package/dist/trace/runtime/truncate-trace.js.map +1 -0
- package/dist/trace/schema/trace-fields.d.ts +1 -1
- package/dist/trace/schema/trace-retained.d.ts +1 -1
- package/dist/trace/schema/trace.d.ts +1 -1
- package/dist/trace/transformer/build-meta-literal.d.ts +3 -0
- package/dist/trace/transformer/build-meta-literal.d.ts.map +1 -0
- package/dist/trace/transformer/build-meta-literal.js +8 -0
- package/dist/trace/transformer/build-meta-literal.js.map +1 -0
- package/dist/trace/transformer/find-flow-step-sites.d.ts +24 -0
- package/dist/trace/transformer/find-flow-step-sites.d.ts.map +1 -0
- package/dist/trace/transformer/find-flow-step-sites.js +69 -0
- package/dist/trace/transformer/find-flow-step-sites.js.map +1 -0
- package/dist/trace/transformer/inject-flow-step-import.d.ts +3 -0
- package/dist/trace/transformer/inject-flow-step-import.d.ts.map +1 -0
- package/dist/trace/transformer/inject-flow-step-import.js +28 -0
- package/dist/trace/transformer/inject-flow-step-import.js.map +1 -0
- package/dist/trace/transformer/transform-error.d.ts +8 -0
- package/dist/trace/transformer/transform-error.d.ts.map +1 -0
- package/dist/trace/transformer/transform-error.js +5 -0
- package/dist/trace/transformer/transform-error.js.map +1 -0
- package/dist/trace/transformer/transform-flow-steps.d.ts +12 -0
- package/dist/trace/transformer/transform-flow-steps.d.ts.map +1 -0
- package/dist/trace/transformer/transform-flow-steps.js +85 -0
- package/dist/trace/transformer/transform-flow-steps.js.map +1 -0
- package/dist/trace/transformer/wrap-function-declaration.d.ts +3 -0
- package/dist/trace/transformer/wrap-function-declaration.d.ts.map +1 -0
- package/dist/trace/transformer/wrap-function-declaration.js +28 -0
- package/dist/trace/transformer/wrap-function-declaration.js.map +1 -0
- package/dist/trace/transformer/wrap-variable-declaration.d.ts +3 -0
- package/dist/trace/transformer/wrap-variable-declaration.d.ts.map +1 -0
- package/dist/trace/transformer/wrap-variable-declaration.js +30 -0
- package/dist/trace/transformer/wrap-variable-declaration.js.map +1 -0
- package/dist/workspace/generate.d.ts.map +1 -1
- package/dist/workspace/generate.js +5 -8
- package/dist/workspace/generate.js.map +1 -1
- package/package.json +21 -6
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// Primary-key resolution for the audit CTE generator. The UPDATE / DELETE /
|
|
2
|
+
// UPSERT verbs need a key to join the `before` snapshot with the post-image
|
|
3
|
+
// (`updated` / `deleted` / `upserted`) so the audit row pairs the right
|
|
4
|
+
// pre/post pair per affected db row. We honor the two ways a `pipe.define()`
|
|
5
|
+
// table declares its key: composite via the `primaryKey` option, or single
|
|
6
|
+
// via a field-level `.primaryKey()`.
|
|
7
|
+
/**
|
|
8
|
+
* Returns the primary-key field keys in declaration order. Throws when the
|
|
9
|
+
* table declares no primary key — the audit CTE needs one to pair rows.
|
|
10
|
+
*/
|
|
11
|
+
export function primaryKeyFieldKeys(
|
|
12
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- DefinedTable's field generics erase at this boundary; we only read $fields and $options
|
|
13
|
+
table) {
|
|
14
|
+
const composite = table.$options.primaryKey;
|
|
15
|
+
if (composite !== undefined && composite.length > 0)
|
|
16
|
+
return composite;
|
|
17
|
+
const fields = table.$fields;
|
|
18
|
+
const single = [];
|
|
19
|
+
for (const [key, descriptor] of Object.entries(fields)) {
|
|
20
|
+
if (descriptor._meta.primaryKey)
|
|
21
|
+
single.push(key);
|
|
22
|
+
}
|
|
23
|
+
if (single.length === 0) {
|
|
24
|
+
throw new Error(`[pipework] Cannot emit audit CTE for "${table.$name}": ` +
|
|
25
|
+
`the table declares no primary key.\n\n` +
|
|
26
|
+
` An audit-emitting write needs a primary key so the pre-image\n` +
|
|
27
|
+
` and post-image rows can be paired. Add .primaryKey() to a field\n` +
|
|
28
|
+
` or pass { primaryKey: ['col1', 'col2'] } to the table options.\n`);
|
|
29
|
+
}
|
|
30
|
+
return single;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=primary-key.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"primary-key.js","sourceRoot":"","sources":["../../../../src/data/write/audit/primary-key.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,4EAA4E;AAC5E,wEAAwE;AACxE,6EAA6E;AAC7E,2EAA2E;AAC3E,qCAAqC;AAIrC;;;GAGG;AACH,MAAM,UAAU,mBAAmB;AACjC,yJAAyJ;AACzJ,KAAwB;IAExB,MAAM,SAAS,GAAG,KAAK,CAAC,QAAQ,CAAC,UAAU,CAAA;IAC3C,IAAI,SAAS,KAAK,SAAS,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,SAAS,CAAA;IACrE,MAAM,MAAM,GAAG,KAAK,CAAC,OAAsB,CAAA;IAC3C,MAAM,MAAM,GAAa,EAAE,CAAA;IAC3B,KAAK,MAAM,CAAC,GAAG,EAAE,UAAU,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QACvD,IAAI,UAAU,CAAC,KAAK,CAAC,UAAU;YAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IACnD,CAAC;IACD,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CACb,yCAAyC,KAAK,CAAC,KAAK,KAAK;YACzD,wCAAwC;YACxC,kEAAkE;YAClE,qEAAqE;YACrE,oEAAoE,CACrE,CAAA;IACH,CAAC;IACD,OAAO,MAAM,CAAA;AACf,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { SQL } from '../../db/types.js';
|
|
2
|
+
import type { DefinedTable } from '../../domain/types.js';
|
|
3
|
+
/**
|
|
4
|
+
* `jsonb_build_object('field', "source"."column", ...)` over each trace-safe
|
|
5
|
+
* field, qualified by `sourceAlias` (the CTE name the column resolves through:
|
|
6
|
+
* `before` for the pre-image, `updated` / `deleted` / `inserted` / `upserted`
|
|
7
|
+
* for the post-image). Returns `null` when the table has no trace-safe fields
|
|
8
|
+
* — the caller substitutes `NULL::jsonb`.
|
|
9
|
+
*/
|
|
10
|
+
export declare function buildTraceSafeProjection(table: DefinedTable<any>, sourceAlias: string): SQL | null;
|
|
11
|
+
//# sourceMappingURL=projection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"projection.d.ts","sourceRoot":"","sources":["../../../../src/data/write/audit/projection.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,mBAAmB,CAAA;AAC5C,OAAO,KAAK,EAAE,YAAY,EAAe,MAAM,uBAAuB,CAAA;AAEtE;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAEtC,KAAK,EAAE,YAAY,CAAC,GAAG,CAAC,EACxB,WAAW,EAAE,MAAM,GAClB,GAAG,GAAG,IAAI,CAUZ"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// Server-side `.traceSafe()` projection. Builds a `jsonb_build_object(...)`
|
|
2
|
+
// fragment from the table's `traceSafeKeys` so the audit row's `before` /
|
|
3
|
+
// `after` columns are populated entirely inside the SQL — never by a
|
|
4
|
+
// round-trip to the runtime. The projection is fail-closed: only fields
|
|
5
|
+
// explicitly marked `.traceSafe()` participate; a new field is never
|
|
6
|
+
// audited by accident.
|
|
7
|
+
//
|
|
8
|
+
// `null` is returned when the table has no trace-safe fields. The verb
|
|
9
|
+
// composers turn that into `NULL::jsonb` in the audit row, so a write
|
|
10
|
+
// against an unprojectable table still emits an audit row — just with
|
|
11
|
+
// empty before/after halves.
|
|
12
|
+
import { sql } from '../../db/sql.js';
|
|
13
|
+
import { quoteIdentifier } from '../../db/identifiers.js';
|
|
14
|
+
import { camelToSnake } from '../../domain/naming.js';
|
|
15
|
+
import { traceSafeKeys } from '../../domain/trace-safe-projection.js';
|
|
16
|
+
/**
|
|
17
|
+
* `jsonb_build_object('field', "source"."column", ...)` over each trace-safe
|
|
18
|
+
* field, qualified by `sourceAlias` (the CTE name the column resolves through:
|
|
19
|
+
* `before` for the pre-image, `updated` / `deleted` / `inserted` / `upserted`
|
|
20
|
+
* for the post-image). Returns `null` when the table has no trace-safe fields
|
|
21
|
+
* — the caller substitutes `NULL::jsonb`.
|
|
22
|
+
*/
|
|
23
|
+
export function buildTraceSafeProjection(
|
|
24
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- DefinedTable's field generics erase at this boundary; we only read $fields
|
|
25
|
+
table, sourceAlias) {
|
|
26
|
+
const keys = traceSafeKeys(table.$fields);
|
|
27
|
+
if (keys.length === 0)
|
|
28
|
+
return null;
|
|
29
|
+
const aliasIdent = quoteIdentifier(sourceAlias, 'CTE alias');
|
|
30
|
+
const parts = [];
|
|
31
|
+
for (const key of keys) {
|
|
32
|
+
const col = quoteIdentifier(camelToSnake(key), 'trace-safe column');
|
|
33
|
+
parts.push(sql.raw(`'${key}', ${aliasIdent}.${col}`));
|
|
34
|
+
}
|
|
35
|
+
return sql `jsonb_build_object(${sql.join(parts, sql.raw(', '))})`;
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=projection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"projection.js","sourceRoot":"","sources":["../../../../src/data/write/audit/projection.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,0EAA0E;AAC1E,qEAAqE;AACrE,wEAAwE;AACxE,qEAAqE;AACrE,uBAAuB;AACvB,EAAE;AACF,uEAAuE;AACvE,sEAAsE;AACtE,sEAAsE;AACtE,6BAA6B;AAE7B,OAAO,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAA;AACrC,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAA;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAA;AACrD,OAAO,EAAE,aAAa,EAAE,MAAM,uCAAuC,CAAA;AAIrE;;;;;;GAMG;AACH,MAAM,UAAU,wBAAwB;AACtC,4IAA4I;AAC5I,KAAwB,EACxB,WAAmB;IAEnB,MAAM,IAAI,GAAG,aAAa,CAAC,KAAK,CAAC,OAAsB,CAAC,CAAA;IACxD,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAA;IAClC,MAAM,UAAU,GAAG,eAAe,CAAC,WAAW,EAAE,WAAW,CAAC,CAAA;IAC5D,MAAM,KAAK,GAAU,EAAE,CAAA;IACvB,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,MAAM,GAAG,GAAG,eAAe,CAAC,YAAY,CAAC,GAAG,CAAC,EAAE,mBAAmB,CAAC,CAAA;QACnE,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,GAAG,MAAM,UAAU,IAAI,GAAG,EAAE,CAAC,CAAC,CAAA;IACvD,CAAC;IACD,OAAO,GAAG,CAAA,sBAAsB,GAAG,CAAC,IAAI,CAAC,KAAK,EAAE,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,CAAA;AACnE,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Flow } from '../../../request/context/types.js';
|
|
2
|
+
import type { BegunStep } from '../../../trace/runtime/step-buffer.js';
|
|
3
|
+
/**
|
|
4
|
+
* Emits one `audit_record` row per `@touches` target for a completed step
|
|
5
|
+
* that performed no write. Resolves when the insert settles. Failures are
|
|
6
|
+
* logged at error level and swallowed — an audit-emit failure must not
|
|
7
|
+
* propagate to the user-visible step outcome (the step already succeeded).
|
|
8
|
+
*/
|
|
9
|
+
export declare function emitReadTouches(flow: Flow, begun: BegunStep): Promise<void>;
|
|
10
|
+
//# sourceMappingURL=read-touches.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"read-touches.d.ts","sourceRoot":"","sources":["../../../../src/data/write/audit/read-touches.ts"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,mCAAmC,CAAA;AAC7D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uCAAuC,CAAA;AAiBtE;;;;;GAKG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAmCjF"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// Read-touches audit emission. A flow_step that declared `@touches Entity:verb`
|
|
2
|
+
// but performed no `pipe.*` write is recording a read, not a mutation: the step
|
|
3
|
+
// observed the entity in service of the request, and the audit substrate must
|
|
4
|
+
// record that observation for compliance and trail-reconstruction. The
|
|
5
|
+
// CTE-form write path already covered the write case (`wroteForAudit === true`
|
|
6
|
+
// flips on `markWrote()`); this module covers the complement.
|
|
7
|
+
//
|
|
8
|
+
// Atomicity: the emit goes through `pipeRaw()` so a bound transaction on the
|
|
9
|
+
// active Flow carries the insert. If the caller wrapped the step in
|
|
10
|
+
// `pipe.transaction(...)`, the audit row lands in that transaction; otherwise
|
|
11
|
+
// it's a standalone write. Either way it's not pool-direct (unlike trace_step
|
|
12
|
+
// flush), so a request-level rollback discards the audit too — read-touches is
|
|
13
|
+
// a record of what *actually* happened, not what was attempted.
|
|
14
|
+
//
|
|
15
|
+
// The emit uses raw drizzle (`db.insert(table).values(...)`) rather than
|
|
16
|
+
// `pipe.insert` to bypass the executor's audit branch — going through
|
|
17
|
+
// `pipe.insert(auditRecord)` while the active step has `@touches` would route
|
|
18
|
+
// through `compileAuditWrite` and try to project audit_record itself, which is
|
|
19
|
+
// nonsense.
|
|
20
|
+
import { auditRecord } from '../../../audit/schema/audit-record.js';
|
|
21
|
+
import { pipeRaw } from '../../db/db.js';
|
|
22
|
+
import { getBaseLogger } from '../../../core/logging/index.js';
|
|
23
|
+
import { resolveActorId } from './actor.js';
|
|
24
|
+
/**
|
|
25
|
+
* Emits one `audit_record` row per `@touches` target for a completed step
|
|
26
|
+
* that performed no write. Resolves when the insert settles. Failures are
|
|
27
|
+
* logged at error level and swallowed — an audit-emit failure must not
|
|
28
|
+
* propagate to the user-visible step outcome (the step already succeeded).
|
|
29
|
+
*/
|
|
30
|
+
export async function emitReadTouches(flow, begun) {
|
|
31
|
+
if (begun.touches.length === 0 || begun.wroteForAudit)
|
|
32
|
+
return;
|
|
33
|
+
if (flow.trace === null)
|
|
34
|
+
return;
|
|
35
|
+
const traceDay = flow.trace.openedDay;
|
|
36
|
+
const traceId = flow.trace.id;
|
|
37
|
+
const tenantId = flow.tenant;
|
|
38
|
+
const actorId = resolveActorId(flow);
|
|
39
|
+
const capturedInputs = begun.capturedInputs;
|
|
40
|
+
const occurredAt = new Date();
|
|
41
|
+
const rows = begun.touches.map(t => ({
|
|
42
|
+
traceDay,
|
|
43
|
+
traceId,
|
|
44
|
+
traceStepId: begun.stepId,
|
|
45
|
+
tenantId,
|
|
46
|
+
actorId,
|
|
47
|
+
entity: t.entity,
|
|
48
|
+
verb: t.verb,
|
|
49
|
+
before: null,
|
|
50
|
+
after: null,
|
|
51
|
+
capturedInputs,
|
|
52
|
+
occurredAt,
|
|
53
|
+
}));
|
|
54
|
+
try {
|
|
55
|
+
const db = pipeRaw(auditRecord.$options.database);
|
|
56
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Drizzle's insert needs the concrete runtime table type
|
|
57
|
+
await db.insert(auditRecord.table()).values(rows);
|
|
58
|
+
}
|
|
59
|
+
catch (err) {
|
|
60
|
+
getBaseLogger().error({ err, traceId, stepId: begun.stepId, touches: begun.touches.length }, 'Failed to emit read-touches audit row — the audit trail is missing a read observation');
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=read-touches.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"read-touches.js","sourceRoot":"","sources":["../../../../src/data/write/audit/read-touches.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,gFAAgF;AAChF,8EAA8E;AAC9E,uEAAuE;AACvE,+EAA+E;AAC/E,8DAA8D;AAC9D,EAAE;AACF,6EAA6E;AAC7E,oEAAoE;AACpE,8EAA8E;AAC9E,8EAA8E;AAC9E,+EAA+E;AAC/E,gEAAgE;AAChE,EAAE;AACF,yEAAyE;AACzE,sEAAsE;AACtE,8EAA8E;AAC9E,+EAA+E;AAC/E,YAAY;AAEZ,OAAO,EAAE,WAAW,EAAE,MAAM,uCAAuC,CAAA;AACnE,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAA;AACxC,OAAO,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAA;AAG9D,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAgB3C;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,IAAU,EAAE,KAAgB;IAChE,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,CAAC,aAAa;QAAE,OAAM;IAC7D,IAAI,IAAI,CAAC,KAAK,KAAK,IAAI;QAAE,OAAM;IAE/B,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAA;IACrC,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAA;IAC7B,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAA;IAC5B,MAAM,OAAO,GAAG,cAAc,CAAC,IAAI,CAAC,CAAA;IACpC,MAAM,cAAc,GAAG,KAAK,CAAC,cAAc,CAAA;IAC3C,MAAM,UAAU,GAAG,IAAI,IAAI,EAAE,CAAA;IAE7B,MAAM,IAAI,GAAwB,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QACxD,QAAQ;QACR,OAAO;QACP,WAAW,EAAE,KAAK,CAAC,MAAM;QACzB,QAAQ;QACR,OAAO;QACP,MAAM,EAAE,CAAC,CAAC,MAAM;QAChB,IAAI,EAAE,CAAC,CAAC,IAAI;QACZ,MAAM,EAAE,IAAI;QACZ,KAAK,EAAE,IAAI;QACX,cAAc;QACd,UAAU;KACX,CAAC,CAAC,CAAA;IAEH,IAAI,CAAC;QACH,MAAM,EAAE,GAAG,OAAO,CAAC,WAAW,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAA;QACjD,wHAAwH;QACxH,MAAO,EAAU,CAAC,MAAM,CAAC,WAAW,CAAC,KAAK,EAAE,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;IAC5D,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,aAAa,EAAE,CAAC,KAAK,CACnB,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,EACrE,uFAAuF,CACxF,CAAA;IACH,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type AuditSelectContext } from './audit-select.js';
|
|
2
|
+
import type { SQL } from '../../db/types.js';
|
|
3
|
+
import type { WriteState } from '../state.js';
|
|
4
|
+
type AnyDb = any;
|
|
5
|
+
export declare function compileDeleteAudit(db: AnyDb, state: WriteState, ctx: AuditSelectContext): SQL;
|
|
6
|
+
export {};
|
|
7
|
+
//# sourceMappingURL=verb-delete.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verb-delete.d.ts","sourceRoot":"","sources":["../../../../src/data/write/audit/verb-delete.ts"],"names":[],"mappings":"AAYA,OAAO,EAAqB,KAAK,kBAAkB,EAAE,MAAM,mBAAmB,CAAA;AAI9E,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,mBAAmB,CAAA;AAC5C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAG7C,KAAK,KAAK,GAAG,GAAG,CAAA;AAEhB,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,GAAG,EAAE,kBAAkB,GAAG,GAAG,CAuC7F"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// DELETE under audit: snapshots the matched rows into `before`, runs the
|
|
2
|
+
// DELETE into `deleted`, then emits one audit row per deleted db row per
|
|
3
|
+
// `@touches` target. The audit's `after` is always `NULL::jsonb` — there is
|
|
4
|
+
// no post-image for a delete. The statement's snapshot keeps `before` and
|
|
5
|
+
// `deleted` consistent — no lock needed; `FOR UPDATE` inside a CTE conflicts
|
|
6
|
+
// with the sibling DELETE on the same row and silently yields zero rows from
|
|
7
|
+
// `before`, suppressing the audit insert.
|
|
8
|
+
import { sql } from '../../db/sql.js';
|
|
9
|
+
import { quoteIdentifier } from '../../db/identifiers.js';
|
|
10
|
+
import { camelToSnake } from '../../domain/naming.js';
|
|
11
|
+
import { buildTraceSafeProjection } from './projection.js';
|
|
12
|
+
import { buildAuditInserts } from './audit-select.js';
|
|
13
|
+
import { primaryKeyFieldKeys } from './primary-key.js';
|
|
14
|
+
import { AUDIT_COLUMNS_SQL } from './audit-columns.js';
|
|
15
|
+
import { buildFinalSelect } from './final-select.js';
|
|
16
|
+
export function compileDeleteAudit(db, state, ctx) {
|
|
17
|
+
const pk = primaryKeyFieldKeys(state.table);
|
|
18
|
+
// `.getSQL()` strips the auto-paren drizzle adds around SQLWrapper builders
|
|
19
|
+
// when they are templated — we add explicit parens at the CTE boundary so
|
|
20
|
+
// the WITH clause keeps a uniform shape across verbs.
|
|
21
|
+
let deleteChain = db.delete(state.table);
|
|
22
|
+
if (state.where !== undefined)
|
|
23
|
+
deleteChain = deleteChain.where(state.where);
|
|
24
|
+
const deleteInner = deleteChain.returning().getSQL();
|
|
25
|
+
let beforeChain = db.select().from(state.table);
|
|
26
|
+
if (state.where !== undefined)
|
|
27
|
+
beforeChain = beforeChain.where(state.where);
|
|
28
|
+
const beforeInner = beforeChain.getSQL();
|
|
29
|
+
const joinOn = pk
|
|
30
|
+
.map(k => {
|
|
31
|
+
const col = quoteIdentifier(camelToSnake(k), 'pk column');
|
|
32
|
+
return `before.${col} = deleted.${col}`;
|
|
33
|
+
})
|
|
34
|
+
.join(' AND ');
|
|
35
|
+
const before = buildTraceSafeProjection(state.table, 'before') ?? sql `NULL::jsonb`;
|
|
36
|
+
// Source the audit from `deleted` — RETURNING * gives every column we need
|
|
37
|
+
// for the pre-image join, and `deleted` includes only the rows that actually
|
|
38
|
+
// matched. Joining onto `before` carries the FOR UPDATE lock.
|
|
39
|
+
const fromClause = sql.raw(`FROM deleted JOIN before ON ${joinOn}`);
|
|
40
|
+
const auditSelect = buildAuditInserts(ctx, fromClause, before, sql `NULL::jsonb`);
|
|
41
|
+
const finalSelect = buildFinalSelect(state, 'deleted');
|
|
42
|
+
return sql `
|
|
43
|
+
WITH before AS (${beforeInner}),
|
|
44
|
+
deleted AS (${deleteInner}),
|
|
45
|
+
audit_inserts AS (
|
|
46
|
+
INSERT INTO "audit_record" (${sql.raw(AUDIT_COLUMNS_SQL)})
|
|
47
|
+
${auditSelect}
|
|
48
|
+
RETURNING 1
|
|
49
|
+
)
|
|
50
|
+
${finalSelect}
|
|
51
|
+
`;
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=verb-delete.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verb-delete.js","sourceRoot":"","sources":["../../../../src/data/write/audit/verb-delete.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,yEAAyE;AACzE,4EAA4E;AAC5E,0EAA0E;AAC1E,6EAA6E;AAC7E,6EAA6E;AAC7E,0CAA0C;AAE1C,OAAO,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAA;AACrC,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAA;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAA;AACrD,OAAO,EAAE,wBAAwB,EAAE,MAAM,iBAAiB,CAAA;AAC1D,OAAO,EAAE,iBAAiB,EAA2B,MAAM,mBAAmB,CAAA;AAC9E,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAA;AACtD,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AAOpD,MAAM,UAAU,kBAAkB,CAAC,EAAS,EAAE,KAAiB,EAAE,GAAuB;IACtF,MAAM,EAAE,GAAG,mBAAmB,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;IAE3C,4EAA4E;IAC5E,0EAA0E;IAC1E,sDAAsD;IACtD,IAAI,WAAW,GAAG,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;IACxC,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS;QAAE,WAAW,GAAG,WAAW,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;IAC3E,MAAM,WAAW,GAAG,WAAW,CAAC,SAAS,EAAE,CAAC,MAAM,EAAE,CAAA;IAEpD,IAAI,WAAW,GAAG,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;IAC/C,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS;QAAE,WAAW,GAAG,WAAW,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;IAC3E,MAAM,WAAW,GAAG,WAAW,CAAC,MAAM,EAAE,CAAA;IAExC,MAAM,MAAM,GAAG,EAAE;SACd,GAAG,CAAC,CAAC,CAAC,EAAE;QACP,MAAM,GAAG,GAAG,eAAe,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,WAAW,CAAC,CAAA;QACzD,OAAO,UAAU,GAAG,cAAc,GAAG,EAAE,CAAA;IACzC,CAAC,CAAC;SACD,IAAI,CAAC,OAAO,CAAC,CAAA;IAEhB,MAAM,MAAM,GAAG,wBAAwB,CAAC,KAAK,CAAC,KAAK,EAAE,QAAQ,CAAC,IAAI,GAAG,CAAA,aAAa,CAAA;IAClF,2EAA2E;IAC3E,6EAA6E;IAC7E,8DAA8D;IAC9D,MAAM,UAAU,GAAG,GAAG,CAAC,GAAG,CAAC,+BAA+B,MAAM,EAAE,CAAC,CAAA;IACnE,MAAM,WAAW,GAAG,iBAAiB,CAAC,GAAG,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,CAAA,aAAa,CAAC,CAAA;IAChF,MAAM,WAAW,GAAG,gBAAgB,CAAC,KAAK,EAAE,SAAS,CAAC,CAAA;IAEtD,OAAO,GAAG,CAAA;sBACU,WAAW;kBACf,WAAW;;oCAEO,GAAG,CAAC,GAAG,CAAC,iBAAiB,CAAC;QACtD,WAAW;;;MAGb,WAAW;GACd,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type AuditSelectContext } from './audit-select.js';
|
|
2
|
+
import type { SQL } from '../../db/types.js';
|
|
3
|
+
import type { WriteState } from '../state.js';
|
|
4
|
+
type AnyDb = any;
|
|
5
|
+
export declare function compileInsertAudit(db: AnyDb, state: WriteState, ctx: AuditSelectContext): SQL;
|
|
6
|
+
export {};
|
|
7
|
+
//# sourceMappingURL=verb-insert.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verb-insert.d.ts","sourceRoot":"","sources":["../../../../src/data/write/audit/verb-insert.ts"],"names":[],"mappings":"AAOA,OAAO,EAAqB,KAAK,kBAAkB,EAAE,MAAM,mBAAmB,CAAA;AAG9E,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,mBAAmB,CAAA;AAC5C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAG7C,KAAK,KAAK,GAAG,GAAG,CAAA;AAEhB,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,GAAG,EAAE,kBAAkB,GAAG,GAAG,CAkB7F"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// INSERT under audit: no `before` snapshot. The audit row pairs `NULL::jsonb`
|
|
2
|
+
// with the `.traceSafe()` projection of the inserted row, drawn from the
|
|
3
|
+
// `inserted` CTE's RETURNING *. One audit row per inserted db row per
|
|
4
|
+
// `@touches` target.
|
|
5
|
+
import { sql } from '../../db/sql.js';
|
|
6
|
+
import { buildTraceSafeProjection } from './projection.js';
|
|
7
|
+
import { buildAuditInserts } from './audit-select.js';
|
|
8
|
+
import { AUDIT_COLUMNS_SQL } from './audit-columns.js';
|
|
9
|
+
import { buildFinalSelect } from './final-select.js';
|
|
10
|
+
export function compileInsertAudit(db, state, ctx) {
|
|
11
|
+
// `.getSQL()` strips the auto-paren drizzle adds around SQLWrapper builders
|
|
12
|
+
// when they are templated — we add explicit parens at the CTE boundary so
|
|
13
|
+
// the WITH clause keeps a uniform shape across verbs.
|
|
14
|
+
const inner = db.insert(state.table).values(state.values ?? []).returning().getSQL();
|
|
15
|
+
const after = buildTraceSafeProjection(state.table, 'inserted') ?? sql `NULL::jsonb`;
|
|
16
|
+
const auditSelect = buildAuditInserts(ctx, sql `FROM inserted`, sql `NULL::jsonb`, after);
|
|
17
|
+
const finalSelect = buildFinalSelect(state, 'inserted');
|
|
18
|
+
return sql `
|
|
19
|
+
WITH inserted AS (${inner}),
|
|
20
|
+
audit_inserts AS (
|
|
21
|
+
INSERT INTO "audit_record" (${sql.raw(AUDIT_COLUMNS_SQL)})
|
|
22
|
+
${auditSelect}
|
|
23
|
+
RETURNING 1
|
|
24
|
+
)
|
|
25
|
+
${finalSelect}
|
|
26
|
+
`;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=verb-insert.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verb-insert.js","sourceRoot":"","sources":["../../../../src/data/write/audit/verb-insert.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,yEAAyE;AACzE,sEAAsE;AACtE,qBAAqB;AAErB,OAAO,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAA;AACrC,OAAO,EAAE,wBAAwB,EAAE,MAAM,iBAAiB,CAAA;AAC1D,OAAO,EAAE,iBAAiB,EAA2B,MAAM,mBAAmB,CAAA;AAC9E,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AAOpD,MAAM,UAAU,kBAAkB,CAAC,EAAS,EAAE,KAAiB,EAAE,GAAuB;IACtF,4EAA4E;IAC5E,0EAA0E;IAC1E,sDAAsD;IACtD,MAAM,KAAK,GAAG,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,SAAS,EAAE,CAAC,MAAM,EAAE,CAAA;IACpF,MAAM,KAAK,GAAG,wBAAwB,CAAC,KAAK,CAAC,KAAK,EAAE,UAAU,CAAC,IAAI,GAAG,CAAA,aAAa,CAAA;IACnF,MAAM,WAAW,GAAG,iBAAiB,CAAC,GAAG,EAAE,GAAG,CAAA,eAAe,EAAE,GAAG,CAAA,aAAa,EAAE,KAAK,CAAC,CAAA;IACvF,MAAM,WAAW,GAAG,gBAAgB,CAAC,KAAK,EAAE,UAAU,CAAC,CAAA;IAEvD,OAAO,GAAG,CAAA;wBACY,KAAK;;oCAEO,GAAG,CAAC,GAAG,CAAC,iBAAiB,CAAC;QACtD,WAAW;;;MAGb,WAAW;GACd,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type AuditSelectContext } from './audit-select.js';
|
|
2
|
+
import type { SQL } from '../../db/types.js';
|
|
3
|
+
import type { WriteState } from '../state.js';
|
|
4
|
+
type AnyDb = any;
|
|
5
|
+
export declare function compileUpdateAudit(db: AnyDb, state: WriteState, ctx: AuditSelectContext): SQL;
|
|
6
|
+
export {};
|
|
7
|
+
//# sourceMappingURL=verb-update.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verb-update.d.ts","sourceRoot":"","sources":["../../../../src/data/write/audit/verb-update.ts"],"names":[],"mappings":"AAYA,OAAO,EAAqB,KAAK,kBAAkB,EAAE,MAAM,mBAAmB,CAAA;AAI9E,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,mBAAmB,CAAA;AAC5C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAG7C,KAAK,KAAK,GAAG,GAAG,CAAA;AAEhB,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,GAAG,EAAE,kBAAkB,GAAG,GAAG,CAqC7F"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// UPDATE under audit: snapshots the matched rows into `before`, runs the
|
|
2
|
+
// UPDATE into `updated`, then emits one audit row per affected db row per
|
|
3
|
+
// `@touches` target, joining `before` and `updated` on the primary key so
|
|
4
|
+
// the pre/post pair lines up. The single statement's snapshot keeps `before`
|
|
5
|
+
// and `updated` consistent — no lock needed; in fact, `FOR UPDATE` inside a
|
|
6
|
+
// CTE conflicts with the sibling UPDATE on the same row and silently yields
|
|
7
|
+
// zero rows from `before`, suppressing the audit insert.
|
|
8
|
+
import { sql } from '../../db/sql.js';
|
|
9
|
+
import { quoteIdentifier } from '../../db/identifiers.js';
|
|
10
|
+
import { camelToSnake } from '../../domain/naming.js';
|
|
11
|
+
import { buildTraceSafeProjection } from './projection.js';
|
|
12
|
+
import { buildAuditInserts } from './audit-select.js';
|
|
13
|
+
import { primaryKeyFieldKeys } from './primary-key.js';
|
|
14
|
+
import { AUDIT_COLUMNS_SQL } from './audit-columns.js';
|
|
15
|
+
import { buildFinalSelect } from './final-select.js';
|
|
16
|
+
export function compileUpdateAudit(db, state, ctx) {
|
|
17
|
+
const pk = primaryKeyFieldKeys(state.table);
|
|
18
|
+
const set = state.set ?? {};
|
|
19
|
+
// `.getSQL()` strips the auto-paren drizzle adds around SQLWrapper builders
|
|
20
|
+
// when they are templated — we add explicit parens at the CTE boundary so
|
|
21
|
+
// the WITH clause keeps a uniform shape across verbs.
|
|
22
|
+
let updateChain = db.update(state.table).set(set);
|
|
23
|
+
if (state.where !== undefined)
|
|
24
|
+
updateChain = updateChain.where(state.where);
|
|
25
|
+
const updateInner = updateChain.returning().getSQL();
|
|
26
|
+
let beforeChain = db.select().from(state.table);
|
|
27
|
+
if (state.where !== undefined)
|
|
28
|
+
beforeChain = beforeChain.where(state.where);
|
|
29
|
+
const beforeInner = beforeChain.getSQL();
|
|
30
|
+
const joinOn = pk
|
|
31
|
+
.map(k => {
|
|
32
|
+
const col = quoteIdentifier(camelToSnake(k), 'pk column');
|
|
33
|
+
return `before.${col} = updated.${col}`;
|
|
34
|
+
})
|
|
35
|
+
.join(' AND ');
|
|
36
|
+
const before = buildTraceSafeProjection(state.table, 'before') ?? sql `NULL::jsonb`;
|
|
37
|
+
const after = buildTraceSafeProjection(state.table, 'updated') ?? sql `NULL::jsonb`;
|
|
38
|
+
const fromClause = sql.raw(`FROM updated JOIN before ON ${joinOn}`);
|
|
39
|
+
const auditSelect = buildAuditInserts(ctx, fromClause, before, after);
|
|
40
|
+
const finalSelect = buildFinalSelect(state, 'updated');
|
|
41
|
+
return sql `
|
|
42
|
+
WITH before AS (${beforeInner}),
|
|
43
|
+
updated AS (${updateInner}),
|
|
44
|
+
audit_inserts AS (
|
|
45
|
+
INSERT INTO "audit_record" (${sql.raw(AUDIT_COLUMNS_SQL)})
|
|
46
|
+
${auditSelect}
|
|
47
|
+
RETURNING 1
|
|
48
|
+
)
|
|
49
|
+
${finalSelect}
|
|
50
|
+
`;
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=verb-update.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verb-update.js","sourceRoot":"","sources":["../../../../src/data/write/audit/verb-update.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,0EAA0E;AAC1E,0EAA0E;AAC1E,6EAA6E;AAC7E,4EAA4E;AAC5E,4EAA4E;AAC5E,yDAAyD;AAEzD,OAAO,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAA;AACrC,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAA;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAA;AACrD,OAAO,EAAE,wBAAwB,EAAE,MAAM,iBAAiB,CAAA;AAC1D,OAAO,EAAE,iBAAiB,EAA2B,MAAM,mBAAmB,CAAA;AAC9E,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAA;AACtD,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AAOpD,MAAM,UAAU,kBAAkB,CAAC,EAAS,EAAE,KAAiB,EAAE,GAAuB;IACtF,MAAM,EAAE,GAAG,mBAAmB,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;IAC3C,MAAM,GAAG,GAAG,KAAK,CAAC,GAAG,IAAI,EAAE,CAAA;IAC3B,4EAA4E;IAC5E,0EAA0E;IAC1E,sDAAsD;IACtD,IAAI,WAAW,GAAG,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;IACjD,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS;QAAE,WAAW,GAAG,WAAW,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;IAC3E,MAAM,WAAW,GAAG,WAAW,CAAC,SAAS,EAAE,CAAC,MAAM,EAAE,CAAA;IAEpD,IAAI,WAAW,GAAG,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;IAC/C,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS;QAAE,WAAW,GAAG,WAAW,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;IAC3E,MAAM,WAAW,GAAG,WAAW,CAAC,MAAM,EAAE,CAAA;IAExC,MAAM,MAAM,GAAG,EAAE;SACd,GAAG,CAAC,CAAC,CAAC,EAAE;QACP,MAAM,GAAG,GAAG,eAAe,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,WAAW,CAAC,CAAA;QACzD,OAAO,UAAU,GAAG,cAAc,GAAG,EAAE,CAAA;IACzC,CAAC,CAAC;SACD,IAAI,CAAC,OAAO,CAAC,CAAA;IAEhB,MAAM,MAAM,GAAG,wBAAwB,CAAC,KAAK,CAAC,KAAK,EAAE,QAAQ,CAAC,IAAI,GAAG,CAAA,aAAa,CAAA;IAClF,MAAM,KAAK,GAAG,wBAAwB,CAAC,KAAK,CAAC,KAAK,EAAE,SAAS,CAAC,IAAI,GAAG,CAAA,aAAa,CAAA;IAClF,MAAM,UAAU,GAAG,GAAG,CAAC,GAAG,CAAC,+BAA+B,MAAM,EAAE,CAAC,CAAA;IACnE,MAAM,WAAW,GAAG,iBAAiB,CAAC,GAAG,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,CAAC,CAAA;IACrE,MAAM,WAAW,GAAG,gBAAgB,CAAC,KAAK,EAAE,SAAS,CAAC,CAAA;IAEtD,OAAO,GAAG,CAAA;sBACU,WAAW;kBACf,WAAW;;oCAEO,GAAG,CAAC,GAAG,CAAC,iBAAiB,CAAC;QACtD,WAAW;;;MAGb,WAAW;GACd,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type AuditSelectContext } from './audit-select.js';
|
|
2
|
+
import type { SQL } from '../../db/types.js';
|
|
3
|
+
import type { WriteState } from '../state.js';
|
|
4
|
+
type AnyDb = any;
|
|
5
|
+
export declare function compileUpsertAudit(db: AnyDb, state: WriteState, ctx: AuditSelectContext): SQL;
|
|
6
|
+
export {};
|
|
7
|
+
//# sourceMappingURL=verb-upsert.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verb-upsert.d.ts","sourceRoot":"","sources":["../../../../src/data/write/audit/verb-upsert.ts"],"names":[],"mappings":"AAYA,OAAO,EAAqB,KAAK,kBAAkB,EAAE,MAAM,mBAAmB,CAAA;AAG9E,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,mBAAmB,CAAA;AAC5C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAG7C,KAAK,KAAK,GAAG,GAAG,CAAA;AAEhB,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,GAAG,EAAE,kBAAkB,GAAG,GAAG,CA0E7F"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// UPSERT under audit: a single INSERT ... ON CONFLICT DO UPDATE / DO NOTHING
|
|
2
|
+
// that either inserts a new row or updates the conflicting one. Audit rows
|
|
3
|
+
// distinguish create vs update by left-joining the upserted row with the
|
|
4
|
+
// `before` snapshot on the conflict-target columns: a NULL `before` row
|
|
5
|
+
// means the upserted row was freshly inserted; non-NULL means it updated
|
|
6
|
+
// an existing row. The audit's `before` jsonb is wrapped in a CASE that
|
|
7
|
+
// returns NULL::jsonb for the insert branch.
|
|
8
|
+
import { sql } from '../../db/sql.js';
|
|
9
|
+
import { quoteIdentifier } from '../../db/identifiers.js';
|
|
10
|
+
import { camelToSnake } from '../../domain/naming.js';
|
|
11
|
+
import { buildTraceSafeProjection } from './projection.js';
|
|
12
|
+
import { buildAuditInserts } from './audit-select.js';
|
|
13
|
+
import { AUDIT_COLUMNS_SQL } from './audit-columns.js';
|
|
14
|
+
import { buildFinalSelect } from './final-select.js';
|
|
15
|
+
export function compileUpsertAudit(db, state, ctx) {
|
|
16
|
+
if (state.onConflictColumns === undefined || state.onConflictColumns.length === 0) {
|
|
17
|
+
throw new Error('pipe.upsert requires .onConflict([...columns]). ' +
|
|
18
|
+
'Pass the conflict-target columns that determine "this row already exists" — ' +
|
|
19
|
+
'usually the primary key or a unique index.');
|
|
20
|
+
}
|
|
21
|
+
const conflictCols = state.onConflictColumns;
|
|
22
|
+
const targetColumns = conflictCols.map(key => {
|
|
23
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- DefinedTable columns are indexed by field key; the lookup was validated at the builder surface
|
|
24
|
+
const col = state.table[key];
|
|
25
|
+
if (col === undefined) {
|
|
26
|
+
throw new Error(`pipe.upsert: onConflict column "${key}" is not a field of "${state.table.$name}". ` +
|
|
27
|
+
`Use a field key declared on the table.`);
|
|
28
|
+
}
|
|
29
|
+
return col;
|
|
30
|
+
});
|
|
31
|
+
let upsertChain = db.insert(state.table).values(state.values ?? []);
|
|
32
|
+
if (state.set === undefined) {
|
|
33
|
+
upsertChain = upsertChain.onConflictDoNothing({ target: targetColumns });
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
upsertChain = upsertChain.onConflictDoUpdate({ target: targetColumns, set: state.set });
|
|
37
|
+
}
|
|
38
|
+
// `.getSQL()` strips the auto-paren drizzle adds around SQLWrapper builders
|
|
39
|
+
// when they are templated — we add explicit parens at the CTE boundary so
|
|
40
|
+
// the WITH clause keeps a uniform shape across verbs.
|
|
41
|
+
const upsertInner = upsertChain.returning().getSQL();
|
|
42
|
+
const tableName = quoteIdentifier(state.table.$name, 'target table');
|
|
43
|
+
const conflictColsSql = conflictCols.map(k => quoteIdentifier(camelToSnake(k), 'conflict column')).join(', ');
|
|
44
|
+
const valuesRows = (state.values ?? []).map(row => {
|
|
45
|
+
const parts = conflictCols.map(k => sql `${row[k] ?? null}`);
|
|
46
|
+
return sql `(${sql.join(parts, sql.raw(', '))})`;
|
|
47
|
+
});
|
|
48
|
+
// No `FOR UPDATE` — the statement's snapshot keeps `before` and `upserted`
|
|
49
|
+
// consistent; locking inside a CTE conflicts with the sibling write on the
|
|
50
|
+
// same row and silently yields zero rows from `before`.
|
|
51
|
+
const beforeSelect = sql `
|
|
52
|
+
SELECT * FROM ${sql.raw(tableName)}
|
|
53
|
+
WHERE (${sql.raw(conflictColsSql)}) IN (${sql.join(valuesRows, sql.raw(', '))})
|
|
54
|
+
`;
|
|
55
|
+
const joinOn = conflictCols
|
|
56
|
+
.map(k => {
|
|
57
|
+
const col = quoteIdentifier(camelToSnake(k), 'conflict column');
|
|
58
|
+
return `before.${col} = upserted.${col}`;
|
|
59
|
+
})
|
|
60
|
+
.join(' AND ');
|
|
61
|
+
// Pivot: the conflict-key column of `before` is non-null iff the upserted
|
|
62
|
+
// row matched an existing row. Use the first conflict col as the discriminator.
|
|
63
|
+
const pivotCol = quoteIdentifier(camelToSnake(conflictCols[0]), 'conflict column');
|
|
64
|
+
const beforeProj = buildTraceSafeProjection(state.table, 'before') ?? sql `NULL::jsonb`;
|
|
65
|
+
const after = buildTraceSafeProjection(state.table, 'upserted') ?? sql `NULL::jsonb`;
|
|
66
|
+
const beforeExpr = sql `CASE WHEN ${sql.raw(`before.${pivotCol}`)} IS NULL THEN NULL::jsonb ELSE (${beforeProj}) END`;
|
|
67
|
+
const fromClause = sql.raw(`FROM upserted LEFT JOIN before ON ${joinOn}`);
|
|
68
|
+
const auditSelect = buildAuditInserts(ctx, fromClause, beforeExpr, after);
|
|
69
|
+
const finalSelect = buildFinalSelect(state, 'upserted');
|
|
70
|
+
return sql `
|
|
71
|
+
WITH before AS (${beforeSelect}),
|
|
72
|
+
upserted AS (${upsertInner}),
|
|
73
|
+
audit_inserts AS (
|
|
74
|
+
INSERT INTO "audit_record" (${sql.raw(AUDIT_COLUMNS_SQL)})
|
|
75
|
+
${auditSelect}
|
|
76
|
+
RETURNING 1
|
|
77
|
+
)
|
|
78
|
+
${finalSelect}
|
|
79
|
+
`;
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=verb-upsert.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verb-upsert.js","sourceRoot":"","sources":["../../../../src/data/write/audit/verb-upsert.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { PgTable } from '../query/pg-core/index.js';
|
|
2
|
+
import type { SQL } from '../db/types.js';
|
|
3
|
+
import type { DefinedTable, FieldRecord } from '../domain/types.js';
|
|
4
|
+
import type { WriteState } from './state.js';
|
|
5
|
+
export declare class DeleteBuilder<T extends PgTable, TResult = unknown> implements PromiseLike<TResult> {
|
|
6
|
+
private readonly state;
|
|
7
|
+
constructor(state: WriteState);
|
|
8
|
+
where(condition: SQL): DeleteBuilder<T, TResult>;
|
|
9
|
+
returning(): DeleteBuilder<T, Record<string, unknown>[]>;
|
|
10
|
+
returning<TShape extends Record<string, unknown>>(shape: TShape): DeleteBuilder<T, Record<string, unknown>[]>;
|
|
11
|
+
then<TFulfilled = TResult, TRejected = never>(onfulfilled?: ((value: TResult) => TFulfilled | PromiseLike<TFulfilled>) | null, onrejected?: ((reason: unknown) => TRejected | PromiseLike<TRejected>) | null): PromiseLike<TFulfilled | TRejected>;
|
|
12
|
+
}
|
|
13
|
+
/** Starts a pipework-owned delete against `table`. Chain with `.where(...).returning(...)`. */
|
|
14
|
+
export declare function remove<T extends DefinedTable<FieldRecord> & PgTable>(table: T): DeleteBuilder<T, any>;
|
|
15
|
+
//# sourceMappingURL=delete.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delete.d.ts","sourceRoot":"","sources":["../../../src/data/write/delete.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,2BAA2B,CAAA;AACxD,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,gBAAgB,CAAA;AACzC,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AACnE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAG5C,qBAAa,aAAa,CAAC,CAAC,SAAS,OAAO,EAAE,OAAO,GAAG,OAAO,CAAE,YAAW,WAAW,CAAC,OAAO,CAAC;IAClF,OAAO,CAAC,QAAQ,CAAC,KAAK;gBAAL,KAAK,EAAE,UAAU;IAE9C,KAAK,CAAC,SAAS,EAAE,GAAG,GAAG,aAAa,CAAC,CAAC,EAAE,OAAO,CAAC;IAIhD,SAAS,IAAI,aAAa,CAAC,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IACxD,SAAS,CAAC,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,KAAK,EAAE,MAAM,GAAG,aAAa,CAAC,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IAM7G,IAAI,CAAC,UAAU,GAAG,OAAO,EAAE,SAAS,GAAG,KAAK,EAC1C,WAAW,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,OAAO,KAAK,UAAU,GAAG,WAAW,CAAC,UAAU,CAAC,CAAC,GAAG,IAAI,EAC/E,UAAU,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE,OAAO,KAAK,SAAS,GAAG,WAAW,CAAC,SAAS,CAAC,CAAC,GAAG,IAAI,GAC5E,WAAW,CAAC,UAAU,GAAG,SAAS,CAAC;CAGvC;AAED,+FAA+F;AAE/F,wBAAgB,MAAM,CAAC,CAAC,SAAS,YAAY,CAAC,WAAW,CAAC,GAAG,OAAO,EAAE,KAAK,EAAE,CAAC,GAAG,aAAa,CAAC,CAAC,EAAE,GAAG,CAAC,CAErG"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// `pipe.delete(table)` — the pipework-owned delete builder. Same intent-based
|
|
2
|
+
// design as UpdateBuilder; see update.ts for the rationale.
|
|
3
|
+
import { executeWrite } from './execute.js';
|
|
4
|
+
export class DeleteBuilder {
|
|
5
|
+
state;
|
|
6
|
+
constructor(state) {
|
|
7
|
+
this.state = state;
|
|
8
|
+
}
|
|
9
|
+
where(condition) {
|
|
10
|
+
return new DeleteBuilder({ ...this.state, where: condition });
|
|
11
|
+
}
|
|
12
|
+
returning(shape) {
|
|
13
|
+
const returning = shape === undefined ? 'all' : shape;
|
|
14
|
+
return new DeleteBuilder({ ...this.state, returning });
|
|
15
|
+
}
|
|
16
|
+
then(onfulfilled, onrejected) {
|
|
17
|
+
return executeWrite(this.state).then(onfulfilled, onrejected);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/** Starts a pipework-owned delete against `table`. Chain with `.where(...).returning(...)`. */
|
|
21
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
22
|
+
export function remove(table) {
|
|
23
|
+
return new DeleteBuilder({ verb: 'delete', table });
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=delete.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delete.js","sourceRoot":"","sources":["../../../src/data/write/delete.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,4DAA4D;AAM5D,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAE3C,MAAM,OAAO,aAAa;IACK;IAA7B,YAA6B,KAAiB;QAAjB,UAAK,GAAL,KAAK,CAAY;IAAG,CAAC;IAElD,KAAK,CAAC,SAAc;QAClB,OAAO,IAAI,aAAa,CAAa,EAAE,GAAG,IAAI,CAAC,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAA;IAC3E,CAAC;IAID,SAAS,CAAC,KAA+B;QACvC,MAAM,SAAS,GAAG,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,KAAc,CAAC,CAAC,CAAC,KAAK,CAAA;QAC9D,OAAO,IAAI,aAAa,CAA+B,EAAE,GAAG,IAAI,CAAC,KAAK,EAAE,SAAS,EAAE,CAAC,CAAA;IACtF,CAAC;IAED,IAAI,CACF,WAA+E,EAC/E,UAA6E;QAE7E,OAAO,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,WAA6C,EAAE,UAAU,CAAwC,CAAA;IACxI,CAAC;CACF;AAED,+FAA+F;AAC/F,8DAA8D;AAC9D,MAAM,UAAU,MAAM,CAAgD,KAAQ;IAC5E,OAAO,IAAI,aAAa,CAAa,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAA;AACjE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"execute.d.ts","sourceRoot":"","sources":["../../../src/data/write/execute.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAQ5C,wBAAsB,YAAY,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,CAiCtE"}
|