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,47 @@
1
+ // Chooses which of a step's entity-matched touches a write is recorded under.
2
+ //
3
+ // A row's verb is `create` (insert, or an upsert row `before` did not match),
4
+ // `update`, or `delete`. Among the matched touches:
5
+ // 1. those whose verb is exactly the row's verb, if any;
6
+ // 2. else those whose verb is not create/update/delete/read (custom verbs);
7
+ // 3. else all matched touches but `read` ones, or all when only `read` ones match.
8
+ // A write marks every matched touch written, so none of them also gets a read row.
9
+
10
+ import type { MetaTouch } from '../../../trace/runtime/step-meta.js'
11
+
12
+ export type RowVerb = 'create' | 'update' | 'delete'
13
+
14
+ const NOT_CUSTOM: ReadonlySet<string> = new Set(['create', 'update', 'delete', 'read'])
15
+
16
+ export function selectTouches(matched: readonly MetaTouch[], verb: RowVerb): MetaTouch[] {
17
+ const exact = matched.filter(t => t.verb === verb)
18
+ if (exact.length > 0) return exact
19
+ const custom = matched.filter(t => !NOT_CUSTOM.has(t.verb))
20
+ if (custom.length > 0) return custom
21
+ const writes = matched.filter(t => t.verb !== 'read')
22
+ return writes.length > 0 ? writes : [...matched]
23
+ }
24
+
25
+ export interface WriteSelection {
26
+ /** Touches that get audit rows, in declaration order. */
27
+ readonly touches: MetaTouch[]
28
+ /** Upsert only: the touches that take rows `before` did not match / did match. */
29
+ readonly split?: { readonly create: readonly MetaTouch[]; readonly update: readonly MetaTouch[] } | undefined
30
+ /** Touches to mark as written: every matched touch. */
31
+ readonly written: MetaTouch[]
32
+ }
33
+
34
+ export function selectForWrite(matched: readonly MetaTouch[], writeVerb: 'insert' | 'update' | 'delete' | 'upsert'): WriteSelection {
35
+ let touches: MetaTouch[]
36
+ let split: WriteSelection['split']
37
+ if (writeVerb === 'upsert') {
38
+ const create = selectTouches(matched, 'create')
39
+ const update = selectTouches(matched, 'update')
40
+ touches = matched.filter(t => create.includes(t) || update.includes(t))
41
+ split = { create, update }
42
+ } else {
43
+ touches = selectTouches(matched, writeVerb === 'insert' ? 'create' : writeVerb)
44
+ }
45
+ const written = [...matched]
46
+ return { touches, split, written }
47
+ }
@@ -0,0 +1,60 @@
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
+
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 compileUpdateAudit(db: AnyDb, state: WriteState, ctx: AuditSelectContext): SQL {
24
+ const pk = primaryKeyFieldKeys(state.table)
25
+ const set = state.set ?? {}
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 updateChain = db.update(state.table).set(set)
30
+ if (state.where !== undefined) updateChain = updateChain.where(state.where)
31
+ const updateInner = updateChain.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} = updated.${col}`
41
+ })
42
+ .join(' AND ')
43
+
44
+ const before = buildTraceSafeProjection(state.table, 'before') ?? sql`NULL::jsonb`
45
+ const after = buildTraceSafeProjection(state.table, 'updated') ?? sql`NULL::jsonb`
46
+ const fromClause = sql.raw(`FROM updated JOIN before ON ${joinOn}`)
47
+ const auditSelect = buildAuditInserts(ctx, fromClause, buildEntityIdExpr(state.table, 'updated'), before, after)
48
+ const finalSelect = buildFinalSelect(state, 'updated')
49
+
50
+ return sql`
51
+ WITH before AS (${beforeInner}),
52
+ updated AS (${updateInner}),
53
+ audit_inserts AS (
54
+ INSERT INTO "audit_record" (${sql.raw(AUDIT_COLUMNS_SQL)})
55
+ ${auditSelect}
56
+ RETURNING 1
57
+ )
58
+ ${finalSelect}
59
+ `
60
+ }
@@ -0,0 +1,104 @@
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 (pre-write rows sharing the upserted rows' conflict-target
5
+ // values, whoever supplied them): a NULL `before` row
6
+ // means the upserted row was freshly inserted; non-NULL means it updated
7
+ // an existing row. The audit's `before` jsonb is wrapped in a CASE that
8
+ // returns NULL::jsonb for the insert branch.
9
+
10
+ import { sql } from '../../db/sql.js'
11
+ import { quoteIdentifier } from '../../db/identifiers.js'
12
+ import { camelToSnake } from '../../domain/naming.js'
13
+ import { buildTraceSafeProjection } from './projection.js'
14
+ import { buildAuditInserts, buildEntityIdExpr, type AuditSelectContext } from './audit-select.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
+ import type { MetaTouch } from '../../../trace/runtime/step-meta.js'
20
+
21
+ // 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
22
+ type AnyDb = any
23
+
24
+ export function compileUpsertAudit(db: AnyDb, state: WriteState, ctx: AuditSelectContext): SQL {
25
+ if (state.onConflictColumns === undefined || state.onConflictColumns.length === 0) {
26
+ throw new Error(
27
+ 'pipe.upsert requires .onConflict([...columns]). ' +
28
+ 'Pass the conflict-target columns that determine "this row already exists" — ' +
29
+ 'usually the primary key or a unique index.',
30
+ )
31
+ }
32
+
33
+ const conflictCols = state.onConflictColumns
34
+ const targetColumns = conflictCols.map(key => {
35
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- DefinedTable columns are indexed by field key; the lookup was validated at the builder surface
36
+ const col = (state.table as any)[key]
37
+ if (col === undefined) {
38
+ throw new Error(
39
+ `pipe.upsert: onConflict column "${key}" is not a field of "${state.table.$name}". ` +
40
+ `Use a field key declared on the table.`,
41
+ )
42
+ }
43
+ return col as unknown
44
+ })
45
+
46
+ let upsertChain = db.insert(state.table).values(state.values ?? [])
47
+ if (state.set === undefined) {
48
+ upsertChain = upsertChain.onConflictDoNothing({ target: targetColumns })
49
+ } else {
50
+ upsertChain = upsertChain.onConflictDoUpdate({ target: targetColumns, set: state.set })
51
+ }
52
+ // `.getSQL()` strips the auto-paren drizzle adds around SQLWrapper builders
53
+ // when they are templated — we add explicit parens at the CTE boundary so
54
+ // the WITH clause keeps a uniform shape across verbs.
55
+ const upsertInner = upsertChain.returning().getSQL()
56
+
57
+ const tableName = quoteIdentifier(state.table.$name, 'target table')
58
+ const conflictColsSql = conflictCols.map(k => quoteIdentifier(camelToSnake(k), 'conflict column')).join(', ')
59
+ // `before` is keyed on the rows `upserted` wrote, not the caller's values, so
60
+ // conflict columns pipework fills in (tenant) or defaults match too. All CTEs
61
+ // see the pre-statement snapshot: `before` reads the replaced row. No `FOR
62
+ // UPDATE`: locking inside a CTE conflicts with the sibling write on the row.
63
+ const beforeSelect = sql`
64
+ SELECT * FROM ${sql.raw(tableName)}
65
+ WHERE (${sql.raw(conflictColsSql)}) IN (SELECT ${sql.raw(conflictColsSql)} FROM upserted)
66
+ `
67
+
68
+ const joinOn = conflictCols
69
+ .map(k => {
70
+ const col = quoteIdentifier(camelToSnake(k), 'conflict column')
71
+ return `before.${col} = upserted.${col}`
72
+ })
73
+ .join(' AND ')
74
+
75
+ // Pivot: the conflict-key column of `before` is non-null iff the upserted
76
+ // row matched an existing row. Use the first conflict col as the discriminator.
77
+ const pivotCol = quoteIdentifier(camelToSnake(conflictCols[0]!), 'conflict column')
78
+ const beforeProj = buildTraceSafeProjection(state.table, 'before') ?? sql`NULL::jsonb`
79
+ const after = buildTraceSafeProjection(state.table, 'upserted') ?? sql`NULL::jsonb`
80
+ const beforeExpr = sql`CASE WHEN ${sql.raw(`before.${pivotCol}`)} IS NULL THEN NULL::jsonb ELSE (${beforeProj}) END`
81
+ const fromClause = sql.raw(`FROM upserted LEFT JOIN before ON ${joinOn}`)
82
+ // A touch in only the create set takes rows `before` did not match; only the
83
+ // update set, rows it did; in both (or no split), every row.
84
+ const split = ctx.split
85
+ const rowFilter = split === undefined ? undefined : (t: MetaTouch): SQL | null => {
86
+ const c = split.create.includes(t)
87
+ const u = split.update.includes(t)
88
+ if (c === u) return null
89
+ return sql.raw(`before.${pivotCol} IS ${c ? '' : 'NOT '}NULL`)
90
+ }
91
+ const auditSelect = buildAuditInserts(ctx, fromClause, buildEntityIdExpr(state.table, 'upserted'), beforeExpr, after, rowFilter)
92
+ const finalSelect = buildFinalSelect(state, 'upserted')
93
+
94
+ return sql`
95
+ WITH upserted AS (${upsertInner}),
96
+ before AS (${beforeSelect}),
97
+ audit_inserts AS (
98
+ INSERT INTO "audit_record" (${sql.raw(AUDIT_COLUMNS_SQL)})
99
+ ${auditSelect}
100
+ RETURNING 1
101
+ )
102
+ ${finalSelect}
103
+ `
104
+ }
@@ -0,0 +1,43 @@
1
+ // `ON CONFLICT DO NOTHING` on `pipe.insert`: the target lookup shared by the
2
+ // vanilla and audited paths, and the trace event naming the rows left alone.
3
+
4
+ import { event } from '../../trace/external/record.js'
5
+ import type { WriteState } from './state.js'
6
+
7
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- drizzle's insert builder is deeply generic; the clause is applied to whatever chain the caller holds
8
+ type AnyChain = any
9
+
10
+ /** Applies the state's `ON CONFLICT DO NOTHING` to an insert chain; the chain is returned unchanged when the state has none. */
11
+ export function applyOnConflict(q: AnyChain, state: WriteState): AnyChain {
12
+ const onConflict = state.onConflict
13
+ if (onConflict === undefined) return q
14
+ if (onConflict.target === undefined) return q.onConflictDoNothing()
15
+ const target = onConflict.target.map(key => {
16
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- DefinedTable columns are indexed by field key; the lookup is validated here
17
+ const col = (state.table as any)[key]
18
+ if (col === undefined) {
19
+ throw new Error(
20
+ `pipe.insert: onConflictDoNothing target "${key}" is not a field of "${state.table.$name}". ` +
21
+ `Use a field key declared on the table.`,
22
+ )
23
+ }
24
+ return col as unknown
25
+ })
26
+ return q.onConflictDoNothing({ target })
27
+ }
28
+
29
+ /** Records the rows an `onConflictDoNothing` insert skipped; nothing when none were. */
30
+ export function reportSkipped(state: WriteState, written: number): void {
31
+ if (state.onConflict === undefined) return
32
+ const attempted = state.values?.length ?? 0
33
+ if (written >= attempted) return
34
+ event('pipework.insert.conflict', {
35
+ capture: {
36
+ table: state.table.$name,
37
+ target: state.onConflict.target ?? null,
38
+ attempted,
39
+ written,
40
+ skipped: attempted - written,
41
+ },
42
+ })
43
+ }
@@ -0,0 +1,38 @@
1
+ // `pipe.delete(table)` — the pipework-owned delete builder. Same intent-based
2
+ // design as UpdateBuilder; see update.ts for the rationale.
3
+
4
+ import type { PgTable } from '../query/pg-core/index.js'
5
+ import type { SQL } from '../db/types.js'
6
+ import { assertNotExternal } from '../domain/external-registry.js'
7
+ import type { DefinedTable } from '../domain/types.js'
8
+ import type { WriteState } from './state.js'
9
+ import { executeWrite } from './execute.js'
10
+
11
+ export class DeleteBuilder<T extends PgTable, TResult = unknown> implements PromiseLike<TResult> {
12
+ constructor(private readonly state: WriteState) {}
13
+
14
+ where(condition: SQL): DeleteBuilder<T, TResult> {
15
+ return new DeleteBuilder<T, TResult>({ ...this.state, where: condition })
16
+ }
17
+
18
+ returning(): DeleteBuilder<T, Record<string, unknown>[]>
19
+ returning<TShape extends Record<string, unknown>>(shape: TShape): DeleteBuilder<T, Record<string, unknown>[]>
20
+ returning(shape?: Record<string, unknown>): DeleteBuilder<T, Record<string, unknown>[]> {
21
+ const returning = shape === undefined ? 'all' as const : shape
22
+ return new DeleteBuilder<T, Record<string, unknown>[]>({ ...this.state, returning })
23
+ }
24
+
25
+ then<TFulfilled = TResult, TRejected = never>(
26
+ onfulfilled?: ((value: TResult) => TFulfilled | PromiseLike<TFulfilled>) | null,
27
+ onrejected?: ((reason: unknown) => TRejected | PromiseLike<TRejected>) | null,
28
+ ): PromiseLike<TFulfilled | TRejected> {
29
+ return executeWrite(this.state).then(onfulfilled as (value: unknown) => TFulfilled, onrejected) as PromiseLike<TFulfilled | TRejected>
30
+ }
31
+ }
32
+
33
+ /** Starts a pipework-owned delete against `table`. Chain with `.where(...).returning(...)`. */
34
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
35
+ export function remove<T extends DefinedTable<any> & PgTable>(table: T): DeleteBuilder<T, any> {
36
+ assertNotExternal(table, 'delete')
37
+ return new DeleteBuilder<T, unknown>({ verb: 'delete', table })
38
+ }
@@ -0,0 +1,243 @@
1
+ // Execution path for the write surface. Two branches: vanilla DML via drizzle
2
+ // when there is no active audit-emitting step, and a single statement with
3
+ // `before` / `updated|inserted|deleted|upserted` / `audit_inserts` CTEs
4
+ // when the current `flow_step` carries a `@touches` target for the written
5
+ // table. The two branches produce the same observable return (the CTE rows are
6
+ // decoded through the table's columns) — the difference is whether an audit_record
7
+ // row lands atomically with the write.
8
+ //
9
+ // Bulk-write chunking (Q8 / Phase 6c): when an insert/upsert under audit
10
+ // would emit more than `trace.auditChunkSize` audit rows
11
+ // (`values.length × touches.length`), the executor splits `state.values`
12
+ // into chunks and runs each one as its own write+audit CTE, wrapped in a
13
+ // transaction so the chunks commit or abort together. Update/delete are not
14
+ // chunked — affected row counts are dynamic at SQL-generation time, so
15
+ // chunking would need a separate strategy.
16
+
17
+ import type { WriteState } from './state.js'
18
+ import type { Flow } from '../../request/context/types.js'
19
+ import { pipe, pipeRaw } from '../db/db.js'
20
+ import { getContext } from '../../request/context/store.js'
21
+ import { compileAuditWrite } from './audit/cte-compile.js'
22
+ import { touchesForTable } from './audit/touch-match.js'
23
+ import { selectForWrite } from './audit/verb-select.js'
24
+ import { decodeReturning } from './audit/decode-returning.js'
25
+ import { resolveActorId } from './audit/actor.js'
26
+ import type { AuditSelectContext } from './audit/audit-select.js'
27
+ import { rowsPerStatement } from '../db/insert-split.js'
28
+ import { applyOnConflict, reportSkipped } from './conflict.js'
29
+
30
+ export async function executeWrite(state: WriteState): Promise<unknown> {
31
+ const db = pipe(state.table.$options.database)
32
+ const flow = getContext()
33
+ if (flow !== undefined && flow.trace !== null) {
34
+ const stepCtx = flow.trace.buffer.currentStep()
35
+ // Only the touches whose entity is this table audit the write; with none,
36
+ // the write takes the vanilla branch below.
37
+ const matched = stepCtx === null ? [] : touchesForTable(state.table, stepCtx.touches)
38
+ if (stepCtx !== null && matched.length > 0) {
39
+ // Each row is recorded under the touches whose verb matches it (verb-select.ts).
40
+ const selection = selectForWrite(matched, state.verb)
41
+ const auditCtx: AuditSelectContext = {
42
+ step: stepCtx,
43
+ touches: selection.touches,
44
+ split: selection.split,
45
+ written: selection.written,
46
+ actorId: resolveActorId(flow),
47
+ tenantId: flow.tenant,
48
+ }
49
+ // The union bounds the audit rows one value can produce.
50
+ const chunkValueSize = computeChunkValueSize(flow, state, selection.touches.length, rowsPerStatement(state.table))
51
+ if (chunkValueSize !== null) {
52
+ return executeChunkedAuditWrite(state, auditCtx, chunkValueSize)
53
+ }
54
+ const stmt = compileAuditWrite(db, state, auditCtx)
55
+ // The compiled CTE is already tenant-scoped at compile time (the user
56
+ // write inside the CTE was built against the scoped `pipe()` DB, so it
57
+ // carries the tenant filter). Execute it against the raw handle — the
58
+ // scoped proxy refuses `.execute()` because CTEs would otherwise bypass
59
+ // its per-table filtering.
60
+ const rawDb = pipeRaw(state.table.$options.database)
61
+ const rows = await rawDb.execute(stmt)
62
+ for (const touch of selection.written) stepCtx.markWrote(touch)
63
+ if (state.onConflict !== undefined) reportSkipped(state, Array.isArray(rows) ? rows.length : 0)
64
+ return decodeReturning(state, rows)
65
+ }
66
+ }
67
+ if ((state.verb === 'insert' || state.verb === 'upsert') && state.values !== undefined) {
68
+ const size = rowsPerStatement(state.table)
69
+ if (state.values.length > size) return executeChunkedWrite(state, size)
70
+ }
71
+ switch (state.verb) {
72
+ case 'update': return executeUpdate(db, state)
73
+ case 'insert': return executeInsert(db, state)
74
+ case 'delete': return executeDelete(db, state)
75
+ case 'upsert': return executeUpsert(db, state)
76
+ }
77
+ }
78
+
79
+ /**
80
+ * Returns the per-chunk `values.length` to slice at, or `null` when the write
81
+ * fits in a single statement. Only insert/upsert are chunkable: update/delete
82
+ * affected-row counts depend on the `WHERE` clause, unknown at compile time.
83
+ */
84
+ function computeChunkValueSize(
85
+ flow: Flow,
86
+ state: WriteState,
87
+ touches: number,
88
+ parameterSize: number,
89
+ ): number | null {
90
+ if (state.verb !== 'insert' && state.verb !== 'upsert') return null
91
+ if (state.values === undefined) return null
92
+ if (touches === 0) return null
93
+
94
+ const limit = flow.instance.config.trace.auditChunkSize
95
+ const projectedAuditRows = state.values.length * touches
96
+ // Floor by touches so the per-chunk audit row count never exceeds `limit`;
97
+ // at least one row per chunk so single-touch writes still make progress.
98
+ const auditSize = Math.max(1, Math.floor(limit / touches))
99
+ const size = Math.min(auditSize, parameterSize)
100
+ if (projectedAuditRows <= limit && state.values.length <= size) return null
101
+ return size
102
+ }
103
+
104
+ /**
105
+ * Vanilla insert/upsert past the parameter limit: one statement per slice of
106
+ * rows, in one transaction (a savepoint when nested), returned rows in input order.
107
+ */
108
+ async function executeChunkedWrite(state: WriteState, size: number): Promise<unknown> {
109
+ const allValues = state.values ?? []
110
+ const collectsRows = state.returning !== undefined && state.returning !== null
111
+ const collected: unknown[] = []
112
+ let written = 0
113
+ const dbName = state.table.$options.database
114
+ const runChunks = async (): Promise<void> => {
115
+ for (let i = 0; i < allValues.length; i += size) {
116
+ const chunkState: WriteState = { ...state, values: allValues.slice(i, i + size) }
117
+ const db = pipe(dbName)
118
+ if (state.verb === 'insert') {
119
+ const out = await runInsert(db, chunkState)
120
+ written += out.written
121
+ if (collectsRows && Array.isArray(out.result)) for (const row of out.result) collected.push(row)
122
+ } else {
123
+ const rows = await executeUpsert(db, chunkState)
124
+ if (collectsRows && Array.isArray(rows)) for (const row of rows) collected.push(row)
125
+ }
126
+ }
127
+ }
128
+ if (dbName === undefined) await pipe.transaction(runChunks)
129
+ else await pipe.transaction(dbName, runChunks)
130
+ if (state.verb === 'insert') reportSkipped(state, written)
131
+ return collectsRows ? collected : undefined
132
+ }
133
+
134
+ async function executeChunkedAuditWrite(
135
+ state: WriteState,
136
+ ctx: AuditSelectContext,
137
+ chunkValueSize: number,
138
+ ): Promise<unknown> {
139
+ const allValues = state.values ?? []
140
+ const collectsRows = state.returning !== undefined && state.returning !== null
141
+ const collected: unknown[] = []
142
+ let written = 0
143
+ const dbName = state.table.$options.database
144
+
145
+ // `pipe.transaction` opens a transaction when none is active and a
146
+ // savepoint when nested — so chunked execution is atomic in both call sites
147
+ // (HTTP fittings with transactionPerRequest enabled, and ad-hoc callers).
148
+ const runChunks = async (): Promise<void> => {
149
+ for (let i = 0; i < allValues.length; i += chunkValueSize) {
150
+ const chunk = allValues.slice(i, i + chunkValueSize)
151
+ const chunkState: WriteState = { ...state, values: chunk }
152
+ const scoped = pipe(dbName)
153
+ const stmt = compileAuditWrite(scoped, chunkState, ctx)
154
+ const rawDb = pipeRaw(dbName)
155
+ const rows = await rawDb.execute(stmt)
156
+ if (Array.isArray(rows)) written += rows.length
157
+ if (collectsRows && Array.isArray(rows)) {
158
+ for (const row of rows) collected.push(row)
159
+ }
160
+ }
161
+ }
162
+ if (dbName === undefined) {
163
+ await pipe.transaction(runChunks)
164
+ } else {
165
+ await pipe.transaction(dbName, runChunks)
166
+ }
167
+
168
+ // An upsert marks the touches of both verb sets: which rows took which is not known here.
169
+ for (const touch of ctx.written) ctx.step.markWrote(touch)
170
+ if (state.onConflict !== undefined) reportSkipped(state, written)
171
+ return collectsRows ? decodeReturning(state, collected) : undefined
172
+ }
173
+
174
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- drizzle's DB/builder types are deeply generic; the WriteState shape has already validated the caller's input at the builder surface
175
+ type AnyDb = any
176
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
177
+ type AnyChain = any
178
+
179
+ async function executeUpdate(db: AnyDb, state: WriteState): Promise<unknown> {
180
+ let q: AnyChain = db.update(state.table).set(state.set ?? {})
181
+ if (state.where !== undefined) q = q.where(state.where)
182
+ return runWithReturning(q, state.returning)
183
+ }
184
+
185
+ async function executeInsert(db: AnyDb, state: WriteState): Promise<unknown> {
186
+ const out = await runInsert(db, state)
187
+ reportSkipped(state, out.written)
188
+ return out.result
189
+ }
190
+
191
+ /**
192
+ * One insert statement. Under `onConflictDoNothing` the rows written are the
193
+ * rows the statement returns, so it asks for them when the caller did not; the
194
+ * caller still gets what it asked for.
195
+ */
196
+ async function runInsert(db: AnyDb, state: WriteState): Promise<{ result: unknown; written: number }> {
197
+ const q: AnyChain = applyOnConflict(db.insert(state.table).values(state.values ?? []), state)
198
+ if (state.onConflict === undefined) return { result: await runWithReturning(q, state.returning), written: 0 }
199
+ const rows = await runWithReturning(q, state.returning ?? 'all') as unknown[]
200
+ const caller = state.returning === undefined || state.returning === null
201
+ return { result: caller ? [] : rows, written: rows.length }
202
+ }
203
+
204
+ async function executeDelete(db: AnyDb, state: WriteState): Promise<unknown> {
205
+ let q: AnyChain = db.delete(state.table)
206
+ if (state.where !== undefined) q = q.where(state.where)
207
+ return runWithReturning(q, state.returning)
208
+ }
209
+
210
+ async function executeUpsert(db: AnyDb, state: WriteState): Promise<unknown> {
211
+ if (state.onConflictColumns === undefined || state.onConflictColumns.length === 0) {
212
+ throw new Error(
213
+ 'pipe.upsert requires .onConflict([...columns]). ' +
214
+ 'Pass the conflict-target columns that determine "this row already exists" — ' +
215
+ 'usually the primary key or a unique index.',
216
+ )
217
+ }
218
+ const targetColumns = state.onConflictColumns.map(key => {
219
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- DefinedTable columns are indexed by field key; the lookup is validated at the builder surface
220
+ const col = (state.table as any)[key]
221
+ if (col === undefined) {
222
+ throw new Error(
223
+ `pipe.upsert: onConflict column "${key}" is not a field of "${state.table.$name}". ` +
224
+ `Use a field key declared on the table.`,
225
+ )
226
+ }
227
+ return col as unknown
228
+ })
229
+
230
+ let q: AnyChain = db.insert(state.table).values(state.values ?? [])
231
+ if (state.set === undefined) {
232
+ q = q.onConflictDoNothing({ target: targetColumns })
233
+ } else {
234
+ q = q.onConflictDoUpdate({ target: targetColumns, set: state.set })
235
+ }
236
+ return runWithReturning(q, state.returning)
237
+ }
238
+
239
+ function runWithReturning(q: AnyChain, returning: WriteState['returning']): Promise<unknown> {
240
+ if (returning === undefined || returning === null) return q as Promise<unknown>
241
+ if (returning === 'all') return q.returning() as Promise<unknown>
242
+ return q.returning(returning) as Promise<unknown>
243
+ }
@@ -0,0 +1,11 @@
1
+ // Re-exports the pipework-owned write surface. Wired into the `pipe` namespace
2
+ // in src/data/db/namespace.ts as `pipe.update / pipe.insert / pipe.delete /
3
+ // pipe.upsert` — the only consumer-facing way to mutate pipework-managed
4
+ // tables (Q8). The raw-write lint rule (#47) enforces that callers don't
5
+ // reach past this surface into drizzle directly.
6
+
7
+ export { update, UpdateBuilder } from './update.js'
8
+ export { insert, InsertBuilder } from './insert.js'
9
+ export { remove, DeleteBuilder } from './delete.js'
10
+ export { upsert, UpsertBuilder } from './upsert.js'
11
+ export type { WriteState, WriteVerb, ReturningShape } from './state.js'
@@ -0,0 +1,63 @@
1
+ // `pipe.insert(table)` — the pipework-owned insert builder. Same intent-based
2
+ // design as UpdateBuilder; see update.ts for the rationale.
3
+
4
+ import type { PgTable } from '../query/pg-core/index.js'
5
+ import { assertNotExternal } from '../domain/external-registry.js'
6
+ import type { DefinedTable } from '../domain/types.js'
7
+ import type { WriteState } from './state.js'
8
+ import type { InsertValues } from './values.js'
9
+ import { executeWrite } from './execute.js'
10
+
11
+ export class InsertBuilder<T extends PgTable, TResult = unknown> implements PromiseLike<TResult> {
12
+ constructor(private readonly state: WriteState) {}
13
+
14
+ values(rows: InsertValues<T> | ReadonlyArray<InsertValues<T>>): InsertBuilder<T, TResult> {
15
+ const normalized = Array.isArray(rows) ? rows : [rows]
16
+ return new InsertBuilder<T, TResult>({
17
+ ...this.state,
18
+ values: normalized as ReadonlyArray<Record<string, unknown>>,
19
+ })
20
+ }
21
+
22
+ /**
23
+ * Leaves a row that conflicts with an existing one and inserts the rest.
24
+ * Without `target`, any unique or exclusion constraint conflict is skipped.
25
+ * `.returning()` yields only the rows written, so a fully skipped insert
26
+ * returns `[]`. Under `@touches`, a skipped row gets no audit row; a traced
27
+ * flow records an `external:pipework.insert.conflict` step with the counts.
28
+ */
29
+ onConflictDoNothing(options?: { target?: readonly (keyof T['$inferSelect'] & string)[] }): InsertBuilder<T, TResult> {
30
+ const target = options?.target
31
+ if (target !== undefined && target.length === 0) {
32
+ throw new Error(
33
+ 'pipe.insert onConflictDoNothing target must not be empty. ' +
34
+ 'Pass the field keys of the unique constraint, or omit target to skip a conflict on any constraint.',
35
+ )
36
+ }
37
+ return new InsertBuilder<T, TResult>({
38
+ ...this.state,
39
+ onConflict: target === undefined ? { action: 'nothing' } : { action: 'nothing', target: target as readonly string[] },
40
+ })
41
+ }
42
+
43
+ returning(): InsertBuilder<T, Record<string, unknown>[]>
44
+ returning<TShape extends Record<string, unknown>>(shape: TShape): InsertBuilder<T, Record<string, unknown>[]>
45
+ returning(shape?: Record<string, unknown>): InsertBuilder<T, Record<string, unknown>[]> {
46
+ const returning = shape === undefined ? 'all' as const : shape
47
+ return new InsertBuilder<T, Record<string, unknown>[]>({ ...this.state, returning })
48
+ }
49
+
50
+ then<TFulfilled = TResult, TRejected = never>(
51
+ onfulfilled?: ((value: TResult) => TFulfilled | PromiseLike<TFulfilled>) | null,
52
+ onrejected?: ((reason: unknown) => TRejected | PromiseLike<TRejected>) | null,
53
+ ): PromiseLike<TFulfilled | TRejected> {
54
+ return executeWrite(this.state).then(onfulfilled as (value: unknown) => TFulfilled, onrejected) as PromiseLike<TFulfilled | TRejected>
55
+ }
56
+ }
57
+
58
+ /** Starts a pipework-owned insert against `table`. Chain with `.values(...).returning(...)`. */
59
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
60
+ export function insert<T extends DefinedTable<any> & PgTable>(table: T): InsertBuilder<T, any> {
61
+ assertNotExternal(table, 'insert')
62
+ return new InsertBuilder<T, unknown>({ verb: 'insert', table })
63
+ }
@@ -0,0 +1,43 @@
1
+ // The intent of a pending write — the data shape every write builder hands to
2
+ // the executor. Today (#44) the executor compiles it to vanilla DML through
3
+ // drizzle. The CTE generator (#45) reads the same shape to compile to a single
4
+ // statement with `before` / `updated` / `diffs` / `audit_inserts` CTEs when an
5
+ // active flow_step carries `@touches`. Keeping the intent on a plain data
6
+ // object — never on the builder's drizzle handle — is what makes both paths
7
+ // produce the same observable behavior.
8
+
9
+ import type { SQL } from '../db/types.js'
10
+ import type { DefinedTable, FieldRecord } from '../domain/types.js'
11
+
12
+ export type WriteVerb = 'insert' | 'update' | 'delete' | 'upsert'
13
+
14
+ /**
15
+ * What the caller asked the write to return.
16
+ *
17
+ * - `null` (default) — no `RETURNING` clause; the executor resolves to `void`.
18
+ * - `'all'` — `RETURNING *`; the executor resolves to every column on the row.
19
+ * - `Record<string, unknown>` — a per-field projection, as accepted by
20
+ * drizzle's `.returning(...)` (column references, sql expressions, etc.).
21
+ */
22
+ export type ReturningShape = Record<string, unknown> | 'all' | null
23
+
24
+ export interface WriteState {
25
+ readonly verb: WriteVerb
26
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- DefinedTable's field generics erase at the write boundary; the executor only needs the table-level surface ($options, drizzle columns)
27
+ readonly table: DefinedTable<any>
28
+ /** `set` clause for `update` / `upsert`. */
29
+ readonly set?: Record<string, unknown>
30
+ /** Row values for `insert` / `upsert`. */
31
+ readonly values?: ReadonlyArray<Record<string, unknown>>
32
+ /** `where` clause for `update` / `delete`. */
33
+ readonly where?: SQL
34
+ /** Conflict-target columns for `upsert`. */
35
+ readonly onConflictColumns?: readonly string[]
36
+ /** `ON CONFLICT DO NOTHING` for `insert`; `target` is field keys, absent for the bare clause. */
37
+ readonly onConflict?: { readonly action: 'nothing'; readonly target?: readonly string[] }
38
+ /** What `RETURNING` should produce. `null` (default) emits no RETURNING. */
39
+ readonly returning?: ReturningShape
40
+ }
41
+
42
+ /** Carrier type used by the builders' chain methods to mutate `WriteState` immutably. */
43
+ export type FieldRecordTable = DefinedTable<FieldRecord>