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,284 @@
1
+ import { Readable, Transform } from 'node:stream'
2
+ import type { FastifyRequest } from 'fastify'
3
+ import type { UploadConfig, BinaryUpload, MultipartUpload } from '../di/types.js'
4
+ import { extensionOf, resolveMediaType, SNIFF_BYTES } from './sniff.js'
5
+ import { isPipeworkError, MalformedMultipartError, PayloadTooLargeError, UnsupportedMediaTypeError, ValidationError } from '../../core/errors/index.js'
6
+
7
+ /** Largest accepted multipart text field, in bytes. */
8
+ export const MULTIPART_FIELD_LIMIT = 64 * 1024
9
+ const MULTIPART_OVERHEAD = 16 * 1024
10
+
11
+ /** The body size Fastify may let through: the file plus multipart framing and fields. */
12
+ export function uploadBodyLimit(config: UploadConfig): number {
13
+ if (config.kind === 'binary') return config.maxBytes
14
+ return config.maxBytes + MULTIPART_OVERHEAD + (config.fields?.length ?? 0) * MULTIPART_FIELD_LIMIT
15
+ }
16
+
17
+ /** `type/subtype` of a Content-Type value, lowercased and without parameters. */
18
+ export function mediaTypeOf(value: string | undefined): string {
19
+ if (value === undefined) return ''
20
+ return (value.split(';')[0] ?? '').trim().toLowerCase()
21
+ }
22
+
23
+ /** True when `mediaType` matches an allowed entry: exactly, by `type/*`, or by a star on each side of the slash (any non-empty type). */
24
+ export function mediaTypeAllowed(allowed: readonly string[], mediaType: string): boolean {
25
+ if (mediaType === '') return false
26
+ for (const entry of allowed) {
27
+ const a = entry.trim().toLowerCase()
28
+ if (a === mediaType || a === '*/*') return true
29
+ if (a.endsWith('/*') && mediaType.startsWith(a.slice(0, -1))) return true
30
+ }
31
+ return false
32
+ }
33
+
34
+ const pipeworkRefusals = new WeakSet<object>()
35
+
36
+ function refusal<T extends Error>(error: T): T {
37
+ pipeworkRefusals.add(error)
38
+ return error
39
+ }
40
+
41
+ /** True for a 413 or 415 pipework raised for a file, as opposed to one a handler threw. */
42
+ export function isUploadRefusal(error: unknown): error is PayloadTooLargeError | UnsupportedMediaTypeError {
43
+ return typeof error === 'object' && error !== null && pipeworkRefusals.has(error)
44
+ }
45
+
46
+ /** What pipework knows of the file at the moment it refuses it. */
47
+ export interface UploadTrack {
48
+ declaredType: string
49
+ extension: string | null
50
+ filename: string | null
51
+ detectedType: string | null
52
+ bytesRead: number
53
+ }
54
+
55
+ export function newUploadTrack(): UploadTrack {
56
+ return { declaredType: '', extension: null, filename: null, detectedType: null, bytesRead: 0 }
57
+ }
58
+
59
+ /**
60
+ * Refuses a multipart route's request that is not `multipart/form-data`, from
61
+ * its headers alone. A protocol error: no file is involved. Binary routes have
62
+ * no header check; their type is settled from the bytes.
63
+ */
64
+ export function checkUploadHeaders(config: UploadConfig, headers: Record<string, string | string[] | undefined>): UnsupportedMediaTypeError | null {
65
+ if (config.kind === 'binary') return null
66
+ const contentType = headers['content-type']
67
+ const mediaType = mediaTypeOf(Array.isArray(contentType) ? contentType[0] : contentType)
68
+ if (mediaType === 'multipart/form-data') return null
69
+ return new UnsupportedMediaTypeError(
70
+ mediaType === ''
71
+ ? 'This route needs a Content-Type; accepted: multipart/form-data'
72
+ : `Media type ${mediaType} is not accepted; accepted: multipart/form-data`,
73
+ { accepted: ['multipart/form-data'] },
74
+ )
75
+ }
76
+
77
+ /** 413 when the request declares a length over what the route may receive. */
78
+ function checkDeclaredLength(config: UploadConfig, headers: Record<string, string | string[] | undefined>): PayloadTooLargeError | null {
79
+ const declared = Number(headers['content-length'])
80
+ if (headers['content-length'] !== undefined && Number.isFinite(declared) && declared > uploadBodyLimit(config)) {
81
+ return refusal(new PayloadTooLargeError(`Request body is ${declared} bytes; the limit is ${config.maxBytes}`, { maxBytes: config.maxBytes }))
82
+ }
83
+ return null
84
+ }
85
+
86
+ /**
87
+ * Passes `source` through, erroring with a 413 once more than `maxBytes` have
88
+ * passed. A failure of `source` becomes the failure of the returned stream.
89
+ */
90
+ export function limitStream(
91
+ source: Readable,
92
+ maxBytes: number,
93
+ mapError: (e: unknown) => Error = (e) => e as Error,
94
+ onUnfinished?: () => Error,
95
+ track?: UploadTrack,
96
+ ): Readable {
97
+ let seen = 0
98
+ const counter = new Transform({
99
+ transform(chunk: Buffer, _enc, cb) {
100
+ seen += chunk.length
101
+ if (track !== undefined) track.bytesRead = seen
102
+ if (seen > maxBytes) {
103
+ source.unpipe(counter)
104
+ cb(refusal(new PayloadTooLargeError(`Upload is over the limit of ${maxBytes} bytes`, { maxBytes })))
105
+ return
106
+ }
107
+ cb(null, chunk)
108
+ },
109
+ })
110
+ source.on('error', (e) => counter.destroy(mapError(e)))
111
+ if (onUnfinished !== undefined) {
112
+ // The parser may have failed and destroyed the part before this stream
113
+ // was attached, so its 'error' and 'close' are already past.
114
+ const unfinished = (): void => {
115
+ if (!source.readableEnded && !counter.destroyed) counter.destroy(onUnfinished())
116
+ }
117
+ if (source.destroyed) unfinished()
118
+ else source.on('close', unfinished)
119
+ }
120
+ source.pipe(counter)
121
+ return counter
122
+ }
123
+
124
+ function toUploadError(e: unknown): Error {
125
+ const code = (e as { code?: unknown } | null)?.code
126
+ if (code === 'FST_REQ_FILE_TOO_LARGE') return refusal(new PayloadTooLargeError('Uploaded file is over the size limit'))
127
+ return asMalformedMultipart(e)
128
+ }
129
+
130
+ /** A parser failure becomes a 400; errors that already carry a status are kept. */
131
+ function asMalformedMultipart(e: unknown): Error {
132
+ if (isPipeworkError(e)) return e
133
+ const status = (e as { statusCode?: unknown } | null)?.statusCode
134
+ if (typeof status === 'number' && status >= 400 && status < 500) return e as Error
135
+ const message = e instanceof Error ? e.message : String(e)
136
+ return new MalformedMultipartError(undefined, { cause: message })
137
+ }
138
+
139
+ /** Lowercase text after the last dot of a filename; null without a filename or a dot. */
140
+
141
+ /**
142
+ * Reads up to SNIFF_BYTES from `source`, then returns them with a stream that
143
+ * yields them again followed by the rest of `source`, byte for byte. Nothing
144
+ * further is buffered. A failure of `source` while reading the head is thrown.
145
+ */
146
+ async function withHead(source: Readable): Promise<{ head: Buffer; stream: Readable }> {
147
+ const it = source[Symbol.asyncIterator]()
148
+ const chunks: Buffer[] = []
149
+ let length = 0
150
+ let ended = false
151
+ while (length < SNIFF_BYTES) {
152
+ const next = await it.next() as IteratorResult<Buffer>
153
+ if (next.done === true) { ended = true; break }
154
+ chunks.push(next.value)
155
+ length += next.value.length
156
+ }
157
+ const all = Buffer.concat(chunks)
158
+ async function* rejoin(): AsyncGenerator<Buffer> {
159
+ try {
160
+ if (all.length > 0) yield all
161
+ while (!ended) {
162
+ const next = await it.next() as IteratorResult<Buffer>
163
+ if (next.done === true) return
164
+ yield next.value
165
+ }
166
+ } finally {
167
+ source.destroy()
168
+ }
169
+ }
170
+ const stream = Readable.from(rejoin(), { objectMode: false })
171
+ stream.on('close', () => source.destroy())
172
+ return { head: all.subarray(0, SNIFF_BYTES), stream }
173
+ }
174
+
175
+ interface SniffedFile {
176
+ readonly stream: Readable
177
+ readonly mediaType: string
178
+ readonly detectedType: string | null
179
+ readonly declaredType: string
180
+ readonly extension: string | null
181
+ }
182
+
183
+ /** Reads the head of `limited`, settles the file's type and refuses a type outside the route's list. */
184
+ async function sniffFile(
185
+ config: UploadConfig,
186
+ limited: Readable,
187
+ declaredType: string,
188
+ filename: string | null,
189
+ track: UploadTrack,
190
+ ): Promise<SniffedFile> {
191
+ const extension = extensionOf(filename)
192
+ track.declaredType = declaredType
193
+ track.filename = filename
194
+ track.extension = extension
195
+ const { head, stream } = await withHead(limited)
196
+ const { detectedType, mediaType } = resolveMediaType({ head, declared: declaredType, extension })
197
+ track.detectedType = detectedType
198
+ if (!mediaTypeAllowed(config.mediaTypes, mediaType)) {
199
+ stream.destroy()
200
+ throw refusal(new UnsupportedMediaTypeError(
201
+ `File media type is not accepted (detected ${detectedType ?? 'none'}, declared ${declaredType === '' ? 'none' : declaredType}); accepted: ${config.mediaTypes.join(', ')}`,
202
+ { accepted: config.mediaTypes, detectedType, declaredType },
203
+ ))
204
+ }
205
+ return { stream, mediaType, detectedType, declaredType, extension }
206
+ }
207
+
208
+ /**
209
+ * Starts an upload: refuses a declared length over the limit, builds the size-
210
+ * limited file stream, reads its first bytes to settle the media type, and
211
+ * refuses a type outside the route's list. `track` follows what is learned.
212
+ */
213
+ export async function startUpload(req: FastifyRequest, config: UploadConfig, track: UploadTrack): Promise<BinaryUpload | MultipartUpload> {
214
+ if (config.kind === 'binary') {
215
+ track.declaredType = mediaTypeOf(req.headers['content-type'])
216
+ const tooLarge = checkDeclaredLength(config, req.headers)
217
+ if (tooLarge !== null) throw tooLarge
218
+ const body = req.body
219
+ const source = body instanceof Readable ? body : Readable.from([])
220
+ const declared = req.headers['content-length']
221
+ const length = declared === undefined ? null : Number(declared)
222
+ const file = await sniffFile(config, limitStream(source, config.maxBytes, undefined, undefined, track), track.declaredType, null, track)
223
+ return { ...file, length: length !== null && Number.isFinite(length) ? length : null }
224
+ }
225
+ const tooLarge = checkDeclaredLength(config, req.headers)
226
+ if (tooLarge !== null) throw tooLarge
227
+ try {
228
+ return await readMultipart(req, config, track)
229
+ } catch (e) {
230
+ throw asMalformedMultipart(e)
231
+ }
232
+ }
233
+
234
+ /**
235
+ * Reads a multipart body up to its file part: the text fields before it, then
236
+ * the file as a size-limited stream. Nothing after the file part is read.
237
+ */
238
+ async function readMultipart(
239
+ req: FastifyRequest,
240
+ config: Extract<UploadConfig, { kind: 'multipart' }>,
241
+ track: UploadTrack,
242
+ ): Promise<MultipartUpload> {
243
+ const allowedFields = new Set(config.fields ?? [])
244
+ const fields: Record<string, string> = {}
245
+ for await (const part of req.parts()) {
246
+ if (part.type === 'field') {
247
+ if (!allowedFields.has(part.fieldname)) {
248
+ throw new ValidationError({ [part.fieldname]: ['Unexpected field'] }, `Unexpected form field "${part.fieldname}"`)
249
+ }
250
+ if (part.valueTruncated) {
251
+ throw new PayloadTooLargeError(`Form field "${part.fieldname}" is over ${MULTIPART_FIELD_LIMIT} bytes`)
252
+ }
253
+ fields[part.fieldname] = String(part.value)
254
+ continue
255
+ }
256
+ if (part.fieldname !== config.file) {
257
+ part.file.resume()
258
+ throw new ValidationError({ [part.fieldname]: ['Unexpected file field'] }, `Unexpected file field "${part.fieldname}"; the file goes in "${config.file}"`)
259
+ }
260
+ const filename = part.filename === '' ? null : part.filename
261
+ const limited = limitStream(part.file, config.maxBytes, toUploadError, () => new MalformedMultipartError(undefined, { cause: 'The body ended before the file part was closed' }), track)
262
+ try {
263
+ const file = await sniffFile(config, limited, mediaTypeOf(part.mimetype), filename, track)
264
+ return { ...file, filename, fields }
265
+ } catch (e) {
266
+ part.file.resume()
267
+ throw e
268
+ }
269
+ }
270
+ throw new ValidationError({ [config.file]: ['Required'] }, `The form has no file part named "${config.file}"`)
271
+ }
272
+
273
+ /**
274
+ * Ends an upload after its handler: a stream not read to the end is destroyed.
275
+ * Returns true when the body was left incomplete, so the caller closes the
276
+ * connection rather than reading the rest.
277
+ */
278
+ export function settleUpload(stream: Readable | undefined): boolean {
279
+ if (stream === undefined) return false
280
+ if (stream.readableEnded) return false
281
+ stream.on('error', () => undefined)
282
+ stream.destroy()
283
+ return true
284
+ }
@@ -0,0 +1,6 @@
1
+ export function singularize(s: string): string {
2
+ if (s.endsWith('ies')) return s.slice(0, -3) + 'y'
3
+ if (s.endsWith('ses') || s.endsWith('xes')) return s.slice(0, -2)
4
+ if (s.endsWith('s') && !s.endsWith('ss')) return s.slice(0, -1)
5
+ return s
6
+ }
@@ -0,0 +1,205 @@
1
+ import type { RouteRegistration } from '../http/types.js'
2
+ import { toJsonSchema } from '../../data/schema/json-schema.js'
3
+
4
+ export interface OpenAPIInfo {
5
+ title: string
6
+ version: string
7
+ description?: string
8
+ }
9
+
10
+ export interface OpenAPIDocument {
11
+ openapi: '3.0.3'
12
+ info: OpenAPIInfo
13
+ paths: Record<string, Record<string, unknown>>
14
+ }
15
+
16
+ /** The body of a validation 422: `{ error: { code, message, fields? } }`. */
17
+ export const VALIDATION_ERROR_SCHEMA = {
18
+ type: 'object',
19
+ properties: {
20
+ error: {
21
+ type: 'object',
22
+ properties: {
23
+ code: { type: 'string' },
24
+ message: { type: 'string' },
25
+ fields: { type: 'object', additionalProperties: { type: 'array', items: { type: 'string' } } },
26
+ },
27
+ required: ['code', 'message'],
28
+ },
29
+ },
30
+ required: ['error'],
31
+ } as const
32
+
33
+ export const TOO_MANY_REQUESTS_SCHEMA = {
34
+ type: 'object',
35
+ properties: {
36
+ error: {
37
+ type: 'object',
38
+ properties: {
39
+ code: { type: 'string' },
40
+ message: { type: 'string' },
41
+ retryAfterSeconds: { type: 'integer', minimum: 0 },
42
+ },
43
+ required: ['code', 'message', 'retryAfterSeconds'],
44
+ },
45
+ },
46
+ required: ['error'],
47
+ } as const
48
+
49
+ export function generateOpenAPI(
50
+ info: OpenAPIInfo,
51
+ routes: readonly RouteRegistration[],
52
+ options?: { rateLimited?: boolean },
53
+ ): OpenAPIDocument {
54
+ const paths: Record<string, Record<string, unknown>> = {}
55
+
56
+ for (const route of routes) {
57
+ const { method, path, handler } = route
58
+ const meta = handler.meta
59
+
60
+ const openApiPath = path.replace(/:(\w+)/g, '{$1}')
61
+
62
+ let pathItem = paths[openApiPath]
63
+ if (!pathItem) {
64
+ pathItem = {}
65
+ paths[openApiPath] = pathItem
66
+ }
67
+
68
+ const operation: Record<string, unknown> = {}
69
+
70
+ const parameters: Record<string, unknown>[] = []
71
+
72
+ if (meta.paramsSchema) {
73
+ const schema = toJsonSchema(meta.paramsSchema)
74
+ if ('properties' in schema) {
75
+ const props = schema['properties'] as Record<string, unknown>
76
+ for (const [name, propSchema] of Object.entries(props)) {
77
+ parameters.push({
78
+ name,
79
+ in: 'path',
80
+ required: true,
81
+ schema: propSchema,
82
+ })
83
+ }
84
+ }
85
+ }
86
+
87
+ if (meta.querySchema) {
88
+ const schema = toJsonSchema(meta.querySchema)
89
+ if ('properties' in schema) {
90
+ const props = schema['properties'] as Record<string, unknown>
91
+ const requiredFields = Array.isArray(schema['required']) ? schema['required'] as string[] : []
92
+ for (const [name, propSchema] of Object.entries(props)) {
93
+ parameters.push({
94
+ name,
95
+ in: 'query',
96
+ required: requiredFields.includes(name),
97
+ schema: propSchema,
98
+ })
99
+ }
100
+ }
101
+ }
102
+
103
+ if (parameters.length > 0) {
104
+ operation['parameters'] = parameters
105
+ }
106
+
107
+ if (meta.inputSchema) {
108
+ operation['requestBody'] = {
109
+ // Mirrors the runtime: a request with no body reaches the input as undefined.
110
+ required: !meta.inputSchema.safeParse(undefined).success,
111
+ content: {
112
+ 'application/json': {
113
+ schema: toJsonSchema(meta.inputSchema),
114
+ },
115
+ },
116
+ }
117
+ }
118
+
119
+ if (meta.upload) {
120
+ const binary = { type: 'string', format: 'binary' }
121
+ if (meta.upload.kind === 'binary') {
122
+ operation['requestBody'] = {
123
+ required: true,
124
+ content: Object.fromEntries(meta.upload.mediaTypes.map((type) => [type, { schema: binary }])),
125
+ }
126
+ } else {
127
+ const properties: Record<string, unknown> = { [meta.upload.file]: binary }
128
+ for (const field of meta.upload.fields ?? []) properties[field] = { type: 'string' }
129
+ operation['requestBody'] = {
130
+ required: true,
131
+ content: {
132
+ 'multipart/form-data': {
133
+ schema: { type: 'object', properties, required: [meta.upload.file] },
134
+ encoding: { [meta.upload.file]: { contentType: meta.upload.mediaTypes.join(', ') } },
135
+ },
136
+ },
137
+ }
138
+ }
139
+ }
140
+
141
+ const responses: Record<string, unknown> = {}
142
+ if (meta.outputSchemas.size > 0) {
143
+ for (const [status, outputSchema] of meta.outputSchemas) {
144
+ responses[String(status)] = {
145
+ description: status === 200 ? 'Successful response' : `${status} response`,
146
+ content: {
147
+ 'application/json': {
148
+ schema: toJsonSchema(outputSchema),
149
+ },
150
+ },
151
+ }
152
+ }
153
+ }
154
+ if (meta.file) {
155
+ responses[String(meta.file.status)] = {
156
+ description: meta.file.status === 200 ? 'Successful response' : `${meta.file.status} response`,
157
+ content: Object.fromEntries(meta.file.mediaTypes.map((type) => [type, { schema: { type: 'string', format: 'binary' } }])),
158
+ }
159
+ }
160
+ if (Object.keys(responses).length === 0) {
161
+ responses['200'] = { description: 'Successful response' }
162
+ }
163
+
164
+ if (meta.inputSchema || meta.querySchema || meta.paramsSchema) {
165
+ const declared = meta.outputSchemas.get(422)
166
+ responses['422'] = {
167
+ description: declared === undefined ? 'Validation failed' : '422 response',
168
+ content: {
169
+ 'application/json': {
170
+ schema: declared === undefined
171
+ ? VALIDATION_ERROR_SCHEMA
172
+ : { oneOf: [toJsonSchema(declared), VALIDATION_ERROR_SCHEMA] },
173
+ },
174
+ },
175
+ }
176
+ }
177
+
178
+ if (typeof meta.rateLimit === 'string' || (options?.rateLimited === true && meta.rateLimit !== false)) {
179
+ const declared = meta.outputSchemas.get(429)
180
+ responses['429'] = {
181
+ description: 'Too many requests',
182
+ content: {
183
+ 'application/json': {
184
+ schema: declared === undefined
185
+ ? TOO_MANY_REQUESTS_SCHEMA
186
+ : { oneOf: [toJsonSchema(declared), TOO_MANY_REQUESTS_SCHEMA] },
187
+ },
188
+ },
189
+ }
190
+ }
191
+
192
+ if (meta.requiresAuth) {
193
+ responses['401'] = { description: 'Unauthorized' }
194
+ }
195
+ if (meta.permission) {
196
+ responses['403'] = { description: 'Forbidden' }
197
+ }
198
+
199
+ operation['responses'] = responses
200
+
201
+ pathItem[method.toLowerCase()] = operation
202
+ }
203
+
204
+ return { openapi: '3.0.3', info, paths }
205
+ }
@@ -0,0 +1,2 @@
1
+ export { generateOpenAPI } from './generate.js'
2
+ export type { OpenAPIDocument, OpenAPIInfo } from './generate.js'
@@ -0,0 +1,7 @@
1
+ import { generateOpenAPI } from './generate.js'
2
+
3
+ /** OpenAPI spec generation namespace. */
4
+ export const openapi = {
5
+ /** Generates an OpenAPI 3.1 document from route registrations — schemas derived from fitting() input/output. */
6
+ generate: generateOpenAPI,
7
+ }
@@ -0,0 +1,13 @@
1
+ import type { BaseAuth } from '../auth/auth/types.js'
2
+
3
+ export function mockAuth<TAuth extends BaseAuth>(overrides: Partial<TAuth> & { userId?: string; tenantId?: string } = {}): TAuth {
4
+ return {
5
+ userId: 'test-user',
6
+ tenantId: 'test-tenant',
7
+ ...overrides,
8
+ } as TAuth
9
+ }
10
+
11
+ export function asTenant<TAuth extends BaseAuth>(tenantId: string, overrides: Partial<TAuth> = {}): TAuth {
12
+ return mockAuth<TAuth>({ tenantId, ...overrides } as Partial<TAuth> & { userId?: string; tenantId?: string })
13
+ }
@@ -0,0 +1,45 @@
1
+ import { beforeAll, afterAll, beforeEach, afterEach } from 'vitest'
2
+ import { loadEnvFiles } from '../core/config/env.js'
3
+ import { discoverForTestFork } from './discover-fork.js'
4
+ import { setupTestDatabases } from './setup.js'
5
+ import { beginTestIsolation, endTestIsolation, type TestContext } from './plugin.js'
6
+ import { _setTestState, teardownAfterAll, useTestDb } from './vitest.js'
7
+ import { logSlowPhases, SETUP_HOOK_TIMEOUT_MS } from './progress.js'
8
+ import { TEARDOWN_HOOK_TIMEOUT_MS } from '../trace/runtime/pending-writes.js'
9
+
10
+ loadEnvFiles()
11
+
12
+ let _testContext: TestContext | null = null
13
+
14
+ const instance = await discoverForTestFork()
15
+
16
+ _setTestState(instance)
17
+
18
+ const dbNames = instance.config.databaseNames()
19
+ if (dbNames.length === 1) {
20
+ ;(globalThis as Record<string, unknown>)['db'] = useTestDb()
21
+ }
22
+
23
+ // Setup narrates: any phase that holds long enough to explain a late hook gets
24
+ // a line, and setup fails by name when a phase stops advancing — never merely
25
+ // because a fixed wall clock elapsed while it queued behind peer forks (#380).
26
+ beforeAll(async () => {
27
+ await setupTestDatabases(instance, { onProgress: logSlowPhases() })
28
+ }, SETUP_HOOK_TIMEOUT_MS)
29
+
30
+ afterAll(async () => {
31
+ await teardownAfterAll(instance)
32
+ }, TEARDOWN_HOOK_TIMEOUT_MS)
33
+
34
+ beforeEach(async ({ task }) => {
35
+ _testContext = await beginTestIsolation(instance, task)
36
+ _setTestState(instance, _testContext)
37
+ })
38
+
39
+ afterEach(async () => {
40
+ if (_testContext) {
41
+ await endTestIsolation(instance, _testContext)
42
+ _testContext = null
43
+ _setTestState(instance, null)
44
+ }
45
+ }, TEARDOWN_HOOK_TIMEOUT_MS)
@@ -0,0 +1,91 @@
1
+ import type { Manifold } from '../core/pipework.js'
2
+ import { runInContext } from '../request/context/store.js'
3
+ import { createRequestContext, createJobContext } from '../request/context/index.js'
4
+ import { isTracingEnabled } from '../trace/entry/tracing-enabled.js'
5
+ import { openTrace } from '../trace/entry/open-trace.js'
6
+ import { parentTraceRef } from '../trace/entry/parent-trace.js'
7
+ import { finalizeTrace } from '../trace/entry/finalize-trace.js'
8
+ import { closeTrace } from '../trace/entry/close-trace.js'
9
+ import { _isInTestIsolation } from './vitest.js'
10
+
11
+ export interface WithContextOptions {
12
+ instance: Manifold
13
+ auth?: unknown
14
+ tenant?: string | null
15
+ requestId?: string
16
+ }
17
+
18
+ export function withContext<T>(options: WithContextOptions, fn: () => T): T {
19
+ if (_isInTestIsolation()) {
20
+ throw new Error(
21
+ '[pipework] withContext() creates a fresh context that bypasses test isolation.\n' +
22
+ ' Queries will not use the test transaction and will not roll back.\n\n' +
23
+ ' Use withFlow() instead — it preserves the test transaction:\n\n' +
24
+ " import { withFlow, asTenant } from 'pipework/test'\n\n" +
25
+ " withFlow({ auth: asTenant('tenant-1'), tenant: 'tenant-1' }, async () => {\n" +
26
+ ' // test transaction is preserved, rollback works\n' +
27
+ ' })',
28
+ )
29
+ }
30
+
31
+ const trace = isTracingEnabled(options.instance)
32
+ ? openTrace({
33
+ instance: options.instance,
34
+ entryKind: 'test',
35
+ parent: parentTraceRef(),
36
+ tenant: options.tenant ?? null,
37
+ auth: options.auth ?? null,
38
+ requestId: options.requestId ?? null,
39
+ sessionId: null,
40
+ })
41
+ : null
42
+
43
+ const ctx = createRequestContext({
44
+ instance: options.instance,
45
+ auth: options.auth ?? null,
46
+ tenant: options.tenant ?? null,
47
+ trace,
48
+ metadata: {
49
+ method: 'TEST',
50
+ path: '/test',
51
+ startedAt: Date.now(),
52
+ traceId: options.requestId ?? 'test-trace',
53
+ sessionId: null,
54
+ },
55
+ })
56
+
57
+ try {
58
+ return finalizeTrace(options.instance, trace, runInContext(ctx, fn))
59
+ } catch (err) {
60
+ void closeTrace(options.instance, trace, 'error')
61
+ throw err
62
+ }
63
+ }
64
+
65
+ export function withJobContext<T>(options: WithContextOptions, fn: () => T): T {
66
+ const trace = isTracingEnabled(options.instance)
67
+ ? openTrace({
68
+ instance: options.instance,
69
+ entryKind: 'test',
70
+ parent: parentTraceRef(),
71
+ tenant: options.tenant ?? null,
72
+ auth: options.auth ?? null,
73
+ requestId: options.requestId ?? null,
74
+ sessionId: null,
75
+ })
76
+ : null
77
+
78
+ const ctx = createJobContext({
79
+ instance: options.instance,
80
+ auth: options.auth ?? null,
81
+ tenant: options.tenant ?? null,
82
+ trace,
83
+ })
84
+
85
+ try {
86
+ return finalizeTrace(options.instance, trace, runInContext(ctx, fn))
87
+ } catch (err) {
88
+ void closeTrace(options.instance, trace, 'error')
89
+ throw err
90
+ }
91
+ }