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,50 @@
1
+ import type postgres from 'postgres'
2
+ import { drizzle, type PostgresJsDatabase } from '../data/query/postgres-js/index.js'
3
+
4
+ /** The setting the test-only policy `pipework_test_system` reads. */
5
+ export const TEST_SYSTEM_SETTING = 'pipework.test_system'
6
+
7
+ /** The setting a door's generated policy reads. */
8
+ export const DOOR_SETTING = 'pipework.door'
9
+
10
+ const ON = `SELECT set_config('${TEST_SYSTEM_SETTING}', 'on', true)`
11
+ const OFF = `SELECT set_config('${TEST_SYSTEM_SETTING}', 'off', true)`
12
+ const quoted = (value: string): string => `'${value.replace(/'/g, "''")}'`
13
+ const noop = (): void => {}
14
+
15
+ /**
16
+ * A handle over the test's own connection whose every statement runs with the
17
+ * test-only escape policy open: the statement is sent between two
18
+ * `set_config` queries enqueued in the same tick, so on this single
19
+ * connection no other query can land between them. The escape is never left
20
+ * on for a span of time. A statement that fails leaves the transaction
21
+ * aborted; the closing query then fails too, and its failure is swallowed.
22
+ */
23
+ export function createSystemHandle(client: postgres.Sql, door?: string): PostgresJsDatabase {
24
+ // A door's statement also names the door, so the door policy is open too
25
+ // wherever the connection's role is the platform role.
26
+ const on = door === undefined ? ON : `${ON}, set_config('${DOOR_SETTING}', ${quoted(door)}, true)`
27
+ const off = door === undefined ? OFF : `${OFF}, set_config('${DOOR_SETTING}', '', true)`
28
+ const bracketed = new Proxy(client, {
29
+ get(target, prop, receiver) {
30
+ if (prop !== 'unsafe') return Reflect.get(target, prop, receiver)
31
+ // Read at call time: the connection's `unsafe` is patched after this handle is built.
32
+ const send = (target as { unsafe: (...args: unknown[]) => unknown }).unsafe.bind(target)
33
+ return (query: string, params?: unknown[], ...rest: unknown[]): unknown => {
34
+ const middle = send(query, params, ...rest) as { handle(): Promise<void>; executed: boolean }
35
+ const start = middle.handle
36
+ middle.handle = function (this: typeof middle): Promise<void> {
37
+ if (this.executed) return Promise.resolve()
38
+ const open = send(on) as { execute(): { catch(f: () => void): unknown } }
39
+ open.execute().catch(noop)
40
+ const result = start.call(this)
41
+ const close = send(off) as { execute(): { catch(f: () => void): unknown } }
42
+ close.execute().catch(noop)
43
+ return result
44
+ }
45
+ return middle
46
+ }
47
+ },
48
+ })
49
+ return drizzle(bracketed)
50
+ }
@@ -0,0 +1,142 @@
1
+ import { existsSync } from 'node:fs'
2
+ import { isAbsolute, sep } from 'node:path'
3
+ import { fileURLToPath } from 'node:url'
4
+ import type { Plugin } from 'vitest/config'
5
+
6
+ import { applyFlowStepTransform } from '../trace/transformer/apply.js'
7
+
8
+ export interface PipeworkVitestOptions {
9
+ globals?: boolean
10
+ isolate?: boolean
11
+ /** Set to `false` to skip auto-setup (manifold discovery, DB provisioning, per-test isolation). Use this for pure unit test configs that never call `pipe()` or `withFlow()`. */
12
+ database?: boolean
13
+ /**
14
+ * Absolute path to the project (package) root. When set, any module that
15
+ * Vite would resolve to `<root>/dist/*.js` is rewritten to its `<root>/src/*.ts`
16
+ * (or `.tsx`) source so `vi.mock` matches the file handlers actually import
17
+ * — without this, a built workspace package gets split into two module
18
+ * identities (dist + src), and mock interception silently fails. Issue #274.
19
+ */
20
+ packageRoot?: string
21
+ /**
22
+ * Map of bare cross-package workspace specifier → absolute source file.
23
+ * vitest does not honour tsconfig `paths`, so an import of `@scope/pkg` (or
24
+ * one of its export subpaths) resolves through the package's `exports` field
25
+ * to `dist/`, which is not built during `pipework test`. This routes those
26
+ * specifiers to `src/` instead. Issue #301.
27
+ *
28
+ * A wildcard export subpath contributes a single `*` entry — key and value
29
+ * both contain one `*` (e.g. `@scope/pkg/events/*` → `<abs>/pkg/src/events/*.ts`).
30
+ * The resolver substitutes the matched segment into the value. Issue #311.
31
+ */
32
+ workspacePackages?: Record<string, string>
33
+ }
34
+
35
+ /**
36
+ * A wildcard workspace entry split at its single `*`: a specifier matches when
37
+ * it starts with `prefix` and ends with `suffix`, and the segment between them
38
+ * is substituted into the value at `valuePrefix`/`valueSuffix`. Issue #311.
39
+ */
40
+ interface WildcardPackage {
41
+ prefix: string
42
+ suffix: string
43
+ valuePrefix: string
44
+ valueSuffix: string
45
+ }
46
+
47
+ function splitWorkspacePackages(workspacePackages: Record<string, string>): {
48
+ exact: Record<string, string>
49
+ wildcard: WildcardPackage[]
50
+ } {
51
+ const exact: Record<string, string> = {}
52
+ const wildcard: WildcardPackage[] = []
53
+ for (const [specifier, value] of Object.entries(workspacePackages)) {
54
+ const star = specifier.indexOf('*')
55
+ const valueStar = value.indexOf('*')
56
+ // A wildcard specifier must map to a wildcard value — otherwise every
57
+ // matched subpath would collapse onto one file. Treat a mismatched entry
58
+ // as exact so a malformed map degrades to a miss rather than misresolving.
59
+ if (star === -1 || valueStar === -1) {
60
+ exact[specifier] = value
61
+ continue
62
+ }
63
+ wildcard.push({
64
+ prefix: specifier.slice(0, star),
65
+ suffix: specifier.slice(star + 1),
66
+ valuePrefix: value.slice(0, valueStar),
67
+ valueSuffix: value.slice(valueStar + 1),
68
+ })
69
+ }
70
+ return { exact, wildcard }
71
+ }
72
+
73
+ // Wraps `@flow_step` functions in consumer source so they run as trace steps
74
+ // under test. Runs before Vite strips types; the printer reflows the file, so
75
+ // stack-trace lines inside an annotated file can shift.
76
+ const flowStepTransform: NonNullable<Plugin['transform']> = function (code, id) {
77
+ const file = id.split('?')[0] ?? id
78
+ if (!/\.tsx?$/.test(file) || file.includes('/node_modules/') || !isAbsolute(file)) return null
79
+ const applied = applyFlowStepTransform(code, file)
80
+ return applied === null ? null : { code: applied.code, map: null }
81
+ }
82
+
83
+ // Forked workers are SIGTERMed right after vitest's `stop` handshake, so their
84
+ // last queued clone drops never run. This module's teardown runs in the parent
85
+ // once every worker is gone and drops what they left (#810).
86
+ const REAP_AFTER_RUN = fileURLToPath(new URL(`./reap-after-run${import.meta.url.slice(import.meta.url.lastIndexOf('.'))}`, import.meta.url))
87
+
88
+ export function pipework(options?: PipeworkVitestOptions): Plugin {
89
+ const reapConfig: Pick<Plugin, 'config'> =
90
+ options?.database === false ? {} : { config: () => ({ test: { globalSetup: [REAP_AFTER_RUN] } }) }
91
+ const packageRoot = options?.packageRoot
92
+ const workspacePackages = options?.workspacePackages
93
+ if (packageRoot === undefined && workspacePackages === undefined) {
94
+ return { name: 'pipework', enforce: 'pre', transform: flowStepTransform, ...reapConfig }
95
+ }
96
+ const distPrefix = packageRoot !== undefined ? packageRoot + sep + 'dist' + sep : undefined
97
+ const srcPrefix = packageRoot !== undefined ? packageRoot + sep + 'src' + sep : undefined
98
+ const { exact, wildcard } =
99
+ workspacePackages !== undefined
100
+ ? splitWorkspacePackages(workspacePackages)
101
+ : { exact: {}, wildcard: [] }
102
+
103
+ return {
104
+ name: 'pipework',
105
+ enforce: 'pre',
106
+ transform: flowStepTransform,
107
+ ...reapConfig,
108
+ async resolveId(source, importer) {
109
+ // Cross-package (#301): a bare workspace specifier resolves straight to src/.
110
+ if (workspacePackages !== undefined) {
111
+ const mapped = exact[source]
112
+ if (mapped !== undefined) return mapped
113
+ // Wildcard export subpath (#311): match `prefix*suffix`, substitute the
114
+ // captured segment, and resolve only when the source file exists — a
115
+ // miss falls through so Vite reports its own error.
116
+ for (const w of wildcard) {
117
+ if (
118
+ source.length > w.prefix.length + w.suffix.length &&
119
+ source.startsWith(w.prefix) &&
120
+ source.endsWith(w.suffix)
121
+ ) {
122
+ const matched = source.slice(w.prefix.length, source.length - w.suffix.length)
123
+ const candidate = w.valuePrefix + matched + w.valueSuffix
124
+ if (existsSync(candidate)) return candidate
125
+ }
126
+ }
127
+ }
128
+ // Intra-package (#274): a built dist/*.js is rewritten to its src/*.ts.
129
+ if (distPrefix === undefined || srcPrefix === undefined) return null
130
+ const resolved = await this.resolve(source, importer, { skipSelf: true })
131
+ if (resolved === null) return null
132
+ const id = resolved.id
133
+ if (!id.startsWith(distPrefix) || !id.endsWith('.js')) return null
134
+ const relative = id.slice(distPrefix.length, -3)
135
+ for (const ext of ['.ts', '.tsx']) {
136
+ const candidate = srcPrefix + relative + ext
137
+ if (existsSync(candidate)) return candidate
138
+ }
139
+ return null
140
+ },
141
+ }
142
+ }
@@ -0,0 +1,507 @@
1
+ import { resolve, dirname } from 'node:path'
2
+ import { configDefaults } from 'vitest/config'
3
+ import type { ViteUserConfig, UserWorkspaceConfig } from 'vitest/config'
4
+ import type { VitestConfig, VitestModuleConfig, VitestProfileConfig, CoverageConfig } from '../core/config/schema.js'
5
+ import type { ResolvedModule } from '../workspace/discover.js'
6
+ import type { Manifold } from '../core/pipework.js'
7
+ import { findConfig, registerTsResolveHooks } from '../core/config/discover.js'
8
+ import { importConfig } from '../core/config/import-config.js'
9
+ import { loadEnvFiles } from '../core/config/env.js'
10
+ import { resolveModules } from '../workspace/discover.js'
11
+ import { workspaceSourcePaths } from '../workspace/source-paths.js'
12
+ import { pipework as pipeworkPlugin } from './vitest-plugin.js'
13
+
14
+ const DEFAULT_TEST_TIMEOUT = 30_000
15
+ export const DEFAULT_HOOK_TIMEOUT = 60_000
16
+
17
+ // Pipework's own defaults, overridable per workspace, module and profile via
18
+ // `vitest.pool` / `vitest.isolate`. `forks` because a test that sets a process-wide
19
+ // Postgres session must not share a worker; `isolate: false` because pipework's
20
+ // per-test database isolation already separates test state and re-isolating the
21
+ // worker costs a process spawn per file.
22
+ const DEFAULT_POOL = 'forks'
23
+ const DEFAULT_ISOLATE = false
24
+
25
+ type PoolName = NonNullable<VitestConfig['pool']>
26
+
27
+ /**
28
+ * Consumer excludes on TOP of vitest's defaults, not in place of them.
29
+ *
30
+ * Vitest replaces `exclude` outright, so a config that adds one glob silently stops
31
+ * excluding `node_modules` and `.git` — the suite then tries to run every test in every
32
+ * installed package. Merging is the behaviour a consumer means (#394).
33
+ */
34
+ function mergeExclude(configured: string[] | undefined): string[] {
35
+ if (configured === undefined) return [...configDefaults.exclude]
36
+ return [...new Set([...configDefaults.exclude, ...configured])]
37
+ }
38
+
39
+ /**
40
+ * Build the full vitest UserConfig from pipework.config.ts.
41
+ *
42
+ * The generated `vitest.config.ts` is a single line: `defineConfig(await resolveVitestConfig())`.
43
+ * Everything that would normally live in `vitest.config.ts` — projects, reporters, sequencer,
44
+ * outer-level Vite options — flows through `pipework.config.ts` so pipework remains the single
45
+ * source of truth and the generated file stays byte-stable for `pipework check`.
46
+ *
47
+ * Shape:
48
+ * - `vitest.extra.test.*` → spread into `test` (reporters, sequence, cache, bail, ...)
49
+ * - `vitest.extra.*` (anything outside `test`) → spread at the top level (resolve, plugins, ...)
50
+ * - `test.projects` is owned by pipework — defining it in `extra` throws.
51
+ */
52
+ export async function resolveVitestConfig(cwd?: string): Promise<ViteUserConfig> {
53
+ const start = cwd ?? process.cwd()
54
+ const configPath = findConfig(start)
55
+
56
+ if (configPath === null) {
57
+ throw new Error(
58
+ `[pipework] No pipework.config.ts found in ${start} or any parent directory.\n\n` +
59
+ ' The vitest config resolver needs pipework.config.ts to discover modules.\n' +
60
+ ' Create one with `pipework init`.\n',
61
+ )
62
+ }
63
+
64
+ const configDir = dirname(configPath)
65
+ loadEnvFiles(configDir)
66
+ registerTsResolveHooks()
67
+
68
+ let manifold: Manifold
69
+ try {
70
+ manifold = await importConfig(configPath)
71
+ } catch (err: unknown) {
72
+ const message = err instanceof Error ? err.message : String(err)
73
+ throw new Error(
74
+ `[pipework] Failed to load pipework.config.ts for vitest config resolution.\n\n` +
75
+ ` ${message}\n\n` +
76
+ ' This usually means database environment variables are missing.\n' +
77
+ ' Ensure your .env file exists and contains DATABASE_URL (or equivalent).\n\n' +
78
+ ' The vitest config resolver imports your config to read module definitions.\n' +
79
+ ' It does not connect to any database.\n',
80
+ )
81
+ }
82
+
83
+ const blueprint = manifold.config.raw
84
+ if (blueprint.vitest === undefined) {
85
+ return { test: { projects: [] } }
86
+ }
87
+
88
+ const resolvedModules = resolveModules(blueprint.modules, configDir)
89
+ const workspacePackages: Record<string, string> = {}
90
+ for (const [specifier, relPath] of workspaceSourcePaths(resolvedModules, blueprint.name ?? 'app')) {
91
+ workspacePackages[specifier] = resolve(configDir, relPath)
92
+ }
93
+ const projects = resolveVitestModules(blueprint.vitest, resolvedModules, configDir, workspacePackages)
94
+ return mergeExtraWithProjects(blueprint.vitest.extra, projects, blueprint.vitest.coverage)
95
+ }
96
+
97
+ /**
98
+ * Merge consumer-supplied top-level vitest config (from `vitest.extra`) with the
99
+ * pipework-controlled `test.projects`. Splits `extra` into outer-level keys (Vite-level:
100
+ * `resolve`, `plugins`, ...) and `test`-level keys (`reporters`, `sequence`, ...),
101
+ * spreading each at the right depth. Throws if the consumer tries to override `projects`.
102
+ */
103
+ function mergeExtraWithProjects(
104
+ extra: Record<string, unknown> | undefined,
105
+ projects: UserWorkspaceConfig[],
106
+ coverage: CoverageConfig | undefined,
107
+ ): ViteUserConfig {
108
+ // Vitest reads coverage at the root only. It used to be written into each project,
109
+ // where it did nothing — including `coverage.enabled`, which had no schema entry at
110
+ // all, so an env-gated preset was unreachable except through `extra.test` (#394).
111
+ const root = coverage !== undefined ? { coverage: rootCoverage(coverage) } : {}
112
+
113
+ if (extra === undefined) {
114
+ return { test: { ...root, projects } }
115
+ }
116
+
117
+ const { test: extraTest, ...extraOuter } = extra
118
+
119
+ if (extraTest !== undefined && (typeof extraTest !== 'object' || extraTest === null || Array.isArray(extraTest))) {
120
+ throw new Error(
121
+ `[pipework] vitest.extra.test must be an object — got ${describe(extraTest)}.\n\n` +
122
+ ' vitest.extra mirrors the shape of `defineConfig({...})`:\n' +
123
+ ' - test-level keys (reporters, sequence, ...) go under `extra.test`\n' +
124
+ ' - outer Vite keys (resolve, plugins, ...) go at the top of `extra`\n',
125
+ )
126
+ }
127
+
128
+ const testRecord = (extraTest ?? {}) as Record<string, unknown>
129
+
130
+ if ('projects' in testRecord) {
131
+ throw new Error(
132
+ `[pipework] vitest.extra.test.projects is owned by pipework — \n` +
133
+ ' define modules in `vitest.modules` (in pipework.config.ts) instead.\n' +
134
+ ' The generated vitest.config.ts wires `projects` through `resolveVitestConfig()`.\n',
135
+ )
136
+ }
137
+
138
+ return {
139
+ ...extraOuter,
140
+ test: {
141
+ ...root,
142
+ ...testRecord,
143
+ projects,
144
+ },
145
+ }
146
+ }
147
+
148
+ // `exactOptionalPropertyTypes` makes an explicit `undefined` a different thing from an
149
+ // absent key, and vitest's InlineConfig accepts only the latter. Drop the unset ones.
150
+ function rootCoverage(coverage: CoverageConfig): Record<string, unknown> {
151
+ const out: Record<string, unknown> = {}
152
+ for (const [key, value] of Object.entries(coverage)) {
153
+ if (value !== undefined) out[key] = value
154
+ }
155
+ return out
156
+ }
157
+
158
+ function describe(v: unknown): string {
159
+ if (v === null) return 'null'
160
+ if (Array.isArray(v)) return 'array'
161
+ return typeof v
162
+ }
163
+
164
+ /**
165
+ * Refuse a `coverage` key on a module or profile.
166
+ *
167
+ * Vitest treats coverage as a root-level option, so one written into a project is
168
+ * discarded without a word — the exact silent-no-op #394 was filed for. Naming the
169
+ * module beats accepting a setting that cannot take effect.
170
+ */
171
+ function refuseProjectCoverage(where: string, config: { coverage?: unknown }): void {
172
+ if (config.coverage === undefined) return
173
+ throw new Error(
174
+ `[pipework] vitest coverage is a root-level option — remove it from \`${where}\`.\n\n` +
175
+ ' Vitest reads `coverage` at the config root; inside a project it is ignored.\n' +
176
+ ' Move the settings to the top-level `vitest.coverage` in pipework.config.ts.\n' +
177
+ ' Gate collection with `vitest.coverage.enabled` (compute the boolean there to\n' +
178
+ ' key it off an environment variable).\n',
179
+ )
180
+ }
181
+
182
+ export function resolveVitestModules(
183
+ vitestConfig: VitestConfig,
184
+ resolvedModules: ResolvedModule[],
185
+ configDir: string,
186
+ workspacePackages: Record<string, string>,
187
+ ): UserWorkspaceConfig[] {
188
+ // A repo with no declared modules is a single-package project: the repo root
189
+ // is the one implicit module, addressed as '.'. The centralized vitest model
190
+ // must be able to express this layout — otherwise single-package projects
191
+ // have no in-config home for their test setup.
192
+ const effectiveModules: ResolvedModule[] = resolvedModules.length === 0
193
+ ? [{ key: '.', relativePath: '.', config: {} }]
194
+ : resolvedModules
195
+ const moduleMap = new Map(effectiveModules.map(m => [m.relativePath, m]))
196
+ const included = resolveIncludedModules(vitestConfig, moduleMap)
197
+
198
+ const projects: UserWorkspaceConfig[] = []
199
+
200
+ for (const [modulePath, moduleConfig] of [...included.entries()].sort(([a], [b]) => a.localeCompare(b))) {
201
+ refuseProjectCoverage(`vitest.modules["${modulePath}"].coverage`, moduleConfig)
202
+ const project = buildProjectConfig(modulePath, moduleConfig, vitestConfig, configDir, workspacePackages)
203
+ projects.push(project)
204
+
205
+ if (moduleConfig.profiles !== undefined) {
206
+ for (const [profileName, profileConfig] of Object.entries(moduleConfig.profiles).sort(([a], [b]) => a.localeCompare(b))) {
207
+ refuseProjectCoverage(`vitest.modules["${modulePath}"].profiles["${profileName}"].coverage`, profileConfig)
208
+ const profileProject = buildProfileConfig(modulePath, profileName, moduleConfig, profileConfig, vitestConfig, configDir, workspacePackages)
209
+ projects.push(profileProject)
210
+ }
211
+ }
212
+ }
213
+
214
+ return projects
215
+ }
216
+
217
+ function resolveIncludedModules(
218
+ vitestConfig: VitestConfig,
219
+ moduleMap: Map<string, ResolvedModule>,
220
+ ): Map<string, VitestModuleConfig> {
221
+ if (vitestConfig.modules === undefined) {
222
+ const result = new Map<string, VitestModuleConfig>()
223
+ for (const key of moduleMap.keys()) {
224
+ result.set(key, {})
225
+ }
226
+ return result
227
+ }
228
+
229
+ const globs: Array<{ pattern: string; config: VitestModuleConfig }> = []
230
+ const explicit: Array<{ key: string; config: VitestModuleConfig }> = []
231
+
232
+ for (const [key, config] of Object.entries(vitestConfig.modules)) {
233
+ if (key.includes('*')) {
234
+ globs.push({ pattern: key, config })
235
+ } else {
236
+ explicit.push({ key, config })
237
+ }
238
+ }
239
+
240
+ const result = new Map<string, VitestModuleConfig>()
241
+
242
+ for (const { pattern, config } of globs) {
243
+ for (const modulePath of matchGlob(pattern, moduleMap)) {
244
+ result.set(modulePath, config)
245
+ }
246
+ }
247
+
248
+ for (const { key, config } of explicit) {
249
+ if (!moduleMap.has(key)) {
250
+ const known = [...moduleMap.keys()]
251
+ const suggestion = closestMatch(key, known)
252
+ const knownList = known.join(', ')
253
+ const hint = suggestion !== null
254
+ ? `Did you mean "${suggestion}"? Known modules: ${knownList}`
255
+ : `Known modules: ${knownList}`
256
+ throw new Error(
257
+ `[pipework] Unknown module "${key}" in vitest.modules.\n\n ${hint}\n`,
258
+ )
259
+ }
260
+
261
+ const existing = result.get(key)
262
+ if (existing !== undefined) {
263
+ result.set(key, mergeModuleConfigs(existing, config))
264
+ } else {
265
+ result.set(key, config)
266
+ }
267
+ }
268
+
269
+ return result
270
+ }
271
+
272
+ function matchGlob(pattern: string, moduleMap: Map<string, ResolvedModule>): string[] {
273
+ const parts = pattern.split('/')
274
+ const starIndex = parts.indexOf('*')
275
+ if (starIndex === -1) return []
276
+
277
+ const prefix = parts.slice(0, starIndex).join('/')
278
+ const suffix = parts.slice(starIndex + 1).join('/')
279
+
280
+ const matches: string[] = []
281
+ for (const key of moduleMap.keys()) {
282
+ const keyParts = key.split('/')
283
+ if (keyParts.length !== parts.length) continue
284
+
285
+ const keyPrefix = keyParts.slice(0, starIndex).join('/')
286
+ const keySuffix = keyParts.slice(starIndex + 1).join('/')
287
+
288
+ if (keyPrefix === prefix && keySuffix === suffix) {
289
+ matches.push(key)
290
+ }
291
+ }
292
+ return matches.sort()
293
+ }
294
+
295
+ function mergeModuleConfigs(base: VitestModuleConfig, override: VitestModuleConfig): VitestModuleConfig {
296
+ return {
297
+ ...override,
298
+ coverage: override.coverage !== undefined || base.coverage !== undefined
299
+ ? { ...base.coverage, ...override.coverage }
300
+ : undefined,
301
+ env: override.env !== undefined || base.env !== undefined
302
+ ? { ...base.env, ...override.env }
303
+ : undefined,
304
+ extra: override.extra !== undefined || base.extra !== undefined
305
+ ? { ...base.extra, ...override.extra }
306
+ : undefined,
307
+ profiles: override.profiles !== undefined || base.profiles !== undefined
308
+ ? { ...base.profiles, ...override.profiles }
309
+ : undefined,
310
+ }
311
+ }
312
+
313
+ function buildProjectConfig(
314
+ modulePath: string,
315
+ moduleConfig: VitestModuleConfig,
316
+ workspace: VitestConfig,
317
+ configDir: string,
318
+ workspacePackages: Record<string, string>,
319
+ ): UserWorkspaceConfig {
320
+ const database = moduleConfig.database ?? workspace.database ?? true
321
+ const testTimeout = moduleConfig.testTimeout ?? workspace.testTimeout ?? DEFAULT_TEST_TIMEOUT
322
+ const hookTimeout = moduleConfig.hookTimeout ?? workspace.hookTimeout ?? DEFAULT_HOOK_TIMEOUT
323
+ const include = moduleConfig.include ?? workspace.include
324
+ const exclude = moduleConfig.exclude ?? workspace.exclude
325
+
326
+ const setupFiles = buildSetupFiles(database, moduleConfig.setupFiles)
327
+ const inlineDeps = buildInlineDeps(workspace.inlineDeps)
328
+
329
+ const extra = moduleConfig.extra ?? {}
330
+
331
+ return buildProject({
332
+ name: modulePath,
333
+ root: resolve(configDir, modulePath),
334
+ database,
335
+ testTimeout,
336
+ hookTimeout,
337
+ include,
338
+ exclude,
339
+ setupFiles,
340
+ inlineDeps,
341
+ pool: moduleConfig.pool ?? workspace.pool,
342
+ isolate: moduleConfig.isolate ?? workspace.isolate,
343
+ env: moduleConfig.env,
344
+ extra,
345
+ workspacePackages,
346
+ })
347
+ }
348
+
349
+ function buildProfileConfig(
350
+ modulePath: string,
351
+ profileName: string,
352
+ moduleConfig: VitestModuleConfig,
353
+ profileConfig: VitestProfileConfig,
354
+ workspace: VitestConfig,
355
+ configDir: string,
356
+ workspacePackages: Record<string, string>,
357
+ ): UserWorkspaceConfig {
358
+ const database = profileConfig.database ?? moduleConfig.database ?? workspace.database ?? true
359
+ const testTimeout = profileConfig.testTimeout ?? moduleConfig.testTimeout ?? workspace.testTimeout ?? DEFAULT_TEST_TIMEOUT
360
+ const hookTimeout = profileConfig.hookTimeout ?? moduleConfig.hookTimeout ?? workspace.hookTimeout ?? DEFAULT_HOOK_TIMEOUT
361
+ const include = profileConfig.include ?? moduleConfig.include ?? workspace.include
362
+ const exclude = profileConfig.exclude ?? moduleConfig.exclude ?? workspace.exclude
363
+
364
+ const profileSetupFiles = profileConfig.setupFiles ?? moduleConfig.setupFiles
365
+ const setupFiles = buildSetupFiles(database, profileSetupFiles)
366
+ const inlineDeps = buildInlineDeps(workspace.inlineDeps)
367
+
368
+ const moduleExtra = moduleConfig.extra ?? {}
369
+ const extra = profileConfig.extra !== undefined
370
+ ? { ...moduleExtra, ...profileConfig.extra }
371
+ : moduleExtra
372
+
373
+ const env = profileConfig.env !== undefined || moduleConfig.env !== undefined
374
+ ? { ...moduleConfig.env, ...profileConfig.env }
375
+ : undefined
376
+
377
+ return buildProject({
378
+ name: `${modulePath}:${profileName}`,
379
+ root: resolve(configDir, modulePath),
380
+ database,
381
+ testTimeout,
382
+ hookTimeout,
383
+ include,
384
+ exclude,
385
+ setupFiles,
386
+ inlineDeps,
387
+ pool: profileConfig.pool ?? moduleConfig.pool ?? workspace.pool,
388
+ isolate: profileConfig.isolate ?? moduleConfig.isolate ?? workspace.isolate,
389
+ env,
390
+ extra,
391
+ workspacePackages,
392
+ })
393
+ }
394
+
395
+ interface ProjectBuildInput {
396
+ name: string
397
+ root: string
398
+ database: boolean
399
+ testTimeout: number
400
+ hookTimeout: number
401
+ include: string[] | undefined
402
+ exclude: string[] | undefined
403
+ setupFiles: string[]
404
+ inlineDeps: string[]
405
+ pool: PoolName | undefined
406
+ isolate: boolean | undefined
407
+ env: Record<string, string> | undefined
408
+ extra: Record<string, unknown>
409
+ workspacePackages: Record<string, string>
410
+ }
411
+
412
+ function buildProject(input: ProjectBuildInput): UserWorkspaceConfig {
413
+ return {
414
+ ...input.extra,
415
+ plugins: [
416
+ pipeworkPlugin({ database: input.database, packageRoot: input.root, workspacePackages: input.workspacePackages }),
417
+ ],
418
+ test: {
419
+ // Pipework defaults — overridable via `extra.test` below (#260).
420
+ pool: DEFAULT_POOL,
421
+ isolate: DEFAULT_ISOLATE,
422
+ ...('test' in input.extra ? input.extra['test'] as Record<string, unknown> : {}),
423
+ // Dedicated config fields and project identity — these win over
424
+ // `extra.test`. `name`/`root` are the project's identity (changing them
425
+ // breaks `--project` matching and module rooting); `setupFiles` and the
426
+ // inline-deps `server` config are load-bearing for test isolation.
427
+ name: input.name,
428
+ root: input.root,
429
+ testTimeout: input.testTimeout,
430
+ hookTimeout: input.hookTimeout,
431
+ server: {
432
+ deps: {
433
+ inline: input.inlineDeps,
434
+ },
435
+ },
436
+ setupFiles: input.setupFiles,
437
+ // `vitest.pool` / `vitest.isolate` are dedicated fields, so an explicitly
438
+ // configured one wins over `extra.test`; unset, the default above stands and
439
+ // `extra.test` still overrides it as it always did.
440
+ ...(input.pool !== undefined ? { pool: input.pool } : {}),
441
+ ...(input.isolate !== undefined ? { isolate: input.isolate } : {}),
442
+ ...(input.include !== undefined ? { include: input.include } : {}),
443
+ exclude: mergeExclude(input.exclude),
444
+ ...(input.env !== undefined ? { env: input.env } : {}),
445
+ },
446
+ }
447
+ }
448
+
449
+ function buildSetupFiles(database: boolean, userSetupFiles: string[] | undefined): string[] {
450
+ const files: string[] = []
451
+ if (database) {
452
+ files.push('pipework/test/setup')
453
+ }
454
+ if (userSetupFiles !== undefined) {
455
+ files.push(...userSetupFiles)
456
+ }
457
+ return files
458
+ }
459
+
460
+ function buildInlineDeps(workspaceInlineDeps: string[] | undefined): string[] {
461
+ const deps = new Set<string>(['pipework'])
462
+ if (workspaceInlineDeps !== undefined) {
463
+ for (const dep of workspaceInlineDeps) {
464
+ deps.add(dep)
465
+ }
466
+ }
467
+ return [...deps]
468
+ }
469
+
470
+ function closestMatch(input: string, candidates: string[]): string | null {
471
+ if (candidates.length === 0) return null
472
+
473
+ let best: string | null = null
474
+ let bestDistance = Infinity
475
+
476
+ for (const candidate of candidates) {
477
+ const d = levenshtein(input.toLowerCase(), candidate.toLowerCase())
478
+ if (d < bestDistance && d <= Math.max(input.length, candidate.length) * 0.6) {
479
+ bestDistance = d
480
+ best = candidate
481
+ }
482
+ }
483
+
484
+ return best
485
+ }
486
+
487
+ function levenshtein(a: string, b: string): number {
488
+ const m = a.length
489
+ const n = b.length
490
+ const dp: number[][] = Array.from({ length: m + 1 }, () => Array.from({ length: n + 1 }, () => 0))
491
+
492
+ for (let i = 0; i <= m; i++) dp[i]![0] = i
493
+ for (let j = 0; j <= n; j++) dp[0]![j] = j
494
+
495
+ for (let i = 1; i <= m; i++) {
496
+ for (let j = 1; j <= n; j++) {
497
+ const cost = a[i - 1] === b[j - 1] ? 0 : 1
498
+ dp[i]![j] = Math.min(
499
+ dp[i - 1]![j]! + 1,
500
+ dp[i]![j - 1]! + 1,
501
+ dp[i - 1]![j - 1]! + cost,
502
+ )
503
+ }
504
+ }
505
+
506
+ return dp[m]![n]!
507
+ }