pipework 0.251.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 (730) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/dist/core/config/load.d.ts +2 -0
  3. package/dist/core/config/load.d.ts.map +1 -1
  4. package/dist/core/config/load.js +1 -0
  5. package/dist/core/config/load.js.map +1 -1
  6. package/dist/core/config/namespace.d.ts +1 -0
  7. package/dist/core/config/namespace.d.ts.map +1 -1
  8. package/dist/core/config/schema.d.ts +2 -0
  9. package/dist/core/config/schema.d.ts.map +1 -1
  10. package/dist/core/config/schema.js +1 -0
  11. package/dist/core/config/schema.js.map +1 -1
  12. package/dist/core/surface/types.d.ts +1 -0
  13. package/dist/core/surface/types.d.ts.map +1 -1
  14. package/dist/data/db/namespace.d.ts +3 -0
  15. package/dist/data/db/namespace.d.ts.map +1 -1
  16. package/dist/data/db/namespace.js +3 -0
  17. package/dist/data/db/namespace.js.map +1 -1
  18. package/dist/data/db/pool.d.ts.map +1 -1
  19. package/dist/data/db/pool.js +6 -1
  20. package/dist/data/db/pool.js.map +1 -1
  21. package/dist/data/db/query-label.d.ts +5 -0
  22. package/dist/data/db/query-label.d.ts.map +1 -0
  23. package/dist/data/db/query-label.js +11 -0
  24. package/dist/data/db/query-label.js.map +1 -0
  25. package/dist/data/db/query-watch.d.ts +21 -0
  26. package/dist/data/db/query-watch.d.ts.map +1 -0
  27. package/dist/data/db/query-watch.js +90 -0
  28. package/dist/data/db/query-watch.js.map +1 -0
  29. package/dist/data/query/postgres-js/driver.js +1 -1
  30. package/dist/data/query/postgres-js/driver.js.map +1 -1
  31. package/dist/data/query/postgres-js/session.d.ts +5 -1
  32. package/dist/data/query/postgres-js/session.d.ts.map +1 -1
  33. package/dist/data/query/postgres-js/session.js +16 -11
  34. package/dist/data/query/postgres-js/session.js.map +1 -1
  35. package/dist/data/query/utils.d.ts +2 -0
  36. package/dist/data/query/utils.d.ts.map +1 -1
  37. package/dist/data/query/utils.js.map +1 -1
  38. package/dist/infra/credits/schema.d.ts +1 -1
  39. package/dist/test/plugin.d.ts.map +1 -1
  40. package/dist/test/plugin.js +10 -4
  41. package/dist/test/plugin.js.map +1 -1
  42. package/package.json +4 -3
  43. package/src/async/jobs/abortable-delay.ts +18 -0
  44. package/src/async/jobs/claims.ts +52 -0
  45. package/src/async/jobs/cron.ts +179 -0
  46. package/src/async/jobs/errors.ts +56 -0
  47. package/src/async/jobs/exclusion.ts +117 -0
  48. package/src/async/jobs/execute.ts +219 -0
  49. package/src/async/jobs/index.ts +10 -0
  50. package/src/async/jobs/listener.ts +135 -0
  51. package/src/async/jobs/namespace.ts +20 -0
  52. package/src/async/jobs/queue-door.ts +44 -0
  53. package/src/async/jobs/queue.ts +1012 -0
  54. package/src/async/jobs/run-script.ts +67 -0
  55. package/src/async/jobs/scheduler.ts +218 -0
  56. package/src/async/jobs/schedules-table.ts +37 -0
  57. package/src/async/jobs/serialize-error.ts +48 -0
  58. package/src/async/jobs/table.ts +191 -0
  59. package/src/async/pipeline/define.ts +53 -0
  60. package/src/async/pipeline/errors.ts +35 -0
  61. package/src/async/pipeline/execute.ts +53 -0
  62. package/src/async/pipeline/index.ts +13 -0
  63. package/src/async/pipeline/namespace.ts +12 -0
  64. package/src/async/pipeline/resume.ts +67 -0
  65. package/src/async/pipeline/run-steps.ts +98 -0
  66. package/src/async/pipeline/types.ts +57 -0
  67. package/src/async/state-machine/define.ts +64 -0
  68. package/src/async/state-machine/errors.ts +29 -0
  69. package/src/async/state-machine/index.ts +3 -0
  70. package/src/async/state-machine/namespace.ts +12 -0
  71. package/src/async/state-machine/transition.ts +90 -0
  72. package/src/async/state-machine/types.ts +37 -0
  73. package/src/async/steps/errors.ts +31 -0
  74. package/src/async/steps/graph.ts +1214 -0
  75. package/src/async/steps/hash.ts +40 -0
  76. package/src/async/steps/index.ts +7 -0
  77. package/src/async/steps/namespace.ts +7 -0
  78. package/src/async/steps/table.ts +97 -0
  79. package/src/async/steps/types.ts +318 -0
  80. package/src/audit/chain/index.ts +9 -0
  81. package/src/audit/chain/install.ts +113 -0
  82. package/src/audit/chain/retire.ts +52 -0
  83. package/src/audit/chain/verify.ts +257 -0
  84. package/src/audit/query/cursor.ts +37 -0
  85. package/src/audit/query/index.ts +9 -0
  86. package/src/audit/query/query.ts +223 -0
  87. package/src/audit/schema/audit-record-fields.ts +68 -0
  88. package/src/audit/schema/audit-record.ts +23 -0
  89. package/src/auth/auth/audit.ts +31 -0
  90. package/src/auth/auth/chain.ts +84 -0
  91. package/src/auth/auth/cookie.ts +136 -0
  92. package/src/auth/auth/email-links.ts +311 -0
  93. package/src/auth/auth/guard.ts +128 -0
  94. package/src/auth/auth/index.ts +18 -0
  95. package/src/auth/auth/multi-org.ts +337 -0
  96. package/src/auth/auth/namespace.ts +34 -0
  97. package/src/auth/auth/parse-expiry.ts +13 -0
  98. package/src/auth/auth/passwords.ts +110 -0
  99. package/src/auth/auth/schema.ts +110 -0
  100. package/src/auth/auth/sessions.ts +565 -0
  101. package/src/auth/auth/sign-in.ts +156 -0
  102. package/src/auth/auth/signing-keys.ts +69 -0
  103. package/src/auth/auth/types.ts +26 -0
  104. package/src/auth/auth/uuid.ts +10 -0
  105. package/src/auth/rbac/cache.ts +55 -0
  106. package/src/auth/rbac/check.ts +30 -0
  107. package/src/auth/rbac/create.ts +35 -0
  108. package/src/auth/rbac/index.ts +5 -0
  109. package/src/auth/rbac/middleware.ts +20 -0
  110. package/src/auth/rbac/namespace.ts +15 -0
  111. package/src/auth/rbac/resolve.ts +31 -0
  112. package/src/auth/rbac/schema.sql +31 -0
  113. package/src/auth/rbac/seed.ts +67 -0
  114. package/src/auth/rbac/tables.ts +32 -0
  115. package/src/auth/rbac/types.ts +65 -0
  116. package/src/auth/tenant/extract.ts +33 -0
  117. package/src/auth/tenant/in-tenant.ts +49 -0
  118. package/src/auth/tenant/index.ts +7 -0
  119. package/src/auth/tenant/namespace.ts +23 -0
  120. package/src/auth/tenant/propagate.ts +20 -0
  121. package/src/auth/tenant/rls.ts +120 -0
  122. package/src/auth/tenant/scope.ts +33 -0
  123. package/src/auth/tenant/scoped-db.ts +581 -0
  124. package/src/auth/tenant/types.ts +14 -0
  125. package/src/auth/tenant/validate.ts +27 -0
  126. package/src/cli/commands/add.ts +158 -0
  127. package/src/cli/commands/alerts.ts +98 -0
  128. package/src/cli/commands/check.ts +1418 -0
  129. package/src/cli/commands/codemod.ts +78 -0
  130. package/src/cli/commands/db.ts +356 -0
  131. package/src/cli/commands/dev.ts +101 -0
  132. package/src/cli/commands/generate.ts +75 -0
  133. package/src/cli/commands/init.ts +36 -0
  134. package/src/cli/commands/install.ts +28 -0
  135. package/src/cli/commands/lint.ts +62 -0
  136. package/src/cli/commands/migrate.ts +132 -0
  137. package/src/cli/commands/openapi.ts +63 -0
  138. package/src/cli/commands/remove.ts +72 -0
  139. package/src/cli/commands/run.ts +50 -0
  140. package/src/cli/commands/scope.ts +54 -0
  141. package/src/cli/commands/serve.ts +68 -0
  142. package/src/cli/commands/shard.ts +31 -0
  143. package/src/cli/commands/shared/config-pipework-version.ts +28 -0
  144. package/src/cli/commands/shared/find-config.ts +22 -0
  145. package/src/cli/commands/shared/load-config.ts +35 -0
  146. package/src/cli/commands/shared/pnpm-install.ts +95 -0
  147. package/src/cli/commands/shared/report-removed.ts +8 -0
  148. package/src/cli/commands/sync.ts +32 -0
  149. package/src/cli/commands/test.ts +307 -0
  150. package/src/cli/commands/upgrade.ts +48 -0
  151. package/src/cli/dev-bootstrap.ts +24 -0
  152. package/src/cli/errors.ts +10 -0
  153. package/src/cli/index.ts +303 -0
  154. package/src/cli/output.ts +17 -0
  155. package/src/cli/port.ts +15 -0
  156. package/src/cli/surface-kinds.ts +20 -0
  157. package/src/client/index.ts +4 -0
  158. package/src/client/poll.ts +130 -0
  159. package/src/client/report-fault.ts +119 -0
  160. package/src/core/config/app-pool.ts +19 -0
  161. package/src/core/config/aws-env.ts +29 -0
  162. package/src/core/config/base-dir.ts +24 -0
  163. package/src/core/config/discover.ts +64 -0
  164. package/src/core/config/env-resolve.ts +87 -0
  165. package/src/core/config/env-types.ts +116 -0
  166. package/src/core/config/env.ts +55 -0
  167. package/src/core/config/errors.ts +9 -0
  168. package/src/core/config/import-config.ts +40 -0
  169. package/src/core/config/index.ts +8 -0
  170. package/src/core/config/load.ts +417 -0
  171. package/src/core/config/namespace.ts +14 -0
  172. package/src/core/config/resolve.ts +195 -0
  173. package/src/core/config/schema.ts +488 -0
  174. package/src/core/config/statement-observer.ts +48 -0
  175. package/src/core/config/ts-inline-hooks.ts +75 -0
  176. package/src/core/config/ts-register.ts +4 -0
  177. package/src/core/config/ts-resolve-hooks.ts +101 -0
  178. package/src/core/errors/base.ts +49 -0
  179. package/src/core/errors/http.ts +146 -0
  180. package/src/core/errors/index.ts +2 -0
  181. package/src/core/errors/namespace.ts +31 -0
  182. package/src/core/lifecycle/drain.ts +33 -0
  183. package/src/core/lifecycle/health.ts +72 -0
  184. package/src/core/lifecycle/index.ts +4 -0
  185. package/src/core/lifecycle/namespace.ts +7 -0
  186. package/src/core/lifecycle/startup.ts +102 -0
  187. package/src/core/lifecycle/types.ts +36 -0
  188. package/src/core/logging/context.ts +60 -0
  189. package/src/core/logging/error-chain.ts +68 -0
  190. package/src/core/logging/index.ts +5 -0
  191. package/src/core/logging/logger.ts +65 -0
  192. package/src/core/logging/namespace.ts +16 -0
  193. package/src/core/logging/proxy.ts +22 -0
  194. package/src/core/logging/redact-match.ts +24 -0
  195. package/src/core/logging/types.ts +47 -0
  196. package/src/core/pipework.ts +566 -0
  197. package/src/core/runtime/broker.ts +69 -0
  198. package/src/core/surface/http.ts +133 -0
  199. package/src/core/surface/index.ts +11 -0
  200. package/src/core/surface/job-timeout.ts +40 -0
  201. package/src/core/surface/namespace.ts +13 -0
  202. package/src/core/surface/script.ts +33 -0
  203. package/src/core/surface/start.ts +142 -0
  204. package/src/core/surface/types.ts +225 -0
  205. package/src/core/surface/validate.ts +88 -0
  206. package/src/core/surface/worker.ts +528 -0
  207. package/src/core/validation/connectivity.ts +49 -0
  208. package/src/core/validation/domain.ts +99 -0
  209. package/src/core/validation/index.ts +5 -0
  210. package/src/core/validation/queue-tables.ts +312 -0
  211. package/src/core/validation/rls-role.ts +138 -0
  212. package/src/core/validation/startup.ts +51 -0
  213. package/src/core/validation/surfaces.ts +65 -0
  214. package/src/core/validation/types.ts +25 -0
  215. package/src/data/db/advisory-lock.ts +55 -0
  216. package/src/data/db/aggregates.ts +172 -0
  217. package/src/data/db/array-elements.ts +111 -0
  218. package/src/data/db/bulk-set.ts +53 -0
  219. package/src/data/db/col/index.ts +14 -0
  220. package/src/data/db/composed.ts +53 -0
  221. package/src/data/db/db.ts +276 -0
  222. package/src/data/db/excluded.ts +16 -0
  223. package/src/data/db/expressions.ts +473 -0
  224. package/src/data/db/filter.ts +90 -0
  225. package/src/data/db/function-source.ts +114 -0
  226. package/src/data/db/identifiers.ts +16 -0
  227. package/src/data/db/idx/index.ts +9 -0
  228. package/src/data/db/in-list.ts +49 -0
  229. package/src/data/db/index.ts +9 -0
  230. package/src/data/db/insert-split.ts +137 -0
  231. package/src/data/db/instrumentation.ts +82 -0
  232. package/src/data/db/maintenance.ts +28 -0
  233. package/src/data/db/namespace.ts +107 -0
  234. package/src/data/db/owner-labels.ts +9 -0
  235. package/src/data/db/pool.ts +187 -0
  236. package/src/data/db/query-label.ts +13 -0
  237. package/src/data/db/query-watch.ts +116 -0
  238. package/src/data/db/raw-client-warning.ts +37 -0
  239. package/src/data/db/readonly.ts +23 -0
  240. package/src/data/db/reservation.ts +151 -0
  241. package/src/data/db/schema-features.ts +14 -0
  242. package/src/data/db/serializable.ts +83 -0
  243. package/src/data/db/sql.ts +1 -0
  244. package/src/data/db/timeouts.ts +76 -0
  245. package/src/data/db/types.ts +18 -0
  246. package/src/data/db/unbound.ts +33 -0
  247. package/src/data/db/values.ts +29 -0
  248. package/src/data/domain/brand.ts +15 -0
  249. package/src/data/domain/build-factory.ts +193 -0
  250. package/src/data/domain/build-schema.ts +161 -0
  251. package/src/data/domain/build-table.ts +355 -0
  252. package/src/data/domain/define.ts +239 -0
  253. package/src/data/domain/external-registry.ts +34 -0
  254. package/src/data/domain/external.ts +77 -0
  255. package/src/data/domain/field.ts +279 -0
  256. package/src/data/domain/index.ts +13 -0
  257. package/src/data/domain/naming.ts +6 -0
  258. package/src/data/domain/projections.ts +57 -0
  259. package/src/data/domain/trace-safe-projection.ts +31 -0
  260. package/src/data/domain/traits.ts +108 -0
  261. package/src/data/domain/types.ts +289 -0
  262. package/src/data/jsonb/composed.ts +18 -0
  263. package/src/data/jsonb/index.ts +1 -0
  264. package/src/data/jsonb/namespace.ts +30 -0
  265. package/src/data/jsonb/query.ts +86 -0
  266. package/src/data/migrate/append-only-generate.ts +47 -0
  267. package/src/data/migrate/applied.ts +47 -0
  268. package/src/data/migrate/apply.ts +309 -0
  269. package/src/data/migrate/check.ts +119 -0
  270. package/src/data/migrate/diff.ts +357 -0
  271. package/src/data/migrate/door-generate.ts +130 -0
  272. package/src/data/migrate/framework-rls.ts +58 -0
  273. package/src/data/migrate/generate.ts +201 -0
  274. package/src/data/migrate/history.ts +215 -0
  275. package/src/data/migrate/index.ts +9 -0
  276. package/src/data/migrate/internal-definitions.ts +175 -0
  277. package/src/data/migrate/post-process.ts +73 -0
  278. package/src/data/migrate/pragma.ts +73 -0
  279. package/src/data/migrate/provision-framework.ts +147 -0
  280. package/src/data/migrate/rls-generate.ts +61 -0
  281. package/src/data/migrate/run.ts +124 -0
  282. package/src/data/migrate/safety.ts +167 -0
  283. package/src/data/migrate/snapshot.ts +368 -0
  284. package/src/data/migrate/sql-emit.ts +392 -0
  285. package/src/data/migrate/statements.ts +312 -0
  286. package/src/data/query/alias.ts +126 -0
  287. package/src/data/query/batch.ts +8 -0
  288. package/src/data/query/cache/core/cache.ts +78 -0
  289. package/src/data/query/cache/core/index.ts +1 -0
  290. package/src/data/query/cache/core/types.ts +29 -0
  291. package/src/data/query/cache/readme.md +230 -0
  292. package/src/data/query/casing.ts +76 -0
  293. package/src/data/query/collation.ts +11 -0
  294. package/src/data/query/column-builder.ts +370 -0
  295. package/src/data/query/column.ts +150 -0
  296. package/src/data/query/entity.ts +42 -0
  297. package/src/data/query/errors.ts +33 -0
  298. package/src/data/query/index.ts +14 -0
  299. package/src/data/query/logger.ts +47 -0
  300. package/src/data/query/operations.ts +45 -0
  301. package/src/data/query/pg-core/alias.ts +12 -0
  302. package/src/data/query/pg-core/checks.ts +32 -0
  303. package/src/data/query/pg-core/columns/all.ts +71 -0
  304. package/src/data/query/pg-core/columns/bigint.ts +110 -0
  305. package/src/data/query/pg-core/columns/bigserial.ts +128 -0
  306. package/src/data/query/pg-core/columns/boolean.ts +43 -0
  307. package/src/data/query/pg-core/columns/char.ts +85 -0
  308. package/src/data/query/pg-core/columns/cidr.ts +43 -0
  309. package/src/data/query/pg-core/columns/common.ts +366 -0
  310. package/src/data/query/pg-core/columns/custom.ts +232 -0
  311. package/src/data/query/pg-core/columns/date.common.ts +15 -0
  312. package/src/data/query/pg-core/columns/date.ts +112 -0
  313. package/src/data/query/pg-core/columns/double-precision.ts +55 -0
  314. package/src/data/query/pg-core/columns/enum.ts +201 -0
  315. package/src/data/query/pg-core/columns/index.ts +35 -0
  316. package/src/data/query/pg-core/columns/inet.ts +43 -0
  317. package/src/data/query/pg-core/columns/int.common.ts +57 -0
  318. package/src/data/query/pg-core/columns/integer.ts +53 -0
  319. package/src/data/query/pg-core/columns/interval.ts +83 -0
  320. package/src/data/query/pg-core/columns/json.ts +58 -0
  321. package/src/data/query/pg-core/columns/jsonb.ts +56 -0
  322. package/src/data/query/pg-core/columns/line.ts +117 -0
  323. package/src/data/query/pg-core/columns/macaddr.ts +43 -0
  324. package/src/data/query/pg-core/columns/macaddr8.ts +43 -0
  325. package/src/data/query/pg-core/columns/numeric.ts +227 -0
  326. package/src/data/query/pg-core/columns/point.ts +127 -0
  327. package/src/data/query/pg-core/columns/postgis_extension/geometry.ts +119 -0
  328. package/src/data/query/pg-core/columns/postgis_extension/utils.ts +47 -0
  329. package/src/data/query/pg-core/columns/real.ts +58 -0
  330. package/src/data/query/pg-core/columns/serial.ts +55 -0
  331. package/src/data/query/pg-core/columns/smallint.ts +53 -0
  332. package/src/data/query/pg-core/columns/smallserial.ts +60 -0
  333. package/src/data/query/pg-core/columns/text.ts +67 -0
  334. package/src/data/query/pg-core/columns/time.ts +72 -0
  335. package/src/data/query/pg-core/columns/timestamp.ts +163 -0
  336. package/src/data/query/pg-core/columns/uuid.ts +51 -0
  337. package/src/data/query/pg-core/columns/varchar.ts +89 -0
  338. package/src/data/query/pg-core/columns/vector_extension/bit.ts +68 -0
  339. package/src/data/query/pg-core/columns/vector_extension/halfvec.ts +80 -0
  340. package/src/data/query/pg-core/columns/vector_extension/sparsevec.ts +67 -0
  341. package/src/data/query/pg-core/columns/vector_extension/vector.ts +80 -0
  342. package/src/data/query/pg-core/db.ts +674 -0
  343. package/src/data/query/pg-core/dialect.ts +1404 -0
  344. package/src/data/query/pg-core/expressions.ts +25 -0
  345. package/src/data/query/pg-core/foreign-keys.ts +130 -0
  346. package/src/data/query/pg-core/index.ts +21 -0
  347. package/src/data/query/pg-core/indexes.ts +276 -0
  348. package/src/data/query/pg-core/policies.ts +56 -0
  349. package/src/data/query/pg-core/primary-keys.ts +62 -0
  350. package/src/data/query/pg-core/query-builders/count.ts +86 -0
  351. package/src/data/query/pg-core/query-builders/delete.ts +293 -0
  352. package/src/data/query/pg-core/query-builders/index.ts +7 -0
  353. package/src/data/query/pg-core/query-builders/insert.ts +452 -0
  354. package/src/data/query/pg-core/query-builders/query-builder.ts +123 -0
  355. package/src/data/query/pg-core/query-builders/query.ts +157 -0
  356. package/src/data/query/pg-core/query-builders/raw.ts +49 -0
  357. package/src/data/query/pg-core/query-builders/refresh-materialized-view.ts +108 -0
  358. package/src/data/query/pg-core/query-builders/select.ts +1346 -0
  359. package/src/data/query/pg-core/query-builders/select.types.ts +437 -0
  360. package/src/data/query/pg-core/query-builders/update.ts +626 -0
  361. package/src/data/query/pg-core/roles.ts +41 -0
  362. package/src/data/query/pg-core/schema.ts +72 -0
  363. package/src/data/query/pg-core/sequence.ts +41 -0
  364. package/src/data/query/pg-core/session.ts +292 -0
  365. package/src/data/query/pg-core/subquery.ts +46 -0
  366. package/src/data/query/pg-core/table.ts +252 -0
  367. package/src/data/query/pg-core/unique-constraint.ts +73 -0
  368. package/src/data/query/pg-core/utils/array.ts +95 -0
  369. package/src/data/query/pg-core/utils/index.ts +1 -0
  370. package/src/data/query/pg-core/utils.ts +104 -0
  371. package/src/data/query/pg-core/view-base.ts +14 -0
  372. package/src/data/query/pg-core/view-common.ts +1 -0
  373. package/src/data/query/pg-core/view.ts +435 -0
  374. package/src/data/query/pg-core/with-builder.ts +41 -0
  375. package/src/data/query/postgres-js/README.md +46 -0
  376. package/src/data/query/postgres-js/array-parser.ts +45 -0
  377. package/src/data/query/postgres-js/driver-error-boundary.ts +149 -0
  378. package/src/data/query/postgres-js/driver.ts +163 -0
  379. package/src/data/query/postgres-js/index.ts +2 -0
  380. package/src/data/query/postgres-js/session.ts +234 -0
  381. package/src/data/query/primary-key.ts +11 -0
  382. package/src/data/query/query-builders/query-builder.ts +19 -0
  383. package/src/data/query/query-builders/select.types.ts +175 -0
  384. package/src/data/query/query-promise.ts +35 -0
  385. package/src/data/query/relations.ts +725 -0
  386. package/src/data/query/runnable-query.ts +12 -0
  387. package/src/data/query/selection-proxy.ts +121 -0
  388. package/src/data/query/session.ts +8 -0
  389. package/src/data/query/sql/expressions/conditions.ts +741 -0
  390. package/src/data/query/sql/expressions/index.ts +2 -0
  391. package/src/data/query/sql/expressions/select.ts +45 -0
  392. package/src/data/query/sql/functions/aggregate.ts +129 -0
  393. package/src/data/query/sql/functions/index.ts +2 -0
  394. package/src/data/query/sql/functions/vector.ts +178 -0
  395. package/src/data/query/sql/index.ts +3 -0
  396. package/src/data/query/sql/sql.ts +794 -0
  397. package/src/data/query/subquery.ts +53 -0
  398. package/src/data/query/table.ts +208 -0
  399. package/src/data/query/table.utils.ts +2 -0
  400. package/src/data/query/tracing-utils.ts +3 -0
  401. package/src/data/query/tracing.ts +11 -0
  402. package/src/data/query/utils.ts +357 -0
  403. package/src/data/query/version.ts +4 -0
  404. package/src/data/query/view-common.ts +1 -0
  405. package/src/data/schema/branded.ts +13 -0
  406. package/src/data/schema/generated/coerce.ts +5 -0
  407. package/src/data/schema/generated/combinator.ts +6 -0
  408. package/src/data/schema/generated/encoding.ts +5 -0
  409. package/src/data/schema/generated/error.ts +6 -0
  410. package/src/data/schema/generated/format.ts +5 -0
  411. package/src/data/schema/generated/type-constructor.ts +10 -0
  412. package/src/data/schema/generated/type.ts +6 -0
  413. package/src/data/schema/index.ts +6 -0
  414. package/src/data/schema/json-schema.ts +33 -0
  415. package/src/data/schema/namespace.ts +55 -0
  416. package/src/data/schema/parse.ts +39 -0
  417. package/src/data/schema/types.ts +39 -0
  418. package/src/data/temporal/append-only.ts +134 -0
  419. package/src/data/temporal/close.ts +27 -0
  420. package/src/data/temporal/columns.ts +10 -0
  421. package/src/data/temporal/definition-queries.ts +70 -0
  422. package/src/data/temporal/errors.ts +10 -0
  423. package/src/data/temporal/get-current.ts +22 -0
  424. package/src/data/temporal/index.ts +9 -0
  425. package/src/data/temporal/namespace.ts +36 -0
  426. package/src/data/temporal/query.ts +25 -0
  427. package/src/data/temporal/resolve.ts +17 -0
  428. package/src/data/temporal/revise.ts +67 -0
  429. package/src/data/temporal/types.ts +22 -0
  430. package/src/data/valve/artifact.ts +33 -0
  431. package/src/data/valve/contract-diff.ts +21 -0
  432. package/src/data/valve/cross.ts +57 -0
  433. package/src/data/valve/crossing-ledger.ts +50 -0
  434. package/src/data/valve/decision.ts +44 -0
  435. package/src/data/valve/durable-crossing.ts +52 -0
  436. package/src/data/valve/execute-crossing.ts +77 -0
  437. package/src/data/valve/faucet.ts +56 -0
  438. package/src/data/valve/gate.ts +114 -0
  439. package/src/data/valve/graph.ts +77 -0
  440. package/src/data/valve/index.ts +53 -0
  441. package/src/data/valve/namespace.ts +57 -0
  442. package/src/data/valve/registry.ts +50 -0
  443. package/src/data/valve/schema/valve-crossing-fields.ts +23 -0
  444. package/src/data/valve/schema/valve-crossing.ts +9 -0
  445. package/src/data/valve/supply.ts +154 -0
  446. package/src/data/valve/types.ts +143 -0
  447. package/src/data/vector/columns.ts +27 -0
  448. package/src/data/vector/index.ts +4 -0
  449. package/src/data/vector/namespace.ts +33 -0
  450. package/src/data/vector/query.ts +85 -0
  451. package/src/data/vector/validate.ts +16 -0
  452. package/src/data/write/audit/actor.ts +15 -0
  453. package/src/data/write/audit/audit-columns.ts +18 -0
  454. package/src/data/write/audit/audit-select.ts +111 -0
  455. package/src/data/write/audit/bound-transaction.ts +14 -0
  456. package/src/data/write/audit/cte-compile.ts +32 -0
  457. package/src/data/write/audit/decode-returning.ts +47 -0
  458. package/src/data/write/audit/final-select.ts +43 -0
  459. package/src/data/write/audit/infra-audit.ts +36 -0
  460. package/src/data/write/audit/primary-key.ts +65 -0
  461. package/src/data/write/audit/projection.ts +41 -0
  462. package/src/data/write/audit/read-touches.ts +86 -0
  463. package/src/data/write/audit/touch-match.ts +78 -0
  464. package/src/data/write/audit/verb-delete.ts +62 -0
  465. package/src/data/write/audit/verb-insert.ts +36 -0
  466. package/src/data/write/audit/verb-select.ts +47 -0
  467. package/src/data/write/audit/verb-update.ts +60 -0
  468. package/src/data/write/audit/verb-upsert.ts +104 -0
  469. package/src/data/write/conflict.ts +43 -0
  470. package/src/data/write/delete.ts +38 -0
  471. package/src/data/write/execute.ts +243 -0
  472. package/src/data/write/index.ts +11 -0
  473. package/src/data/write/insert.ts +63 -0
  474. package/src/data/write/state.ts +43 -0
  475. package/src/data/write/update.ts +54 -0
  476. package/src/data/write/upsert.ts +58 -0
  477. package/src/data/write/values.ts +19 -0
  478. package/src/index.ts +230 -0
  479. package/src/infra/access-links/code.ts +70 -0
  480. package/src/infra/access-links/handle.ts +892 -0
  481. package/src/infra/access-links/index.ts +9 -0
  482. package/src/infra/access-links/namespace.ts +30 -0
  483. package/src/infra/access-links/person-check.ts +177 -0
  484. package/src/infra/access-links/schema.ts +71 -0
  485. package/src/infra/alerts/async-sources.ts +231 -0
  486. package/src/infra/alerts/audit.ts +36 -0
  487. package/src/infra/alerts/beat.ts +123 -0
  488. package/src/infra/alerts/client-faults.ts +111 -0
  489. package/src/infra/alerts/faults.ts +156 -0
  490. package/src/infra/alerts/handle.ts +778 -0
  491. package/src/infra/alerts/http.ts +192 -0
  492. package/src/infra/alerts/index.ts +12 -0
  493. package/src/infra/alerts/infra-sources.ts +180 -0
  494. package/src/infra/alerts/namespace.ts +81 -0
  495. package/src/infra/alerts/redact.ts +21 -0
  496. package/src/infra/alerts/request-faults.ts +41 -0
  497. package/src/infra/alerts/schema.ts +124 -0
  498. package/src/infra/alerts/sinks.ts +125 -0
  499. package/src/infra/alerts/source-options.ts +26 -0
  500. package/src/infra/alerts/sources.ts +503 -0
  501. package/src/infra/alerts/subsystem-sources.ts +137 -0
  502. package/src/infra/audit/emit.ts +87 -0
  503. package/src/infra/audit/index.ts +2 -0
  504. package/src/infra/audit/namespace.ts +22 -0
  505. package/src/infra/audit/schema.sql +35 -0
  506. package/src/infra/audit/types.ts +18 -0
  507. package/src/infra/aws/credentials.ts +147 -0
  508. package/src/infra/aws/sigv4.ts +140 -0
  509. package/src/infra/cache/create.ts +58 -0
  510. package/src/infra/cache/entry.ts +81 -0
  511. package/src/infra/cache/index.ts +4 -0
  512. package/src/infra/cache/namespace.ts +10 -0
  513. package/src/infra/cache/tenant.ts +93 -0
  514. package/src/infra/cache/types.ts +24 -0
  515. package/src/infra/crawl/audit.ts +35 -0
  516. package/src/infra/crawl/errors.ts +25 -0
  517. package/src/infra/crawl/handle.ts +693 -0
  518. package/src/infra/crawl/index.ts +9 -0
  519. package/src/infra/crawl/links.ts +160 -0
  520. package/src/infra/crawl/namespace.ts +36 -0
  521. package/src/infra/crawl/robots.ts +100 -0
  522. package/src/infra/crawl/schema.ts +89 -0
  523. package/src/infra/credit-line/handle.ts +883 -0
  524. package/src/infra/credit-line/index.ts +6 -0
  525. package/src/infra/credit-line/namespace.ts +7 -0
  526. package/src/infra/credit-line/schema.ts +96 -0
  527. package/src/infra/credits/handle.ts +706 -0
  528. package/src/infra/credits/index.ts +5 -0
  529. package/src/infra/credits/namespace.ts +7 -0
  530. package/src/infra/credits/schema.ts +83 -0
  531. package/src/infra/email/audit.ts +36 -0
  532. package/src/infra/email/inbound.ts +622 -0
  533. package/src/infra/email/index.ts +11 -0
  534. package/src/infra/email/mail.ts +517 -0
  535. package/src/infra/email/mime.ts +447 -0
  536. package/src/infra/email/namespace.ts +22 -0
  537. package/src/infra/email/providers.ts +111 -0
  538. package/src/infra/email/schema.ts +96 -0
  539. package/src/infra/email/sns.ts +200 -0
  540. package/src/infra/email/unsubscribe.ts +44 -0
  541. package/src/infra/fetch/address.ts +88 -0
  542. package/src/infra/fetch/connect-retry.ts +87 -0
  543. package/src/infra/fetch/handle.ts +329 -0
  544. package/src/infra/fetch/index.ts +7 -0
  545. package/src/infra/fetch/namespace.ts +70 -0
  546. package/src/infra/idempotency/index.ts +3 -0
  547. package/src/infra/idempotency/middleware.ts +87 -0
  548. package/src/infra/idempotency/store.ts +92 -0
  549. package/src/infra/idempotency/types.ts +30 -0
  550. package/src/infra/join-requests/handle.ts +312 -0
  551. package/src/infra/join-requests/index.ts +2 -0
  552. package/src/infra/join-requests/namespace.ts +7 -0
  553. package/src/infra/join-requests/schema.ts +33 -0
  554. package/src/infra/outbox/core.ts +165 -0
  555. package/src/infra/quota/errors.ts +32 -0
  556. package/src/infra/quota/handle.ts +215 -0
  557. package/src/infra/quota/index.ts +5 -0
  558. package/src/infra/quota/namespace.ts +7 -0
  559. package/src/infra/quota/schema.ts +15 -0
  560. package/src/infra/storage/backend.ts +34 -0
  561. package/src/infra/storage/handle.ts +513 -0
  562. package/src/infra/storage/index.ts +9 -0
  563. package/src/infra/storage/local.ts +104 -0
  564. package/src/infra/storage/namespace.ts +13 -0
  565. package/src/infra/storage/s3.ts +257 -0
  566. package/src/infra/storage/schema.ts +24 -0
  567. package/src/infra/support/handle.ts +854 -0
  568. package/src/infra/support/http.ts +229 -0
  569. package/src/infra/support/index.ts +7 -0
  570. package/src/infra/support/namespace.ts +12 -0
  571. package/src/infra/support/schema.ts +96 -0
  572. package/src/infra/webhook/inbound.ts +45 -0
  573. package/src/infra/webhook/index.ts +5 -0
  574. package/src/infra/webhook/namespace.ts +24 -0
  575. package/src/infra/webhook/outbound.ts +315 -0
  576. package/src/infra/webhook/sign.ts +32 -0
  577. package/src/infra/webhook/verify.ts +82 -0
  578. package/src/invariants/assert.ts +26 -0
  579. package/src/invariants/index.ts +1 -0
  580. package/src/lint/config.ts +56 -0
  581. package/src/lint/doctrine.ts +163 -0
  582. package/src/lint/include.ts +97 -0
  583. package/src/lint/index.ts +3 -0
  584. package/src/lint/preset.ts +10 -0
  585. package/src/lint/resolver.ts +218 -0
  586. package/src/lint/rules/no-reexports.ts +46 -0
  587. package/src/request/behavior/audited.ts +85 -0
  588. package/src/request/behavior/cached.ts +62 -0
  589. package/src/request/behavior/compose.ts +28 -0
  590. package/src/request/behavior/index.ts +5 -0
  591. package/src/request/behavior/namespace.ts +16 -0
  592. package/src/request/behavior/types.ts +26 -0
  593. package/src/request/behavior/versioned.ts +54 -0
  594. package/src/request/context/create.ts +103 -0
  595. package/src/request/context/guard.ts +49 -0
  596. package/src/request/context/index.ts +4 -0
  597. package/src/request/context/run-in-transaction.ts +200 -0
  598. package/src/request/context/store.ts +28 -0
  599. package/src/request/context/types.ts +50 -0
  600. package/src/request/di/builder.ts +535 -0
  601. package/src/request/di/index.ts +8 -0
  602. package/src/request/di/resolve.ts +271 -0
  603. package/src/request/di/types.ts +262 -0
  604. package/src/request/fitting/namespace.ts +7 -0
  605. package/src/request/fixture/batch.ts +71 -0
  606. package/src/request/fixture/builder.ts +26 -0
  607. package/src/request/fixture/errors.ts +10 -0
  608. package/src/request/fixture/index.ts +8 -0
  609. package/src/request/fixture/namespace.ts +23 -0
  610. package/src/request/fixture/paginate.ts +44 -0
  611. package/src/request/fixture/to-handlers.ts +260 -0
  612. package/src/request/fixture/types.ts +79 -0
  613. package/src/request/flow/act-as.ts +78 -0
  614. package/src/request/flow/namespace.ts +17 -0
  615. package/src/request/http/error-step.ts +42 -0
  616. package/src/request/http/file.ts +141 -0
  617. package/src/request/http/index.ts +4 -0
  618. package/src/request/http/middleware.ts +141 -0
  619. package/src/request/http/namespace.ts +5 -0
  620. package/src/request/http/rate-limit-classes.ts +170 -0
  621. package/src/request/http/response.ts +82 -0
  622. package/src/request/http/server.ts +916 -0
  623. package/src/request/http/sniff.ts +261 -0
  624. package/src/request/http/types.ts +250 -0
  625. package/src/request/http/upload.ts +284 -0
  626. package/src/request/internal/text.ts +6 -0
  627. package/src/request/openapi/generate.ts +205 -0
  628. package/src/request/openapi/index.ts +2 -0
  629. package/src/request/openapi/namespace.ts +7 -0
  630. package/src/test/auth.ts +13 -0
  631. package/src/test/auto-setup.ts +45 -0
  632. package/src/test/context.ts +91 -0
  633. package/src/test/crawl-fixture.ts +157 -0
  634. package/src/test/discover-fork.ts +17 -0
  635. package/src/test/fingerprint.ts +135 -0
  636. package/src/test/globals.d.ts +7 -0
  637. package/src/test/index.ts +19 -0
  638. package/src/test/namespace.ts +87 -0
  639. package/src/test/plugin.ts +189 -0
  640. package/src/test/progress.ts +185 -0
  641. package/src/test/reap-after-run.ts +28 -0
  642. package/src/test/reap.ts +286 -0
  643. package/src/test/reset.ts +253 -0
  644. package/src/test/savepoints.ts +172 -0
  645. package/src/test/setup.ts +1422 -0
  646. package/src/test/system-channel.ts +50 -0
  647. package/src/test/vitest-plugin.ts +142 -0
  648. package/src/test/vitest-workspace.ts +507 -0
  649. package/src/test/vitest.ts +333 -0
  650. package/src/test/watch.ts +105 -0
  651. package/src/trace/capture/bounded-serializer.ts +146 -0
  652. package/src/trace/cardinality/analyze.ts +173 -0
  653. package/src/trace/cardinality/branch-context.ts +214 -0
  654. package/src/trace/cardinality/check.ts +33 -0
  655. package/src/trace/cardinality/classify-edge.ts +64 -0
  656. package/src/trace/cardinality/structural-max.ts +107 -0
  657. package/src/trace/check/coverage-check.ts +188 -0
  658. package/src/trace/check/flow-check.ts +103 -0
  659. package/src/trace/check/flow-step-site.ts +234 -0
  660. package/src/trace/check/io-pattern.ts +116 -0
  661. package/src/trace/codemod/add-cardinality.ts +152 -0
  662. package/src/trace/codemod/run.ts +80 -0
  663. package/src/trace/entry/child-trace.ts +21 -0
  664. package/src/trace/entry/close-trace.ts +87 -0
  665. package/src/trace/entry/finalize-trace.ts +28 -0
  666. package/src/trace/entry/open-trace.ts +138 -0
  667. package/src/trace/entry/parent-trace.ts +20 -0
  668. package/src/trace/entry/parse-traceparent.ts +39 -0
  669. package/src/trace/entry/process-start.ts +38 -0
  670. package/src/trace/entry/trace-context.ts +26 -0
  671. package/src/trace/entry/tracing-enabled.ts +10 -0
  672. package/src/trace/external/record.ts +195 -0
  673. package/src/trace/external/report.ts +66 -0
  674. package/src/trace/namespace.ts +19 -0
  675. package/src/trace/partition/day.ts +47 -0
  676. package/src/trace/partition/index.ts +14 -0
  677. package/src/trace/partition/partition-name.ts +23 -0
  678. package/src/trace/partition/plan.ts +82 -0
  679. package/src/trace/partition/schedule.ts +26 -0
  680. package/src/trace/partition/startup-hook.ts +39 -0
  681. package/src/trace/partition/sync.ts +154 -0
  682. package/src/trace/partition/tables.ts +53 -0
  683. package/src/trace/read/tree.ts +83 -0
  684. package/src/trace/retain/copy-on-error.ts +76 -0
  685. package/src/trace/runtime/expire-stale-traces.ts +54 -0
  686. package/src/trace/runtime/flow-step.ts +147 -0
  687. package/src/trace/runtime/flush-steps.ts +31 -0
  688. package/src/trace/runtime/pending-writes.ts +54 -0
  689. package/src/trace/runtime/step-buffer.ts +298 -0
  690. package/src/trace/runtime/step-error.ts +21 -0
  691. package/src/trace/runtime/step-meta.ts +55 -0
  692. package/src/trace/runtime/thenable.ts +12 -0
  693. package/src/trace/runtime/truncate-trace.ts +62 -0
  694. package/src/trace/schema/trace-fields.ts +52 -0
  695. package/src/trace/schema/trace-retained.ts +22 -0
  696. package/src/trace/schema/trace-step-fields.ts +44 -0
  697. package/src/trace/schema/trace-step-retained.ts +25 -0
  698. package/src/trace/schema/trace-step.ts +18 -0
  699. package/src/trace/schema/trace.ts +12 -0
  700. package/src/trace/transformer/apply.ts +79 -0
  701. package/src/trace/transformer/build-meta-literal.ts +30 -0
  702. package/src/trace/transformer/find-flow-step-sites.ts +85 -0
  703. package/src/trace/transformer/inject-flow-step-import.ts +39 -0
  704. package/src/trace/transformer/transform-error.ts +14 -0
  705. package/src/trace/transformer/transform-flow-steps.ts +123 -0
  706. package/src/trace/transformer/wrap-function-declaration.ts +62 -0
  707. package/src/trace/transformer/wrap-variable-declaration.ts +67 -0
  708. package/src/workspace/affected.ts +110 -0
  709. package/src/workspace/changed-files.ts +163 -0
  710. package/src/workspace/check-deps.ts +213 -0
  711. package/src/workspace/checksum.ts +20 -0
  712. package/src/workspace/dependency-graph.ts +67 -0
  713. package/src/workspace/detect-pipework-copies.ts +93 -0
  714. package/src/workspace/discover.ts +206 -0
  715. package/src/workspace/generate.ts +401 -0
  716. package/src/workspace/index.ts +7 -0
  717. package/src/workspace/module-imports-pipework.ts +39 -0
  718. package/src/workspace/module-package-name.ts +12 -0
  719. package/src/workspace/scrub-git-env.ts +23 -0
  720. package/src/workspace/source-paths.ts +36 -0
  721. package/src/workspace/sync.ts +182 -0
  722. package/dist/REFERENCE.md +0 -1477
  723. package/dist/infra/access-links/rate-limit.d.ts +0 -10
  724. package/dist/infra/access-links/rate-limit.d.ts.map +0 -1
  725. package/dist/infra/access-links/rate-limit.js +0 -28
  726. package/dist/infra/access-links/rate-limit.js.map +0 -1
  727. package/dist/infra/email/sigv4.d.ts +0 -24
  728. package/dist/infra/email/sigv4.d.ts.map +0 -1
  729. package/dist/infra/email/sigv4.js +0 -55
  730. package/dist/infra/email/sigv4.js.map +0 -1
