pipework 0.252.0 → 0.253.0

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 (695) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/dist/infra/support/handle.d.ts +8 -1
  3. package/dist/infra/support/handle.d.ts.map +1 -1
  4. package/dist/infra/support/handle.js +9 -2
  5. package/dist/infra/support/handle.js.map +1 -1
  6. package/dist/infra/support/namespace.d.ts.map +1 -1
  7. package/package.json +4 -3
  8. package/src/async/jobs/abortable-delay.ts +18 -0
  9. package/src/async/jobs/claims.ts +52 -0
  10. package/src/async/jobs/cron.ts +179 -0
  11. package/src/async/jobs/errors.ts +56 -0
  12. package/src/async/jobs/exclusion.ts +117 -0
  13. package/src/async/jobs/execute.ts +219 -0
  14. package/src/async/jobs/index.ts +10 -0
  15. package/src/async/jobs/listener.ts +135 -0
  16. package/src/async/jobs/namespace.ts +20 -0
  17. package/src/async/jobs/queue-door.ts +44 -0
  18. package/src/async/jobs/queue.ts +1012 -0
  19. package/src/async/jobs/run-script.ts +67 -0
  20. package/src/async/jobs/scheduler.ts +218 -0
  21. package/src/async/jobs/schedules-table.ts +37 -0
  22. package/src/async/jobs/serialize-error.ts +48 -0
  23. package/src/async/jobs/table.ts +191 -0
  24. package/src/async/pipeline/define.ts +53 -0
  25. package/src/async/pipeline/errors.ts +35 -0
  26. package/src/async/pipeline/execute.ts +53 -0
  27. package/src/async/pipeline/index.ts +13 -0
  28. package/src/async/pipeline/namespace.ts +12 -0
  29. package/src/async/pipeline/resume.ts +67 -0
  30. package/src/async/pipeline/run-steps.ts +98 -0
  31. package/src/async/pipeline/types.ts +57 -0
  32. package/src/async/state-machine/define.ts +64 -0
  33. package/src/async/state-machine/errors.ts +29 -0
  34. package/src/async/state-machine/index.ts +3 -0
  35. package/src/async/state-machine/namespace.ts +12 -0
  36. package/src/async/state-machine/transition.ts +90 -0
  37. package/src/async/state-machine/types.ts +37 -0
  38. package/src/async/steps/errors.ts +31 -0
  39. package/src/async/steps/graph.ts +1214 -0
  40. package/src/async/steps/hash.ts +40 -0
  41. package/src/async/steps/index.ts +7 -0
  42. package/src/async/steps/namespace.ts +7 -0
  43. package/src/async/steps/table.ts +97 -0
  44. package/src/async/steps/types.ts +318 -0
  45. package/src/audit/chain/index.ts +9 -0
  46. package/src/audit/chain/install.ts +113 -0
  47. package/src/audit/chain/retire.ts +52 -0
  48. package/src/audit/chain/verify.ts +257 -0
  49. package/src/audit/query/cursor.ts +37 -0
  50. package/src/audit/query/index.ts +9 -0
  51. package/src/audit/query/query.ts +223 -0
  52. package/src/audit/schema/audit-record-fields.ts +68 -0
  53. package/src/audit/schema/audit-record.ts +23 -0
  54. package/src/auth/auth/audit.ts +31 -0
  55. package/src/auth/auth/chain.ts +84 -0
  56. package/src/auth/auth/cookie.ts +136 -0
  57. package/src/auth/auth/email-links.ts +311 -0
  58. package/src/auth/auth/guard.ts +128 -0
  59. package/src/auth/auth/index.ts +18 -0
  60. package/src/auth/auth/multi-org.ts +337 -0
  61. package/src/auth/auth/namespace.ts +34 -0
  62. package/src/auth/auth/parse-expiry.ts +13 -0
  63. package/src/auth/auth/passwords.ts +110 -0
  64. package/src/auth/auth/schema.ts +110 -0
  65. package/src/auth/auth/sessions.ts +565 -0
  66. package/src/auth/auth/sign-in.ts +156 -0
  67. package/src/auth/auth/signing-keys.ts +69 -0
  68. package/src/auth/auth/types.ts +26 -0
  69. package/src/auth/auth/uuid.ts +10 -0
  70. package/src/auth/rbac/cache.ts +55 -0
  71. package/src/auth/rbac/check.ts +30 -0
  72. package/src/auth/rbac/create.ts +35 -0
  73. package/src/auth/rbac/index.ts +5 -0
  74. package/src/auth/rbac/middleware.ts +20 -0
  75. package/src/auth/rbac/namespace.ts +15 -0
  76. package/src/auth/rbac/resolve.ts +31 -0
  77. package/src/auth/rbac/schema.sql +31 -0
  78. package/src/auth/rbac/seed.ts +67 -0
  79. package/src/auth/rbac/tables.ts +32 -0
  80. package/src/auth/rbac/types.ts +65 -0
  81. package/src/auth/tenant/extract.ts +33 -0
  82. package/src/auth/tenant/in-tenant.ts +49 -0
  83. package/src/auth/tenant/index.ts +7 -0
  84. package/src/auth/tenant/namespace.ts +23 -0
  85. package/src/auth/tenant/propagate.ts +20 -0
  86. package/src/auth/tenant/rls.ts +120 -0
  87. package/src/auth/tenant/scope.ts +33 -0
  88. package/src/auth/tenant/scoped-db.ts +581 -0
  89. package/src/auth/tenant/types.ts +14 -0
  90. package/src/auth/tenant/validate.ts +27 -0
  91. package/src/cli/commands/add.ts +158 -0
  92. package/src/cli/commands/alerts.ts +98 -0
  93. package/src/cli/commands/check.ts +1418 -0
  94. package/src/cli/commands/codemod.ts +78 -0
  95. package/src/cli/commands/db.ts +356 -0
  96. package/src/cli/commands/dev.ts +101 -0
  97. package/src/cli/commands/generate.ts +75 -0
  98. package/src/cli/commands/init.ts +36 -0
  99. package/src/cli/commands/install.ts +28 -0
  100. package/src/cli/commands/lint.ts +62 -0
  101. package/src/cli/commands/migrate.ts +132 -0
  102. package/src/cli/commands/openapi.ts +63 -0
  103. package/src/cli/commands/remove.ts +72 -0
  104. package/src/cli/commands/run.ts +50 -0
  105. package/src/cli/commands/scope.ts +54 -0
  106. package/src/cli/commands/serve.ts +68 -0
  107. package/src/cli/commands/shard.ts +31 -0
  108. package/src/cli/commands/shared/config-pipework-version.ts +28 -0
  109. package/src/cli/commands/shared/find-config.ts +22 -0
  110. package/src/cli/commands/shared/load-config.ts +35 -0
  111. package/src/cli/commands/shared/pnpm-install.ts +95 -0
  112. package/src/cli/commands/shared/report-removed.ts +8 -0
  113. package/src/cli/commands/sync.ts +32 -0
  114. package/src/cli/commands/test.ts +307 -0
  115. package/src/cli/commands/upgrade.ts +48 -0
  116. package/src/cli/dev-bootstrap.ts +24 -0
  117. package/src/cli/errors.ts +10 -0
  118. package/src/cli/index.ts +303 -0
  119. package/src/cli/output.ts +17 -0
  120. package/src/cli/port.ts +15 -0
  121. package/src/cli/surface-kinds.ts +20 -0
  122. package/src/client/index.ts +4 -0
  123. package/src/client/poll.ts +130 -0
  124. package/src/client/report-fault.ts +119 -0
  125. package/src/core/config/app-pool.ts +19 -0
  126. package/src/core/config/aws-env.ts +29 -0
  127. package/src/core/config/base-dir.ts +24 -0
  128. package/src/core/config/discover.ts +64 -0
  129. package/src/core/config/env-resolve.ts +87 -0
  130. package/src/core/config/env-types.ts +116 -0
  131. package/src/core/config/env.ts +55 -0
  132. package/src/core/config/errors.ts +9 -0
  133. package/src/core/config/import-config.ts +40 -0
  134. package/src/core/config/index.ts +8 -0
  135. package/src/core/config/load.ts +417 -0
  136. package/src/core/config/namespace.ts +14 -0
  137. package/src/core/config/resolve.ts +195 -0
  138. package/src/core/config/schema.ts +488 -0
  139. package/src/core/config/statement-observer.ts +48 -0
  140. package/src/core/config/ts-inline-hooks.ts +75 -0
  141. package/src/core/config/ts-register.ts +4 -0
  142. package/src/core/config/ts-resolve-hooks.ts +101 -0
  143. package/src/core/errors/base.ts +49 -0
  144. package/src/core/errors/http.ts +146 -0
  145. package/src/core/errors/index.ts +2 -0
  146. package/src/core/errors/namespace.ts +31 -0
  147. package/src/core/lifecycle/drain.ts +33 -0
  148. package/src/core/lifecycle/health.ts +72 -0
  149. package/src/core/lifecycle/index.ts +4 -0
  150. package/src/core/lifecycle/namespace.ts +7 -0
  151. package/src/core/lifecycle/startup.ts +102 -0
  152. package/src/core/lifecycle/types.ts +36 -0
  153. package/src/core/logging/context.ts +60 -0
  154. package/src/core/logging/error-chain.ts +68 -0
  155. package/src/core/logging/index.ts +5 -0
  156. package/src/core/logging/logger.ts +65 -0
  157. package/src/core/logging/namespace.ts +16 -0
  158. package/src/core/logging/proxy.ts +22 -0
  159. package/src/core/logging/redact-match.ts +24 -0
  160. package/src/core/logging/types.ts +47 -0
  161. package/src/core/pipework.ts +566 -0
  162. package/src/core/runtime/broker.ts +69 -0
  163. package/src/core/surface/http.ts +133 -0
  164. package/src/core/surface/index.ts +11 -0
  165. package/src/core/surface/job-timeout.ts +40 -0
  166. package/src/core/surface/namespace.ts +13 -0
  167. package/src/core/surface/script.ts +33 -0
  168. package/src/core/surface/start.ts +142 -0
  169. package/src/core/surface/types.ts +225 -0
  170. package/src/core/surface/validate.ts +88 -0
  171. package/src/core/surface/worker.ts +528 -0
  172. package/src/core/validation/connectivity.ts +49 -0
  173. package/src/core/validation/domain.ts +99 -0
  174. package/src/core/validation/index.ts +5 -0
  175. package/src/core/validation/queue-tables.ts +312 -0
  176. package/src/core/validation/rls-role.ts +138 -0
  177. package/src/core/validation/startup.ts +51 -0
  178. package/src/core/validation/surfaces.ts +65 -0
  179. package/src/core/validation/types.ts +25 -0
  180. package/src/data/db/advisory-lock.ts +55 -0
  181. package/src/data/db/aggregates.ts +172 -0
  182. package/src/data/db/array-elements.ts +111 -0
  183. package/src/data/db/bulk-set.ts +53 -0
  184. package/src/data/db/col/index.ts +14 -0
  185. package/src/data/db/composed.ts +53 -0
  186. package/src/data/db/db.ts +276 -0
  187. package/src/data/db/excluded.ts +16 -0
  188. package/src/data/db/expressions.ts +473 -0
  189. package/src/data/db/filter.ts +90 -0
  190. package/src/data/db/function-source.ts +114 -0
  191. package/src/data/db/identifiers.ts +16 -0
  192. package/src/data/db/idx/index.ts +9 -0
  193. package/src/data/db/in-list.ts +49 -0
  194. package/src/data/db/index.ts +9 -0
  195. package/src/data/db/insert-split.ts +137 -0
  196. package/src/data/db/instrumentation.ts +82 -0
  197. package/src/data/db/maintenance.ts +28 -0
  198. package/src/data/db/namespace.ts +107 -0
  199. package/src/data/db/owner-labels.ts +9 -0
  200. package/src/data/db/pool.ts +187 -0
  201. package/src/data/db/query-label.ts +13 -0
  202. package/src/data/db/query-watch.ts +116 -0
  203. package/src/data/db/raw-client-warning.ts +37 -0
  204. package/src/data/db/readonly.ts +23 -0
  205. package/src/data/db/reservation.ts +151 -0
  206. package/src/data/db/schema-features.ts +14 -0
  207. package/src/data/db/serializable.ts +83 -0
  208. package/src/data/db/sql.ts +1 -0
  209. package/src/data/db/timeouts.ts +76 -0
  210. package/src/data/db/types.ts +18 -0
  211. package/src/data/db/unbound.ts +33 -0
  212. package/src/data/db/values.ts +29 -0
  213. package/src/data/domain/brand.ts +15 -0
  214. package/src/data/domain/build-factory.ts +193 -0
  215. package/src/data/domain/build-schema.ts +161 -0
  216. package/src/data/domain/build-table.ts +355 -0
  217. package/src/data/domain/define.ts +239 -0
  218. package/src/data/domain/external-registry.ts +34 -0
  219. package/src/data/domain/external.ts +77 -0
  220. package/src/data/domain/field.ts +279 -0
  221. package/src/data/domain/index.ts +13 -0
  222. package/src/data/domain/naming.ts +6 -0
  223. package/src/data/domain/projections.ts +57 -0
  224. package/src/data/domain/trace-safe-projection.ts +31 -0
  225. package/src/data/domain/traits.ts +108 -0
  226. package/src/data/domain/types.ts +289 -0
  227. package/src/data/jsonb/composed.ts +18 -0
  228. package/src/data/jsonb/index.ts +1 -0
  229. package/src/data/jsonb/namespace.ts +30 -0
  230. package/src/data/jsonb/query.ts +86 -0
  231. package/src/data/migrate/append-only-generate.ts +47 -0
  232. package/src/data/migrate/applied.ts +47 -0
  233. package/src/data/migrate/apply.ts +309 -0
  234. package/src/data/migrate/check.ts +119 -0
  235. package/src/data/migrate/diff.ts +357 -0
  236. package/src/data/migrate/door-generate.ts +130 -0
  237. package/src/data/migrate/framework-rls.ts +58 -0
  238. package/src/data/migrate/generate.ts +201 -0
  239. package/src/data/migrate/history.ts +215 -0
  240. package/src/data/migrate/index.ts +9 -0
  241. package/src/data/migrate/internal-definitions.ts +175 -0
  242. package/src/data/migrate/post-process.ts +73 -0
  243. package/src/data/migrate/pragma.ts +73 -0
  244. package/src/data/migrate/provision-framework.ts +147 -0
  245. package/src/data/migrate/rls-generate.ts +61 -0
  246. package/src/data/migrate/run.ts +124 -0
  247. package/src/data/migrate/safety.ts +167 -0
  248. package/src/data/migrate/snapshot.ts +368 -0
  249. package/src/data/migrate/sql-emit.ts +392 -0
  250. package/src/data/migrate/statements.ts +312 -0
  251. package/src/data/query/alias.ts +126 -0
  252. package/src/data/query/batch.ts +8 -0
  253. package/src/data/query/cache/core/cache.ts +78 -0
  254. package/src/data/query/cache/core/index.ts +1 -0
  255. package/src/data/query/cache/core/types.ts +29 -0
  256. package/src/data/query/cache/readme.md +230 -0
  257. package/src/data/query/casing.ts +76 -0
  258. package/src/data/query/collation.ts +11 -0
  259. package/src/data/query/column-builder.ts +370 -0
  260. package/src/data/query/column.ts +150 -0
  261. package/src/data/query/entity.ts +42 -0
  262. package/src/data/query/errors.ts +33 -0
  263. package/src/data/query/index.ts +14 -0
  264. package/src/data/query/logger.ts +47 -0
  265. package/src/data/query/operations.ts +45 -0
  266. package/src/data/query/pg-core/alias.ts +12 -0
  267. package/src/data/query/pg-core/checks.ts +32 -0
  268. package/src/data/query/pg-core/columns/all.ts +71 -0
  269. package/src/data/query/pg-core/columns/bigint.ts +110 -0
  270. package/src/data/query/pg-core/columns/bigserial.ts +128 -0
  271. package/src/data/query/pg-core/columns/boolean.ts +43 -0
  272. package/src/data/query/pg-core/columns/char.ts +85 -0
  273. package/src/data/query/pg-core/columns/cidr.ts +43 -0
  274. package/src/data/query/pg-core/columns/common.ts +366 -0
  275. package/src/data/query/pg-core/columns/custom.ts +232 -0
  276. package/src/data/query/pg-core/columns/date.common.ts +15 -0
  277. package/src/data/query/pg-core/columns/date.ts +112 -0
  278. package/src/data/query/pg-core/columns/double-precision.ts +55 -0
  279. package/src/data/query/pg-core/columns/enum.ts +201 -0
  280. package/src/data/query/pg-core/columns/index.ts +35 -0
  281. package/src/data/query/pg-core/columns/inet.ts +43 -0
  282. package/src/data/query/pg-core/columns/int.common.ts +57 -0
  283. package/src/data/query/pg-core/columns/integer.ts +53 -0
  284. package/src/data/query/pg-core/columns/interval.ts +83 -0
  285. package/src/data/query/pg-core/columns/json.ts +58 -0
  286. package/src/data/query/pg-core/columns/jsonb.ts +56 -0
  287. package/src/data/query/pg-core/columns/line.ts +117 -0
  288. package/src/data/query/pg-core/columns/macaddr.ts +43 -0
  289. package/src/data/query/pg-core/columns/macaddr8.ts +43 -0
  290. package/src/data/query/pg-core/columns/numeric.ts +227 -0
  291. package/src/data/query/pg-core/columns/point.ts +127 -0
  292. package/src/data/query/pg-core/columns/postgis_extension/geometry.ts +119 -0
  293. package/src/data/query/pg-core/columns/postgis_extension/utils.ts +47 -0
  294. package/src/data/query/pg-core/columns/real.ts +58 -0
  295. package/src/data/query/pg-core/columns/serial.ts +55 -0
  296. package/src/data/query/pg-core/columns/smallint.ts +53 -0
  297. package/src/data/query/pg-core/columns/smallserial.ts +60 -0
  298. package/src/data/query/pg-core/columns/text.ts +67 -0
  299. package/src/data/query/pg-core/columns/time.ts +72 -0
  300. package/src/data/query/pg-core/columns/timestamp.ts +163 -0
  301. package/src/data/query/pg-core/columns/uuid.ts +51 -0
  302. package/src/data/query/pg-core/columns/varchar.ts +89 -0
  303. package/src/data/query/pg-core/columns/vector_extension/bit.ts +68 -0
  304. package/src/data/query/pg-core/columns/vector_extension/halfvec.ts +80 -0
  305. package/src/data/query/pg-core/columns/vector_extension/sparsevec.ts +67 -0
  306. package/src/data/query/pg-core/columns/vector_extension/vector.ts +80 -0
  307. package/src/data/query/pg-core/db.ts +674 -0
  308. package/src/data/query/pg-core/dialect.ts +1404 -0
  309. package/src/data/query/pg-core/expressions.ts +25 -0
  310. package/src/data/query/pg-core/foreign-keys.ts +130 -0
  311. package/src/data/query/pg-core/index.ts +21 -0
  312. package/src/data/query/pg-core/indexes.ts +276 -0
  313. package/src/data/query/pg-core/policies.ts +56 -0
  314. package/src/data/query/pg-core/primary-keys.ts +62 -0
  315. package/src/data/query/pg-core/query-builders/count.ts +86 -0
  316. package/src/data/query/pg-core/query-builders/delete.ts +293 -0
  317. package/src/data/query/pg-core/query-builders/index.ts +7 -0
  318. package/src/data/query/pg-core/query-builders/insert.ts +452 -0
  319. package/src/data/query/pg-core/query-builders/query-builder.ts +123 -0
  320. package/src/data/query/pg-core/query-builders/query.ts +157 -0
  321. package/src/data/query/pg-core/query-builders/raw.ts +49 -0
  322. package/src/data/query/pg-core/query-builders/refresh-materialized-view.ts +108 -0
  323. package/src/data/query/pg-core/query-builders/select.ts +1346 -0
  324. package/src/data/query/pg-core/query-builders/select.types.ts +437 -0
  325. package/src/data/query/pg-core/query-builders/update.ts +626 -0
  326. package/src/data/query/pg-core/roles.ts +41 -0
  327. package/src/data/query/pg-core/schema.ts +72 -0
  328. package/src/data/query/pg-core/sequence.ts +41 -0
  329. package/src/data/query/pg-core/session.ts +292 -0
  330. package/src/data/query/pg-core/subquery.ts +46 -0
  331. package/src/data/query/pg-core/table.ts +252 -0
  332. package/src/data/query/pg-core/unique-constraint.ts +73 -0
  333. package/src/data/query/pg-core/utils/array.ts +95 -0
  334. package/src/data/query/pg-core/utils/index.ts +1 -0
  335. package/src/data/query/pg-core/utils.ts +104 -0
  336. package/src/data/query/pg-core/view-base.ts +14 -0
  337. package/src/data/query/pg-core/view-common.ts +1 -0
  338. package/src/data/query/pg-core/view.ts +435 -0
  339. package/src/data/query/pg-core/with-builder.ts +41 -0
  340. package/src/data/query/postgres-js/README.md +46 -0
  341. package/src/data/query/postgres-js/array-parser.ts +45 -0
  342. package/src/data/query/postgres-js/driver-error-boundary.ts +149 -0
  343. package/src/data/query/postgres-js/driver.ts +163 -0
  344. package/src/data/query/postgres-js/index.ts +2 -0
  345. package/src/data/query/postgres-js/session.ts +234 -0
  346. package/src/data/query/primary-key.ts +11 -0
  347. package/src/data/query/query-builders/query-builder.ts +19 -0
  348. package/src/data/query/query-builders/select.types.ts +175 -0
  349. package/src/data/query/query-promise.ts +35 -0
  350. package/src/data/query/relations.ts +725 -0
  351. package/src/data/query/runnable-query.ts +12 -0
  352. package/src/data/query/selection-proxy.ts +121 -0
  353. package/src/data/query/session.ts +8 -0
  354. package/src/data/query/sql/expressions/conditions.ts +741 -0
  355. package/src/data/query/sql/expressions/index.ts +2 -0
  356. package/src/data/query/sql/expressions/select.ts +45 -0
  357. package/src/data/query/sql/functions/aggregate.ts +129 -0
  358. package/src/data/query/sql/functions/index.ts +2 -0
  359. package/src/data/query/sql/functions/vector.ts +178 -0
  360. package/src/data/query/sql/index.ts +3 -0
  361. package/src/data/query/sql/sql.ts +794 -0
  362. package/src/data/query/subquery.ts +53 -0
  363. package/src/data/query/table.ts +208 -0
  364. package/src/data/query/table.utils.ts +2 -0
  365. package/src/data/query/tracing-utils.ts +3 -0
  366. package/src/data/query/tracing.ts +11 -0
  367. package/src/data/query/utils.ts +357 -0
  368. package/src/data/query/version.ts +4 -0
  369. package/src/data/query/view-common.ts +1 -0
  370. package/src/data/schema/branded.ts +13 -0
  371. package/src/data/schema/generated/coerce.ts +5 -0
  372. package/src/data/schema/generated/combinator.ts +6 -0
  373. package/src/data/schema/generated/encoding.ts +5 -0
  374. package/src/data/schema/generated/error.ts +6 -0
  375. package/src/data/schema/generated/format.ts +5 -0
  376. package/src/data/schema/generated/type-constructor.ts +10 -0
  377. package/src/data/schema/generated/type.ts +6 -0
  378. package/src/data/schema/index.ts +6 -0
  379. package/src/data/schema/json-schema.ts +33 -0
  380. package/src/data/schema/namespace.ts +55 -0
  381. package/src/data/schema/parse.ts +39 -0
  382. package/src/data/schema/types.ts +39 -0
  383. package/src/data/temporal/append-only.ts +134 -0
  384. package/src/data/temporal/close.ts +27 -0
  385. package/src/data/temporal/columns.ts +10 -0
  386. package/src/data/temporal/definition-queries.ts +70 -0
  387. package/src/data/temporal/errors.ts +10 -0
  388. package/src/data/temporal/get-current.ts +22 -0
  389. package/src/data/temporal/index.ts +9 -0
  390. package/src/data/temporal/namespace.ts +36 -0
  391. package/src/data/temporal/query.ts +25 -0
  392. package/src/data/temporal/resolve.ts +17 -0
  393. package/src/data/temporal/revise.ts +67 -0
  394. package/src/data/temporal/types.ts +22 -0
  395. package/src/data/valve/artifact.ts +33 -0
  396. package/src/data/valve/contract-diff.ts +21 -0
  397. package/src/data/valve/cross.ts +57 -0
  398. package/src/data/valve/crossing-ledger.ts +50 -0
  399. package/src/data/valve/decision.ts +44 -0
  400. package/src/data/valve/durable-crossing.ts +52 -0
  401. package/src/data/valve/execute-crossing.ts +77 -0
  402. package/src/data/valve/faucet.ts +56 -0
  403. package/src/data/valve/gate.ts +114 -0
  404. package/src/data/valve/graph.ts +77 -0
  405. package/src/data/valve/index.ts +53 -0
  406. package/src/data/valve/namespace.ts +57 -0
  407. package/src/data/valve/registry.ts +50 -0
  408. package/src/data/valve/schema/valve-crossing-fields.ts +23 -0
  409. package/src/data/valve/schema/valve-crossing.ts +9 -0
  410. package/src/data/valve/supply.ts +154 -0
  411. package/src/data/valve/types.ts +143 -0
  412. package/src/data/vector/columns.ts +27 -0
  413. package/src/data/vector/index.ts +4 -0
  414. package/src/data/vector/namespace.ts +33 -0
  415. package/src/data/vector/query.ts +85 -0
  416. package/src/data/vector/validate.ts +16 -0
  417. package/src/data/write/audit/actor.ts +15 -0
  418. package/src/data/write/audit/audit-columns.ts +18 -0
  419. package/src/data/write/audit/audit-select.ts +111 -0
  420. package/src/data/write/audit/bound-transaction.ts +14 -0
  421. package/src/data/write/audit/cte-compile.ts +32 -0
  422. package/src/data/write/audit/decode-returning.ts +47 -0
  423. package/src/data/write/audit/final-select.ts +43 -0
  424. package/src/data/write/audit/infra-audit.ts +36 -0
  425. package/src/data/write/audit/primary-key.ts +65 -0
  426. package/src/data/write/audit/projection.ts +41 -0
  427. package/src/data/write/audit/read-touches.ts +86 -0
  428. package/src/data/write/audit/touch-match.ts +78 -0
  429. package/src/data/write/audit/verb-delete.ts +62 -0
  430. package/src/data/write/audit/verb-insert.ts +36 -0
  431. package/src/data/write/audit/verb-select.ts +47 -0
  432. package/src/data/write/audit/verb-update.ts +60 -0
  433. package/src/data/write/audit/verb-upsert.ts +104 -0
  434. package/src/data/write/conflict.ts +43 -0
  435. package/src/data/write/delete.ts +38 -0
  436. package/src/data/write/execute.ts +243 -0
  437. package/src/data/write/index.ts +11 -0
  438. package/src/data/write/insert.ts +63 -0
  439. package/src/data/write/state.ts +43 -0
  440. package/src/data/write/update.ts +54 -0
  441. package/src/data/write/upsert.ts +58 -0
  442. package/src/data/write/values.ts +19 -0
  443. package/src/index.ts +230 -0
  444. package/src/infra/access-links/code.ts +70 -0
  445. package/src/infra/access-links/handle.ts +892 -0
  446. package/src/infra/access-links/index.ts +9 -0
  447. package/src/infra/access-links/namespace.ts +30 -0
  448. package/src/infra/access-links/person-check.ts +177 -0
  449. package/src/infra/access-links/schema.ts +71 -0
  450. package/src/infra/alerts/async-sources.ts +231 -0
  451. package/src/infra/alerts/audit.ts +36 -0
  452. package/src/infra/alerts/beat.ts +123 -0
  453. package/src/infra/alerts/client-faults.ts +111 -0
  454. package/src/infra/alerts/faults.ts +156 -0
  455. package/src/infra/alerts/handle.ts +778 -0
  456. package/src/infra/alerts/http.ts +192 -0
  457. package/src/infra/alerts/index.ts +12 -0
  458. package/src/infra/alerts/infra-sources.ts +180 -0
  459. package/src/infra/alerts/namespace.ts +81 -0
  460. package/src/infra/alerts/redact.ts +21 -0
  461. package/src/infra/alerts/request-faults.ts +41 -0
  462. package/src/infra/alerts/schema.ts +124 -0
  463. package/src/infra/alerts/sinks.ts +125 -0
  464. package/src/infra/alerts/source-options.ts +26 -0
  465. package/src/infra/alerts/sources.ts +503 -0
  466. package/src/infra/alerts/subsystem-sources.ts +137 -0
  467. package/src/infra/audit/emit.ts +87 -0
  468. package/src/infra/audit/index.ts +2 -0
  469. package/src/infra/audit/namespace.ts +22 -0
  470. package/src/infra/audit/schema.sql +35 -0
  471. package/src/infra/audit/types.ts +18 -0
  472. package/src/infra/aws/credentials.ts +147 -0
  473. package/src/infra/aws/sigv4.ts +140 -0
  474. package/src/infra/cache/create.ts +58 -0
  475. package/src/infra/cache/entry.ts +81 -0
  476. package/src/infra/cache/index.ts +4 -0
  477. package/src/infra/cache/namespace.ts +10 -0
  478. package/src/infra/cache/tenant.ts +93 -0
  479. package/src/infra/cache/types.ts +24 -0
  480. package/src/infra/crawl/audit.ts +35 -0
  481. package/src/infra/crawl/errors.ts +25 -0
  482. package/src/infra/crawl/handle.ts +693 -0
  483. package/src/infra/crawl/index.ts +9 -0
  484. package/src/infra/crawl/links.ts +160 -0
  485. package/src/infra/crawl/namespace.ts +36 -0
  486. package/src/infra/crawl/robots.ts +100 -0
  487. package/src/infra/crawl/schema.ts +89 -0
  488. package/src/infra/credit-line/handle.ts +883 -0
  489. package/src/infra/credit-line/index.ts +6 -0
  490. package/src/infra/credit-line/namespace.ts +7 -0
  491. package/src/infra/credit-line/schema.ts +96 -0
  492. package/src/infra/credits/handle.ts +706 -0
  493. package/src/infra/credits/index.ts +5 -0
  494. package/src/infra/credits/namespace.ts +7 -0
  495. package/src/infra/credits/schema.ts +83 -0
  496. package/src/infra/email/audit.ts +36 -0
  497. package/src/infra/email/inbound.ts +622 -0
  498. package/src/infra/email/index.ts +11 -0
  499. package/src/infra/email/mail.ts +517 -0
  500. package/src/infra/email/mime.ts +447 -0
  501. package/src/infra/email/namespace.ts +22 -0
  502. package/src/infra/email/providers.ts +111 -0
  503. package/src/infra/email/schema.ts +96 -0
  504. package/src/infra/email/sns.ts +200 -0
  505. package/src/infra/email/unsubscribe.ts +44 -0
  506. package/src/infra/fetch/address.ts +88 -0
  507. package/src/infra/fetch/connect-retry.ts +87 -0
  508. package/src/infra/fetch/handle.ts +329 -0
  509. package/src/infra/fetch/index.ts +7 -0
  510. package/src/infra/fetch/namespace.ts +70 -0
  511. package/src/infra/idempotency/index.ts +3 -0
  512. package/src/infra/idempotency/middleware.ts +87 -0
  513. package/src/infra/idempotency/store.ts +92 -0
  514. package/src/infra/idempotency/types.ts +30 -0
  515. package/src/infra/join-requests/handle.ts +312 -0
  516. package/src/infra/join-requests/index.ts +2 -0
  517. package/src/infra/join-requests/namespace.ts +7 -0
  518. package/src/infra/join-requests/schema.ts +33 -0
  519. package/src/infra/outbox/core.ts +165 -0
  520. package/src/infra/quota/errors.ts +32 -0
  521. package/src/infra/quota/handle.ts +215 -0
  522. package/src/infra/quota/index.ts +5 -0
  523. package/src/infra/quota/namespace.ts +7 -0
  524. package/src/infra/quota/schema.ts +15 -0
  525. package/src/infra/storage/backend.ts +34 -0
  526. package/src/infra/storage/handle.ts +513 -0
  527. package/src/infra/storage/index.ts +9 -0
  528. package/src/infra/storage/local.ts +104 -0
  529. package/src/infra/storage/namespace.ts +13 -0
  530. package/src/infra/storage/s3.ts +257 -0
  531. package/src/infra/storage/schema.ts +24 -0
  532. package/src/infra/support/handle.ts +869 -0
  533. package/src/infra/support/http.ts +229 -0
  534. package/src/infra/support/index.ts +7 -0
  535. package/src/infra/support/namespace.ts +12 -0
  536. package/src/infra/support/schema.ts +96 -0
  537. package/src/infra/webhook/inbound.ts +45 -0
  538. package/src/infra/webhook/index.ts +5 -0
  539. package/src/infra/webhook/namespace.ts +24 -0
  540. package/src/infra/webhook/outbound.ts +315 -0
  541. package/src/infra/webhook/sign.ts +32 -0
  542. package/src/infra/webhook/verify.ts +82 -0
  543. package/src/invariants/assert.ts +26 -0
  544. package/src/invariants/index.ts +1 -0
  545. package/src/lint/config.ts +56 -0
  546. package/src/lint/doctrine.ts +163 -0
  547. package/src/lint/include.ts +97 -0
  548. package/src/lint/index.ts +3 -0
  549. package/src/lint/preset.ts +10 -0
  550. package/src/lint/resolver.ts +218 -0
  551. package/src/lint/rules/no-reexports.ts +46 -0
  552. package/src/request/behavior/audited.ts +85 -0
  553. package/src/request/behavior/cached.ts +62 -0
  554. package/src/request/behavior/compose.ts +28 -0
  555. package/src/request/behavior/index.ts +5 -0
  556. package/src/request/behavior/namespace.ts +16 -0
  557. package/src/request/behavior/types.ts +26 -0
  558. package/src/request/behavior/versioned.ts +54 -0
  559. package/src/request/context/create.ts +103 -0
  560. package/src/request/context/guard.ts +49 -0
  561. package/src/request/context/index.ts +4 -0
  562. package/src/request/context/run-in-transaction.ts +200 -0
  563. package/src/request/context/store.ts +28 -0
  564. package/src/request/context/types.ts +50 -0
  565. package/src/request/di/builder.ts +535 -0
  566. package/src/request/di/index.ts +8 -0
  567. package/src/request/di/resolve.ts +271 -0
  568. package/src/request/di/types.ts +262 -0
  569. package/src/request/fitting/namespace.ts +7 -0
  570. package/src/request/fixture/batch.ts +71 -0
  571. package/src/request/fixture/builder.ts +26 -0
  572. package/src/request/fixture/errors.ts +10 -0
  573. package/src/request/fixture/index.ts +8 -0
  574. package/src/request/fixture/namespace.ts +23 -0
  575. package/src/request/fixture/paginate.ts +44 -0
  576. package/src/request/fixture/to-handlers.ts +260 -0
  577. package/src/request/fixture/types.ts +79 -0
  578. package/src/request/flow/act-as.ts +78 -0
  579. package/src/request/flow/namespace.ts +17 -0
  580. package/src/request/http/error-step.ts +42 -0
  581. package/src/request/http/file.ts +141 -0
  582. package/src/request/http/index.ts +4 -0
  583. package/src/request/http/middleware.ts +141 -0
  584. package/src/request/http/namespace.ts +5 -0
  585. package/src/request/http/rate-limit-classes.ts +170 -0
  586. package/src/request/http/response.ts +82 -0
  587. package/src/request/http/server.ts +916 -0
  588. package/src/request/http/sniff.ts +261 -0
  589. package/src/request/http/types.ts +250 -0
  590. package/src/request/http/upload.ts +284 -0
  591. package/src/request/internal/text.ts +6 -0
  592. package/src/request/openapi/generate.ts +205 -0
  593. package/src/request/openapi/index.ts +2 -0
  594. package/src/request/openapi/namespace.ts +7 -0
  595. package/src/test/auth.ts +13 -0
  596. package/src/test/auto-setup.ts +45 -0
  597. package/src/test/context.ts +91 -0
  598. package/src/test/crawl-fixture.ts +157 -0
  599. package/src/test/discover-fork.ts +17 -0
  600. package/src/test/fingerprint.ts +135 -0
  601. package/src/test/globals.d.ts +7 -0
  602. package/src/test/index.ts +19 -0
  603. package/src/test/namespace.ts +87 -0
  604. package/src/test/plugin.ts +189 -0
  605. package/src/test/progress.ts +185 -0
  606. package/src/test/reap-after-run.ts +28 -0
  607. package/src/test/reap.ts +286 -0
  608. package/src/test/reset.ts +253 -0
  609. package/src/test/savepoints.ts +172 -0
  610. package/src/test/setup.ts +1422 -0
  611. package/src/test/system-channel.ts +50 -0
  612. package/src/test/vitest-plugin.ts +142 -0
  613. package/src/test/vitest-workspace.ts +507 -0
  614. package/src/test/vitest.ts +333 -0
  615. package/src/test/watch.ts +105 -0
  616. package/src/trace/capture/bounded-serializer.ts +146 -0
  617. package/src/trace/cardinality/analyze.ts +173 -0
  618. package/src/trace/cardinality/branch-context.ts +214 -0
  619. package/src/trace/cardinality/check.ts +33 -0
  620. package/src/trace/cardinality/classify-edge.ts +64 -0
  621. package/src/trace/cardinality/structural-max.ts +107 -0
  622. package/src/trace/check/coverage-check.ts +188 -0
  623. package/src/trace/check/flow-check.ts +103 -0
  624. package/src/trace/check/flow-step-site.ts +234 -0
  625. package/src/trace/check/io-pattern.ts +116 -0
  626. package/src/trace/codemod/add-cardinality.ts +152 -0
  627. package/src/trace/codemod/run.ts +80 -0
  628. package/src/trace/entry/child-trace.ts +21 -0
  629. package/src/trace/entry/close-trace.ts +87 -0
  630. package/src/trace/entry/finalize-trace.ts +28 -0
  631. package/src/trace/entry/open-trace.ts +138 -0
  632. package/src/trace/entry/parent-trace.ts +20 -0
  633. package/src/trace/entry/parse-traceparent.ts +39 -0
  634. package/src/trace/entry/process-start.ts +38 -0
  635. package/src/trace/entry/trace-context.ts +26 -0
  636. package/src/trace/entry/tracing-enabled.ts +10 -0
  637. package/src/trace/external/record.ts +195 -0
  638. package/src/trace/external/report.ts +66 -0
  639. package/src/trace/namespace.ts +19 -0
  640. package/src/trace/partition/day.ts +47 -0
  641. package/src/trace/partition/index.ts +14 -0
  642. package/src/trace/partition/partition-name.ts +23 -0
  643. package/src/trace/partition/plan.ts +82 -0
  644. package/src/trace/partition/schedule.ts +26 -0
  645. package/src/trace/partition/startup-hook.ts +39 -0
  646. package/src/trace/partition/sync.ts +154 -0
  647. package/src/trace/partition/tables.ts +53 -0
  648. package/src/trace/read/tree.ts +83 -0
  649. package/src/trace/retain/copy-on-error.ts +76 -0
  650. package/src/trace/runtime/expire-stale-traces.ts +54 -0
  651. package/src/trace/runtime/flow-step.ts +147 -0
  652. package/src/trace/runtime/flush-steps.ts +31 -0
  653. package/src/trace/runtime/pending-writes.ts +54 -0
  654. package/src/trace/runtime/step-buffer.ts +298 -0
  655. package/src/trace/runtime/step-error.ts +21 -0
  656. package/src/trace/runtime/step-meta.ts +55 -0
  657. package/src/trace/runtime/thenable.ts +12 -0
  658. package/src/trace/runtime/truncate-trace.ts +62 -0
  659. package/src/trace/schema/trace-fields.ts +52 -0
  660. package/src/trace/schema/trace-retained.ts +22 -0
  661. package/src/trace/schema/trace-step-fields.ts +44 -0
  662. package/src/trace/schema/trace-step-retained.ts +25 -0
  663. package/src/trace/schema/trace-step.ts +18 -0
  664. package/src/trace/schema/trace.ts +12 -0
  665. package/src/trace/transformer/apply.ts +79 -0
  666. package/src/trace/transformer/build-meta-literal.ts +30 -0
  667. package/src/trace/transformer/find-flow-step-sites.ts +85 -0
  668. package/src/trace/transformer/inject-flow-step-import.ts +39 -0
  669. package/src/trace/transformer/transform-error.ts +14 -0
  670. package/src/trace/transformer/transform-flow-steps.ts +123 -0
  671. package/src/trace/transformer/wrap-function-declaration.ts +62 -0
  672. package/src/trace/transformer/wrap-variable-declaration.ts +67 -0
  673. package/src/workspace/affected.ts +110 -0
  674. package/src/workspace/changed-files.ts +163 -0
  675. package/src/workspace/check-deps.ts +213 -0
  676. package/src/workspace/checksum.ts +20 -0
  677. package/src/workspace/dependency-graph.ts +67 -0
  678. package/src/workspace/detect-pipework-copies.ts +93 -0
  679. package/src/workspace/discover.ts +206 -0
  680. package/src/workspace/generate.ts +401 -0
  681. package/src/workspace/index.ts +7 -0
  682. package/src/workspace/module-imports-pipework.ts +39 -0
  683. package/src/workspace/module-package-name.ts +12 -0
  684. package/src/workspace/scrub-git-env.ts +23 -0
  685. package/src/workspace/source-paths.ts +36 -0
  686. package/src/workspace/sync.ts +182 -0
  687. package/dist/REFERENCE.md +0 -1477
  688. package/dist/infra/access-links/rate-limit.d.ts +0 -10
  689. package/dist/infra/access-links/rate-limit.d.ts.map +0 -1
  690. package/dist/infra/access-links/rate-limit.js +0 -28
  691. package/dist/infra/access-links/rate-limit.js.map +0 -1
  692. package/dist/infra/email/sigv4.d.ts +0 -24
  693. package/dist/infra/email/sigv4.d.ts.map +0 -1
  694. package/dist/infra/email/sigv4.js +0 -55
  695. package/dist/infra/email/sigv4.js.map +0 -1
