pipework 0.252.0 → 0.252.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (690) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/package.json +4 -3
  3. package/src/async/jobs/abortable-delay.ts +18 -0
  4. package/src/async/jobs/claims.ts +52 -0
  5. package/src/async/jobs/cron.ts +179 -0
  6. package/src/async/jobs/errors.ts +56 -0
  7. package/src/async/jobs/exclusion.ts +117 -0
  8. package/src/async/jobs/execute.ts +219 -0
  9. package/src/async/jobs/index.ts +10 -0
  10. package/src/async/jobs/listener.ts +135 -0
  11. package/src/async/jobs/namespace.ts +20 -0
  12. package/src/async/jobs/queue-door.ts +44 -0
  13. package/src/async/jobs/queue.ts +1012 -0
  14. package/src/async/jobs/run-script.ts +67 -0
  15. package/src/async/jobs/scheduler.ts +218 -0
  16. package/src/async/jobs/schedules-table.ts +37 -0
  17. package/src/async/jobs/serialize-error.ts +48 -0
  18. package/src/async/jobs/table.ts +191 -0
  19. package/src/async/pipeline/define.ts +53 -0
  20. package/src/async/pipeline/errors.ts +35 -0
  21. package/src/async/pipeline/execute.ts +53 -0
  22. package/src/async/pipeline/index.ts +13 -0
  23. package/src/async/pipeline/namespace.ts +12 -0
  24. package/src/async/pipeline/resume.ts +67 -0
  25. package/src/async/pipeline/run-steps.ts +98 -0
  26. package/src/async/pipeline/types.ts +57 -0
  27. package/src/async/state-machine/define.ts +64 -0
  28. package/src/async/state-machine/errors.ts +29 -0
  29. package/src/async/state-machine/index.ts +3 -0
  30. package/src/async/state-machine/namespace.ts +12 -0
  31. package/src/async/state-machine/transition.ts +90 -0
  32. package/src/async/state-machine/types.ts +37 -0
  33. package/src/async/steps/errors.ts +31 -0
  34. package/src/async/steps/graph.ts +1214 -0
  35. package/src/async/steps/hash.ts +40 -0
  36. package/src/async/steps/index.ts +7 -0
  37. package/src/async/steps/namespace.ts +7 -0
  38. package/src/async/steps/table.ts +97 -0
  39. package/src/async/steps/types.ts +318 -0
  40. package/src/audit/chain/index.ts +9 -0
  41. package/src/audit/chain/install.ts +113 -0
  42. package/src/audit/chain/retire.ts +52 -0
  43. package/src/audit/chain/verify.ts +257 -0
  44. package/src/audit/query/cursor.ts +37 -0
  45. package/src/audit/query/index.ts +9 -0
  46. package/src/audit/query/query.ts +223 -0
  47. package/src/audit/schema/audit-record-fields.ts +68 -0
  48. package/src/audit/schema/audit-record.ts +23 -0
  49. package/src/auth/auth/audit.ts +31 -0
  50. package/src/auth/auth/chain.ts +84 -0
  51. package/src/auth/auth/cookie.ts +136 -0
  52. package/src/auth/auth/email-links.ts +311 -0
  53. package/src/auth/auth/guard.ts +128 -0
  54. package/src/auth/auth/index.ts +18 -0
  55. package/src/auth/auth/multi-org.ts +337 -0
  56. package/src/auth/auth/namespace.ts +34 -0
  57. package/src/auth/auth/parse-expiry.ts +13 -0
  58. package/src/auth/auth/passwords.ts +110 -0
  59. package/src/auth/auth/schema.ts +110 -0
  60. package/src/auth/auth/sessions.ts +565 -0
  61. package/src/auth/auth/sign-in.ts +156 -0
  62. package/src/auth/auth/signing-keys.ts +69 -0
  63. package/src/auth/auth/types.ts +26 -0
  64. package/src/auth/auth/uuid.ts +10 -0
  65. package/src/auth/rbac/cache.ts +55 -0
  66. package/src/auth/rbac/check.ts +30 -0
  67. package/src/auth/rbac/create.ts +35 -0
  68. package/src/auth/rbac/index.ts +5 -0
  69. package/src/auth/rbac/middleware.ts +20 -0
  70. package/src/auth/rbac/namespace.ts +15 -0
  71. package/src/auth/rbac/resolve.ts +31 -0
  72. package/src/auth/rbac/schema.sql +31 -0
  73. package/src/auth/rbac/seed.ts +67 -0
  74. package/src/auth/rbac/tables.ts +32 -0
  75. package/src/auth/rbac/types.ts +65 -0
  76. package/src/auth/tenant/extract.ts +33 -0
  77. package/src/auth/tenant/in-tenant.ts +49 -0
  78. package/src/auth/tenant/index.ts +7 -0
  79. package/src/auth/tenant/namespace.ts +23 -0
  80. package/src/auth/tenant/propagate.ts +20 -0
  81. package/src/auth/tenant/rls.ts +120 -0
  82. package/src/auth/tenant/scope.ts +33 -0
  83. package/src/auth/tenant/scoped-db.ts +581 -0
  84. package/src/auth/tenant/types.ts +14 -0
  85. package/src/auth/tenant/validate.ts +27 -0
  86. package/src/cli/commands/add.ts +158 -0
  87. package/src/cli/commands/alerts.ts +98 -0
  88. package/src/cli/commands/check.ts +1418 -0
  89. package/src/cli/commands/codemod.ts +78 -0
  90. package/src/cli/commands/db.ts +356 -0
  91. package/src/cli/commands/dev.ts +101 -0
  92. package/src/cli/commands/generate.ts +75 -0
  93. package/src/cli/commands/init.ts +36 -0
  94. package/src/cli/commands/install.ts +28 -0
  95. package/src/cli/commands/lint.ts +62 -0
  96. package/src/cli/commands/migrate.ts +132 -0
  97. package/src/cli/commands/openapi.ts +63 -0
  98. package/src/cli/commands/remove.ts +72 -0
  99. package/src/cli/commands/run.ts +50 -0
  100. package/src/cli/commands/scope.ts +54 -0
  101. package/src/cli/commands/serve.ts +68 -0
  102. package/src/cli/commands/shard.ts +31 -0
  103. package/src/cli/commands/shared/config-pipework-version.ts +28 -0
  104. package/src/cli/commands/shared/find-config.ts +22 -0
  105. package/src/cli/commands/shared/load-config.ts +35 -0
  106. package/src/cli/commands/shared/pnpm-install.ts +95 -0
  107. package/src/cli/commands/shared/report-removed.ts +8 -0
  108. package/src/cli/commands/sync.ts +32 -0
  109. package/src/cli/commands/test.ts +307 -0
  110. package/src/cli/commands/upgrade.ts +48 -0
  111. package/src/cli/dev-bootstrap.ts +24 -0
  112. package/src/cli/errors.ts +10 -0
  113. package/src/cli/index.ts +303 -0
  114. package/src/cli/output.ts +17 -0
  115. package/src/cli/port.ts +15 -0
  116. package/src/cli/surface-kinds.ts +20 -0
  117. package/src/client/index.ts +4 -0
  118. package/src/client/poll.ts +130 -0
  119. package/src/client/report-fault.ts +119 -0
  120. package/src/core/config/app-pool.ts +19 -0
  121. package/src/core/config/aws-env.ts +29 -0
  122. package/src/core/config/base-dir.ts +24 -0
  123. package/src/core/config/discover.ts +64 -0
  124. package/src/core/config/env-resolve.ts +87 -0
  125. package/src/core/config/env-types.ts +116 -0
  126. package/src/core/config/env.ts +55 -0
  127. package/src/core/config/errors.ts +9 -0
  128. package/src/core/config/import-config.ts +40 -0
  129. package/src/core/config/index.ts +8 -0
  130. package/src/core/config/load.ts +417 -0
  131. package/src/core/config/namespace.ts +14 -0
  132. package/src/core/config/resolve.ts +195 -0
  133. package/src/core/config/schema.ts +488 -0
  134. package/src/core/config/statement-observer.ts +48 -0
  135. package/src/core/config/ts-inline-hooks.ts +75 -0
  136. package/src/core/config/ts-register.ts +4 -0
  137. package/src/core/config/ts-resolve-hooks.ts +101 -0
  138. package/src/core/errors/base.ts +49 -0
  139. package/src/core/errors/http.ts +146 -0
  140. package/src/core/errors/index.ts +2 -0
  141. package/src/core/errors/namespace.ts +31 -0
  142. package/src/core/lifecycle/drain.ts +33 -0
  143. package/src/core/lifecycle/health.ts +72 -0
  144. package/src/core/lifecycle/index.ts +4 -0
  145. package/src/core/lifecycle/namespace.ts +7 -0
  146. package/src/core/lifecycle/startup.ts +102 -0
  147. package/src/core/lifecycle/types.ts +36 -0
  148. package/src/core/logging/context.ts +60 -0
  149. package/src/core/logging/error-chain.ts +68 -0
  150. package/src/core/logging/index.ts +5 -0
  151. package/src/core/logging/logger.ts +65 -0
  152. package/src/core/logging/namespace.ts +16 -0
  153. package/src/core/logging/proxy.ts +22 -0
  154. package/src/core/logging/redact-match.ts +24 -0
  155. package/src/core/logging/types.ts +47 -0
  156. package/src/core/pipework.ts +566 -0
  157. package/src/core/runtime/broker.ts +69 -0
  158. package/src/core/surface/http.ts +133 -0
  159. package/src/core/surface/index.ts +11 -0
  160. package/src/core/surface/job-timeout.ts +40 -0
  161. package/src/core/surface/namespace.ts +13 -0
  162. package/src/core/surface/script.ts +33 -0
  163. package/src/core/surface/start.ts +142 -0
  164. package/src/core/surface/types.ts +225 -0
  165. package/src/core/surface/validate.ts +88 -0
  166. package/src/core/surface/worker.ts +528 -0
  167. package/src/core/validation/connectivity.ts +49 -0
  168. package/src/core/validation/domain.ts +99 -0
  169. package/src/core/validation/index.ts +5 -0
  170. package/src/core/validation/queue-tables.ts +312 -0
  171. package/src/core/validation/rls-role.ts +138 -0
  172. package/src/core/validation/startup.ts +51 -0
  173. package/src/core/validation/surfaces.ts +65 -0
  174. package/src/core/validation/types.ts +25 -0
  175. package/src/data/db/advisory-lock.ts +55 -0
  176. package/src/data/db/aggregates.ts +172 -0
  177. package/src/data/db/array-elements.ts +111 -0
  178. package/src/data/db/bulk-set.ts +53 -0
  179. package/src/data/db/col/index.ts +14 -0
  180. package/src/data/db/composed.ts +53 -0
  181. package/src/data/db/db.ts +276 -0
  182. package/src/data/db/excluded.ts +16 -0
  183. package/src/data/db/expressions.ts +473 -0
  184. package/src/data/db/filter.ts +90 -0
  185. package/src/data/db/function-source.ts +114 -0
  186. package/src/data/db/identifiers.ts +16 -0
  187. package/src/data/db/idx/index.ts +9 -0
  188. package/src/data/db/in-list.ts +49 -0
  189. package/src/data/db/index.ts +9 -0
  190. package/src/data/db/insert-split.ts +137 -0
  191. package/src/data/db/instrumentation.ts +82 -0
  192. package/src/data/db/maintenance.ts +28 -0
  193. package/src/data/db/namespace.ts +107 -0
  194. package/src/data/db/owner-labels.ts +9 -0
  195. package/src/data/db/pool.ts +187 -0
  196. package/src/data/db/query-label.ts +13 -0
  197. package/src/data/db/query-watch.ts +116 -0
  198. package/src/data/db/raw-client-warning.ts +37 -0
  199. package/src/data/db/readonly.ts +23 -0
  200. package/src/data/db/reservation.ts +151 -0
  201. package/src/data/db/schema-features.ts +14 -0
  202. package/src/data/db/serializable.ts +83 -0
  203. package/src/data/db/sql.ts +1 -0
  204. package/src/data/db/timeouts.ts +76 -0
  205. package/src/data/db/types.ts +18 -0
  206. package/src/data/db/unbound.ts +33 -0
  207. package/src/data/db/values.ts +29 -0
  208. package/src/data/domain/brand.ts +15 -0
  209. package/src/data/domain/build-factory.ts +193 -0
  210. package/src/data/domain/build-schema.ts +161 -0
  211. package/src/data/domain/build-table.ts +355 -0
  212. package/src/data/domain/define.ts +239 -0
  213. package/src/data/domain/external-registry.ts +34 -0
  214. package/src/data/domain/external.ts +77 -0
  215. package/src/data/domain/field.ts +279 -0
  216. package/src/data/domain/index.ts +13 -0
  217. package/src/data/domain/naming.ts +6 -0
  218. package/src/data/domain/projections.ts +57 -0
  219. package/src/data/domain/trace-safe-projection.ts +31 -0
  220. package/src/data/domain/traits.ts +108 -0
  221. package/src/data/domain/types.ts +289 -0
  222. package/src/data/jsonb/composed.ts +18 -0
  223. package/src/data/jsonb/index.ts +1 -0
  224. package/src/data/jsonb/namespace.ts +30 -0
  225. package/src/data/jsonb/query.ts +86 -0
  226. package/src/data/migrate/append-only-generate.ts +47 -0
  227. package/src/data/migrate/applied.ts +47 -0
  228. package/src/data/migrate/apply.ts +309 -0
  229. package/src/data/migrate/check.ts +119 -0
  230. package/src/data/migrate/diff.ts +357 -0
  231. package/src/data/migrate/door-generate.ts +130 -0
  232. package/src/data/migrate/framework-rls.ts +58 -0
  233. package/src/data/migrate/generate.ts +201 -0
  234. package/src/data/migrate/history.ts +215 -0
  235. package/src/data/migrate/index.ts +9 -0
  236. package/src/data/migrate/internal-definitions.ts +175 -0
  237. package/src/data/migrate/post-process.ts +73 -0
  238. package/src/data/migrate/pragma.ts +73 -0
  239. package/src/data/migrate/provision-framework.ts +147 -0
  240. package/src/data/migrate/rls-generate.ts +61 -0
  241. package/src/data/migrate/run.ts +124 -0
  242. package/src/data/migrate/safety.ts +167 -0
  243. package/src/data/migrate/snapshot.ts +368 -0
  244. package/src/data/migrate/sql-emit.ts +392 -0
  245. package/src/data/migrate/statements.ts +312 -0
  246. package/src/data/query/alias.ts +126 -0
  247. package/src/data/query/batch.ts +8 -0
  248. package/src/data/query/cache/core/cache.ts +78 -0
  249. package/src/data/query/cache/core/index.ts +1 -0
  250. package/src/data/query/cache/core/types.ts +29 -0
  251. package/src/data/query/cache/readme.md +230 -0
  252. package/src/data/query/casing.ts +76 -0
  253. package/src/data/query/collation.ts +11 -0
  254. package/src/data/query/column-builder.ts +370 -0
  255. package/src/data/query/column.ts +150 -0
  256. package/src/data/query/entity.ts +42 -0
  257. package/src/data/query/errors.ts +33 -0
  258. package/src/data/query/index.ts +14 -0
  259. package/src/data/query/logger.ts +47 -0
  260. package/src/data/query/operations.ts +45 -0
  261. package/src/data/query/pg-core/alias.ts +12 -0
  262. package/src/data/query/pg-core/checks.ts +32 -0
  263. package/src/data/query/pg-core/columns/all.ts +71 -0
  264. package/src/data/query/pg-core/columns/bigint.ts +110 -0
  265. package/src/data/query/pg-core/columns/bigserial.ts +128 -0
  266. package/src/data/query/pg-core/columns/boolean.ts +43 -0
  267. package/src/data/query/pg-core/columns/char.ts +85 -0
  268. package/src/data/query/pg-core/columns/cidr.ts +43 -0
  269. package/src/data/query/pg-core/columns/common.ts +366 -0
  270. package/src/data/query/pg-core/columns/custom.ts +232 -0
  271. package/src/data/query/pg-core/columns/date.common.ts +15 -0
  272. package/src/data/query/pg-core/columns/date.ts +112 -0
  273. package/src/data/query/pg-core/columns/double-precision.ts +55 -0
  274. package/src/data/query/pg-core/columns/enum.ts +201 -0
  275. package/src/data/query/pg-core/columns/index.ts +35 -0
  276. package/src/data/query/pg-core/columns/inet.ts +43 -0
  277. package/src/data/query/pg-core/columns/int.common.ts +57 -0
  278. package/src/data/query/pg-core/columns/integer.ts +53 -0
  279. package/src/data/query/pg-core/columns/interval.ts +83 -0
  280. package/src/data/query/pg-core/columns/json.ts +58 -0
  281. package/src/data/query/pg-core/columns/jsonb.ts +56 -0
  282. package/src/data/query/pg-core/columns/line.ts +117 -0
  283. package/src/data/query/pg-core/columns/macaddr.ts +43 -0
  284. package/src/data/query/pg-core/columns/macaddr8.ts +43 -0
  285. package/src/data/query/pg-core/columns/numeric.ts +227 -0
  286. package/src/data/query/pg-core/columns/point.ts +127 -0
  287. package/src/data/query/pg-core/columns/postgis_extension/geometry.ts +119 -0
  288. package/src/data/query/pg-core/columns/postgis_extension/utils.ts +47 -0
  289. package/src/data/query/pg-core/columns/real.ts +58 -0
  290. package/src/data/query/pg-core/columns/serial.ts +55 -0
  291. package/src/data/query/pg-core/columns/smallint.ts +53 -0
  292. package/src/data/query/pg-core/columns/smallserial.ts +60 -0
  293. package/src/data/query/pg-core/columns/text.ts +67 -0
  294. package/src/data/query/pg-core/columns/time.ts +72 -0
  295. package/src/data/query/pg-core/columns/timestamp.ts +163 -0
  296. package/src/data/query/pg-core/columns/uuid.ts +51 -0
  297. package/src/data/query/pg-core/columns/varchar.ts +89 -0
  298. package/src/data/query/pg-core/columns/vector_extension/bit.ts +68 -0
  299. package/src/data/query/pg-core/columns/vector_extension/halfvec.ts +80 -0
  300. package/src/data/query/pg-core/columns/vector_extension/sparsevec.ts +67 -0
  301. package/src/data/query/pg-core/columns/vector_extension/vector.ts +80 -0
  302. package/src/data/query/pg-core/db.ts +674 -0
  303. package/src/data/query/pg-core/dialect.ts +1404 -0
  304. package/src/data/query/pg-core/expressions.ts +25 -0
  305. package/src/data/query/pg-core/foreign-keys.ts +130 -0
  306. package/src/data/query/pg-core/index.ts +21 -0
  307. package/src/data/query/pg-core/indexes.ts +276 -0
  308. package/src/data/query/pg-core/policies.ts +56 -0
  309. package/src/data/query/pg-core/primary-keys.ts +62 -0
  310. package/src/data/query/pg-core/query-builders/count.ts +86 -0
  311. package/src/data/query/pg-core/query-builders/delete.ts +293 -0
  312. package/src/data/query/pg-core/query-builders/index.ts +7 -0
  313. package/src/data/query/pg-core/query-builders/insert.ts +452 -0
  314. package/src/data/query/pg-core/query-builders/query-builder.ts +123 -0
  315. package/src/data/query/pg-core/query-builders/query.ts +157 -0
  316. package/src/data/query/pg-core/query-builders/raw.ts +49 -0
  317. package/src/data/query/pg-core/query-builders/refresh-materialized-view.ts +108 -0
  318. package/src/data/query/pg-core/query-builders/select.ts +1346 -0
  319. package/src/data/query/pg-core/query-builders/select.types.ts +437 -0
  320. package/src/data/query/pg-core/query-builders/update.ts +626 -0
  321. package/src/data/query/pg-core/roles.ts +41 -0
  322. package/src/data/query/pg-core/schema.ts +72 -0
  323. package/src/data/query/pg-core/sequence.ts +41 -0
  324. package/src/data/query/pg-core/session.ts +292 -0
  325. package/src/data/query/pg-core/subquery.ts +46 -0
  326. package/src/data/query/pg-core/table.ts +252 -0
  327. package/src/data/query/pg-core/unique-constraint.ts +73 -0
  328. package/src/data/query/pg-core/utils/array.ts +95 -0
  329. package/src/data/query/pg-core/utils/index.ts +1 -0
  330. package/src/data/query/pg-core/utils.ts +104 -0
  331. package/src/data/query/pg-core/view-base.ts +14 -0
  332. package/src/data/query/pg-core/view-common.ts +1 -0
  333. package/src/data/query/pg-core/view.ts +435 -0
  334. package/src/data/query/pg-core/with-builder.ts +41 -0
  335. package/src/data/query/postgres-js/README.md +46 -0
  336. package/src/data/query/postgres-js/array-parser.ts +45 -0
  337. package/src/data/query/postgres-js/driver-error-boundary.ts +149 -0
  338. package/src/data/query/postgres-js/driver.ts +163 -0
  339. package/src/data/query/postgres-js/index.ts +2 -0
  340. package/src/data/query/postgres-js/session.ts +234 -0
  341. package/src/data/query/primary-key.ts +11 -0
  342. package/src/data/query/query-builders/query-builder.ts +19 -0
  343. package/src/data/query/query-builders/select.types.ts +175 -0
  344. package/src/data/query/query-promise.ts +35 -0
  345. package/src/data/query/relations.ts +725 -0
  346. package/src/data/query/runnable-query.ts +12 -0
  347. package/src/data/query/selection-proxy.ts +121 -0
  348. package/src/data/query/session.ts +8 -0
  349. package/src/data/query/sql/expressions/conditions.ts +741 -0
  350. package/src/data/query/sql/expressions/index.ts +2 -0
  351. package/src/data/query/sql/expressions/select.ts +45 -0
  352. package/src/data/query/sql/functions/aggregate.ts +129 -0
  353. package/src/data/query/sql/functions/index.ts +2 -0
  354. package/src/data/query/sql/functions/vector.ts +178 -0
  355. package/src/data/query/sql/index.ts +3 -0
  356. package/src/data/query/sql/sql.ts +794 -0
  357. package/src/data/query/subquery.ts +53 -0
  358. package/src/data/query/table.ts +208 -0
  359. package/src/data/query/table.utils.ts +2 -0
  360. package/src/data/query/tracing-utils.ts +3 -0
  361. package/src/data/query/tracing.ts +11 -0
  362. package/src/data/query/utils.ts +357 -0
  363. package/src/data/query/version.ts +4 -0
  364. package/src/data/query/view-common.ts +1 -0
  365. package/src/data/schema/branded.ts +13 -0
  366. package/src/data/schema/generated/coerce.ts +5 -0
  367. package/src/data/schema/generated/combinator.ts +6 -0
  368. package/src/data/schema/generated/encoding.ts +5 -0
  369. package/src/data/schema/generated/error.ts +6 -0
  370. package/src/data/schema/generated/format.ts +5 -0
  371. package/src/data/schema/generated/type-constructor.ts +10 -0
  372. package/src/data/schema/generated/type.ts +6 -0
  373. package/src/data/schema/index.ts +6 -0
  374. package/src/data/schema/json-schema.ts +33 -0
  375. package/src/data/schema/namespace.ts +55 -0
  376. package/src/data/schema/parse.ts +39 -0
  377. package/src/data/schema/types.ts +39 -0
  378. package/src/data/temporal/append-only.ts +134 -0
  379. package/src/data/temporal/close.ts +27 -0
  380. package/src/data/temporal/columns.ts +10 -0
  381. package/src/data/temporal/definition-queries.ts +70 -0
  382. package/src/data/temporal/errors.ts +10 -0
  383. package/src/data/temporal/get-current.ts +22 -0
  384. package/src/data/temporal/index.ts +9 -0
  385. package/src/data/temporal/namespace.ts +36 -0
  386. package/src/data/temporal/query.ts +25 -0
  387. package/src/data/temporal/resolve.ts +17 -0
  388. package/src/data/temporal/revise.ts +67 -0
  389. package/src/data/temporal/types.ts +22 -0
  390. package/src/data/valve/artifact.ts +33 -0
  391. package/src/data/valve/contract-diff.ts +21 -0
  392. package/src/data/valve/cross.ts +57 -0
  393. package/src/data/valve/crossing-ledger.ts +50 -0
  394. package/src/data/valve/decision.ts +44 -0
  395. package/src/data/valve/durable-crossing.ts +52 -0
  396. package/src/data/valve/execute-crossing.ts +77 -0
  397. package/src/data/valve/faucet.ts +56 -0
  398. package/src/data/valve/gate.ts +114 -0
  399. package/src/data/valve/graph.ts +77 -0
  400. package/src/data/valve/index.ts +53 -0
  401. package/src/data/valve/namespace.ts +57 -0
  402. package/src/data/valve/registry.ts +50 -0
  403. package/src/data/valve/schema/valve-crossing-fields.ts +23 -0
  404. package/src/data/valve/schema/valve-crossing.ts +9 -0
  405. package/src/data/valve/supply.ts +154 -0
  406. package/src/data/valve/types.ts +143 -0
  407. package/src/data/vector/columns.ts +27 -0
  408. package/src/data/vector/index.ts +4 -0
  409. package/src/data/vector/namespace.ts +33 -0
  410. package/src/data/vector/query.ts +85 -0
  411. package/src/data/vector/validate.ts +16 -0
  412. package/src/data/write/audit/actor.ts +15 -0
  413. package/src/data/write/audit/audit-columns.ts +18 -0
  414. package/src/data/write/audit/audit-select.ts +111 -0
  415. package/src/data/write/audit/bound-transaction.ts +14 -0
  416. package/src/data/write/audit/cte-compile.ts +32 -0
  417. package/src/data/write/audit/decode-returning.ts +47 -0
  418. package/src/data/write/audit/final-select.ts +43 -0
  419. package/src/data/write/audit/infra-audit.ts +36 -0
  420. package/src/data/write/audit/primary-key.ts +65 -0
  421. package/src/data/write/audit/projection.ts +41 -0
  422. package/src/data/write/audit/read-touches.ts +86 -0
  423. package/src/data/write/audit/touch-match.ts +78 -0
  424. package/src/data/write/audit/verb-delete.ts +62 -0
  425. package/src/data/write/audit/verb-insert.ts +36 -0
  426. package/src/data/write/audit/verb-select.ts +47 -0
  427. package/src/data/write/audit/verb-update.ts +60 -0
  428. package/src/data/write/audit/verb-upsert.ts +104 -0
  429. package/src/data/write/conflict.ts +43 -0
  430. package/src/data/write/delete.ts +38 -0
  431. package/src/data/write/execute.ts +243 -0
  432. package/src/data/write/index.ts +11 -0
  433. package/src/data/write/insert.ts +63 -0
  434. package/src/data/write/state.ts +43 -0
  435. package/src/data/write/update.ts +54 -0
  436. package/src/data/write/upsert.ts +58 -0
  437. package/src/data/write/values.ts +19 -0
  438. package/src/index.ts +230 -0
  439. package/src/infra/access-links/code.ts +70 -0
  440. package/src/infra/access-links/handle.ts +892 -0
  441. package/src/infra/access-links/index.ts +9 -0
  442. package/src/infra/access-links/namespace.ts +30 -0
  443. package/src/infra/access-links/person-check.ts +177 -0
  444. package/src/infra/access-links/schema.ts +71 -0
  445. package/src/infra/alerts/async-sources.ts +231 -0
  446. package/src/infra/alerts/audit.ts +36 -0
  447. package/src/infra/alerts/beat.ts +123 -0
  448. package/src/infra/alerts/client-faults.ts +111 -0
  449. package/src/infra/alerts/faults.ts +156 -0
  450. package/src/infra/alerts/handle.ts +778 -0
  451. package/src/infra/alerts/http.ts +192 -0
  452. package/src/infra/alerts/index.ts +12 -0
  453. package/src/infra/alerts/infra-sources.ts +180 -0
  454. package/src/infra/alerts/namespace.ts +81 -0
  455. package/src/infra/alerts/redact.ts +21 -0
  456. package/src/infra/alerts/request-faults.ts +41 -0
  457. package/src/infra/alerts/schema.ts +124 -0
  458. package/src/infra/alerts/sinks.ts +125 -0
  459. package/src/infra/alerts/source-options.ts +26 -0
  460. package/src/infra/alerts/sources.ts +503 -0
  461. package/src/infra/alerts/subsystem-sources.ts +137 -0
  462. package/src/infra/audit/emit.ts +87 -0
  463. package/src/infra/audit/index.ts +2 -0
  464. package/src/infra/audit/namespace.ts +22 -0
  465. package/src/infra/audit/schema.sql +35 -0
  466. package/src/infra/audit/types.ts +18 -0
  467. package/src/infra/aws/credentials.ts +147 -0
  468. package/src/infra/aws/sigv4.ts +140 -0
  469. package/src/infra/cache/create.ts +58 -0
  470. package/src/infra/cache/entry.ts +81 -0
  471. package/src/infra/cache/index.ts +4 -0
  472. package/src/infra/cache/namespace.ts +10 -0
  473. package/src/infra/cache/tenant.ts +93 -0
  474. package/src/infra/cache/types.ts +24 -0
  475. package/src/infra/crawl/audit.ts +35 -0
  476. package/src/infra/crawl/errors.ts +25 -0
  477. package/src/infra/crawl/handle.ts +693 -0
  478. package/src/infra/crawl/index.ts +9 -0
  479. package/src/infra/crawl/links.ts +160 -0
  480. package/src/infra/crawl/namespace.ts +36 -0
  481. package/src/infra/crawl/robots.ts +100 -0
  482. package/src/infra/crawl/schema.ts +89 -0
  483. package/src/infra/credit-line/handle.ts +883 -0
  484. package/src/infra/credit-line/index.ts +6 -0
  485. package/src/infra/credit-line/namespace.ts +7 -0
  486. package/src/infra/credit-line/schema.ts +96 -0
  487. package/src/infra/credits/handle.ts +706 -0
  488. package/src/infra/credits/index.ts +5 -0
  489. package/src/infra/credits/namespace.ts +7 -0
  490. package/src/infra/credits/schema.ts +83 -0
  491. package/src/infra/email/audit.ts +36 -0
  492. package/src/infra/email/inbound.ts +622 -0
  493. package/src/infra/email/index.ts +11 -0
  494. package/src/infra/email/mail.ts +517 -0
  495. package/src/infra/email/mime.ts +447 -0
  496. package/src/infra/email/namespace.ts +22 -0
  497. package/src/infra/email/providers.ts +111 -0
  498. package/src/infra/email/schema.ts +96 -0
  499. package/src/infra/email/sns.ts +200 -0
  500. package/src/infra/email/unsubscribe.ts +44 -0
  501. package/src/infra/fetch/address.ts +88 -0
  502. package/src/infra/fetch/connect-retry.ts +87 -0
  503. package/src/infra/fetch/handle.ts +329 -0
  504. package/src/infra/fetch/index.ts +7 -0
  505. package/src/infra/fetch/namespace.ts +70 -0
  506. package/src/infra/idempotency/index.ts +3 -0
  507. package/src/infra/idempotency/middleware.ts +87 -0
  508. package/src/infra/idempotency/store.ts +92 -0
  509. package/src/infra/idempotency/types.ts +30 -0
  510. package/src/infra/join-requests/handle.ts +312 -0
  511. package/src/infra/join-requests/index.ts +2 -0
  512. package/src/infra/join-requests/namespace.ts +7 -0
  513. package/src/infra/join-requests/schema.ts +33 -0
  514. package/src/infra/outbox/core.ts +165 -0
  515. package/src/infra/quota/errors.ts +32 -0
  516. package/src/infra/quota/handle.ts +215 -0
  517. package/src/infra/quota/index.ts +5 -0
  518. package/src/infra/quota/namespace.ts +7 -0
  519. package/src/infra/quota/schema.ts +15 -0
  520. package/src/infra/storage/backend.ts +34 -0
  521. package/src/infra/storage/handle.ts +513 -0
  522. package/src/infra/storage/index.ts +9 -0
  523. package/src/infra/storage/local.ts +104 -0
  524. package/src/infra/storage/namespace.ts +13 -0
  525. package/src/infra/storage/s3.ts +257 -0
  526. package/src/infra/storage/schema.ts +24 -0
  527. package/src/infra/support/handle.ts +854 -0
  528. package/src/infra/support/http.ts +229 -0
  529. package/src/infra/support/index.ts +7 -0
  530. package/src/infra/support/namespace.ts +12 -0
  531. package/src/infra/support/schema.ts +96 -0
  532. package/src/infra/webhook/inbound.ts +45 -0
  533. package/src/infra/webhook/index.ts +5 -0
  534. package/src/infra/webhook/namespace.ts +24 -0
  535. package/src/infra/webhook/outbound.ts +315 -0
  536. package/src/infra/webhook/sign.ts +32 -0
  537. package/src/infra/webhook/verify.ts +82 -0
  538. package/src/invariants/assert.ts +26 -0
  539. package/src/invariants/index.ts +1 -0
  540. package/src/lint/config.ts +56 -0
  541. package/src/lint/doctrine.ts +163 -0
  542. package/src/lint/include.ts +97 -0
  543. package/src/lint/index.ts +3 -0
  544. package/src/lint/preset.ts +10 -0
  545. package/src/lint/resolver.ts +218 -0
  546. package/src/lint/rules/no-reexports.ts +46 -0
  547. package/src/request/behavior/audited.ts +85 -0
  548. package/src/request/behavior/cached.ts +62 -0
  549. package/src/request/behavior/compose.ts +28 -0
  550. package/src/request/behavior/index.ts +5 -0
  551. package/src/request/behavior/namespace.ts +16 -0
  552. package/src/request/behavior/types.ts +26 -0
  553. package/src/request/behavior/versioned.ts +54 -0
  554. package/src/request/context/create.ts +103 -0
  555. package/src/request/context/guard.ts +49 -0
  556. package/src/request/context/index.ts +4 -0
  557. package/src/request/context/run-in-transaction.ts +200 -0
  558. package/src/request/context/store.ts +28 -0
  559. package/src/request/context/types.ts +50 -0
  560. package/src/request/di/builder.ts +535 -0
  561. package/src/request/di/index.ts +8 -0
  562. package/src/request/di/resolve.ts +271 -0
  563. package/src/request/di/types.ts +262 -0
  564. package/src/request/fitting/namespace.ts +7 -0
  565. package/src/request/fixture/batch.ts +71 -0
  566. package/src/request/fixture/builder.ts +26 -0
  567. package/src/request/fixture/errors.ts +10 -0
  568. package/src/request/fixture/index.ts +8 -0
  569. package/src/request/fixture/namespace.ts +23 -0
  570. package/src/request/fixture/paginate.ts +44 -0
  571. package/src/request/fixture/to-handlers.ts +260 -0
  572. package/src/request/fixture/types.ts +79 -0
  573. package/src/request/flow/act-as.ts +78 -0
  574. package/src/request/flow/namespace.ts +17 -0
  575. package/src/request/http/error-step.ts +42 -0
  576. package/src/request/http/file.ts +141 -0
  577. package/src/request/http/index.ts +4 -0
  578. package/src/request/http/middleware.ts +141 -0
  579. package/src/request/http/namespace.ts +5 -0
  580. package/src/request/http/rate-limit-classes.ts +170 -0
  581. package/src/request/http/response.ts +82 -0
  582. package/src/request/http/server.ts +916 -0
  583. package/src/request/http/sniff.ts +261 -0
  584. package/src/request/http/types.ts +250 -0
  585. package/src/request/http/upload.ts +284 -0
  586. package/src/request/internal/text.ts +6 -0
  587. package/src/request/openapi/generate.ts +205 -0
  588. package/src/request/openapi/index.ts +2 -0
  589. package/src/request/openapi/namespace.ts +7 -0
  590. package/src/test/auth.ts +13 -0
  591. package/src/test/auto-setup.ts +45 -0
  592. package/src/test/context.ts +91 -0
  593. package/src/test/crawl-fixture.ts +157 -0
  594. package/src/test/discover-fork.ts +17 -0
  595. package/src/test/fingerprint.ts +135 -0
  596. package/src/test/globals.d.ts +7 -0
  597. package/src/test/index.ts +19 -0
  598. package/src/test/namespace.ts +87 -0
  599. package/src/test/plugin.ts +189 -0
  600. package/src/test/progress.ts +185 -0
  601. package/src/test/reap-after-run.ts +28 -0
  602. package/src/test/reap.ts +286 -0
  603. package/src/test/reset.ts +253 -0
  604. package/src/test/savepoints.ts +172 -0
  605. package/src/test/setup.ts +1422 -0
  606. package/src/test/system-channel.ts +50 -0
  607. package/src/test/vitest-plugin.ts +142 -0
  608. package/src/test/vitest-workspace.ts +507 -0
  609. package/src/test/vitest.ts +333 -0
  610. package/src/test/watch.ts +105 -0
  611. package/src/trace/capture/bounded-serializer.ts +146 -0
  612. package/src/trace/cardinality/analyze.ts +173 -0
  613. package/src/trace/cardinality/branch-context.ts +214 -0
  614. package/src/trace/cardinality/check.ts +33 -0
  615. package/src/trace/cardinality/classify-edge.ts +64 -0
  616. package/src/trace/cardinality/structural-max.ts +107 -0
  617. package/src/trace/check/coverage-check.ts +188 -0
  618. package/src/trace/check/flow-check.ts +103 -0
  619. package/src/trace/check/flow-step-site.ts +234 -0
  620. package/src/trace/check/io-pattern.ts +116 -0
  621. package/src/trace/codemod/add-cardinality.ts +152 -0
  622. package/src/trace/codemod/run.ts +80 -0
  623. package/src/trace/entry/child-trace.ts +21 -0
  624. package/src/trace/entry/close-trace.ts +87 -0
  625. package/src/trace/entry/finalize-trace.ts +28 -0
  626. package/src/trace/entry/open-trace.ts +138 -0
  627. package/src/trace/entry/parent-trace.ts +20 -0
  628. package/src/trace/entry/parse-traceparent.ts +39 -0
  629. package/src/trace/entry/process-start.ts +38 -0
  630. package/src/trace/entry/trace-context.ts +26 -0
  631. package/src/trace/entry/tracing-enabled.ts +10 -0
  632. package/src/trace/external/record.ts +195 -0
  633. package/src/trace/external/report.ts +66 -0
  634. package/src/trace/namespace.ts +19 -0
  635. package/src/trace/partition/day.ts +47 -0
  636. package/src/trace/partition/index.ts +14 -0
  637. package/src/trace/partition/partition-name.ts +23 -0
  638. package/src/trace/partition/plan.ts +82 -0
  639. package/src/trace/partition/schedule.ts +26 -0
  640. package/src/trace/partition/startup-hook.ts +39 -0
  641. package/src/trace/partition/sync.ts +154 -0
  642. package/src/trace/partition/tables.ts +53 -0
  643. package/src/trace/read/tree.ts +83 -0
  644. package/src/trace/retain/copy-on-error.ts +76 -0
  645. package/src/trace/runtime/expire-stale-traces.ts +54 -0
  646. package/src/trace/runtime/flow-step.ts +147 -0
  647. package/src/trace/runtime/flush-steps.ts +31 -0
  648. package/src/trace/runtime/pending-writes.ts +54 -0
  649. package/src/trace/runtime/step-buffer.ts +298 -0
  650. package/src/trace/runtime/step-error.ts +21 -0
  651. package/src/trace/runtime/step-meta.ts +55 -0
  652. package/src/trace/runtime/thenable.ts +12 -0
  653. package/src/trace/runtime/truncate-trace.ts +62 -0
  654. package/src/trace/schema/trace-fields.ts +52 -0
  655. package/src/trace/schema/trace-retained.ts +22 -0
  656. package/src/trace/schema/trace-step-fields.ts +44 -0
  657. package/src/trace/schema/trace-step-retained.ts +25 -0
  658. package/src/trace/schema/trace-step.ts +18 -0
  659. package/src/trace/schema/trace.ts +12 -0
  660. package/src/trace/transformer/apply.ts +79 -0
  661. package/src/trace/transformer/build-meta-literal.ts +30 -0
  662. package/src/trace/transformer/find-flow-step-sites.ts +85 -0
  663. package/src/trace/transformer/inject-flow-step-import.ts +39 -0
  664. package/src/trace/transformer/transform-error.ts +14 -0
  665. package/src/trace/transformer/transform-flow-steps.ts +123 -0
  666. package/src/trace/transformer/wrap-function-declaration.ts +62 -0
  667. package/src/trace/transformer/wrap-variable-declaration.ts +67 -0
  668. package/src/workspace/affected.ts +110 -0
  669. package/src/workspace/changed-files.ts +163 -0
  670. package/src/workspace/check-deps.ts +213 -0
  671. package/src/workspace/checksum.ts +20 -0
  672. package/src/workspace/dependency-graph.ts +67 -0
  673. package/src/workspace/detect-pipework-copies.ts +93 -0
  674. package/src/workspace/discover.ts +206 -0
  675. package/src/workspace/generate.ts +401 -0
  676. package/src/workspace/index.ts +7 -0
  677. package/src/workspace/module-imports-pipework.ts +39 -0
  678. package/src/workspace/module-package-name.ts +12 -0
  679. package/src/workspace/scrub-git-env.ts +23 -0
  680. package/src/workspace/source-paths.ts +36 -0
  681. package/src/workspace/sync.ts +182 -0
  682. package/dist/REFERENCE.md +0 -1477
  683. package/dist/infra/access-links/rate-limit.d.ts +0 -10
  684. package/dist/infra/access-links/rate-limit.d.ts.map +0 -1
  685. package/dist/infra/access-links/rate-limit.js +0 -28
  686. package/dist/infra/access-links/rate-limit.js.map +0 -1
  687. package/dist/infra/email/sigv4.d.ts +0 -24
  688. package/dist/infra/email/sigv4.d.ts.map +0 -1
  689. package/dist/infra/email/sigv4.js +0 -55
  690. package/dist/infra/email/sigv4.js.map +0 -1