@@ -0,0 +1,40 @@
1
+ // Content hashing for step results. A result is identified by what it was made from,
2
+ // so two values hash alike exactly when their canonical JSON is the same.
3
+
4
+ import { createHash } from 'node:crypto'
5
+
6
+ /** JSON with object keys sorted at every depth; `undefined` members are dropped as in JSON.stringify. */
7
+ export function canonicalJson(value: unknown): string {
8
+ return JSON.stringify(canonicalize(value))
9
+ }
10
+
11
+ function canonicalize(value: unknown): unknown {
12
+ if (value === null || typeof value !== 'object') return value
13
+ if (Array.isArray(value)) return value.map(v => canonicalize(v === undefined ? null : v))
14
+ const toJson = (value as { toJSON?: () => unknown }).toJSON
15
+ if (typeof toJson === 'function') return canonicalize(toJson.call(value))
16
+ const out: Record<string, unknown> = {}
17
+ for (const key of Object.keys(value).sort()) {
18
+ const member = (value as Record<string, unknown>)[key]
19
+ if (member !== undefined) out[key] = canonicalize(member)
20
+ }
21
+ return out
22
+ }
23
+
24
+ export function sha256Hex(text: string): string {
25
+ return createHash('sha256').update(text).digest('hex')
26
+ }
27
+
28
+ /** The hash of a step result or an element: its canonical JSON. */
29
+ export function valueHash(value: unknown): string {
30
+ return sha256Hex(canonicalJson(value === undefined ? null : value))
31
+ }
32
+
33
+ /** The hash of a request input: a stored file (an object with a `sha256` string) hashes by that field alone. */
34
+ export function inputHash(value: unknown): string {
35
+ if (value !== null && typeof value === 'object' && !Array.isArray(value)) {
36
+ const sha = (value as { sha256?: unknown }).sha256
37
+ if (typeof sha === 'string') return sha256Hex(canonicalJson({ sha256: sha }))
38
+ }
39
+ return valueHash(value)
40
+ }
@@ -0,0 +1,7 @@
1
+ export { defineGraph } from './graph.js'
2
+ export { StepItemRunningError } from './errors.js'
3
+ export { steps } from './namespace.js'
4
+ export type {
5
+ GraphConfig, GraphStepDefinition, GraphStepContext, GraphForEachContext, GraphRunInfo, GraphFailure, GraphRunStatus,
6
+ RequestOptions, RequestResult, PlanOptions, PlanResult, StepPlan, StepCounts, TaskState, RunTask, RunStatus, EndedRun, EndedRunsOptions, DecideOptions, ScopeOptions, StepResultRecord, RemoveItemOptions, RemoveItemResult, StepGraph, GraphSelection, GraphHost,
7
+ } from './types.js'
@@ -0,0 +1,7 @@
1
+ import { defineGraph } from './graph.js'
2
+
3
+ /** Step graphs over a job queue: results recorded by their inputs, only missing steps run. */
4
+ export const steps = {
5
+ /** Defines a graph of named steps over a jobs Queue. Pass it to `createWorkerSurface({ graphs })` to run its steps. */
6
+ define: defineGraph,
7
+ }
@@ -0,0 +1,97 @@
1
+ // The three tables behind step graphs, all tenant-scoped (forced row-level security
2
+ // in generated migrations). A graph registers them at define() time, so a worker
3
+ // surface or `pipework generate` that has loaded a graph emits their DDL.
4
+
5
+ import { define } from '../../data/domain/index.js'
6
+ import { field } from '../../data/domain/field.js'
7
+ import type { DefinedTable } from '../../data/domain/types.js'
8
+ import { schema } from '../../data/schema/namespace.js'
9
+ import { index, uniqueIndex } from '../../data/db/idx/index.js'
10
+
11
+ export const STEP_RUN_STATUSES = ['running', 'done', 'failed', 'cancelled'] as const
12
+ export const STEP_TASK_STATUSES = ['blocked', 'queued', 'waiting', 'done', 'reused', 'failed', 'cancelled'] as const
13
+
14
+ /** Instance number of a step's single task, or of a fan-out step's expander. */
15
+ export const SINGLE = -1
16
+
17
+ export const stepRun = define('pipework_step_run', {
18
+ id: field.uuid().defaultRandom().primaryKey(),
19
+ tenantId: field.uuid().tenant(),
20
+ graph: field.text(),
21
+ item: field.text(),
22
+ targets: field.jsonb(schema.check.json()),
23
+ status: field.enum(STEP_RUN_STATUSES).default('running'),
24
+ priority: field.integer().default(0),
25
+ stepPriority: field.jsonb(schema.check.json()).nullable(),
26
+ inputs: field.jsonb(schema.check.json()),
27
+ traceId: field.uuid().nullable(),
28
+ traceRootId: field.uuid().nullable(),
29
+ traceStepId: field.uuid().nullable(),
30
+ actor: field.text().nullable(),
31
+ notified: field.boolean().default(false),
32
+ createdAt: field.timestamp().default('now'),
33
+ finishedAt: field.timestamp().nullable(),
34
+ error: field.jsonb(schema.check.json()).nullable(),
35
+ }, {
36
+ indexes: (t) => ({
37
+ byItem: index('idx_pipework_step_run_item').on(t['tenantId'], t['graph'], t['item']),
38
+ byFinished: index('idx_pipework_step_run_finished').on(t['finishedAt']),
39
+ }),
40
+ })
41
+
42
+ export const stepTask = define('pipework_step_task', {
43
+ id: field.uuid().defaultRandom().primaryKey(),
44
+ tenantId: field.uuid().tenant(),
45
+ runId: field.uuid(),
46
+ step: field.text(),
47
+ instance: field.integer().default(SINGLE),
48
+ element: field.jsonb(schema.check.json()).nullable(),
49
+ status: field.enum(STEP_TASK_STATUSES).default('blocked'),
50
+ jobId: field.uuid().nullable(),
51
+ inputKey: field.text().nullable(),
52
+ resultId: field.uuid().nullable(),
53
+ error: field.jsonb(schema.check.json()).nullable(),
54
+ }, {
55
+ indexes: (t) => ({
56
+ byInstance: uniqueIndex('uq_pipework_step_task_instance').on(t['runId'], t['step'], t['instance']),
57
+ byJob: index('idx_pipework_step_task_job').on(t['jobId']),
58
+ }),
59
+ })
60
+
61
+ export const stepResult = define('pipework_step_result', {
62
+ id: field.uuid().defaultRandom().primaryKey(),
63
+ tenantId: field.uuid().tenant(),
64
+ graph: field.text(),
65
+ item: field.text(),
66
+ step: field.text(),
67
+ version: field.integer(),
68
+ inputKey: field.text(),
69
+ instance: field.integer().default(SINGLE),
70
+ value: field.jsonb(schema.check.json()),
71
+ valueHash: field.text(),
72
+ createdAt: field.timestamp().default('now'),
73
+ }, {
74
+ indexes: (t) => ({
75
+ byKey: uniqueIndex('uq_pipework_step_result_key')
76
+ .on(t['tenantId'], t['graph'], t['item'], t['step'], t['version'], t['inputKey'], t['instance']),
77
+ byCreated: index('idx_pipework_step_result_created').on(t['createdAt']),
78
+ }),
79
+ })
80
+
81
+ let registered = false
82
+
83
+ /** Marks the step tables as in use, so `pipework generate` emits them. */
84
+ export function registerStepTables(): void {
85
+ registered = true
86
+ }
87
+
88
+ /** Registered step table definitions — merged into `pipework generate` via internalDefinitions. */
89
+ export function stepDefinitions(): ReadonlyMap<string, DefinedTable> {
90
+ return registered
91
+ ? new Map<string, DefinedTable>([
92
+ ['pipework_step_run', stepRun as unknown as DefinedTable],
93
+ ['pipework_step_task', stepTask as unknown as DefinedTable],
94
+ ['pipework_step_result', stepResult as unknown as DefinedTable],
95
+ ])
96
+ : new Map()
97
+ }
@@ -0,0 +1,318 @@
1
+ import type { DB } from '../../data/db/index.js'
2
+ import type { Queue } from '../jobs/queue.js'
3
+ import type { Handler } from '../../request/di/types.js'
4
+ import type { Manifold } from '../../core/pipework.js'
5
+ import type { DeadLetterInfo } from '../jobs/queue.js'
6
+
7
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- a recorded result has the shape its step returned; the graph cannot know it, and callers read fields off it directly
8
+ export type StepValue = any
9
+
10
+ /** What a step's `run` (and `autoAccept`) is handed. */
11
+ export interface GraphStepContext {
12
+ /** Each needed request input by its value; each needed step by its result — an array in instance order for a fan-out step, or that instance's one result for a step named in `byElement`. */
13
+ readonly needs: Readonly<Record<string, StepValue>>
14
+ /** The element this instance was made for; undefined outside a fan-out step. */
15
+ readonly element: StepValue
16
+ /** The instance number; null outside a fan-out step. */
17
+ readonly index: number | null
18
+ readonly item: string
19
+ /** The request inputs this step declared in `needs`. */
20
+ readonly inputs: Readonly<Record<string, unknown>>
21
+ readonly runId: string
22
+ /** The step job's 1-based attempt, counting an attempt a lost worker used. Not available in `autoAccept`. */
23
+ readonly attempt: number
24
+ /** The step job's attempt limit: the step's `maxAttempts`, else the queue's default. Not available in `autoAccept`. */
25
+ readonly maxAttempts: number
26
+ /** Aborted when `cancel()` is called in this process while the step runs. */
27
+ readonly signal: AbortSignal
28
+ /**
29
+ * Runs `fn` in its own short transaction on the graph's database, as the run's tenant (RLS applies),
30
+ * and commits it on return. `run` itself holds no transaction: it is never open while the step waits.
31
+ * In `autoAccept`, `fn` runs in the transaction that advances the run.
32
+ */
33
+ checkpoint<R>(fn: (tx: DB) => Promise<R>): Promise<R>
34
+ /**
35
+ * Registers `fn` to run, in registration order, in the transaction that records this step's result:
36
+ * the rows it writes and the result commit together or not at all. If `fn` throws, neither is kept,
37
+ * the attempt fails and the task stays queued. Not available in `autoAccept` (throws).
38
+ */
39
+ withResult(fn: (tx: DB) => Promise<void>): void
40
+ }
41
+
42
+ /** What a `forEach` is handed: the step's needs, before any element exists. */
43
+ export type GraphForEachContext = Omit<GraphStepContext, 'element' | 'index' | 'signal' | 'checkpoint' | 'withResult' | 'attempt' | 'maxAttempts'>
44
+
45
+ export interface GraphStepDefinition {
46
+ /** Part of the content key: bump it and the step (and what depends on it) runs again. */
47
+ readonly version: number
48
+ /** Request inputs and other steps this step reads. */
49
+ readonly needs?: readonly string[]
50
+ /** Makes this a fan-out step: one instance per element of the returned list. */
51
+ readonly forEach?: (ctx: GraphForEachContext) => readonly unknown[] | Promise<readonly unknown[]>
52
+ /**
53
+ * Names from `needs` that only choose the list. `forEach` sees them; they are not part of an instance's
54
+ * content key, and `run` and `autoAccept` do not see them. They are still needs: the step waits for them.
55
+ */
56
+ readonly forEachOnly?: readonly string[]
57
+ /**
58
+ * Names from `needs` that are fan-out steps. Makes this step a fan-out over the same list as they: it has
59
+ * no `forEach`; instance i's element is their instance i's element, it waits only for their instance i,
60
+ * its content key hashes only their instance i's result, and `needs[name]` holds that one result (not an
61
+ * array). Every name must share one list: each is a step with `forEach` or itself has `byElement` leading
62
+ * back to the same `forEach` step. A step needing a `byElement` step without listing it here gets the
63
+ * whole array, as for any fan-out step.
64
+ */
65
+ readonly byElement?: readonly string[]
66
+ /** The step's work; its JSON return value is the recorded result. */
67
+ readonly run?: (ctx: GraphStepContext) => unknown
68
+ /** The step waits for `decide()`, holding no worker. */
69
+ readonly person?: boolean
70
+ /** Person steps: a returned value (not undefined) records it and skips the wait. */
71
+ readonly autoAccept?: (ctx: Omit<GraphStepContext, 'attempt' | 'maxAttempts'>) => unknown
72
+ /**
73
+ * The priority of this step's own jobs (an integer), in place of the run's, for every run and every instance of a
74
+ * fan-out step. Higher is claimed first, across runs, per worker class. A measurement step sets one below every
75
+ * run priority in use: it is claimed after every other step, and its run still finishes. The steps it needs and
76
+ * the steps that need it keep the run's priority, as does the run's finish job.
77
+ */
78
+ readonly priority?: number
79
+ /** Attempts the step's job gets. Default: the queue's. */
80
+ readonly maxAttempts?: number
81
+ /**
82
+ * The longest one try of `run` may take, in milliseconds (a positive integer). A try that exceeds it is aborted
83
+ * through `signal` and counted as a failed attempt, so `maxAttempts` and dead-lettering apply; a `run` that
84
+ * ignores its signal still ends the try on time. The class's job timeout, if any, still applies on top.
85
+ */
86
+ readonly timeoutMs?: number
87
+ /**
88
+ * The class of worker that takes this step: letters, digits, "_" and "-", starting with a letter. Default "default".
89
+ * The step's job type is `pipework.step.<graph>` for the default class, `pipework.step.<graph>:<class>` otherwise.
90
+ */
91
+ readonly workers?: string
92
+ }
93
+
94
+ /** The run a callback is about. */
95
+ export interface GraphRunInfo {
96
+ readonly runId: string
97
+ readonly item: string
98
+ readonly tenant: string
99
+ }
100
+
101
+ export interface GraphFailure extends GraphRunInfo {
102
+ readonly step: string
103
+ readonly error: unknown
104
+ }
105
+
106
+ export interface GraphConfig {
107
+ /** The graph's name; its step jobs have type `pipework.step.<name>`. */
108
+ readonly name: string
109
+ /** The queue whose table holds the step jobs. A worker surface running them must read the same table. */
110
+ readonly queue: Queue
111
+ /** Request input names a step may list in `needs`. */
112
+ readonly inputs?: readonly string[]
113
+ readonly steps: Readonly<Record<string, GraphStepDefinition>>
114
+ /** Default priority of a run. Default 0. */
115
+ readonly priority?: number
116
+ /** `sweep()` deletes results older than this many days. Default: never. */
117
+ readonly resultRetentionDays?: number
118
+ /** Session variable the tenant policies read. Default: the Manifold's `rls.sessionVar`, else `pipework.tenant_id`. */
119
+ readonly sessionVar?: string
120
+ /** Called once when a run is done, in the run's tenant Flow and a transaction. A throw fails the attempt that finished the run. */
121
+ readonly onDone?: (run: GraphRunInfo) => void | Promise<void>
122
+ /** Called once when a run fails, in the run's tenant Flow, in a savepoint; a throw is logged and swallowed. With tracing on it runs in its own job trace, under the failed step job's parent, inside a `pipework/steps:on-failed` step. */
123
+ readonly onFailed?: (failure: GraphFailure) => void | Promise<void>
124
+ }
125
+
126
+ export interface RequestOptions {
127
+ readonly item: string
128
+ /** The steps whose results are wanted. */
129
+ readonly targets: readonly string[]
130
+ readonly inputs?: Readonly<Record<string, unknown>>
131
+ readonly priority?: number
132
+ /** Priorities for named steps of this run only, in place of their definitions' `priority` and the run's. Keys must be steps of the graph; values integers. */
133
+ readonly stepPriority?: Readonly<Record<string, number>>
134
+ /** Default: the active Flow's tenant. */
135
+ readonly tenant?: string
136
+ /** Record the requesting Flow's actor (`auth.userId`) on the run; its step jobs, onDone and onFailed run with `auth = { userId }`. Default true. */
137
+ readonly carryActor?: boolean
138
+ }
139
+
140
+ export interface RequestResult {
141
+ readonly runId: string
142
+ /** Tasks already satisfied by a recorded result at the moment of the request. */
143
+ readonly reused: number
144
+ /** Step jobs the request enqueued. */
145
+ readonly queued: number
146
+ }
147
+
148
+ export interface PlanOptions {
149
+ readonly item: string
150
+ readonly targets: readonly string[]
151
+ readonly inputs?: Readonly<Record<string, unknown>>
152
+ readonly tenant?: string
153
+ }
154
+
155
+ export interface StepPlan {
156
+ /** Instances whose result is recorded. */
157
+ readonly reusable: number
158
+ /** Instances that must run; null when a fan-out step's list is not yet known. */
159
+ readonly toRun: number | null
160
+ }
161
+
162
+ export interface PlanResult {
163
+ readonly steps: Readonly<Record<string, StepPlan>>
164
+ }
165
+
166
+ export interface StepCounts {
167
+ readonly total: number
168
+ readonly done: number
169
+ readonly reused: number
170
+ readonly running: number
171
+ readonly waiting: number
172
+ readonly failed: number
173
+ readonly cancelled: number
174
+ /** Fan-out steps only: whether the step's list is known, i.e. its instances exist. */
175
+ readonly listKnown?: boolean
176
+ }
177
+
178
+ /** A task's state, classified as the step counts are. */
179
+ export type TaskState = 'done' | 'reused' | 'running' | 'waiting' | 'failed' | 'cancelled'
180
+
181
+ /** One task of a run. */
182
+ export interface RunTask {
183
+ readonly step: string
184
+ /** The fan-out instance's index; -1 for a single step and for a fan-out's list task. */
185
+ readonly instance: number
186
+ /** The fan-out list's item for this instance; null for instance -1. */
187
+ readonly element: unknown
188
+ readonly state: TaskState
189
+ }
190
+
191
+ export type GraphRunStatus = 'running' | 'done' | 'failed' | 'cancelled'
192
+
193
+ export interface RunStatus {
194
+ readonly status: GraphRunStatus
195
+ readonly steps: Readonly<Record<string, StepCounts>>
196
+ /** Every task of the run, in step order then instance order, fan-out list tasks (instance -1) included. */
197
+ readonly tasks: readonly RunTask[]
198
+ /** The inputs the run was requested with. */
199
+ readonly inputs: Readonly<Record<string, unknown>>
200
+ /** When the run ended; null while it runs. */
201
+ readonly finishedAt: Date | null
202
+ }
203
+
204
+ export interface EndedRunsOptions extends ScopeOptions {
205
+ /** Only these runs. */
206
+ readonly ids?: readonly string[]
207
+ /** Only runs that finished before this time. */
208
+ readonly finishedBefore?: Date
209
+ /** At most this many, oldest finish first. Default 1000. */
210
+ readonly limit?: number
211
+ }
212
+
213
+ /** A run of the graph that is no longer running. */
214
+ export interface EndedRun {
215
+ readonly id: string
216
+ readonly tenant: string
217
+ readonly item: string
218
+ readonly status: Exclude<GraphRunStatus, 'running'>
219
+ readonly finishedAt: Date
220
+ /** The row's delivery flag: for a done run, true once onDone has run (or the graph has none); set at once for a failed run, so it does not show that onFailed ran. */
221
+ readonly notified: boolean
222
+ }
223
+
224
+ /** The tenant a call acts for; default the active Flow's. Needed under row-level security on a connection that has not set it. */
225
+ export interface ScopeOptions {
226
+ readonly tenant?: string
227
+ }
228
+
229
+ /** One recorded result of a step. `instance` is -1 for a step that does not fan out. */
230
+ export interface StepResultRecord {
231
+ readonly id: string
232
+ readonly step: string
233
+ readonly version: number
234
+ readonly instance: number
235
+ readonly value: unknown
236
+ readonly createdAt: Date
237
+ }
238
+
239
+ export interface RemoveItemOptions extends ScopeOptions {
240
+ /** A run still in progress: 'refuse' (default) throws StepItemRunningError and deletes nothing; 'cancel' cancels it first. */
241
+ readonly running?: 'refuse' | 'cancel'
242
+ }
243
+
244
+ export interface RemoveItemResult {
245
+ readonly runs: number
246
+ readonly tasks: number
247
+ readonly results: number
248
+ }
249
+
250
+ export interface DecideOptions extends ScopeOptions {
251
+ readonly runId: string
252
+ readonly step: string
253
+ /** The instance, for a fan-out person step. */
254
+ readonly index?: number
255
+ readonly value: unknown
256
+ }
257
+
258
+ /** Where a worker surface runs a graph: the Manifold and the database name its handlers use. */
259
+ export interface GraphHost {
260
+ readonly instance: Manifold
261
+ readonly database: string | undefined
262
+ }
263
+
264
+ /** Some classes of a graph's steps, for a worker surface to take. */
265
+ export interface GraphSelection {
266
+ readonly graph: StepGraph
267
+ readonly classes: readonly string[]
268
+ }
269
+
270
+ export interface StepGraph {
271
+ readonly name: string
272
+ /** `pipework.step.<name>`: the job type of the default class. */
273
+ readonly jobType: string
274
+ /** The job type of every class the graph's steps use. */
275
+ readonly jobTypes: readonly string[]
276
+ /** The queue the step jobs are enqueued on. */
277
+ readonly queue: Queue
278
+ /** Asks for the targets of an item. Only steps without a recorded result for their inputs run. */
279
+ request(db: DB, options: RequestOptions): Promise<RequestResult>
280
+ /** What a request would reuse and what it would run, without running anything. */
281
+ plan(db: DB, options: PlanOptions): Promise<PlanResult>
282
+ status(db: DB, runId: string, options?: ScopeOptions): Promise<RunStatus>
283
+ /** Cancels a running run: pending work is cancelled, no callback fires. */
284
+ cancel(db: DB, runId: string, options?: ScopeOptions): Promise<void>
285
+ /** Records a person's value on a waiting person step, or replaces it on a finished one. */
286
+ decide(db: DB, options: DecideOptions): Promise<void>
287
+ /** Resolves with the run's final status, or 'timeout'. */
288
+ wait(db: DB, runId: string, options: { readonly timeoutMs: number; readonly tenant?: string }): Promise<GraphRunStatus | 'timeout'>
289
+ /**
290
+ * Deletes, for one tenant, the results and the finished runs (with their tasks) older than `resultRetentionDays`;
291
+ * returns how many results. Runs in `options.tenant` or the Flow's; under forced row-level security a sweep with
292
+ * neither sees no rows, so sweep each tenant. On an owner connection with no tenant it sweeps all of them.
293
+ */
294
+ sweep(db: DB, options?: ScopeOptions): Promise<number>
295
+ /**
296
+ * The runs of this graph that have ended, oldest finish first, in `options.tenant` or the Flow's; under forced
297
+ * row-level security a call with neither sees no rows. On an owner connection with no tenant it lists every tenant's.
298
+ */
299
+ ended(db: DB, options?: EndedRunsOptions): Promise<readonly EndedRun[]>
300
+ /** Deletes a tenant's runs, tasks and results. */
301
+ removeTenant(db: DB, tenantId: string): Promise<void>
302
+ /** The results recorded for one item of this graph, in the tenant; their values hold whatever the steps returned (e.g. file references to remove). */
303
+ results(db: DB, item: string, options?: ScopeOptions): Promise<readonly StepResultRecord[]>
304
+ /** Deletes one item's runs, tasks and results in the caller's transaction and tenant. A done run not yet delivered gets no onDone. */
305
+ removeItem(db: DB, item: string, options?: RemoveItemOptions): Promise<RemoveItemResult>
306
+ /**
307
+ * A selection of the steps of the named worker classes, for `WorkerSurfaceConfig.graphs`; the graph itself selects all.
308
+ * Concurrency is per surface: run one worker surface per class, each with its own `concurrency`.
309
+ * Throws naming the graph and class when no step uses a class.
310
+ */
311
+ workers(...classes: string[]): GraphSelection
312
+ /** Used by the worker surface. */
313
+ readonly worker: {
314
+ /** One handler per class, or per selected class. */
315
+ handlers(database: string | undefined, classes?: readonly string[]): Handler<Record<string, unknown>, unknown>[]
316
+ onDeadLetter(tx: DB, job: DeadLetterInfo, host: GraphHost): Promise<void>
317
+ }
318
+ }
@@ -0,0 +1,9 @@
1
+ export { installAuditChain } from './install.js'
2
+ export { chainHead, verify, removeTenant } from './verify.js'
3
+ export type {
4
+ AuditChainHead,
5
+ AuditChainBreak,
6
+ AuditChainBreakReason,
7
+ AuditVerifyOptions,
8
+ AuditVerifyResult,
9
+ } from './verify.js'
@@ -0,0 +1,113 @@
1
+ // Installs the audit hash chain: head/retired bookkeeping tables, the
2
+ // canonical-form function, and the triggers on `audit_record`. Everything is
3
+ // CREATE OR REPLACE / IF NOT EXISTS, so installing twice is a no-op.
4
+ //
5
+ // The chain is written only by the BEFORE INSERT trigger: it locks the tenant's
6
+ // head row, so concurrent writers to one chain queue behind each other and the
7
+ // sequence is gapless (a rolled-back transaction releases the lock and leaves
8
+ // no gap). A second trigger rejects UPDATE and DELETE; partition DROP, which
9
+ // is how retention works, does not fire row triggers.
10
+
11
+ import { sql } from '../../data/db/sql.js'
12
+ import type { DB } from '../../data/db/index.js'
13
+ import { quoteIdentifier } from '../../data/db/identifiers.js'
14
+ import { MAINTENANCE_SETTING } from '../../data/db/maintenance.js'
15
+
16
+ /** Chain key of rows with no tenant — they form their own chain. */
17
+ export const NULL_TENANT_CHAIN_KEY = '00000000-0000-0000-0000-000000000000'
18
+
19
+ /** Set transaction-locally by `audit.removeTenant` to the one tenant whose rows may be deleted. */
20
+ export const AUDIT_ERASE_SETTING = 'pipework.audit_erase_tenant'
21
+
22
+ export const CHAIN_HEAD_TABLE = 'pipework_audit_chain_head'
23
+ export const CHAIN_RETIRED_TABLE = 'pipework_audit_chain_retired'
24
+
25
+ /** SQL expression for a row's chain key; the chain index is built on exactly this. */
26
+ export const CHAIN_KEY_SQL = `coalesce(tenant_id, '${NULL_TENANT_CHAIN_KEY}'::uuid)`
27
+
28
+ const GENESIS_HASH_HEX = '00'.repeat(32)
29
+
30
+ /**
31
+ * The text hashed for one row. `jsonb_build_array(...)::text` of, in order:
32
+ * chain_seq, id, trace_day (YYYY-MM-DD), trace_id, trace_step_id, tenant_id,
33
+ * actor_id, entity, verb, before, after, captured_inputs, occurred_at (UTC,
34
+ * `YYYY-MM-DD"T"HH24:MI:SS.US"Z"`), then entity_id only when it is not null.
35
+ * A row without an entity_id (read-touches, rows written before the column
36
+ * existed) hashes exactly as it always did. Independent of the session TimeZone.
37
+ */
38
+ export const CANONICAL_FUNCTION_SQL = `
39
+ CREATE OR REPLACE FUNCTION pipework_audit_record_canonical(r audit_record) RETURNS text
40
+ LANGUAGE sql IMMUTABLE SET search_path = pg_catalog AS $$
41
+ SELECT (jsonb_build_array(
42
+ r.chain_seq, r.id, to_char(r.trace_day, 'YYYY-MM-DD'), r.trace_id, r.trace_step_id,
43
+ r.tenant_id, r.actor_id, r.entity, r.verb, r.before, r.after, r.captured_inputs,
44
+ to_char(r.occurred_at AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.US"Z"')
45
+ ) || CASE WHEN r.entity_id IS NULL THEN '[]'::jsonb ELSE jsonb_build_array(r.entity_id) END)::text
46
+ $$`
47
+
48
+ function insertTriggerFunctionSql(schema: string): string {
49
+ return `
50
+ CREATE OR REPLACE FUNCTION pipework_audit_chain_before_insert() RETURNS trigger
51
+ LANGUAGE plpgsql SECURITY DEFINER SET search_path = pg_catalog, ${schema} AS $$
52
+ DECLARE
53
+ k uuid := coalesce(NEW.tenant_id, '${NULL_TENANT_CHAIN_KEY}'::uuid);
54
+ h record;
55
+ BEGIN
56
+ INSERT INTO ${CHAIN_HEAD_TABLE} (chain_key, seq, hash)
57
+ VALUES (k, 0, decode('${GENESIS_HASH_HEX}', 'hex'))
58
+ ON CONFLICT DO NOTHING;
59
+ SELECT seq, hash INTO h FROM ${CHAIN_HEAD_TABLE} WHERE chain_key = k FOR UPDATE;
60
+ NEW.chain_seq := h.seq + 1;
61
+ NEW.prev_hash := encode(h.hash, 'hex');
62
+ NEW.hash := encode(sha256(h.hash || convert_to(pipework_audit_record_canonical(NEW), 'UTF8')), 'hex');
63
+ UPDATE ${CHAIN_HEAD_TABLE} SET seq = NEW.chain_seq, hash = decode(NEW.hash, 'hex') WHERE chain_key = k;
64
+ RETURN NEW;
65
+ END
66
+ $$`
67
+ }
68
+
69
+ const APPEND_ONLY_FUNCTION_SQL = `
70
+ CREATE OR REPLACE FUNCTION pipework_audit_chain_append_only() RETURNS trigger
71
+ LANGUAGE plpgsql SET search_path = pg_catalog AS $$
72
+ BEGIN
73
+ IF TG_OP = 'DELETE'
74
+ AND current_setting('${MAINTENANCE_SETTING}', true) = 'on'
75
+ AND OLD.tenant_id::text = current_setting('${AUDIT_ERASE_SETTING}', true) THEN
76
+ RETURN OLD;
77
+ END IF;
78
+ RAISE EXCEPTION 'audit_record is append-only: % is not allowed', TG_OP
79
+ USING ERRCODE = 'restrict_violation';
80
+ END
81
+ $$`
82
+
83
+ /**
84
+ * Install the audit hash chain on `audit_record`. The table must exist.
85
+ * Idempotent.
86
+ */
87
+ export async function installAuditChain(db: DB): Promise<void> {
88
+ const schemaRows = await db.execute(sql`SELECT current_schema() AS schema`)
89
+ const schema = quoteIdentifier(String(schemaRows[0]!['schema']), 'schema')
90
+
91
+ await db.execute(sql.raw(
92
+ `CREATE TABLE IF NOT EXISTS ${CHAIN_HEAD_TABLE} (` +
93
+ `chain_key uuid PRIMARY KEY, seq bigint NOT NULL, hash bytea NOT NULL)`,
94
+ ))
95
+ await db.execute(sql.raw(
96
+ `CREATE TABLE IF NOT EXISTS ${CHAIN_RETIRED_TABLE} (` +
97
+ `chain_key uuid NOT NULL, seq bigint NOT NULL, hash bytea NOT NULL, PRIMARY KEY (chain_key, seq))`,
98
+ ))
99
+ await db.execute(sql.raw(CANONICAL_FUNCTION_SQL))
100
+ await db.execute(sql.raw(insertTriggerFunctionSql(schema)))
101
+ await db.execute(sql.raw(APPEND_ONLY_FUNCTION_SQL))
102
+ await db.execute(sql.raw(
103
+ `CREATE OR REPLACE TRIGGER pipework_audit_chain_insert BEFORE INSERT ON audit_record ` +
104
+ `FOR EACH ROW EXECUTE FUNCTION pipework_audit_chain_before_insert()`,
105
+ ))
106
+ await db.execute(sql.raw(
107
+ `CREATE OR REPLACE TRIGGER pipework_audit_chain_append_only BEFORE UPDATE OR DELETE ON audit_record ` +
108
+ `FOR EACH ROW EXECUTE FUNCTION pipework_audit_chain_append_only()`,
109
+ ))
110
+ await db.execute(sql.raw(
111
+ `CREATE INDEX IF NOT EXISTS audit_record_chain_idx ON audit_record ((${CHAIN_KEY_SQL}), chain_seq)`,
112
+ ))
113
+ }
@@ -0,0 +1,52 @@
1
+ // Retention for the audit chain: before an `audit_record` day partition is
2
+ // dropped, its (seq, hash) pairs move to the retired table so the rows that
3
+ // remain still link to the ones that went.
4
+
5
+ import { sql } from '../../data/db/sql.js'
6
+ import type { DB } from '../../data/db/index.js'
7
+ import { CHAIN_HEAD_TABLE, CHAIN_RETIRED_TABLE, CHAIN_KEY_SQL } from './install.js'
8
+
9
+ /** True when the chain tables exist, i.e. the chain was installed on this database. */
10
+ export async function auditChainInstalled(db: DB): Promise<boolean> {
11
+ const rows = await db.execute(sql`
12
+ SELECT to_regclass(${CHAIN_HEAD_TABLE}) IS NOT NULL
13
+ AND to_regclass(${CHAIN_RETIRED_TABLE}) IS NOT NULL AS installed
14
+ `)
15
+ return rows[0]?.['installed'] === true
16
+ }
17
+
18
+ /**
19
+ * Removes one tenant's chain: its head and its retired entries. Run inside the
20
+ * same maintenance transaction that deletes the tenant's records.
21
+ */
22
+ export async function removeTenantChain(tx: DB, tenantId: string): Promise<void> {
23
+ await tx.execute(sql`DELETE FROM ${sql.raw(CHAIN_HEAD_TABLE)} WHERE chain_key = ${tenantId}::uuid`)
24
+ await tx.execute(sql`DELETE FROM ${sql.raw(CHAIN_RETIRED_TABLE)} WHERE chain_key = ${tenantId}::uuid`)
25
+ }
26
+
27
+ /**
28
+ * Retire and drop one audit partition. Run inside a transaction. `child` is the
29
+ * already-quoted partition name.
30
+ *
31
+ * After the drop, a chain keeps only the retired entries at or above
32
+ * `(lowest remaining seq) - 1` — the one entry the oldest remaining row links
33
+ * to. A chain with no rows left keeps just its highest retired entry.
34
+ */
35
+ export async function retireAuditPartition(tx: DB, child: string): Promise<void> {
36
+ // The pruning DELETE below scans every audit partition; with a few hundred
37
+ // partitions Postgres spends tens of seconds JIT-compiling it (measured 10s
38
+ // with no row-level security, 20s with it) to save milliseconds.
39
+ await tx.execute(sql`SELECT set_config('jit', 'off', true)`)
40
+ await tx.execute(sql.raw(
41
+ `INSERT INTO ${CHAIN_RETIRED_TABLE} (chain_key, seq, hash) ` +
42
+ `SELECT ${CHAIN_KEY_SQL}, chain_seq, decode(hash, 'hex') FROM ${child} ` +
43
+ `WHERE chain_seq IS NOT NULL ON CONFLICT DO NOTHING`,
44
+ ))
45
+ await tx.execute(sql.raw(`DROP TABLE IF EXISTS ${child}`))
46
+ await tx.execute(sql.raw(
47
+ `DELETE FROM ${CHAIN_RETIRED_TABLE} r WHERE r.seq < coalesce(` +
48
+ `(SELECT min(a.chain_seq) - 1 FROM audit_record a ` +
49
+ `WHERE ${CHAIN_KEY_SQL.replace('tenant_id', 'a.tenant_id')} = r.chain_key AND a.chain_seq IS NOT NULL), ` +
50
+ `(SELECT max(x.seq) FROM ${CHAIN_RETIRED_TABLE} x WHERE x.chain_key = r.chain_key))`,
51
+ ))
52
+ }