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.
Files changed (690) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/package.json +4 -3
  3. package/src/async/jobs/abortable-delay.ts +18 -0
  4. package/src/async/jobs/claims.ts +52 -0
  5. package/src/async/jobs/cron.ts +179 -0
  6. package/src/async/jobs/errors.ts +56 -0
  7. package/src/async/jobs/exclusion.ts +117 -0
  8. package/src/async/jobs/execute.ts +219 -0
  9. package/src/async/jobs/index.ts +10 -0
  10. package/src/async/jobs/listener.ts +135 -0
  11. package/src/async/jobs/namespace.ts +20 -0
  12. package/src/async/jobs/queue-door.ts +44 -0
  13. package/src/async/jobs/queue.ts +1012 -0
  14. package/src/async/jobs/run-script.ts +67 -0
  15. package/src/async/jobs/scheduler.ts +218 -0
  16. package/src/async/jobs/schedules-table.ts +37 -0
  17. package/src/async/jobs/serialize-error.ts +48 -0
  18. package/src/async/jobs/table.ts +191 -0
  19. package/src/async/pipeline/define.ts +53 -0
  20. package/src/async/pipeline/errors.ts +35 -0
  21. package/src/async/pipeline/execute.ts +53 -0
  22. package/src/async/pipeline/index.ts +13 -0
  23. package/src/async/pipeline/namespace.ts +12 -0
  24. package/src/async/pipeline/resume.ts +67 -0
  25. package/src/async/pipeline/run-steps.ts +98 -0
  26. package/src/async/pipeline/types.ts +57 -0
  27. package/src/async/state-machine/define.ts +64 -0
  28. package/src/async/state-machine/errors.ts +29 -0
  29. package/src/async/state-machine/index.ts +3 -0
  30. package/src/async/state-machine/namespace.ts +12 -0
  31. package/src/async/state-machine/transition.ts +90 -0
  32. package/src/async/state-machine/types.ts +37 -0
  33. package/src/async/steps/errors.ts +31 -0
  34. package/src/async/steps/graph.ts +1214 -0
  35. package/src/async/steps/hash.ts +40 -0
  36. package/src/async/steps/index.ts +7 -0
  37. package/src/async/steps/namespace.ts +7 -0
  38. package/src/async/steps/table.ts +97 -0
  39. package/src/async/steps/types.ts +318 -0
  40. package/src/audit/chain/index.ts +9 -0
  41. package/src/audit/chain/install.ts +113 -0
  42. package/src/audit/chain/retire.ts +52 -0
  43. package/src/audit/chain/verify.ts +257 -0
  44. package/src/audit/query/cursor.ts +37 -0
  45. package/src/audit/query/index.ts +9 -0
  46. package/src/audit/query/query.ts +223 -0
  47. package/src/audit/schema/audit-record-fields.ts +68 -0
  48. package/src/audit/schema/audit-record.ts +23 -0
  49. package/src/auth/auth/audit.ts +31 -0
  50. package/src/auth/auth/chain.ts +84 -0
  51. package/src/auth/auth/cookie.ts +136 -0
  52. package/src/auth/auth/email-links.ts +311 -0
  53. package/src/auth/auth/guard.ts +128 -0
  54. package/src/auth/auth/index.ts +18 -0
  55. package/src/auth/auth/multi-org.ts +337 -0
  56. package/src/auth/auth/namespace.ts +34 -0
  57. package/src/auth/auth/parse-expiry.ts +13 -0
  58. package/src/auth/auth/passwords.ts +110 -0
  59. package/src/auth/auth/schema.ts +110 -0
  60. package/src/auth/auth/sessions.ts +565 -0
  61. package/src/auth/auth/sign-in.ts +156 -0
  62. package/src/auth/auth/signing-keys.ts +69 -0
  63. package/src/auth/auth/types.ts +26 -0
  64. package/src/auth/auth/uuid.ts +10 -0
  65. package/src/auth/rbac/cache.ts +55 -0
  66. package/src/auth/rbac/check.ts +30 -0
  67. package/src/auth/rbac/create.ts +35 -0
  68. package/src/auth/rbac/index.ts +5 -0
  69. package/src/auth/rbac/middleware.ts +20 -0
  70. package/src/auth/rbac/namespace.ts +15 -0
  71. package/src/auth/rbac/resolve.ts +31 -0
  72. package/src/auth/rbac/schema.sql +31 -0
  73. package/src/auth/rbac/seed.ts +67 -0
  74. package/src/auth/rbac/tables.ts +32 -0
  75. package/src/auth/rbac/types.ts +65 -0
  76. package/src/auth/tenant/extract.ts +33 -0
  77. package/src/auth/tenant/in-tenant.ts +49 -0
  78. package/src/auth/tenant/index.ts +7 -0
  79. package/src/auth/tenant/namespace.ts +23 -0
  80. package/src/auth/tenant/propagate.ts +20 -0
  81. package/src/auth/tenant/rls.ts +120 -0
  82. package/src/auth/tenant/scope.ts +33 -0
  83. package/src/auth/tenant/scoped-db.ts +581 -0
  84. package/src/auth/tenant/types.ts +14 -0
  85. package/src/auth/tenant/validate.ts +27 -0
  86. package/src/cli/commands/add.ts +158 -0
  87. package/src/cli/commands/alerts.ts +98 -0
  88. package/src/cli/commands/check.ts +1418 -0
  89. package/src/cli/commands/codemod.ts +78 -0
  90. package/src/cli/commands/db.ts +356 -0
  91. package/src/cli/commands/dev.ts +101 -0
  92. package/src/cli/commands/generate.ts +75 -0
  93. package/src/cli/commands/init.ts +36 -0
  94. package/src/cli/commands/install.ts +28 -0
  95. package/src/cli/commands/lint.ts +62 -0
  96. package/src/cli/commands/migrate.ts +132 -0
  97. package/src/cli/commands/openapi.ts +63 -0
  98. package/src/cli/commands/remove.ts +72 -0
  99. package/src/cli/commands/run.ts +50 -0
  100. package/src/cli/commands/scope.ts +54 -0
  101. package/src/cli/commands/serve.ts +68 -0
  102. package/src/cli/commands/shard.ts +31 -0
  103. package/src/cli/commands/shared/config-pipework-version.ts +28 -0
  104. package/src/cli/commands/shared/find-config.ts +22 -0
  105. package/src/cli/commands/shared/load-config.ts +35 -0
  106. package/src/cli/commands/shared/pnpm-install.ts +95 -0
  107. package/src/cli/commands/shared/report-removed.ts +8 -0
  108. package/src/cli/commands/sync.ts +32 -0
  109. package/src/cli/commands/test.ts +307 -0
  110. package/src/cli/commands/upgrade.ts +48 -0
  111. package/src/cli/dev-bootstrap.ts +24 -0
  112. package/src/cli/errors.ts +10 -0
  113. package/src/cli/index.ts +303 -0
  114. package/src/cli/output.ts +17 -0
  115. package/src/cli/port.ts +15 -0
  116. package/src/cli/surface-kinds.ts +20 -0
  117. package/src/client/index.ts +4 -0
  118. package/src/client/poll.ts +130 -0
  119. package/src/client/report-fault.ts +119 -0
  120. package/src/core/config/app-pool.ts +19 -0
  121. package/src/core/config/aws-env.ts +29 -0
  122. package/src/core/config/base-dir.ts +24 -0
  123. package/src/core/config/discover.ts +64 -0
  124. package/src/core/config/env-resolve.ts +87 -0
  125. package/src/core/config/env-types.ts +116 -0
  126. package/src/core/config/env.ts +55 -0
  127. package/src/core/config/errors.ts +9 -0
  128. package/src/core/config/import-config.ts +40 -0
  129. package/src/core/config/index.ts +8 -0
  130. package/src/core/config/load.ts +417 -0
  131. package/src/core/config/namespace.ts +14 -0
  132. package/src/core/config/resolve.ts +195 -0
  133. package/src/core/config/schema.ts +488 -0
  134. package/src/core/config/statement-observer.ts +48 -0
  135. package/src/core/config/ts-inline-hooks.ts +75 -0
  136. package/src/core/config/ts-register.ts +4 -0
  137. package/src/core/config/ts-resolve-hooks.ts +101 -0
  138. package/src/core/errors/base.ts +49 -0
  139. package/src/core/errors/http.ts +146 -0
  140. package/src/core/errors/index.ts +2 -0
  141. package/src/core/errors/namespace.ts +31 -0
  142. package/src/core/lifecycle/drain.ts +33 -0
  143. package/src/core/lifecycle/health.ts +72 -0
  144. package/src/core/lifecycle/index.ts +4 -0
  145. package/src/core/lifecycle/namespace.ts +7 -0
  146. package/src/core/lifecycle/startup.ts +102 -0
  147. package/src/core/lifecycle/types.ts +36 -0
  148. package/src/core/logging/context.ts +60 -0
  149. package/src/core/logging/error-chain.ts +68 -0
  150. package/src/core/logging/index.ts +5 -0
  151. package/src/core/logging/logger.ts +65 -0
  152. package/src/core/logging/namespace.ts +16 -0
  153. package/src/core/logging/proxy.ts +22 -0
  154. package/src/core/logging/redact-match.ts +24 -0
  155. package/src/core/logging/types.ts +47 -0
  156. package/src/core/pipework.ts +566 -0
  157. package/src/core/runtime/broker.ts +69 -0
  158. package/src/core/surface/http.ts +133 -0
  159. package/src/core/surface/index.ts +11 -0
  160. package/src/core/surface/job-timeout.ts +40 -0
  161. package/src/core/surface/namespace.ts +13 -0
  162. package/src/core/surface/script.ts +33 -0
  163. package/src/core/surface/start.ts +142 -0
  164. package/src/core/surface/types.ts +225 -0
  165. package/src/core/surface/validate.ts +88 -0
  166. package/src/core/surface/worker.ts +528 -0
  167. package/src/core/validation/connectivity.ts +49 -0
  168. package/src/core/validation/domain.ts +99 -0
  169. package/src/core/validation/index.ts +5 -0
  170. package/src/core/validation/queue-tables.ts +312 -0
  171. package/src/core/validation/rls-role.ts +138 -0
  172. package/src/core/validation/startup.ts +51 -0
  173. package/src/core/validation/surfaces.ts +65 -0
  174. package/src/core/validation/types.ts +25 -0
  175. package/src/data/db/advisory-lock.ts +55 -0
  176. package/src/data/db/aggregates.ts +172 -0
  177. package/src/data/db/array-elements.ts +111 -0
  178. package/src/data/db/bulk-set.ts +53 -0
  179. package/src/data/db/col/index.ts +14 -0
  180. package/src/data/db/composed.ts +53 -0
  181. package/src/data/db/db.ts +276 -0
  182. package/src/data/db/excluded.ts +16 -0
  183. package/src/data/db/expressions.ts +473 -0
  184. package/src/data/db/filter.ts +90 -0
  185. package/src/data/db/function-source.ts +114 -0
  186. package/src/data/db/identifiers.ts +16 -0
  187. package/src/data/db/idx/index.ts +9 -0
  188. package/src/data/db/in-list.ts +49 -0
  189. package/src/data/db/index.ts +9 -0
  190. package/src/data/db/insert-split.ts +137 -0
  191. package/src/data/db/instrumentation.ts +82 -0
  192. package/src/data/db/maintenance.ts +28 -0
  193. package/src/data/db/namespace.ts +107 -0
  194. package/src/data/db/owner-labels.ts +9 -0
  195. package/src/data/db/pool.ts +187 -0
  196. package/src/data/db/query-label.ts +13 -0
  197. package/src/data/db/query-watch.ts +116 -0
  198. package/src/data/db/raw-client-warning.ts +37 -0
  199. package/src/data/db/readonly.ts +23 -0
  200. package/src/data/db/reservation.ts +151 -0
  201. package/src/data/db/schema-features.ts +14 -0
  202. package/src/data/db/serializable.ts +83 -0
  203. package/src/data/db/sql.ts +1 -0
  204. package/src/data/db/timeouts.ts +76 -0
  205. package/src/data/db/types.ts +18 -0
  206. package/src/data/db/unbound.ts +33 -0
  207. package/src/data/db/values.ts +29 -0
  208. package/src/data/domain/brand.ts +15 -0
  209. package/src/data/domain/build-factory.ts +193 -0
  210. package/src/data/domain/build-schema.ts +161 -0
  211. package/src/data/domain/build-table.ts +355 -0
  212. package/src/data/domain/define.ts +239 -0
  213. package/src/data/domain/external-registry.ts +34 -0
  214. package/src/data/domain/external.ts +77 -0
  215. package/src/data/domain/field.ts +279 -0
  216. package/src/data/domain/index.ts +13 -0
  217. package/src/data/domain/naming.ts +6 -0
  218. package/src/data/domain/projections.ts +57 -0
  219. package/src/data/domain/trace-safe-projection.ts +31 -0
  220. package/src/data/domain/traits.ts +108 -0
  221. package/src/data/domain/types.ts +289 -0
  222. package/src/data/jsonb/composed.ts +18 -0
  223. package/src/data/jsonb/index.ts +1 -0
  224. package/src/data/jsonb/namespace.ts +30 -0
  225. package/src/data/jsonb/query.ts +86 -0
  226. package/src/data/migrate/append-only-generate.ts +47 -0
  227. package/src/data/migrate/applied.ts +47 -0
  228. package/src/data/migrate/apply.ts +309 -0
  229. package/src/data/migrate/check.ts +119 -0
  230. package/src/data/migrate/diff.ts +357 -0
  231. package/src/data/migrate/door-generate.ts +130 -0
  232. package/src/data/migrate/framework-rls.ts +58 -0
  233. package/src/data/migrate/generate.ts +201 -0
  234. package/src/data/migrate/history.ts +215 -0
  235. package/src/data/migrate/index.ts +9 -0
  236. package/src/data/migrate/internal-definitions.ts +175 -0
  237. package/src/data/migrate/post-process.ts +73 -0
  238. package/src/data/migrate/pragma.ts +73 -0
  239. package/src/data/migrate/provision-framework.ts +147 -0
  240. package/src/data/migrate/rls-generate.ts +61 -0
  241. package/src/data/migrate/run.ts +124 -0
  242. package/src/data/migrate/safety.ts +167 -0
  243. package/src/data/migrate/snapshot.ts +368 -0
  244. package/src/data/migrate/sql-emit.ts +392 -0
  245. package/src/data/migrate/statements.ts +312 -0
  246. package/src/data/query/alias.ts +126 -0
  247. package/src/data/query/batch.ts +8 -0
  248. package/src/data/query/cache/core/cache.ts +78 -0
  249. package/src/data/query/cache/core/index.ts +1 -0
  250. package/src/data/query/cache/core/types.ts +29 -0
  251. package/src/data/query/cache/readme.md +230 -0
  252. package/src/data/query/casing.ts +76 -0
  253. package/src/data/query/collation.ts +11 -0
  254. package/src/data/query/column-builder.ts +370 -0
  255. package/src/data/query/column.ts +150 -0
  256. package/src/data/query/entity.ts +42 -0
  257. package/src/data/query/errors.ts +33 -0
  258. package/src/data/query/index.ts +14 -0
  259. package/src/data/query/logger.ts +47 -0
  260. package/src/data/query/operations.ts +45 -0
  261. package/src/data/query/pg-core/alias.ts +12 -0
  262. package/src/data/query/pg-core/checks.ts +32 -0
  263. package/src/data/query/pg-core/columns/all.ts +71 -0
  264. package/src/data/query/pg-core/columns/bigint.ts +110 -0
  265. package/src/data/query/pg-core/columns/bigserial.ts +128 -0
  266. package/src/data/query/pg-core/columns/boolean.ts +43 -0
  267. package/src/data/query/pg-core/columns/char.ts +85 -0
  268. package/src/data/query/pg-core/columns/cidr.ts +43 -0
  269. package/src/data/query/pg-core/columns/common.ts +366 -0
  270. package/src/data/query/pg-core/columns/custom.ts +232 -0
  271. package/src/data/query/pg-core/columns/date.common.ts +15 -0
  272. package/src/data/query/pg-core/columns/date.ts +112 -0
  273. package/src/data/query/pg-core/columns/double-precision.ts +55 -0
  274. package/src/data/query/pg-core/columns/enum.ts +201 -0
  275. package/src/data/query/pg-core/columns/index.ts +35 -0
  276. package/src/data/query/pg-core/columns/inet.ts +43 -0
  277. package/src/data/query/pg-core/columns/int.common.ts +57 -0
  278. package/src/data/query/pg-core/columns/integer.ts +53 -0
  279. package/src/data/query/pg-core/columns/interval.ts +83 -0
  280. package/src/data/query/pg-core/columns/json.ts +58 -0
  281. package/src/data/query/pg-core/columns/jsonb.ts +56 -0
  282. package/src/data/query/pg-core/columns/line.ts +117 -0
  283. package/src/data/query/pg-core/columns/macaddr.ts +43 -0
  284. package/src/data/query/pg-core/columns/macaddr8.ts +43 -0
  285. package/src/data/query/pg-core/columns/numeric.ts +227 -0
  286. package/src/data/query/pg-core/columns/point.ts +127 -0
  287. package/src/data/query/pg-core/columns/postgis_extension/geometry.ts +119 -0
  288. package/src/data/query/pg-core/columns/postgis_extension/utils.ts +47 -0
  289. package/src/data/query/pg-core/columns/real.ts +58 -0
  290. package/src/data/query/pg-core/columns/serial.ts +55 -0
  291. package/src/data/query/pg-core/columns/smallint.ts +53 -0
  292. package/src/data/query/pg-core/columns/smallserial.ts +60 -0
  293. package/src/data/query/pg-core/columns/text.ts +67 -0
  294. package/src/data/query/pg-core/columns/time.ts +72 -0
  295. package/src/data/query/pg-core/columns/timestamp.ts +163 -0
  296. package/src/data/query/pg-core/columns/uuid.ts +51 -0
  297. package/src/data/query/pg-core/columns/varchar.ts +89 -0
  298. package/src/data/query/pg-core/columns/vector_extension/bit.ts +68 -0
  299. package/src/data/query/pg-core/columns/vector_extension/halfvec.ts +80 -0
  300. package/src/data/query/pg-core/columns/vector_extension/sparsevec.ts +67 -0
  301. package/src/data/query/pg-core/columns/vector_extension/vector.ts +80 -0
  302. package/src/data/query/pg-core/db.ts +674 -0
  303. package/src/data/query/pg-core/dialect.ts +1404 -0
  304. package/src/data/query/pg-core/expressions.ts +25 -0
  305. package/src/data/query/pg-core/foreign-keys.ts +130 -0
  306. package/src/data/query/pg-core/index.ts +21 -0
  307. package/src/data/query/pg-core/indexes.ts +276 -0
  308. package/src/data/query/pg-core/policies.ts +56 -0
  309. package/src/data/query/pg-core/primary-keys.ts +62 -0
  310. package/src/data/query/pg-core/query-builders/count.ts +86 -0
  311. package/src/data/query/pg-core/query-builders/delete.ts +293 -0
  312. package/src/data/query/pg-core/query-builders/index.ts +7 -0
  313. package/src/data/query/pg-core/query-builders/insert.ts +452 -0
  314. package/src/data/query/pg-core/query-builders/query-builder.ts +123 -0
  315. package/src/data/query/pg-core/query-builders/query.ts +157 -0
  316. package/src/data/query/pg-core/query-builders/raw.ts +49 -0
  317. package/src/data/query/pg-core/query-builders/refresh-materialized-view.ts +108 -0
  318. package/src/data/query/pg-core/query-builders/select.ts +1346 -0
  319. package/src/data/query/pg-core/query-builders/select.types.ts +437 -0
  320. package/src/data/query/pg-core/query-builders/update.ts +626 -0
  321. package/src/data/query/pg-core/roles.ts +41 -0
  322. package/src/data/query/pg-core/schema.ts +72 -0
  323. package/src/data/query/pg-core/sequence.ts +41 -0
  324. package/src/data/query/pg-core/session.ts +292 -0
  325. package/src/data/query/pg-core/subquery.ts +46 -0
  326. package/src/data/query/pg-core/table.ts +252 -0
  327. package/src/data/query/pg-core/unique-constraint.ts +73 -0
  328. package/src/data/query/pg-core/utils/array.ts +95 -0
  329. package/src/data/query/pg-core/utils/index.ts +1 -0
  330. package/src/data/query/pg-core/utils.ts +104 -0
  331. package/src/data/query/pg-core/view-base.ts +14 -0
  332. package/src/data/query/pg-core/view-common.ts +1 -0
  333. package/src/data/query/pg-core/view.ts +435 -0
  334. package/src/data/query/pg-core/with-builder.ts +41 -0
  335. package/src/data/query/postgres-js/README.md +46 -0
  336. package/src/data/query/postgres-js/array-parser.ts +45 -0
  337. package/src/data/query/postgres-js/driver-error-boundary.ts +149 -0
  338. package/src/data/query/postgres-js/driver.ts +163 -0
  339. package/src/data/query/postgres-js/index.ts +2 -0
  340. package/src/data/query/postgres-js/session.ts +234 -0
  341. package/src/data/query/primary-key.ts +11 -0
  342. package/src/data/query/query-builders/query-builder.ts +19 -0
  343. package/src/data/query/query-builders/select.types.ts +175 -0
  344. package/src/data/query/query-promise.ts +35 -0
  345. package/src/data/query/relations.ts +725 -0
  346. package/src/data/query/runnable-query.ts +12 -0
  347. package/src/data/query/selection-proxy.ts +121 -0
  348. package/src/data/query/session.ts +8 -0
  349. package/src/data/query/sql/expressions/conditions.ts +741 -0
  350. package/src/data/query/sql/expressions/index.ts +2 -0
  351. package/src/data/query/sql/expressions/select.ts +45 -0
  352. package/src/data/query/sql/functions/aggregate.ts +129 -0
  353. package/src/data/query/sql/functions/index.ts +2 -0
  354. package/src/data/query/sql/functions/vector.ts +178 -0
  355. package/src/data/query/sql/index.ts +3 -0
  356. package/src/data/query/sql/sql.ts +794 -0
  357. package/src/data/query/subquery.ts +53 -0
  358. package/src/data/query/table.ts +208 -0
  359. package/src/data/query/table.utils.ts +2 -0
  360. package/src/data/query/tracing-utils.ts +3 -0
  361. package/src/data/query/tracing.ts +11 -0
  362. package/src/data/query/utils.ts +357 -0
  363. package/src/data/query/version.ts +4 -0
  364. package/src/data/query/view-common.ts +1 -0
  365. package/src/data/schema/branded.ts +13 -0
  366. package/src/data/schema/generated/coerce.ts +5 -0
  367. package/src/data/schema/generated/combinator.ts +6 -0
  368. package/src/data/schema/generated/encoding.ts +5 -0
  369. package/src/data/schema/generated/error.ts +6 -0
  370. package/src/data/schema/generated/format.ts +5 -0
  371. package/src/data/schema/generated/type-constructor.ts +10 -0
  372. package/src/data/schema/generated/type.ts +6 -0
  373. package/src/data/schema/index.ts +6 -0
  374. package/src/data/schema/json-schema.ts +33 -0
  375. package/src/data/schema/namespace.ts +55 -0
  376. package/src/data/schema/parse.ts +39 -0
  377. package/src/data/schema/types.ts +39 -0
  378. package/src/data/temporal/append-only.ts +134 -0
  379. package/src/data/temporal/close.ts +27 -0
  380. package/src/data/temporal/columns.ts +10 -0
  381. package/src/data/temporal/definition-queries.ts +70 -0
  382. package/src/data/temporal/errors.ts +10 -0
  383. package/src/data/temporal/get-current.ts +22 -0
  384. package/src/data/temporal/index.ts +9 -0
  385. package/src/data/temporal/namespace.ts +36 -0
  386. package/src/data/temporal/query.ts +25 -0
  387. package/src/data/temporal/resolve.ts +17 -0
  388. package/src/data/temporal/revise.ts +67 -0
  389. package/src/data/temporal/types.ts +22 -0
  390. package/src/data/valve/artifact.ts +33 -0
  391. package/src/data/valve/contract-diff.ts +21 -0
  392. package/src/data/valve/cross.ts +57 -0
  393. package/src/data/valve/crossing-ledger.ts +50 -0
  394. package/src/data/valve/decision.ts +44 -0
  395. package/src/data/valve/durable-crossing.ts +52 -0
  396. package/src/data/valve/execute-crossing.ts +77 -0
  397. package/src/data/valve/faucet.ts +56 -0
  398. package/src/data/valve/gate.ts +114 -0
  399. package/src/data/valve/graph.ts +77 -0
  400. package/src/data/valve/index.ts +53 -0
  401. package/src/data/valve/namespace.ts +57 -0
  402. package/src/data/valve/registry.ts +50 -0
  403. package/src/data/valve/schema/valve-crossing-fields.ts +23 -0
  404. package/src/data/valve/schema/valve-crossing.ts +9 -0
  405. package/src/data/valve/supply.ts +154 -0
  406. package/src/data/valve/types.ts +143 -0
  407. package/src/data/vector/columns.ts +27 -0
  408. package/src/data/vector/index.ts +4 -0
  409. package/src/data/vector/namespace.ts +33 -0
  410. package/src/data/vector/query.ts +85 -0
  411. package/src/data/vector/validate.ts +16 -0
  412. package/src/data/write/audit/actor.ts +15 -0
  413. package/src/data/write/audit/audit-columns.ts +18 -0
  414. package/src/data/write/audit/audit-select.ts +111 -0
  415. package/src/data/write/audit/bound-transaction.ts +14 -0
  416. package/src/data/write/audit/cte-compile.ts +32 -0
  417. package/src/data/write/audit/decode-returning.ts +47 -0
  418. package/src/data/write/audit/final-select.ts +43 -0
  419. package/src/data/write/audit/infra-audit.ts +36 -0
  420. package/src/data/write/audit/primary-key.ts +65 -0
  421. package/src/data/write/audit/projection.ts +41 -0
  422. package/src/data/write/audit/read-touches.ts +86 -0
  423. package/src/data/write/audit/touch-match.ts +78 -0
  424. package/src/data/write/audit/verb-delete.ts +62 -0
  425. package/src/data/write/audit/verb-insert.ts +36 -0
  426. package/src/data/write/audit/verb-select.ts +47 -0
  427. package/src/data/write/audit/verb-update.ts +60 -0
  428. package/src/data/write/audit/verb-upsert.ts +104 -0
  429. package/src/data/write/conflict.ts +43 -0
  430. package/src/data/write/delete.ts +38 -0
  431. package/src/data/write/execute.ts +243 -0
  432. package/src/data/write/index.ts +11 -0
  433. package/src/data/write/insert.ts +63 -0
  434. package/src/data/write/state.ts +43 -0
  435. package/src/data/write/update.ts +54 -0
  436. package/src/data/write/upsert.ts +58 -0
  437. package/src/data/write/values.ts +19 -0
  438. package/src/index.ts +230 -0
  439. package/src/infra/access-links/code.ts +70 -0
  440. package/src/infra/access-links/handle.ts +892 -0
  441. package/src/infra/access-links/index.ts +9 -0
  442. package/src/infra/access-links/namespace.ts +30 -0
  443. package/src/infra/access-links/person-check.ts +177 -0
  444. package/src/infra/access-links/schema.ts +71 -0
  445. package/src/infra/alerts/async-sources.ts +231 -0
  446. package/src/infra/alerts/audit.ts +36 -0
  447. package/src/infra/alerts/beat.ts +123 -0
  448. package/src/infra/alerts/client-faults.ts +111 -0
  449. package/src/infra/alerts/faults.ts +156 -0
  450. package/src/infra/alerts/handle.ts +778 -0
  451. package/src/infra/alerts/http.ts +192 -0
  452. package/src/infra/alerts/index.ts +12 -0
  453. package/src/infra/alerts/infra-sources.ts +180 -0
  454. package/src/infra/alerts/namespace.ts +81 -0
  455. package/src/infra/alerts/redact.ts +21 -0
  456. package/src/infra/alerts/request-faults.ts +41 -0
  457. package/src/infra/alerts/schema.ts +124 -0
  458. package/src/infra/alerts/sinks.ts +125 -0
  459. package/src/infra/alerts/source-options.ts +26 -0
  460. package/src/infra/alerts/sources.ts +503 -0
  461. package/src/infra/alerts/subsystem-sources.ts +137 -0
  462. package/src/infra/audit/emit.ts +87 -0
  463. package/src/infra/audit/index.ts +2 -0
  464. package/src/infra/audit/namespace.ts +22 -0
  465. package/src/infra/audit/schema.sql +35 -0
  466. package/src/infra/audit/types.ts +18 -0
  467. package/src/infra/aws/credentials.ts +147 -0
  468. package/src/infra/aws/sigv4.ts +140 -0
  469. package/src/infra/cache/create.ts +58 -0
  470. package/src/infra/cache/entry.ts +81 -0
  471. package/src/infra/cache/index.ts +4 -0
  472. package/src/infra/cache/namespace.ts +10 -0
  473. package/src/infra/cache/tenant.ts +93 -0
  474. package/src/infra/cache/types.ts +24 -0
  475. package/src/infra/crawl/audit.ts +35 -0
  476. package/src/infra/crawl/errors.ts +25 -0
  477. package/src/infra/crawl/handle.ts +693 -0
  478. package/src/infra/crawl/index.ts +9 -0
  479. package/src/infra/crawl/links.ts +160 -0
  480. package/src/infra/crawl/namespace.ts +36 -0
  481. package/src/infra/crawl/robots.ts +100 -0
  482. package/src/infra/crawl/schema.ts +89 -0
  483. package/src/infra/credit-line/handle.ts +883 -0
  484. package/src/infra/credit-line/index.ts +6 -0
  485. package/src/infra/credit-line/namespace.ts +7 -0
  486. package/src/infra/credit-line/schema.ts +96 -0
  487. package/src/infra/credits/handle.ts +706 -0
  488. package/src/infra/credits/index.ts +5 -0
  489. package/src/infra/credits/namespace.ts +7 -0
  490. package/src/infra/credits/schema.ts +83 -0
  491. package/src/infra/email/audit.ts +36 -0
  492. package/src/infra/email/inbound.ts +622 -0
  493. package/src/infra/email/index.ts +11 -0
  494. package/src/infra/email/mail.ts +517 -0
  495. package/src/infra/email/mime.ts +447 -0
  496. package/src/infra/email/namespace.ts +22 -0
  497. package/src/infra/email/providers.ts +111 -0
  498. package/src/infra/email/schema.ts +96 -0
  499. package/src/infra/email/sns.ts +200 -0
  500. package/src/infra/email/unsubscribe.ts +44 -0
  501. package/src/infra/fetch/address.ts +88 -0
  502. package/src/infra/fetch/connect-retry.ts +87 -0
  503. package/src/infra/fetch/handle.ts +329 -0
  504. package/src/infra/fetch/index.ts +7 -0
  505. package/src/infra/fetch/namespace.ts +70 -0
  506. package/src/infra/idempotency/index.ts +3 -0
  507. package/src/infra/idempotency/middleware.ts +87 -0
  508. package/src/infra/idempotency/store.ts +92 -0
  509. package/src/infra/idempotency/types.ts +30 -0
  510. package/src/infra/join-requests/handle.ts +312 -0
  511. package/src/infra/join-requests/index.ts +2 -0
  512. package/src/infra/join-requests/namespace.ts +7 -0
  513. package/src/infra/join-requests/schema.ts +33 -0
  514. package/src/infra/outbox/core.ts +165 -0
  515. package/src/infra/quota/errors.ts +32 -0
  516. package/src/infra/quota/handle.ts +215 -0
  517. package/src/infra/quota/index.ts +5 -0
  518. package/src/infra/quota/namespace.ts +7 -0
  519. package/src/infra/quota/schema.ts +15 -0
  520. package/src/infra/storage/backend.ts +34 -0
  521. package/src/infra/storage/handle.ts +513 -0
  522. package/src/infra/storage/index.ts +9 -0
  523. package/src/infra/storage/local.ts +104 -0
  524. package/src/infra/storage/namespace.ts +13 -0
  525. package/src/infra/storage/s3.ts +257 -0
  526. package/src/infra/storage/schema.ts +24 -0
  527. package/src/infra/support/handle.ts +854 -0
  528. package/src/infra/support/http.ts +229 -0
  529. package/src/infra/support/index.ts +7 -0
  530. package/src/infra/support/namespace.ts +12 -0
  531. package/src/infra/support/schema.ts +96 -0
  532. package/src/infra/webhook/inbound.ts +45 -0
  533. package/src/infra/webhook/index.ts +5 -0
  534. package/src/infra/webhook/namespace.ts +24 -0
  535. package/src/infra/webhook/outbound.ts +315 -0
  536. package/src/infra/webhook/sign.ts +32 -0
  537. package/src/infra/webhook/verify.ts +82 -0
  538. package/src/invariants/assert.ts +26 -0
  539. package/src/invariants/index.ts +1 -0
  540. package/src/lint/config.ts +56 -0
  541. package/src/lint/doctrine.ts +163 -0
  542. package/src/lint/include.ts +97 -0
  543. package/src/lint/index.ts +3 -0
  544. package/src/lint/preset.ts +10 -0
  545. package/src/lint/resolver.ts +218 -0
  546. package/src/lint/rules/no-reexports.ts +46 -0
  547. package/src/request/behavior/audited.ts +85 -0
  548. package/src/request/behavior/cached.ts +62 -0
  549. package/src/request/behavior/compose.ts +28 -0
  550. package/src/request/behavior/index.ts +5 -0
  551. package/src/request/behavior/namespace.ts +16 -0
  552. package/src/request/behavior/types.ts +26 -0
  553. package/src/request/behavior/versioned.ts +54 -0
  554. package/src/request/context/create.ts +103 -0
  555. package/src/request/context/guard.ts +49 -0
  556. package/src/request/context/index.ts +4 -0
  557. package/src/request/context/run-in-transaction.ts +200 -0
  558. package/src/request/context/store.ts +28 -0
  559. package/src/request/context/types.ts +50 -0
  560. package/src/request/di/builder.ts +535 -0
  561. package/src/request/di/index.ts +8 -0
  562. package/src/request/di/resolve.ts +271 -0
  563. package/src/request/di/types.ts +262 -0
  564. package/src/request/fitting/namespace.ts +7 -0
  565. package/src/request/fixture/batch.ts +71 -0
  566. package/src/request/fixture/builder.ts +26 -0
  567. package/src/request/fixture/errors.ts +10 -0
  568. package/src/request/fixture/index.ts +8 -0
  569. package/src/request/fixture/namespace.ts +23 -0
  570. package/src/request/fixture/paginate.ts +44 -0
  571. package/src/request/fixture/to-handlers.ts +260 -0
  572. package/src/request/fixture/types.ts +79 -0
  573. package/src/request/flow/act-as.ts +78 -0
  574. package/src/request/flow/namespace.ts +17 -0
  575. package/src/request/http/error-step.ts +42 -0
  576. package/src/request/http/file.ts +141 -0
  577. package/src/request/http/index.ts +4 -0
  578. package/src/request/http/middleware.ts +141 -0
  579. package/src/request/http/namespace.ts +5 -0
  580. package/src/request/http/rate-limit-classes.ts +170 -0
  581. package/src/request/http/response.ts +82 -0
  582. package/src/request/http/server.ts +916 -0
  583. package/src/request/http/sniff.ts +261 -0
  584. package/src/request/http/types.ts +250 -0
  585. package/src/request/http/upload.ts +284 -0
  586. package/src/request/internal/text.ts +6 -0
  587. package/src/request/openapi/generate.ts +205 -0
  588. package/src/request/openapi/index.ts +2 -0
  589. package/src/request/openapi/namespace.ts +7 -0
  590. package/src/test/auth.ts +13 -0
  591. package/src/test/auto-setup.ts +45 -0
  592. package/src/test/context.ts +91 -0
  593. package/src/test/crawl-fixture.ts +157 -0
  594. package/src/test/discover-fork.ts +17 -0
  595. package/src/test/fingerprint.ts +135 -0
  596. package/src/test/globals.d.ts +7 -0
  597. package/src/test/index.ts +19 -0
  598. package/src/test/namespace.ts +87 -0
  599. package/src/test/plugin.ts +189 -0
  600. package/src/test/progress.ts +185 -0
  601. package/src/test/reap-after-run.ts +28 -0
  602. package/src/test/reap.ts +286 -0
  603. package/src/test/reset.ts +253 -0
  604. package/src/test/savepoints.ts +172 -0
  605. package/src/test/setup.ts +1422 -0
  606. package/src/test/system-channel.ts +50 -0
  607. package/src/test/vitest-plugin.ts +142 -0
  608. package/src/test/vitest-workspace.ts +507 -0
  609. package/src/test/vitest.ts +333 -0
  610. package/src/test/watch.ts +105 -0
  611. package/src/trace/capture/bounded-serializer.ts +146 -0
  612. package/src/trace/cardinality/analyze.ts +173 -0
  613. package/src/trace/cardinality/branch-context.ts +214 -0
  614. package/src/trace/cardinality/check.ts +33 -0
  615. package/src/trace/cardinality/classify-edge.ts +64 -0
  616. package/src/trace/cardinality/structural-max.ts +107 -0
  617. package/src/trace/check/coverage-check.ts +188 -0
  618. package/src/trace/check/flow-check.ts +103 -0
  619. package/src/trace/check/flow-step-site.ts +234 -0
  620. package/src/trace/check/io-pattern.ts +116 -0
  621. package/src/trace/codemod/add-cardinality.ts +152 -0
  622. package/src/trace/codemod/run.ts +80 -0
  623. package/src/trace/entry/child-trace.ts +21 -0
  624. package/src/trace/entry/close-trace.ts +87 -0
  625. package/src/trace/entry/finalize-trace.ts +28 -0
  626. package/src/trace/entry/open-trace.ts +138 -0
  627. package/src/trace/entry/parent-trace.ts +20 -0
  628. package/src/trace/entry/parse-traceparent.ts +39 -0
  629. package/src/trace/entry/process-start.ts +38 -0
  630. package/src/trace/entry/trace-context.ts +26 -0
  631. package/src/trace/entry/tracing-enabled.ts +10 -0
  632. package/src/trace/external/record.ts +195 -0
  633. package/src/trace/external/report.ts +66 -0
  634. package/src/trace/namespace.ts +19 -0
  635. package/src/trace/partition/day.ts +47 -0
  636. package/src/trace/partition/index.ts +14 -0
  637. package/src/trace/partition/partition-name.ts +23 -0
  638. package/src/trace/partition/plan.ts +82 -0
  639. package/src/trace/partition/schedule.ts +26 -0
  640. package/src/trace/partition/startup-hook.ts +39 -0
  641. package/src/trace/partition/sync.ts +154 -0
  642. package/src/trace/partition/tables.ts +53 -0
  643. package/src/trace/read/tree.ts +83 -0
  644. package/src/trace/retain/copy-on-error.ts +76 -0
  645. package/src/trace/runtime/expire-stale-traces.ts +54 -0
  646. package/src/trace/runtime/flow-step.ts +147 -0
  647. package/src/trace/runtime/flush-steps.ts +31 -0
  648. package/src/trace/runtime/pending-writes.ts +54 -0
  649. package/src/trace/runtime/step-buffer.ts +298 -0
  650. package/src/trace/runtime/step-error.ts +21 -0
  651. package/src/trace/runtime/step-meta.ts +55 -0
  652. package/src/trace/runtime/thenable.ts +12 -0
  653. package/src/trace/runtime/truncate-trace.ts +62 -0
  654. package/src/trace/schema/trace-fields.ts +52 -0
  655. package/src/trace/schema/trace-retained.ts +22 -0
  656. package/src/trace/schema/trace-step-fields.ts +44 -0
  657. package/src/trace/schema/trace-step-retained.ts +25 -0
  658. package/src/trace/schema/trace-step.ts +18 -0
  659. package/src/trace/schema/trace.ts +12 -0
  660. package/src/trace/transformer/apply.ts +79 -0
  661. package/src/trace/transformer/build-meta-literal.ts +30 -0
  662. package/src/trace/transformer/find-flow-step-sites.ts +85 -0
  663. package/src/trace/transformer/inject-flow-step-import.ts +39 -0
  664. package/src/trace/transformer/transform-error.ts +14 -0
  665. package/src/trace/transformer/transform-flow-steps.ts +123 -0
  666. package/src/trace/transformer/wrap-function-declaration.ts +62 -0
  667. package/src/trace/transformer/wrap-variable-declaration.ts +67 -0
  668. package/src/workspace/affected.ts +110 -0
  669. package/src/workspace/changed-files.ts +163 -0
  670. package/src/workspace/check-deps.ts +213 -0
  671. package/src/workspace/checksum.ts +20 -0
  672. package/src/workspace/dependency-graph.ts +67 -0
  673. package/src/workspace/detect-pipework-copies.ts +93 -0
  674. package/src/workspace/discover.ts +206 -0
  675. package/src/workspace/generate.ts +401 -0
  676. package/src/workspace/index.ts +7 -0
  677. package/src/workspace/module-imports-pipework.ts +39 -0
  678. package/src/workspace/module-package-name.ts +12 -0
  679. package/src/workspace/scrub-git-env.ts +23 -0
  680. package/src/workspace/source-paths.ts +36 -0
  681. package/src/workspace/sync.ts +182 -0
  682. package/dist/REFERENCE.md +0 -1477
  683. package/dist/infra/access-links/rate-limit.d.ts +0 -10
  684. package/dist/infra/access-links/rate-limit.d.ts.map +0 -1
  685. package/dist/infra/access-links/rate-limit.js +0 -28
  686. package/dist/infra/access-links/rate-limit.js.map +0 -1
  687. package/dist/infra/email/sigv4.d.ts +0 -24
  688. package/dist/infra/email/sigv4.d.ts.map +0 -1
  689. package/dist/infra/email/sigv4.js +0 -55
  690. package/dist/infra/email/sigv4.js.map +0 -1
