@pithy-sh/core 0.1.2 → 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,136 @@
1
+ import { ControlPlaneScope } from "../scope/scope.js";
2
+ import { CapabilityHealthReport, HealthSummary, HealthSummaryKey, healthReport, healthWire } from "./healthSummary.js";
3
+ import { ManifestConfigKey, ManifestConfigValues } from "./configuration.js";
4
+ import { z } from "zod";
5
+ //#region src/controlPlane/discovery/adminRoute.ts
6
+ /**
7
+ * What a capability tells a management client about its own admin surface.
8
+ *
9
+ * **This exists because knowing a capability is installed is not enough to call it.** A management
10
+ * client that learns only names still has to hold a route table for every capability, guess that
11
+ * payments is mounted at `/payments`, and know out-of-band which scope each operation needs. Every one
12
+ * of those is a thing that can be wrong, and `basePath` is configurable — an adopter who mounts
13
+ * payments at `/billing` would silently 404 a client that hardcoded the default.
14
+ *
15
+ * So the capability declares the routes it actually registered, with the paths it actually used, and
16
+ * `GET /control-plane/manifest` reports them. That is what makes discovery-over-configuration real
17
+ * rather than aspirational: a client composes its navigation *and* its calls from what the Worker says
18
+ * about itself, and an adopter who moves a mount point or upgrades a capability changes what the client
19
+ * does without either side coordinating.
20
+ *
21
+ * **The declaration is checked against the router**, not trusted — see `missingAdminRoutes`. A manifest
22
+ * that drifts from the routes is worse than no manifest, because a client would believe it.
23
+ */
24
+ /** The HTTP methods an admin route may use. */
25
+ const AdminRouteMethod = z.enum([
26
+ "GET",
27
+ "POST",
28
+ "PUT",
29
+ "PATCH",
30
+ "DELETE"
31
+ ]).describe("The HTTP method this admin route answers on.");
32
+ /** One admin route a capability contributes behind the `control-plane` strategy. */
33
+ const AdminRoute = z.object({
34
+ method: AdminRouteMethod.describe("The method to call this route with."),
35
+ path: z.string().min(1).describe("The **fully mounted** path, with the capability's configured `basePath` already applied — `/billing/entitlements/grant`, not `/entitlements/grant`. Built by the capability at assembly, so an adopter who moved the mount point is reflected here rather than silently breaking a client that assumed the default."),
36
+ scope: ControlPlaneScope.nullable().describe("The scope this route requires, or null when it needs only a verified caller (the seam's `ping`). A client compares it against the connection's granted scopes to know which operations it may actually offer, instead of discovering a 403 by trying."),
37
+ summary: z.string().min(1).describe("One line describing what the route does, for a management client to render beside it.")
38
+ }).describe("One admin route, described well enough for a management client to call it without holding any capability-specific knowledge.");
39
+ /**
40
+ * One composed capability's declaration — everything about it that is the same for every caller.
41
+ *
42
+ * Split from {@link CapabilityDescriptor} because the health *values* are the one part of a manifest
43
+ * entry that depends on who is asking, and the seam holds this half from assembly (`compose`) while the
44
+ * other half is resolved per request.
45
+ */
46
+ const CapabilityDeclaration = z.object({
47
+ name: z.string().min(1).describe("The capability's name — the same token used as its migration namespace and error domain."),
48
+ version: z.string().nullable().describe("The npm version of the package supplying this capability, or null where there is none — the adopter's own `app` capability has a name and no package. Reported per capability and never aggregated: the package name is the join key against a release feed, and a project composes some capabilities and not others, so only the intersection of what it composes and what changed is worth reporting."),
49
+ adminRoutes: z.array(AdminRoute).describe("Every admin route this capability contributes, or empty when it contributes none. Most capabilities are empty: having no management surface is the normal case, and saying so explicitly is what lets a client render a capability it cannot act on."),
50
+ healthKeys: z.array(HealthSummaryKey).default([]).describe("The closed vocabulary of scalars this capability may report about its own state, or empty when it reports none — including when the Worker predates this field and says nothing at all. Declared alongside the routes so a client can render a key it has never heard of from what the Worker says about it — and so a *withheld* number is visible as a key with no value, rather than as silence."),
51
+ configKeys: z.array(ManifestConfigKey).default([]).describe("The configured facts this capability states, or empty when it states none — including when the Worker predates this field. Declared alongside the routes, because a fact is what a client needs to *call* one: `POST /billing/entitlements/grant` names a holder and never assumes it, so a client that cannot learn what this project bills can only guess — and a guess writes a row nothing reads."),
52
+ config: ManifestConfigValues.default({}).describe("What each declared fact resolved to for this deployment. The same answer for every caller, read off the capability's parsed config at assembly: there is no producer here, so no failure state, no per-request cost, and no staleness beyond the manifest itself.")
53
+ }).describe("One capability this Worker composes, the version it is at, the admin surface it exposes, the summary it may report, and the configured facts a client must respect to call any of it.");
54
+ /**
55
+ * One composed capability's manifest entry **as it goes over the wire**.
56
+ *
57
+ * Two flat fields, and neither is what a consumer reads: {@link CapabilityDescriptor} decodes them into
58
+ * one value that says which of the four states it is in. They are flat here because the wire has an
59
+ * older half to keep working. A Worker deployed before #350 sends no `healthUnavailable` and a Worker
60
+ * deployed before #317 sends neither field, and both must still parse; and a client pinned to a build
61
+ * older than the Worker it is reading strips the field it has never heard of and lands on `health:
62
+ * null`, which renders as silence rather than as a zero. Putting the fourth state *inside* `health`
63
+ * would have cost that client the whole manifest for one capability's bad afternoon, which is the
64
+ * failure #352 is about.
65
+ */
66
+ const CapabilityDescriptorWire = CapabilityDeclaration.extend({
67
+ health: HealthSummary.nullable().default(null).describe("Every declared value this caller may see, or null when there is none to give — either nothing is declared, or this connection lacks the scope the value is behind, or producing it failed, or the Worker predates this field. **Null is never zero.**"),
68
+ healthUnavailable: z.boolean().default(false).describe("Whether producing this capability's summary failed on this read — the fourth state (#350), and the reason `health` being null is not enough on its own. Carries no message, no code and no error: what the producer threw may name a row or a key, so nothing derived from it travels. False when the Worker predates this field.")
69
+ });
70
+ /**
71
+ * One composed capability, as `GET /control-plane/manifest` reports it to **this** caller.
72
+ *
73
+ * `health` is the only per-caller part of a manifest entry, and it is a four-state value rather than a
74
+ * nullable record: a capability that declares nothing, one that declares a number this connection was
75
+ * not granted, one that reports a number — zero included — and one whose producer failed. A management
76
+ * client that collapsed any two of those tells an adopter something untrue. The first two collapsed
77
+ * says everything is fine when it has simply not been allowed to look; the last two collapsed says
78
+ * nothing is pending when the store is down.
79
+ *
80
+ * The state travels **on** the value, so the scalars are unreachable without narrowing and a consumer
81
+ * that forgets the sick case gets a type error rather than a zero.
82
+ */
83
+ const CapabilityDescriptorRead = CapabilityDeclaration.extend({ health: CapabilityHealthReport.describe("What this caller is told about the capability's own state: nothing declared, a number withheld, the numbers themselves, or a producer that failed. Four states on one value, so none of them is reachable by forgetting to check another.") });
84
+ const CapabilityDescriptor = z.codec(CapabilityDescriptorWire, CapabilityDescriptorRead, {
85
+ decode: ({ health, healthUnavailable, ...declaration }) => ({
86
+ ...declaration,
87
+ health: healthReport({
88
+ healthKeys: declaration.healthKeys,
89
+ health,
90
+ healthUnavailable
91
+ })
92
+ }),
93
+ encode: ({ health, healthKeys, configKeys, config, ...declaration }) => ({
94
+ ...declaration,
95
+ healthKeys: (healthKeys ?? []).map((key) => ({
96
+ ...key,
97
+ nominal: key.nominal ?? null
98
+ })),
99
+ configKeys: configKeys ?? [],
100
+ config: config ?? {},
101
+ ...healthWire(health)
102
+ })
103
+ }).describe("One capability this Worker composes, as reported to one caller: its version, its admin surface, and the summary that caller is entitled to — or the named reason there is none.");
104
+ /**
105
+ * The manifest a management client reads to build itself.
106
+ *
107
+ * **There is deliberately no manifest schema version, and that is a different thing from the two build
108
+ * identities below.** A client dispatches on the routes described here — what this Worker declares right
109
+ * now — so a capability that changed its paths or its scopes reports the change directly, and a schema
110
+ * version would be a second source of truth to keep in sync with the first. That reasoning is unchanged.
111
+ *
112
+ * What the manifest does carry is **identity**, not schema, and it carries two of them because they
113
+ * answer questions neither can answer alone. `version` is Cloudflare's opaque per-deploy id: it says
114
+ * *exactly which build* is running, which is what forensics needs, what reproduces a report, and what
115
+ * pins the code an audited action ran against. It carries no version semantics, so it says nothing about
116
+ * features. `capabilities[].version` is the npm version of each composed package: it says *which
117
+ * features*, which is what answers "should this customer upgrade", "which customers are exposed to what
118
+ * we just fixed", and "does this project predate the capability a pane needs". Reporting only one leaves
119
+ * half the questions unanswerable, and they are the halves people actually ask.
120
+ *
121
+ * **And each entry carries a third thing: the configured facts that capability states** (#422). Knowing
122
+ * where a route is and which scope it needs is not always enough to call it — `POST
123
+ * {base}/entitlements/grant` names a holder, and whether this project's holders are people or
124
+ * organizations is a decision the adopter made in config. A client that cannot read the decision guesses
125
+ * it, and a guess writes a row nothing reads. See `discovery/configuration.ts` for why a fact is not a
126
+ * health number, despite the two fields sitting side by side.
127
+ */
128
+ const ControlPlaneManifest = z.object({
129
+ environment: z.string().describe("The environment this connection is bound to, echoed so a client can label what it is looking at."),
130
+ connectionId: z.string().describe("The connection this call authenticated as."),
131
+ version: z.string().nullable().describe("The Cloudflare version id of the build answering this call, from the `CF_VERSION_METADATA` binding, or null where the binding is absent. Opaque and per-deploy: it identifies which build is running, never which features it has. Null is honest — a Worker scaffolded before the binding was declared genuinely cannot say."),
132
+ capabilities: z.array(CapabilityDescriptor).describe("Every capability composed into this Worker, in composition order with the adopter's own app last. A capability absent here has no data to show, which is a fact the client discovers rather than a setting anybody maintains."),
133
+ grantedScopes: z.array(ControlPlaneScope).describe("Every scope this connection holds. Together with each route's own `scope`, this is what tells a client which operations to offer and which to hide.")
134
+ }).describe("What this Worker is, what it composes, and how to call the admin surface it exposes.");
135
+ //#endregion
136
+ export { AdminRoute, AdminRouteMethod, CapabilityDeclaration, CapabilityDescriptor, ControlPlaneManifest };
@@ -0,0 +1,108 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * One configured fact's value. A string, a number, or a boolean — never anything else.
4
+ *
5
+ * The rule is in the type rather than in a comment: a config object, a product list, or a credentials
6
+ * bag does not compile, so the seam cannot become the route by which an adopter's catalog reaches a
7
+ * discovery read.
8
+ */
9
+ export declare const ManifestConfigValue: z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>;
10
+ export type ManifestConfigValue = z.output<typeof ManifestConfigValue>;
11
+ /** Anything that is not a scalar. Named as a type so the tripwire below is a list to delete from. */
12
+ type NonScalar = object | ((...args: never[]) => unknown);
13
+ /**
14
+ * `true` while `T` is scalars only, `never` the moment it admits anything else — so the assignment
15
+ * below stops compiling if {@link ManifestConfigValue} is ever widened.
16
+ *
17
+ * Declared here rather than imported from `health.ts`: that module reaches `capability.ts` and, through
18
+ * it, hono and kysely, and this one is zod-only so a browser client can read the manifest without them
19
+ * (#430). The tuple wrapper is not decoration — a bare `Extract<...> extends never` distributes over a
20
+ * union and would answer `true` for every member, which is the one way this check could quietly pass.
21
+ */
22
+ type ScalarOnly<T> = [Extract<T, NonScalar>] extends [never] ? true : never;
23
+ /** The compile-time half of "scalars only". Widen the value type and this line names the file. */
24
+ export declare const MANIFEST_CONFIG_VALUE_IS_SCALAR_ONLY: ScalarOnly<ManifestConfigValue>;
25
+ /**
26
+ * One configured fact a capability may state, declared alongside its routes.
27
+ *
28
+ * `choices` is nullable rather than optional, and a null means "no closed list" rather than "not
29
+ * decided yet": a client renders a fact it has never heard of from the summary, and offers a picker
30
+ * only where the values are enumerable. A base path has no list; what a project bills has exactly two.
31
+ */
32
+ export declare const ManifestConfigKey: z.ZodObject<{
33
+ key: z.ZodString;
34
+ choices: z.ZodNullable<z.ZodArray<z.ZodString>>;
35
+ summary: z.ZodString;
36
+ }, z.core.$strip>;
37
+ export type ManifestConfigKey = z.output<typeof ManifestConfigKey>;
38
+ /**
39
+ * A capability's configured facts as the manifest carries them: declared key → scalar.
40
+ *
41
+ * A record rather than a closed object, because the vocabulary is federated the way scopes and audit
42
+ * actions are — the capability name already namespaces it. **Parsing tolerates a key it has never heard
43
+ * of on purpose**: a client reading a newer Worker must drop the unknown one rather than fail the whole
44
+ * manifest, which is what {@link namedConfigValues} does with it.
45
+ */
46
+ export declare const ManifestConfigValues: z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>;
47
+ export type ManifestConfigValues = z.output<typeof ManifestConfigValues>;
48
+ /**
49
+ * The brand only {@link defineManifestConfig} can produce.
50
+ *
51
+ * Module-local and never exported, so nothing outside this file can write it: the factory becomes the
52
+ * **only** way to build the seam, and a fact therefore cannot reach a manifest without having been
53
+ * checked against a declaration. An inline object literal on `Capability.manifestConfig` is a compile
54
+ * error, which is what stops the next capability from stating a fact nothing validated.
55
+ */
56
+ declare const manifestConfigSeam: unique symbol;
57
+ /** What a capability declares and what it resolved to. Built only by {@link defineManifestConfig}. */
58
+ export interface CapabilityManifestConfig {
59
+ /** The brand. Present only on a declaration that went through the factory, and never serialized. */
60
+ readonly [manifestConfigSeam]: true;
61
+ /** The closed vocabulary: every fact this capability states, parsed. */
62
+ readonly keys: readonly ManifestConfigKey[];
63
+ /** What each declared fact resolved to for this composition. One answer, for every caller. */
64
+ readonly values: ManifestConfigValues;
65
+ }
66
+ /** The authoring shape — facts as written, before parsing. */
67
+ export interface CapabilityManifestConfigInput {
68
+ /** Every fact this capability states. At least one; an empty vocabulary states nothing. */
69
+ keys: readonly z.input<typeof ManifestConfigKey>[];
70
+ /** The resolved value of each declared fact. Built from the capability's parsed config, never its defaults. */
71
+ values: ManifestConfigValues;
72
+ }
73
+ /**
74
+ * Declare a capability's configured facts. The one constructor, so every declaration in the tree is
75
+ * checked — the key against the name pattern, the values against the keys, and each value against its
76
+ * own closed list.
77
+ *
78
+ * **Nothing downstream re-checks this.** `missingAdminRoutes` keeps a route declaration honest against
79
+ * the router that mounted it, and there is no equivalent here: the declaration and the value come from
80
+ * the same object, so the only defense against a fact that disagrees with the capability is building it
81
+ * from the *resolved* config at the one call site, exactly as `adminRoutes` is built from the resolved
82
+ * `basePath`.
83
+ *
84
+ * Every refusal is an {@link InternalError} rather than a bare `ZodError`, key named, because every one
85
+ * of them is an author's mistake read off a deploy that would not start — and an author reading it
86
+ * wants the problem line and the action line the rest of this kit gives them.
87
+ */
88
+ export declare function defineManifestConfig(input: CapabilityManifestConfigInput): CapabilityManifestConfig;
89
+ /** One fact with the declaration that says how to read it. */
90
+ export interface NamedConfigValue {
91
+ /** The declaration — its closed list, if it has one, and what it decides. */
92
+ key: ManifestConfigKey;
93
+ /** The value this composition resolved it to. */
94
+ value: ManifestConfigValue;
95
+ }
96
+ /**
97
+ * Pair a capability's configured facts with their declarations, dropping any value nothing declares.
98
+ *
99
+ * This is how "an unknown fact reads as nothing rather than as an error" is real rather than
100
+ * aspirational: a client reads what this returns, so a key from a Worker newer than its build simply is
101
+ * not in the list — and a fact it cannot name is a fact it would otherwise have to guess the meaning of.
102
+ */
103
+ export declare function namedConfigValues(descriptor: {
104
+ configKeys: readonly ManifestConfigKey[];
105
+ config: ManifestConfigValues;
106
+ }): NamedConfigValue[];
107
+ export {};
108
+ //# sourceMappingURL=configuration.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"configuration.d.ts","sourceRoot":"","sources":["../../../src/controlPlane/discovery/configuration.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAuDxB;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB,+DAI7B,CAAC;AACJ,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEvE,qGAAqG;AACrG,KAAK,SAAS,GAAG,MAAM,GAAG,CAAC,CAAC,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,OAAO,CAAC,CAAC;AAE1D;;;;;;;;GAQG;AACH,KAAK,UAAU,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,IAAI,GAAG,KAAK,CAAC;AAE5E,kGAAkG;AAClG,eAAO,MAAM,oCAAoC,EAAE,UAAU,CAAC,mBAAmB,CAAQ,CAAC;AAE1F;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB;;;;iBAoB3B,CAAC;AACJ,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAEnE;;;;;;;GAOG;AACH,eAAO,MAAM,oBAAoB,yFAI9B,CAAC;AACJ,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAEzE;;;;;;;GAOG;AACH,QAAA,MAAM,kBAAkB,EAAE,OAAO,MAAoD,CAAC;AAEtF,sGAAsG;AACtG,MAAM,WAAW,wBAAwB;IACvC,oGAAoG;IACpG,QAAQ,CAAC,CAAC,kBAAkB,CAAC,EAAE,IAAI,CAAC;IACpC,wEAAwE;IACxE,QAAQ,CAAC,IAAI,EAAE,SAAS,iBAAiB,EAAE,CAAC;IAC5C,8FAA8F;IAC9F,QAAQ,CAAC,MAAM,EAAE,oBAAoB,CAAC;CACvC;AAED,8DAA8D;AAC9D,MAAM,WAAW,6BAA6B;IAC5C,2FAA2F;IAC3F,IAAI,EAAE,SAAS,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,EAAE,CAAC;IACnD,+GAA+G;IAC/G,MAAM,EAAE,oBAAoB,CAAC;CAC9B;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,6BAA6B,GAAG,wBAAwB,CAuEnG;AAED,8DAA8D;AAC9D,MAAM,WAAW,gBAAgB;IAC/B,6EAA6E;IAC7E,GAAG,EAAE,iBAAiB,CAAC;IACvB,iDAAiD;IACjD,KAAK,EAAE,mBAAmB,CAAC;CAC5B;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,EAAE;IAC5C,UAAU,EAAE,SAAS,iBAAiB,EAAE,CAAC;IACzC,MAAM,EAAE,oBAAoB,CAAC;CAC9B,GAAG,gBAAgB,EAAE,CAWrB"}
@@ -0,0 +1,187 @@
1
+ import { InternalError } from "../../error/pithyError.js";
2
+ import { z } from "zod";
3
+ //#region src/controlPlane/discovery/configuration.ts
4
+ /**
5
+ * The configured facts a capability states into its own manifest entry (#422).
6
+ *
7
+ * **A management client cannot call an operation it has to guess an argument for.**
8
+ * `POST {base}/entitlements/grant` names the holder and never assumes it, which is right — but
9
+ * `PaymentsConfig.billingSubject` decides whether this project's holders are people or organizations,
10
+ * it is required with no default, and nothing on the wire said so. A client guessing `user` against an
11
+ * organization-billed project writes a row nothing reads: the call succeeds, the audit line is real,
12
+ * the screen says `Done.`, and the person still cannot use what somebody just gave them. That is worse
13
+ * than a refusal, because it looks like it worked.
14
+ *
15
+ * So a capability declares the configured facts a client must respect, and the manifest carries them
16
+ * beside the routes they govern. `billingSubject` is the first and will not be the last, which is why
17
+ * this is a vocabulary rather than one field bolted onto the manifest.
18
+ *
19
+ * ## This is not health, and the difference is the whole design
20
+ *
21
+ * The shapes rhyme — `configKeys`/`config` beside `healthKeys`/`health` — and a reader who folds them
22
+ * together gets both wrong. They differ on every axis that matters:
23
+ *
24
+ * - A health value is **per caller**; a configured fact is the same for everybody.
25
+ * - A health value is **produced**, so it may fail, and `unavailable` is one of its four states. A fact
26
+ * is read off resolved config at assembly, so there is no producer, no query, and no failure state to
27
+ * collapse into a zero.
28
+ * - A health value is **as old as the manifest a client cached** and must never be shown as live. A
29
+ * fact changes only when the adopter redeploys, which is when the manifest changes anyway.
30
+ * - A health value is a number to **render**. A fact is a decision to **respect** — it goes into the
31
+ * next request a client makes, not onto a rail.
32
+ *
33
+ * ## No per-key scope, deliberately
34
+ *
35
+ * A health key's `scope` is required, on the stated ground that a count is still a fact
36
+ * about somebody's security posture. A configured fact is not that. `billingSubject` names no account,
37
+ * no transaction and no amount, and reads identically against an empty database — it is a fact about
38
+ * the adopter's own configuration, and the whole manifest already sits behind `manifest:read`, the same
39
+ * gate that discloses every route, every scope and every composed version.
40
+ *
41
+ * Withholding stays addable with no wire change if a later fact wants it — a from-address, a region. A
42
+ * value absent from `config` while its key is present in `configKeys` already means "declared and not
43
+ * shown", which is the same pairing that tells `undeclared` from `withheld` for health, and
44
+ * {@link namedConfigValues} already survives it.
45
+ *
46
+ * ## What keeps it from becoming a config dump
47
+ *
48
+ * A capability's resolved config holds provider secrets, price ids and an adopter's whole catalog. The
49
+ * factory is the only door, and it is narrow on purpose: scalars only, a closed declared vocabulary,
50
+ * and a value refused unless something declared it. Nobody can write `manifestConfig: resolved`.
51
+ */
52
+ /** A fact's name: one camelCase token, so it reads as a field and never as a path or an id. */
53
+ const MANIFEST_CONFIG_KEY_PATTERN = /^[a-z][A-Za-z0-9]*$/;
54
+ /**
55
+ * One configured fact's value. A string, a number, or a boolean — never anything else.
56
+ *
57
+ * The rule is in the type rather than in a comment: a config object, a product list, or a credentials
58
+ * bag does not compile, so the seam cannot become the route by which an adopter's catalog reaches a
59
+ * discovery read.
60
+ */
61
+ const ManifestConfigValue = z.union([
62
+ z.string(),
63
+ z.number(),
64
+ z.boolean()
65
+ ]).describe("One scalar: a word, a number, or a flag. Nothing else is representable — no nested objects, no lists, no credentials.");
66
+ /** The compile-time half of "scalars only". Widen the value type and this line names the file. */
67
+ const MANIFEST_CONFIG_VALUE_IS_SCALAR_ONLY = true;
68
+ /**
69
+ * One configured fact a capability may state, declared alongside its routes.
70
+ *
71
+ * `choices` is nullable rather than optional, and a null means "no closed list" rather than "not
72
+ * decided yet": a client renders a fact it has never heard of from the summary, and offers a picker
73
+ * only where the values are enumerable. A base path has no list; what a project bills has exactly two.
74
+ */
75
+ const ManifestConfigKey = z.object({
76
+ key: z.string().regex(MANIFEST_CONFIG_KEY_PATTERN, "A config key is one camelCase token — `billingSubject`.").describe("The key this fact appears under in the capability's config. One camelCase token."),
77
+ choices: z.array(z.string().min(1)).min(1, "A closed list needs a value in it. Write null where the values are not enumerable.").nullable().describe("Every value this fact may take, so a client can offer the same choice the adopter made, or null where the values are not enumerable. At least one where it is a list: an empty one is not `null` spelled differently, it is a list nothing satisfies, so the capability could never boot and the refusal would name no permitted value at all. Read off the capability's own enum rather than retyped, so a new member cannot land without the manifest learning it."),
78
+ summary: z.string().min(1).describe("One line saying what the fact governs, for a client to render beside it without knowing the key.")
79
+ }).describe("One configured fact a capability states: what it is called, what it may be, and what it decides — enough for a client to respect a decision it did not make.");
80
+ /**
81
+ * A capability's configured facts as the manifest carries them: declared key → scalar.
82
+ *
83
+ * A record rather than a closed object, because the vocabulary is federated the way scopes and audit
84
+ * actions are — the capability name already namespaces it. **Parsing tolerates a key it has never heard
85
+ * of on purpose**: a client reading a newer Worker must drop the unknown one rather than fail the whole
86
+ * manifest, which is what {@link namedConfigValues} does with it.
87
+ */
88
+ const ManifestConfigValues = z.record(z.string(), ManifestConfigValue).describe("One capability's configured facts: each declared key to its scalar. Unknown keys parse rather than throw, so a client of an older build reads what it knows and nothing for the rest.");
89
+ /**
90
+ * The brand only {@link defineManifestConfig} can produce.
91
+ *
92
+ * Module-local and never exported, so nothing outside this file can write it: the factory becomes the
93
+ * **only** way to build the seam, and a fact therefore cannot reach a manifest without having been
94
+ * checked against a declaration. An inline object literal on `Capability.manifestConfig` is a compile
95
+ * error, which is what stops the next capability from stating a fact nothing validated.
96
+ */
97
+ const manifestConfigSeam = Symbol("pithy.controlPlane.manifestConfig");
98
+ /**
99
+ * Declare a capability's configured facts. The one constructor, so every declaration in the tree is
100
+ * checked — the key against the name pattern, the values against the keys, and each value against its
101
+ * own closed list.
102
+ *
103
+ * **Nothing downstream re-checks this.** `missingAdminRoutes` keeps a route declaration honest against
104
+ * the router that mounted it, and there is no equivalent here: the declaration and the value come from
105
+ * the same object, so the only defense against a fact that disagrees with the capability is building it
106
+ * from the *resolved* config at the one call site, exactly as `adminRoutes` is built from the resolved
107
+ * `basePath`.
108
+ *
109
+ * Every refusal is an {@link InternalError} rather than a bare `ZodError`, key named, because every one
110
+ * of them is an author's mistake read off a deploy that would not start — and an author reading it
111
+ * wants the problem line and the action line the rest of this kit gives them.
112
+ */
113
+ function defineManifestConfig(input) {
114
+ if (input.keys.length === 0) throw new InternalError({
115
+ message: "A capability declared configured facts with no keys.",
116
+ action: "Declare at least one key, or omit the manifest config entirely.",
117
+ detail: "defineManifestConfig received an empty key list; an empty vocabulary states no fact."
118
+ });
119
+ const keys = [];
120
+ const declared = /* @__PURE__ */ new Map();
121
+ for (const written of input.keys) {
122
+ const parsed = ManifestConfigKey.safeParse(written);
123
+ if (!parsed.success) throw new InternalError({
124
+ message: "A capability declared a configured fact its own schema refuses.",
125
+ action: "Name the fact with one camelCase token, list its values or null, and say what it decides.",
126
+ detail: `manifest config key ${JSON.stringify(written)} is invalid: ${parsed.error.message}`
127
+ });
128
+ if (declared.has(parsed.data.key)) throw new InternalError({
129
+ message: "A capability declared the same configured fact twice.",
130
+ action: "Give each fact one declaration — the second silently decides what the first meant.",
131
+ detail: `manifest config key ${parsed.data.key} is declared more than once`
132
+ });
133
+ declared.set(parsed.data.key, parsed.data);
134
+ keys.push(parsed.data);
135
+ }
136
+ const parsed = ManifestConfigValues.safeParse(input.values);
137
+ if (!parsed.success) throw new InternalError({
138
+ message: "A capability stated a configured fact that is not a scalar.",
139
+ action: "State a word, a number or a flag. A capability's own config never crosses whole.",
140
+ detail: `manifest config values are invalid: ${parsed.error.message}`
141
+ });
142
+ const values = parsed.data;
143
+ for (const key of Object.keys(values)) if (!declared.has(key)) throw new InternalError({
144
+ message: "A capability stated a configured fact it never declared.",
145
+ action: "Declare the fact alongside the capability's routes, or stop stating it.",
146
+ detail: `manifest config value ${key} has no declaration, so a client could only guess at what it means`
147
+ });
148
+ for (const key of keys) {
149
+ const value = Object.hasOwn(values, key.key) ? values[key.key] : void 0;
150
+ if (value === void 0) throw new InternalError({
151
+ message: "A capability declared a configured fact and stated no value for it.",
152
+ action: "State every declared fact, or remove the declaration.",
153
+ detail: `manifest config key ${key.key} is declared and has no value`
154
+ });
155
+ if (key.choices !== null && !(typeof value === "string" && key.choices.includes(value))) throw new InternalError({
156
+ message: "A capability stated a configured fact its own declaration does not permit.",
157
+ action: "State one of the declared choices, or widen the declaration to the enum it came from.",
158
+ detail: `manifest config key ${key.key} was given ${JSON.stringify(value)}, which is not one of ${key.choices.join(", ")}`
159
+ });
160
+ }
161
+ return {
162
+ [manifestConfigSeam]: true,
163
+ keys,
164
+ values
165
+ };
166
+ }
167
+ /**
168
+ * Pair a capability's configured facts with their declarations, dropping any value nothing declares.
169
+ *
170
+ * This is how "an unknown fact reads as nothing rather than as an error" is real rather than
171
+ * aspirational: a client reads what this returns, so a key from a Worker newer than its build simply is
172
+ * not in the list — and a fact it cannot name is a fact it would otherwise have to guess the meaning of.
173
+ */
174
+ function namedConfigValues(descriptor) {
175
+ const named = [];
176
+ for (const key of descriptor.configKeys) {
177
+ if (!Object.hasOwn(descriptor.config, key.key)) continue;
178
+ const value = descriptor.config[key.key];
179
+ if (value !== void 0) named.push({
180
+ key,
181
+ value
182
+ });
183
+ }
184
+ return named;
185
+ }
186
+ //#endregion
187
+ export { MANIFEST_CONFIG_VALUE_IS_SCALAR_ONLY, ManifestConfigKey, ManifestConfigValue, ManifestConfigValues, defineManifestConfig, namedConfigValues };
@@ -0,0 +1,64 @@
1
+ import type { Hono } from "hono";
2
+ import type { Capability } from "../../capability/capability";
3
+ import type { AdminRoute } from "./adminRoute";
4
+ /**
5
+ * The gate that keeps `GET /control-plane/manifest` honest.
6
+ *
7
+ * A capability declares its admin routes by hand, and a hand-maintained list beside generated
8
+ * behavior is a list that rots: someone renames a path, changes a `basePath` default, moves an
9
+ * operation behind a different scope, and the declaration keeps confidently describing the old shape.
10
+ *
11
+ * **That failure is worse than having no manifest at all.** With no manifest a client knows it is
12
+ * guessing. With a stale one it believes a route exists, calls it, and gets a 404 it has no way to
13
+ * interpret — and the adopter sees a management client that is broken for reasons entirely inside
14
+ * somebody else's package.
15
+ *
16
+ * So the declaration is checked against the router that actually mounted, the same way
17
+ * `uncoveredParamRoutes` checks that every `:segment` carries a validator. Each capability asserts this
18
+ * in its own `routeContract.test.ts`, so a drifting declaration fails that package's CI rather than
19
+ * some integration run much later.
20
+ */
21
+ /** One declared route that no composed route answers, and which capability claimed it. */
22
+ export interface AdminRouteDrift {
23
+ /** The capability whose declaration is wrong. */
24
+ capability: string;
25
+ /** The route it claims to expose. */
26
+ route: AdminRoute;
27
+ }
28
+ /** One mounted control-plane route that no capability declares. */
29
+ export interface UndeclaredAdminRoute {
30
+ /** The method it answers on. */
31
+ method: string;
32
+ /** The path it is mounted at. */
33
+ path: string;
34
+ }
35
+ /**
36
+ * Every admin route a capability declares that the composed app does not actually serve.
37
+ *
38
+ * Empty is the passing state. A non-empty result names the capability and the route, because the fix
39
+ * is always in one of two places — the declaration or the registration — and knowing which capability
40
+ * owns both is most of the diagnosis.
41
+ *
42
+ * The comparison is on method and path only. Whether the route carries the right *scope* is not
43
+ * knowable from the router (a middleware is an opaque function to Hono), so that half stays the
44
+ * capability's responsibility and is covered by its own route tests.
45
+ */
46
+ export declare function missingAdminRoutes(app: Hono<never>, capabilities: readonly Capability[]): AdminRouteDrift[];
47
+ /**
48
+ * Every control-plane route the app actually serves that no capability declares — the other direction.
49
+ *
50
+ * {@link missingAdminRoutes} walks declared → mounted and catches a declaration that lies. This walks
51
+ * mounted → declared and catches the opposite: a route that is guarded, reachable, and invisible.
52
+ *
53
+ * **That gap stopped being cosmetic when CORS started deriving from this table** (#468). An undeclared
54
+ * admin route was previously discoverable-by-trying — the manifest omitted it, a client that knew the
55
+ * path could still call it. Now it also gets no `app.use`, so its preflight 404s and no browser can
56
+ * reach it at all, while every test in the capability that owns it keeps passing. The failure is
57
+ * invisible from the capability's own suite and shows up as a pane that will not load.
58
+ *
59
+ * A route is control-plane if its handler chain carries a {@link isControlPlaneGuard} gate, which is
60
+ * the only thing that distinguishes one — a middleware is otherwise an opaque function to Hono, and
61
+ * inferring it from the path would be guessing about the one table this is meant to check.
62
+ */
63
+ export declare function undeclaredAdminRoutes(app: Hono<never>, capabilities: readonly Capability[]): UndeclaredAdminRoute[];
64
+ //# sourceMappingURL=drift.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"drift.d.ts","sourceRoot":"","sources":["../../../src/controlPlane/discovery/drift.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AACjC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAE9D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE/C;;;;;;;;;;;;;;;;GAgBG;AAEH,0FAA0F;AAC1F,MAAM,WAAW,eAAe;IAC9B,iDAAiD;IACjD,UAAU,EAAE,MAAM,CAAC;IACnB,qCAAqC;IACrC,KAAK,EAAE,UAAU,CAAC;CACnB;AAED,mEAAmE;AACnE,MAAM,WAAW,oBAAoB;IACnC,gCAAgC;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,iCAAiC;IACjC,IAAI,EAAE,MAAM,CAAC;CACd;AAOD;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,YAAY,EAAE,SAAS,UAAU,EAAE,GAAG,eAAe,EAAE,CAS3G;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,YAAY,EAAE,SAAS,UAAU,EAAE,GAAG,oBAAoB,EAAE,CAcnH"}
@@ -0,0 +1,58 @@
1
+ import { isControlPlaneGuard } from "../http/guard.js";
2
+ //#region src/controlPlane/discovery/drift.ts
3
+ /** `app.routes` entries, reduced to the pairs worth comparing. */
4
+ function mounted(app) {
5
+ return new Set(app.routes.map((route) => `${route.method.toUpperCase()} ${route.path}`));
6
+ }
7
+ /**
8
+ * Every admin route a capability declares that the composed app does not actually serve.
9
+ *
10
+ * Empty is the passing state. A non-empty result names the capability and the route, because the fix
11
+ * is always in one of two places — the declaration or the registration — and knowing which capability
12
+ * owns both is most of the diagnosis.
13
+ *
14
+ * The comparison is on method and path only. Whether the route carries the right *scope* is not
15
+ * knowable from the router (a middleware is an opaque function to Hono), so that half stays the
16
+ * capability's responsibility and is covered by its own route tests.
17
+ */
18
+ function missingAdminRoutes(app, capabilities) {
19
+ const served = mounted(app);
20
+ const drift = [];
21
+ for (const capability of capabilities) for (const route of capability.adminRoutes ?? []) if (!served.has(`${route.method} ${route.path}`)) drift.push({
22
+ capability: capability.name,
23
+ route
24
+ });
25
+ return drift;
26
+ }
27
+ /**
28
+ * Every control-plane route the app actually serves that no capability declares — the other direction.
29
+ *
30
+ * {@link missingAdminRoutes} walks declared → mounted and catches a declaration that lies. This walks
31
+ * mounted → declared and catches the opposite: a route that is guarded, reachable, and invisible.
32
+ *
33
+ * **That gap stopped being cosmetic when CORS started deriving from this table** (#468). An undeclared
34
+ * admin route was previously discoverable-by-trying — the manifest omitted it, a client that knew the
35
+ * path could still call it. Now it also gets no `app.use`, so its preflight 404s and no browser can
36
+ * reach it at all, while every test in the capability that owns it keeps passing. The failure is
37
+ * invisible from the capability's own suite and shows up as a pane that will not load.
38
+ *
39
+ * A route is control-plane if its handler chain carries a {@link isControlPlaneGuard} gate, which is
40
+ * the only thing that distinguishes one — a middleware is otherwise an opaque function to Hono, and
41
+ * inferring it from the path would be guessing about the one table this is meant to check.
42
+ */
43
+ function undeclaredAdminRoutes(app, capabilities) {
44
+ const declared = /* @__PURE__ */ new Set();
45
+ for (const capability of capabilities) for (const route of capability.adminRoutes ?? []) declared.add(`${route.method} ${route.path}`);
46
+ const guarded = /* @__PURE__ */ new Map();
47
+ for (const route of app.routes) {
48
+ if (!isControlPlaneGuard(route.handler)) continue;
49
+ const method = route.method.toUpperCase();
50
+ guarded.set(`${method} ${route.path}`, {
51
+ method,
52
+ path: route.path
53
+ });
54
+ }
55
+ return [...guarded.entries()].filter(([key]) => !declared.has(key)).map(([, route]) => route);
56
+ }
57
+ //#endregion
58
+ export { missingAdminRoutes, undeclaredAdminRoutes };