@pithy-sh/core 0.1.1 → 0.1.3

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 (393) hide show
  1. package/dist/address/address.d.ts +57 -0
  2. package/dist/address/address.d.ts.map +1 -0
  3. package/dist/address/address.js +71 -0
  4. package/dist/audit/auditEvent.d.ts +89 -0
  5. package/dist/audit/auditEvent.d.ts.map +1 -0
  6. package/dist/audit/auditEvent.js +69 -0
  7. package/dist/audit/recorder.d.ts +18 -0
  8. package/dist/audit/recorder.d.ts.map +1 -0
  9. package/dist/audit/recorder.js +8 -0
  10. package/dist/capability/bindings.d.ts +62 -0
  11. package/dist/capability/bindings.d.ts.map +1 -0
  12. package/dist/capability/bindings.js +97 -0
  13. package/dist/capability/capability.d.ts +501 -0
  14. package/dist/capability/capability.d.ts.map +1 -0
  15. package/dist/capability/capability.js +16 -0
  16. package/dist/capability/client.d.ts +70 -0
  17. package/dist/capability/client.d.ts.map +1 -0
  18. package/dist/capability/client.js +86 -0
  19. package/dist/capability/compose.d.ts +25 -0
  20. package/dist/capability/compose.d.ts.map +1 -0
  21. package/dist/capability/compose.js +54 -0
  22. package/dist/capability/composition.d.ts +26 -0
  23. package/dist/capability/composition.d.ts.map +1 -0
  24. package/dist/capability/composition.js +84 -0
  25. package/dist/capability/config.d.ts +7 -0
  26. package/dist/capability/config.d.ts.map +1 -0
  27. package/dist/capability/config.js +14 -0
  28. package/dist/capability/devSecret.d.ts +32 -0
  29. package/dist/capability/devSecret.d.ts.map +1 -0
  30. package/dist/capability/devSecret.js +28 -0
  31. package/dist/capability/manifest.d.ts +298 -0
  32. package/dist/capability/manifest.d.ts.map +1 -0
  33. package/dist/capability/manifest.js +356 -0
  34. package/dist/capability/secretOrigin.d.ts +121 -0
  35. package/dist/capability/secretOrigin.d.ts.map +1 -0
  36. package/dist/capability/secretOrigin.js +134 -0
  37. package/dist/capability/settings.d.ts +119 -0
  38. package/dist/capability/settings.d.ts.map +1 -0
  39. package/dist/capability/settings.js +66 -0
  40. package/dist/capability/validateBindings.d.ts +4 -0
  41. package/dist/capability/validateBindings.d.ts.map +1 -0
  42. package/dist/capability/validateBindings.js +34 -0
  43. package/dist/capability/vanishingKey.d.ts +31 -0
  44. package/dist/capability/vanishingKey.d.ts.map +1 -0
  45. package/dist/capability/vanishingKey.js +79 -0
  46. package/dist/controlPlane/audit/actions.d.ts +70 -0
  47. package/dist/controlPlane/audit/actions.d.ts.map +1 -0
  48. package/dist/controlPlane/audit/actions.js +76 -0
  49. package/dist/controlPlane/capability.d.ts +47 -0
  50. package/dist/controlPlane/capability.d.ts.map +1 -0
  51. package/dist/controlPlane/capability.js +138 -0
  52. package/dist/controlPlane/config/config.d.ts +19 -0
  53. package/dist/controlPlane/config/config.d.ts.map +1 -0
  54. package/dist/controlPlane/config/config.js +85 -0
  55. package/dist/controlPlane/context.d.ts +27 -0
  56. package/dist/controlPlane/context.d.ts.map +1 -0
  57. package/dist/controlPlane/context.js +29 -0
  58. package/dist/controlPlane/data/connection.d.ts +79 -0
  59. package/dist/controlPlane/data/connection.d.ts.map +1 -0
  60. package/dist/controlPlane/data/connection.js +55 -0
  61. package/dist/controlPlane/data/keyLifecycle.d.ts +62 -0
  62. package/dist/controlPlane/data/keyLifecycle.d.ts.map +1 -0
  63. package/dist/controlPlane/data/keyLifecycle.js +126 -0
  64. package/dist/controlPlane/data/replay.d.ts +18 -0
  65. package/dist/controlPlane/data/replay.d.ts.map +1 -0
  66. package/dist/controlPlane/data/replay.js +20 -0
  67. package/dist/controlPlane/data/tables.d.ts +34 -0
  68. package/dist/controlPlane/data/tables.d.ts.map +1 -0
  69. package/dist/controlPlane/data/tables.js +37 -0
  70. package/dist/controlPlane/discovery/adminRoute.d.ts +313 -0
  71. package/dist/controlPlane/discovery/adminRoute.d.ts.map +1 -0
  72. package/dist/controlPlane/discovery/adminRoute.js +136 -0
  73. package/dist/controlPlane/discovery/configuration.d.ts +108 -0
  74. package/dist/controlPlane/discovery/configuration.d.ts.map +1 -0
  75. package/dist/controlPlane/discovery/configuration.js +187 -0
  76. package/dist/controlPlane/discovery/drift.d.ts +64 -0
  77. package/dist/controlPlane/discovery/drift.d.ts.map +1 -0
  78. package/dist/controlPlane/discovery/drift.js +58 -0
  79. package/dist/controlPlane/discovery/health.d.ts +99 -0
  80. package/dist/controlPlane/discovery/health.d.ts.map +1 -0
  81. package/dist/controlPlane/discovery/health.js +151 -0
  82. package/dist/controlPlane/discovery/healthSummary.d.ts +236 -0
  83. package/dist/controlPlane/discovery/healthSummary.d.ts.map +1 -0
  84. package/dist/controlPlane/discovery/healthSummary.js +278 -0
  85. package/dist/controlPlane/error/errors.d.ts +67 -0
  86. package/dist/controlPlane/error/errors.d.ts.map +1 -0
  87. package/dist/controlPlane/error/errors.js +79 -0
  88. package/dist/controlPlane/http/cors.d.ts +112 -0
  89. package/dist/controlPlane/http/cors.d.ts.map +1 -0
  90. package/dist/controlPlane/http/cors.js +192 -0
  91. package/dist/controlPlane/http/guard.d.ts +77 -0
  92. package/dist/controlPlane/http/guard.d.ts.map +1 -0
  93. package/dist/controlPlane/http/guard.js +142 -0
  94. package/dist/controlPlane/http/handlers.d.ts +179 -0
  95. package/dist/controlPlane/http/handlers.d.ts.map +1 -0
  96. package/dist/controlPlane/http/handlers.js +245 -0
  97. package/dist/controlPlane/http/responses.d.ts +99 -0
  98. package/dist/controlPlane/http/responses.d.ts.map +1 -0
  99. package/dist/controlPlane/http/responses.js +63 -0
  100. package/dist/controlPlane/http/routes.d.ts +40 -0
  101. package/dist/controlPlane/http/routes.d.ts.map +1 -0
  102. package/dist/controlPlane/http/routes.js +83 -0
  103. package/dist/controlPlane/http/schemas.d.ts +39 -0
  104. package/dist/controlPlane/http/schemas.d.ts.map +1 -0
  105. package/dist/controlPlane/http/schemas.js +31 -0
  106. package/dist/controlPlane/http/verify.d.ts +84 -0
  107. package/dist/controlPlane/http/verify.d.ts.map +1 -0
  108. package/dist/controlPlane/http/verify.js +65 -0
  109. package/dist/controlPlane/migrations/0001_init.d.ts +51 -0
  110. package/dist/controlPlane/migrations/0001_init.d.ts.map +1 -0
  111. package/dist/controlPlane/migrations/0001_init.js +65 -0
  112. package/dist/controlPlane/replay/d1Guard.d.ts +29 -0
  113. package/dist/controlPlane/replay/d1Guard.d.ts.map +1 -0
  114. package/dist/controlPlane/replay/d1Guard.js +39 -0
  115. package/dist/controlPlane/replay/guard.d.ts +52 -0
  116. package/dist/controlPlane/replay/guard.d.ts.map +1 -0
  117. package/dist/controlPlane/replay/guard.js +0 -0
  118. package/dist/controlPlane/replay/kvGuard.d.ts +99 -0
  119. package/dist/controlPlane/replay/kvGuard.d.ts.map +1 -0
  120. package/dist/controlPlane/replay/kvGuard.js +101 -0
  121. package/dist/controlPlane/scope/scope.d.ts +68 -0
  122. package/dist/controlPlane/scope/scope.d.ts.map +1 -0
  123. package/dist/controlPlane/scope/scope.js +63 -0
  124. package/dist/controlPlane/token/base64url.d.ts +10 -0
  125. package/dist/controlPlane/token/base64url.d.ts.map +1 -0
  126. package/dist/controlPlane/token/base64url.js +48 -0
  127. package/dist/controlPlane/token/claims.d.ts +83 -0
  128. package/dist/controlPlane/token/claims.d.ts.map +1 -0
  129. package/dist/controlPlane/token/claims.js +86 -0
  130. package/dist/controlPlane/token/digest.d.ts +44 -0
  131. package/dist/controlPlane/token/digest.d.ts.map +1 -0
  132. package/dist/controlPlane/token/digest.js +56 -0
  133. package/dist/controlPlane/token/jws.d.ts +52 -0
  134. package/dist/controlPlane/token/jws.d.ts.map +1 -0
  135. package/dist/controlPlane/token/jws.js +65 -0
  136. package/dist/controlPlane/token/mint.d.ts +56 -0
  137. package/dist/controlPlane/token/mint.d.ts.map +1 -0
  138. package/dist/controlPlane/token/mint.js +55 -0
  139. package/dist/controlPlane/wire.d.ts +122 -0
  140. package/dist/controlPlane/wire.d.ts.map +1 -0
  141. package/dist/controlPlane/wire.js +123 -0
  142. package/dist/createBackend.d.ts +58 -0
  143. package/dist/createBackend.d.ts.map +1 -0
  144. package/dist/createBackend.js +170 -0
  145. package/dist/createEntrypoint.d.ts +53 -0
  146. package/dist/createEntrypoint.d.ts.map +1 -0
  147. package/dist/createEntrypoint.js +52 -0
  148. package/dist/data/boundParameters.d.ts +83 -0
  149. package/dist/data/boundParameters.d.ts.map +1 -0
  150. package/dist/data/boundParameters.js +162 -0
  151. package/dist/data/codecs.d.ts +34 -0
  152. package/dist/data/codecs.d.ts.map +1 -0
  153. package/dist/data/codecs.js +135 -0
  154. package/dist/data/cursor.d.ts +81 -0
  155. package/dist/data/cursor.d.ts.map +1 -0
  156. package/dist/data/cursor.js +104 -0
  157. package/dist/data/databases.d.ts +55 -0
  158. package/dist/data/databases.d.ts.map +1 -0
  159. package/dist/data/databases.js +32 -0
  160. package/dist/data/db.d.ts +42 -0
  161. package/dist/data/db.d.ts.map +1 -0
  162. package/dist/data/db.js +24 -0
  163. package/dist/data/withD1Retry.d.ts +91 -0
  164. package/dist/data/withD1Retry.d.ts.map +1 -0
  165. package/dist/data/withD1Retry.js +152 -0
  166. package/dist/entitlement/entitlement.d.ts +118 -0
  167. package/dist/entitlement/entitlement.d.ts.map +1 -0
  168. package/dist/entitlement/entitlement.js +59 -0
  169. package/dist/entitlement/gateScan.d.ts +26 -0
  170. package/dist/entitlement/gateScan.d.ts.map +1 -0
  171. package/dist/entitlement/gateScan.js +96 -0
  172. package/dist/entitlement/require.d.ts +12 -0
  173. package/dist/entitlement/require.d.ts.map +1 -0
  174. package/dist/entitlement/require.js +123 -0
  175. package/dist/env/ambient.d.ts +52 -0
  176. package/dist/env/ambient.d.ts.map +1 -0
  177. package/dist/env/ambient.js +35 -0
  178. package/dist/env/ci.d.ts +36 -0
  179. package/dist/env/ci.d.ts.map +1 -0
  180. package/dist/env/ci.js +40 -0
  181. package/dist/env/stem.d.ts +23 -0
  182. package/dist/env/stem.d.ts.map +1 -0
  183. package/dist/env/stem.js +29 -0
  184. package/dist/error/cause.d.ts +143 -0
  185. package/dist/error/cause.d.ts.map +1 -0
  186. package/dist/error/cause.js +192 -0
  187. package/dist/error/client.d.ts +35 -0
  188. package/dist/error/client.d.ts.map +1 -0
  189. package/dist/error/client.js +38 -0
  190. package/dist/error/extend.d.ts +101 -0
  191. package/dist/error/extend.d.ts.map +1 -0
  192. package/dist/error/extend.js +76 -0
  193. package/dist/error/http.d.ts +1516 -0
  194. package/dist/error/http.d.ts.map +1 -0
  195. package/dist/error/http.js +72 -0
  196. package/dist/error/payload.d.ts +3034 -0
  197. package/dist/error/payload.d.ts.map +1 -0
  198. package/dist/error/payload.js +1110 -0
  199. package/dist/error/pithyError.d.ts +154 -0
  200. package/dist/error/pithyError.d.ts.map +1 -0
  201. package/dist/error/pithyError.js +197 -0
  202. package/dist/error/terminal.d.ts +28 -0
  203. package/dist/error/terminal.d.ts.map +1 -0
  204. package/dist/error/terminal.js +33 -0
  205. package/dist/http/authContext.d.ts +21 -0
  206. package/dist/http/authContext.d.ts.map +1 -0
  207. package/dist/http/authContext.js +23 -0
  208. package/dist/http/routeContract.d.ts +39 -0
  209. package/dist/http/routeContract.d.ts.map +1 -0
  210. package/dist/http/routeContract.js +82 -0
  211. package/dist/http/sameOrigin.d.ts +47 -0
  212. package/dist/http/sameOrigin.d.ts.map +1 -0
  213. package/dist/http/sameOrigin.js +24 -0
  214. package/dist/http/signedWebhook.d.ts +225 -0
  215. package/dist/http/signedWebhook.d.ts.map +1 -0
  216. package/dist/http/signedWebhook.js +247 -0
  217. package/dist/http/validation.d.ts +37 -0
  218. package/dist/http/validation.d.ts.map +1 -0
  219. package/dist/http/validation.js +14 -0
  220. package/dist/http/verification.d.ts +10 -0
  221. package/dist/http/verification.d.ts.map +1 -0
  222. package/dist/http/verification.js +17 -0
  223. package/dist/i18n/acceptLanguage.d.ts +22 -0
  224. package/dist/i18n/acceptLanguage.d.ts.map +1 -0
  225. package/dist/i18n/acceptLanguage.js +55 -0
  226. package/dist/i18n/catalog.d.ts +39 -0
  227. package/dist/i18n/catalog.d.ts.map +1 -0
  228. package/dist/i18n/catalog.js +57 -0
  229. package/dist/i18n/locale.d.ts +74 -0
  230. package/dist/i18n/locale.d.ts.map +1 -0
  231. package/dist/i18n/locale.js +109 -0
  232. package/dist/i18n/localeMarker.d.ts +29 -0
  233. package/dist/i18n/localeMarker.d.ts.map +1 -0
  234. package/dist/i18n/localeMarker.js +112 -0
  235. package/dist/i18n/match.d.ts +26 -0
  236. package/dist/i18n/match.d.ts.map +1 -0
  237. package/dist/i18n/match.js +88 -0
  238. package/dist/i18n/registry.d.ts +25 -0
  239. package/dist/i18n/registry.d.ts.map +1 -0
  240. package/dist/i18n/registry.js +51 -0
  241. package/dist/i18n/translator.d.ts +107 -0
  242. package/dist/i18n/translator.d.ts.map +1 -0
  243. package/dist/i18n/translator.js +80 -0
  244. package/dist/index.d.ts +32 -0
  245. package/dist/index.d.ts.map +1 -0
  246. package/dist/index.js +17 -0
  247. package/dist/kv/kv.d.ts +166 -0
  248. package/dist/kv/kv.d.ts.map +1 -0
  249. package/dist/kv/kv.js +261 -0
  250. package/dist/kv/namespaces.d.ts +58 -0
  251. package/dist/kv/namespaces.d.ts.map +1 -0
  252. package/dist/kv/namespaces.js +40 -0
  253. package/dist/logger/local.d.ts +41 -0
  254. package/dist/logger/local.d.ts.map +1 -0
  255. package/dist/logger/local.js +45 -0
  256. package/dist/logger/logger.d.ts +56 -0
  257. package/dist/logger/logger.d.ts.map +1 -0
  258. package/dist/logger/logger.js +83 -0
  259. package/dist/logger/record.d.ts +919 -0
  260. package/dist/logger/record.d.ts.map +1 -0
  261. package/dist/logger/record.js +79 -0
  262. package/dist/logger/worker.d.ts +74 -0
  263. package/dist/logger/worker.d.ts.map +1 -0
  264. package/dist/logger/worker.js +54 -0
  265. package/dist/migrations/batch.d.ts +10 -0
  266. package/dist/migrations/batch.d.ts.map +1 -0
  267. package/dist/migrations/batch.js +193 -0
  268. package/dist/migrations/bookkeeping.d.ts +24 -0
  269. package/dist/migrations/bookkeeping.d.ts.map +1 -0
  270. package/dist/migrations/bookkeeping.js +57 -0
  271. package/dist/migrations/owner.d.ts +41 -0
  272. package/dist/migrations/owner.d.ts.map +1 -0
  273. package/dist/migrations/owner.js +105 -0
  274. package/dist/migrations/registry.d.ts +36 -0
  275. package/dist/migrations/registry.d.ts.map +1 -0
  276. package/dist/migrations/registry.js +70 -0
  277. package/dist/migrations/runner.d.ts +67 -0
  278. package/dist/migrations/runner.d.ts.map +1 -0
  279. package/dist/migrations/runner.js +218 -0
  280. package/dist/naming/domains.d.ts +108 -0
  281. package/dist/naming/domains.d.ts.map +1 -0
  282. package/dist/naming/domains.js +156 -0
  283. package/dist/naming/environment.d.ts +118 -0
  284. package/dist/naming/environment.d.ts.map +1 -0
  285. package/dist/naming/environment.js +190 -0
  286. package/dist/naming/feature.d.ts +68 -0
  287. package/dist/naming/feature.d.ts.map +1 -0
  288. package/dist/naming/feature.js +106 -0
  289. package/dist/naming/limits.d.ts +229 -0
  290. package/dist/naming/limits.d.ts.map +1 -0
  291. package/dist/naming/limits.js +187 -0
  292. package/dist/naming/provisionScope.d.ts +91 -0
  293. package/dist/naming/provisionScope.d.ts.map +1 -0
  294. package/dist/naming/provisionScope.js +65 -0
  295. package/dist/naming/resource.d.ts +169 -0
  296. package/dist/naming/resource.d.ts.map +1 -0
  297. package/dist/naming/resource.js +220 -0
  298. package/dist/naming/resourceNames.d.ts +83 -0
  299. package/dist/naming/resourceNames.d.ts.map +1 -0
  300. package/dist/naming/resourceNames.js +102 -0
  301. package/dist/naming/segment.d.ts +29 -0
  302. package/dist/naming/segment.d.ts.map +1 -0
  303. package/dist/naming/segment.js +31 -0
  304. package/dist/projection/asRead.d.ts +72 -0
  305. package/dist/projection/asRead.d.ts.map +1 -0
  306. package/dist/projection/asRead.js +169 -0
  307. package/dist/projection/published.d.ts +95 -0
  308. package/dist/projection/published.d.ts.map +1 -0
  309. package/dist/projection/published.js +114 -0
  310. package/dist/schema/describedness.d.ts +55 -0
  311. package/dist/schema/describedness.d.ts.map +1 -0
  312. package/dist/schema/describedness.js +180 -0
  313. package/dist/seed/compose.d.ts +41 -0
  314. package/dist/seed/compose.d.ts.map +1 -0
  315. package/dist/seed/compose.js +51 -0
  316. package/dist/seed/devLogin.d.ts +50 -0
  317. package/dist/seed/devLogin.d.ts.map +1 -0
  318. package/dist/seed/devLogin.js +52 -0
  319. package/dist/seed/exampleIdentities.d.ts +58 -0
  320. package/dist/seed/exampleIdentities.d.ts.map +1 -0
  321. package/dist/seed/exampleIdentities.js +26 -0
  322. package/dist/seed/metadata.d.ts +20 -0
  323. package/dist/seed/metadata.d.ts.map +1 -0
  324. package/dist/seed/metadata.js +27 -0
  325. package/dist/seed/seed.d.ts +264 -0
  326. package/dist/seed/seed.d.ts.map +1 -0
  327. package/dist/seed/seed.js +37 -0
  328. package/dist/seed/seededRows.d.ts +26 -0
  329. package/dist/seed/seededRows.d.ts.map +1 -0
  330. package/dist/seed/seededRows.js +20 -0
  331. package/dist/seed/writeD1.d.ts +43 -0
  332. package/dist/seed/writeD1.d.ts.map +1 -0
  333. package/dist/seed/writeD1.js +73 -0
  334. package/dist/seed/writeKv.d.ts +29 -0
  335. package/dist/seed/writeKv.d.ts.map +1 -0
  336. package/dist/seed/writeKv.js +63 -0
  337. package/dist/semver/semver.d.ts +62 -0
  338. package/dist/semver/semver.d.ts.map +1 -0
  339. package/dist/semver/semver.js +137 -0
  340. package/dist/text/comments.d.ts +62 -0
  341. package/dist/text/comments.d.ts.map +1 -0
  342. package/dist/text/comments.js +161 -0
  343. package/dist/version.generated.d.ts +5 -0
  344. package/dist/version.generated.d.ts.map +1 -0
  345. package/dist/version.generated.js +7 -0
  346. package/dist/worker/health.d.ts +39 -0
  347. package/dist/worker/health.d.ts.map +1 -0
  348. package/dist/worker/health.js +41 -0
  349. package/dist/worker/identity.d.ts +191 -0
  350. package/dist/worker/identity.d.ts.map +1 -0
  351. package/dist/worker/identity.js +162 -0
  352. package/dist/workflow/bindings.d.ts +41 -0
  353. package/dist/workflow/bindings.d.ts.map +1 -0
  354. package/dist/workflow/bindings.js +53 -0
  355. package/dist/workflow/dispatch.d.ts +98 -0
  356. package/dist/workflow/dispatch.d.ts.map +1 -0
  357. package/dist/workflow/dispatch.js +148 -0
  358. package/dist/workflow/dispatchRoute.d.ts +80 -0
  359. package/dist/workflow/dispatchRoute.d.ts.map +1 -0
  360. package/dist/workflow/dispatchRoute.js +134 -0
  361. package/dist/workflow/faults.d.ts +97 -0
  362. package/dist/workflow/faults.d.ts.map +1 -0
  363. package/dist/workflow/faults.js +111 -0
  364. package/dist/workflow/host.d.ts +176 -0
  365. package/dist/workflow/host.d.ts.map +1 -0
  366. package/dist/workflow/host.js +116 -0
  367. package/dist/workflow/hostEntry.d.ts +50 -0
  368. package/dist/workflow/hostEntry.d.ts.map +1 -0
  369. package/dist/workflow/hostEntry.js +24 -0
  370. package/dist/workflow/hostEnv.d.ts +143 -0
  371. package/dist/workflow/hostEnv.d.ts.map +1 -0
  372. package/dist/workflow/hostEnv.js +170 -0
  373. package/dist/workflow/loopback.d.ts +52 -0
  374. package/dist/workflow/loopback.d.ts.map +1 -0
  375. package/dist/workflow/loopback.js +76 -0
  376. package/dist/workflow/naming.d.ts +79 -0
  377. package/dist/workflow/naming.d.ts.map +1 -0
  378. package/dist/workflow/naming.js +138 -0
  379. package/dist/workflow/register.d.ts +19 -0
  380. package/dist/workflow/register.d.ts.map +1 -0
  381. package/dist/workflow/register.js +40 -0
  382. package/dist/workflow/schemas.d.ts +44 -0
  383. package/dist/workflow/schemas.d.ts.map +1 -0
  384. package/dist/workflow/schemas.js +43 -0
  385. package/dist/workflow/spec.d.ts +81 -0
  386. package/dist/workflow/spec.d.ts.map +1 -0
  387. package/dist/workflow/spec.js +0 -0
  388. package/dist/workflow/stepMessage.d.ts +122 -0
  389. package/dist/workflow/stepMessage.d.ts.map +1 -0
  390. package/dist/workflow/stepMessage.js +152 -0
  391. package/package.json +20 -9
  392. package/src/capability/manifest.ts +21 -0
  393. package/src/version.generated.ts +1 -1
