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,229 @@
1
+ import { createHash, timingSafeEqual } from 'node:crypto'
2
+ import { Builder } from '../../request/di/builder.js'
3
+ import type { Handler } from '../../request/di/types.js'
4
+ import { schema } from '../../data/schema/namespace.js'
5
+ import { NotFoundError, UnauthorizedError, ForbiddenError } from '../../core/errors/index.js'
6
+ import { requireContext, runInContext } from '../../request/context/store.js'
7
+ import { __flowStep } from '../../trace/runtime/flow-step.js'
8
+ import { pipeSystem } from '../../data/db/db.js'
9
+ import { sql } from '../../data/db/sql.js'
10
+ import { BadRequestError } from '../alerts/http.js'
11
+ import { TICKET_STATES } from './schema.js'
12
+ import type { Support, TicketState, DraftInput } from './handle.js'
13
+
14
+ export interface SupportHttpOptions {
15
+ /** Route prefix, e.g. '/ops/support'. No trailing slash. */
16
+ readonly path: string
17
+ /**
18
+ * Service credentials, name -> secret, or name -> `{ secret, approve? }`. A request sends `Authorization: Bearer <secret>`.
19
+ * The name is the actor: audit rows, a draft's author and an approval carry it. A key approves only with `approve: true`;
20
+ * a plain secret is a drafting key. At least one; secrets at least 32 chars.
21
+ */
22
+ readonly credentials: Readonly<Record<string, string | { readonly secret: string; readonly approve?: boolean }>>
23
+ }
24
+
25
+ interface Principal { readonly name: string; readonly approve: boolean }
26
+
27
+ const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i
28
+ const sha = (s: string): Buffer => createHash('sha256').update(s).digest()
29
+
30
+ function authenticate(credentials: readonly (readonly [Principal, Buffer])[], header: string | undefined): Principal {
31
+ const match = header !== undefined ? /^Bearer (.+)$/.exec(header) : null
32
+ const given = sha(match?.[1] ?? '')
33
+ let found: Principal | null = null
34
+ for (const [principal, digest] of credentials) {
35
+ if (timingSafeEqual(given, digest) && match !== null && found === null) found = principal
36
+ }
37
+ if (found === null) throw new UnauthorizedError('A valid service credential is required: send Authorization: Bearer <secret>.')
38
+ return found
39
+ }
40
+
41
+ function bad(field: string, problem: string): never {
42
+ throw new BadRequestError(`Invalid ${field}: ${problem}.`, { field })
43
+ }
44
+
45
+ function optString(source: unknown, field: string): string | undefined {
46
+ const value = (source as Record<string, unknown> | null | undefined)?.[field]
47
+ if (value === undefined) return undefined
48
+ if (typeof value !== 'string') bad(field, 'must be a string')
49
+ return value
50
+ }
51
+
52
+ function reqString(source: unknown, field: string): string {
53
+ const value = optString(source, field)
54
+ if (value === undefined) bad(field, 'is required')
55
+ return value
56
+ }
57
+
58
+ function body(raw: unknown): Record<string, unknown> {
59
+ if (raw === undefined || raw === null) return {}
60
+ if (typeof raw !== 'object' || Array.isArray(raw)) bad('body', 'must be a JSON object')
61
+ return raw as Record<string, unknown>
62
+ }
63
+
64
+ function limitOf(raw: string | undefined): number | undefined {
65
+ if (raw === undefined) return undefined
66
+ if (!/^[1-9][0-9]*$/.test(raw)) bad('limit', 'must be a positive integer')
67
+ return Number(raw)
68
+ }
69
+
70
+ function validateOptions(options: SupportHttpOptions): void {
71
+ if (!options.path.startsWith('/') || options.path.length < 2 || options.path.endsWith('/')) {
72
+ throw new Error(`support.http: path "${options.path}" must start with "/" and have no trailing slash, e.g. "/ops/support".`)
73
+ }
74
+ const entries = Object.entries(options.credentials)
75
+ if (entries.length === 0) {
76
+ throw new Error('support.http: credentials needs at least one entry, name -> secret (the secret is at least 32 characters).')
77
+ }
78
+ for (const [name, cred] of entries) {
79
+ const secret = typeof cred === 'string' ? cred : (cred as { secret?: unknown } | null)?.secret
80
+ if (typeof secret !== 'string' || secret.length < 32) {
81
+ throw new Error(`support.http: the secret for credential "${name}" is under 32 characters; use a long random value.`)
82
+ }
83
+ if (typeof cred !== 'string' && cred.approve !== undefined && typeof cred.approve !== 'boolean') {
84
+ throw new Error(`support.http: "approve" for credential "${name}" must be true or false.`)
85
+ }
86
+ }
87
+ }
88
+
89
+ /**
90
+ * Routes for a support agent, under `options.path` (all JSON, no session, no tenant): GET list (`state`, `category`,
91
+ * `tenantId`, `limit`, `cursor`), GET `/:id` (ticket, messages, links), PATCH `/:id` (`{ category }`, null clears),
92
+ * POST `/:id/transition` (`{ to }`), `/:id/notes` (`{ text }`), `/:id/links` (`{ kind, ref, label? }`),
93
+ * `/:id/drafts` (`{ text, html?, subject? }`, answers `{ draftId, version }`), PATCH and DELETE `/drafts/:id`, and
94
+ * POST `/drafts/:id/approve` (`{ version }`, 403 for a key without `approve: true`). A stale or already-approved draft is 409.
95
+ * Every request needs `Authorization: Bearer <secret>` matching one of `credentials`; the credential's name is the actor
96
+ * (audit rows, a draft's author, the approver), so `canApprove` and `distinctApprover` judge the key's name.
97
+ * Each call is the handle's own, traced and audited by it. Ids are looked up across tenants on the system channel, which a
98
+ * database with `rls.platformRole` refuses over the support tables; there these routes do not work. A bad value is 400 naming the field.
99
+ */
100
+ export function supportHttp(handle: Support, options: SupportHttpOptions): Handler<Record<string, unknown>, unknown>[] {
101
+ validateOptions(options)
102
+ const credentials = Object.entries(options.credentials).map(([name, cred]) => {
103
+ const principal: Principal = typeof cred === 'string' ? { name, approve: false } : { name, approve: cred.approve === true }
104
+ return [principal, sha(typeof cred === 'string' ? cred : cred.secret)] as const
105
+ })
106
+ const base = options.path
107
+ const any = schema.check.unknown()
108
+
109
+ const get = (path: string) => new Builder().public().request().query(any).params(any).route('GET', path)
110
+ const withBody = (method: 'POST' | 'PATCH', path: string) => new Builder().public().request().query(any).params(any).input(any).route(method, path)
111
+ const del = (path: string) => new Builder().public().request().query(any).params(any).route('DELETE', path)
112
+
113
+ const idOf = (params: unknown): string => String((params as { id: string }).id)
114
+
115
+ // The handle takes its actor from the Flow's auth: run each call as the key's name, in a trace step so its audit
116
+ // records have one to sit under.
117
+ const as = <T>(who: Principal, fn: () => Promise<T>): Promise<T> => {
118
+ const flow = requireContext()
119
+ const step = __flowStep({ ref: 'src/infra/support/http.ts:supportHttp' }, fn)
120
+ return runInContext({ ...flow, auth: { userId: who.name } }, step)
121
+ }
122
+
123
+ const tenantOfTicket = async (id: string): Promise<string> => {
124
+ if (UUID_RE.test(id)) {
125
+ const rows = await pipeSystem().execute(sql`SELECT tenant_id FROM support_ticket WHERE id = ${id}::uuid`)
126
+ if (rows[0] !== undefined) return rows[0]['tenant_id'] as string
127
+ }
128
+ throw new NotFoundError('Support ticket', { ticketId: id })
129
+ }
130
+ const tenantOfDraft = async (id: string): Promise<string> => {
131
+ if (UUID_RE.test(id)) {
132
+ const rows = await pipeSystem().execute(sql`SELECT tenant_id FROM support_message WHERE id = ${id}::uuid AND kind = 'draft'`)
133
+ if (rows[0] !== undefined) return rows[0]['tenant_id'] as string
134
+ }
135
+ throw new NotFoundError('Support draft', { draftId: id })
136
+ }
137
+
138
+ const draftPatch = (b: Record<string, unknown>): Partial<DraftInput> => {
139
+ const text = optString(b, 'text')
140
+ const html = optString(b, 'html')
141
+ const subject = optString(b, 'subject')
142
+ return { ...(text === undefined ? {} : { text }), ...(html === undefined ? {} : { html }), ...(subject === undefined ? {} : { subject }) }
143
+ }
144
+
145
+ return [
146
+ get(base).fit(async ({ request, query }) => {
147
+ const who = authenticate(credentials, request.headers['authorization'])
148
+ const state = optString(query, 'state')
149
+ if (state !== undefined && !(TICKET_STATES as readonly string[]).includes(state)) bad('state', `"${state}" is not one of ${TICKET_STATES.join(', ')}`)
150
+ const category = optString(query, 'category')
151
+ const tenantId = optString(query, 'tenantId')
152
+ const limit = limitOf(optString(query, 'limit'))
153
+ const cursor = optString(query, 'cursor')
154
+ if (tenantId !== undefined && !UUID_RE.test(tenantId)) bad('tenantId', 'must be a UUID')
155
+ return as(who, () => handle.listAll({
156
+ ...(state === undefined ? {} : { state: state as TicketState }), ...(category === undefined ? {} : { category }), ...(tenantId === undefined ? {} : { tenantId }),
157
+ ...(limit === undefined ? {} : { limit }), ...(cursor === undefined ? {} : { cursor }),
158
+ }))
159
+ }),
160
+ get(`${base}/:id`).fit(async ({ request, params }) => {
161
+ const who = authenticate(credentials, request.headers['authorization'])
162
+ const id = idOf(params)
163
+ return as(who, async () => handle.getFor(await tenantOfTicket(id), id))
164
+ }),
165
+ withBody('PATCH', `${base}/:id`).fit(async ({ request, params, input }) => {
166
+ const who = authenticate(credentials, request.headers['authorization'])
167
+ const b = body(input)
168
+ const category = b['category']
169
+ if (category !== null && typeof category !== 'string') bad('category', 'must be a string or null')
170
+ const id = idOf(params)
171
+ return as(who, async () => handle.setCategoryFor(await tenantOfTicket(id), id, category))
172
+ }),
173
+ withBody('POST', `${base}/:id/transition`).fit(async ({ request, params, input }) => {
174
+ const who = authenticate(credentials, request.headers['authorization'])
175
+ const to = reqString(body(input), 'to')
176
+ if (!(TICKET_STATES as readonly string[]).includes(to)) bad('to', `"${to}" is not one of ${TICKET_STATES.join(', ')}`)
177
+ const id = idOf(params)
178
+ return as(who, async () => handle.transitionFor(await tenantOfTicket(id), id, to as TicketState))
179
+ }),
180
+ withBody('POST', `${base}/:id/notes`).fit(async ({ request, params, input }) => {
181
+ const who = authenticate(credentials, request.headers['authorization'])
182
+ const text = reqString(body(input), 'text')
183
+ const id = idOf(params)
184
+ return as(who, async () => handle.addNoteFor(await tenantOfTicket(id), id, text))
185
+ }),
186
+ withBody('POST', `${base}/:id/links`).fit(async ({ request, params, input }) => {
187
+ const who = authenticate(credentials, request.headers['authorization'])
188
+ const b = body(input)
189
+ const kind = reqString(b, 'kind')
190
+ const ref = reqString(b, 'ref')
191
+ const label = optString(b, 'label')
192
+ const id = idOf(params)
193
+ return as(who, async () => handle.linkFor(await tenantOfTicket(id), id, { kind, ref, ...(label === undefined ? {} : { label }) }))
194
+ }),
195
+ withBody('POST', `${base}/:id/drafts`).fit(async ({ request, params, input }) => {
196
+ const who = authenticate(credentials, request.headers['authorization'])
197
+ const b = body(input)
198
+ const text = reqString(b, 'text')
199
+ const html = optString(b, 'html')
200
+ const subject = optString(b, 'subject')
201
+ const id = idOf(params)
202
+ return as(who, async () => handle.draftReplyFor(await tenantOfTicket(id), id, {
203
+ text, ...(html === undefined ? {} : { html }), ...(subject === undefined ? {} : { subject }),
204
+ }))
205
+ }),
206
+ withBody('PATCH', `${base}/drafts/:id`).fit(async ({ request, params, input }) => {
207
+ const who = authenticate(credentials, request.headers['authorization'])
208
+ const patch = draftPatch(body(input))
209
+ const id = idOf(params)
210
+ return as(who, async () => handle.editDraftFor(await tenantOfDraft(id), id, patch))
211
+ }),
212
+ del(`${base}/drafts/:id`).fit(async ({ request, params }) => {
213
+ const who = authenticate(credentials, request.headers['authorization'])
214
+ const id = idOf(params)
215
+ return as(who, async () => {
216
+ await handle.discardDraftFor(await tenantOfDraft(id), id)
217
+ return { discarded: true }
218
+ })
219
+ }),
220
+ withBody('POST', `${base}/drafts/:id/approve`).fit(async ({ request, params, input }) => {
221
+ const who = authenticate(credentials, request.headers['authorization'])
222
+ if (!who.approve) throw new ForbiddenError('This credential may not approve replies.', { actorId: who.name })
223
+ const version = body(input)['version']
224
+ if (typeof version !== 'number' || !Number.isInteger(version)) bad('version', 'must be an integer')
225
+ const id = idOf(params)
226
+ return as(who, async () => handle.approveFor(await tenantOfDraft(id), id, { expectedVersion: version }))
227
+ }),
228
+ ]
229
+ }
@@ -0,0 +1,7 @@
1
+ export { createSupport, supportTemplates, DraftStateError } from './handle.js'
2
+ export { supportHttp } from './http.js'
3
+ export type { SupportHttpOptions } from './http.js'
4
+ export type {
5
+ Support, SupportConfig, TicketApi, Ticket, TicketMessage, TicketLink, TicketDetail, TicketState, ListOptions, ListPage,
6
+ DraftInput, SupportSink, SupportDelivery, ScreenVerdict, ReceiveResult, ReceiveInput, SupportEventKind, SupportEventPayload,
7
+ } from './handle.js'
@@ -0,0 +1,12 @@
1
+ import { createSupport, supportTemplates } from './handle.js'
2
+ import { supportHttp } from './http.js'
3
+
4
+ /** Support namespace — tickets from email (and later chat), with replies sent only after an approval. */
5
+ export const support = {
6
+ /** Creates the support handle: tickets, messages, notes, links, drafts, `approve`, `receiveEmail`, and the events job. */
7
+ create: createSupport,
8
+ /** Routes for a support agent: list, read, triage, notes, links, drafts, approve; bearer credentials that name the actor, and only some may approve. */
9
+ http: supportHttp,
10
+ /** The reply template to spread into `mail.create({ templates })`; it renders the approved draft verbatim. */
11
+ templates: supportTemplates,
12
+ } as const
@@ -0,0 +1,96 @@
1
+ // The tables behind support tickets. Tickets, messages and links are tenant-scoped (forced
2
+ // row-level security in generated migrations); the event outbox is not, because one tick
3
+ // delivers every tenant's events.
4
+
5
+ import { define } from '../../data/domain/index.js'
6
+ import { field } from '../../data/domain/field.js'
7
+ import { schema } from '../../data/schema/namespace.js'
8
+ import { index, uniqueIndex } from '../../data/db/idx/index.js'
9
+ import { sql } from '../../data/db/sql.js'
10
+
11
+ export const TICKET_STATES = ['open', 'triaged', 'awaiting_approval', 'answered', 'closed'] as const
12
+ export const MESSAGE_KINDS = ['inbound', 'note', 'draft', 'outbound'] as const
13
+ export const MESSAGE_CHANNELS = ['email', 'chat', 'api'] as const
14
+ export const DRAFT_STATUSES = ['draft', 'approved', 'discarded'] as const
15
+ export const EVENT_KINDS = ['ticket.created', 'ticket.customer-replied', 'draft.awaiting-approval'] as const
16
+
17
+ export const supportTicket = define('support_ticket', {
18
+ id: field.uuid().defaultRandom().primaryKey(),
19
+ tenantId: field.uuid().tenant(),
20
+ state: field.enum(TICKET_STATES).default('open'),
21
+ category: field.text().nullable(),
22
+ subject: field.text(),
23
+ contactEmail: field.text(),
24
+ contactName: field.text().nullable(),
25
+ userId: field.text().nullable(),
26
+ conversationId: field.uuid().nullable(),
27
+ createdAt: field.timestamp().default('now'),
28
+ updatedAt: field.timestamp().default('now'),
29
+ lastCustomerMessageAt: field.timestamp().default('now'),
30
+ }, {
31
+ indexes: (t) => ({
32
+ byConversation: uniqueIndex('uq_support_ticket_conversation').on(t['tenantId'], t['conversationId']),
33
+ byState: index('idx_support_ticket_state').on(t['tenantId'], t['state'], t['createdAt']),
34
+ }),
35
+ })
36
+
37
+ export const supportMessage = define('support_message', {
38
+ id: field.uuid().defaultRandom().primaryKey(),
39
+ tenantId: field.uuid().tenant(),
40
+ ticketId: field.uuid(),
41
+ kind: field.enum(MESSAGE_KINDS),
42
+ channel: field.enum(MESSAGE_CHANNELS),
43
+ subject: field.text().nullable(),
44
+ text: field.text(),
45
+ html: field.text().nullable(),
46
+ fromAddress: field.text().nullable(),
47
+ inboundEmailId: field.uuid().nullable(),
48
+ attachments: field.jsonb(schema.check.json()).nullable(),
49
+ draftStatus: field.enum(DRAFT_STATUSES).nullable(),
50
+ version: field.integer().default(1),
51
+ approvedBy: field.text().nullable(),
52
+ approvedAt: field.timestamp().nullable(),
53
+ emailMessageId: field.uuid().nullable(),
54
+ createdBy: field.text().nullable(),
55
+ createdAt: field.timestamp().default('now'),
56
+ }, {
57
+ indexes: (t) => ({
58
+ byInbound: uniqueIndex('uq_support_message_inbound').on(t['tenantId'], t['inboundEmailId']),
59
+ byTicket: index('idx_support_message_ticket').on(t['ticketId'], t['createdAt']),
60
+ }),
61
+ })
62
+
63
+ export const supportLink = define('support_link', {
64
+ id: field.uuid().defaultRandom().primaryKey(),
65
+ tenantId: field.uuid().tenant(),
66
+ ticketId: field.uuid(),
67
+ kind: field.text(),
68
+ ref: field.text(),
69
+ label: field.text().nullable(),
70
+ createdAt: field.timestamp().default('now'),
71
+ }, {
72
+ indexes: (t) => ({
73
+ byTarget: uniqueIndex('uq_support_link_target').on(t['ticketId'], t['kind'], t['ref']),
74
+ }),
75
+ })
76
+
77
+ export const supportEventDelivery = define('support_event_delivery', {
78
+ id: field.uuid().defaultRandom().primaryKey(),
79
+ ticketId: field.uuid(),
80
+ kind: field.enum(EVENT_KINDS),
81
+ sink: field.text(),
82
+ payload: field.jsonb(schema.check.json()),
83
+ attempts: field.integer().default(0),
84
+ nextAttemptAt: field.timestamp().default('now'),
85
+ deliveredAt: field.timestamp().nullable(),
86
+ failedAt: field.timestamp().nullable(),
87
+ lastError: field.text().nullable(),
88
+ createdAt: field.timestamp().default('now'),
89
+ }, {
90
+ indexes: (t) => ({
91
+ due: index('idx_support_event_delivery_due')
92
+ .on(t['nextAttemptAt'])
93
+ .where(sql.raw('delivered_at IS NULL AND failed_at IS NULL')),
94
+ byTicket: index('idx_support_event_delivery_ticket').on(t['ticketId']),
95
+ }),
96
+ })
@@ -0,0 +1,45 @@
1
+ import type { Handler, HttpMethod } from '../../request/di/types.js'
2
+ import type { SignatureVerifier } from './verify.js'
3
+ import { Builder } from '../../request/di/builder.js'
4
+ import { UnauthorizedError } from '../../core/errors/index.js'
5
+
6
+ export interface InboundWebhookConfig<TPayload> {
7
+ path: string
8
+ method?: HttpMethod
9
+ verifier: SignatureVerifier
10
+ parsePayload?: (rawBody: Buffer) => TPayload
11
+ idempotencyKey?: (payload: TPayload) => string
12
+ }
13
+
14
+ export interface InboundWebhookContext<TPayload> {
15
+ payload: TPayload
16
+ rawBody: Buffer
17
+ headers: Readonly<Record<string, string | undefined>>
18
+ }
19
+
20
+ export function defineInboundWebhook<TPayload = unknown>(
21
+ config: InboundWebhookConfig<TPayload>,
22
+ handler: (ctx: InboundWebhookContext<TPayload>) => Promise<unknown>,
23
+ ): Handler<Record<string, unknown>, unknown> {
24
+ const method = config.method ?? 'POST'
25
+ const parse = config.parsePayload ?? ((raw: Buffer) => JSON.parse(raw.toString('utf-8')) as TPayload)
26
+
27
+ return new Builder()
28
+ .public()
29
+ .rawBody()
30
+ .request()
31
+ .route(method, config.path)
32
+ .fit(async ({ rawBody, request }) => {
33
+ if (!config.verifier.verify(rawBody, request.headers)) {
34
+ throw new UnauthorizedError(
35
+ 'Webhook signature verification failed.\n\n' +
36
+ ' The request signature does not match the expected value.\n' +
37
+ ' Check that the signing secret is correct and the raw body is unmodified.\n',
38
+ )
39
+ }
40
+
41
+ const payload = parse(rawBody)
42
+
43
+ return handler({ payload, rawBody, headers: request.headers })
44
+ })
45
+ }
@@ -0,0 +1,5 @@
1
+ export { defineInboundWebhook, type InboundWebhookConfig, type InboundWebhookContext } from './inbound.js'
2
+ export { hmacVerifier, stripeVerifier, githubVerifier, type SignatureVerifier } from './verify.js'
3
+ export { signPayload, type SignedPayload, type WebhookSignatureOptions } from './sign.js'
4
+ export { createOutboundWebhook, type OutboundWebhookConfig, type OutboundWebhook,
5
+ type WebhookEndpoint, type DeliveryAttempt } from './outbound.js'
@@ -0,0 +1,24 @@
1
+ import { defineInboundWebhook } from './inbound.js'
2
+ import { hmacVerifier, stripeVerifier, githubVerifier } from './verify.js'
3
+ import { signPayload } from './sign.js'
4
+ import { createOutboundWebhook } from './outbound.js'
5
+
6
+ /** Webhook namespace — inbound verification, outbound delivery with retries, HMAC signing. */
7
+ export const webhook = {
8
+ /** Defines an inbound webhook handler with signature verification and optional idempotency. */
9
+ defineInbound: defineInboundWebhook,
10
+ /** Creates an outbound webhook system — endpoint registration, payload signing, delivery with retries. */
11
+ createOutbound: createOutboundWebhook,
12
+
13
+ verify: {
14
+ /** Creates an HMAC signature verifier for inbound webhooks. */
15
+ hmac: hmacVerifier,
16
+ /** Creates a Stripe-compatible signature verifier. */
17
+ stripe: stripeVerifier,
18
+ /** Creates a GitHub-compatible signature verifier. */
19
+ github: githubVerifier,
20
+ },
21
+
22
+ /** Signs a payload with HMAC for outbound delivery — returns body, signature, timestamp, messageId. */
23
+ sign: signPayload,
24
+ } as const