pipework 0.252.0 → 0.253.0

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 (695) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/dist/infra/support/handle.d.ts +8 -1
  3. package/dist/infra/support/handle.d.ts.map +1 -1
  4. package/dist/infra/support/handle.js +9 -2
  5. package/dist/infra/support/handle.js.map +1 -1
  6. package/dist/infra/support/namespace.d.ts.map +1 -1
  7. package/package.json +4 -3
  8. package/src/async/jobs/abortable-delay.ts +18 -0
  9. package/src/async/jobs/claims.ts +52 -0
  10. package/src/async/jobs/cron.ts +179 -0
  11. package/src/async/jobs/errors.ts +56 -0
  12. package/src/async/jobs/exclusion.ts +117 -0
  13. package/src/async/jobs/execute.ts +219 -0
  14. package/src/async/jobs/index.ts +10 -0
  15. package/src/async/jobs/listener.ts +135 -0
  16. package/src/async/jobs/namespace.ts +20 -0
  17. package/src/async/jobs/queue-door.ts +44 -0
  18. package/src/async/jobs/queue.ts +1012 -0
  19. package/src/async/jobs/run-script.ts +67 -0
  20. package/src/async/jobs/scheduler.ts +218 -0
  21. package/src/async/jobs/schedules-table.ts +37 -0
  22. package/src/async/jobs/serialize-error.ts +48 -0
  23. package/src/async/jobs/table.ts +191 -0
  24. package/src/async/pipeline/define.ts +53 -0
  25. package/src/async/pipeline/errors.ts +35 -0
  26. package/src/async/pipeline/execute.ts +53 -0
  27. package/src/async/pipeline/index.ts +13 -0
  28. package/src/async/pipeline/namespace.ts +12 -0
  29. package/src/async/pipeline/resume.ts +67 -0
  30. package/src/async/pipeline/run-steps.ts +98 -0
  31. package/src/async/pipeline/types.ts +57 -0
  32. package/src/async/state-machine/define.ts +64 -0
  33. package/src/async/state-machine/errors.ts +29 -0
  34. package/src/async/state-machine/index.ts +3 -0
  35. package/src/async/state-machine/namespace.ts +12 -0
  36. package/src/async/state-machine/transition.ts +90 -0
  37. package/src/async/state-machine/types.ts +37 -0
  38. package/src/async/steps/errors.ts +31 -0
  39. package/src/async/steps/graph.ts +1214 -0
  40. package/src/async/steps/hash.ts +40 -0
  41. package/src/async/steps/index.ts +7 -0
  42. package/src/async/steps/namespace.ts +7 -0
  43. package/src/async/steps/table.ts +97 -0
  44. package/src/async/steps/types.ts +318 -0
  45. package/src/audit/chain/index.ts +9 -0
  46. package/src/audit/chain/install.ts +113 -0
  47. package/src/audit/chain/retire.ts +52 -0
  48. package/src/audit/chain/verify.ts +257 -0
  49. package/src/audit/query/cursor.ts +37 -0
  50. package/src/audit/query/index.ts +9 -0
  51. package/src/audit/query/query.ts +223 -0
  52. package/src/audit/schema/audit-record-fields.ts +68 -0
  53. package/src/audit/schema/audit-record.ts +23 -0
  54. package/src/auth/auth/audit.ts +31 -0
  55. package/src/auth/auth/chain.ts +84 -0
  56. package/src/auth/auth/cookie.ts +136 -0
  57. package/src/auth/auth/email-links.ts +311 -0
  58. package/src/auth/auth/guard.ts +128 -0
  59. package/src/auth/auth/index.ts +18 -0
  60. package/src/auth/auth/multi-org.ts +337 -0
  61. package/src/auth/auth/namespace.ts +34 -0
  62. package/src/auth/auth/parse-expiry.ts +13 -0
  63. package/src/auth/auth/passwords.ts +110 -0
  64. package/src/auth/auth/schema.ts +110 -0
  65. package/src/auth/auth/sessions.ts +565 -0
  66. package/src/auth/auth/sign-in.ts +156 -0
  67. package/src/auth/auth/signing-keys.ts +69 -0
  68. package/src/auth/auth/types.ts +26 -0
  69. package/src/auth/auth/uuid.ts +10 -0
  70. package/src/auth/rbac/cache.ts +55 -0
  71. package/src/auth/rbac/check.ts +30 -0
  72. package/src/auth/rbac/create.ts +35 -0
  73. package/src/auth/rbac/index.ts +5 -0
  74. package/src/auth/rbac/middleware.ts +20 -0
  75. package/src/auth/rbac/namespace.ts +15 -0
  76. package/src/auth/rbac/resolve.ts +31 -0
  77. package/src/auth/rbac/schema.sql +31 -0
  78. package/src/auth/rbac/seed.ts +67 -0
  79. package/src/auth/rbac/tables.ts +32 -0
  80. package/src/auth/rbac/types.ts +65 -0
  81. package/src/auth/tenant/extract.ts +33 -0
  82. package/src/auth/tenant/in-tenant.ts +49 -0
  83. package/src/auth/tenant/index.ts +7 -0
  84. package/src/auth/tenant/namespace.ts +23 -0
  85. package/src/auth/tenant/propagate.ts +20 -0
  86. package/src/auth/tenant/rls.ts +120 -0
  87. package/src/auth/tenant/scope.ts +33 -0
  88. package/src/auth/tenant/scoped-db.ts +581 -0
  89. package/src/auth/tenant/types.ts +14 -0
  90. package/src/auth/tenant/validate.ts +27 -0
  91. package/src/cli/commands/add.ts +158 -0
  92. package/src/cli/commands/alerts.ts +98 -0
  93. package/src/cli/commands/check.ts +1418 -0
  94. package/src/cli/commands/codemod.ts +78 -0
  95. package/src/cli/commands/db.ts +356 -0
  96. package/src/cli/commands/dev.ts +101 -0
  97. package/src/cli/commands/generate.ts +75 -0
  98. package/src/cli/commands/init.ts +36 -0
  99. package/src/cli/commands/install.ts +28 -0
  100. package/src/cli/commands/lint.ts +62 -0
  101. package/src/cli/commands/migrate.ts +132 -0
  102. package/src/cli/commands/openapi.ts +63 -0
  103. package/src/cli/commands/remove.ts +72 -0
  104. package/src/cli/commands/run.ts +50 -0
  105. package/src/cli/commands/scope.ts +54 -0
  106. package/src/cli/commands/serve.ts +68 -0
  107. package/src/cli/commands/shard.ts +31 -0
  108. package/src/cli/commands/shared/config-pipework-version.ts +28 -0
  109. package/src/cli/commands/shared/find-config.ts +22 -0
  110. package/src/cli/commands/shared/load-config.ts +35 -0
  111. package/src/cli/commands/shared/pnpm-install.ts +95 -0
  112. package/src/cli/commands/shared/report-removed.ts +8 -0
  113. package/src/cli/commands/sync.ts +32 -0
  114. package/src/cli/commands/test.ts +307 -0
  115. package/src/cli/commands/upgrade.ts +48 -0
  116. package/src/cli/dev-bootstrap.ts +24 -0
  117. package/src/cli/errors.ts +10 -0
  118. package/src/cli/index.ts +303 -0
  119. package/src/cli/output.ts +17 -0
  120. package/src/cli/port.ts +15 -0
  121. package/src/cli/surface-kinds.ts +20 -0
  122. package/src/client/index.ts +4 -0
  123. package/src/client/poll.ts +130 -0
  124. package/src/client/report-fault.ts +119 -0
  125. package/src/core/config/app-pool.ts +19 -0
  126. package/src/core/config/aws-env.ts +29 -0
  127. package/src/core/config/base-dir.ts +24 -0
  128. package/src/core/config/discover.ts +64 -0
  129. package/src/core/config/env-resolve.ts +87 -0
  130. package/src/core/config/env-types.ts +116 -0
  131. package/src/core/config/env.ts +55 -0
  132. package/src/core/config/errors.ts +9 -0
  133. package/src/core/config/import-config.ts +40 -0
  134. package/src/core/config/index.ts +8 -0
  135. package/src/core/config/load.ts +417 -0
  136. package/src/core/config/namespace.ts +14 -0
  137. package/src/core/config/resolve.ts +195 -0
  138. package/src/core/config/schema.ts +488 -0
  139. package/src/core/config/statement-observer.ts +48 -0
  140. package/src/core/config/ts-inline-hooks.ts +75 -0
  141. package/src/core/config/ts-register.ts +4 -0
  142. package/src/core/config/ts-resolve-hooks.ts +101 -0
  143. package/src/core/errors/base.ts +49 -0
  144. package/src/core/errors/http.ts +146 -0
  145. package/src/core/errors/index.ts +2 -0
  146. package/src/core/errors/namespace.ts +31 -0
  147. package/src/core/lifecycle/drain.ts +33 -0
  148. package/src/core/lifecycle/health.ts +72 -0
  149. package/src/core/lifecycle/index.ts +4 -0
  150. package/src/core/lifecycle/namespace.ts +7 -0
  151. package/src/core/lifecycle/startup.ts +102 -0
  152. package/src/core/lifecycle/types.ts +36 -0
  153. package/src/core/logging/context.ts +60 -0
  154. package/src/core/logging/error-chain.ts +68 -0
  155. package/src/core/logging/index.ts +5 -0
  156. package/src/core/logging/logger.ts +65 -0
  157. package/src/core/logging/namespace.ts +16 -0
  158. package/src/core/logging/proxy.ts +22 -0
  159. package/src/core/logging/redact-match.ts +24 -0
  160. package/src/core/logging/types.ts +47 -0
  161. package/src/core/pipework.ts +566 -0
  162. package/src/core/runtime/broker.ts +69 -0
  163. package/src/core/surface/http.ts +133 -0
  164. package/src/core/surface/index.ts +11 -0
  165. package/src/core/surface/job-timeout.ts +40 -0
  166. package/src/core/surface/namespace.ts +13 -0
  167. package/src/core/surface/script.ts +33 -0
  168. package/src/core/surface/start.ts +142 -0
  169. package/src/core/surface/types.ts +225 -0
  170. package/src/core/surface/validate.ts +88 -0
  171. package/src/core/surface/worker.ts +528 -0
  172. package/src/core/validation/connectivity.ts +49 -0
  173. package/src/core/validation/domain.ts +99 -0
  174. package/src/core/validation/index.ts +5 -0
  175. package/src/core/validation/queue-tables.ts +312 -0
  176. package/src/core/validation/rls-role.ts +138 -0
  177. package/src/core/validation/startup.ts +51 -0
  178. package/src/core/validation/surfaces.ts +65 -0
  179. package/src/core/validation/types.ts +25 -0
  180. package/src/data/db/advisory-lock.ts +55 -0
  181. package/src/data/db/aggregates.ts +172 -0
  182. package/src/data/db/array-elements.ts +111 -0
  183. package/src/data/db/bulk-set.ts +53 -0
  184. package/src/data/db/col/index.ts +14 -0
  185. package/src/data/db/composed.ts +53 -0
  186. package/src/data/db/db.ts +276 -0
  187. package/src/data/db/excluded.ts +16 -0
  188. package/src/data/db/expressions.ts +473 -0
  189. package/src/data/db/filter.ts +90 -0
  190. package/src/data/db/function-source.ts +114 -0
  191. package/src/data/db/identifiers.ts +16 -0
  192. package/src/data/db/idx/index.ts +9 -0
  193. package/src/data/db/in-list.ts +49 -0
  194. package/src/data/db/index.ts +9 -0
  195. package/src/data/db/insert-split.ts +137 -0
  196. package/src/data/db/instrumentation.ts +82 -0
  197. package/src/data/db/maintenance.ts +28 -0
  198. package/src/data/db/namespace.ts +107 -0
  199. package/src/data/db/owner-labels.ts +9 -0
  200. package/src/data/db/pool.ts +187 -0
  201. package/src/data/db/query-label.ts +13 -0
  202. package/src/data/db/query-watch.ts +116 -0
  203. package/src/data/db/raw-client-warning.ts +37 -0
  204. package/src/data/db/readonly.ts +23 -0
  205. package/src/data/db/reservation.ts +151 -0
  206. package/src/data/db/schema-features.ts +14 -0
  207. package/src/data/db/serializable.ts +83 -0
  208. package/src/data/db/sql.ts +1 -0
  209. package/src/data/db/timeouts.ts +76 -0
  210. package/src/data/db/types.ts +18 -0
  211. package/src/data/db/unbound.ts +33 -0
  212. package/src/data/db/values.ts +29 -0
  213. package/src/data/domain/brand.ts +15 -0
  214. package/src/data/domain/build-factory.ts +193 -0
  215. package/src/data/domain/build-schema.ts +161 -0
  216. package/src/data/domain/build-table.ts +355 -0
  217. package/src/data/domain/define.ts +239 -0
  218. package/src/data/domain/external-registry.ts +34 -0
  219. package/src/data/domain/external.ts +77 -0
  220. package/src/data/domain/field.ts +279 -0
  221. package/src/data/domain/index.ts +13 -0
  222. package/src/data/domain/naming.ts +6 -0
  223. package/src/data/domain/projections.ts +57 -0
  224. package/src/data/domain/trace-safe-projection.ts +31 -0
  225. package/src/data/domain/traits.ts +108 -0
  226. package/src/data/domain/types.ts +289 -0
  227. package/src/data/jsonb/composed.ts +18 -0
  228. package/src/data/jsonb/index.ts +1 -0
  229. package/src/data/jsonb/namespace.ts +30 -0
  230. package/src/data/jsonb/query.ts +86 -0
  231. package/src/data/migrate/append-only-generate.ts +47 -0
  232. package/src/data/migrate/applied.ts +47 -0
  233. package/src/data/migrate/apply.ts +309 -0
  234. package/src/data/migrate/check.ts +119 -0
  235. package/src/data/migrate/diff.ts +357 -0
  236. package/src/data/migrate/door-generate.ts +130 -0
  237. package/src/data/migrate/framework-rls.ts +58 -0
  238. package/src/data/migrate/generate.ts +201 -0
  239. package/src/data/migrate/history.ts +215 -0
  240. package/src/data/migrate/index.ts +9 -0
  241. package/src/data/migrate/internal-definitions.ts +175 -0
  242. package/src/data/migrate/post-process.ts +73 -0
  243. package/src/data/migrate/pragma.ts +73 -0
  244. package/src/data/migrate/provision-framework.ts +147 -0
  245. package/src/data/migrate/rls-generate.ts +61 -0
  246. package/src/data/migrate/run.ts +124 -0
  247. package/src/data/migrate/safety.ts +167 -0
  248. package/src/data/migrate/snapshot.ts +368 -0
  249. package/src/data/migrate/sql-emit.ts +392 -0
  250. package/src/data/migrate/statements.ts +312 -0
  251. package/src/data/query/alias.ts +126 -0
  252. package/src/data/query/batch.ts +8 -0
  253. package/src/data/query/cache/core/cache.ts +78 -0
  254. package/src/data/query/cache/core/index.ts +1 -0
  255. package/src/data/query/cache/core/types.ts +29 -0
  256. package/src/data/query/cache/readme.md +230 -0
  257. package/src/data/query/casing.ts +76 -0
  258. package/src/data/query/collation.ts +11 -0
  259. package/src/data/query/column-builder.ts +370 -0
  260. package/src/data/query/column.ts +150 -0
  261. package/src/data/query/entity.ts +42 -0
  262. package/src/data/query/errors.ts +33 -0
  263. package/src/data/query/index.ts +14 -0
  264. package/src/data/query/logger.ts +47 -0
  265. package/src/data/query/operations.ts +45 -0
  266. package/src/data/query/pg-core/alias.ts +12 -0
  267. package/src/data/query/pg-core/checks.ts +32 -0
  268. package/src/data/query/pg-core/columns/all.ts +71 -0
  269. package/src/data/query/pg-core/columns/bigint.ts +110 -0
  270. package/src/data/query/pg-core/columns/bigserial.ts +128 -0
  271. package/src/data/query/pg-core/columns/boolean.ts +43 -0
  272. package/src/data/query/pg-core/columns/char.ts +85 -0
  273. package/src/data/query/pg-core/columns/cidr.ts +43 -0
  274. package/src/data/query/pg-core/columns/common.ts +366 -0
  275. package/src/data/query/pg-core/columns/custom.ts +232 -0
  276. package/src/data/query/pg-core/columns/date.common.ts +15 -0
  277. package/src/data/query/pg-core/columns/date.ts +112 -0
  278. package/src/data/query/pg-core/columns/double-precision.ts +55 -0
  279. package/src/data/query/pg-core/columns/enum.ts +201 -0
  280. package/src/data/query/pg-core/columns/index.ts +35 -0
  281. package/src/data/query/pg-core/columns/inet.ts +43 -0
  282. package/src/data/query/pg-core/columns/int.common.ts +57 -0
  283. package/src/data/query/pg-core/columns/integer.ts +53 -0
  284. package/src/data/query/pg-core/columns/interval.ts +83 -0
  285. package/src/data/query/pg-core/columns/json.ts +58 -0
  286. package/src/data/query/pg-core/columns/jsonb.ts +56 -0
  287. package/src/data/query/pg-core/columns/line.ts +117 -0
  288. package/src/data/query/pg-core/columns/macaddr.ts +43 -0
  289. package/src/data/query/pg-core/columns/macaddr8.ts +43 -0
  290. package/src/data/query/pg-core/columns/numeric.ts +227 -0
  291. package/src/data/query/pg-core/columns/point.ts +127 -0
  292. package/src/data/query/pg-core/columns/postgis_extension/geometry.ts +119 -0
  293. package/src/data/query/pg-core/columns/postgis_extension/utils.ts +47 -0
  294. package/src/data/query/pg-core/columns/real.ts +58 -0
  295. package/src/data/query/pg-core/columns/serial.ts +55 -0
  296. package/src/data/query/pg-core/columns/smallint.ts +53 -0
  297. package/src/data/query/pg-core/columns/smallserial.ts +60 -0
  298. package/src/data/query/pg-core/columns/text.ts +67 -0
  299. package/src/data/query/pg-core/columns/time.ts +72 -0
  300. package/src/data/query/pg-core/columns/timestamp.ts +163 -0
  301. package/src/data/query/pg-core/columns/uuid.ts +51 -0
  302. package/src/data/query/pg-core/columns/varchar.ts +89 -0
  303. package/src/data/query/pg-core/columns/vector_extension/bit.ts +68 -0
  304. package/src/data/query/pg-core/columns/vector_extension/halfvec.ts +80 -0
  305. package/src/data/query/pg-core/columns/vector_extension/sparsevec.ts +67 -0
  306. package/src/data/query/pg-core/columns/vector_extension/vector.ts +80 -0
  307. package/src/data/query/pg-core/db.ts +674 -0
  308. package/src/data/query/pg-core/dialect.ts +1404 -0
  309. package/src/data/query/pg-core/expressions.ts +25 -0
  310. package/src/data/query/pg-core/foreign-keys.ts +130 -0
  311. package/src/data/query/pg-core/index.ts +21 -0
  312. package/src/data/query/pg-core/indexes.ts +276 -0
  313. package/src/data/query/pg-core/policies.ts +56 -0
  314. package/src/data/query/pg-core/primary-keys.ts +62 -0
  315. package/src/data/query/pg-core/query-builders/count.ts +86 -0
  316. package/src/data/query/pg-core/query-builders/delete.ts +293 -0
  317. package/src/data/query/pg-core/query-builders/index.ts +7 -0
  318. package/src/data/query/pg-core/query-builders/insert.ts +452 -0
  319. package/src/data/query/pg-core/query-builders/query-builder.ts +123 -0
  320. package/src/data/query/pg-core/query-builders/query.ts +157 -0
  321. package/src/data/query/pg-core/query-builders/raw.ts +49 -0
  322. package/src/data/query/pg-core/query-builders/refresh-materialized-view.ts +108 -0
  323. package/src/data/query/pg-core/query-builders/select.ts +1346 -0
  324. package/src/data/query/pg-core/query-builders/select.types.ts +437 -0
  325. package/src/data/query/pg-core/query-builders/update.ts +626 -0
  326. package/src/data/query/pg-core/roles.ts +41 -0
  327. package/src/data/query/pg-core/schema.ts +72 -0
  328. package/src/data/query/pg-core/sequence.ts +41 -0
  329. package/src/data/query/pg-core/session.ts +292 -0
  330. package/src/data/query/pg-core/subquery.ts +46 -0
  331. package/src/data/query/pg-core/table.ts +252 -0
  332. package/src/data/query/pg-core/unique-constraint.ts +73 -0
  333. package/src/data/query/pg-core/utils/array.ts +95 -0
  334. package/src/data/query/pg-core/utils/index.ts +1 -0
  335. package/src/data/query/pg-core/utils.ts +104 -0
  336. package/src/data/query/pg-core/view-base.ts +14 -0
  337. package/src/data/query/pg-core/view-common.ts +1 -0
  338. package/src/data/query/pg-core/view.ts +435 -0
  339. package/src/data/query/pg-core/with-builder.ts +41 -0
  340. package/src/data/query/postgres-js/README.md +46 -0
  341. package/src/data/query/postgres-js/array-parser.ts +45 -0
  342. package/src/data/query/postgres-js/driver-error-boundary.ts +149 -0
  343. package/src/data/query/postgres-js/driver.ts +163 -0
  344. package/src/data/query/postgres-js/index.ts +2 -0
  345. package/src/data/query/postgres-js/session.ts +234 -0
  346. package/src/data/query/primary-key.ts +11 -0
  347. package/src/data/query/query-builders/query-builder.ts +19 -0
  348. package/src/data/query/query-builders/select.types.ts +175 -0
  349. package/src/data/query/query-promise.ts +35 -0
  350. package/src/data/query/relations.ts +725 -0
  351. package/src/data/query/runnable-query.ts +12 -0
  352. package/src/data/query/selection-proxy.ts +121 -0
  353. package/src/data/query/session.ts +8 -0
  354. package/src/data/query/sql/expressions/conditions.ts +741 -0
  355. package/src/data/query/sql/expressions/index.ts +2 -0
  356. package/src/data/query/sql/expressions/select.ts +45 -0
  357. package/src/data/query/sql/functions/aggregate.ts +129 -0
  358. package/src/data/query/sql/functions/index.ts +2 -0
  359. package/src/data/query/sql/functions/vector.ts +178 -0
  360. package/src/data/query/sql/index.ts +3 -0
  361. package/src/data/query/sql/sql.ts +794 -0
  362. package/src/data/query/subquery.ts +53 -0
  363. package/src/data/query/table.ts +208 -0
  364. package/src/data/query/table.utils.ts +2 -0
  365. package/src/data/query/tracing-utils.ts +3 -0
  366. package/src/data/query/tracing.ts +11 -0
  367. package/src/data/query/utils.ts +357 -0
  368. package/src/data/query/version.ts +4 -0
  369. package/src/data/query/view-common.ts +1 -0
  370. package/src/data/schema/branded.ts +13 -0
  371. package/src/data/schema/generated/coerce.ts +5 -0
  372. package/src/data/schema/generated/combinator.ts +6 -0
  373. package/src/data/schema/generated/encoding.ts +5 -0
  374. package/src/data/schema/generated/error.ts +6 -0
  375. package/src/data/schema/generated/format.ts +5 -0
  376. package/src/data/schema/generated/type-constructor.ts +10 -0
  377. package/src/data/schema/generated/type.ts +6 -0
  378. package/src/data/schema/index.ts +6 -0
  379. package/src/data/schema/json-schema.ts +33 -0
  380. package/src/data/schema/namespace.ts +55 -0
  381. package/src/data/schema/parse.ts +39 -0
  382. package/src/data/schema/types.ts +39 -0
  383. package/src/data/temporal/append-only.ts +134 -0
  384. package/src/data/temporal/close.ts +27 -0
  385. package/src/data/temporal/columns.ts +10 -0
  386. package/src/data/temporal/definition-queries.ts +70 -0
  387. package/src/data/temporal/errors.ts +10 -0
  388. package/src/data/temporal/get-current.ts +22 -0
  389. package/src/data/temporal/index.ts +9 -0
  390. package/src/data/temporal/namespace.ts +36 -0
  391. package/src/data/temporal/query.ts +25 -0
  392. package/src/data/temporal/resolve.ts +17 -0
  393. package/src/data/temporal/revise.ts +67 -0
  394. package/src/data/temporal/types.ts +22 -0
  395. package/src/data/valve/artifact.ts +33 -0
  396. package/src/data/valve/contract-diff.ts +21 -0
  397. package/src/data/valve/cross.ts +57 -0
  398. package/src/data/valve/crossing-ledger.ts +50 -0
  399. package/src/data/valve/decision.ts +44 -0
  400. package/src/data/valve/durable-crossing.ts +52 -0
  401. package/src/data/valve/execute-crossing.ts +77 -0
  402. package/src/data/valve/faucet.ts +56 -0
  403. package/src/data/valve/gate.ts +114 -0
  404. package/src/data/valve/graph.ts +77 -0
  405. package/src/data/valve/index.ts +53 -0
  406. package/src/data/valve/namespace.ts +57 -0
  407. package/src/data/valve/registry.ts +50 -0
  408. package/src/data/valve/schema/valve-crossing-fields.ts +23 -0
  409. package/src/data/valve/schema/valve-crossing.ts +9 -0
  410. package/src/data/valve/supply.ts +154 -0
  411. package/src/data/valve/types.ts +143 -0
  412. package/src/data/vector/columns.ts +27 -0
  413. package/src/data/vector/index.ts +4 -0
  414. package/src/data/vector/namespace.ts +33 -0
  415. package/src/data/vector/query.ts +85 -0
  416. package/src/data/vector/validate.ts +16 -0
  417. package/src/data/write/audit/actor.ts +15 -0
  418. package/src/data/write/audit/audit-columns.ts +18 -0
  419. package/src/data/write/audit/audit-select.ts +111 -0
  420. package/src/data/write/audit/bound-transaction.ts +14 -0
  421. package/src/data/write/audit/cte-compile.ts +32 -0
  422. package/src/data/write/audit/decode-returning.ts +47 -0
  423. package/src/data/write/audit/final-select.ts +43 -0
  424. package/src/data/write/audit/infra-audit.ts +36 -0
  425. package/src/data/write/audit/primary-key.ts +65 -0
  426. package/src/data/write/audit/projection.ts +41 -0
  427. package/src/data/write/audit/read-touches.ts +86 -0
  428. package/src/data/write/audit/touch-match.ts +78 -0
  429. package/src/data/write/audit/verb-delete.ts +62 -0
  430. package/src/data/write/audit/verb-insert.ts +36 -0
  431. package/src/data/write/audit/verb-select.ts +47 -0
  432. package/src/data/write/audit/verb-update.ts +60 -0
  433. package/src/data/write/audit/verb-upsert.ts +104 -0
  434. package/src/data/write/conflict.ts +43 -0
  435. package/src/data/write/delete.ts +38 -0
  436. package/src/data/write/execute.ts +243 -0
  437. package/src/data/write/index.ts +11 -0
  438. package/src/data/write/insert.ts +63 -0
  439. package/src/data/write/state.ts +43 -0
  440. package/src/data/write/update.ts +54 -0
  441. package/src/data/write/upsert.ts +58 -0
  442. package/src/data/write/values.ts +19 -0
  443. package/src/index.ts +230 -0
  444. package/src/infra/access-links/code.ts +70 -0
  445. package/src/infra/access-links/handle.ts +892 -0
  446. package/src/infra/access-links/index.ts +9 -0
  447. package/src/infra/access-links/namespace.ts +30 -0
  448. package/src/infra/access-links/person-check.ts +177 -0
  449. package/src/infra/access-links/schema.ts +71 -0
  450. package/src/infra/alerts/async-sources.ts +231 -0
  451. package/src/infra/alerts/audit.ts +36 -0
  452. package/src/infra/alerts/beat.ts +123 -0
  453. package/src/infra/alerts/client-faults.ts +111 -0
  454. package/src/infra/alerts/faults.ts +156 -0
  455. package/src/infra/alerts/handle.ts +778 -0
  456. package/src/infra/alerts/http.ts +192 -0
  457. package/src/infra/alerts/index.ts +12 -0
  458. package/src/infra/alerts/infra-sources.ts +180 -0
  459. package/src/infra/alerts/namespace.ts +81 -0
  460. package/src/infra/alerts/redact.ts +21 -0
  461. package/src/infra/alerts/request-faults.ts +41 -0
  462. package/src/infra/alerts/schema.ts +124 -0
  463. package/src/infra/alerts/sinks.ts +125 -0
  464. package/src/infra/alerts/source-options.ts +26 -0
  465. package/src/infra/alerts/sources.ts +503 -0
  466. package/src/infra/alerts/subsystem-sources.ts +137 -0
  467. package/src/infra/audit/emit.ts +87 -0
  468. package/src/infra/audit/index.ts +2 -0
  469. package/src/infra/audit/namespace.ts +22 -0
  470. package/src/infra/audit/schema.sql +35 -0
  471. package/src/infra/audit/types.ts +18 -0
  472. package/src/infra/aws/credentials.ts +147 -0
  473. package/src/infra/aws/sigv4.ts +140 -0
  474. package/src/infra/cache/create.ts +58 -0
  475. package/src/infra/cache/entry.ts +81 -0
  476. package/src/infra/cache/index.ts +4 -0
  477. package/src/infra/cache/namespace.ts +10 -0
  478. package/src/infra/cache/tenant.ts +93 -0
  479. package/src/infra/cache/types.ts +24 -0
  480. package/src/infra/crawl/audit.ts +35 -0
  481. package/src/infra/crawl/errors.ts +25 -0
  482. package/src/infra/crawl/handle.ts +693 -0
  483. package/src/infra/crawl/index.ts +9 -0
  484. package/src/infra/crawl/links.ts +160 -0
  485. package/src/infra/crawl/namespace.ts +36 -0
  486. package/src/infra/crawl/robots.ts +100 -0
  487. package/src/infra/crawl/schema.ts +89 -0
  488. package/src/infra/credit-line/handle.ts +883 -0
  489. package/src/infra/credit-line/index.ts +6 -0
  490. package/src/infra/credit-line/namespace.ts +7 -0
  491. package/src/infra/credit-line/schema.ts +96 -0
  492. package/src/infra/credits/handle.ts +706 -0
  493. package/src/infra/credits/index.ts +5 -0
  494. package/src/infra/credits/namespace.ts +7 -0
  495. package/src/infra/credits/schema.ts +83 -0
  496. package/src/infra/email/audit.ts +36 -0
  497. package/src/infra/email/inbound.ts +622 -0
  498. package/src/infra/email/index.ts +11 -0
  499. package/src/infra/email/mail.ts +517 -0
  500. package/src/infra/email/mime.ts +447 -0
  501. package/src/infra/email/namespace.ts +22 -0
  502. package/src/infra/email/providers.ts +111 -0
  503. package/src/infra/email/schema.ts +96 -0
  504. package/src/infra/email/sns.ts +200 -0
  505. package/src/infra/email/unsubscribe.ts +44 -0
  506. package/src/infra/fetch/address.ts +88 -0
  507. package/src/infra/fetch/connect-retry.ts +87 -0
  508. package/src/infra/fetch/handle.ts +329 -0
  509. package/src/infra/fetch/index.ts +7 -0
  510. package/src/infra/fetch/namespace.ts +70 -0
  511. package/src/infra/idempotency/index.ts +3 -0
  512. package/src/infra/idempotency/middleware.ts +87 -0
  513. package/src/infra/idempotency/store.ts +92 -0
  514. package/src/infra/idempotency/types.ts +30 -0
  515. package/src/infra/join-requests/handle.ts +312 -0
  516. package/src/infra/join-requests/index.ts +2 -0
  517. package/src/infra/join-requests/namespace.ts +7 -0
  518. package/src/infra/join-requests/schema.ts +33 -0
  519. package/src/infra/outbox/core.ts +165 -0
  520. package/src/infra/quota/errors.ts +32 -0
  521. package/src/infra/quota/handle.ts +215 -0
  522. package/src/infra/quota/index.ts +5 -0
  523. package/src/infra/quota/namespace.ts +7 -0
  524. package/src/infra/quota/schema.ts +15 -0
  525. package/src/infra/storage/backend.ts +34 -0
  526. package/src/infra/storage/handle.ts +513 -0
  527. package/src/infra/storage/index.ts +9 -0
  528. package/src/infra/storage/local.ts +104 -0
  529. package/src/infra/storage/namespace.ts +13 -0
  530. package/src/infra/storage/s3.ts +257 -0
  531. package/src/infra/storage/schema.ts +24 -0
  532. package/src/infra/support/handle.ts +869 -0
  533. package/src/infra/support/http.ts +229 -0
  534. package/src/infra/support/index.ts +7 -0
  535. package/src/infra/support/namespace.ts +12 -0
  536. package/src/infra/support/schema.ts +96 -0
  537. package/src/infra/webhook/inbound.ts +45 -0
  538. package/src/infra/webhook/index.ts +5 -0
  539. package/src/infra/webhook/namespace.ts +24 -0
  540. package/src/infra/webhook/outbound.ts +315 -0
  541. package/src/infra/webhook/sign.ts +32 -0
  542. package/src/infra/webhook/verify.ts +82 -0
  543. package/src/invariants/assert.ts +26 -0
  544. package/src/invariants/index.ts +1 -0
  545. package/src/lint/config.ts +56 -0
  546. package/src/lint/doctrine.ts +163 -0
  547. package/src/lint/include.ts +97 -0
  548. package/src/lint/index.ts +3 -0
  549. package/src/lint/preset.ts +10 -0
  550. package/src/lint/resolver.ts +218 -0
  551. package/src/lint/rules/no-reexports.ts +46 -0
  552. package/src/request/behavior/audited.ts +85 -0
  553. package/src/request/behavior/cached.ts +62 -0
  554. package/src/request/behavior/compose.ts +28 -0
  555. package/src/request/behavior/index.ts +5 -0
  556. package/src/request/behavior/namespace.ts +16 -0
  557. package/src/request/behavior/types.ts +26 -0
  558. package/src/request/behavior/versioned.ts +54 -0
  559. package/src/request/context/create.ts +103 -0
  560. package/src/request/context/guard.ts +49 -0
  561. package/src/request/context/index.ts +4 -0
  562. package/src/request/context/run-in-transaction.ts +200 -0
  563. package/src/request/context/store.ts +28 -0
  564. package/src/request/context/types.ts +50 -0
  565. package/src/request/di/builder.ts +535 -0
  566. package/src/request/di/index.ts +8 -0
  567. package/src/request/di/resolve.ts +271 -0
  568. package/src/request/di/types.ts +262 -0
  569. package/src/request/fitting/namespace.ts +7 -0
  570. package/src/request/fixture/batch.ts +71 -0
  571. package/src/request/fixture/builder.ts +26 -0
  572. package/src/request/fixture/errors.ts +10 -0
  573. package/src/request/fixture/index.ts +8 -0
  574. package/src/request/fixture/namespace.ts +23 -0
  575. package/src/request/fixture/paginate.ts +44 -0
  576. package/src/request/fixture/to-handlers.ts +260 -0
  577. package/src/request/fixture/types.ts +79 -0
  578. package/src/request/flow/act-as.ts +78 -0
  579. package/src/request/flow/namespace.ts +17 -0
  580. package/src/request/http/error-step.ts +42 -0
  581. package/src/request/http/file.ts +141 -0
  582. package/src/request/http/index.ts +4 -0
  583. package/src/request/http/middleware.ts +141 -0
  584. package/src/request/http/namespace.ts +5 -0
  585. package/src/request/http/rate-limit-classes.ts +170 -0
  586. package/src/request/http/response.ts +82 -0
  587. package/src/request/http/server.ts +916 -0
  588. package/src/request/http/sniff.ts +261 -0
  589. package/src/request/http/types.ts +250 -0
  590. package/src/request/http/upload.ts +284 -0
  591. package/src/request/internal/text.ts +6 -0
  592. package/src/request/openapi/generate.ts +205 -0
  593. package/src/request/openapi/index.ts +2 -0
  594. package/src/request/openapi/namespace.ts +7 -0
  595. package/src/test/auth.ts +13 -0
  596. package/src/test/auto-setup.ts +45 -0
  597. package/src/test/context.ts +91 -0
  598. package/src/test/crawl-fixture.ts +157 -0
  599. package/src/test/discover-fork.ts +17 -0
  600. package/src/test/fingerprint.ts +135 -0
  601. package/src/test/globals.d.ts +7 -0
  602. package/src/test/index.ts +19 -0
  603. package/src/test/namespace.ts +87 -0
  604. package/src/test/plugin.ts +189 -0
  605. package/src/test/progress.ts +185 -0
  606. package/src/test/reap-after-run.ts +28 -0
  607. package/src/test/reap.ts +286 -0
  608. package/src/test/reset.ts +253 -0
  609. package/src/test/savepoints.ts +172 -0
  610. package/src/test/setup.ts +1422 -0
  611. package/src/test/system-channel.ts +50 -0
  612. package/src/test/vitest-plugin.ts +142 -0
  613. package/src/test/vitest-workspace.ts +507 -0
  614. package/src/test/vitest.ts +333 -0
  615. package/src/test/watch.ts +105 -0
  616. package/src/trace/capture/bounded-serializer.ts +146 -0
  617. package/src/trace/cardinality/analyze.ts +173 -0
  618. package/src/trace/cardinality/branch-context.ts +214 -0
  619. package/src/trace/cardinality/check.ts +33 -0
  620. package/src/trace/cardinality/classify-edge.ts +64 -0
  621. package/src/trace/cardinality/structural-max.ts +107 -0
  622. package/src/trace/check/coverage-check.ts +188 -0
  623. package/src/trace/check/flow-check.ts +103 -0
  624. package/src/trace/check/flow-step-site.ts +234 -0
  625. package/src/trace/check/io-pattern.ts +116 -0
  626. package/src/trace/codemod/add-cardinality.ts +152 -0
  627. package/src/trace/codemod/run.ts +80 -0
  628. package/src/trace/entry/child-trace.ts +21 -0
  629. package/src/trace/entry/close-trace.ts +87 -0
  630. package/src/trace/entry/finalize-trace.ts +28 -0
  631. package/src/trace/entry/open-trace.ts +138 -0
  632. package/src/trace/entry/parent-trace.ts +20 -0
  633. package/src/trace/entry/parse-traceparent.ts +39 -0
  634. package/src/trace/entry/process-start.ts +38 -0
  635. package/src/trace/entry/trace-context.ts +26 -0
  636. package/src/trace/entry/tracing-enabled.ts +10 -0
  637. package/src/trace/external/record.ts +195 -0
  638. package/src/trace/external/report.ts +66 -0
  639. package/src/trace/namespace.ts +19 -0
  640. package/src/trace/partition/day.ts +47 -0
  641. package/src/trace/partition/index.ts +14 -0
  642. package/src/trace/partition/partition-name.ts +23 -0
  643. package/src/trace/partition/plan.ts +82 -0
  644. package/src/trace/partition/schedule.ts +26 -0
  645. package/src/trace/partition/startup-hook.ts +39 -0
  646. package/src/trace/partition/sync.ts +154 -0
  647. package/src/trace/partition/tables.ts +53 -0
  648. package/src/trace/read/tree.ts +83 -0
  649. package/src/trace/retain/copy-on-error.ts +76 -0
  650. package/src/trace/runtime/expire-stale-traces.ts +54 -0
  651. package/src/trace/runtime/flow-step.ts +147 -0
  652. package/src/trace/runtime/flush-steps.ts +31 -0
  653. package/src/trace/runtime/pending-writes.ts +54 -0
  654. package/src/trace/runtime/step-buffer.ts +298 -0
  655. package/src/trace/runtime/step-error.ts +21 -0
  656. package/src/trace/runtime/step-meta.ts +55 -0
  657. package/src/trace/runtime/thenable.ts +12 -0
  658. package/src/trace/runtime/truncate-trace.ts +62 -0
  659. package/src/trace/schema/trace-fields.ts +52 -0
  660. package/src/trace/schema/trace-retained.ts +22 -0
  661. package/src/trace/schema/trace-step-fields.ts +44 -0
  662. package/src/trace/schema/trace-step-retained.ts +25 -0
  663. package/src/trace/schema/trace-step.ts +18 -0
  664. package/src/trace/schema/trace.ts +12 -0
  665. package/src/trace/transformer/apply.ts +79 -0
  666. package/src/trace/transformer/build-meta-literal.ts +30 -0
  667. package/src/trace/transformer/find-flow-step-sites.ts +85 -0
  668. package/src/trace/transformer/inject-flow-step-import.ts +39 -0
  669. package/src/trace/transformer/transform-error.ts +14 -0
  670. package/src/trace/transformer/transform-flow-steps.ts +123 -0
  671. package/src/trace/transformer/wrap-function-declaration.ts +62 -0
  672. package/src/trace/transformer/wrap-variable-declaration.ts +67 -0
  673. package/src/workspace/affected.ts +110 -0
  674. package/src/workspace/changed-files.ts +163 -0
  675. package/src/workspace/check-deps.ts +213 -0
  676. package/src/workspace/checksum.ts +20 -0
  677. package/src/workspace/dependency-graph.ts +67 -0
  678. package/src/workspace/detect-pipework-copies.ts +93 -0
  679. package/src/workspace/discover.ts +206 -0
  680. package/src/workspace/generate.ts +401 -0
  681. package/src/workspace/index.ts +7 -0
  682. package/src/workspace/module-imports-pipework.ts +39 -0
  683. package/src/workspace/module-package-name.ts +12 -0
  684. package/src/workspace/scrub-git-env.ts +23 -0
  685. package/src/workspace/source-paths.ts +36 -0
  686. package/src/workspace/sync.ts +182 -0
  687. package/dist/REFERENCE.md +0 -1477
  688. package/dist/infra/access-links/rate-limit.d.ts +0 -10
  689. package/dist/infra/access-links/rate-limit.d.ts.map +0 -1
  690. package/dist/infra/access-links/rate-limit.js +0 -28
  691. package/dist/infra/access-links/rate-limit.js.map +0 -1
  692. package/dist/infra/email/sigv4.d.ts +0 -24
  693. package/dist/infra/email/sigv4.d.ts.map +0 -1
  694. package/dist/infra/email/sigv4.js +0 -55
  695. package/dist/infra/email/sigv4.js.map +0 -1
