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,185 @@
1
+ // Test setup is one awaited call from a vitest `beforeAll`. When forty forks
2
+ // start at once, a healthy setup can spend a minute queued behind its peers —
3
+ // and the consumer's hook timeout fires with nothing to say but the number of
4
+ // milliseconds it waited. So setup narrates: every step it reaches is reported
5
+ // to the caller, and the bound it enforces itself is on PHASE ADVANCE, never on
6
+ // total elapsed time. A queued setup that keeps advancing finishes late and
7
+ // green; a genuinely stuck one fails naming the phase it reached. Issue #380.
8
+
9
+ /** Steps `setupTestDatabases` passes through, in the order it reaches them. */
10
+ export type SetupPhase =
11
+ | 'rls-probe'
12
+ | 'run-prepare'
13
+ | 'run-reap'
14
+ | 'template-pin'
15
+ | 'template-wait'
16
+ | 'template-build'
17
+ | 'template-migrate'
18
+ | 'template-framework'
19
+ | 'template-partitions'
20
+ | 'template-grants'
21
+ | 'template-ready'
22
+ | 'queue-check'
23
+ | 'clone-start'
24
+ | 'clone-done'
25
+ | 'ready'
26
+
27
+ export interface SetupProgress {
28
+ readonly phase: SetupPhase
29
+ /** The configured database this step is about, or null for whole-run steps. */
30
+ readonly database: string | null
31
+ /** Free-form detail — a migration tag, a poll count. Never parsed. */
32
+ readonly detail?: string
33
+ /** Milliseconds since setup started. */
34
+ readonly elapsedMs: number
35
+ /** The phase this one displaced, and how long it held. Null on the first. */
36
+ readonly previous: { readonly phase: SetupPhase, readonly database: string | null, readonly heldMs: number } | null
37
+ }
38
+
39
+ export type SetupProgressListener = (progress: SetupProgress) => void
40
+
41
+ /** Setup made no progress for the configured bound. Names the last phase reached. */
42
+ export class SetupStalled extends Error {
43
+ constructor(
44
+ readonly phase: SetupPhase,
45
+ readonly database: string | null,
46
+ readonly stalledMs: number,
47
+ readonly elapsedMs: number,
48
+ ) {
49
+ const where = database === null ? phase : `${phase} (database "${database}")`
50
+ super(
51
+ `[pipework] Test setup failed:\n\n` +
52
+ ` No progress for ${(stalledMs / 1000).toFixed(1)}s.\n\n` +
53
+ ` last phase reached: ${where}\n` +
54
+ ` total elapsed: ${(elapsedMs / 1000).toFixed(1)}s\n\n` +
55
+ ` This bound is on phase advance, not on total time — setup queued behind\n` +
56
+ ` other processes keeps advancing and does not trip it. Something at the\n` +
57
+ ` phase above is stuck. Raise the bound with test.setupStallMs in\n` +
58
+ ` pipework.config.ts if the step is legitimately this slow.\n`,
59
+ )
60
+ this.name = 'SetupStalled'
61
+ }
62
+ }
63
+
64
+ /**
65
+ * Records the phase setup is in and when it got there. The watchdog reads it;
66
+ * the listener is told about every advance.
67
+ */
68
+ export class PhaseTracker {
69
+ phase: SetupPhase = 'run-prepare'
70
+ database: string | null = null
71
+ private started = false
72
+ private at: number
73
+ private readonly start: number
74
+
75
+ constructor(
76
+ private readonly listener: SetupProgressListener = () => {},
77
+ private readonly clock: () => number = () => Date.now(),
78
+ ) {
79
+ this.start = this.clock()
80
+ this.at = this.start
81
+ }
82
+
83
+ /** How long the current phase has held. */
84
+ idleMs(): number {
85
+ return this.clock() - this.at
86
+ }
87
+
88
+ elapsedMs(): number {
89
+ return this.clock() - this.start
90
+ }
91
+
92
+ reached(phase: SetupPhase, database: string | null = null, detail?: string): void {
93
+ const now = this.clock()
94
+ const previous = this.started
95
+ ? { phase: this.phase, database: this.database, heldMs: now - this.at }
96
+ : null
97
+ this.started = true
98
+ this.phase = phase
99
+ this.database = database
100
+ this.at = now
101
+ this.listener({
102
+ phase,
103
+ database,
104
+ ...(detail === undefined ? {} : { detail }),
105
+ elapsedMs: now - this.start,
106
+ previous,
107
+ })
108
+ }
109
+ }
110
+
111
+ export const DEFAULT_SETUP_STALL_MS = 120_000
112
+
113
+ /**
114
+ * Timeout for the `beforeAll` that runs setup: setTimeout's ceiling, so vitest's
115
+ * hookTimeout never bounds a setup that is still advancing. Only the stall
116
+ * watchdog fails setup.
117
+ */
118
+ export const SETUP_HOOK_TIMEOUT_MS = 2 ** 31 - 1
119
+
120
+ export interface StallWatchOptions {
121
+ stallMs?: number
122
+ /** How often the watchdog looks. Defaults to a tenth of the bound. */
123
+ pollMs?: number
124
+ }
125
+
126
+ /**
127
+ * Run `work`, failing it if `tracker` reports no phase advance for `stallMs`.
128
+ *
129
+ * The watchdog only ever REJECTS earlier than `work` would settle — it cannot
130
+ * make a completed setup fail, and its timer is unref'd so it never holds the
131
+ * process open.
132
+ */
133
+ export async function withStallWatchdog<T>(
134
+ work: () => Promise<T>,
135
+ tracker: PhaseTracker,
136
+ options: StallWatchOptions = {},
137
+ ): Promise<T> {
138
+ const stallMs = options.stallMs ?? DEFAULT_SETUP_STALL_MS
139
+ const pollMs = options.pollMs ?? Math.max(50, Math.floor(stallMs / 10))
140
+
141
+ let timer: ReturnType<typeof setInterval> | undefined
142
+ const stalled = new Promise<never>((_resolve, reject) => {
143
+ timer = setInterval(() => {
144
+ const idle = tracker.idleMs()
145
+ if (idle >= stallMs) {
146
+ reject(new SetupStalled(tracker.phase, tracker.database, idle, tracker.elapsedMs()))
147
+ }
148
+ }, pollMs)
149
+ timer.unref()
150
+ })
151
+
152
+ try {
153
+ return await Promise.race([work(), stalled])
154
+ } finally {
155
+ if (timer !== undefined) clearInterval(timer)
156
+ }
157
+ }
158
+
159
+ /** Prefix for the stderr lines `auto-setup` writes about slow phases. */
160
+ export const SETUP_PROGRESS_LOG_PREFIX = 'pipework:test-setup'
161
+
162
+ /**
163
+ * A phase that held this long is worth a line. Below it setup is silent: a
164
+ * healthy run must not spray forty lines per test file.
165
+ */
166
+ export const SLOW_PHASE_MS = 10_000
167
+
168
+ /**
169
+ * The listener `auto-setup` installs. Silent while setup is quick, one
170
+ * key=value line per phase that took long enough to explain a late hook.
171
+ */
172
+ export function logSlowPhases(
173
+ write: (line: string) => void = message => console.warn(message),
174
+ slowMs: number = SLOW_PHASE_MS,
175
+ ): SetupProgressListener {
176
+ return progress => {
177
+ const previous = progress.previous
178
+ if (previous === null || previous.heldMs < slowMs) return
179
+ const db = previous.database === null ? '' : ` db=${previous.database}`
180
+ write(
181
+ `${SETUP_PROGRESS_LOG_PREFIX} slow phase=${previous.phase}${db} ` +
182
+ `held_ms=${previous.heldMs} next=${progress.phase} elapsed_ms=${progress.elapsedMs}`,
183
+ )
184
+ }
185
+ }
@@ -0,0 +1,28 @@
1
+ import { loadEnvFiles } from '../core/config/env.js'
2
+ import { discoverInstance } from '../core/config/discover.js'
3
+ import { reapConfiguredTestDatabases } from './reap.js'
4
+ import { TEST_REAP_LOG_PREFIX } from './setup.js'
5
+
6
+ // Vitest globalSetup, added by the pipework plugin. `teardown` runs in the
7
+ // parent after every worker has stopped, so the clones a SIGTERMed fork left
8
+ // queued are orphans by then: their run locks died with the workers. Clones
9
+ // only (templates outlive runs), and only those whose run lock is free, so a
10
+ // concurrent run's live clones are never touched. Best effort, never fails a run.
11
+ export function setup(): void {}
12
+
13
+ export async function teardown(): Promise<void> {
14
+ try {
15
+ loadEnvFiles()
16
+ const instance = await discoverInstance()
17
+ try {
18
+ const { droppedDatabases } = await reapConfiguredTestDatabases(instance, { clonesOnly: true })
19
+ if (droppedDatabases.length > 0) {
20
+ process.stderr.write(`${TEST_REAP_LOG_PREFIX} dropped ${String(droppedDatabases.length)} leftover clone(s): ${droppedDatabases.join(', ')}\n`)
21
+ }
22
+ } finally {
23
+ await instance.pool.closeAll()
24
+ }
25
+ } catch {
26
+ // Best effort — the next run's reaper drops what this missed.
27
+ }
28
+ }
@@ -0,0 +1,286 @@
1
+ import { createHash } from 'node:crypto'
2
+ import postgres from 'postgres'
3
+ import type { Manifold } from '../core/pipework.js'
4
+ import { quoteIdentifier } from '../data/db/identifiers.js'
5
+ import {
6
+ testDbPrefix,
7
+ appRolePrefix,
8
+ testDbLikePattern,
9
+ templateDbPrefix,
10
+ templateDbLikePattern,
11
+ } from './namespace.js'
12
+
13
+
14
+ // The run nonce every name `testDbName` writes ends with: 12 random hex chars.
15
+ // Carrying one is the reaper's evidence that it created the database, and the nonce
16
+ // rebuilds the owning run's advisory-lock key. Deliberately not the fuller
17
+ // `_<seq>_<pid>_<nonce>` tail: an orphan left by an older pipework whose names had
18
+ // no clone sequence would then never be reaped by any later version.
19
+ //
20
+ // The residual limit, stated rather than hidden: a foreign database inside this
21
+ // project's prefix whose name happens to end in exactly 12 hex characters is
22
+ // indistinguishable from a clone and is still eligible. Nothing in the name can
23
+ // separate them; `PIPEWORK_TEST_NAMESPACE` is the way out of a shared prefix.
24
+ const CLONE_SUFFIX = /_([0-9a-f]{12})$/
25
+
26
+ export interface ReapOptions {
27
+ /**
28
+ * Called before each candidate database is handled, `index` counting 1..`total`
29
+ * across the clones and the stale templates together, so a hung drop is named.
30
+ */
31
+ onReap?: (datname: string, index: number, total: number) => void
32
+ /** Leave shared templates alone: only per-run clones are candidates. */
33
+ clonesOnly?: boolean
34
+ }
35
+
36
+ export interface ReapResult {
37
+ readonly droppedDatabases: readonly string[]
38
+ /**
39
+ * Databases inside this project's prefix that the reaper did NOT create and so
40
+ * will not drop. Reported rather than dropped: sharing a prefix is not evidence
41
+ * of ownership, and a name pipework never wrote belongs to somebody else (#391).
42
+ */
43
+ readonly skippedDatabases: readonly string[]
44
+ }
45
+
46
+ /**
47
+ * Advisory-lock key identifying a test run. A live run holds this lock on a
48
+ * dedicated connection for its entire lifetime; Postgres releases it
49
+ * automatically when that connection ends by any means (clean exit, crash,
50
+ * SIGKILL, reboot). The reaper reads "lock is free" as "owning run is dead".
51
+ */
52
+ export function runLockKey(nonce: string): bigint {
53
+ return BigInt('0x' + nonce)
54
+ }
55
+
56
+ /**
57
+ * Advisory-lock keys for a shared template, derived from its content
58
+ * fingerprint. Both are salted hashes truncated to 60 bits, which keeps them
59
+ * inside a signed `bigint` and makes an accidental collision with a 48-bit run
60
+ * lock key or with each other a non-event.
61
+ *
62
+ * Two keys, because the two things being coordinated are different:
63
+ *
64
+ * - the USE key is taken in SHARE mode by every process that intends to clone
65
+ * the template, for that process's whole life, and in EXCLUSIVE mode by the
66
+ * reaper. That is what stops a reaper from dropping a template between
67
+ * another process's readiness check and its `CREATE DATABASE ... WITH
68
+ * TEMPLATE`: the reaper simply cannot get the lock while anyone holds it.
69
+ * - the BUILD key is taken in EXCLUSIVE mode only while building, so that N
70
+ * processes starting at once produce one template and one migration replay
71
+ * rather than N. It is separate because advisory locks do not upgrade from
72
+ * share to exclusive, and a builder must keep its own pin while it builds.
73
+ *
74
+ * Issue #340.
75
+ */
76
+ function templateLockKey(salt: string, fingerprint: string): bigint {
77
+ return BigInt('0x' + createHash('sha256').update(`${salt}:${fingerprint}`).digest('hex').slice(0, 15))
78
+ }
79
+
80
+ export function templateUseKey(fingerprint: string): bigint {
81
+ return templateLockKey('pipework-template-use', fingerprint)
82
+ }
83
+
84
+ export function templateBuildKey(fingerprint: string): bigint {
85
+ return templateLockKey('pipework-template-build', fingerprint)
86
+ }
87
+
88
+ /** Admin (`/postgres`) connection URL for a database URL. */
89
+ export function getAdminUrl(databaseUrl: string): string {
90
+ const url = new URL(databaseUrl)
91
+ url.pathname = '/postgres'
92
+ return url.toString()
93
+ }
94
+
95
+ function pairedRoleName(datname: string): string {
96
+ return appRolePrefix() + datname.slice(testDbPrefix().length)
97
+ }
98
+
99
+ /** A shared template's app role is content-keyed the same way the template is. */
100
+ function pairedTemplateRoleName(datname: string): string {
101
+ return appRolePrefix() + datname.slice(templateDbPrefix().length)
102
+ }
103
+
104
+ /** Content fingerprint trailing a shared template's name: 16 lowercase hex. */
105
+ const TEMPLATE_FINGERPRINT_SUFFIX = /_([0-9a-f]{16})$/
106
+
107
+ /**
108
+ * Drop test databases left behind by runs that never reached teardown
109
+ * (Ctrl-C, timeout, crash). A database is reaped only when ALL THREE hold:
110
+ *
111
+ * - the reaper created it — its name carries the 12-hex run nonce every name
112
+ * `testDbName` writes. Sharing the project prefix is not evidence of
113
+ * ownership: a cluster carrying `<project>_test_<slot>` control databases
114
+ * lost all of them at once to a prefix-only sweep (#391). Anything else in
115
+ * the prefix is reported in `skippedDatabases` and left alone;
116
+ * - its run's advisory lock is free — the owning run's process is gone; and
117
+ * - `numbackends = 0` — no connections remain, so reaping cannot pull a
118
+ * database out from under workers orphaned by a SIGKILL'd parent.
119
+ *
120
+ * All three only ever make the reaper more conservative, so it can never drop a
121
+ * database belonging to a live or still-provisioning run, or to anyone else.
122
+ */
123
+ export async function reapOrphanTestDatabases(
124
+ admin: postgres.Sql,
125
+ liveTemplates: ReadonlySet<string> = new Set(),
126
+ options: ReapOptions = {},
127
+ ): Promise<ReapResult> {
128
+ const rows = await admin<{ datname: string, numbackends: number }[]>`
129
+ SELECT datname, numbackends
130
+ FROM pg_stat_database
131
+ WHERE datname LIKE ${testDbLikePattern()}
132
+ `
133
+ const templateRows = options.clonesOnly === true ? [] : await staleTemplateCandidates(admin)
134
+ const total = rows.length + templateRows.length
135
+ const { onReap } = options
136
+ const droppedDatabases: string[] = []
137
+ const skippedDatabases: string[] = []
138
+ for (const [i, { datname, numbackends }] of rows.entries()) {
139
+ onReap?.(datname, i + 1, total)
140
+ // A name in this prefix that pipework did not write. It could be anything —
141
+ // a control database, a hand-made fixture, another tool's namespace that
142
+ // happens to collide. Dropping it because the prefix matched is what #391
143
+ // reported; say what was left alone instead.
144
+ const nonce = CLONE_SUFFIX.exec(datname)?.[1]
145
+ if (nonce === undefined) {
146
+ skippedDatabases.push(datname)
147
+ continue
148
+ }
149
+
150
+ if (numbackends > 0) continue
151
+
152
+ const key = runLockKey(nonce)
153
+ if (!(await tryAcquire(admin, key))) continue
154
+
155
+ try {
156
+ if (await dropDatabase(admin, datname)) {
157
+ droppedDatabases.push(datname)
158
+ await dropRole(admin, pairedRoleName(datname))
159
+ }
160
+ } finally {
161
+ await admin`SELECT pg_advisory_unlock(${key.toString()}::bigint)`
162
+ }
163
+ }
164
+ const templates = await reapStaleTemplates(admin, liveTemplates, templateRows, (datname, i) => onReap?.(datname, rows.length + i, total))
165
+ droppedDatabases.push(...templates.droppedDatabases)
166
+ skippedDatabases.push(...templates.skippedDatabases)
167
+ return { droppedDatabases, skippedDatabases }
168
+ }
169
+
170
+ /**
171
+ * Drop shared templates nobody wants any more.
172
+ *
173
+ * A template is deliberately long-lived — it survives the run that built it, so
174
+ * the next run clones instead of replaying migrations (#340) — which means
175
+ * "left behind by a dead run" is no longer a reason to drop one. The two
176
+ * reasons that remain:
177
+ *
178
+ * - it is not in `liveTemplates`, the set of content keys the calling run
179
+ * actually intends to use. Editing a migration changes the key, so the
180
+ * template built from the old migrations is named something nobody will ask
181
+ * for again. That is what makes invalidation automatic rather than manual.
182
+ * - nobody holds its use lock, in any mode. Every process that intends to
183
+ * clone a template holds that lock in SHARE mode for its whole life, so
184
+ * failing to take it EXCLUSIVE means no live process is cloning from it —
185
+ * including no process sitting between a readiness check and its `CREATE
186
+ * DATABASE ... WITH TEMPLATE`.
187
+ *
188
+ * `numbackends = 0` is required as well, for the same conservatism as clones.
189
+ *
190
+ * An empty `liveTemplates` — what `pipework test --reap` passes — therefore
191
+ * means "drop every template not currently in use", which is the right reading
192
+ * of an explicit cleanup command.
193
+ */
194
+ async function reapStaleTemplates(
195
+ admin: postgres.Sql,
196
+ liveTemplates: ReadonlySet<string>,
197
+ rows: readonly { datname: string, numbackends: number }[],
198
+ onEach: (datname: string, index: number) => void,
199
+ ): Promise<ReapResult> {
200
+ const droppedDatabases: string[] = []
201
+ const skippedDatabases: string[] = []
202
+ for (const [i, { datname, numbackends }] of rows.entries()) {
203
+ onEach(datname, i + 1)
204
+ const fingerprint = TEMPLATE_FINGERPRINT_SUFFIX.exec(datname)?.[1]
205
+ // A name in the template namespace without a fingerprint was not written by
206
+ // this scheme. Leave it alone rather than guess at its owner — and say so.
207
+ if (fingerprint === undefined) {
208
+ skippedDatabases.push(datname)
209
+ continue
210
+ }
211
+
212
+ if (numbackends > 0) continue
213
+ if (liveTemplates.has(datname)) continue
214
+
215
+ const key = templateUseKey(fingerprint)
216
+ if (!(await tryAcquire(admin, key))) continue
217
+
218
+ try {
219
+ if (await dropDatabase(admin, datname)) {
220
+ droppedDatabases.push(datname)
221
+ await dropRole(admin, pairedTemplateRoleName(datname))
222
+ }
223
+ } finally {
224
+ await admin`SELECT pg_advisory_unlock(${key.toString()}::bigint)`
225
+ }
226
+ }
227
+ return { droppedDatabases, skippedDatabases }
228
+ }
229
+
230
+ async function staleTemplateCandidates(admin: postgres.Sql): Promise<{ datname: string, numbackends: number }[]> {
231
+ return [...await admin<{ datname: string, numbackends: number }[]>`
232
+ SELECT datname, numbackends
233
+ FROM pg_stat_database
234
+ WHERE datname LIKE ${templateDbLikePattern()}
235
+ `]
236
+ }
237
+
238
+ /**
239
+ * Reap orphan test databases across every Postgres server referenced by the
240
+ * manifold's database config. Used by `pipework test --reap`.
241
+ */
242
+ export async function reapConfiguredTestDatabases(instance: Manifold, options: ReapOptions = {}): Promise<ReapResult> {
243
+ const adminUrls = new Set<string>()
244
+ for (const [, dbConfig] of instance.config.databases()) {
245
+ adminUrls.add(getAdminUrl(dbConfig.url))
246
+ }
247
+ const droppedDatabases: string[] = []
248
+ const skippedDatabases: string[] = []
249
+ for (const adminUrl of adminUrls) {
250
+ const client = postgres(adminUrl, { max: 1, onnotice: () => {} })
251
+ try {
252
+ const result = await reapOrphanTestDatabases(client, new Set(), options)
253
+ droppedDatabases.push(...result.droppedDatabases)
254
+ skippedDatabases.push(...result.skippedDatabases)
255
+ } finally {
256
+ await client.end()
257
+ }
258
+ }
259
+ return { droppedDatabases, skippedDatabases }
260
+ }
261
+
262
+ async function tryAcquire(admin: postgres.Sql, key: bigint): Promise<boolean> {
263
+ const rows = await admin<{ locked: boolean }[]>`
264
+ SELECT pg_try_advisory_lock(${key.toString()}::bigint) AS locked
265
+ `
266
+ return rows[0]?.locked === true
267
+ }
268
+
269
+ async function dropDatabase(admin: postgres.Sql, datname: string): Promise<boolean> {
270
+ try {
271
+ await admin.unsafe(`DROP DATABASE IF EXISTS ${quoteIdentifier(datname, 'database')}`)
272
+ return true
273
+ } catch {
274
+ // A backend connected between the numbackends check and the DROP — leave
275
+ // this database for the next run's reaper.
276
+ return false
277
+ }
278
+ }
279
+
280
+ async function dropRole(admin: postgres.Sql, role: string): Promise<void> {
281
+ try {
282
+ await admin.unsafe(`DROP ROLE IF EXISTS ${quoteIdentifier(role, 'role')}`)
283
+ } catch {
284
+ // Role still owns objects in another database — best effort.
285
+ }
286
+ }