@@ -0,0 +1,148 @@
1
+ import { PithyError, messageOf } from "../error/pithyError.js";
2
+ import "../worker/identity.js";
3
+ import { noopLogger } from "../logger/logger.js";
4
+ import { originVarName } from "../env/stem.js";
5
+ import { loopbackWorkflowBinding } from "./loopback.js";
6
+ //#region src/workflow/dispatch.ts
7
+ /**
8
+ * Read a binding off the per-request env, narrowed to the one method dispatch needs.
9
+ *
10
+ * Exported because the host's dispatch route ({@link ./dispatchRoute.ts}) asks the same question of
11
+ * the same env, and a second duck-type would be a second answer to "is this a Workflow binding" the
12
+ * first day one of them learned about a new shape.
13
+ */
14
+ function workflowBindingFor(env, name) {
15
+ const value = env[name];
16
+ if (typeof value !== "object" || value === null) return void 0;
17
+ return typeof value.create === "function" ? value : void 0;
18
+ }
19
+ /** The one environment a loopback substitution is made in. Verbatim, and the only one. */
20
+ const LOOPBACK_ENVIRONMENT = "dev";
21
+ /** A non-blank string off the env, or `undefined`. Anything else on that key is not an address. */
22
+ function stringVar(env, name) {
23
+ const value = env[name];
24
+ if (typeof value !== "string") return void 0;
25
+ const trimmed = value.trim();
26
+ return trimmed.length > 0 ? trimmed : void 0;
27
+ }
28
+ /**
29
+ * The binding to dispatch on: the real one, or — under `pithy dev` — a loopback stand-in for it.
30
+ *
31
+ * ## Why a stand-in exists at all
32
+ *
33
+ * A deployed app Worker starts a capability's Workflow through a **cross-script** binding pointing at
34
+ * `<project>-<env>-email`. `pithy dev` runs no script under that name, so locally that binding cannot
35
+ * work — it is absent, or it is present and every `create` on it fails — and every dispatch it carried
36
+ * went nowhere (pithy-sh/pithy#410). What *does* exist locally is the capability host itself, on a
37
+ * pinned port, with its address in this Worker's own vars as `<STEM>_ORIGIN`. So the seam is filled
38
+ * rather than left empty, and the call site — `enqueueEmail`, the cron handler, `c.var.workflows` — is
39
+ * byte-identical either way.
40
+ *
41
+ * ## Three rules, and each of them is a refusal
42
+ *
43
+ * **In `dev`, a published origin wins over the binding.** The deliberate order, and the one that
44
+ * closes #410 whatever wrangler decides to hand a local Worker for a script it is not running: a
45
+ * binding that is present but cannot reach anything is indistinguishable at runtime from one that
46
+ * works, so preferring it would leave the silence in place and depend on a wrangler behavior nothing
47
+ * here controls. Preferring the origin costs nothing, because a published origin is not something a
48
+ * composition can have by accident — see the next rule.
49
+ *
50
+ * **A published origin names a host `pithy dev` is running.** The orchestrator writes one `--var` per
51
+ * *capability host* in the dev set, and never hands a host its own address. So `EMAIL_ORIGIN` on a
52
+ * Worker's env means exactly one thing: the email host is up, locally, there. An adopter's own
53
+ * app-owned Workflow — the same-script shape, which `wrangler dev` implements unchanged — is never
54
+ * published an origin and so is never diverted.
55
+ *
56
+ * **Only `dev`.** Not `staging`, not `prod`, and — the case that matters — not a composition that
57
+ * stamped no environment at all. A gate that reads silence as `dev` opens itself in exactly the
58
+ * deployment whose `wrangler.jsonc` lost the var.
59
+ *
60
+ * **The environment comes off the request env, not off the host's shell.** `--var ENVIRONMENT` is
61
+ * what puts it on a Worker, and the shell that ran `wrangler dev` does not cross into workerd. The
62
+ * dispatch *route* reads the ambient env instead, because it is registered before any request exists;
63
+ * here there is a request env in hand, and it is the truthful one.
64
+ */
65
+ function resolveWorkflowBinding(env, request) {
66
+ const origin = stringVar(env, "ENVIRONMENT") === LOOPBACK_ENVIRONMENT ? stringVar(env, originVarName(request.capability)) : void 0;
67
+ if (origin) {
68
+ request.log?.debug("workflow dispatch over loopback", {
69
+ binding: request.binding,
70
+ capability: request.capability,
71
+ origin
72
+ });
73
+ return loopbackWorkflowBinding({
74
+ origin,
75
+ binding: request.binding,
76
+ capability: request.capability,
77
+ fetch: request.fetch
78
+ });
79
+ }
80
+ return workflowBindingFor(env, request.binding);
81
+ }
82
+ /**
83
+ * Start one registered job. The single dispatch path — `createBackend`'s per-request dispatcher and
84
+ * `createEntrypoint`'s cron handler both route through here, so a scheduled run and a manual one
85
+ * validate identically.
86
+ */
87
+ async function triggerWorkflow(env, registry, key, params, log = noopLogger) {
88
+ const entry = registry[key];
89
+ if (!entry) {
90
+ const known = Object.keys(registry).sort().join(", ");
91
+ throw new PithyError({
92
+ code: "core/unknown_workflow",
93
+ status: 500,
94
+ message: `No workflow is registered as "${key}".`,
95
+ action: "Check the key, and compose the capability that declares the job.",
96
+ detail: known ? `Registered workflows: ${known}.` : "No capability registered any workflow."
97
+ });
98
+ }
99
+ const parsed = entry.spec.params.safeParse(params);
100
+ if (!parsed.success) throw new PithyError({
101
+ code: "core/invalid_workflow_params",
102
+ status: 400,
103
+ message: `The parameters for workflow "${key}" are not valid.`,
104
+ action: "Correct the parameters to match the workflow's schema.",
105
+ detail: parsed.error.issues.map((issue) => `${issue.path.join(".")}: ${issue.message}`).join("; ")
106
+ });
107
+ const binding = resolveWorkflowBinding(env, {
108
+ binding: entry.spec.binding,
109
+ capability: entry.capability,
110
+ log
111
+ });
112
+ if (!binding) {
113
+ if (entry.spec.optional) {
114
+ log.warn("workflow skipped", {
115
+ workflow: key,
116
+ binding: entry.spec.binding,
117
+ reason: "binding absent — the workflow host is not deployed or not bound"
118
+ });
119
+ return;
120
+ }
121
+ throw new PithyError({
122
+ code: "core/missing_workflow_binding",
123
+ status: 500,
124
+ message: `The workflow binding "${entry.spec.binding}" is not available.`,
125
+ action: `Deploy the ${entry.capability} workflow host, then bind ${entry.spec.binding} in wrangler.jsonc.`,
126
+ detail: `Dispatching "${key}" needs a Workflow binding named ${entry.spec.binding} on the Worker env.`
127
+ });
128
+ }
129
+ try {
130
+ await binding.create({ params: parsed.data });
131
+ } catch (error) {
132
+ if (!entry.spec.optional) throw error;
133
+ log.warn("workflow skipped", {
134
+ workflow: key,
135
+ binding: entry.spec.binding,
136
+ reason: messageOf(error)
137
+ });
138
+ }
139
+ }
140
+ /**
141
+ * Build the per-request dispatcher over one registry. A closure rather than a lazily-built registry
142
+ * object: there is one method and no per-job resource to construct, so there is nothing to defer.
143
+ */
144
+ function buildWorkflowDispatcher(env, registry, log = noopLogger) {
145
+ return { trigger: (key, params) => triggerWorkflow(env, registry, key, params, log) };
146
+ }
147
+ //#endregion
148
+ export { buildWorkflowDispatcher, resolveWorkflowBinding, triggerWorkflow, workflowBindingFor };
@@ -0,0 +1,80 @@
1
+ import type { Hono } from "hono";
2
+ import type { PithyHonoEnv } from "../capability/capability";
3
+ import { type AmbientEnv } from "../env/ambient";
4
+ import type { VerificationStrategy } from "../http/verification";
5
+ import type { WorkflowRegistry } from "./spec";
6
+ /**
7
+ * The host's dispatch route — the loopback door `pithy dev` starts an instance through.
8
+ *
9
+ * ## Why a host needs an HTTP door at all
10
+ *
11
+ * A deployed app Worker starts a capability's Workflow through a **cross-script** binding:
12
+ * `{ binding: "EMAIL_SENDER", script_name: "<project>-<env>-email" }`. `pithy dev` does not run that
13
+ * second Worker's script under that name, and CLAUDE.md rules out wrangler's cross-`wrangler dev`
14
+ * registry, so locally the binding names nothing and the call throws (pithy-sh/pithy#410). What
15
+ * *does* exist locally is loopback: every worker in the dev set has a pinned port and an
16
+ * `<STEM>_ORIGIN` in every sibling's env. This route is the other end of that wire.
17
+ *
18
+ * The instance is started on the **host's own same-script binding** — the one with no `script_name`,
19
+ * which `wrangler dev` implements unchanged. So the local path and the deployed path run the same
20
+ * Workflow class, from the same registry, with the same params schema. Only the hop differs.
21
+ *
22
+ * ## The environment gate is the security boundary
23
+ *
24
+ * An HTTP door into another Worker's Workflows is not something the kit leaves open. The route is
25
+ * served in a `dev` composition and refused in every other, by a guard that runs before anything
26
+ * reads the request.
27
+ *
28
+ * **It is a mounted refusal, not an unmounted route** — deliberately the opposite of
29
+ * `@pithy-sh/auth`'s dev-login, and the difference is what the two routes do. Dev-login mints an
30
+ * authenticated session with no credential presented, so its existence in a production route table is
31
+ * itself the finding, and it registers nothing outside `dev`. This route starts a job the host
32
+ * already runs on its own cron, on a binding the caller must already have, and it starts nothing at
33
+ * all before the guard has run. What an operator gains from mounting it is the answer: a loopback
34
+ * dispatcher wired into `staging` by mistake gets `auth/forbidden` naming the binding as the path,
35
+ * rather than a 404 that reads as a typo and sends them looking at the URL.
36
+ *
37
+ * **There is no second CI gate**, and that too is the opposite of dev-login's, on purpose. CI is
38
+ * precisely where the local loop has to work — `pithy dev` under an integration suite is a `dev`
39
+ * composition dispatching over loopback — and a gate that shut it there would turn the silence #410
40
+ * repairs back on in the one place nobody is watching for it.
41
+ *
42
+ * ## Contract
43
+ *
44
+ * `public` verification and a declared request contract, with the guard ahead of both validators so
45
+ * a refusal is never downgraded to a 400 that tells a caller which request shapes are well-formed.
46
+ */
47
+ /**
48
+ * How the route verifies its caller: it does not.
49
+ *
50
+ * Stated rather than omitted, because every route declares one (CLAUDE.md §HTTP) and `public` is the
51
+ * honest answer — there is no credential on this wire. What stands in for one is the environment
52
+ * gate plus the address: the route answers only in a `dev` composition, and a `dev` composition is a
53
+ * `wrangler dev` bound to loopback on a port `pithy dev` pinned. Deployed environments reach the same
54
+ * Workflow through the cross-script binding and never through this.
55
+ */
56
+ export declare const WORKFLOW_DISPATCH_VERIFICATION: VerificationStrategy;
57
+ /** What the route needs to serve: whose host this is, what it hosts, and where it is running. */
58
+ export interface WorkflowDispatchOptions {
59
+ /** The capability that owns the host. Named in refusals, so an operator knows which Worker answered. */
60
+ capability: string;
61
+ /**
62
+ * The composed registry this host runs. The route resolves `:binding` against it, so a binding the
63
+ * host hosts no Workflow for is refused by name — and the params validate against the declaring
64
+ * spec's own schema rather than against anything restated here.
65
+ */
66
+ registry: WorkflowRegistry;
67
+ /**
68
+ * The ambient environment the gate reads. Defaults to the process env, which in a Worker is the
69
+ * script's own vars — where `pithy init` stamps `ENVIRONMENT`. Injectable for tests.
70
+ */
71
+ env?: AmbientEnv;
72
+ }
73
+ /**
74
+ * Mount the dispatch route on a host worker's app.
75
+ *
76
+ * The guard is registered as middleware ahead of the validators so the order is structural rather
77
+ * than remembered: outside `dev` nothing reads the path, the body, or the env.
78
+ */
79
+ export declare function registerWorkflowDispatchRoute(app: Hono<PithyHonoEnv>, options: WorkflowDispatchOptions): void;
80
+ //# sourceMappingURL=dispatchRoute.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dispatchRoute.d.ts","sourceRoot":"","sources":["../../src/workflow/dispatchRoute.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAW,IAAI,EAAQ,MAAM,MAAM,CAAC;AAChD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EAAE,KAAK,UAAU,EAAsC,MAAM,gBAAgB,CAAC;AAGrF,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAQjE,OAAO,KAAK,EAAsB,gBAAgB,EAAE,MAAM,QAAQ,CAAC;AAEnE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AAEH;;;;;;;;GAQG;AACH,eAAO,MAAM,8BAA8B,EAAE,oBAA+B,CAAC;AAK7E,iGAAiG;AACjG,MAAM,WAAW,uBAAuB;IACtC,wGAAwG;IACxG,UAAU,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,QAAQ,EAAE,gBAAgB,CAAC;IAC3B;;;OAGG;IACH,GAAG,CAAC,EAAE,UAAU,CAAC;CAClB;AAqBD;;;;;GAKG;AACH,wBAAgB,6BAA6B,CAAC,GAAG,EAAE,IAAI,CAAC,YAAY,CAAC,EAAE,OAAO,EAAE,uBAAuB,GAAG,IAAI,CAkE7G"}
@@ -0,0 +1,134 @@
1
+ import { ForbiddenError, PithyError } from "../error/pithyError.js";
2
+ import { validationHook } from "../http/validation.js";
3
+ import { WORKFLOW_DISPATCH_ROUTE, WorkflowDispatchParams, WorkflowDispatchRequest } from "./schemas.js";
4
+ import { workflowBindingFor } from "./dispatch.js";
5
+ import { ambientEnv, compositionEnvironment } from "../env/ambient.js";
6
+ import { zValidator } from "@hono/zod-validator";
7
+ //#region src/workflow/dispatchRoute.ts
8
+ /**
9
+ * The host's dispatch route — the loopback door `pithy dev` starts an instance through.
10
+ *
11
+ * ## Why a host needs an HTTP door at all
12
+ *
13
+ * A deployed app Worker starts a capability's Workflow through a **cross-script** binding:
14
+ * `{ binding: "EMAIL_SENDER", script_name: "<project>-<env>-email" }`. `pithy dev` does not run that
15
+ * second Worker's script under that name, and CLAUDE.md rules out wrangler's cross-`wrangler dev`
16
+ * registry, so locally the binding names nothing and the call throws (pithy-sh/pithy#410). What
17
+ * *does* exist locally is loopback: every worker in the dev set has a pinned port and an
18
+ * `<STEM>_ORIGIN` in every sibling's env. This route is the other end of that wire.
19
+ *
20
+ * The instance is started on the **host's own same-script binding** — the one with no `script_name`,
21
+ * which `wrangler dev` implements unchanged. So the local path and the deployed path run the same
22
+ * Workflow class, from the same registry, with the same params schema. Only the hop differs.
23
+ *
24
+ * ## The environment gate is the security boundary
25
+ *
26
+ * An HTTP door into another Worker's Workflows is not something the kit leaves open. The route is
27
+ * served in a `dev` composition and refused in every other, by a guard that runs before anything
28
+ * reads the request.
29
+ *
30
+ * **It is a mounted refusal, not an unmounted route** — deliberately the opposite of
31
+ * `@pithy-sh/auth`'s dev-login, and the difference is what the two routes do. Dev-login mints an
32
+ * authenticated session with no credential presented, so its existence in a production route table is
33
+ * itself the finding, and it registers nothing outside `dev`. This route starts a job the host
34
+ * already runs on its own cron, on a binding the caller must already have, and it starts nothing at
35
+ * all before the guard has run. What an operator gains from mounting it is the answer: a loopback
36
+ * dispatcher wired into `staging` by mistake gets `auth/forbidden` naming the binding as the path,
37
+ * rather than a 404 that reads as a typo and sends them looking at the URL.
38
+ *
39
+ * **There is no second CI gate**, and that too is the opposite of dev-login's, on purpose. CI is
40
+ * precisely where the local loop has to work — `pithy dev` under an integration suite is a `dev`
41
+ * composition dispatching over loopback — and a gate that shut it there would turn the silence #410
42
+ * repairs back on in the one place nobody is watching for it.
43
+ *
44
+ * ## Contract
45
+ *
46
+ * `public` verification and a declared request contract, with the guard ahead of both validators so
47
+ * a refusal is never downgraded to a 400 that tells a caller which request shapes are well-formed.
48
+ */
49
+ /**
50
+ * How the route verifies its caller: it does not.
51
+ *
52
+ * Stated rather than omitted, because every route declares one (CLAUDE.md §HTTP) and `public` is the
53
+ * honest answer — there is no credential on this wire. What stands in for one is the environment
54
+ * gate plus the address: the route answers only in a `dev` composition, and a `dev` composition is a
55
+ * `wrangler dev` bound to loopback on a port `pithy dev` pinned. Deployed environments reach the same
56
+ * Workflow through the cross-script binding and never through this.
57
+ */
58
+ const WORKFLOW_DISPATCH_VERIFICATION = "public";
59
+ /** The environment this route serves in. Verbatim, and the only one. */
60
+ const DISPATCH_ENVIRONMENT = "dev";
61
+ /** The job this host hosts on a given binding, or `undefined`. */
62
+ function specForBinding(registry, binding) {
63
+ return Object.values(registry).find((entry) => entry.spec.binding === binding);
64
+ }
65
+ /**
66
+ * The refusal. `auth/forbidden` (403) rather than a 404: the door exists, it is shut because of
67
+ * *where* this Worker is running, and pretending otherwise costs the operator the sentence that ends
68
+ * their search. `action` names the real path and is stripped before it reaches the caller, like every
69
+ * other action.
70
+ */
71
+ function refuseOutsideDev(capability, environment) {
72
+ return new ForbiddenError({
73
+ message: `Workflow dispatch over HTTP is served only in the ${DISPATCH_ENVIRONMENT} environment.`,
74
+ action: `Dispatch through the cross-script ${capability} Workflow binding, or run this host under pithy dev.`,
75
+ detail: `The composition's ENVIRONMENT is ${environment ?? "unstamped"}.`
76
+ });
77
+ }
78
+ /**
79
+ * Mount the dispatch route on a host worker's app.
80
+ *
81
+ * The guard is registered as middleware ahead of the validators so the order is structural rather
82
+ * than remembered: outside `dev` nothing reads the path, the body, or the env.
83
+ */
84
+ function registerWorkflowDispatchRoute(app, options) {
85
+ const { capability, registry } = options;
86
+ const ambient = options.env;
87
+ const guard = async (_c, next) => {
88
+ const environment = compositionEnvironment(ambient ?? ambientEnv());
89
+ if (environment !== DISPATCH_ENVIRONMENT) throw refuseOutsideDev(capability, environment);
90
+ await next();
91
+ };
92
+ app.post(WORKFLOW_DISPATCH_ROUTE, guard, zValidator("param", WorkflowDispatchParams, validationHook), zValidator("json", WorkflowDispatchRequest, validationHook), async (c) => {
93
+ const { binding } = c.req.valid("param");
94
+ const { id, params } = c.req.valid("json");
95
+ const entry = specForBinding(registry, binding);
96
+ if (!entry) {
97
+ const known = Object.values(registry).map((registered) => registered.spec.binding).sort().join(", ");
98
+ throw new PithyError({
99
+ code: "core/unknown_workflow",
100
+ status: 500,
101
+ message: `No workflow on this host is bound to "${binding}".`,
102
+ action: `Check the binding name against the ${capability} capability's declared workflows.`,
103
+ detail: known ? `This host hosts: ${known}.` : "This host hosts no workflows at all."
104
+ });
105
+ }
106
+ const parsed = entry.spec.params.safeParse(params);
107
+ if (!parsed.success) throw new PithyError({
108
+ code: "core/invalid_workflow_params",
109
+ status: 400,
110
+ message: `The parameters for workflow "${entry.key}" are not valid.`,
111
+ action: "Correct the parameters to match the workflow's schema.",
112
+ detail: parsed.error.issues.map((issue) => `${issue.path.join(".")}: ${issue.message}`).join("; ")
113
+ });
114
+ const target = workflowBindingFor(c.env, binding);
115
+ if (!target) throw new PithyError({
116
+ code: "core/missing_workflow_binding",
117
+ status: 500,
118
+ message: `The workflow binding "${binding}" is not available on this host.`,
119
+ action: `Bind ${binding} in the ${capability} host's wrangler.jsonc, then restart it.`,
120
+ detail: `Dispatching "${entry.key}" needs a same-script Workflow binding named ${binding}.`
121
+ });
122
+ await target.create({
123
+ id,
124
+ params: parsed.data
125
+ });
126
+ return c.json({
127
+ binding,
128
+ id,
129
+ started: true
130
+ }, 202);
131
+ });
132
+ }
133
+ //#endregion
134
+ export { WORKFLOW_DISPATCH_VERIFICATION, registerWorkflowDispatchRoute };
@@ -0,0 +1,97 @@
1
+ /**
2
+ * **Every fault that reaches a durable step is classified — retryable or terminal — at a stated site.**
3
+ *
4
+ * A Workflow is chosen *because of* its retry semantics: durable execution, a journal, steps that resume,
5
+ * backoff that survives an eviction. A step that inherits the platform default has not decided; it has
6
+ * deferred, and the default is retry-everything. So a deterministic refusal — a name that already exists,
7
+ * a value too large, a payload that will not parse — is answered slowly instead of immediately, the retry
8
+ * budget burns down on an answer that cannot change, and an instant refusal looks like an outage
9
+ * (pithy-sh/pithy#338).
10
+ *
11
+ * ## The three layers, and which owns what
12
+ *
13
+ * 1. **The capability states its own codes.** A {@link WorkflowRetryPolicy} names the `PithyError` codes
14
+ * that capability retries, each with the reason a second attempt can answer differently. One statement
15
+ * per capability, in one file, the way `@pithy-sh/email`'s `errorMapping.ts` already states the Email
16
+ * Service's `E_*` codes.
17
+ * 2. **Core owns D1.** `withD1Retry` already classifies D1's fault vocabulary, so this asks it rather than
18
+ * growing a second copy — and that is also the no-double-retry rule: a fault the inner layer refused to
19
+ * retry (a constraint violation, a SQL error) is terminal here too, while a transient one stays
20
+ * retryable so the step re-drives it with backoff far longer than the inner loop's.
21
+ * 3. **Everything else is terminal.** Retry is opted into, never inherited. The platform default is the
22
+ * opposite, and it is the wrong way round: a terminal fault wrongly called transient burns the whole
23
+ * budget before surfacing, while a transient one wrongly called terminal surfaces immediately with the
24
+ * fault written on it. One of those is a diagnosis; the other is a hang.
25
+ *
26
+ * ## Where the wrapping happens, and why it cannot be anywhere else
27
+ *
28
+ * {@link classifiedSteps} converts a terminal fault **inside** the step callback. The platform's retry loop
29
+ * lives inside `step.do`: by the time a `step.do` promise rejects, every retry has already been spent. A
30
+ * wrapper around the call would classify a fault the engine had already re-driven five times — correct
31
+ * looking, and exactly as slow as doing nothing.
32
+ */
33
+ /** Whether the platform should try a step's body again, or stop with the answer it has. */
34
+ export type WorkflowFaultDisposition = "retry" | "terminal";
35
+ /**
36
+ * One capability's stated retry classification — the whole of what it retries, and why.
37
+ *
38
+ * A record rather than a set, because the value is the argument: "what would a second attempt see that
39
+ * this one did not" is the question that decides, and writing the answer down is what keeps the next code
40
+ * from being added to the list by reflex.
41
+ */
42
+ export interface WorkflowRetryPolicy {
43
+ /** The capability whose Workflows this governs — `secrets`, `payments`, `email`. Named in the fault's reason. */
44
+ capability: string;
45
+ /**
46
+ * `PithyError` code → why a retry can answer differently. **Everything absent is terminal**, including
47
+ * codes another capability retries: a 502 from a payment rail is worth re-driving, and a 502 from a
48
+ * store that has already taken the write is not.
49
+ */
50
+ retryable: Readonly<Record<string, string>>;
51
+ }
52
+ /** One fault, classified: what it was, what happens next, and the stated reason for it. */
53
+ export interface WorkflowFault {
54
+ /** Retry, or stop. */
55
+ disposition: WorkflowFaultDisposition;
56
+ /** The `PithyError` code, `d1/<fault>` for a D1 fault class, or `unclassified`. */
57
+ code: string;
58
+ /** Why — the policy's own sentence for a retry, or what made this terminal. */
59
+ reason: string;
60
+ }
61
+ /**
62
+ * Classify one fault against a capability's policy. Pure, so a capability's classification is a unit
63
+ * test rather than a claim — and so the same function decides in a test, in Miniflare, and in production.
64
+ */
65
+ export declare function classifyWorkflowFault(error: unknown, policy: WorkflowRetryPolicy): WorkflowFault;
66
+ /** The half of a Workflow step this kit uses: run a named body, or serve its journalled result. */
67
+ export interface WorkflowStepLike {
68
+ /** Run a named step, or return its journalled result if this instance already completed it. */
69
+ do<T>(name: string, fn: () => Promise<T>): Promise<T>;
70
+ }
71
+ /**
72
+ * The platform's terminal-error class — `NonRetryableError` from `cloudflare:workflows`, taken as a
73
+ * parameter.
74
+ *
75
+ * Injected rather than imported because `@pithy-sh/core` is bundled into every adopter's Worker *and*
76
+ * imported by the CLI under node, so it may not import a `cloudflare:` module. The Workflow class is
77
+ * already inside workerd and already imports one; handing the class in costs it a word and keeps the
78
+ * classification itself testable anywhere.
79
+ *
80
+ * **One argument, deliberately.** `NonRetryableError`'s constructor also takes a `name`, and the engine
81
+ * recognizes the error *by* that name: measured against a real Workflow in `wrangler dev`, passing any
82
+ * name but the default turned an instance that failed in 0.7s back into one that retried for 32s and
83
+ * then failed. The signature is narrowed so nobody can reintroduce that by being helpful.
84
+ */
85
+ export interface TerminalErrorConstructor {
86
+ new (message: string): Error;
87
+ }
88
+ /**
89
+ * Wrap a Workflow's step runner so every fault it raises is classified against `policy`, and a terminal
90
+ * one is re-thrown as the platform's terminal error — **inside the step**, before the retry loop reads it.
91
+ *
92
+ * The result is structurally a step runner, so a body that already takes one (`reconcilePayments`,
93
+ * `runSendBatch`, `runAtRestKeyRotation`) takes this instead with no change: the classification is
94
+ * declared once, at the entrypoint, and every step below it inherits the decision.
95
+ */
96
+ export declare function classifiedSteps(step: WorkflowStepLike, policy: WorkflowRetryPolicy, terminalError: TerminalErrorConstructor): WorkflowStepLike;
97
+ //# sourceMappingURL=faults.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"faults.d.ts","sourceRoot":"","sources":["../../src/workflow/faults.ts"],"names":[],"mappings":"AAOA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,2FAA2F;AAC3F,MAAM,MAAM,wBAAwB,GAAG,OAAO,GAAG,UAAU,CAAC;AAE5D;;;;;;GAMG;AACH,MAAM,WAAW,mBAAmB;IAClC,iHAAiH;IACjH,UAAU,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CAC7C;AAED,2FAA2F;AAC3F,MAAM,WAAW,aAAa;IAC5B,sBAAsB;IACtB,WAAW,EAAE,wBAAwB,CAAC;IACtC,mFAAmF;IACnF,IAAI,EAAE,MAAM,CAAC;IACb,+EAA+E;IAC/E,MAAM,EAAE,MAAM,CAAC;CAChB;AAsDD;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,mBAAmB,GAAG,aAAa,CA0BhG;AAED,mGAAmG;AACnG,MAAM,WAAW,gBAAgB;IAC/B,+FAA+F;IAC/F,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;CACvD;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,wBAAwB;IACvC,KAAK,OAAO,EAAE,MAAM,GAAG,KAAK,CAAC;CAC9B;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,gBAAgB,EACtB,MAAM,EAAE,mBAAmB,EAC3B,aAAa,EAAE,wBAAwB,GACtC,gBAAgB,CA2BlB"}
@@ -0,0 +1,111 @@
1
+ import { PithyError } from "../error/pithyError.js";
2
+ import { d1TransientFault } from "../data/withD1Retry.js";
3
+ import { encodeWorkflowStepMessage } from "./stepMessage.js";
4
+ //#region src/workflow/faults.ts
5
+ /** The `code` reported for a throw no layer recognizes. */
6
+ const UNCLASSIFIED = "unclassified";
7
+ /**
8
+ * The code a thrown value carries, or `null` when it is not a `PithyError`.
9
+ *
10
+ * The structural read is not belt-and-braces: `instanceof` is per module instance, and a bundler that
11
+ * gives a host worker two copies of core would silently answer `false` for a perfectly good
12
+ * `PithyError` — which here means classifying a stated retryable fault as terminal. The shape is
13
+ * checked, not assumed.
14
+ */
15
+ function pithyCodeOf(error) {
16
+ if (error instanceof PithyError) return error.payload.code;
17
+ if (error && typeof error === "object" && "payload" in error) {
18
+ const payload = error.payload;
19
+ if (payload && typeof payload === "object" && "code" in payload) {
20
+ const code = payload.code;
21
+ if (typeof code === "string") return code;
22
+ }
23
+ }
24
+ return null;
25
+ }
26
+ /** The public message of a thrown value, for the terminal error's own text. */
27
+ function messageOf(error) {
28
+ return error instanceof Error ? error.message : String(error);
29
+ }
30
+ /**
31
+ * The operator's remedy a thrown value carries, or `undefined` when it states none.
32
+ *
33
+ * Read structurally for the same reason {@link pithyCodeOf} is: `instanceof` is per module instance,
34
+ * and two copies of core in one bundle would answer `false` for a perfectly good `PithyError` — here
35
+ * meaning a remedy the raising error stated is silently dropped at the boundary. That is the bug this
36
+ * function exists to end, so it is not reintroduced by the check.
37
+ *
38
+ * **`detail` is deliberately not read here, and there is no sibling for it.** `action` is already
39
+ * operator-facing — the CLI prints it, `operatorError` includes it. `detail` is throw-site context and
40
+ * stays at the throw site.
41
+ */
42
+ function pithyActionOf(error) {
43
+ if (error instanceof PithyError) return error.payload.action;
44
+ if (error && typeof error === "object" && "payload" in error) {
45
+ const payload = error.payload;
46
+ if (payload && typeof payload === "object" && "action" in payload) {
47
+ const action = payload.action;
48
+ if (typeof action === "string") return action;
49
+ }
50
+ }
51
+ }
52
+ /**
53
+ * Classify one fault against a capability's policy. Pure, so a capability's classification is a unit
54
+ * test rather than a claim — and so the same function decides in a test, in Miniflare, and in production.
55
+ */
56
+ function classifyWorkflowFault(error, policy) {
57
+ const code = pithyCodeOf(error);
58
+ if (code !== null) {
59
+ const reason = policy.retryable[code];
60
+ if (reason !== void 0) return {
61
+ disposition: "retry",
62
+ code,
63
+ reason
64
+ };
65
+ return {
66
+ disposition: "terminal",
67
+ code,
68
+ reason: `${policy.capability} does not retry ${code}: a second attempt reaches the same answer.`
69
+ };
70
+ }
71
+ const d1 = d1TransientFault(error);
72
+ if (d1 !== null) return {
73
+ disposition: "retry",
74
+ code: `d1/${d1}`,
75
+ reason: `D1 reported a transient ${d1} fault; withD1Retry classifies it retryable.`
76
+ };
77
+ return {
78
+ disposition: "terminal",
79
+ code: UNCLASSIFIED,
80
+ reason: `Nothing classifies this fault, and ${policy.capability} retries only what it states.`
81
+ };
82
+ }
83
+ /**
84
+ * Wrap a Workflow's step runner so every fault it raises is classified against `policy`, and a terminal
85
+ * one is re-thrown as the platform's terminal error — **inside the step**, before the retry loop reads it.
86
+ *
87
+ * The result is structurally a step runner, so a body that already takes one (`reconcilePayments`,
88
+ * `runSendBatch`, `runAtRestKeyRotation`) takes this instead with no change: the classification is
89
+ * declared once, at the entrypoint, and every step below it inherits the decision.
90
+ */
91
+ function classifiedSteps(step, policy, terminalError) {
92
+ return { do(name, fn) {
93
+ return step.do(name, async () => {
94
+ try {
95
+ return await fn();
96
+ } catch (error) {
97
+ const fault = classifyWorkflowFault(error, policy);
98
+ if (fault.disposition === "retry") throw error;
99
+ const terminal = new terminalError(encodeWorkflowStepMessage({
100
+ code: fault.code,
101
+ message: messageOf(error),
102
+ action: pithyActionOf(error)
103
+ }));
104
+ terminal.cause = error;
105
+ throw terminal;
106
+ }
107
+ });
108
+ } };
109
+ }
110
+ //#endregion
111
+ export { classifiedSteps, classifyWorkflowFault };