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,67 @@
1
+ import type { Manifold } from '../../core/pipework.js'
2
+ import { runInContext } from '../../request/context/store.js'
3
+ import { createJobContext } from '../../request/context/create.js'
4
+ import { executeInTransaction } from '../../request/context/run-in-transaction.js'
5
+ import { isTracingEnabled } from '../../trace/entry/tracing-enabled.js'
6
+ import { openTrace } from '../../trace/entry/open-trace.js'
7
+ import { parentTraceRef } from '../../trace/entry/parent-trace.js'
8
+ import { closeTrace } from '../../trace/entry/close-trace.js'
9
+ import { stageProcessStart } from '../../trace/entry/process-start.js'
10
+
11
+ const SCRIPT_TRANSACTION_TIMEOUT_MS = 5000
12
+
13
+ export interface RunScriptOptions {
14
+ tenant?: string | null
15
+ auth?: unknown
16
+ jobType?: string
17
+ /** Names the process in the trace: stages the opening `external:pipework.process.start` step. */
18
+ process?: { phase: 'preStart' | 'script'; surfaces: readonly string[] }
19
+ }
20
+
21
+ /**
22
+ * Runs an operator script in the flow a job gets: a trace (entry kind `job`) that
23
+ * is closed `closed` or `error`, and, with a tenant, the default database's
24
+ * transaction bound to the flow with the tenant session variables set. The
25
+ * transaction commits when `fn` returns and rolls back when it throws. Without a
26
+ * tenant nothing is bound, as for a tenantless job.
27
+ *
28
+ * The trace close is awaited, so the row is written before the caller closes the pool.
29
+ */
30
+ export async function runScript<T>(
31
+ instance: Manifold,
32
+ fn: () => T | Promise<T>,
33
+ options: RunScriptOptions = {},
34
+ ): Promise<T> {
35
+ const tenant = options.tenant ?? null
36
+ const auth = options.auth ?? null
37
+ const jobType = options.jobType ?? 'script'
38
+ const databases = tenant === null ? [] : [instance.config.defaultDatabaseName()]
39
+ const trace = isTracingEnabled(instance)
40
+ ? openTrace({
41
+ instance,
42
+ entryKind: 'job',
43
+ parent: parentTraceRef(),
44
+ tenant,
45
+ auth,
46
+ requestId: null,
47
+ sessionId: null,
48
+ })
49
+ : null
50
+ if (trace !== null && options.process !== undefined) {
51
+ stageProcessStart(trace, { ...options.process, release: instance.release })
52
+ }
53
+
54
+ const ctx = createJobContext({ instance, jobType, auth, tenant, ...(trace !== null ? { trace } : {}) })
55
+ const run = async (): Promise<T> => fn()
56
+
57
+ try {
58
+ const result = tenant === null
59
+ ? await runInContext(ctx, run)
60
+ : await executeInTransaction(ctx, databases, null, SCRIPT_TRANSACTION_TIMEOUT_MS, run)
61
+ await closeTrace(instance, trace, 'closed')
62
+ return result
63
+ } catch (err) {
64
+ await closeTrace(instance, trace, 'error')
65
+ throw err
66
+ }
67
+ }
@@ -0,0 +1,218 @@
1
+ import { sql } from '../../data/db/sql.js'
2
+ import type { DB } from '../../data/db/index.js'
3
+ import { parseCron, nextTick } from './cron.js'
4
+ import type { EnqueueOptions } from './queue.js'
5
+ import { describeError } from '../../core/logging/error-chain.js'
6
+
7
+ export interface Enqueuer {
8
+ enqueue(db: DB, opts: EnqueueOptions): Promise<string>
9
+ /** `enqueue` that also says whether it inserted a job (false: it returned the live job holding the `liveKey`). */
10
+ enqueueJob(db: DB, opts: EnqueueOptions): Promise<{ id: string; created: boolean }>
11
+ }
12
+
13
+ export interface ScheduleSpec {
14
+ readonly name: string
15
+ readonly cron: string
16
+ readonly timezone?: string
17
+ readonly payload?: unknown
18
+ }
19
+
20
+ export interface ScheduleInfo {
21
+ readonly name: string
22
+ readonly cronExpression: string
23
+ readonly timezone: string
24
+ readonly payload: unknown
25
+ readonly lastRunAt: Date | null
26
+ readonly nextRunAt: Date
27
+ }
28
+
29
+ export interface SchedulerOptions {
30
+ readonly checkIntervalMs?: number
31
+ /** Fire only these schedules (by name). Omitted: every schedule in the table. */
32
+ readonly names?: readonly string[]
33
+ /** Called when a tick throws; without it the error is swallowed so the next tick still runs. */
34
+ readonly onError?: (err: unknown) => void
35
+ /** Called after every check, whether it succeeded or threw. */
36
+ readonly onCheck?: (outcome: { readonly ok: boolean; readonly error?: string }) => void
37
+ }
38
+
39
+ export interface Scheduler {
40
+ /** Upserts a schedule. An unchanged cron and timezone keep `next_run_at` and `last_run_at`. */
41
+ schedule(db: DB, spec: ScheduleSpec): Promise<void>
42
+ /** Upserts every declared spec and removes schedules named in `owned` that are not declared. Returns the removed names. */
43
+ sync(db: DB, declared: readonly ScheduleSpec[], owned: readonly string[]): Promise<string[]>
44
+ /** Enqueues the schedule's job now, as a tick would, without moving `next_run_at`. Returns the job id. */
45
+ fire(db: DB, name: string): Promise<string>
46
+ /**
47
+ * Fires every due schedule once and moves its `next_run_at` to the next future
48
+ * tick (a schedule missed by many ticks fires once, not once per missed tick).
49
+ * Each schedule is handled in its own transaction under `FOR UPDATE SKIP LOCKED`,
50
+ * so across processes a due schedule enqueues exactly one job. Returns the count fired.
51
+ */
52
+ tick(db: DB, names?: readonly string[]): Promise<number>
53
+ unschedule(db: DB, name: string): Promise<boolean>
54
+ listSchedules(db: DB): Promise<ScheduleInfo[]>
55
+ start(db: DB, opts?: SchedulerOptions): void
56
+ stop(): void
57
+ }
58
+
59
+ export function createScheduler(enqueuer: Enqueuer, jobsTableQ: string): Scheduler {
60
+ let intervalHandle: ReturnType<typeof setInterval> | null = null
61
+ let running = false
62
+
63
+ async function schedule(db: DB, spec: ScheduleSpec): Promise<void> {
64
+ const parsed = parseCron(spec.cron)
65
+ const tz = spec.timezone ?? 'UTC'
66
+ const nextRunAt = nextTick(parsed, new Date(), tz)
67
+ const payloadJson = spec.payload !== undefined ? JSON.stringify(spec.payload) : null
68
+
69
+ await db.execute(sql`
70
+ INSERT INTO pipework_schedules (name, cron_expression, timezone, payload, next_run_at)
71
+ VALUES (${spec.name}, ${spec.cron}, ${tz}, ${payloadJson !== null ? sql`${payloadJson}::jsonb` : sql`NULL`}, ${nextRunAt.toISOString()}::timestamptz)
72
+ ON CONFLICT (name) DO UPDATE SET
73
+ next_run_at = CASE
74
+ WHEN pipework_schedules.cron_expression IS DISTINCT FROM EXCLUDED.cron_expression
75
+ OR pipework_schedules.timezone IS DISTINCT FROM EXCLUDED.timezone
76
+ THEN EXCLUDED.next_run_at
77
+ ELSE pipework_schedules.next_run_at
78
+ END,
79
+ cron_expression = EXCLUDED.cron_expression,
80
+ timezone = EXCLUDED.timezone,
81
+ payload = EXCLUDED.payload,
82
+ updated_at = now()
83
+ `)
84
+ }
85
+
86
+ async function unschedule(db: DB, name: string): Promise<boolean> {
87
+ const rows = await db.execute(
88
+ sql`DELETE FROM pipework_schedules WHERE name = ${name} RETURNING name`,
89
+ )
90
+ return rows.length > 0
91
+ }
92
+
93
+ async function listSchedules(db: DB): Promise<ScheduleInfo[]> {
94
+ const rows = await db.execute(
95
+ sql`SELECT name, cron_expression, timezone, payload, last_run_at, next_run_at
96
+ FROM pipework_schedules ORDER BY name`,
97
+ )
98
+ return rows.map(row => ({
99
+ name: row['name'] as string,
100
+ cronExpression: row['cron_expression'] as string,
101
+ timezone: row['timezone'] as string,
102
+ payload: row['payload'] ?? null,
103
+ lastRunAt: row['last_run_at'] !== null && row['last_run_at'] !== undefined ? new Date(row['last_run_at'] as string) : null,
104
+ nextRunAt: new Date(row['next_run_at'] as string),
105
+ }))
106
+ }
107
+
108
+ async function sync(db: DB, declared: readonly ScheduleSpec[], owned: readonly string[]): Promise<string[]> {
109
+ for (const spec of declared) await schedule(db, spec)
110
+ const keep = new Set(declared.map(spec => spec.name))
111
+ const removed: string[] = []
112
+ for (const info of await listSchedules(db)) {
113
+ if (owned.includes(info.name) && !keep.has(info.name)) {
114
+ if (await unschedule(db, info.name)) removed.push(info.name)
115
+ }
116
+ }
117
+ return removed
118
+ }
119
+
120
+ async function fire(db: DB, name: string): Promise<string> {
121
+ const rows = await db.execute(sql`SELECT payload FROM pipework_schedules WHERE name = ${name}`)
122
+ if (rows.length === 0) {
123
+ throw new Error(
124
+ `[pipework] No schedule named "${name}".\n\n` +
125
+ ' fireSchedule() enqueues the job of a registered schedule; register it with\n' +
126
+ ' queue.schedule() or declare it on the worker surface.\n',
127
+ )
128
+ }
129
+ return enqueuer.enqueue(db, { type: name, payload: rows[0]!['payload'] ?? {}, continueFlow: false })
130
+ }
131
+
132
+ // One due schedule, locked for the transaction: a second process skips the
133
+ // locked row, and once the first commits next_run_at is in the future.
134
+ async function fireOneDue(db: DB, names: readonly string[] | undefined): Promise<'none' | 'skipped' | 'fired'> {
135
+ return db.transaction(async (tx: DB) => {
136
+ const filter = names !== undefined
137
+ ? sql`AND name ${sql.inList(names)}`
138
+ : sql``
139
+ const due = await tx.execute(sql`
140
+ SELECT name, cron_expression, timezone, payload
141
+ FROM pipework_schedules
142
+ WHERE next_run_at <= now() ${filter}
143
+ ORDER BY next_run_at
144
+ LIMIT 1
145
+ FOR UPDATE SKIP LOCKED
146
+ `)
147
+ if (due.length === 0) return 'none'
148
+
149
+ const row = due[0]!
150
+ const name = row['name'] as string
151
+ const next = nextTick(parseCron(row['cron_expression'] as string), new Date(), row['timezone'] as string)
152
+
153
+ // A job of this type queued by other means (no key) also holds the schedule back. The key then
154
+ // makes concurrent ticks of this schedule atomic where the check alone is not.
155
+ const queued = await tx.execute(sql`
156
+ SELECT 1 FROM ${sql.raw(jobsTableQ)}
157
+ WHERE job_type = ${name} AND status IN ('pending', 'claimed')
158
+ LIMIT 1
159
+ `)
160
+ const created = queued.length === 0 && (await enqueuer.enqueueJob(tx, {
161
+ type: name, payload: row['payload'] ?? {}, continueFlow: false, liveKey: `schedule:${name}`,
162
+ })).created
163
+ if (!created) {
164
+ await tx.execute(sql`
165
+ UPDATE pipework_schedules SET next_run_at = ${next.toISOString()}::timestamptz, updated_at = now()
166
+ WHERE name = ${name}
167
+ `)
168
+ return 'skipped'
169
+ }
170
+
171
+ await tx.execute(sql`
172
+ UPDATE pipework_schedules SET
173
+ last_run_at = now(),
174
+ next_run_at = ${next.toISOString()}::timestamptz,
175
+ updated_at = now()
176
+ WHERE name = ${name}
177
+ `)
178
+ return 'fired'
179
+ })
180
+ }
181
+
182
+ async function tick(db: DB, names?: readonly string[]): Promise<number> {
183
+ let fired = 0
184
+ // Every pass moves one schedule's next_run_at into the future, so the loop ends.
185
+ for (;;) {
186
+ const outcome = await fireOneDue(db, names)
187
+ if (outcome === 'none') return fired
188
+ if (outcome === 'fired') fired++
189
+ }
190
+ }
191
+
192
+ function start(db: DB, opts?: SchedulerOptions): void {
193
+ if (running) return
194
+ running = true
195
+ const intervalMs = opts?.checkIntervalMs ?? 60_000
196
+ let inTick = false
197
+ intervalHandle = setInterval(() => {
198
+ if (inTick) return
199
+ inTick = true
200
+ tick(db, opts?.names).then(
201
+ () => { opts?.onCheck?.({ ok: true }) },
202
+ (err: unknown) => {
203
+ opts?.onError?.(err)
204
+ opts?.onCheck?.({ ok: false, error: describeError(err).message })
205
+ },
206
+ ).finally(() => { inTick = false })
207
+ }, intervalMs)
208
+ }
209
+
210
+ function stop(): void {
211
+ if (!running || intervalHandle === null) return
212
+ clearInterval(intervalHandle)
213
+ intervalHandle = null
214
+ running = false
215
+ }
216
+
217
+ return { schedule, sync, fire, tick, unschedule, listSchedules, start, stop }
218
+ }
@@ -0,0 +1,37 @@
1
+ // The schedules table as a pipe.define() definition. The table used to be a
2
+ // hand-written schedules.schema.sql that `pipework generate` could not emit.
3
+ // A worker surface that declares schedules registers the definition at
4
+ // construction; the migration generator merges it through internalDefinitions.
5
+ // Its CREATE statements are IF NOT EXISTS (SCHEDULE_TABLE_NAMES in generate.ts),
6
+ // so apps that applied the old .sql by hand migrate cleanly.
7
+
8
+ import { define } from '../../data/domain/index.js'
9
+ import { field } from '../../data/domain/field.js'
10
+ import type { DefinedTable, FieldDescriptor } from '../../data/domain/types.js'
11
+ import { schema } from '../../data/schema/namespace.js'
12
+
13
+ export const SCHEDULE_TABLE_NAMES: ReadonlySet<string> = new Set(['pipework_schedules'])
14
+
15
+ const scheduleFields: Record<string, FieldDescriptor> = {
16
+ name: field.text().primaryKey(),
17
+ cronExpression: field.text(),
18
+ timezone: field.text().default('UTC'),
19
+ payload: field.jsonb(schema.check.json()).nullable(),
20
+ lastRunAt: field.timestamp().nullable(),
21
+ nextRunAt: field.timestamp(),
22
+ createdAt: field.timestamp().default('now'),
23
+ updatedAt: field.timestamp().default('now'),
24
+ }
25
+
26
+ export const schedules: DefinedTable = define('pipework_schedules', scheduleFields)
27
+
28
+ let registered = false
29
+
30
+ /** Marks the schedules table as in use, so `pipework generate` emits it. */
31
+ export function registerSchedulesDefinition(): void {
32
+ registered = true
33
+ }
34
+
35
+ export function schedulesDefinitions(): ReadonlyMap<string, DefinedTable> {
36
+ return registered ? new Map([['pipework_schedules', schedules]]) : new Map()
37
+ }
@@ -0,0 +1,48 @@
1
+ /** Serialize an arbitrary thrown value for durable storage in a job's `error_payload`.
2
+ *
3
+ * A bare `{ message, stack }` capture is a diagnostics trap: wrapper errors hide the
4
+ * root cause. Drizzle throws `DrizzleQueryError` whose `.message` is only the failed
5
+ * SQL text; the real `PostgresError` — carrying `code`/`detail`/`constraint`/`table`
6
+ * — lives on `.cause`. This walks the full `cause` chain and copies each error's
7
+ * own-enumerable properties (where both Drizzle's `query` and Postgres' fields live),
8
+ * so a dead-lettered job records *why* it failed, not just *which query* failed.
9
+ *
10
+ * Parameters are never stored (`params`, `parameters`, `args`, `types`); `query` is cut
11
+ * to 2,000 characters and any other string to 4,000, ending `…[truncated N chars]`.
12
+ * Non-Error values pass through unchanged. Circular `cause` references are broken. */
13
+ export function serializeError(error: unknown): unknown {
14
+ return serialize(error, new Set<Error>())
15
+ }
16
+
17
+ const DROPPED_KEYS = new Set(['params', 'parameters', 'args', 'types'])
18
+ const MAX_QUERY_CHARS = 2_000
19
+ const MAX_STRING_CHARS = 4_000
20
+
21
+ function cut(text: string, max: number): string {
22
+ return text.length <= max ? text : `${text.slice(0, max)}…[truncated ${String(text.length - max)} chars]`
23
+ }
24
+
25
+ function serialize(value: unknown, seen: Set<Error>): unknown {
26
+ if (!(value instanceof Error)) return value
27
+ if (seen.has(value)) return { message: '[circular error reference]' }
28
+ seen.add(value)
29
+
30
+ // Drizzle's message (and so its stack) is `Failed query: <sql>\nparams: <values>`: keep the text before the values.
31
+ const paramsAt = Object.hasOwn(value, 'params') ? value.message.indexOf('\nparams: ') : -1
32
+ const message = paramsAt === -1 ? value.message : value.message.slice(0, paramsAt)
33
+ const stack = typeof value.stack === 'string' && paramsAt !== -1 ? value.stack.replace(value.message, message) : value.stack
34
+ const out: Record<string, unknown> = {
35
+ name: value.name,
36
+ message: cut(message, MAX_STRING_CHARS),
37
+ stack: typeof stack === 'string' ? cut(stack, MAX_STRING_CHARS) : stack,
38
+ }
39
+ for (const key of Object.keys(value)) {
40
+ if (key === 'name' || key === 'message' || key === 'stack' || key === 'cause' || DROPPED_KEYS.has(key)) continue
41
+ const v = (value as unknown as Record<string, unknown>)[key]
42
+ out[key] = typeof v === 'string' ? cut(v, key === 'query' ? MAX_QUERY_CHARS : MAX_STRING_CHARS) : v
43
+ }
44
+ if (value.cause !== undefined) {
45
+ out['cause'] = serialize(value.cause, seen)
46
+ }
47
+ return out
48
+ }
@@ -0,0 +1,191 @@
1
+ // The queue table as a pipe.define() definition, built from queue config.
2
+ //
3
+ // The table shape used to live in a hand-written schema.sql that no TS code
4
+ // referenced, so `pipework generate` could not emit it and a regenerated
5
+ // baseline silently dropped the queue migration. Expressing it as a
6
+ // DefinedTable puts it on the same generate path as every other table:
7
+ // createQueue() registers the definition here, and the migration generator
8
+ // merges this registry through its internalDefinitions hook.
9
+
10
+ import { define } from '../../data/domain/index.js'
11
+ import { field } from '../../data/domain/field.js'
12
+ import type { DefinedTable, FieldDescriptor } from '../../data/domain/types.js'
13
+ import { schema } from '../../data/schema/namespace.js'
14
+ import { index, uniqueIndex } from '../../data/db/idx/index.js'
15
+ import { quoteIdentifier } from '../../data/db/identifiers.js'
16
+ import { sql } from '../../data/db/sql.js'
17
+ import type { ResolvedExclusion } from './exclusion.js'
18
+
19
+ export const QUEUE_STATUSES = ['pending', 'claimed', 'running', 'completed', 'failed', 'cancelled', 'dead_letter'] as const
20
+
21
+ /** Column names every queue table owns — exclusion columns must not collide with these. */
22
+ export const QUEUE_BASE_COLUMNS: ReadonlySet<string> = new Set([
23
+ 'id', 'job_type', 'priority', 'status', 'payload', 'output', 'error_payload',
24
+ 'created_lsn', 'queued_at', 'claimed_at', 'heartbeat_at', 'completed_at',
25
+ 'worker_id', 'worker_release', 'claims', 'attempt_count', 'max_attempts', 'next_retry_at', 'claim_token',
26
+ 'flow_trace_id', 'flow_root_id', 'flow_step_id', 'flow_tenant', 'flow_actor', 'live_key', 'pending_key',
27
+ ])
28
+
29
+ /** The live-key index's tenant expression and predicate; enqueue's ON CONFLICT must repeat them exactly. */
30
+ export const LIVE_KEY_TENANT_EXPR = `coalesce(flow_tenant, '')`
31
+ export const LIVE_KEY_PREDICATE = `live_key IS NOT NULL AND status IN ('pending', 'claimed', 'running')`
32
+ export function liveKeyIndexName(tableName: string): string {
33
+ return `uq_${tableName}_live_key`
34
+ }
35
+
36
+ /** The pending-key index's tenant expression and predicate; enqueue's ON CONFLICT must repeat them exactly. */
37
+ export const PENDING_KEY_TENANT_EXPR = LIVE_KEY_TENANT_EXPR
38
+ export const PENDING_KEY_PREDICATE = `pending_key IS NOT NULL AND status = 'pending'`
39
+ export function pendingKeyIndexName(tableName: string): string {
40
+ return `uq_${tableName}_pending_key`
41
+ }
42
+
43
+ function queueFields(exclusion: ResolvedExclusion | undefined): Record<string, FieldDescriptor> {
44
+ const fields: Record<string, FieldDescriptor> = {
45
+ id: field.uuid().primaryKey().defaultRandom(),
46
+ jobType: field.text(),
47
+ priority: field.integer().default(0),
48
+ status: field.enum(QUEUE_STATUSES).default('pending'),
49
+ payload: field.jsonb(schema.check.json()),
50
+ output: field.jsonb(schema.check.json()).nullable(),
51
+ errorPayload: field.jsonb(schema.check.json()).nullable(),
52
+ createdLsn: field.pgLsn().default('currentWal'),
53
+ queuedAt: field.timestamp().default('now'),
54
+ claimedAt: field.timestamp().nullable(),
55
+ heartbeatAt: field.timestamp().nullable(),
56
+ completedAt: field.timestamp().nullable(),
57
+ workerId: field.text().nullable(),
58
+ // The release of the worker that last claimed the job. Unlike worker_id it
59
+ // survives completion, failure, hand-back and later.
60
+ workerRelease: field.text().nullable(),
61
+ // Append-only history of the job's claims: claim()'s UPDATE appends one
62
+ // element, every statement that ends a claim closes the last one.
63
+ claims: field.jsonb(schema.check.json()).default([]),
64
+ attemptCount: field.integer().default(0),
65
+ maxAttempts: field.integer().default(1),
66
+ nextRetryAt: field.timestamp().nullable(),
67
+ // Minted only inside claim()'s own UPDATE and nulled by every release, so
68
+ // a consumer can fence a claimant's writes on an exact uuid comparison.
69
+ claimToken: field.uuid().nullable(),
70
+ // The flow that enqueued the job, restored when a worker runs it: the trace
71
+ // (and open step) it continues, the tenant it runs under, and optionally
72
+ // the actor. All null for a job enqueued outside any Flow.
73
+ flowTraceId: field.uuid().nullable(),
74
+ flowRootId: field.uuid().nullable(),
75
+ flowStepId: field.uuid().nullable(),
76
+ flowTenant: field.text().nullable(),
77
+ flowActor: field.text().nullable(),
78
+ // enqueue's liveKey: at most one live job per (key, tenant), enforced by the partial unique index.
79
+ liveKey: field.text().nullable(),
80
+ // enqueue's pendingKey: at most one pending job per (key, tenant), enforced by the partial unique index.
81
+ pendingKey: field.text().nullable(),
82
+ }
83
+
84
+ if (exclusion?.tenantColumn !== undefined) {
85
+ fields[exclusion.tenantColumn] = field.uuid().tenant()
86
+ }
87
+ if (exclusion?.arrayColumn !== undefined) {
88
+ const column = exclusion.arrayElementType === 'text'
89
+ ? schema.col.text(exclusion.arrayColumn).array()
90
+ : schema.col.uuid(exclusion.arrayColumn).array()
91
+ fields[exclusion.arrayColumn] = field.custom(column, schema.check.array(schema.check.string())).nullable()
92
+ }
93
+ if (exclusion?.blocksAllColumn !== undefined) {
94
+ fields[exclusion.blocksAllColumn] = field.boolean().default(false)
95
+ }
96
+
97
+ return fields
98
+ }
99
+
100
+ interface QueueTableEntry {
101
+ readonly definition: DefinedTable
102
+ readonly configKey: string
103
+ }
104
+
105
+ const queueTables = new Map<string, QueueTableEntry>()
106
+ const progressColumns = new Map<string, string>()
107
+
108
+ /** Records (or clears) the queue's configured progress column so startup validation can check the table has it. */
109
+ export function registerProgressColumn(tableName: string, column: string | undefined): void {
110
+ if (column === undefined) progressColumns.delete(tableName)
111
+ else progressColumns.set(tableName, column)
112
+ }
113
+
114
+ /** Progress columns configured per queue table. */
115
+ export function queueProgressColumns(): ReadonlyMap<string, string> {
116
+ return progressColumns
117
+ }
118
+
119
+ /**
120
+ * Builds (or reuses) the DefinedTable for a queue. Cached per table name so
121
+ * repeated createQueue() calls share one definition; the same name with a
122
+ * different shape is a config error, not a silent overwrite.
123
+ */
124
+ export function queueDefinition(tableName: string, exclusion: ResolvedExclusion | undefined): DefinedTable {
125
+ const configKey = JSON.stringify(exclusion ?? null)
126
+
127
+ const existing = queueTables.get(tableName)
128
+ if (existing !== undefined) {
129
+ if (existing.configKey !== configKey) {
130
+ throw new Error(
131
+ `[pipework] Queue table "${tableName}" is already defined with a different exclusion config.\n\n` +
132
+ ` Existing: ${existing.configKey}\n` +
133
+ ` New: ${configKey}\n` +
134
+ ' One table, one shape — give the second queue its own table name.\n',
135
+ )
136
+ }
137
+ return existing.definition
138
+ }
139
+
140
+ const definition = define(tableName, queueFields(exclusion), {
141
+ indexes: (t) => ({
142
+ claimable: index(`idx_${tableName}_claimable`)
143
+ .on(t['priority'].desc(), t['queuedAt'].asc())
144
+ .where(sql.raw(`status = 'pending'`)),
145
+ liveKey: uniqueIndex(liveKeyIndexName(tableName))
146
+ .on(t['liveKey'], sql.raw(LIVE_KEY_TENANT_EXPR))
147
+ .where(sql.raw(LIVE_KEY_PREDICATE)),
148
+ pendingKey: uniqueIndex(pendingKeyIndexName(tableName))
149
+ .on(t['pendingKey'], sql.raw(PENDING_KEY_TENANT_EXPR))
150
+ .where(sql.raw(PENDING_KEY_PREDICATE)),
151
+ stale: index(`idx_${tableName}_stale`)
152
+ .on(t['heartbeatAt'])
153
+ .where(sql.raw(`status IN ('claimed', 'running')`)),
154
+ retryable: index(`idx_${tableName}_retryable`)
155
+ .on(t['nextRetryAt'])
156
+ .where(sql.raw(`status = 'pending' AND next_retry_at IS NOT NULL`)),
157
+ // The claim query's exclusion checks run under SERIALIZABLE; without a
158
+ // matching index the overlap NOT EXISTS scans the whole claimed/running
159
+ // set and takes coarse predicate locks, so claimers with disjoint arrays
160
+ // SSI-conflict and starve. Partial GIN keeps the locks at the key level;
161
+ // fastupdate must be off — a GIN pending list forces relation-level
162
+ // predicate locks, voiding the granularity.
163
+ ...(exclusion?.arrayColumn !== undefined ? {
164
+ exclusionArray: index(`idx_${tableName}_excl_arr`)
165
+ .using('gin', t[exclusion.arrayColumn]!)
166
+ .with({ fastupdate: 'off' })
167
+ .where(sql.raw(`status IN ('claimed', 'running')`)),
168
+ } : {}),
169
+ // Same starvation mechanism for the blocks-all NOT EXISTS: without its
170
+ // own index that check scans every claimed/running row. This partial
171
+ // index contains only active blocks-all jobs (normally none), so
172
+ // ordinary claims neither read beyond it nor insert into it — only a
173
+ // genuine blocks-all claim conflicts, which is the intended semantics.
174
+ ...(exclusion?.blocksAllColumn !== undefined ? {
175
+ exclusionBlocks: index(`idx_${tableName}_excl_blocks`)
176
+ .on(t[exclusion.tenantColumn ?? exclusion.blocksAllColumn]!)
177
+ .where(sql.raw(
178
+ `status IN ('claimed', 'running') AND ${quoteIdentifier(exclusion.blocksAllColumn, 'exclusion column')} = true`,
179
+ )),
180
+ } : {}),
181
+ }),
182
+ })
183
+
184
+ queueTables.set(tableName, { definition, configKey })
185
+ return definition
186
+ }
187
+
188
+ /** Registered queue definitions — merged into `pipework generate` via internalDefinitions. */
189
+ export function queueDefinitions(): ReadonlyMap<string, DefinedTable> {
190
+ return new Map([...queueTables].map(([name, entry]) => [name, entry.definition]))
191
+ }
@@ -0,0 +1,53 @@
1
+ import type { DB } from '../../data/db/index.js'
2
+ import { sql } from '../../data/db/sql.js'
3
+ import type { PipelineConfig, Pipeline, PipelineRun, PipelineExecuteContext } from './types.js'
4
+ import { executePipeline } from './execute.js'
5
+ import { resumePipeline } from './resume.js'
6
+ import { refuseScopedHandle } from '../../auth/tenant/scoped-db.js'
7
+
8
+ export function definePipeline(config: PipelineConfig): Pipeline {
9
+ if (config.steps.length === 0) {
10
+ throw new Error(`[pipework] Pipeline "${config.name}" must have at least one step.`)
11
+ }
12
+
13
+ const stepNames = new Set<string>()
14
+ for (const step of config.steps) {
15
+ if (stepNames.has(step.name)) {
16
+ throw new Error(`[pipework] Pipeline "${config.name}" has duplicate step name "${step.name}".`)
17
+ }
18
+ stepNames.add(step.name)
19
+ }
20
+
21
+ return refuseScopedHandle({
22
+ async execute(db: DB, input: unknown, context: PipelineExecuteContext): Promise<PipelineRun> {
23
+ return executePipeline(db, config, input, context)
24
+ },
25
+
26
+ async resume(db: DB, runId: string, stepOutput: unknown): Promise<PipelineRun> {
27
+ return resumePipeline(db, config, runId, stepOutput)
28
+ },
29
+
30
+ async getStatus(db: DB, runId: string): Promise<PipelineRun | null> {
31
+ const [row] = await db.execute(sql`
32
+ SELECT id, pipeline_name, status, current_step, input, started_at, completed_at, error
33
+ FROM pipework_pipeline_runs
34
+ WHERE id = ${runId}::uuid
35
+ `)
36
+
37
+ if (row === undefined) return null
38
+
39
+ return {
40
+ id: String(row['id']),
41
+ pipelineName: String(row['pipeline_name']),
42
+ status: String(row['status']) as PipelineRun['status'],
43
+ currentStep: Number(row['current_step']),
44
+ input: row['input'],
45
+ startedAt: new Date(String(row['started_at'])),
46
+ completedAt: row['completed_at'] !== null ? new Date(String(row['completed_at'])) : null,
47
+ error: row['error'] ?? null,
48
+ }
49
+ },
50
+
51
+ definition: config,
52
+ }, `pipeline "${config.name}"`, 'its step handlers receive the connection it runs on and would bypass the tenant filter')
53
+ }
@@ -0,0 +1,35 @@
1
+ import { PipeworkError } from '../../core/errors/base.js'
2
+
3
+ export class PipelineStepError extends PipeworkError {
4
+ readonly code = 'PIPELINE_STEP_FAILED' as const
5
+ readonly httpStatus = 500 as const
6
+ readonly pipelineName: string
7
+ readonly stepName: string
8
+ readonly stepIndex: number
9
+
10
+ constructor(pipelineName: string, stepName: string, stepIndex: number, cause: unknown) {
11
+ const causeMsg = cause instanceof Error ? cause.message : String(cause)
12
+ super(
13
+ `Pipeline "${pipelineName}" failed at step ${stepIndex} ("${stepName}"): ${causeMsg}. ` +
14
+ `Prior steps may have committed. Check pipeline run status for details.`,
15
+ )
16
+ this.pipelineName = pipelineName
17
+ this.stepName = stepName
18
+ this.stepIndex = stepIndex
19
+ }
20
+ }
21
+
22
+ export class PipelineResumeError extends PipeworkError {
23
+ readonly code = 'PIPELINE_RESUME_FAILED' as const
24
+ readonly httpStatus = 409 as const
25
+ readonly runId: string
26
+ readonly reason: string
27
+
28
+ constructor(runId: string, reason: string) {
29
+ super(
30
+ `Cannot resume pipeline run "${runId}": ${reason}`,
31
+ )
32
+ this.runId = runId
33
+ this.reason = reason
34
+ }
35
+ }