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,123 @@
1
+ // The heartbeat of each background loop: one row per loop name in pipework_loop_beats, written at the end of every
2
+ // iteration, and the rule that decides when a loop has stopped.
3
+
4
+ import { sql } from '../../data/db/sql.js'
5
+ import type { DB } from '../../data/db/index.js'
6
+ import { getBaseLogger } from '../../core/logging/index.js'
7
+ import type { Manifold } from '../../core/pipework.js'
8
+
9
+ /** The loops pipework itself records. */
10
+ export const LOOP_NAMES = ['alerts.tick', 'jobs.scheduler', 'jobs.reaper', 'jobs.claim', 'support.events'] as const
11
+ export const ALERTS_TICK_LOOP = 'alerts.tick'
12
+ export const DEFAULT_LOOP_EVERY_MS = 60_000
13
+ const MIN_ALLOWANCE_MS = 60_000
14
+ const MAX_ERROR_LENGTH = 500
15
+
16
+ export interface BeatOutcome {
17
+ readonly ok: boolean
18
+ readonly error?: string
19
+ readonly everyMs: number
20
+ }
21
+
22
+ /** Records one iteration of a loop. Never throws: a failed beat is logged and the loop carries on. */
23
+ export async function recordBeat(db: DB | (() => DB), name: string, outcome: BeatOutcome): Promise<void> {
24
+ try {
25
+ const conn = typeof db === 'function' ? db() : db
26
+ const ok = outcome.ok
27
+ const error = ok ? null : (outcome.error ?? 'failed').slice(0, MAX_ERROR_LENGTH)
28
+ await conn.execute(sql`
29
+ INSERT INTO pipework_loop_beats (name, last_at, last_ok_at, last_error, expect_every_ms)
30
+ VALUES (${name}, clock_timestamp(), ${ok ? sql`clock_timestamp()` : sql`NULL`}, ${error}, ${Math.round(outcome.everyMs)})
31
+ ON CONFLICT (name) DO UPDATE SET
32
+ last_at = EXCLUDED.last_at,
33
+ last_ok_at = CASE WHEN ${ok} THEN EXCLUDED.last_ok_at ELSE pipework_loop_beats.last_ok_at END,
34
+ last_error = EXCLUDED.last_error,
35
+ expect_every_ms = EXCLUDED.expect_every_ms`)
36
+ } catch (err) {
37
+ try {
38
+ getBaseLogger().warn({ err, loop: name }, 'loop beat could not be recorded')
39
+ } catch { /* logging must not break the loop either */ }
40
+ }
41
+ }
42
+
43
+ /**
44
+ * `recordBeat` for a loop in a process that is not a pipework surface: writes on the manifold's default database,
45
+ * outside any Flow, and does nothing where the blueprint has no `alerts`.
46
+ */
47
+ export function recordBeatFor(instance: Manifold, name: string, outcome: BeatOutcome): Promise<void> {
48
+ if (!beatsEnabled(instance.config)) return Promise.resolve()
49
+ return recordBeat(() => instance.pool.getOrCreate(instance.config.database()).drizzle, name, outcome)
50
+ }
51
+
52
+ /** Whether a blueprint has the `alerts` flag, which brings the beat table. */
53
+ export function beatsEnabled(config: { readonly raw: unknown }): boolean {
54
+ const raw = config.raw as { readonly alerts?: unknown } | null | undefined
55
+ return raw?.alerts !== undefined
56
+ }
57
+
58
+ /** Every beat row with its ages measured on the database's clock; the one query behind the source, the check and the CLI. */
59
+ export const LOOP_BEATS_QUERY = `
60
+ SELECT name, last_at, last_ok_at, last_error, expect_every_ms,
61
+ (extract(epoch FROM clock_timestamp() - last_at) * 1000)::bigint AS since_at_ms,
62
+ (extract(epoch FROM clock_timestamp() - last_ok_at) * 1000)::bigint AS since_ok_ms
63
+ FROM pipework_loop_beats ORDER BY name`
64
+
65
+ export interface StaleLoop {
66
+ readonly name: string
67
+ readonly message: string
68
+ readonly lastAt: string
69
+ readonly lastOkAt: string | null
70
+ readonly lastError: string | null
71
+ readonly expectEveryMs: number
72
+ readonly allowanceMs: number
73
+ readonly ageMs: number
74
+ }
75
+
76
+ export function formatAge(ms: number): string {
77
+ const s = Math.max(0, Math.floor(ms / 1000))
78
+ if (s >= 3600) return `${String(Math.floor(s / 3600))} h`
79
+ if (s >= 60) return `${String(Math.floor(s / 60))} min`
80
+ return `${String(s)} s`
81
+ }
82
+
83
+ export function allowanceFor(name: string, expectEveryMs: number, maxAge: Readonly<Record<string, number>> | undefined): number {
84
+ const override = maxAge?.[name]
85
+ return override ?? Math.max(3 * expectEveryMs, MIN_ALLOWANCE_MS)
86
+ }
87
+
88
+ const iso = (v: unknown): string => new Date(v as string | Date).toISOString()
89
+
90
+ /** The loops among `rows` (the result of LOOP_BEATS_QUERY) that are stale under the allowance rule. */
91
+ export function staleLoops(
92
+ rows: readonly Record<string, unknown>[],
93
+ options: { maxAge?: Readonly<Record<string, number>>; include?: readonly string[]; exclude?: readonly string[] } = {},
94
+ ): StaleLoop[] {
95
+ const out: StaleLoop[] = []
96
+ for (const r of rows) {
97
+ const name = String(r['name'])
98
+ if (options.include !== undefined && !options.include.includes(name)) continue
99
+ if (options.exclude?.includes(name) === true) continue
100
+ const expectEveryMs = Number(r['expect_every_ms'])
101
+ const allowanceMs = allowanceFor(name, expectEveryMs, options.maxAge)
102
+ const sinceAt = Number(r['since_at_ms'])
103
+ const lastOk = r['last_ok_at'] === null || r['last_ok_at'] === undefined ? null : iso(r['last_ok_at'])
104
+ const sinceOk = lastOk === null ? null : Number(r['since_ok_ms'])
105
+ if (sinceOk !== null && sinceOk <= allowanceMs) continue
106
+ const lastError = r['last_error'] === null || r['last_error'] === undefined ? null : String(r['last_error'])
107
+ // Not run at all within the allowance, or running but failing (a loop that never succeeded is aged from its last run).
108
+ const notRun = sinceAt > allowanceMs
109
+ const ageMs = notRun ? sinceAt : (sinceOk ?? sinceAt)
110
+ const message = notRun
111
+ ? `${name} has not run in ${formatAge(ageMs)}`
112
+ : `${name} has not succeeded in ${formatAge(ageMs)}; last error: ${lastError ?? 'none'}`
113
+ out.push({ name, message, lastAt: iso(r['last_at']), lastOkAt: lastOk, lastError, expectEveryMs, allowanceMs, ageMs })
114
+ }
115
+ return out
116
+ }
117
+
118
+ /** `90s`, `5m` or `2h` as milliseconds; null when it is none of those. */
119
+ export function parseDuration(text: string): number | null {
120
+ const m = /^(\d+)(s|m|h)$/.exec(text.trim())
121
+ if (m === null) return null
122
+ return Number(m[1]) * ({ s: 1000, m: 60_000, h: 3_600_000 }[m[2] as 's' | 'm' | 'h'])
123
+ }
@@ -0,0 +1,111 @@
1
+ import { Builder } from '../../request/di/builder.js'
2
+ import type { Handler } from '../../request/di/types.js'
3
+ import type { CorsConfig } from '../../request/http/types.js'
4
+ import { schema } from '../../data/schema/namespace.js'
5
+ import { getContext } from '../../request/context/store.js'
6
+ import { getBaseLogger } from '../../core/logging/index.js'
7
+ import { recordFault, faultMessage } from './faults.js'
8
+ import { __flowStep } from '../../trace/runtime/flow-step.js'
9
+ import type { ClientFaultReport } from '../../client/report-fault.js'
10
+ import { CLIENT_FAULT_MESSAGE_MAX, CLIENT_FAULT_STACK_MAX } from '../../client/report-fault.js'
11
+ import type { Alerts, JsonValue } from './handle.js'
12
+ import { BadRequestError } from './http.js'
13
+
14
+ export type { ClientFaultReport }
15
+
16
+ export interface ClientFaultsOptions {
17
+ /** Route path, e.g. '/client-faults'. No trailing slash. */
18
+ readonly path: string
19
+ /** The apps that may report, by name. At least one. A report names one in `app`. */
20
+ readonly apps: Readonly<Record<string, { readonly owner?: string; readonly runbook?: string }>>
21
+ /** The alert key for a report. Default `client-fault:<app>:<where>`. */
22
+ readonly key?: (report: ClientFaultReport) => string
23
+ /**
24
+ * The owner and runbook for one report, over the app's: each field returned replaces the app's, an undefined
25
+ * field or result keeps it. A throw is handled as a throwing `key`: logged and recorded, nothing raised, still 204.
26
+ */
27
+ readonly assign?: (report: ClientFaultReport) => { readonly owner?: string; readonly runbook?: string } | undefined
28
+ /** This route's CORS grant (see `Builder.cors`). */
29
+ readonly cors?: CorsConfig
30
+ /** A `rateLimits` class for the route. Default: the class of a route with optional auth. */
31
+ readonly rateLimit?: string
32
+ }
33
+
34
+ const c = schema.check
35
+ const reportSchema = (apps: readonly string[]) => c.strictObject({
36
+ app: c.string().refine(a => apps.includes(a), `must be one of ${apps.join(', ')}`),
37
+ where: c.string().min(1).max(100).regex(/^[A-Za-z0-9._:/-]+$/),
38
+ kind: c.enum(['error', 'rejection', 'render', 'request']),
39
+ severity: c.enum(['warning', 'error']).optional(),
40
+ code: c.string().max(100).optional(),
41
+ message: c.string().min(1).max(CLIENT_FAULT_MESSAGE_MAX),
42
+ stack: c.string().max(CLIENT_FAULT_STACK_MAX).optional(),
43
+ route: c.string().max(200).startsWith('/').refine(r => !/[?#]|:\/\//.test(r), 'must be a route pattern, not a page URL').optional(),
44
+ release: c.string().max(100).optional(),
45
+ traceId: c.uuid().optional(),
46
+ requestId: c.string().min(1).max(100).regex(/^[A-Za-z0-9._:-]+$/).optional(),
47
+ })
48
+
49
+ const REF = 'pipework/alerts:client-fault'
50
+
51
+ /**
52
+ * One POST route at `options.path` that takes failures reported by browser code (see `reportClientFault` in
53
+ * `pipework/client`) and answers 204. No session is needed; the tenant and user are used when there is one.
54
+ * Each report is a trace step `pipework/alerts:client-fault` capturing the report, and raises
55
+ * `client-fault:<app>:<where>` (or `options.key`) with the app's owner and runbook, or the report's own from `options.assign`. The stack stays in the
56
+ * step's capture, not the alert. A body that breaks the contract is 400 and raises nothing.
57
+ */
58
+ export function clientFaults(handle: Alerts, options: ClientFaultsOptions): Handler<Record<string, unknown>, unknown>[] {
59
+ if (!options.path.startsWith('/') || options.path.length < 2 || options.path.endsWith('/')) {
60
+ throw new Error(`alerts.clientFaults: path "${options.path}" must start with "/" and have no trailing slash, e.g. "/client-faults".`)
61
+ }
62
+ if (Object.keys(options.apps).length === 0) {
63
+ throw new Error('alerts.clientFaults: apps needs at least one entry, name -> { owner?, runbook? }.')
64
+ }
65
+
66
+ const record = __flowStep({ ref: REF, capture: [null] }, async (report: ClientFaultReport): Promise<void> => {
67
+ const app = options.apps[report.app]!
68
+ const flow = getContext()
69
+ const context: Record<string, JsonValue> = { app: report.app, where: report.where, kind: report.kind }
70
+ for (const field of ['code', 'route', 'release', 'traceId', 'requestId'] as const) {
71
+ if (report[field] !== undefined) context[field] = report[field]
72
+ }
73
+ try {
74
+ const key = options.key?.(report) ?? `client-fault:${report.app}:${report.where}`
75
+ const assigned = options.assign?.(report)
76
+ const owner = assigned?.owner ?? app.owner
77
+ const runbook = assigned?.runbook ?? app.runbook
78
+ await handle.raise(key, {
79
+ severity: report.severity ?? 'error',
80
+ message: `${report.app} ${report.where}: ${report.message}`,
81
+ subject: { ...(flow?.tenant != null ? { tenantId: flow.tenant } : {}), entity: 'client', entityId: report.where },
82
+ ...(owner !== undefined ? { owner } : {}),
83
+ ...(runbook !== undefined ? { runbook } : {}),
84
+ context,
85
+ })
86
+ } catch (err) {
87
+ getBaseLogger().error({ err, app: report.app, where: report.where }, 'Failed to raise a client fault')
88
+ void recordFault('client-fault-raise', faultMessage(err), { app: report.app })
89
+ }
90
+ })
91
+
92
+ const reportShape = reportSchema(Object.keys(options.apps))
93
+ let builder = new Builder().authOptional().input(c.unknown())
94
+ if (options.cors !== undefined) builder = builder.cors(options.cors)
95
+ if (options.rateLimit !== undefined) builder = builder.rateLimit(options.rateLimit)
96
+
97
+ return [
98
+ builder.route('POST', options.path).fit(async ({ input }) => {
99
+ const parsed = reportShape.safeParse(input)
100
+ if (!parsed.success) {
101
+ const issue = parsed.error.issues[0]
102
+ const path = issue?.path.join('.') ?? ''
103
+ const field = path === '' ? 'body' : path
104
+ throw new BadRequestError(`Invalid ${field}: ${issue?.message ?? 'does not match the client fault contract'}.`, { field })
105
+ }
106
+ const report = parsed.data as ClientFaultReport
107
+ await record(report)
108
+ return { status: 204 }
109
+ }) as unknown as Handler<Record<string, unknown>, unknown>,
110
+ ]
111
+ }
@@ -0,0 +1,156 @@
1
+ // Failures that are swallowed and only logged leave no trace an alert could read. `recordFault` writes one row per
2
+ // such failure to pipework_faults, on the system connection and outside any transaction, so it survives a rollback.
3
+
4
+ import { sql } from '../../data/db/sql.js'
5
+ import type { DB } from '../../data/db/index.js'
6
+ import { pipeSystem } from '../../data/db/db.js'
7
+ import { getContext } from '../../request/context/store.js'
8
+ import type { Manifold } from '../../core/pipework.js'
9
+ import { getBaseLogger } from '../../core/logging/index.js'
10
+ import { describeError } from '../../core/logging/error-chain.js'
11
+ import { redactText, redactJson } from './redact.js'
12
+ import { beatsEnabled } from './beat.js'
13
+ import type { AlertSeverity, JsonValue } from './handle.js'
14
+ import { newRowsSource, type AlertSource } from './sources.js'
15
+
16
+ const MAX_MESSAGE_LENGTH = 2000
17
+
18
+ interface FaultTarget {
19
+ readonly db: DB | (() => DB)
20
+ readonly tenant?: string | null
21
+ }
22
+
23
+ const targets: FaultTarget[] = []
24
+
25
+ const isManifold = (v: unknown): v is Manifold =>
26
+ typeof v === 'object' && v !== null && 'pool' in v && 'config' in v
27
+
28
+ /**
29
+ * Registers the connection faults raised outside any Flow are written on (a worker's loops, or a custom drain
30
+ * process); returns the release. Pass the manifold to use its default database, resolved when a fault is written; it
31
+ * registers nothing where the blueprint has no `alerts`. A `DB` or a function returning one is used as given.
32
+ */
33
+ export function useFaultTarget(target: Manifold | DB | (() => DB)): () => void {
34
+ let db: DB | (() => DB)
35
+ if (isManifold(target)) {
36
+ if (!beatsEnabled(target.config)) return () => undefined
37
+ db = () => target.pool.getOrCreate(target.config.database()).drizzle
38
+ } else {
39
+ db = target
40
+ }
41
+ const entry: FaultTarget = { db }
42
+ targets.push(entry)
43
+ return () => {
44
+ const i = targets.indexOf(entry)
45
+ if (i >= 0) targets.splice(i, 1)
46
+ }
47
+ }
48
+
49
+ function resolveTarget(): { db: DB | (() => DB); tenant: string | null } | null {
50
+ const flow = getContext()
51
+ if (flow !== undefined) {
52
+ if (!beatsEnabled(flow.instance.config)) return null
53
+ return { db: () => pipeSystem(), tenant: flow.tenant }
54
+ }
55
+ const t = targets[targets.length - 1]
56
+ return t === undefined ? null : { db: t.db, tenant: null }
57
+ }
58
+
59
+ const warnedDropped = new Set<string>()
60
+
61
+ function warnDropped(kind: string): void {
62
+ if (warnedDropped.has(kind)) return
63
+ warnedDropped.add(kind)
64
+ try {
65
+ getBaseLogger().warn({ kind }, `fault "${kind}" dropped: no Flow and no fault target; call alerts.useFaultTarget(manifold) at boot`)
66
+ } catch { /* logging must not break the caller */ }
67
+ }
68
+
69
+ /**
70
+ * Records a failure the code swallowed, for `alerts.source.faults` to raise. Call it as `void recordFault(...)` next
71
+ * to the log line. Best effort: it never throws, does nothing where the blueprint has no `alerts`, writes outside the
72
+ * caller's transaction, and records the current tenant. `kind` is a short stable name; `message` is cut at 2000 characters. Email addresses are redacted from both before they are stored.
73
+ */
74
+ export function recordFault(kind: string, message: string, context?: Readonly<Record<string, JsonValue>>): Promise<void> {
75
+ return write(resolveTarget, kind, message, context, true)
76
+ }
77
+
78
+ /** `recordFault` for code that holds the manifold but may run outside any Flow (a trace flush). */
79
+ export function recordFaultFor(instance: Manifold, kind: string, message: string, context?: Readonly<Record<string, JsonValue>>): Promise<void> {
80
+ const flow = getContext()
81
+ return write(() => {
82
+ if (!beatsEnabled(instance.config)) return null
83
+ return {
84
+ db: () => instance.pool.getOrCreate(instance.config.database()).drizzle,
85
+ tenant: flow?.tenant ?? null,
86
+ }
87
+ }, kind, message, context, false)
88
+ }
89
+
90
+ async function write(
91
+ resolve: () => { db: DB | (() => DB); tenant: string | null } | null,
92
+ kind: string,
93
+ message: string,
94
+ context: Readonly<Record<string, JsonValue>> | undefined,
95
+ warnWhenUnresolved: boolean,
96
+ ): Promise<void> {
97
+ try {
98
+ const target = resolve()
99
+ if (target === null) {
100
+ if (warnWhenUnresolved && getContext() === undefined) warnDropped(kind)
101
+ return
102
+ }
103
+ const conn = typeof target.db === 'function' ? target.db() : target.db
104
+ await conn.execute(sql`
105
+ INSERT INTO pipework_faults (kind, message, context, tenant_id, recorded_at)
106
+ VALUES (${kind}, ${redactText(message).slice(0, MAX_MESSAGE_LENGTH)},
107
+ ${context === undefined ? null : JSON.stringify(redactJson(context))}::jsonb, ${target.tenant}, clock_timestamp())`)
108
+ } catch (err) {
109
+ try {
110
+ getBaseLogger().warn({ err, kind }, 'fault could not be recorded')
111
+ } catch { /* logging must not break the caller */ }
112
+ }
113
+ }
114
+
115
+ /** The text of a caught value, for a fault message: its message with its causes' text folded in. */
116
+ export function faultMessage(err: unknown): string {
117
+ return describeError(err).message
118
+ }
119
+
120
+ interface Basics { owner?: string; runbook?: string; severity?: AlertSeverity; database?: string }
121
+
122
+ /**
123
+ * Raises `fault:<kind>` for failures recorded by `recordFault` since the last tick, one condition per kind: message
124
+ * `<count> <kind> fault(s); last: <message>`, context `count`, `lastMessage` and `lastContext`. `kinds` limits it to
125
+ * those kinds (default all). `settleMs` is as in `alerts.source.newRows` (default 30 s). Clears by the handle's quiet
126
+ * period. Needs `alerts` configured, which brings the `pipework_faults` table.
127
+ */
128
+ export function faultsSource(options: Basics & { kinds?: readonly string[]; settleMs?: number } = {}): AlertSource {
129
+ const kinds = options.kinds === undefined ? null : [...new Set(options.kinds)]
130
+ if (kinds !== null && kinds.length === 0) throw new Error('[pipework] alerts.source.faults kinds must not be empty; omit it to watch every kind.')
131
+ if (options.settleMs !== undefined && (typeof options.settleMs !== 'number' || !Number.isFinite(options.settleMs) || options.settleMs < 0)) {
132
+ throw new Error(`[pipework] alerts.source.faults settleMs must be a non-negative number of milliseconds, got ${String(options.settleMs)}.`)
133
+ }
134
+ const where = kinds === null ? undefined : sql`t.kind IN (SELECT jsonb_array_elements_text(${JSON.stringify(kinds)}::jsonb))`
135
+ const text = (v: unknown): string => v === null || v === undefined ? 'none' : String(v)
136
+ return newRowsSource({
137
+ name: 'fault',
138
+ stateKey: kinds === null ? 'fault' : `fault:${[...kinds].sort().join(',')}`,
139
+ table: 'pipework_faults',
140
+ at: 'recorded_at',
141
+ groupBy: 'kind',
142
+ ...(where !== undefined ? { where } : {}),
143
+ ...(options.settleMs !== undefined ? { settleMs: options.settleMs } : {}),
144
+ ...(options.severity !== undefined ? { severity: options.severity } : {}),
145
+ ...(options.owner !== undefined ? { owner: options.owner } : {}),
146
+ ...(options.runbook !== undefined ? { runbook: options.runbook } : {}),
147
+ ...(options.database !== undefined ? { database: options.database } : {}),
148
+ message: g => `${String(g.count)} ${g.id} fault(s); last: ${text(g.last['message'])}`,
149
+ subject: g => ({ entity: 'fault', entityId: g.id }),
150
+ context: g => ({
151
+ count: g.count,
152
+ lastMessage: text(g.last['message']),
153
+ lastContext: (g.last['context'] ?? null) as JsonValue,
154
+ }),
155
+ })
156
+ }