@@ -0,0 +1,141 @@
1
+ import { Readable } from 'node:stream'
2
+ import type { FileConfig } from '../di/types.js'
3
+ import { mediaTypeAllowed } from './upload.js'
4
+
5
+ const FILE_RESPONSE = Symbol.for('pipework.fileResponse')
6
+
7
+ /** What `http.file()` takes. */
8
+ export interface FileResponseInit {
9
+ /** The bytes, or a stream that is read as it is sent. */
10
+ readonly body: Buffer | Uint8Array | Readable
11
+ /** Content-Type of the response, e.g. `application/pdf`. */
12
+ readonly mediaType: string
13
+ /** Name the browser saves the file under. Any characters; the header is escaped. */
14
+ readonly filename?: string
15
+ /** `'attachment'` (default) downloads; `'inline'` lets the browser display it. */
16
+ readonly disposition?: 'attachment' | 'inline'
17
+ /** Byte length of a stream body, sent as Content-Length. Ignored for a Buffer, whose length is known. */
18
+ readonly length?: number
19
+ }
20
+
21
+ /**
22
+ * A file for a route to answer with, made by `http.file()`. Return it from a
23
+ * handler declared with `.file()`; it is sent as the response body, streamed
24
+ * when `body` is a Readable.
25
+ */
26
+ export interface FileResponse {
27
+ readonly [FILE_RESPONSE]: true
28
+ readonly body: Buffer | Readable
29
+ readonly mediaType: string
30
+ readonly filename: string | undefined
31
+ readonly disposition: 'attachment' | 'inline'
32
+ readonly length: number | undefined
33
+ }
34
+
35
+ /**
36
+ * Builds the value a handler returns to answer with a file: `body` (Buffer,
37
+ * Uint8Array or Readable), `mediaType`, and optionally `filename`,
38
+ * `disposition` (`'attachment'` default, or `'inline'`) and `length` (the byte
39
+ * length of a stream, sent as Content-Length; without it a stream is sent
40
+ * chunked).
41
+ *
42
+ * Declare the route with `.file({ mediaTypes })` so OpenAPI describes it. The
43
+ * response carries Content-Type, Content-Length (Buffer, or stream with
44
+ * `length`), Content-Disposition with an ASCII `filename=` fallback and the
45
+ * exact name in `filename*=UTF-8''...`, and `X-Content-Type-Options: nosniff`.
46
+ *
47
+ * The request transaction (auto mode) has committed before the first byte is
48
+ * sent, so a stream must not need it. A stream that fails before its first
49
+ * byte gets a 500; one that fails after destroys the connection and logs the
50
+ * error, so the client sees an aborted download, never a complete-looking
51
+ * short one. Range requests are not supported.
52
+ */
53
+ export function file(init: FileResponseInit): FileResponse {
54
+ if (typeof init.mediaType !== 'string' || init.mediaType.trim() === '' || /[\r\n]/.test(init.mediaType)) {
55
+ throw new Error('[pipework] http.file() needs a mediaType such as "application/pdf" (one line, not empty).')
56
+ }
57
+ const disposition: string = init.disposition ?? 'attachment'
58
+ if (disposition !== 'attachment' && disposition !== 'inline') {
59
+ throw new Error(`[pipework] http.file() disposition must be 'attachment' or 'inline', got ${String(disposition)}.`)
60
+ }
61
+ if (init.length !== undefined && (!Number.isInteger(init.length) || init.length < 0)) {
62
+ throw new Error(`[pipework] http.file() length must be a non-negative integer number of bytes, got ${String(init.length)}.`)
63
+ }
64
+ let body: Buffer | Readable
65
+ if (init.body instanceof Readable) body = init.body
66
+ else if (Buffer.isBuffer(init.body)) body = init.body
67
+ else if (init.body instanceof Uint8Array) body = Buffer.from(init.body.buffer, init.body.byteOffset, init.body.byteLength)
68
+ else throw new Error('[pipework] http.file() body must be a Buffer, Uint8Array or Readable stream.')
69
+ return Object.freeze({
70
+ [FILE_RESPONSE]: true as const,
71
+ body,
72
+ mediaType: init.mediaType.trim(),
73
+ filename: init.filename,
74
+ disposition,
75
+ length: init.length,
76
+ })
77
+ }
78
+
79
+ /** True for a value made by `http.file()`. */
80
+ export function isFileResponse(value: unknown): value is FileResponse {
81
+ return value !== null && typeof value === 'object' && (value as Record<symbol, unknown>)[FILE_RESPONSE] === true
82
+ }
83
+
84
+ /** The reason a file response cannot be sent for a route declared with `declared`, or null. */
85
+ export function fileResponseProblem(declared: FileConfig | null, file: FileResponse): string | null {
86
+ if (declared === null) return null
87
+ if (!mediaTypeAllowed(declared.mediaTypes, file.mediaType.split(';')[0]!.trim().toLowerCase())) {
88
+ return `The file's mediaType ${file.mediaType} is not in the route's .file() list (${declared.mediaTypes.join(', ')}).`
89
+ }
90
+ return null
91
+ }
92
+
93
+ /** Content-Disposition value per RFC 6266 / RFC 5987: ASCII fallback plus the exact name percent-encoded. */
94
+ export function contentDisposition(disposition: 'attachment' | 'inline', filename: string | undefined): string {
95
+ if (filename === undefined || filename === '') return disposition
96
+ const wellFormed = filename.replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g, '\uFFFD')
97
+ let fallback = ''
98
+ for (const ch of wellFormed) {
99
+ const code = ch.codePointAt(0)!
100
+ if (code > 0x7e) fallback += '_'
101
+ else if (code < 0x20 || code === 0x7f || ch === '"' || ch === '\\' || ch === '/') continue
102
+ else fallback += ch
103
+ }
104
+ if (fallback.trim() === '') fallback = 'file'
105
+ const encoded = encodeURIComponent(wellFormed).replace(/['()*]/g, (c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`)
106
+ return `${disposition}; filename="${fallback}"; filename*=UTF-8''${encoded}`
107
+ }
108
+
109
+ /**
110
+ * Reads a stream's first chunk before anything is sent, so a stream that fails
111
+ * at once can still become a normal error response. Returns the stream to send
112
+ * (the same bytes, replayed), which fails after `length` mismatches or on error.
113
+ */
114
+ export async function primeStream(source: Readable, length: number | undefined): Promise<Readable> {
115
+ const iterator = source[Symbol.asyncIterator]()
116
+ let first: IteratorResult<unknown>
117
+ try {
118
+ first = await iterator.next()
119
+ } catch (error) {
120
+ source.destroy()
121
+ throw error
122
+ }
123
+ async function* replay(): AsyncGenerator<Buffer> {
124
+ let sent = 0
125
+ try {
126
+ let step = first
127
+ while (step.done !== true) {
128
+ const chunk = typeof step.value === 'string' ? Buffer.from(step.value) : (step.value as Buffer)
129
+ sent += chunk.length
130
+ yield chunk
131
+ step = await iterator.next()
132
+ }
133
+ if (length !== undefined && sent !== length) {
134
+ throw new Error(`[pipework] The file stream ended after ${sent} bytes but http.file() declared length ${length}.`)
135
+ }
136
+ } finally {
137
+ if (!source.destroyed) source.destroy()
138
+ }
139
+ }
140
+ return Readable.from(replay(), { objectMode: false })
141
+ }
@@ -0,0 +1,4 @@
1
+ export { createServer, type PipeworkServer } from './server.js'
2
+ export type { ServerConfig, RouteRegistration, ErrorResponse, HandlerResponse, InjectOptions, InjectResponse, HttpRequest, HttpResponse, CorsConfig, SecurityConfig, RateLimitConfig, RateLimitClassesConfig, RateLimitClass, RateLimitWindow, RateLimitRefusal, InternalErrorFault } from './types.js'
3
+ export type { FileResponse, FileResponseInit } from './file.js'
4
+ export { buildErrorResponse, buildInternalErrorResponse } from './response.js'
@@ -0,0 +1,141 @@
1
+ import type { FastifyRequest } from 'fastify'
2
+ import type { Manifold } from '../../core/pipework.js'
3
+ import type { AuthStrategy, AuthRequest, AuthContext } from '../../auth/auth/types.js'
4
+ import { parseCookies } from '../../auth/auth/cookie.js'
5
+ import type { AuthEnricher, TenantResolver } from '../../auth/auth/chain.js'
6
+ import type { AuthRequirement } from '../di/types.js'
7
+ import type { TenantConfig } from '../../auth/tenant/types.js'
8
+ import type { LoggingConfig } from '../../core/logging/types.js'
9
+ import { createRequestContext, type Flow } from '../context/index.js'
10
+ import { runAuthChain } from '../../auth/auth/chain.js'
11
+ import { extractTenant } from '../../auth/tenant/index.js'
12
+ import { validateTenantId } from '../../auth/tenant/validate.js'
13
+ import { UnauthorizedError } from '../../core/errors/index.js'
14
+ import { isTracingEnabled } from '../../trace/entry/tracing-enabled.js'
15
+ import { openTrace, type ParentTraceRef } from '../../trace/entry/open-trace.js'
16
+ import { parentTraceRef } from '../../trace/entry/parent-trace.js'
17
+ import { parseTraceparent } from '../../trace/entry/parse-traceparent.js'
18
+
19
+ export function buildAuthRequest(req: FastifyRequest): AuthRequest {
20
+ return {
21
+ headers: req.headers as Record<string, string | undefined>,
22
+ cookies: parseCookies(req.headers['cookie']),
23
+ method: req.method.toUpperCase(),
24
+ }
25
+ }
26
+
27
+ export interface ContextOptions {
28
+ instance: Manifold
29
+ strategies: AuthStrategy<unknown>[]
30
+ enrichers: AuthEnricher<unknown>[]
31
+ tenantResolver: TenantResolver<unknown> | undefined
32
+ tenantConfig: TenantConfig | null
33
+ authOptional: boolean
34
+ logging: LoggingConfig | null
35
+ }
36
+
37
+ export async function buildContext(
38
+ req: FastifyRequest,
39
+ options: ContextOptions,
40
+ authRequirement?: AuthRequirement,
41
+ ): Promise<Flow> {
42
+ let auth: unknown = null
43
+ let tenant: string | null = null
44
+
45
+ const isPublic = authRequirement === 'none'
46
+ const isOptional = authRequirement === 'optional' || (authRequirement !== 'required' && options.authOptional)
47
+
48
+ if (options.strategies.length > 0 && !isPublic) {
49
+ const authReq = buildAuthRequest(req)
50
+ const authContext = buildAuthContext(options.instance)
51
+ const result = await runAuthChain(
52
+ {
53
+ strategies: options.strategies,
54
+ ...(options.enrichers.length > 0 ? { enrichers: options.enrichers } : {}),
55
+ ...(options.tenantResolver ? { tenantResolver: options.tenantResolver } : {}),
56
+ },
57
+ authReq,
58
+ { optional: isOptional },
59
+ authContext,
60
+ )
61
+ auth = result.auth
62
+ tenant = result.tenant
63
+ }
64
+
65
+ if (options.tenantConfig) {
66
+ if (tenant === null && auth !== null) {
67
+ const extracted = extractTenant(auth, options.tenantConfig)
68
+ tenant = extracted.tenant
69
+ } else if (tenant !== null) {
70
+ const validator = options.tenantConfig.validate ?? validateTenantId
71
+ if (!validator(tenant)) {
72
+ throw new UnauthorizedError(
73
+ `Invalid tenant identifier: "${tenant.slice(0, 40)}${tenant.length > 40 ? '...' : ''}". ` +
74
+ `Tenant IDs must be valid UUIDs (or pass your custom validate function).`,
75
+ )
76
+ }
77
+ }
78
+ }
79
+
80
+ const loggingCfg = options.logging
81
+ const traceHeader = loggingCfg?.traceHeader ?? 'x-trace-id'
82
+ const requestIdHeader = loggingCfg?.requestIdHeader ?? 'x-request-id'
83
+ const sessionHeader = loggingCfg?.sessionHeader ?? 'x-session-id'
84
+
85
+ const traceId = (req.headers[traceHeader] as string | undefined)
86
+ ?? (req.headers[requestIdHeader] as string | undefined)
87
+ ?? req.id
88
+
89
+ const sessionId = (req.headers[sessionHeader] as string | undefined) ?? null
90
+
91
+ const trace = isTracingEnabled(options.instance)
92
+ ? openTrace({
93
+ instance: options.instance,
94
+ entryKind: 'http',
95
+ parent: resolveParent(req),
96
+ tenant,
97
+ auth,
98
+ requestId: String(req.id),
99
+ sessionId,
100
+ })
101
+ : null
102
+
103
+ return createRequestContext({
104
+ instance: options.instance,
105
+ requestId: String(req.id),
106
+ auth,
107
+ tenant,
108
+ tenantConfig: options.tenantConfig,
109
+ trace,
110
+ metadata: {
111
+ method: req.method,
112
+ path: req.url,
113
+ startedAt: Date.now(),
114
+ traceId: String(traceId),
115
+ sessionId,
116
+ },
117
+ })
118
+ }
119
+
120
+ /**
121
+ * Picks the parent trace for an inbound HTTP request. An incoming W3C
122
+ * `traceparent` (Q10) wins — it's the explicit cross-system propagation
123
+ * signal — and yields a synthesized parent whose `id` and `rootId` both
124
+ * equal the header's trace-id (the frontend's distributed trace is the root
125
+ * of pipework's tree). If the header is absent or malformed, we fall back to
126
+ * the in-process parent for nested entries (HTTP-via-inject inside a Flow).
127
+ */
128
+ function resolveParent(req: FastifyRequest): ParentTraceRef | null {
129
+ const parsed = parseTraceparent(req.headers['traceparent'])
130
+ if (parsed !== null) return { id: parsed.traceId, rootId: parsed.traceId }
131
+ return parentTraceRef()
132
+ }
133
+
134
+ function buildAuthContext(instance: Manifold): AuthContext {
135
+ return {
136
+ db(name?: string) {
137
+ const dbConfig = instance.config.database(name)
138
+ return instance.pool.getOrCreate(dbConfig).drizzle
139
+ },
140
+ }
141
+ }
@@ -0,0 +1,5 @@
1
+ import { file } from './file.js'
2
+ import { detectMediaType, resolveMediaType, SNIFF_BYTES } from './sniff.js'
3
+
4
+ /** HTTP namespace: `http.file()` builds a file response; `http.detectMediaType()` names bytes the way uploads do; the server itself is managed by manifold.start(). */
5
+ export const http = { file, detectMediaType, resolveMediaType, SNIFF_BYTES } as const
@@ -0,0 +1,170 @@
1
+ import type { FastifyRequest } from 'fastify'
2
+ import type { Manifold } from '../../core/pipework.js'
3
+ import { parseRouteInputs } from '../di/resolve.js'
4
+ import type { HandlerMeta } from '../di/types.js'
5
+ import type { Flow } from '../context/index.js'
6
+ import { runInContext } from '../context/store.js'
7
+ import type { DB } from '../../data/db/index.js'
8
+ import { sql } from '../../data/db/sql.js'
9
+ import { define } from '../../data/domain/index.js'
10
+ import { field } from '../../data/domain/field.js'
11
+ import { TooManyRequestsError } from '../../core/errors/index.js'
12
+ import type { RateLimitClassesConfig, RateLimitRefusal, RateLimitWindow, RateLimitWindowName } from './types.js'
13
+
14
+ // Counts behind `ServerConfig.rateLimits`; `key` is '<class>:<limit>:<value>' for a key with one window,
15
+ // '<class>:<limit>:<windowSeconds>s:<value>' for a key with an array, and value '*' for `total`.
16
+ export const rateLimitCounts = define('pipework_rate_limit_counts', {
17
+ key: field.text().primaryKey(),
18
+ count: field.integer().default(0),
19
+ windowStart: field.timestamp().default('now'),
20
+ })
21
+
22
+ const WINDOWS: readonly RateLimitWindowName[] = ['perUser', 'perTenant', 'perAddress', 'total']
23
+
24
+ function windowsOf(win: RateLimitWindow | readonly RateLimitWindow[] | undefined): readonly RateLimitWindow[] {
25
+ return win === undefined ? [] : Array.isArray(win) ? (win as readonly RateLimitWindow[]) : [win as RateLimitWindow]
26
+ }
27
+ const CLEANUP_EVERY_MS = 60_000
28
+
29
+ export interface RateLimitClasses {
30
+ /** Throws at construction for a route that names a class the config does not have. */
31
+ checkRoute(method: string, path: string, meta: HandlerMeta): void
32
+ /** Counts the request against its class and throws a 429 when any window is over. */
33
+ check(req: FastifyRequest, ctx: Flow, meta: HandlerMeta, route: { method: string; url: string }): Promise<void>
34
+ }
35
+
36
+ export function createRateLimitClasses(instance: Manifold, config: RateLimitClassesConfig): RateLimitClasses {
37
+ const classes = config.classes
38
+ let longest = 0
39
+ for (const [name, cls] of Object.entries(classes)) {
40
+ for (const w of WINDOWS) {
41
+ const raw = cls[w]
42
+ if (Array.isArray(raw)) {
43
+ const seen = new Set<number>()
44
+ if (raw.length === 0) {
45
+ throw new Error(
46
+ `[pipework] rateLimits.classes.${name}.${w} is an empty array.\n` +
47
+ ' Fix: Give it at least one window, or remove the key.',
48
+ )
49
+ }
50
+ for (const win of raw) {
51
+ if (seen.has(win.windowSeconds)) {
52
+ throw new Error(
53
+ `[pipework] rateLimits.classes.${name}.${w} has two windows of ${win.windowSeconds} seconds.\n` +
54
+ ' Fix: Keep one window for each windowSeconds in a key.',
55
+ )
56
+ }
57
+ seen.add(win.windowSeconds)
58
+ }
59
+ }
60
+ for (const win of windowsOf(raw)) if (win.windowSeconds > longest) longest = win.windowSeconds
61
+ }
62
+ }
63
+ let lastCleanup = 0
64
+
65
+ const database = (): DB => instance.pool.getOrCreate(instance.config.database(instance.config.defaultDatabaseName())).drizzle
66
+
67
+ function classOf(meta: HandlerMeta, signedIn: boolean): { name: string; explicit: boolean } | null {
68
+ const own = meta.rateLimit ?? null
69
+ if (own === false) return null
70
+ if (own !== null) return { name: own, explicit: true }
71
+ const req = meta.authRequirement
72
+ const name = req === 'required' ? 'signedIn' : req === 'none' ? 'public' : signedIn ? 'signedIn' : 'public'
73
+ return { name, explicit: false }
74
+ }
75
+
76
+ // Counts the request against a key; atomic per key. Runs on the system pool,
77
+ // outside the request's transaction, so a refused or rolled-back request still counts.
78
+ async function count(db: DB, key: string, windowSeconds: number): Promise<{ count: number; retryAfter: number }> {
79
+ const rows = await db.execute(sql`
80
+ INSERT INTO pipework_rate_limit_counts AS r (key, count, window_start)
81
+ VALUES (${key}, 1, now())
82
+ ON CONFLICT (key) DO UPDATE SET
83
+ count = CASE WHEN r.window_start < now() - ${windowSeconds}::int * interval '1 second' THEN 1 ELSE r.count + 1 END,
84
+ window_start = CASE WHEN r.window_start < now() - ${windowSeconds}::int * interval '1 second' THEN now() ELSE r.window_start END
85
+ RETURNING count, ceil(extract(epoch FROM (window_start + ${windowSeconds}::int * interval '1 second' - now())))::int AS retry`)
86
+ const row = rows[0]!
87
+ return { count: Number(row['count']), retryAfter: Math.max(1, Number(row['retry'])) }
88
+ }
89
+
90
+ function cleanup(db: DB, req: FastifyRequest): void {
91
+ const now = Date.now()
92
+ if (now - lastCleanup < CLEANUP_EVERY_MS) return
93
+ lastCleanup = now
94
+ void Promise.resolve(db.execute(sql`DELETE FROM pipework_rate_limit_counts WHERE window_start < now() - ${longest}::int * interval '1 second'`))
95
+ .catch((error: unknown) => req.log.error(error, 'Rate limit cleanup failed'))
96
+ }
97
+
98
+ function report(req: FastifyRequest, ctx: Flow, event: RateLimitRefusal): void {
99
+ const hook = config.onRefused
100
+ if (hook === undefined) return
101
+ try {
102
+ const result = runInContext(ctx, () => hook(event))
103
+ if (result !== undefined && typeof (result as Promise<void>).catch === 'function') {
104
+ ;(result as Promise<void>).catch((error: unknown) => req.log.error(error, 'rateLimits.onRefused threw'))
105
+ }
106
+ } catch (error) {
107
+ req.log.error(error, 'rateLimits.onRefused threw')
108
+ }
109
+ }
110
+
111
+ return {
112
+ checkRoute(method, path, meta) {
113
+ const own = meta.rateLimit ?? null
114
+ if (typeof own === 'string' && classes[own] === undefined) {
115
+ throw new Error(
116
+ `[pipework] ${method} ${path}: rate limit class "${own}" is not in rateLimits.classes.\n` +
117
+ ` Configured classes: ${Object.keys(classes).join(', ') || '(none)'}.\n` +
118
+ ' Fix: Add the class to rateLimits.classes, or use another class name, or .rateLimit(false).',
119
+ )
120
+ }
121
+ },
122
+
123
+ async check(req, ctx, meta, route) {
124
+ const auth = ctx.auth as Record<string, unknown> | null
125
+ const rawUser = auth?.['userId']
126
+ const userId = rawUser === undefined || rawUser === null || rawUser === '' ? null : String(rawUser)
127
+ const resolved = classOf(meta, userId !== null)
128
+ if (resolved === null) return
129
+ const cls = classes[resolved.name]
130
+ if (cls === undefined) return
131
+
132
+ const values: Record<RateLimitWindowName, string | null> = {
133
+ perUser: userId,
134
+ perTenant: ctx.tenant ?? null,
135
+ perAddress: req.ip,
136
+ total: '*',
137
+ }
138
+ const db = database()
139
+ const refused: RateLimitRefusal[] = []
140
+ // Parsed only once a window refuses: most requests pass and never need them.
141
+ let inputs: ReturnType<typeof parseRouteInputs> | undefined
142
+ for (const limit of WINDOWS) {
143
+ const raw = cls[limit]
144
+ const value = values[limit]
145
+ if (raw === undefined || value === null) continue
146
+ const wins = windowsOf(raw)
147
+ const many = Array.isArray(raw)
148
+ for (const win of wins) {
149
+ const key = many ? `${resolved.name}:${limit}:${win.windowSeconds}s:${value}` : `${resolved.name}:${limit}:${value}`
150
+ const got = await count(db, key, win.windowSeconds)
151
+ if (got.count > win.max) {
152
+ refused.push({
153
+ class: resolved.name, limit, key: value, tenantId: ctx.tenant ?? null, userId,
154
+ requestId: ctx.requestId, traceId: ctx.trace?.id ?? null, count: got.count, max: win.max,
155
+ windowSeconds: win.windowSeconds, retryAfterSeconds: got.retryAfter, route,
156
+ ...(inputs ??= parseRouteInputs(meta, req.query, req.params)),
157
+ })
158
+ }
159
+ }
160
+ // A refused request is not counted against the later keys: one user over
161
+ // their own limit does not spend the tenant's.
162
+ if (refused.length > 0) break
163
+ }
164
+ cleanup(db, req)
165
+ if (refused.length === 0) return
166
+ for (const event of refused) report(req, ctx, event)
167
+ throw new TooManyRequestsError(Math.max(...refused.map((e) => e.retryAfterSeconds)))
168
+ },
169
+ }
170
+ }
@@ -0,0 +1,82 @@
1
+ import { PipeworkError, ValidationError } from '../../core/errors/index.js'
2
+ import type { FastifyReply } from 'fastify'
3
+ import type { ErrorResponse } from './types.js'
4
+
5
+ export function buildErrorResponse(error: PipeworkError): ErrorResponse {
6
+ const response: ErrorResponse = {
7
+ error: {
8
+ code: error.code,
9
+ message: error.message,
10
+ },
11
+ }
12
+
13
+ if (error instanceof ValidationError) {
14
+ response.error.fields = error.fieldErrors
15
+ }
16
+
17
+ const wait = (error as { retryAfterSeconds?: unknown }).retryAfterSeconds
18
+ if (typeof wait === 'number' && Number.isFinite(wait)) response.error.retryAfterSeconds = Math.max(0, Math.ceil(wait))
19
+
20
+ return response
21
+ }
22
+
23
+ export function buildInternalErrorResponse(error?: unknown, exposeDetails?: boolean): ErrorResponse {
24
+ const response: ErrorResponse = {
25
+ error: {
26
+ code: 'INTERNAL_ERROR',
27
+ message: 'An unexpected error occurred',
28
+ },
29
+ }
30
+
31
+ if (exposeDetails === true && error !== undefined) {
32
+ const root = unwrapCause(error)
33
+ if (root !== null) {
34
+ response.error.details = { name: root.name, message: root.message }
35
+ }
36
+ }
37
+
38
+ return response
39
+ }
40
+
41
+ function unwrapCause(error: unknown): { name: string; message: string } | null {
42
+ if (!(error instanceof Error)) return null
43
+ let current: Error = error
44
+ const seen = new Set<Error>()
45
+ while (current.cause instanceof Error && !seen.has(current.cause)) {
46
+ seen.add(current)
47
+ current = current.cause
48
+ }
49
+ return { name: current.name, message: current.message }
50
+ }
51
+
52
+ /**
53
+ * Sends an error reply whole. The per-reply serializer bypasses the route's
54
+ * declared response schema, which describes only what the handler returns.
55
+ */
56
+ export function sendError(reply: FastifyReply, status: number, body: unknown): FastifyReply {
57
+ void reply.serializer(JSON.stringify)
58
+ void reply.header('content-type', 'application/json; charset=utf-8')
59
+ return reply.status(status).send(body)
60
+ }
61
+
62
+ // The codes of the error classes in core/errors/http.ts, by status.
63
+ const CLIENT_ERROR_CODES: Readonly<Record<number, string>> = {
64
+ 401: 'UNAUTHORIZED',
65
+ 403: 'FORBIDDEN',
66
+ 404: 'NOT_FOUND',
67
+ 409: 'CONFLICT',
68
+ 413: 'PAYLOAD_TOO_LARGE',
69
+ 415: 'UNSUPPORTED_MEDIA_TYPE',
70
+ 429: 'TOO_MANY_REQUESTS',
71
+ }
72
+
73
+ /** Maps a client error (4xx) raised by Fastify itself to Pipework's error shape. */
74
+ export function buildClientErrorResponse(status: number, error: unknown, retryAfter?: unknown): ErrorResponse {
75
+ const code = CLIENT_ERROR_CODES[status] ?? 'BAD_REQUEST'
76
+ const raw = (error as { message?: unknown } | null)?.message
77
+ const response: ErrorResponse = { error: { code, message: typeof raw === 'string' && raw !== '' ? raw : 'Bad request' } }
78
+ // @fastify/rate-limit sets Retry-After on the reply before it throws.
79
+ const wait = Number(retryAfter)
80
+ if (status === 429 && retryAfter !== undefined && retryAfter !== '' && Number.isFinite(wait)) response.error.retryAfterSeconds = Math.max(0, Math.ceil(wait))
81
+ return response
82
+ }