@@ -0,0 +1,57 @@
1
+ import type { FieldDescriptor, FieldFlags, FieldRecord } from './types.js'
2
+ import type { InferFieldOutput } from './brand.js'
3
+
4
+ type FlagsOf<F> = F extends FieldDescriptor<unknown, infer TF> ? TF : FieldFlags
5
+ type HasFlag<F, K extends keyof FieldFlags> = FlagsOf<F>[K] extends true ? true : false
6
+
7
+ type InsertIncluded<F> =
8
+ HasFlag<F, 'autoManaged'> extends true ? false
9
+ : HasFlag<F, 'generated'> extends true ? false
10
+ : HasFlag<F, 'primaryKey'> extends true
11
+ ? HasFlag<F, 'defaultRandom'> extends true ? false
12
+ : HasFlag<F, 'serial'> extends true ? false
13
+ : true
14
+ : true
15
+
16
+ type InsertOptional<F> =
17
+ HasFlag<F, 'nullable'> extends true ? true
18
+ : HasFlag<F, 'hasDefault'> extends true ? true
19
+ : false
20
+
21
+ type InsertRequiredKeys<TFields extends FieldRecord> = {
22
+ [K in keyof TFields]: InsertIncluded<TFields[K]> extends true
23
+ ? InsertOptional<TFields[K]> extends true ? never : K
24
+ : never
25
+ }[keyof TFields]
26
+
27
+ type InsertOptionalKeys<TFields extends FieldRecord> = {
28
+ [K in keyof TFields]: InsertIncluded<TFields[K]> extends true
29
+ ? InsertOptional<TFields[K]> extends true ? K : never
30
+ : never
31
+ }[keyof TFields]
32
+
33
+ /** Insert input shape: omits auto-managed and PK+(defaultRandom|serial); marks nullable/defaulted fields optional. */
34
+ export type InsertProjection<TFields extends FieldRecord> =
35
+ { -readonly [K in InsertRequiredKeys<TFields>]: InferFieldOutput<TFields[K]> }
36
+ & { -readonly [K in InsertOptionalKeys<TFields>]?: InferFieldOutput<TFields[K]> }
37
+
38
+ /** Select output shape: includes every field with its nullable status preserved through TOutput. */
39
+ export type SelectProjection<TFields extends FieldRecord> = {
40
+ -readonly [K in keyof TFields]: InferFieldOutput<TFields[K]>
41
+ }
42
+
43
+ type UpdateIncluded<F> =
44
+ HasFlag<F, 'primaryKey'> extends true ? false
45
+ : HasFlag<F, 'readOnly'> extends true ? false
46
+ : HasFlag<F, 'autoManaged'> extends true ? false
47
+ : HasFlag<F, 'generated'> extends true ? false
48
+ : true
49
+
50
+ type UpdateIncludedKeys<TFields extends FieldRecord> = {
51
+ [K in keyof TFields]: UpdateIncluded<TFields[K]> extends true ? K : never
52
+ }[keyof TFields]
53
+
54
+ /** Update input shape: omits PK/readOnly/auto-managed; all remaining fields are optional. */
55
+ export type UpdateProjection<TFields extends FieldRecord> = {
56
+ -readonly [K in UpdateIncludedKeys<TFields>]?: InferFieldOutput<TFields[K]>
57
+ }
@@ -0,0 +1,31 @@
1
+ // The trace-safe projection of a domain definition: the subset of its fields a
2
+ // trace step is allowed to capture into its input snapshot. A field is in the
3
+ // projection only if it was explicitly marked `.traceSafe()` — fail-closed, so
4
+ // a newly added field is never captured by accident.
5
+
6
+ import type { FieldDescriptor } from './types.js'
7
+
8
+ /** Field keys marked `.traceSafe()`, in declaration order. */
9
+ export function traceSafeKeys(fields: Record<string, FieldDescriptor>): readonly string[] {
10
+ return Object.keys(fields).filter(key => fields[key]!._meta.traceSafe)
11
+ }
12
+
13
+ /**
14
+ * Project a row down to its trace-safe fields.
15
+ *
16
+ * Keys not declared `.traceSafe()` are dropped. Keys that are trace-safe but
17
+ * absent from `row` are skipped rather than emitted as `undefined`, so a
18
+ * partial row projects to a partial result with no holes.
19
+ */
20
+ export function projectTraceSafe(
21
+ fields: Record<string, FieldDescriptor>,
22
+ row: Record<string, unknown>,
23
+ ): Record<string, unknown> {
24
+ const projected: Record<string, unknown> = {}
25
+ for (const key of traceSafeKeys(fields)) {
26
+ if (Object.prototype.hasOwnProperty.call(row, key)) {
27
+ projected[key] = row[key]
28
+ }
29
+ }
30
+ return projected
31
+ }
@@ -0,0 +1,108 @@
1
+ import { field } from './field.js'
2
+ import type { FieldDescriptor, TraitDefinition } from './types.js'
3
+
4
+ function autoManaged(f: FieldDescriptor): FieldDescriptor {
5
+ return { _meta: { ...f._meta, autoManaged: true } }
6
+ }
7
+
8
+ function autoManagedReadOnly(f: FieldDescriptor): FieldDescriptor {
9
+ return { _meta: { ...f._meta, autoManaged: true, readOnly: true } }
10
+ }
11
+
12
+ export const effectiveDatedTrait: TraitDefinition = {
13
+ name: 'effectiveDated',
14
+ fields: {
15
+ effectiveFrom: autoManaged(field.timestamp().default('now')),
16
+ effectiveTo: autoManaged(field.timestamp().nullable()),
17
+ version: autoManaged(field.integer().default(1)),
18
+ },
19
+ }
20
+
21
+ /**
22
+ * Versions that are only ever appended: each row is in force from its
23
+ * `effectiveFrom` until the next row's, so no column is ever set on an earlier
24
+ * version. Numbered per scope (the tenant field if the definition has one,
25
+ * otherwise the whole table). Write with `temporal.setVersion`, read with
26
+ * `temporal.versionAt`.
27
+ *
28
+ * `pipework migrate generate` adds a trigger refusing UPDATE and DELETE and a
29
+ * unique index on (scope, version) — only in the migration that has the
30
+ * CREATE TABLE for the table, like tenant RLS.
31
+ */
32
+ export const appendOnlyVersionsTrait: TraitDefinition = {
33
+ name: 'appendOnlyVersions',
34
+ fields: {
35
+ effectiveFrom: autoManaged(field.timestamp()),
36
+ version: autoManaged(field.integer()),
37
+ setBy: autoManaged(field.text().nullable()),
38
+ },
39
+ }
40
+
41
+ export const auditedTrait: TraitDefinition = {
42
+ name: 'audited',
43
+ fields: {
44
+ createdAt: autoManagedReadOnly(field.timestamp().default('now')),
45
+ updatedAt: autoManaged(field.timestamp().default('now')),
46
+ createdBy: autoManagedReadOnly(field.text().nullable()),
47
+ updatedBy: autoManaged(field.text().nullable()),
48
+ },
49
+ }
50
+
51
+ export const softDeletedTrait: TraitDefinition = {
52
+ name: 'softDeleted',
53
+ fields: {
54
+ deletedAt: autoManaged(field.timestamp().nullable()),
55
+ deletedBy: autoManaged(field.text().nullable()),
56
+ },
57
+ }
58
+
59
+ const TRAIT_CONFLICTS: ReadonlyArray<readonly [string, string, string]> = [
60
+ ['appendOnlyVersions', 'effectiveDated',
61
+ 'effectiveDated closes the previous version by UPDATE-ing effectiveTo and both define effectiveFrom and version, while appendOnlyVersions forbids every UPDATE.'],
62
+ ['appendOnlyVersions', 'softDeleted',
63
+ 'softDeleted marks a row deleted by UPDATE, while appendOnlyVersions forbids every UPDATE and DELETE.'],
64
+ ]
65
+
66
+ export function applyTrait(
67
+ definitionName: string,
68
+ existingFields: Record<string, FieldDescriptor>,
69
+ existingTraits: string[],
70
+ trait: TraitDefinition,
71
+ ): { fields: Record<string, FieldDescriptor>; traits: string[] } {
72
+ if (existingTraits.includes(trait.name)) {
73
+ throw new Error(
74
+ `[pipework] Trait "${trait.name}" is already applied to "${definitionName}".`,
75
+ )
76
+ }
77
+
78
+ for (const [a, b, why] of TRAIT_CONFLICTS) {
79
+ const other = trait.name === a ? b : trait.name === b ? a : undefined
80
+ if (other !== undefined && existingTraits.includes(other)) {
81
+ throw new Error(
82
+ `[pipework] Traits "${a}" and "${b}" cannot be combined on "${definitionName}".\n\n` +
83
+ ` ${why}\n` +
84
+ ` Use one of them.`,
85
+ )
86
+ }
87
+ }
88
+
89
+ for (const fieldName of Object.keys(trait.fields)) {
90
+ if (fieldName in existingFields) {
91
+ throw new Error(
92
+ `[pipework] Trait "${trait.name}" adds field "${fieldName}" which already exists in "${definitionName}".\n\n` +
93
+ ` Remove the field from your definition or choose a different name.\n` +
94
+ ` Trait "${trait.name}" provides: ${Object.keys(trait.fields).join(', ')}`,
95
+ )
96
+ }
97
+ }
98
+
99
+ return {
100
+ fields: { ...existingFields, ...trait.fields },
101
+ traits: [...existingTraits, trait.name],
102
+ }
103
+ }
104
+
105
+ /** The field versions are numbered within: the definition's tenant field, else undefined (the whole table). */
106
+ export function appendOnlyScopeField(definition: { readonly $fields: Record<string, FieldDescriptor> }): string | undefined {
107
+ return Object.keys(definition.$fields).find((key) => definition.$fields[key]!._meta.tenant === true)
108
+ }
@@ -0,0 +1,289 @@
1
+ import type { CollationName } from '../query/collation.js'
2
+ import type { z } from 'zod/v4'
3
+ import type { SQL, Table } from '../db/types.js'
4
+ import type { InferSelect, InferFieldOutput } from './brand.js'
5
+ import type { AnyPgColumn, PgTable } from '../query/pg-core/index.js'
6
+ import type { AcceptReadonly } from '../db/readonly.js'
7
+ import type { InsertProjection, SelectProjection, UpdateProjection } from './projections.js'
8
+
9
+ export type FieldKind =
10
+ | 'uuid'
11
+ | 'text'
12
+ | 'integer'
13
+ | 'serial'
14
+ | 'bigint'
15
+ | 'boolean'
16
+ | 'timestamp'
17
+ | 'date'
18
+ | 'enum'
19
+ | 'jsonb'
20
+ | 'decimal'
21
+ | 'money'
22
+ | 'pgLsn'
23
+ | 'custom'
24
+
25
+ export interface FieldMeta {
26
+ readonly kind: FieldKind
27
+ readonly nullable: boolean
28
+ readonly hasDefault: boolean
29
+ readonly defaultValue?: unknown
30
+ readonly primaryKey: boolean
31
+ readonly defaultRandom: boolean
32
+ readonly unique: boolean
33
+ readonly readOnly: boolean
34
+ readonly tenant: boolean
35
+ /** A stored generated column: Postgres computes it from `as`; writers never send it. */
36
+ readonly generated?: { readonly as: SQL | (() => SQL) }
37
+ /** The column's collation (text fields only): `COLLATE "name"` in generated migrations. */
38
+ readonly collation?: CollationName
39
+ /** A list of the field's type (`uuid[]`, `text[]`); `.min/.max/...` apply to each element. */
40
+ readonly array: boolean
41
+ /**
42
+ * Whether this field may be captured into a trace step's input snapshot.
43
+ * Fail-closed: fields are trace-unsafe unless `.traceSafe()` opts them in,
44
+ * so a new field is never captured by accident.
45
+ */
46
+ readonly traceSafe: boolean
47
+ readonly brand?: string
48
+ readonly reference?: {
49
+ /**
50
+ * Eager DefinedTable, or a thunk returning one. The thunk form lets a
51
+ * field reference its own table (self-reference) or a table defined later
52
+ * in the same import cycle (forward-reference) — the thunk is only
53
+ * resolved at table build / validation time, after the cycle has settled.
54
+ */
55
+ readonly definition: DomainDefinition | (() => DomainDefinition)
56
+ readonly field: string
57
+ /**
58
+ * When `false`, the brand and the validator's brand-reuse check inherit
59
+ * from the referenced field, but no FK constraint is emitted in the
60
+ * generated DDL. Used for cross-partition references where an enforced
61
+ * FK would prevent independent partition lifecycle (e.g. `audit_record`
62
+ * keeping `traceId` after the trace partition is dropped). Default true.
63
+ */
64
+ readonly enforced?: boolean
65
+ }
66
+ readonly min?: number
67
+ readonly max?: number
68
+ readonly autoManaged: boolean
69
+ readonly email: boolean
70
+ readonly url: boolean
71
+ readonly regex?: RegExp
72
+ readonly precision?: { readonly p: number; readonly s: number }
73
+ readonly enumValues?: readonly string[]
74
+ readonly jsonbShape?: z.ZodType
75
+ readonly serializedAs?: 'string'
76
+ readonly customColumn?: unknown
77
+ readonly customValidator?: z.ZodType
78
+ }
79
+
80
+ /**
81
+ * Phantom flags tracked at the type level so insert/select/update projection
82
+ * types can omit auto-managed columns, mark primary-key+defaulted columns as
83
+ * optional, etc. The runtime reads from `_meta`; `_flags` exists only for the
84
+ * type system.
85
+ */
86
+ export interface FieldFlags {
87
+ readonly nullable?: boolean
88
+ readonly hasDefault?: boolean
89
+ readonly primaryKey?: boolean
90
+ readonly defaultRandom?: boolean
91
+ readonly autoManaged?: boolean
92
+ readonly readOnly?: boolean
93
+ /** A stored generated column; omitted from insert and update shapes. */
94
+ readonly generated?: boolean
95
+ readonly serial?: boolean
96
+ /** The tenant column; a scoped write fills it from the flow, so inserts may omit it. */
97
+ readonly tenant?: boolean
98
+ }
99
+
100
+ /** A field in a domain definition — carries runtime metadata and phantom output/flag types. */
101
+ export interface FieldDescriptor<TOutput = unknown, TFlags extends FieldFlags = FieldFlags> {
102
+ readonly _meta: FieldMeta
103
+ readonly _output?: TOutput
104
+ readonly _flags?: TFlags
105
+ }
106
+
107
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- variance-safe constraint for exactOptionalPropertyTypes
108
+ type AnyFieldDescriptor = FieldDescriptor<any, any>
109
+
110
+ /** Constraint alias for generic bounds — safe under exactOptionalPropertyTypes. */
111
+ export type FieldRecord = Record<string, AnyFieldDescriptor>
112
+
113
+ /** Referential action for a foreign key on UPDATE / DELETE. */
114
+ export type ForeignKeyAction = 'cascade' | 'restrict' | 'no action' | 'set null' | 'set default'
115
+
116
+ /** A multi-column foreign key — the only way to reference a partitioned table's composite key. */
117
+ export interface CompositeForeignKey {
118
+ /** Local field keys forming the key, in order. */
119
+ readonly columns: readonly string[]
120
+ readonly references: {
121
+ /**
122
+ * Eager DefinedTable or a thunk returning one. The thunk form lets a
123
+ * composite FK target a table from the same import cycle (e.g.
124
+ * `trace_step` referencing `trace` where both modules import each other
125
+ * via field-level thunked references).
126
+ */
127
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- DefinedTable's PgTable base is invariant under exactOptionalPropertyTypes; the concrete field generics are irrelevant here
128
+ readonly definition: DefinedTable<any> | (() => DefinedTable<any>)
129
+ /** Foreign field keys, positionally matched to `columns`. */
130
+ readonly columns: readonly string[]
131
+ }
132
+ /**
133
+ * `{ setNull: [...] }` / `{ setDefault: [...] }` clear only the listed local
134
+ * fields (Postgres 15+), so a tenant column in the key stays in place.
135
+ */
136
+ readonly onDelete?: ForeignKeyAction | { readonly setNull: readonly string[] } | { readonly setDefault: readonly string[] }
137
+ readonly onUpdate?: ForeignKeyAction
138
+ }
139
+
140
+ /** Resolves a possibly-lazy definition reference to its concrete DefinedTable. */
141
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- structural pass-through; concrete generics flow at the call site
142
+ export function resolveDefinitionRef<T extends DefinedTable<any>>(ref: T | (() => T)): T {
143
+ return typeof ref === 'function' ? ref() : ref
144
+ }
145
+
146
+ /** Declares a table as RANGE-partitioned. The partition column must be part of the primary key. */
147
+ export interface PartitionSpec {
148
+ readonly strategy: 'range'
149
+ /** Field key of the partition column. */
150
+ readonly column: string
151
+ }
152
+
153
+ /** Options for pipe.define() — database affinity, virtual mode, custom indexes, keys, partitioning. */
154
+ export interface DefineOptions {
155
+ readonly database?: string
156
+ readonly virtual?: boolean
157
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Drizzle column/index types are deeply generic
158
+ readonly indexes?: (columns: Record<string, any>) => Record<string, any>
159
+ /** Composite primary key by field key. Mutually exclusive with field-level .primaryKey(). */
160
+ readonly primaryKey?: readonly string[]
161
+ /** Multi-column foreign keys. Single-column FKs use field-level .references(). */
162
+ readonly foreignKeys?: readonly CompositeForeignKey[]
163
+ /** Marks the table RANGE-partitioned. Partitions are managed outside the migration. */
164
+ readonly partitionBy?: PartitionSpec
165
+ /**
166
+ * The `@touches` entity name writes to this table are audited under. When set it
167
+ * must equal the touch's entity exactly; otherwise the touch matches by normalized
168
+ * name (case-insensitive, underscores ignored, table name optionally plural).
169
+ */
170
+ readonly auditEntity?: string
171
+ }
172
+
173
+ /** Column type projected from a FieldDescriptor — carries the right data type for Drizzle operators. */
174
+ export type FieldColumn<F extends FieldDescriptor, TName extends string = string> = AnyPgColumn<{
175
+ data: NonNullable<InferFieldOutput<F>>
176
+ notNull: null extends InferFieldOutput<F> ? false : true
177
+ // A tenant column counts as defaulted: the scoped `pipe()` DB fills it from the flow (auth/tenant/scoped-db.ts wrapInsertBuilder).
178
+ hasDefault: F extends FieldDescriptor<unknown, infer TF> ? (TF['hasDefault'] extends true ? true : TF['tenant'] extends true ? true : TF['generated'] extends true ? true : false) : false
179
+ tableName: TName
180
+ }> & {
181
+ // Plain columns are neither generated nor identity, so drizzle's insert model lists them as optional when nullable/defaulted;
182
+ // a `.generatedAlwaysAs()` field is generated, which drizzle's insert and update models omit.
183
+ readonly _: { readonly generated: F extends FieldDescriptor<unknown, infer TF> ? (TF['generated'] extends true ? { type: 'always' } : undefined) : undefined; readonly identity: undefined }
184
+ }
185
+
186
+ /** Maps field definitions to typed Drizzle column references. */
187
+ export type ProjectedColumns<TFields extends FieldRecord, TName extends string = string> = {
188
+ readonly [K in keyof TFields & string]: FieldColumn<TFields[K], TName>
189
+ }
190
+
191
+ /** Definition metadata and projection methods — the non-table surface of a DefinedTable. */
192
+ export interface DefinitionMethods<TFields extends FieldRecord = FieldRecord, TName extends string = string> {
193
+ readonly $name: TName
194
+ readonly $fields: TFields
195
+ readonly $options: DefineOptions
196
+ readonly $traits: readonly string[]
197
+ table(): Table
198
+ insertShape(): z.ZodType<InsertProjection<TFields>>
199
+ selectShape(): z.ZodType<SelectProjection<TFields>>
200
+ updateShape(): z.ZodType<UpdateProjection<TFields>>
201
+ factory(): FactoryBuilder
202
+ effectiveDated(): DefinedTable<TFields & EffectiveDatedFields, TName>
203
+ /** Versions never updated or deleted; see `temporal.setVersion` / `temporal.versionAt`. Not combinable with effectiveDated or softDeleted. */
204
+ appendOnlyVersions(): DefinedTable<TFields & AppendOnlyVersionsFields, TName>
205
+ audited(): DefinedTable<TFields & AuditedFields, TName>
206
+ softDeleted(): DefinedTable<TFields & SoftDeletedFields, TName>
207
+ readonly Select: InferSelect<TFields>
208
+ readonly Insert: AcceptReadonly<InsertProjection<TFields>>
209
+ readonly Update: AcceptReadonly<UpdateProjection<TFields>>
210
+ }
211
+
212
+ /**
213
+ * A domain definition that IS a Drizzle PgTable — columns are direct properties,
214
+ * usable in from(), eq(), orderBy(), joins, and partial selects.
215
+ *
216
+ * Created by pipe.define(). Metadata uses $ prefix ($name, $fields, $options, $traits)
217
+ * to avoid collisions with column properties.
218
+ *
219
+ * The PgTable base is parameterized with ProjectedColumns so that Drizzle's
220
+ * _['columns'] carries named, branded column types. Without this, select()
221
+ * and returning() fall back to index-signature records.
222
+ */
223
+ export type DefinedTable<TFields extends FieldRecord = FieldRecord, TName extends string = string> =
224
+ PgTable<{ name: TName; schema: string | undefined; columns: ProjectedColumns<TFields, TName>; dialect: 'pg' }>
225
+ & ProjectedColumns<TFields, TName>
226
+ & DefinitionMethods<TFields, TName>
227
+
228
+ /**
229
+ * A table or view owned by another service, declared with pipe.external. Readable
230
+ * with every query operator; not a DefinedTable, so the write builders reject it.
231
+ */
232
+ export type ExternalTable<TFields extends FieldRecord = FieldRecord, TName extends string = string> =
233
+ PgTable<{ name: TName; schema: string; columns: ProjectedColumns<TFields, TName>; dialect: 'pg' }>
234
+ & ProjectedColumns<TFields, TName>
235
+ & {
236
+ readonly $name: TName
237
+ readonly $fields: TFields
238
+ readonly $external: { readonly schema: string; readonly table: string }
239
+ selectShape(): z.ZodType<SelectProjection<TFields>>
240
+ readonly Select: InferSelect<TFields>
241
+ }
242
+
243
+ /**
244
+ * @deprecated Use DefinedTable instead. DomainDefinition is kept for backward compatibility
245
+ * with code that hasn't migrated to the direct-column-access API.
246
+ */
247
+ export type DomainDefinition<TFields extends FieldRecord = FieldRecord, TName extends string = string> =
248
+ DefinedTable<TFields, TName>
249
+
250
+ /** A named set of fields that can be applied to a domain definition via trait methods. */
251
+ export interface TraitDefinition {
252
+ readonly name: string
253
+ readonly fields: FieldRecord
254
+ }
255
+
256
+ export interface EffectiveDatedFields {
257
+ readonly effectiveFrom: FieldDescriptor<Date, { autoManaged: true; hasDefault: true }>
258
+ readonly effectiveTo: FieldDescriptor<Date | null, { autoManaged: true; nullable: true }>
259
+ readonly version: FieldDescriptor<number, { autoManaged: true; hasDefault: true }>
260
+ }
261
+
262
+ export interface AppendOnlyVersionsFields {
263
+ readonly effectiveFrom: FieldDescriptor<Date, { autoManaged: true }>
264
+ readonly version: FieldDescriptor<number, { autoManaged: true }>
265
+ readonly setBy: FieldDescriptor<string | null, { autoManaged: true; nullable: true }>
266
+ }
267
+
268
+ export interface AuditedFields {
269
+ readonly createdAt: FieldDescriptor<Date, { autoManaged: true; readOnly: true; hasDefault: true }>
270
+ readonly updatedAt: FieldDescriptor<Date, { autoManaged: true; hasDefault: true }>
271
+ readonly createdBy: FieldDescriptor<string | null, { autoManaged: true; readOnly: true; nullable: true }>
272
+ readonly updatedBy: FieldDescriptor<string | null, { autoManaged: true; nullable: true }>
273
+ }
274
+
275
+ export interface SoftDeletedFields {
276
+ readonly deletedAt: FieldDescriptor<Date | null, { autoManaged: true; nullable: true }>
277
+ readonly deletedBy: FieldDescriptor<string | null, { autoManaged: true; nullable: true }>
278
+ }
279
+
280
+ /** Test data factory — inserts rows and resolves FK dependencies automatically. */
281
+ export interface FactoryBuilder {
282
+ create(overrides?: Partial<Record<string, unknown>>, options?: FactoryOptions): Promise<Record<string, unknown>>
283
+ createMany(count: number, overrides?: Partial<Record<string, unknown>> | ((index: number) => Partial<Record<string, unknown>>), options?: FactoryOptions): Promise<Record<string, unknown>[]>
284
+ }
285
+
286
+ export interface FactoryOptions {
287
+ readonly db?: unknown
288
+ readonly tenantId?: string
289
+ }
@@ -0,0 +1,18 @@
1
+ import type { SQL, Column } from '../db/types.js'
2
+ import { cast } from '../db/expressions.js'
3
+ import { pathText } from './query.js'
4
+
5
+ /** Extracts a boolean from a JSONB column: `(column->>'key')::boolean`. Supports nested paths. */
6
+ export function boolean(column: Column | SQL, ...keys: string[]): SQL<boolean> {
7
+ return cast<boolean>(pathText(column, ...keys), 'boolean')
8
+ }
9
+
10
+ /** Extracts a number from a JSONB column: `(column->>'key')::numeric`. Supports nested paths. */
11
+ export function number(column: Column | SQL, ...keys: string[]): SQL<number> {
12
+ return cast<number>(pathText(column, ...keys), 'numeric')
13
+ }
14
+
15
+ /** Extracts an integer from a JSONB column: `(column->>'key')::integer`. Supports nested paths. */
16
+ export function integer(column: Column | SQL, ...keys: string[]): SQL<number> {
17
+ return cast<number>(pathText(column, ...keys), 'integer')
18
+ }
@@ -0,0 +1 @@
1
+ export { contains, containedBy, hasKey } from './query.js'
@@ -0,0 +1,30 @@
1
+ import { contains, merge, remove, containedBy, hasKey, text, json, path, pathText } from './query.js'
2
+ import { boolean, number, integer } from './composed.js'
3
+
4
+ /** JSONB query operators — typed helpers for PostgreSQL JSONB containment, key checks, and field extraction. */
5
+ export const jsonb = {
6
+ /** The @> containment operator — tests whether a JSONB column contains the given value. */
7
+ contains,
8
+ /** Merges an object into a JSONB column in `.set()`: `coalesce(col, '{}') || value`. Top-level keys overwrite; others are kept. */
9
+ merge,
10
+ /** Removes top-level keys from a JSONB column in `.set()`: `col - 'key'`. Composes as the column of `jsonb.merge`. */
11
+ remove,
12
+ /** The <@ contained-by operator — tests whether a JSONB column is contained by the given value. */
13
+ containedBy,
14
+ /** The ? key-existence operator — tests whether a JSONB column has the given top-level key. */
15
+ hasKey,
16
+ /** Extracts a text value: `column->>'key'`. Composable with pipe.filter operators. */
17
+ text,
18
+ /** Extracts a JSONB value: `column->'key'`. Returns JSONB — use with jsonb.contains() etc. */
19
+ json,
20
+ /** Extracts a nested JSONB value: `column->'a'->'b'`. Returns JSONB. */
21
+ path,
22
+ /** Extracts a nested text value: `column->'a'->>'b'`. Intermediate keys use `->`, final uses `->>`. */
23
+ pathText,
24
+ /** Extracts a boolean: `(column->>'key')::boolean`. Supports nested paths. */
25
+ boolean,
26
+ /** Extracts a number: `(column->>'key')::numeric`. Supports nested paths. */
27
+ number,
28
+ /** Extracts an integer: `(column->>'key')::integer`. Supports nested paths. */
29
+ integer,
30
+ }
@@ -0,0 +1,86 @@
1
+ import { sql } from '../db/sql.js'
2
+ import type { SQL, Column } from '../db/types.js'
3
+
4
+ export function contains(column: Column | SQL, value: unknown): SQL<boolean> {
5
+ return sql<boolean>`${column} @> ${JSON.stringify(value)}::jsonb`
6
+ }
7
+
8
+ export function containedBy(column: Column | SQL, value: unknown): SQL<boolean> {
9
+ return sql<boolean>`${column} <@ ${JSON.stringify(value)}::jsonb`
10
+ }
11
+
12
+ export function hasKey(column: Column | SQL, key: string): SQL<boolean> {
13
+ return sql<boolean>`${column} ? ${key}`
14
+ }
15
+
16
+ /** Extracts a text value from a JSONB column: `column->>'key'`. Composable with pipe.filter operators (eq, ilike, etc.). */
17
+ export function text(column: Column | SQL, key: string): SQL<string> {
18
+ return sql<string>`${column}->>${key}`
19
+ }
20
+
21
+ /** Extracts a JSONB value from a JSONB column: `column->'key'`. Returns JSONB, not text — use with jsonb.contains() etc. */
22
+ export function json(column: Column | SQL, key: string): SQL {
23
+ return sql`${column}->${key}`
24
+ }
25
+
26
+ /** Extracts a nested JSONB value: `column->'a'->'b'`. Returns JSONB. For text output, use `pathText`. */
27
+ export function path(column: Column | SQL, ...keys: string[]): SQL {
28
+ if (keys.length === 0) return sql`${column}`
29
+ let result: SQL = sql`${column}`
30
+ for (const key of keys) {
31
+ result = sql`${result}->${key}`
32
+ }
33
+ return result
34
+ }
35
+
36
+ /** Extracts a nested text value: `column->'a'->>'b'`. Intermediate keys use `->`, final key uses `->>`. */
37
+ export function pathText(column: Column | SQL, ...keys: string[]): SQL<string> {
38
+ if (keys.length === 0) return sql<string>`${column}`
39
+ if (keys.length === 1) return text(column, keys[0]!)
40
+ const intermediateKeys = keys.slice(0, -1)
41
+ const finalKey = keys[keys.length - 1]!
42
+ const intermediate = path(column, ...intermediateKeys)
43
+ return text(intermediate, finalKey)
44
+ }
45
+
46
+ /**
47
+ * Merges `value` into a JSONB column for `.set()`: `coalesce(col, '{}'::jsonb) || value::jsonb`. Top-level keys
48
+ * in `value` overwrite; others are kept; a null column becomes `value`. The merge happens in the statement,
49
+ * so concurrent updates that touch different keys both survive.
50
+ */
51
+ export function merge(column: Column | SQL, value: Record<string, unknown>): SQL {
52
+ const checked: unknown = value
53
+ if (typeof checked !== 'object' || checked === null || Array.isArray(checked)) {
54
+ throw new Error(
55
+ `[pipework] jsonb.merge() needs a plain object, got ${checked === null ? 'null' : Array.isArray(checked) ? 'an array' : typeof checked}.\n\n` +
56
+ ' Only an object can be merged into an object.\n' +
57
+ " Fix: pass an object: jsonb.merge(col, { key: 'value' }).\n",
58
+ )
59
+ }
60
+ return sql`coalesce(${column}, '{}'::jsonb) || ${JSON.stringify(value)}::jsonb`
61
+ }
62
+
63
+ /**
64
+ * Removes top-level keys from a JSONB column for `.set()`: `col - key`, or `col - array[k1, k2]::text[]` for
65
+ * several. Missing keys are ignored; a null column stays null. Composes as the column of `jsonb.merge`.
66
+ */
67
+ export function remove(column: Column | SQL, ...keys: string[]): SQL {
68
+ if (keys.length === 0) {
69
+ throw new Error(
70
+ '[pipework] jsonb.remove() needs at least one key.\n\n' +
71
+ ' With no keys there is nothing to remove.\n' +
72
+ " Fix: pass the keys to drop: jsonb.remove(col, 'key').\n",
73
+ )
74
+ }
75
+ for (const key of keys as unknown[]) {
76
+ if (typeof key !== 'string') {
77
+ throw new Error(
78
+ `[pipework] jsonb.remove() keys must be strings, got ${key === null ? 'null' : Array.isArray(key) ? 'an array' : typeof key}.\n\n` +
79
+ ' Each key names a top-level key of the object.\n' +
80
+ " Fix: pass strings as separate arguments: jsonb.remove(col, 'a', 'b').\n",
81
+ )
82
+ }
83
+ }
84
+ if (keys.length === 1) return sql`${column} - ${keys[0]!}::text`
85
+ return sql`${column} - array[${sql.join(keys.map(k => sql`${k}`), sql`, `)}]::text[]`
86
+ }
@@ -0,0 +1,47 @@
1
+ import type { DefinedTable } from '../domain/types.js'
2
+ import { camelToSnake } from '../domain/naming.js'
3
+ import { appendOnlyScopeField } from '../domain/traits.js'
4
+
5
+ // A fresh matcher per call: a shared `g` regex carries lastIndex between runs.
6
+ const createTableMatcher = (): RegExp => /CREATE\s+TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?(?:"([^"]+)"|([a-z_][a-z0-9_]*))/gi
7
+
8
+ const REFUSE_FUNCTION =
9
+ `CREATE OR REPLACE FUNCTION pipework_refuse_version_rewrite() RETURNS trigger LANGUAGE plpgsql AS $$ ` +
10
+ `BEGIN RAISE EXCEPTION '% is append-only: % is not allowed', TG_TABLE_NAME, TG_OP ` +
11
+ `USING ERRCODE = 'restrict_violation'; END $$`
12
+
13
+ /**
14
+ * For each table the migration creates whose definition has the
15
+ * `.appendOnlyVersions()` trait, appends the trigger that refuses UPDATE and
16
+ * DELETE and the unique index on (scope, version). Only a CREATE TABLE in this
17
+ * migration triggers it.
18
+ */
19
+ export function generateAppendOnlyStatements(
20
+ migrationSql: string,
21
+ definitions: ReadonlyMap<string, DefinedTable<any>>, // eslint-disable-line @typescript-eslint/no-explicit-any -- registry erases field generics
22
+ ): string {
23
+ const blocks: string[] = []
24
+ const seen = new Set<string>()
25
+ const createTableRe = createTableMatcher()
26
+ let match: RegExpExecArray | null
27
+ while ((match = createTableRe.exec(migrationSql)) !== null) {
28
+ const tableName = match[1] ?? match[2]
29
+ if (tableName === undefined || seen.has(tableName)) continue
30
+ const definition = definitions.get(tableName)
31
+ if (definition === undefined || !definition.$traits.includes('appendOnlyVersions')) continue
32
+ seen.add(tableName)
33
+
34
+ const scope = appendOnlyScopeField(definition)
35
+ const indexColumns = scope === undefined
36
+ ? `"version"`
37
+ : `"${camelToSnake(scope)}", "version"`
38
+ blocks.push(
39
+ `CREATE TRIGGER pipework_append_only BEFORE UPDATE OR DELETE ON "${tableName}" ` +
40
+ `FOR EACH ROW EXECUTE FUNCTION pipework_refuse_version_rewrite()`,
41
+ `CREATE UNIQUE INDEX "${tableName}_version_unique" ON "${tableName}" (${indexColumns})`,
42
+ )
43
+ }
44
+
45
+ if (blocks.length === 0) return migrationSql
46
+ return migrationSql + '\n\n' + [REFUSE_FUNCTION, ...blocks].join(';\n\n') + ';'
47
+ }