@@ -0,0 +1,315 @@
1
+ import { sql } from '../../data/db/sql.js'
2
+ import type { DB } from '../../data/db/index.js'
3
+ import { signPayload, type WebhookSignatureOptions } from './sign.js'
4
+ import { untrustedPost } from '../fetch/handle.js'
5
+ import type { FetchTarget } from '../fetch/handle.js'
6
+ import type { Socket } from 'node:net'
7
+ import { unscopedExecutor, scopedExecutor, requireUnscopedExecutor } from '../../auth/tenant/scoped-db.js'
8
+
9
+ export interface OutboundWebhookConfig {
10
+ table?: string
11
+ deliveryTable?: string
12
+ signing?: WebhookSignatureOptions
13
+ retry?: {
14
+ maxAttempts?: number
15
+ initialDelayMs?: number
16
+ maxDelayMs?: number
17
+ }
18
+ timeoutMs?: number
19
+ /**
20
+ * Where deliveries may go. `'public'` (default): only public addresses; a private, loopback or link-local address, a name
21
+ * resolving to one, or a redirect is refused and recorded on the delivery row's `error` column. `'any'`: for an app that
22
+ * sends webhooks to its own internal services; redirects are still not followed and the kept response is still capped.
23
+ * `'any'` throws at creation when `environment` is `production`.
24
+ */
25
+ addresses?: 'public' | 'any'
26
+ /** The app's environment (`config.environment`); `addresses: 'any'` is refused when it is `production`. */
27
+ environment?: string
28
+ /** For tests: replaces DNS and the socket of the `'public'` path, as `untrustedFetch.get` takes them. The address check still runs. */
29
+ network?: {
30
+ resolve: (hostname: string) => Promise<string[]>
31
+ connect: (target: FetchTarget) => Socket
32
+ }
33
+ }
34
+
35
+ export interface WebhookEndpoint {
36
+ id: string
37
+ tenantId: string
38
+ url: string
39
+ secret: string
40
+ events: string[]
41
+ active: boolean
42
+ }
43
+
44
+ export interface DeliveryAttempt {
45
+ id: string
46
+ endpointId: string
47
+ tenantId: string
48
+ eventType: string
49
+ payload: unknown
50
+ statusCode: number | null
51
+ responseBody: string | null
52
+ attempt: number
53
+ maxAttempts: number
54
+ nextRetryAt: Date | null
55
+ createdAt: Date
56
+ }
57
+
58
+ export interface OutboundWebhook {
59
+ ensureTables(db: DB): Promise<void>
60
+ send(db: DB, tenantId: string, eventType: string, payload: unknown): Promise<string[]>
61
+ deliver(db: DB, deliveryId: string): Promise<{ success: boolean; statusCode: number | null }>
62
+ processPending(db: DB): Promise<number>
63
+ registerEndpoint(db: DB, endpoint: Omit<WebhookEndpoint, 'id' | 'events'> & { events: readonly string[] }): Promise<string>
64
+ listEndpoints(db: DB, tenantId: string): Promise<WebhookEndpoint[]>
65
+ removeEndpoint(db: DB, tenantId: string, endpointId: string): Promise<boolean>
66
+ }
67
+
68
+ /** The delivery table `createOutboundWebhook` uses unless `deliveryTable` is set. */
69
+ export const DEFAULT_WEBHOOK_DELIVERY_TABLE = '__pipework_webhook_deliveries'
70
+
71
+ const DEFAULTS = {
72
+ table: '__pipework_webhook_endpoints',
73
+ deliveryTable: DEFAULT_WEBHOOK_DELIVERY_TABLE,
74
+ maxAttempts: 5,
75
+ initialDelayMs: 1000,
76
+ maxDelayMs: 3_600_000,
77
+ timeoutMs: 30_000,
78
+ } as const
79
+
80
+ /** The most of an endpoint's response kept on the delivery row. */
81
+ const RESPONSE_BODY_CAP = 64 * 1024
82
+
83
+ /** What a refused or failed attempt is recorded as on the delivery row's `error` column. */
84
+ type AttemptError = 'address' | 'scheme' | 'redirects' | 'network' | 'timeout' | 'too_large' | 'status' | 'rejected'
85
+
86
+ interface Attempt { statusCode: number | null; responseBody: string | null; success: boolean; error: AttemptError | null }
87
+
88
+ /** Plain fetch, for `addresses: 'any'`: no redirect followed, the kept body capped, one time limit. */
89
+ async function sendAnywhere(url: string, headers: Record<string, string>, body: string, timeoutMs: number): Promise<Attempt> {
90
+ const controller = new AbortController()
91
+ const timer = setTimeout(() => controller.abort(), timeoutMs)
92
+ try {
93
+ const response = await fetch(url, { method: 'POST', headers, body, signal: controller.signal, redirect: 'manual' })
94
+ const statusCode = response.status
95
+ if (statusCode >= 300 && statusCode < 400) {
96
+ await response.body?.cancel().catch(() => undefined)
97
+ return { statusCode, responseBody: null, success: false, error: 'redirects' }
98
+ }
99
+ const success = statusCode >= 200 && statusCode < 300
100
+ const chunks: Buffer[] = []
101
+ let total = 0
102
+ let over = false
103
+ const reader = response.body?.getReader()
104
+ try {
105
+ while (reader !== undefined) {
106
+ const { done, value } = await reader.read()
107
+ if (done) break
108
+ total += value.length
109
+ if (total > RESPONSE_BODY_CAP) { over = true; break }
110
+ chunks.push(Buffer.from(value))
111
+ }
112
+ } catch { /* a body that breaks is not kept; the status decides */ }
113
+ await reader?.cancel().catch(() => undefined)
114
+ return { statusCode, responseBody: over ? null : Buffer.concat(chunks).toString('utf8'), success, error: null }
115
+ } catch {
116
+ return { statusCode: null, responseBody: null, success: false, error: controller.signal.aborted ? 'timeout' : 'network' }
117
+ } finally {
118
+ clearTimeout(timer)
119
+ }
120
+ }
121
+
122
+ export function createOutboundWebhook(config: OutboundWebhookConfig = {}): OutboundWebhook {
123
+ const endpointTable = config.table ?? DEFAULTS.table
124
+ const deliveryTable = config.deliveryTable ?? DEFAULTS.deliveryTable
125
+ const maxAttempts = config.retry?.maxAttempts ?? DEFAULTS.maxAttempts
126
+ const initialDelayMs = config.retry?.initialDelayMs ?? DEFAULTS.initialDelayMs
127
+ const maxDelayMs = config.retry?.maxDelayMs ?? DEFAULTS.maxDelayMs
128
+ const timeoutMs = config.timeoutMs ?? DEFAULTS.timeoutMs
129
+ const signingOptions = config.signing ?? {}
130
+ const addresses = config.addresses ?? 'public'
131
+ if (addresses === 'any' && config.environment === 'production') {
132
+ throw new Error(
133
+ '[pipework] webhook.createOutbound: `addresses: \'any\'` is refused when the environment is production, because a tenant-registered URL could then reach the app\'s private network (cloud metadata, internal services). '
134
+ + 'Fix: remove `addresses` so deliveries go to public addresses only, or set it only outside production.\n',
135
+ )
136
+ }
137
+
138
+ return {
139
+ async ensureTables(handle) {
140
+ const db = unscopedExecutor(handle).db
141
+ await db.execute(sql.raw(`
142
+ CREATE TABLE IF NOT EXISTS ${endpointTable} (
143
+ id TEXT NOT NULL PRIMARY KEY DEFAULT gen_random_uuid()::text,
144
+ tenant_id TEXT NOT NULL,
145
+ url TEXT NOT NULL,
146
+ secret TEXT NOT NULL,
147
+ events TEXT[] NOT NULL DEFAULT '{}',
148
+ active BOOLEAN NOT NULL DEFAULT true,
149
+ created_at TIMESTAMPTZ NOT NULL DEFAULT now()
150
+ )
151
+ `))
152
+
153
+ await db.execute(sql.raw(`
154
+ CREATE TABLE IF NOT EXISTS ${deliveryTable} (
155
+ id TEXT NOT NULL PRIMARY KEY DEFAULT gen_random_uuid()::text,
156
+ endpoint_id TEXT NOT NULL,
157
+ tenant_id TEXT NOT NULL,
158
+ event_type TEXT NOT NULL,
159
+ payload JSONB NOT NULL,
160
+ status_code INTEGER,
161
+ response_body TEXT,
162
+ error TEXT,
163
+ attempt INTEGER NOT NULL DEFAULT 0,
164
+ max_attempts INTEGER NOT NULL,
165
+ next_retry_at TIMESTAMPTZ,
166
+ completed_at TIMESTAMPTZ,
167
+ created_at TIMESTAMPTZ NOT NULL DEFAULT now()
168
+ )
169
+ `))
170
+ await db.execute(sql.raw(`ALTER TABLE ${deliveryTable} ADD COLUMN IF NOT EXISTS error TEXT`))
171
+ },
172
+
173
+ async send(handle, tenantId, eventType, payload) {
174
+ const db = scopedExecutor(handle, tenantId, 'webhook.send()').db
175
+ const endpoints: unknown[] = await db.execute(
176
+ sql`SELECT id, url, secret FROM ${sql.raw(endpointTable)}
177
+ WHERE tenant_id = ${tenantId}
178
+ AND active = true
179
+ AND (events = '{}' OR ${eventType} = ANY(events))`,
180
+ )
181
+
182
+ const deliveryIds: string[] = []
183
+ for (const row of endpoints as { id: string }[]) {
184
+ const result: unknown[] = await db.execute(
185
+ sql`INSERT INTO ${sql.raw(deliveryTable)} (endpoint_id, tenant_id, event_type, payload, max_attempts, next_retry_at)
186
+ VALUES (${row.id}, ${tenantId}, ${eventType}, ${JSON.stringify(payload)}, ${maxAttempts}, now())
187
+ RETURNING id`,
188
+ )
189
+ const inserted = (result as { id: string }[])[0]
190
+ if (inserted !== undefined) deliveryIds.push(inserted.id)
191
+ }
192
+
193
+ return deliveryIds
194
+ },
195
+
196
+ async deliver(handle, deliveryId) {
197
+ const db = requireUnscopedExecutor(handle, 'webhook.deliver()', 'a delivery is looked up by id across tenants')
198
+ const rows: unknown[] = await db.execute(
199
+ sql`SELECT d.id, d.endpoint_id, d.event_type, d.payload, d.attempt, d.max_attempts,
200
+ e.url, e.secret
201
+ FROM ${sql.raw(deliveryTable)} d
202
+ JOIN ${sql.raw(endpointTable)} e ON e.id = d.endpoint_id
203
+ WHERE d.id = ${deliveryId} AND d.completed_at IS NULL`,
204
+ )
205
+
206
+ const delivery = (rows as Record<string, unknown>[])[0]
207
+ if (delivery === undefined) return { success: false, statusCode: null }
208
+
209
+ const attempt = (delivery['attempt'] as number) + 1
210
+ const signed = signPayload(delivery['payload'], delivery['secret'] as string, signingOptions)
211
+
212
+ const headers = {
213
+ 'Content-Type': 'application/json',
214
+ 'X-Webhook-Id': signed.messageId,
215
+ 'X-Webhook-Timestamp': signed.timestamp.toString(),
216
+ 'X-Webhook-Signature': signed.signature,
217
+ }
218
+ const url = delivery['url'] as string
219
+ let outcome: Attempt
220
+ if (addresses === 'any') {
221
+ outcome = await sendAnywhere(url, headers, signed.body, timeoutMs)
222
+ } else {
223
+ const sent = await untrustedPost(url, {
224
+ body: signed.body, headers, maxBytes: RESPONSE_BODY_CAP, timeoutMs, maxRedirects: 0,
225
+ ...(config.network ?? {}),
226
+ })
227
+ if (sent.ok) {
228
+ outcome = { statusCode: sent.status, responseBody: Buffer.from(sent.body).toString('utf8'), success: true, error: null }
229
+ } else if (sent.status !== undefined) {
230
+ // The answer arrived; the body is not kept (non-2xx, or a 2xx body over the cap).
231
+ outcome = { statusCode: sent.status, responseBody: null, success: sent.status >= 200 && sent.status < 300, error: null }
232
+ } else {
233
+ outcome = { statusCode: null, responseBody: null, success: false, error: sent.reason }
234
+ }
235
+ }
236
+ const { statusCode, responseBody, success, error } = outcome
237
+
238
+ if (success || attempt >= (delivery['max_attempts'] as number)) {
239
+ await db.execute(
240
+ sql`UPDATE ${sql.raw(deliveryTable)}
241
+ SET attempt = ${attempt}, status_code = ${statusCode},
242
+ response_body = ${responseBody}, error = ${error}, completed_at = now(), next_retry_at = NULL
243
+ WHERE id = ${deliveryId}`,
244
+ )
245
+ } else {
246
+ const delayMs = Math.min(initialDelayMs * Math.pow(2, attempt - 1), maxDelayMs)
247
+ const nextRetry = new Date(Date.now() + delayMs)
248
+ await db.execute(
249
+ sql`UPDATE ${sql.raw(deliveryTable)}
250
+ SET attempt = ${attempt}, status_code = ${statusCode},
251
+ response_body = ${responseBody}, error = ${error}, next_retry_at = ${nextRetry.toISOString()}
252
+ WHERE id = ${deliveryId}`,
253
+ )
254
+ }
255
+
256
+ return { success, statusCode }
257
+ },
258
+
259
+ async processPending(handle) {
260
+ const db = requireUnscopedExecutor(handle, 'webhook.processPending()', 'it delivers every tenant\'s pending webhooks')
261
+ const rows: unknown[] = await db.execute(
262
+ sql`SELECT id FROM ${sql.raw(deliveryTable)}
263
+ WHERE completed_at IS NULL AND next_retry_at <= now()
264
+ ORDER BY next_retry_at ASC
265
+ LIMIT 100`,
266
+ )
267
+
268
+ let processed = 0
269
+ for (const row of rows as { id: string }[]) {
270
+ await this.deliver(db, row.id)
271
+ processed++
272
+ }
273
+ return processed
274
+ },
275
+
276
+ async registerEndpoint(handle, endpoint) {
277
+ const db = scopedExecutor(handle, endpoint.tenantId, 'webhook.registerEndpoint()').db
278
+ const rows: unknown[] = await db.execute(
279
+ sql`INSERT INTO ${sql.raw(endpointTable)} (tenant_id, url, secret, events, active)
280
+ VALUES (${endpoint.tenantId}, ${endpoint.url}, ${endpoint.secret},
281
+ ${`{${endpoint.events.join(',')}}`}, ${endpoint.active})
282
+ RETURNING id`,
283
+ )
284
+ return ((rows as { id: string }[])[0])!.id
285
+ },
286
+
287
+ async listEndpoints(handle, tenantId) {
288
+ const db = scopedExecutor(handle, tenantId, 'webhook.listEndpoints()').db
289
+ const rows: unknown[] = await db.execute(
290
+ sql`SELECT id, tenant_id, url, secret, events, active
291
+ FROM ${sql.raw(endpointTable)}
292
+ WHERE tenant_id = ${tenantId}
293
+ ORDER BY created_at ASC`,
294
+ )
295
+ return (rows as Record<string, unknown>[]).map(row => ({
296
+ id: row['id'] as string,
297
+ tenantId: row['tenant_id'] as string,
298
+ url: row['url'] as string,
299
+ secret: row['secret'] as string,
300
+ events: row['events'] as string[],
301
+ active: row['active'] as boolean,
302
+ }))
303
+ },
304
+
305
+ async removeEndpoint(handle, tenantId, endpointId) {
306
+ const db = scopedExecutor(handle, tenantId, 'webhook.removeEndpoint()').db
307
+ const rows: unknown[] = await db.execute(
308
+ sql`DELETE FROM ${sql.raw(endpointTable)}
309
+ WHERE id = ${endpointId} AND tenant_id = ${tenantId}
310
+ RETURNING id`,
311
+ )
312
+ return (rows as unknown[]).length > 0
313
+ },
314
+ }
315
+ }
@@ -0,0 +1,32 @@
1
+ import { createHmac, randomUUID } from 'node:crypto'
2
+
3
+ export interface WebhookSignatureOptions {
4
+ algorithm?: string
5
+ encoding?: 'hex' | 'base64'
6
+ /** Message id to sign and return; default a fresh UUID. Pass a stable id so a retry is recognisable as the same message. */
7
+ messageId?: string
8
+ }
9
+
10
+ export interface SignedPayload {
11
+ body: string
12
+ signature: string
13
+ timestamp: number
14
+ messageId: string
15
+ }
16
+
17
+ export function signPayload(
18
+ payload: unknown,
19
+ secret: string,
20
+ options: WebhookSignatureOptions = {},
21
+ ): SignedPayload {
22
+ const algorithm = options.algorithm ?? 'sha256'
23
+ const encoding = options.encoding ?? 'hex'
24
+
25
+ const messageId = options.messageId ?? randomUUID()
26
+ const timestamp = Math.floor(Date.now() / 1000)
27
+ const body = typeof payload === 'string' ? payload : JSON.stringify(payload)
28
+ const toSign = `${messageId}.${timestamp}.${body}`
29
+ const signature = createHmac(algorithm, secret).update(toSign).digest(encoding)
30
+
31
+ return { body, signature, timestamp, messageId }
32
+ }
@@ -0,0 +1,82 @@
1
+ import { createHmac, timingSafeEqual } from 'node:crypto'
2
+
3
+ export interface SignatureVerifier {
4
+ verify(rawBody: Buffer, headers: Readonly<Record<string, string | undefined>>): boolean
5
+ }
6
+
7
+ type LazySecret = string | (() => string)
8
+
9
+ function resolveSecret(secret: LazySecret): string {
10
+ return typeof secret === 'function' ? secret() : secret
11
+ }
12
+
13
+ export function hmacVerifier(options: {
14
+ secret: LazySecret
15
+ algorithm?: string
16
+ header: string
17
+ prefix?: string
18
+ encoding?: 'hex' | 'base64'
19
+ computePayload?: (rawBody: Buffer, headers: Readonly<Record<string, string | undefined>>) => Buffer
20
+ }): SignatureVerifier {
21
+ const algorithm = options.algorithm ?? 'sha256'
22
+ const encoding = options.encoding ?? 'hex'
23
+ const prefix = options.prefix ?? ''
24
+
25
+ return {
26
+ verify(rawBody, headers) {
27
+ const headerValue = headers[options.header.toLowerCase()]
28
+ if (headerValue === undefined || headerValue === '') return false
29
+
30
+ const payload = options.computePayload !== undefined
31
+ ? options.computePayload(rawBody, headers)
32
+ : rawBody
33
+
34
+ const expected = prefix + createHmac(algorithm, resolveSecret(options.secret)).update(payload).digest(encoding)
35
+
36
+ try {
37
+ return timingSafeEqual(Buffer.from(headerValue), Buffer.from(expected))
38
+ } catch {
39
+ return false
40
+ }
41
+ },
42
+ }
43
+ }
44
+
45
+ export function stripeVerifier(secret: LazySecret): SignatureVerifier {
46
+ return {
47
+ verify(rawBody, headers) {
48
+ const sigHeader = headers['stripe-signature']
49
+ if (sigHeader === undefined) return false
50
+
51
+ const parts = new Map<string, string>()
52
+ for (const pair of sigHeader.split(',')) {
53
+ const eqIdx = pair.indexOf('=')
54
+ if (eqIdx === -1) continue
55
+ parts.set(pair.slice(0, eqIdx).trim(), pair.slice(eqIdx + 1).trim())
56
+ }
57
+
58
+ const timestamp = parts.get('t')
59
+ const v1Sig = parts.get('v1')
60
+ if (timestamp === undefined || v1Sig === undefined) return false
61
+
62
+ const signedPayload = Buffer.from(`${timestamp}.${rawBody.toString('utf-8')}`)
63
+ const expected = createHmac('sha256', resolveSecret(secret)).update(signedPayload).digest('hex')
64
+
65
+ try {
66
+ return timingSafeEqual(Buffer.from(v1Sig), Buffer.from(expected))
67
+ } catch {
68
+ return false
69
+ }
70
+ },
71
+ }
72
+ }
73
+
74
+ export function githubVerifier(secret: LazySecret): SignatureVerifier {
75
+ return hmacVerifier({
76
+ secret,
77
+ algorithm: 'sha256',
78
+ header: 'x-hub-signature-256',
79
+ prefix: 'sha256=',
80
+ encoding: 'hex',
81
+ })
82
+ }
@@ -0,0 +1,26 @@
1
+ export class InvariantViolation extends Error {
2
+ constructor(
3
+ public readonly invariant: string,
4
+ public readonly context: Record<string, unknown>,
5
+ ) {
6
+ const details = Object.entries(context)
7
+ .map(([k, v]) => ` ${k}: ${JSON.stringify(v)}`)
8
+ .join('\n')
9
+ super(
10
+ `[pipework] Invariant violation: ${invariant}\n\n` +
11
+ ` Context:\n${details}\n\n` +
12
+ ` This is a framework bug. Please report it.\n`
13
+ )
14
+ this.name = 'TrunkInvariantViolation'
15
+ }
16
+ }
17
+
18
+ export function invariant(
19
+ condition: boolean,
20
+ name: string,
21
+ context: Record<string, unknown> = {},
22
+ ): asserts condition {
23
+ if (!condition) {
24
+ throw new InvariantViolation(name, context)
25
+ }
26
+ }
@@ -0,0 +1 @@
1
+ export { invariant, InvariantViolation } from './assert.js'
@@ -0,0 +1,56 @@
1
+ import tseslint from '@typescript-eslint/eslint-plugin'
2
+ import tsparser from '@typescript-eslint/parser'
3
+ import { noReexports } from './rules/no-reexports.js'
4
+
5
+ export const rules = {
6
+ '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
7
+ '@typescript-eslint/no-explicit-any': 'error',
8
+ '@typescript-eslint/no-floating-promises': 'error',
9
+ '@typescript-eslint/no-misused-promises': 'error',
10
+ '@typescript-eslint/require-await': 'error',
11
+ '@typescript-eslint/no-unnecessary-condition': 'error',
12
+ '@typescript-eslint/strict-boolean-expressions': 'error',
13
+ 'pipework/no-reexports': 'error',
14
+ } as const
15
+
16
+ export interface LintOptions {
17
+ readonly tsconfigRootDir?: string
18
+ readonly include?: readonly string[]
19
+ readonly ignores?: readonly string[]
20
+ readonly extraRules?: Readonly<Record<string, unknown>>
21
+ readonly extraPlugins?: Readonly<Record<string, unknown>>
22
+ readonly extraConfigs?: readonly unknown[]
23
+ }
24
+
25
+ export function createLintConfig(config?: LintOptions): unknown[] {
26
+ const files = config?.include ?? ['src/**/*.ts', 'src/**/*.tsx']
27
+ const parserOptions: Record<string, unknown> = { projectService: true }
28
+ if (config?.tsconfigRootDir !== undefined) {
29
+ parserOptions['tsconfigRootDir'] = config.tsconfigRootDir
30
+ }
31
+
32
+ const appliedRules = { ...rules, ...config?.extraRules }
33
+ const appliedPlugins: Record<string, unknown> = {
34
+ '@typescript-eslint': tseslint,
35
+ 'pipework': { rules: { 'no-reexports': noReexports } },
36
+ ...config?.extraPlugins,
37
+ }
38
+
39
+ const blocks: unknown[] = [
40
+ {
41
+ files: [...files],
42
+ languageOptions: {
43
+ parser: tsparser,
44
+ parserOptions,
45
+ },
46
+ plugins: appliedPlugins,
47
+ rules: appliedRules,
48
+ },
49
+ ...(config?.extraConfigs ?? []),
50
+ {
51
+ ignores: ['dist/', 'node_modules/', ...(config?.ignores ?? [])],
52
+ },
53
+ ]
54
+
55
+ return blocks
56
+ }
@@ -0,0 +1,163 @@
1
+ import { readFileSync, existsSync, readdirSync } from 'node:fs'
2
+ import { join, relative } from 'node:path'
3
+
4
+ export interface DoctrineViolation {
5
+ readonly rule: string
6
+ readonly file: string
7
+ readonly line: number
8
+ readonly message: string
9
+ }
10
+
11
+ export interface DoctrineResult {
12
+ readonly violations: readonly DoctrineViolation[]
13
+ readonly scanned: number
14
+ }
15
+
16
+ export function validateDoctrine(cwd: string, sourceFiles: readonly string[]): DoctrineResult {
17
+ const violations: DoctrineViolation[] = []
18
+ let scanned = 0
19
+
20
+ for (const file of sourceFiles) {
21
+ const abs = join(cwd, file)
22
+ if (!existsSync(abs)) continue
23
+
24
+ const content = readFileSync(abs, 'utf-8')
25
+ const lines = content.split('\n')
26
+ scanned++
27
+
28
+ checkSchemaTableUsage(file, lines, violations)
29
+ checkRawPgTableImport(file, lines, violations)
30
+ checkUnbrandedPrimaryKeys(file, lines, violations)
31
+ checkHandWrittenTraitFields(file, lines, violations)
32
+ }
33
+
34
+ return { violations, scanned }
35
+ }
36
+
37
+ const SCHEMA_TABLE_PATTERN = /\bschema\.table\s*\(/
38
+ const PIPE_DEFINE_FILE_PATTERN = /\bpipe\.define\s*\(/
39
+
40
+ function checkSchemaTableUsage(
41
+ file: string,
42
+ lines: readonly string[],
43
+ violations: DoctrineViolation[],
44
+ ): void {
45
+ for (let i = 0; i < lines.length; i++) {
46
+ const line = lines[i]!
47
+ if (SCHEMA_TABLE_PATTERN.test(line)) {
48
+ violations.push({
49
+ rule: 'define/prefer-pipe-define',
50
+ file,
51
+ line: i + 1,
52
+ message: 'Use pipe.define() instead of schema.table(). pipe.define() provides branded types, shape validators, test factories, and tenant isolation.',
53
+ })
54
+ }
55
+ }
56
+ }
57
+
58
+ const RAW_PG_TABLE_IMPORT_LINE = /import\s+.*\bpgTable\b.*from\s+/
59
+
60
+ function checkRawPgTableImport(
61
+ file: string,
62
+ lines: readonly string[],
63
+ violations: DoctrineViolation[],
64
+ ): void {
65
+ for (let i = 0; i < lines.length; i++) {
66
+ const line = lines[i]!
67
+ if (RAW_PG_TABLE_IMPORT_LINE.test(line)) {
68
+ violations.push({
69
+ rule: 'define/no-raw-pgTable',
70
+ file,
71
+ line: i + 1,
72
+ message: 'Raw pgTable() import — use pipe.define() instead. pipe.define() returns a DefinedTable that IS a PgTable, with branded types that flow through select().from() and eq().',
73
+ })
74
+ }
75
+ }
76
+ }
77
+
78
+ const PRIMARY_KEY_PATTERN = /\.primaryKey\(\)/
79
+ const BRAND_PATTERN = /\.brand\(/
80
+ const PIPE_FIELD_UUID_PATTERN = /pipe\.field\.(uuid|serial)\(\)/
81
+
82
+ function checkUnbrandedPrimaryKeys(
83
+ file: string,
84
+ lines: readonly string[],
85
+ violations: DoctrineViolation[],
86
+ ): void {
87
+ if (!lines.some(l => PIPE_DEFINE_FILE_PATTERN.test(l))) return
88
+
89
+ for (let i = 0; i < lines.length; i++) {
90
+ const line = lines[i]!
91
+ if (PIPE_FIELD_UUID_PATTERN.test(line) && PRIMARY_KEY_PATTERN.test(line) && !BRAND_PATTERN.test(line)) {
92
+ violations.push({
93
+ rule: 'define/brand-primary-key',
94
+ file,
95
+ line: i + 1,
96
+ message: 'Primary key field missing .brand(). Add .brand(\'EntityNameId\') for nominal type safety.',
97
+ })
98
+ }
99
+ }
100
+ }
101
+
102
+ const TRAIT_FIELD_PATTERNS: ReadonlyArray<{ pattern: RegExp; trait: string; field: string }> = [
103
+ { pattern: /\bcreatedAt\s*:\s*pipe\.field\.timestamp\(\)/, trait: '.audited()', field: 'createdAt' },
104
+ { pattern: /\bupdatedAt\s*:\s*pipe\.field\.timestamp\(\)/, trait: '.audited()', field: 'updatedAt' },
105
+ { pattern: /\beffectiveFrom\s*:\s*pipe\.field\.timestamp\(\)/, trait: '.effectiveDated()', field: 'effectiveFrom' },
106
+ { pattern: /\beffectiveTo\s*:\s*pipe\.field\.timestamp\(\)/, trait: '.effectiveDated()', field: 'effectiveTo' },
107
+ { pattern: /\bdeletedAt\s*:\s*pipe\.field\.timestamp\(\)/, trait: '.softDeleted()', field: 'deletedAt' },
108
+ ]
109
+
110
+ function checkHandWrittenTraitFields(
111
+ file: string,
112
+ lines: readonly string[],
113
+ violations: DoctrineViolation[],
114
+ ): void {
115
+ if (!lines.some(l => PIPE_DEFINE_FILE_PATTERN.test(l))) return
116
+
117
+ for (let i = 0; i < lines.length; i++) {
118
+ const line = lines[i]!
119
+ for (const { pattern, trait, field } of TRAIT_FIELD_PATTERNS) {
120
+ if (pattern.test(line)) {
121
+ violations.push({
122
+ rule: 'define/use-traits',
123
+ file,
124
+ line: i + 1,
125
+ message: `Hand-written '${field}' field — use ${trait} trait instead. Traits are auto-managed and excluded from insert/update shapes.`,
126
+ })
127
+ }
128
+ }
129
+ }
130
+ }
131
+
132
+ export function findSourceFiles(cwd: string, modulePaths: readonly string[]): string[] {
133
+ const files: string[] = []
134
+
135
+ for (const modPath of modulePaths) {
136
+ const srcDir = join(cwd, modPath, 'src')
137
+ if (!existsSync(srcDir)) continue
138
+ collectTsFiles(srcDir, cwd, files)
139
+ }
140
+
141
+ return files.sort()
142
+ }
143
+
144
+ /** A `.ts` or `.tsx` source: not a test file, not a declaration file. */
145
+ export function isSourceFileName(name: string): boolean {
146
+ if (name.endsWith('.d.ts')) return false
147
+ if (name.endsWith('.test.ts') || name.endsWith('.test.tsx')) return false
148
+ return name.endsWith('.ts') || name.endsWith('.tsx')
149
+ }
150
+
151
+ function collectTsFiles(dir: string, cwd: string, out: string[]): void {
152
+ const entries = readdirSync(dir, { withFileTypes: true })
153
+
154
+ for (const entry of entries) {
155
+ const full = join(dir, entry.name)
156
+ if (entry.isDirectory()) {
157
+ if (entry.name === 'node_modules' || entry.name === 'dist' || entry.name === '__tests__') continue
158
+ collectTsFiles(full, cwd, out)
159
+ } else if (isSourceFileName(entry.name)) {
160
+ out.push(relative(cwd, full))
161
+ }
162
+ }
163
+ }