@@ -0,0 +1,111 @@
1
+ // Builds the SELECT projection inside the `audit_inserts` CTE — one row per
2
+ // (affected-db-row × `@touches` target). The verb composers (insert / update
3
+ // / delete / upsert) supply the data-source clause (`FROM ...`) and the
4
+ // `before` / `after` jsonb expressions; everything else — the actor/tenant/
5
+ // trace fields, the entity/verb literals, the captured_inputs literal — is
6
+ // constant across rows and lives here.
7
+ //
8
+ // `@touches Entity:verb` is multi-target: a single write can carry several
9
+ // targets, and each one emits its own audit row per affected db row. We
10
+ // model that as `UNION ALL` over one SELECT per target, sharing the same
11
+ // FROM. (Postgres folds these into a single insert plan.) Empty targets is
12
+ // a programmer error — the executor short-circuits to the vanilla branch.
13
+
14
+ import { sql } from '../../db/sql.js'
15
+ import type { SQL } from '../../db/types.js'
16
+ import type { ActiveAuditStep } from '../../../trace/runtime/step-buffer.js'
17
+ import type { MetaTouch } from '../../../trace/runtime/step-meta.js'
18
+ import { quoteIdentifier } from '../../db/identifiers.js'
19
+ import { camelToSnake } from '../../domain/naming.js'
20
+ import { declaredPrimaryKeyFieldKeys } from './primary-key.js'
21
+ import type { DefinedTable } from '../../domain/types.js'
22
+
23
+ /**
24
+ * SQL for `audit_record.entity_id` read from CTE alias `alias`: the single key
25
+ * column cast to text, or a compact JSON array of the key columns as text for
26
+ * a composite key; null for a table with no declared key. Matches `auditEntityId` in `primary-key.ts`.
27
+ */
28
+ export function buildEntityIdExpr(
29
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- see primaryKeyFieldKeys
30
+ table: DefinedTable<any>,
31
+ alias: string,
32
+ ): SQL {
33
+ const keys = declaredPrimaryKeyFieldKeys(table)
34
+ if (keys.length === 0) return sql`NULL::text`
35
+ const cols = keys.map(k => `${alias}.${quoteIdentifier(camelToSnake(k), 'pk column')}::text`)
36
+ return cols.length === 1
37
+ ? sql.raw(cols[0]!)
38
+ : sql.raw(`array_to_json(ARRAY[${cols.join(', ')}])::text`)
39
+ }
40
+
41
+ /**
42
+ * Information about the active flow_step plus the actor/tenant the row is
43
+ * attributed to. The CTE compiler resolves these from the active `Flow` and
44
+ * passes them in — keeps the SQL composer free of context-store coupling.
45
+ */
46
+ export interface AuditSelectContext {
47
+ readonly step: ActiveAuditStep
48
+ /** The touches the write is recorded under — one audit row per affected row per touch. */
49
+ readonly touches: readonly MetaTouch[]
50
+ /** Upsert only: which touches take newly inserted rows (`create`) and which take updated rows (`update`). Absent: every touch takes every row. */
51
+ readonly split?: { readonly create: readonly MetaTouch[]; readonly update: readonly MetaTouch[] } | undefined
52
+ /** The touches the executor marks written once the write lands. */
53
+ readonly written: readonly MetaTouch[]
54
+ /** `auth?.userId`, or null when the trace ran without an authenticated principal. */
55
+ readonly actorId: string | null
56
+ /** `flow.tenant`, or null when the entry was not tenant-scoped. */
57
+ readonly tenantId: string | null
58
+ }
59
+
60
+ /**
61
+ * Composes the `audit_inserts` CTE body: one `SELECT` per `@touches` target,
62
+ * joined by `UNION ALL`, projecting onto the `audit_record` column order.
63
+ *
64
+ * The shape is positional — matches `AUDIT_COLUMNS_SQL` exactly:
65
+ * id, trace_day, trace_id, trace_step_id, tenant_id, actor_id,
66
+ * entity, verb, entity_id, before, after, captured_inputs, occurred_at
67
+ *
68
+ * `from` is the verb's contributing FROM clause (e.g. `FROM updated u LEFT
69
+ * JOIN before b ON ...`), `before` / `after` are the per-row jsonb
70
+ * expressions to project (or `NULL::jsonb` when the half doesn't apply).
71
+ */
72
+ export function buildAuditInserts(
73
+ ctx: AuditSelectContext,
74
+ from: SQL,
75
+ entityId: SQL,
76
+ before: SQL,
77
+ after: SQL,
78
+ /** Optional per-touch predicate appended to that touch's SELECT as a WHERE. */
79
+ rowFilter?: (touch: MetaTouch) => SQL | null,
80
+ ): SQL {
81
+ const capturedInputs = ctx.step.capturedInputs
82
+ const capturedJson = capturedInputs === null
83
+ ? sql`NULL::jsonb`
84
+ : sql`${JSON.stringify(capturedInputs)}::jsonb`
85
+
86
+ const selects = ctx.touches.map(t => sql`
87
+ SELECT
88
+ gen_random_uuid(),
89
+ ${ctx.step.traceDay}::date,
90
+ ${ctx.step.traceId}::uuid,
91
+ ${ctx.step.stepId}::uuid,
92
+ ${ctx.tenantId}::uuid,
93
+ ${ctx.actorId},
94
+ ${t.entity},
95
+ ${t.verb},
96
+ ${entityId},
97
+ ${before},
98
+ ${after},
99
+ ${capturedJson},
100
+ now()
101
+ ${from}
102
+ ${filterFor(rowFilter, t)}
103
+ `)
104
+
105
+ return sql.join(selects, sql` UNION ALL `)
106
+ }
107
+
108
+ function filterFor(rowFilter: ((t: MetaTouch) => SQL | null) | undefined, t: MetaTouch): SQL {
109
+ const f = rowFilter?.(t) ?? null
110
+ return f === null ? sql`` : sql`WHERE ${f}`
111
+ }
@@ -0,0 +1,14 @@
1
+ // The Flow's open transaction on a database, for audit writers that must not
2
+ // wait on it from a second connection. Null when none is bound.
3
+
4
+ import type { DB } from '../../db/index.js'
5
+ import type { Flow } from '../../../request/context/types.js'
6
+ import { verifyTransactionBinding } from '../../../request/context/guard.js'
7
+
8
+ export function boundTransaction(flow: Flow, database?: string): DB | null {
9
+ const name = database ?? flow.instance.config.defaultDatabaseName()
10
+ const bound = flow.transactions.get(name)
11
+ if (bound === undefined) return null
12
+ verifyTransactionBinding(flow, bound, name)
13
+ return bound.drizzle
14
+ }
@@ -0,0 +1,32 @@
1
+ // Entry point for the write+audit CTE generator. Routes by `state.verb` to
2
+ // the per-verb composer. Empty `touches` is a programmer error — the caller
3
+ // (executor) is expected to fall through to the vanilla branch when the
4
+ // active step has no audit projection. We assert here so a regression in
5
+ // the executor surfaces immediately rather than producing an audit row
6
+ // with empty entity/verb.
7
+
8
+ import { compileInsertAudit } from './verb-insert.js'
9
+ import { compileUpdateAudit } from './verb-update.js'
10
+ import { compileDeleteAudit } from './verb-delete.js'
11
+ import { compileUpsertAudit } from './verb-upsert.js'
12
+ import type { SQL } from '../../db/types.js'
13
+ import type { WriteState } from '../state.js'
14
+ import type { AuditSelectContext } from './audit-select.js'
15
+
16
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- drizzle's DB type is deeply generic; the executor passes the resolved per-database handle through unchanged
17
+ type AnyDb = any
18
+
19
+ export function compileAuditWrite(db: AnyDb, state: WriteState, ctx: AuditSelectContext): SQL {
20
+ if (ctx.touches.length === 0) {
21
+ throw new Error(
22
+ `[pipework] compileAuditWrite called with empty touches on "${state.table.$name}". ` +
23
+ `Active step "${ctx.step.stepId}" has no @touches matching this table — the executor should have taken the vanilla DML branch.`,
24
+ )
25
+ }
26
+ switch (state.verb) {
27
+ case 'insert': return compileInsertAudit(db, state, ctx)
28
+ case 'update': return compileUpdateAudit(db, state, ctx)
29
+ case 'delete': return compileDeleteAudit(db, state, ctx)
30
+ case 'upsert': return compileUpsertAudit(db, state, ctx)
31
+ }
32
+ }
@@ -0,0 +1,47 @@
1
+ // Decodes the CTE statement's raw rows the way drizzle decodes a vanilla
2
+ // `.returning()`: each value is read from its physical column and run through
3
+ // the column's `mapFromDriverValue` (timestamps to Date, bigint, json, ...),
4
+ // nulls passing through. Keys are the table's field keys for `returning('all')`
5
+ // and the caller's aliases for a per-key projection.
6
+
7
+ import { physicalColumnName } from './final-select.js'
8
+ import type { WriteState } from '../state.js'
9
+
10
+ type Decoder = (value: unknown) => unknown
11
+ interface Slot { readonly key: string; readonly physical: string; readonly decode: Decoder | undefined }
12
+
13
+ export function decodeReturning(state: WriteState, rows: unknown): unknown {
14
+ const returning = state.returning
15
+ if (returning === undefined || returning === null) return undefined
16
+ if (!Array.isArray(rows)) return rows
17
+ const slots = slotsFor(state)
18
+ return rows.map(row => {
19
+ const raw = row as Record<string, unknown>
20
+ const out: Record<string, unknown> = {}
21
+ for (const slot of slots) {
22
+ const value = raw[slot.physical]
23
+ out[slot.key] = value === null || value === undefined || slot.decode === undefined ? value : slot.decode(value)
24
+ }
25
+ return out
26
+ })
27
+ }
28
+
29
+ function slotsFor(state: WriteState): Slot[] {
30
+ const returning = state.returning
31
+ const table = state.table as unknown as Record<string, unknown>
32
+ if (returning === 'all' || returning === null || returning === undefined || Object.keys(returning).length === 0) {
33
+ return Object.keys(state.table.$fields).map(key => slot(key, key, table[key]))
34
+ }
35
+ // Per-key rows are aliased by the final select, so they are read by alias.
36
+ return Object.keys(returning).map(key => ({ key, physical: key, decode: decoderOf(returning[key]) }))
37
+ }
38
+
39
+ function slot(key: string, alias: string, column: unknown): Slot {
40
+ return { key, physical: physicalColumnName(alias, column), decode: decoderOf(column) }
41
+ }
42
+
43
+ function decoderOf(column: unknown): Decoder | undefined {
44
+ const fn = (column as { mapFromDriverValue?: unknown } | null)?.mapFromDriverValue
45
+ if (typeof fn !== 'function') return undefined
46
+ return (v: unknown) => (fn as Decoder).call(column, v)
47
+ }
@@ -0,0 +1,43 @@
1
+ // Builds the final SELECT of the CTE statement — the row(s) the caller sees.
2
+ // Mirrors the shape `vanilla DML` would have produced for the same `returning`:
3
+ // no `returning` → no rows (one placeholder row per written row under onConflict); `returning('all')` → SELECT *; per-key projection
4
+ // → aliased column list. Reads from the verb's post-image CTE (`inserted` /
5
+ // `updated` / `deleted` / `upserted`).
6
+
7
+ import { sql } from '../../db/sql.js'
8
+ import { quoteIdentifier } from '../../db/identifiers.js'
9
+ import { camelToSnake } from '../../domain/naming.js'
10
+ import type { SQL } from '../../db/types.js'
11
+ import type { WriteState } from '../state.js'
12
+
13
+ export function buildFinalSelect(state: WriteState, sourceCte: string): SQL {
14
+ const source = quoteIdentifier(sourceCte, 'CTE alias')
15
+ const returning = state.returning ?? null
16
+ if (returning === null) {
17
+ // Under onConflictDoNothing the rows written are counted from the rows returned.
18
+ if (state.onConflict !== undefined) return sql.raw(`SELECT 1 FROM ${source}`)
19
+ return sql.raw(`SELECT 1 FROM ${source} WHERE FALSE`)
20
+ }
21
+ if (returning === 'all') {
22
+ return sql.raw(`SELECT * FROM ${source}`)
23
+ }
24
+ const keys = Object.keys(returning)
25
+ if (keys.length === 0) return sql.raw(`SELECT * FROM ${source}`)
26
+ const cols = keys.map(k => {
27
+ const physical = quoteIdentifier(physicalColumnName(k, returning[k]), 'returning column')
28
+ const alias = quoteIdentifier(k, 'returning alias')
29
+ return `${source}.${physical} AS ${alias}`
30
+ }).join(', ')
31
+ return sql.raw(`SELECT ${cols} FROM ${source}`)
32
+ }
33
+
34
+ /** The projected column's own SQL name when `value` is a column; otherwise the alias, snake-cased. */
35
+ export function physicalColumnName(alias: string, value: unknown): string {
36
+ const name = (value as { name?: unknown } | null)?.name
37
+ if (isColumn(value) && typeof name === 'string') return name
38
+ return camelToSnake(alias)
39
+ }
40
+
41
+ export function isColumn(value: unknown): value is { name: string; mapFromDriverValue?: (v: unknown) => unknown } {
42
+ return typeof value === 'object' && value !== null && 'columnType' in value
43
+ }
@@ -0,0 +1,36 @@
1
+ // Writes one audit_record for an infrastructure operation (access links,
2
+ // credits) into the open trace step, in the operation's own transaction.
3
+ // No trace or no open step: nothing is written. A failed write fails the
4
+ // operation: the error reaches the caller and its transaction rolls back, so
5
+ // no row commits without its audit record. The database error stays on `cause`.
6
+
7
+ import { sql } from '../../db/sql.js'
8
+ import type { DB } from '../../db/index.js'
9
+ import type { Flow } from '../../../request/context/types.js'
10
+ import { resolveActorId } from './actor.js'
11
+
12
+ export interface InfraAudit {
13
+ readonly tenantId: string
14
+ readonly entity: string
15
+ readonly verb: string
16
+ readonly entityId: string
17
+ readonly before?: Record<string, unknown>
18
+ readonly after: Record<string, unknown>
19
+ }
20
+
21
+ export async function writeInfraAudit(flow: Flow, tx: DB, record: InfraAudit): Promise<void> {
22
+ const trace = flow.trace
23
+ const stepId = trace?.buffer.openStepId ?? null
24
+ if (trace === null || stepId === null) return
25
+ try {
26
+ await tx.execute(sql`
27
+ INSERT INTO audit_record
28
+ (trace_day, trace_id, trace_step_id, tenant_id, actor_id, entity, verb, entity_id, before, after, occurred_at)
29
+ VALUES (${trace.openedDay}, ${trace.id}, ${stepId}, ${record.tenantId}, ${resolveActorId(flow)},
30
+ ${record.entity}, ${record.verb}, ${record.entityId},
31
+ ${record.before === undefined ? null : JSON.stringify(record.before)}::jsonb, ${JSON.stringify(record.after)}::jsonb, now())
32
+ `)
33
+ } catch (err) {
34
+ throw new Error(`Failed to write the ${record.entity} ${record.verb} audit record`, { cause: err })
35
+ }
36
+ }
@@ -0,0 +1,65 @@
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
+ import type { DefinedTable, FieldRecord } from '../../domain/types.js'
9
+
10
+ /**
11
+ * Returns the primary-key field keys in declaration order. Throws when the
12
+ * table declares no primary key — the audit CTE needs one to pair rows.
13
+ */
14
+ export function primaryKeyFieldKeys(
15
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- DefinedTable's field generics erase at this boundary; we only read $fields and $options
16
+ table: DefinedTable<any>,
17
+ ): readonly string[] {
18
+ const keys = declaredPrimaryKeyFieldKeys(table)
19
+ if (keys.length === 0) {
20
+ throw new Error(
21
+ `[pipework] Cannot emit audit CTE for "${table.$name}": ` +
22
+ `the table declares no primary key.\n\n` +
23
+ ` An audit-emitting write needs a primary key so the pre-image\n` +
24
+ ` and post-image rows can be paired. Add .primaryKey() to a field\n` +
25
+ ` or pass { primaryKey: ['col1', 'col2'] } to the table options.\n`,
26
+ )
27
+ }
28
+ return keys
29
+ }
30
+
31
+ /** The declared primary-key field keys in declaration order; empty when the table has none. */
32
+ export function declaredPrimaryKeyFieldKeys(
33
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- see primaryKeyFieldKeys
34
+ table: DefinedTable<any>,
35
+ ): readonly string[] {
36
+ const composite = table.$options.primaryKey
37
+ if (composite !== undefined && composite.length > 0) return composite
38
+ const fields = table.$fields as FieldRecord
39
+ const single: string[] = []
40
+ for (const [key, descriptor] of Object.entries(fields)) {
41
+ if (descriptor._meta.primaryKey) single.push(key)
42
+ }
43
+ return single
44
+ }
45
+
46
+ /**
47
+ * The audited row's primary key as `audit_record.entity_id` text: the single
48
+ * key's value, or for a composite key a compact JSON array of the key values
49
+ * as strings, in key order (`["a","7"]`).
50
+ */
51
+ export function auditEntityId(
52
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- see primaryKeyFieldKeys
53
+ table: DefinedTable<any>,
54
+ row: Readonly<Record<string, unknown>>,
55
+ ): string {
56
+ const keys = primaryKeyFieldKeys(table)
57
+ const values = keys.map(k => {
58
+ const v = row[k]
59
+ if (v === null || v === undefined) {
60
+ throw new Error(`audit entity id for "${table.$name}": primary key field "${k}" is missing from the row.`)
61
+ }
62
+ return v instanceof Date ? v.toISOString() : String(v)
63
+ })
64
+ return keys.length === 1 ? values[0]! : JSON.stringify(values)
65
+ }
@@ -0,0 +1,41 @@
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
+
13
+ import { sql } from '../../db/sql.js'
14
+ import { quoteIdentifier } from '../../db/identifiers.js'
15
+ import { camelToSnake } from '../../domain/naming.js'
16
+ import { traceSafeKeys } from '../../domain/trace-safe-projection.js'
17
+ import type { SQL } from '../../db/types.js'
18
+ import type { DefinedTable, FieldRecord } from '../../domain/types.js'
19
+
20
+ /**
21
+ * `jsonb_build_object('field', "source"."column", ...)` over each trace-safe
22
+ * field, qualified by `sourceAlias` (the CTE name the column resolves through:
23
+ * `before` for the pre-image, `updated` / `deleted` / `inserted` / `upserted`
24
+ * for the post-image). Returns `null` when the table has no trace-safe fields
25
+ * — the caller substitutes `NULL::jsonb`.
26
+ */
27
+ export function buildTraceSafeProjection(
28
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- DefinedTable's field generics erase at this boundary; we only read $fields
29
+ table: DefinedTable<any>,
30
+ sourceAlias: string,
31
+ ): SQL | null {
32
+ const keys = traceSafeKeys(table.$fields as FieldRecord)
33
+ if (keys.length === 0) return null
34
+ const aliasIdent = quoteIdentifier(sourceAlias, 'CTE alias')
35
+ const parts: SQL[] = []
36
+ for (const key of keys) {
37
+ const col = quoteIdentifier(camelToSnake(key), 'trace-safe column')
38
+ parts.push(sql.raw(`'${key}', ${aliasIdent}.${col}`))
39
+ }
40
+ return sql`jsonb_build_object(${sql.join(parts, sql.raw(', '))})`
41
+ }
@@ -0,0 +1,86 @@
1
+ // Read-touches audit emission. A flow_step that declared `@touches Entity:verb`
2
+ // but performed no `pipe.*` write to its entity 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 covers the touches it wrote (recorded by
6
+ // `markWrote(touch)`); this module covers the rest.
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
+
21
+ import { auditRecord } from '../../../audit/schema/audit-record.js'
22
+ import { pipeRaw } from '../../db/db.js'
23
+ import { getBaseLogger } from '../../../core/logging/index.js'
24
+ import { recordFault, faultMessage } from '../../../infra/alerts/faults.js'
25
+ import type { Flow } from '../../../request/context/types.js'
26
+ import type { BegunStep } from '../../../trace/runtime/step-buffer.js'
27
+ import { resolveActorId } from './actor.js'
28
+
29
+ interface AuditRecordInsert {
30
+ readonly traceDay: string
31
+ readonly traceId: string
32
+ readonly traceStepId: string
33
+ readonly tenantId: string | null
34
+ readonly actorId: string | null
35
+ readonly entity: string
36
+ readonly verb: string
37
+ readonly before: null
38
+ readonly after: null
39
+ readonly capturedInputs: unknown
40
+ readonly occurredAt: Date
41
+ }
42
+
43
+ /**
44
+ * Emits one `audit_record` row per `@touches` target that no write of the
45
+ * completed step audited. Resolves when the insert settles. Failures are
46
+ * logged at error level and swallowed — an audit-emit failure must not
47
+ * propagate to the user-visible step outcome (the step already succeeded).
48
+ */
49
+ export async function emitReadTouches(flow: Flow, begun: BegunStep): Promise<void> {
50
+ const unwritten = begun.touches.filter(t => !begun.wroteTouches.has(t))
51
+ if (unwritten.length === 0) return
52
+ if (flow.trace === null) return
53
+
54
+ const traceDay = flow.trace.openedDay
55
+ const traceId = flow.trace.id
56
+ const tenantId = flow.tenant
57
+ const actorId = resolveActorId(flow)
58
+ const capturedInputs = begun.capturedInputs
59
+ const occurredAt = new Date()
60
+
61
+ const rows: AuditRecordInsert[] = unwritten.map(t => ({
62
+ traceDay,
63
+ traceId,
64
+ traceStepId: begun.stepId,
65
+ tenantId,
66
+ actorId,
67
+ entity: t.entity,
68
+ verb: t.verb,
69
+ before: null,
70
+ after: null,
71
+ capturedInputs,
72
+ occurredAt,
73
+ }))
74
+
75
+ try {
76
+ const db = pipeRaw(auditRecord.$options.database)
77
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Drizzle's insert needs the concrete runtime table type
78
+ await (db as any).insert(auditRecord.table()).values(rows)
79
+ } catch (err) {
80
+ getBaseLogger().error(
81
+ { err, traceId, stepId: begun.stepId, touches: unwritten.length },
82
+ 'Failed to emit read-touches audit row — the audit trail is missing a read observation',
83
+ )
84
+ void recordFault('audit-write', faultMessage(err), { entity: 'ReadTouch', touches: unwritten.length })
85
+ }
86
+ }
@@ -0,0 +1,78 @@
1
+ // Maps a flow step's `@touches Entity:verb` targets onto the table a write hits.
2
+ //
3
+ // A touch's `Entity` matches a table when:
4
+ // (a) the definition declares `define(name, fields, { auditEntity })` and it
5
+ // equals `Entity` exactly; otherwise
6
+ // (b) the normalized names agree: lowercase with underscores removed, with the
7
+ // table name optionally ending in `s` or `es` (`Version` matches
8
+ // `version` / `versions`; `DocumentVersion` matches `document_versions`).
9
+ //
10
+ // A write is audited only under the touches that match its table. A touch whose
11
+ // entity matches no registered definition cannot be audited by any write, so a
12
+ // write under it is refused rather than silently unaudited.
13
+
14
+ import { definitions } from '../../domain/define.js'
15
+ import type { DefinedTable } from '../../domain/types.js'
16
+ import type { MetaTouch } from '../../../trace/runtime/step-meta.js'
17
+
18
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- registry erases field-level generics
19
+ type AnyDefinition = DefinedTable<any>
20
+
21
+ function normalize(name: string): string {
22
+ return name.toLowerCase().replace(/_/g, '')
23
+ }
24
+
25
+ export function touchMatchesTable(entity: string, table: AnyDefinition): boolean {
26
+ const declared = table.$options.auditEntity
27
+ if (declared !== undefined) return declared === entity
28
+ const want = normalize(entity)
29
+ const have = normalize(table.$name)
30
+ if (have === want) return true
31
+ return (have.endsWith('es') && have.slice(0, -2) === want) ||
32
+ (have.endsWith('s') && have.slice(0, -1) === want)
33
+ }
34
+
35
+ /**
36
+ * The touches a write to `table` is audited under. Throws when any of the
37
+ * step's touches names an entity no registered definition matches.
38
+ */
39
+ export function touchesForTable(table: AnyDefinition, touches: readonly MetaTouch[]): MetaTouch[] {
40
+ const all = [...definitions().values()]
41
+ for (const t of touches) {
42
+ if (touchMatchesTable(t.entity, table)) continue
43
+ if (all.some(d => touchMatchesTable(t.entity, d))) continue
44
+ const near = closest(t.entity, all.map(d => d.$name))
45
+ throw new Error(
46
+ `[pipework] @touches entity "${t.entity}" matches no defined table, so a write under it cannot be audited. ` +
47
+ (near.length > 0 ? `Closest table names: ${near.join(', ')}. ` : '') +
48
+ 'Name the entity after the table (case-insensitive, underscores ignored, plural allowed) ' +
49
+ "or set define(name, fields, { auditEntity: '<Entity>' }) on the table.",
50
+ )
51
+ }
52
+ return touches.filter(t => touchMatchesTable(t.entity, table))
53
+ }
54
+
55
+ function closest(entity: string, names: readonly string[]): string[] {
56
+ const want = normalize(entity)
57
+ return names
58
+ .map(n => ({ n, d: editDistance(want, normalize(n)) }))
59
+ .sort((a, b) => a.d - b.d)
60
+ .slice(0, 3)
61
+ .map(x => x.n)
62
+ }
63
+
64
+ function editDistance(a: string, b: string): number {
65
+ let prev = Array.from({ length: b.length + 1 }, (_, j) => j)
66
+ for (let i = 1; i <= a.length; i++) {
67
+ const cur = [i]
68
+ for (let j = 1; j <= b.length; j++) {
69
+ cur[j] = Math.min(
70
+ prev[j]! + 1,
71
+ cur[j - 1]! + 1,
72
+ prev[j - 1]! + (a[i - 1] === b[j - 1] ? 0 : 1),
73
+ )
74
+ }
75
+ prev = cur
76
+ }
77
+ return prev[b.length]!
78
+ }
@@ -0,0 +1,62 @@
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
+
9
+ import { sql } from '../../db/sql.js'
10
+ import { quoteIdentifier } from '../../db/identifiers.js'
11
+ import { camelToSnake } from '../../domain/naming.js'
12
+ import { buildTraceSafeProjection } from './projection.js'
13
+ import { buildAuditInserts, buildEntityIdExpr, type AuditSelectContext } from './audit-select.js'
14
+ import { primaryKeyFieldKeys } from './primary-key.js'
15
+ import { AUDIT_COLUMNS_SQL } from './audit-columns.js'
16
+ import { buildFinalSelect } from './final-select.js'
17
+ import type { SQL } from '../../db/types.js'
18
+ import type { WriteState } from '../state.js'
19
+
20
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- drizzle's DB type is deeply generic; the executor passes the resolved per-database handle through unchanged
21
+ type AnyDb = any
22
+
23
+ export function compileDeleteAudit(db: AnyDb, state: WriteState, ctx: AuditSelectContext): SQL {
24
+ const pk = primaryKeyFieldKeys(state.table)
25
+
26
+ // `.getSQL()` strips the auto-paren drizzle adds around SQLWrapper builders
27
+ // when they are templated — we add explicit parens at the CTE boundary so
28
+ // the WITH clause keeps a uniform shape across verbs.
29
+ let deleteChain = db.delete(state.table)
30
+ if (state.where !== undefined) deleteChain = deleteChain.where(state.where)
31
+ const deleteInner = deleteChain.returning().getSQL()
32
+
33
+ let beforeChain = db.select().from(state.table)
34
+ if (state.where !== undefined) beforeChain = beforeChain.where(state.where)
35
+ const beforeInner = beforeChain.getSQL()
36
+
37
+ const joinOn = pk
38
+ .map(k => {
39
+ const col = quoteIdentifier(camelToSnake(k), 'pk column')
40
+ return `before.${col} = deleted.${col}`
41
+ })
42
+ .join(' AND ')
43
+
44
+ const before = buildTraceSafeProjection(state.table, 'before') ?? sql`NULL::jsonb`
45
+ // Source the audit from `deleted` — RETURNING * gives every column we need
46
+ // for the pre-image join, and `deleted` includes only the rows that actually
47
+ // matched. Joining onto `before` carries the FOR UPDATE lock.
48
+ const fromClause = sql.raw(`FROM deleted JOIN before ON ${joinOn}`)
49
+ const auditSelect = buildAuditInserts(ctx, fromClause, buildEntityIdExpr(state.table, 'deleted'), before, sql`NULL::jsonb`)
50
+ const finalSelect = buildFinalSelect(state, 'deleted')
51
+
52
+ return sql`
53
+ WITH before AS (${beforeInner}),
54
+ deleted AS (${deleteInner}),
55
+ audit_inserts AS (
56
+ INSERT INTO "audit_record" (${sql.raw(AUDIT_COLUMNS_SQL)})
57
+ ${auditSelect}
58
+ RETURNING 1
59
+ )
60
+ ${finalSelect}
61
+ `
62
+ }
@@ -0,0 +1,36 @@
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
+
6
+ import { sql } from '../../db/sql.js'
7
+ import { buildTraceSafeProjection } from './projection.js'
8
+ import { buildAuditInserts, buildEntityIdExpr, type AuditSelectContext } from './audit-select.js'
9
+ import { AUDIT_COLUMNS_SQL } from './audit-columns.js'
10
+ import { buildFinalSelect } from './final-select.js'
11
+ import { applyOnConflict } from '../conflict.js'
12
+ import type { SQL } from '../../db/types.js'
13
+ import type { WriteState } from '../state.js'
14
+
15
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- drizzle's DB type is deeply generic; the executor passes the resolved per-database handle through unchanged
16
+ type AnyDb = any
17
+
18
+ export function compileInsertAudit(db: AnyDb, state: WriteState, ctx: AuditSelectContext): SQL {
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
+ const inner = applyOnConflict(db.insert(state.table).values(state.values ?? []), state).returning().getSQL()
23
+ const after = buildTraceSafeProjection(state.table, 'inserted') ?? sql`NULL::jsonb`
24
+ const auditSelect = buildAuditInserts(ctx, sql`FROM inserted`, buildEntityIdExpr(state.table, 'inserted'), sql`NULL::jsonb`, after)
25
+ const finalSelect = buildFinalSelect(state, 'inserted')
26
+
27
+ return sql`
28
+ WITH inserted AS (${inner}),
29
+ audit_inserts AS (
30
+ INSERT INTO "audit_record" (${sql.raw(AUDIT_COLUMNS_SQL)})
31
+ ${auditSelect}
32
+ RETURNING 1
33
+ )
34
+ ${finalSelect}
35
+ `
36
+ }