@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,156 @@
1
+ import { ENVIRONMENTS } from "./environment.js";
2
+ import { z } from "zod";
3
+ //#region src/naming/domains.ts
4
+ /**
5
+ * Where a Worker answers, declared once per Worker per environment.
6
+ *
7
+ * ## The problem this replaces
8
+ *
9
+ * Nothing in `pithy.config.ts` described a Worker's address, so three commands each reconstructed it
10
+ * differently and none of them reconciled: `pithy env` scraped the first `routes` pattern out of
11
+ * `wrangler.jsonc`, `pithy email provision` and `pithy turnstile` read a hand-set `vars.BASE_URL`, and
12
+ * `pithy deploy` scraped the last URL wrangler happened to print. `email provision` refused to run
13
+ * without `vars.BASE_URL` while `pithy env` cheerfully printed a URL derived from routes, and nothing
14
+ * noticed when the two disagreed. Three answers to one question, in one CLI.
15
+ *
16
+ * So the address is declared, and everything else is generated from it — the `routes` entry with
17
+ * `custom_domain` and `zone_name`, and `vars.BASE_URL`. `BASE_URL` stays a wrangler var because the
18
+ * **Worker reads it at runtime**: `@pithy-sh/email` builds tracking and unsubscribe links against it and
19
+ * Turnstile binds its widget to that domain. What changes is that it is derived rather than hand-set and
20
+ * able to contradict the routes beside it.
21
+ *
22
+ * ## Per Worker, per environment
23
+ *
24
+ * Per Worker because each serves its own hostname. Per environment because `staging.api.example.com` and
25
+ * `api.example.com` are one Worker in two environments, and `env.<name>` stanzas in `wrangler.jsonc`
26
+ * replace rather than merge — so one declaration has to fan out to all of them.
27
+ *
28
+ * ## `dev` carries no domain
29
+ *
30
+ * Deliberately absent from the accepted keys. Local answers on `http://localhost:<port>` from the port
31
+ * the feature's `.dev.config.json` pinned at creation, and a domain there would be a second answer to a
32
+ * question the port allocator already answers. `requireManagedEnvironment` refuses `dev` for the same
33
+ * reason everywhere else.
34
+ */
35
+ /** A hostname a Worker answers on. No scheme, no path, no port — wrangler's `routes` pattern is a host. */
36
+ const HOSTNAME_PATTERN = /^(?!-)[a-z0-9-]{1,63}(?<!-)(\.(?!-)[a-z0-9-]{1,63}(?<!-))+$/;
37
+ /** The environments a domain may be declared for — every managed one, never `dev`. */
38
+ const DOMAIN_ENVIRONMENTS = ENVIRONMENTS.filter((environment) => environment !== "dev");
39
+ /** One environment's public address for one Worker. */
40
+ const WorkerDomain = z.object({
41
+ pattern: z.string().regex(HOSTNAME_PATTERN, "A domain is a bare hostname — no scheme, no path, no port (e.g. `api.example.com`, not `https://api.example.com/`).").describe("The hostname this Worker answers on in this environment, e.g. `api.example.com`. Written into `wrangler.jsonc` as a `routes` entry with `custom_domain: true`, and into `vars.BASE_URL` as `https://<pattern>`. A bare hostname rather than a URL, because that is what wrangler's route matcher takes — a scheme here would be silently wrong."),
42
+ zone: z.string().regex(HOSTNAME_PATTERN, "A zone is the registrable domain on your Cloudflare account, e.g. `example.com`.").describe("The Cloudflare zone `pattern` sits under — the registrable domain as it appears on your account, e.g. `example.com` for `api.example.com`. Cloudflare needs it to attach a custom domain, and it is not always derivable from the hostname: a zone can be a subdomain, and a public-suffix guess would be wrong for exactly the adopters who are hardest to debug.")
43
+ }).describe("Where one Worker answers in one environment: the hostname, and the Cloudflare zone it sits under.").check((ctx) => {
44
+ const { pattern, zone } = ctx.value;
45
+ if (pattern !== zone && !pattern.endsWith(`.${zone}`)) ctx.issues.push({
46
+ code: "custom",
47
+ input: ctx.value,
48
+ path: ["pattern"],
49
+ message: `The domain \`${pattern}\` is not inside the zone \`${zone}\`. A custom domain must sit on the zone it names — set \`zone\` to the registrable domain \`${pattern}\` belongs to.`
50
+ });
51
+ });
52
+ /**
53
+ * A Worker's domains, keyed by environment.
54
+ *
55
+ * Every key optional: a project with no domain yet is legitimate, and so is one that has staging wired
56
+ * and production not. Adding one later is a config edit plus a deploy, never a rescaffold.
57
+ */
58
+ const WorkerDomains = z.object({
59
+ staging: WorkerDomain.optional().describe("Where this Worker answers in `staging`, if it has a domain yet."),
60
+ prod: WorkerDomain.optional().describe("Where this Worker answers in `prod`, if it has a domain yet.")
61
+ }).describe("Where this Worker answers, per environment. `dev` is absent by design — local runs on `http://localhost:<port>` from the port pinned in `.dev.config.json`. Everything else is generated from this: the `routes` entry with `custom_domain` and `zone_name`, and `vars.BASE_URL`.");
62
+ /**
63
+ * The declared domain for one environment, or null where none is declared (including every `dev`).
64
+ *
65
+ * `environment` may be `undefined`, which is what `compositionEnvironment()` answers when nothing stamped
66
+ * one. That is not a `dev` default — it is "this composition names no environment", and an environment
67
+ * nobody named has declared no domain, so the answer is the same `null` an undeclared one gets.
68
+ */
69
+ function domainFor(domains, environment) {
70
+ if (!domains) return null;
71
+ if (environment === "staging") return domains.staging ?? null;
72
+ if (environment === "prod") return domains.prod ?? null;
73
+ return null;
74
+ }
75
+ /** The base URL a declared domain implies. Always `https` — a custom domain on Cloudflare is TLS-terminated. */
76
+ function baseUrlFor(domain) {
77
+ return `https://${domain.pattern}`;
78
+ }
79
+ /**
80
+ * The origin an environment with no declared domain resolves to.
81
+ *
82
+ * **No port, and that is the whole design.** Local dev's port is assigned per Worker per run from the
83
+ * feature's reserved block, so it is the one address nobody can write down — `@pithy-sh/auth` resolves
84
+ * dev's base URL from the host the request arrived at for exactly that reason, and a config-time value
85
+ * could only ever be a stale guess at it. So this is a *placeholder that fails closed*: a link built
86
+ * against it goes nowhere, which is useless rather than harmful.
87
+ *
88
+ * Compare what it replaces. Every capability that made an adopter write an origin got production's
89
+ * written into staging, and the failure was never "the link is broken" — it was a staging deploy mailing
90
+ * real users magic links **into production**, an unsubscribe from a staging test unsubscribing that
91
+ * person in production, and a Checkout return landing a staging payer in production on an account that
92
+ * had bought nothing. A fallback that goes nowhere is the only one that cannot do any of that.
93
+ */
94
+ const LOCAL_ORIGIN = "http://localhost";
95
+ /**
96
+ * **Where is this Worker publicly reachable, in this environment?** — asked once, answered once.
97
+ *
98
+ * The two halves already existed ({@link domainFor} and {@link baseUrlFor}) and nothing composed them,
99
+ * so every caller that needed the answer did the two-step for itself and every *adopter* who needed it
100
+ * wrote a URL down. The first adopter's one Worker config carried `https://app.pithy.sh` three times —
101
+ * `auth.baseURL`, `email.baseUrl`, and the Stripe return URLs — each wrong for staging in its own way,
102
+ * each found on a different day. Fixing it inside one capability does not stop the next capability
103
+ * asking the same question, so the question gets one answer that anyone can call.
104
+ *
105
+ * **The fallback never reaches for another environment's origin.** An environment absent from `domains`
106
+ * is one that is not published, so it resolves to {@link LOCAL_ORIGIN} and nothing else. Falling back to
107
+ * production's is what the shape being replaced did, and it is the one behavior here that was actively
108
+ * dangerous rather than merely wrong.
109
+ *
110
+ * **A deployed environment must never *keep* that fallback**, and it is not this function's job to say
111
+ * so — `pithy deploy --env <name>` refuses an environment whose origin its config does not declare
112
+ * (#253), and `pithy doctor` reports it first. One rule in two halves: this one cannot invent another
113
+ * environment's origin, and deploy makes sure the placeholder never ships.
114
+ *
115
+ * **Not every origin should derive from this, and `controlplane.issuer` is the one to name.** That is an
116
+ * **identity**, not an address: a connection stores the issuer it was created with and verification
117
+ * checks that stored value, so a per-environment issuer would make a connection minted in staging
118
+ * unverifiable in production. That may be the better isolation, but it is a decision about trust rather
119
+ * than about reachability, and a helper whose job is "where am I reachable" must not sweep it up.
120
+ */
121
+ function resolveOrigin(environment, domains) {
122
+ const domain = domainFor(domains, environment);
123
+ if (domain) return {
124
+ origin: baseUrlFor(domain),
125
+ hostname: domain.pattern,
126
+ declared: true
127
+ };
128
+ return {
129
+ origin: LOCAL_ORIGIN,
130
+ hostname: "localhost",
131
+ declared: false
132
+ };
133
+ }
134
+ /**
135
+ * The one line an adopter writes: this Worker's public origin for the environment it is running in.
136
+ *
137
+ * ```ts
138
+ * const DOMAINS = { staging: { … }, prod: { … } };
139
+ * const PUBLIC_ORIGIN = originFor(compositionEnvironment(), DOMAINS);
140
+ * ```
141
+ *
142
+ * That is the line `pithy init` scaffolds, so an adopter who never thinks about it gets it right and one
143
+ * who wants a literal can still write one. `compositionEnvironment()` is `string | undefined`, and it is
144
+ * taken as such rather than defaulted to `dev` at the call site: an unstamped composition has declared no
145
+ * domain either way, and a `?? "dev"` in every adopter's config would be a default nobody chose.
146
+ *
147
+ * Named for what it is rather than for who asked. The first adopter's own version was called
148
+ * `AUTH_BASE_URL`, which is part of why `email` and `payments` were missed for days: the constant read
149
+ * as auth's private business when it is the Worker's address, and every capability that needs an origin
150
+ * needs this one.
151
+ */
152
+ function originFor(environment, domains) {
153
+ return resolveOrigin(environment, domains).origin;
154
+ }
155
+ //#endregion
156
+ export { DOMAIN_ENVIRONMENTS, LOCAL_ORIGIN, WorkerDomain, WorkerDomains, baseUrlFor, domainFor, originFor, resolveOrigin };
@@ -0,0 +1,118 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * What an environment may be called, in one module.
4
+ *
5
+ * An environment name is not decoration: it sits in the middle of every Cloudflare name a project
6
+ * composes (`<project>-<env>-<thing>`), so its length is subtracted from every other segment's
7
+ * budget, and its characters have to be legal in every namespace Pithy writes into. Until this
8
+ * module existed, `--env` was a bare string that nothing checked — a fourteen-character environment
9
+ * quietly ate the room a project name had already been accepted against.
10
+ */
11
+ /**
12
+ * The three first-class environments: local `dev`, `staging` for test users, `prod` for paid ones.
13
+ *
14
+ * **`prod`, not `production`.** The long form cost three characters of every project name, in a
15
+ * budget where thirty-three was the ceiling, and bought nothing a four-letter word does not say.
16
+ */
17
+ export declare const ENVIRONMENTS: readonly ["dev", "staging", "prod"];
18
+ /** One of Pithy's first-class environments. A custom environment is a `string`, and still validated. */
19
+ export type Environment = (typeof ENVIRONMENTS)[number];
20
+ /**
21
+ * The scope that sits beside the environments: one value shared by all of them.
22
+ *
23
+ * It occupies the same slot as an environment in a composed name, which is exactly why an
24
+ * environment may not be *called* `global` — a project would then have one set of names for two
25
+ * different scopes, and teardown could not tell them apart.
26
+ */
27
+ export declare const GLOBAL_SCOPE = "global";
28
+ /**
29
+ * The environment one branch gets — the `env.feature` stanza a feature's provisioning run writes into.
30
+ *
31
+ * **A legal environment name, and never a declared one.** Legal because it is a real wrangler stanza
32
+ * key, so every rule that governs a key governs it: seven characters, which is
33
+ * {@link MAX_ENVIRONMENT_NAME} exactly. Never declared because the two kinds of environment write to
34
+ * different files — a declared environment's ids are source, in the tracked `wrangler.jsonc`; a
35
+ * feature's are a build artifact under `.wrangler/`. A project that declared this name would own two
36
+ * files claiming one stanza, and the resolver that picks between them answers "generated" for this
37
+ * name — so a migrate would read bytes no provisioning run wrote.
38
+ *
39
+ * It lives here rather than beside the scope that uses it because it is first an environment name, and
40
+ * because {@link DeclaredEnvironments} has to refuse it: a constant defined downstream of the schema
41
+ * that rejects it would be a cycle.
42
+ */
43
+ export declare const FEATURE_ENVIRONMENT = "feature";
44
+ /**
45
+ * The longest an environment name may be — **the longest canonical one, `staging`**.
46
+ *
47
+ * This is a derivation input, not a preference: `WORKFLOW_DERIVED_PROJECT_NAME` and
48
+ * `FEATURE_DERIVED_PROJECT_NAME` are both computed against it, so every project name Pithy has ever
49
+ * accepted was accepted on the assumption that no environment exceeds it. Letting a custom
50
+ * environment run longer would retroactively shrink that cap — and a provisioned project cannot be
51
+ * renamed. So a longer environment is refused here, at the one place that can still say no.
52
+ *
53
+ * Read off {@link ENVIRONMENTS} rather than typed, so adding an environment cannot leave the two
54
+ * disagreeing.
55
+ */
56
+ export declare const MAX_ENVIRONMENT_NAME: number;
57
+ /**
58
+ * Is this a name an environment may carry?
59
+ *
60
+ * **Read raw, never kebabbed** — and that is the difference between this rule and the project rule.
61
+ * A project name is prose an adopter types once into `pithy.config.ts`, so `Acme Corp` is politely
62
+ * composed into `acme-corp`. An environment is an identifier repeated in `--env`, in
63
+ * `.dev.vars.<environment>`, and in a wrangler environment key; normalizing `Prod` to `prod` would
64
+ * make two spellings name one environment in some places and two in others.
65
+ */
66
+ export declare function isValidEnvironment(name: string): boolean;
67
+ /**
68
+ * Refuse a name no environment may carry, as a `ValidationError` — an environment comes from a flag
69
+ * or a config file, so it is a 400 with an action, never an internal fault.
70
+ *
71
+ * Three different mistakes get three different sentences, because "invalid environment" helps with
72
+ * none of them: `production` is the old spelling and gets the new one, a long name gets the number,
73
+ * and anything else gets the charset.
74
+ */
75
+ export declare function assertValidEnvironment(name: string): void;
76
+ /**
77
+ * The environments a project has when it declares nothing: `staging` for test users, `prod` for paid ones.
78
+ *
79
+ * The same two {@link ENVIRONMENTS} names minus `dev`, and the same two the starter scaffolded from a
80
+ * hardcoded template before anything could say otherwise — so a project that never opens this setting
81
+ * gets exactly what it got before.
82
+ */
83
+ export declare const DEFAULT_ENVIRONMENTS: readonly ["staging", "prod"];
84
+ /**
85
+ * The local environment. Never declared: it is the top-level wrangler stanza, and it always exists.
86
+ *
87
+ * Exported because "always exists and is never declared" is a fact other code has to act on, not only a
88
+ * rule this schema enforces. Anything deriving something over *every* environment a project has must add
89
+ * this one itself — {@link DeclaredEnvironments} will never hand it over — and a caller spelling `"dev"`
90
+ * inline is a caller that can spell it differently.
91
+ */
92
+ export declare const LOCAL_ENVIRONMENT = "dev";
93
+ /**
94
+ * **The set of deployed environments a project has**, declared once in the root `pithy.config.ts`.
95
+ *
96
+ * Until this existed a project never said. The set lived only as `env.<name>` stanzas in each Worker's
97
+ * `wrangler.jsonc` — per Worker, so two Workers in one project could disagree and nothing reconciled them
98
+ * — while `ManagedEnvironment` held a closed enum of two and `seed.productionEnvironments` invited a
99
+ * project to name a third. An adopter adding `env.live` got `pithy migrate --env live` working and
100
+ * `pithy secrets provision` skipping it in silence, because the three answers were never the same answer.
101
+ *
102
+ * **Ordered, and the order is meaningful.** It is the order provisioning walks — least-production first,
103
+ * so a mistake is made in staging before it is made in prod — and the last entry is the one a
104
+ * `global` account-level secret is written through (see `@pithy-sh/secrets`'s `resolveWriteTargets`).
105
+ * `["staging", "prod"]` therefore behaves exactly as the hardcoded pair did.
106
+ *
107
+ * **`dev` is not declarable.** It is local, it is the top-level wrangler stanza rather than an `env.dev`,
108
+ * and it resolves from Miniflare rather than an account — so a project always has it and provisioning
109
+ * never has it. Declaring it would ask a deploy to happen for an environment that never deploys.
110
+ *
111
+ * **Nothing is renamed by editing this.** `<project>-<env>-<thing>` is computed from the declaration and
112
+ * never stored, so changing a name here orphans everything already provisioned under the old one, exactly
113
+ * as renaming `name` does. `pithy doctor` reports the change rather than applying it.
114
+ */
115
+ export declare const DeclaredEnvironments: z.ZodArray<z.ZodString>;
116
+ /** Every deployed environment a project has. Same name as its schema, as every Zod object here is. */
117
+ export type DeclaredEnvironments = z.output<typeof DeclaredEnvironments>;
118
+ //# sourceMappingURL=environment.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"environment.d.ts","sourceRoot":"","sources":["../../src/naming/environment.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB;;;;;;;;GAQG;AAEH;;;;;GAKG;AACH,eAAO,MAAM,YAAY,YAAI,KAAK,EAAE,SAAS,EAAE,MAAM,CAAU,CAAC;AAEhE,wGAAwG;AACxG,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC;AAExD;;;;;;GAMG;AACH,eAAO,MAAM,YAAY,WAAW,CAAC;AAErC;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,mBAAmB,YAAY,CAAC;AAE7C;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,oBAAoB,QAAqE,CAAC;AAEvG;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAGxD;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CA4BzD;AAED;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,YAAI,SAAS,EAAE,MAAM,CAAU,CAAC;AAEjE;;;;;;;GAOG;AACH,eAAO,MAAM,iBAAiB,QAAQ,CAAC;AAEvC;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,oBAAoB,yBA2D9B,CAAC;AAEJ,sGAAsG;AACtG,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,oBAAoB,CAAC,CAAC"}
@@ -0,0 +1,190 @@
1
+ import { NAME_SEGMENT } from "./segment.js";
2
+ import { PithyError, ValidationError } from "../error/pithyError.js";
3
+ import { z } from "zod";
4
+ //#region src/naming/environment.ts
5
+ /**
6
+ * What an environment may be called, in one module.
7
+ *
8
+ * An environment name is not decoration: it sits in the middle of every Cloudflare name a project
9
+ * composes (`<project>-<env>-<thing>`), so its length is subtracted from every other segment's
10
+ * budget, and its characters have to be legal in every namespace Pithy writes into. Until this
11
+ * module existed, `--env` was a bare string that nothing checked — a fourteen-character environment
12
+ * quietly ate the room a project name had already been accepted against.
13
+ */
14
+ /**
15
+ * The three first-class environments: local `dev`, `staging` for test users, `prod` for paid ones.
16
+ *
17
+ * **`prod`, not `production`.** The long form cost three characters of every project name, in a
18
+ * budget where thirty-three was the ceiling, and bought nothing a four-letter word does not say.
19
+ */
20
+ const ENVIRONMENTS = [
21
+ "dev",
22
+ "staging",
23
+ "prod"
24
+ ];
25
+ /**
26
+ * The scope that sits beside the environments: one value shared by all of them.
27
+ *
28
+ * It occupies the same slot as an environment in a composed name, which is exactly why an
29
+ * environment may not be *called* `global` — a project would then have one set of names for two
30
+ * different scopes, and teardown could not tell them apart.
31
+ */
32
+ const GLOBAL_SCOPE = "global";
33
+ /**
34
+ * The environment one branch gets — the `env.feature` stanza a feature's provisioning run writes into.
35
+ *
36
+ * **A legal environment name, and never a declared one.** Legal because it is a real wrangler stanza
37
+ * key, so every rule that governs a key governs it: seven characters, which is
38
+ * {@link MAX_ENVIRONMENT_NAME} exactly. Never declared because the two kinds of environment write to
39
+ * different files — a declared environment's ids are source, in the tracked `wrangler.jsonc`; a
40
+ * feature's are a build artifact under `.wrangler/`. A project that declared this name would own two
41
+ * files claiming one stanza, and the resolver that picks between them answers "generated" for this
42
+ * name — so a migrate would read bytes no provisioning run wrote.
43
+ *
44
+ * It lives here rather than beside the scope that uses it because it is first an environment name, and
45
+ * because {@link DeclaredEnvironments} has to refuse it: a constant defined downstream of the schema
46
+ * that rejects it would be a cycle.
47
+ */
48
+ const FEATURE_ENVIRONMENT = "feature";
49
+ /**
50
+ * The longest an environment name may be — **the longest canonical one, `staging`**.
51
+ *
52
+ * This is a derivation input, not a preference: `WORKFLOW_DERIVED_PROJECT_NAME` and
53
+ * `FEATURE_DERIVED_PROJECT_NAME` are both computed against it, so every project name Pithy has ever
54
+ * accepted was accepted on the assumption that no environment exceeds it. Letting a custom
55
+ * environment run longer would retroactively shrink that cap — and a provisioned project cannot be
56
+ * renamed. So a longer environment is refused here, at the one place that can still say no.
57
+ *
58
+ * Read off {@link ENVIRONMENTS} rather than typed, so adding an environment cannot leave the two
59
+ * disagreeing.
60
+ */
61
+ const MAX_ENVIRONMENT_NAME = Math.max(...ENVIRONMENTS.map((environment) => environment.length));
62
+ /**
63
+ * Is this a name an environment may carry?
64
+ *
65
+ * **Read raw, never kebabbed** — and that is the difference between this rule and the project rule.
66
+ * A project name is prose an adopter types once into `pithy.config.ts`, so `Acme Corp` is politely
67
+ * composed into `acme-corp`. An environment is an identifier repeated in `--env`, in
68
+ * `.dev.vars.<environment>`, and in a wrangler environment key; normalizing `Prod` to `prod` would
69
+ * make two spellings name one environment in some places and two in others.
70
+ */
71
+ function isValidEnvironment(name) {
72
+ if (name === "global") return false;
73
+ return name.length <= MAX_ENVIRONMENT_NAME && NAME_SEGMENT.test(name);
74
+ }
75
+ /**
76
+ * Refuse a name no environment may carry, as a `ValidationError` — an environment comes from a flag
77
+ * or a config file, so it is a 400 with an action, never an internal fault.
78
+ *
79
+ * Three different mistakes get three different sentences, because "invalid environment" helps with
80
+ * none of them: `production` is the old spelling and gets the new one, a long name gets the number,
81
+ * and anything else gets the charset.
82
+ */
83
+ function assertValidEnvironment(name) {
84
+ if (isValidEnvironment(name)) return;
85
+ if (name === "production") throw new ValidationError({
86
+ message: `"production" is not an environment name in Pithy.`,
87
+ action: "Use `prod`.",
88
+ detail: `An environment stops at ${MAX_ENVIRONMENT_NAME} characters, the length of the longest of ${ENVIRONMENTS.join(", ")}.`
89
+ });
90
+ if (name === "global") throw new ValidationError({
91
+ message: `"${GLOBAL_SCOPE}" is a scope, not an environment.`,
92
+ action: `Name the environment one of ${ENVIRONMENTS.join(", ")}.`,
93
+ detail: `${GLOBAL_SCOPE} occupies the environment slot of a composed name for values shared across every environment.`
94
+ });
95
+ if (name.length > MAX_ENVIRONMENT_NAME && NAME_SEGMENT.test(name)) throw new ValidationError({
96
+ message: `"${name}" is ${name.length} characters. An environment stops at ${MAX_ENVIRONMENT_NAME}.`,
97
+ action: `Use one of ${ENVIRONMENTS.join(", ")}, or a shorter name.`,
98
+ detail: `Every project-name budget is derived against a ${MAX_ENVIRONMENT_NAME}-character environment, and a provisioned project cannot be renamed.`
99
+ });
100
+ throw new ValidationError({
101
+ message: `"${name}" can't be an environment name.`,
102
+ action: "Use lowercase letters, digits, and single hyphens, starting with a letter.",
103
+ detail: `An environment is used verbatim in Cloudflare resource names, so it must match ${NAME_SEGMENT.source}.`
104
+ });
105
+ }
106
+ /**
107
+ * The environments a project has when it declares nothing: `staging` for test users, `prod` for paid ones.
108
+ *
109
+ * The same two {@link ENVIRONMENTS} names minus `dev`, and the same two the starter scaffolded from a
110
+ * hardcoded template before anything could say otherwise — so a project that never opens this setting
111
+ * gets exactly what it got before.
112
+ */
113
+ const DEFAULT_ENVIRONMENTS = ["staging", "prod"];
114
+ /**
115
+ * The local environment. Never declared: it is the top-level wrangler stanza, and it always exists.
116
+ *
117
+ * Exported because "always exists and is never declared" is a fact other code has to act on, not only a
118
+ * rule this schema enforces. Anything deriving something over *every* environment a project has must add
119
+ * this one itself — {@link DeclaredEnvironments} will never hand it over — and a caller spelling `"dev"`
120
+ * inline is a caller that can spell it differently.
121
+ */
122
+ const LOCAL_ENVIRONMENT = "dev";
123
+ /**
124
+ * **The set of deployed environments a project has**, declared once in the root `pithy.config.ts`.
125
+ *
126
+ * Until this existed a project never said. The set lived only as `env.<name>` stanzas in each Worker's
127
+ * `wrangler.jsonc` — per Worker, so two Workers in one project could disagree and nothing reconciled them
128
+ * — while `ManagedEnvironment` held a closed enum of two and `seed.productionEnvironments` invited a
129
+ * project to name a third. An adopter adding `env.live` got `pithy migrate --env live` working and
130
+ * `pithy secrets provision` skipping it in silence, because the three answers were never the same answer.
131
+ *
132
+ * **Ordered, and the order is meaningful.** It is the order provisioning walks — least-production first,
133
+ * so a mistake is made in staging before it is made in prod — and the last entry is the one a
134
+ * `global` account-level secret is written through (see `@pithy-sh/secrets`'s `resolveWriteTargets`).
135
+ * `["staging", "prod"]` therefore behaves exactly as the hardcoded pair did.
136
+ *
137
+ * **`dev` is not declarable.** It is local, it is the top-level wrangler stanza rather than an `env.dev`,
138
+ * and it resolves from Miniflare rather than an account — so a project always has it and provisioning
139
+ * never has it. Declaring it would ask a deploy to happen for an environment that never deploys.
140
+ *
141
+ * **Nothing is renamed by editing this.** `<project>-<env>-<thing>` is computed from the declaration and
142
+ * never stored, so changing a name here orphans everything already provisioned under the old one, exactly
143
+ * as renaming `name` does. `pithy doctor` reports the change rather than applying it.
144
+ */
145
+ const DeclaredEnvironments = z.array(z.string().describe("One deployed environment's name, used verbatim in the middle of every Cloudflare name the project composes and in each Worker's `env.<name>` wrangler stanza.")).check((ctx) => {
146
+ if (ctx.value.length === 0) ctx.issues.push({
147
+ code: "custom",
148
+ input: ctx.value,
149
+ message: `Declare at least one environment. A project with none can never deploy — ${DEFAULT_ENVIRONMENTS.join(" and ")} is the default.`
150
+ });
151
+ const seen = /* @__PURE__ */ new Set();
152
+ for (const name of ctx.value) {
153
+ if (name === "dev") {
154
+ ctx.issues.push({
155
+ code: "custom",
156
+ input: ctx.value,
157
+ message: `"dev" is local and always present, so it is never declared. List only the environments this project deploys.`
158
+ });
159
+ continue;
160
+ }
161
+ if (name === "feature") {
162
+ ctx.issues.push({
163
+ code: "custom",
164
+ input: ctx.value,
165
+ message: `"${FEATURE_ENVIRONMENT}" is the environment a branch gets, and its config is generated rather than committed. Declaring it would give one stanza two owners.`
166
+ });
167
+ continue;
168
+ }
169
+ if (seen.has(name)) {
170
+ ctx.issues.push({
171
+ code: "custom",
172
+ input: ctx.value,
173
+ message: `"${name}" is declared twice. One environment is one set of Cloudflare names.`
174
+ });
175
+ continue;
176
+ }
177
+ seen.add(name);
178
+ try {
179
+ assertValidEnvironment(name);
180
+ } catch (error) {
181
+ ctx.issues.push({
182
+ code: "custom",
183
+ input: ctx.value,
184
+ message: error instanceof PithyError ? error.payload.message : `"${name}" can't be an environment name.`
185
+ });
186
+ }
187
+ }
188
+ }).describe("Every deployed environment this project has, in the order provisioning walks them — least-production first. Declared once in the root pithy.config.ts, because the set cannot be per-Worker. `dev` is never listed: it is local and always present.");
189
+ //#endregion
190
+ export { DEFAULT_ENVIRONMENTS, DeclaredEnvironments, ENVIRONMENTS, FEATURE_ENVIRONMENT, GLOBAL_SCOPE, LOCAL_ENVIRONMENT, MAX_ENVIRONMENT_NAME, assertValidEnvironment, isValidEnvironment };
@@ -0,0 +1,68 @@
1
+ /**
2
+ * The names a feature environment provisions under: `<project>-f<issue>-<slug>-<binding>-<kind>` for a
3
+ * resource, `<project>-f<issue>-<slug>-<worker>` for a Worker script.
4
+ *
5
+ * **No environment segment, and no Worker segment.** A feature *is* an environment, and two Workers that
6
+ * declare the same binding are meant to share one resource — so the binding name, not the topology, is
7
+ * where sharing is expressed.
8
+ *
9
+ * Every name here is **derived and recomputed**, never stored: `provision` and `destroy` both compute the
10
+ * same string from `(identity, binding, kind)`, which is what lets teardown reconcile exactly rather than
11
+ * scan a prefix a hyphenated sibling slug could ambiguously match. That is also why these names
12
+ * **truncate** where a capability's Workflow refuses: a feature name addresses nothing that outlives the
13
+ * feature, and failing a CI run because a branch slug was long would be the worse failure.
14
+ *
15
+ * It lives in `core`, beside the rest of the naming rule, so the CLI and the facade compose one
16
+ * implementation rather than two that drift.
17
+ */
18
+ /** A Cloudflare resource kind a feature can provision. All two characters — `MAX_FEATURE_KIND` in `./limits`. */
19
+ export type FeatureResourceKind = "d1" | "kv" | "r2";
20
+ /** Every feature resource kind, for tests and for callers that enumerate. */
21
+ export declare const FEATURE_RESOURCE_KINDS: readonly FeatureResourceKind[];
22
+ /** The identity of a feature — everything a feature name is derived from. */
23
+ export interface FeatureIdentity {
24
+ /** The project name, from the root `pithy.config.ts` (e.g. `acme`). */
25
+ project: string;
26
+ /** The issue number as a string (e.g. `69`). Digits only, up to {@link MAX_ISSUE_DIGITS}. */
27
+ issue: string;
28
+ /** The kebab-case feature slug, from the branch (e.g. `media-cli`). */
29
+ slug: string;
30
+ }
31
+ /**
32
+ * The full Cloudflare resource name for a feature's binding — `<project>-f<issue>-<slug>-<binding>-<kind>`.
33
+ *
34
+ * Held to **R2's 63**, the strictest of the three kinds a feature provisions, so one shape is legal for
35
+ * all of them: lowercase, hyphenated, alphanumeric at both ends. A D1 or KV name could be longer, but a
36
+ * feature that provisions a bucket and a database wants them recognizably the same name.
37
+ */
38
+ export declare function featureResourceName(identity: FeatureIdentity, binding: string, kind: FeatureResourceKind): string;
39
+ /**
40
+ * The CF Secrets Store entry name holding one of a feature's **environment-scoped** secrets —
41
+ * `<project>-f<issue>-<slug>-<secret>`.
42
+ *
43
+ * A Cloudflare account has one Secrets Store, flat and unpartitionable, so the entry name is the only
44
+ * partition there is. A feature therefore needs its own names for the same reason it needs its own
45
+ * database: without them a branch would adopt staging's master key, and its teardown would delete it.
46
+ *
47
+ * Held to the **Secrets Store** limit rather than R2's 63, like every other entry name — an entry has
48
+ * no documented Cloudflare cap, and holding it to the strictest kind's would hash
49
+ * `secrets-encryption-keys` down to nothing for no reason.
50
+ *
51
+ * A `global` secret is not named here at all: it is one account-level value every environment binds,
52
+ * so a feature binds the project's `<project>-global-<secret>` rather than minting a second copy.
53
+ */
54
+ export declare function featureSecretEntryName(identity: FeatureIdentity, secret: string): string;
55
+ /**
56
+ * The Worker **script name** for one of a feature's workers — `<project>-f<issue>-<slug>-<worker>`.
57
+ *
58
+ * This is the name the feature's Workers deploy under, and therefore the name a sibling's `service`
59
+ * binding must target, so RPC inside a feature environment reaches that feature's deployment rather than
60
+ * production's.
61
+ *
62
+ * Held to the **Worker rule** ({@link NAMESPACE_LIMITS.worker}), which is the bug this used to have: it
63
+ * floored the slug at one character and never touched the worker segment, so it ran unbounded — 69
64
+ * characters for a worker called `collaboration-realtime-gateway`, 109 for a 70-character one. `apps/<name>`
65
+ * has a charset rule and no length rule, so nothing upstream was going to stop it either.
66
+ */
67
+ export declare function featureWorkerName(identity: FeatureIdentity, worker: string): string;
68
+ //# sourceMappingURL=feature.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"feature.d.ts","sourceRoot":"","sources":["../../src/naming/feature.ts"],"names":[],"mappings":"AAOA;;;;;;;;;;;;;;;;GAgBG;AAEH,iHAAiH;AACjH,MAAM,MAAM,mBAAmB,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAErD,6EAA6E;AAC7E,eAAO,MAAM,sBAAsB,EAAE,SAAS,mBAAmB,EAAuB,CAAC;AAEzF,6EAA6E;AAC7E,MAAM,WAAW,eAAe;IAC9B,uEAAuE;IACvE,OAAO,EAAE,MAAM,CAAC;IAChB,6FAA6F;IAC7F,KAAK,EAAE,MAAM,CAAC;IACd,uEAAuE;IACvE,IAAI,EAAE,MAAM,CAAC;CACd;AAkED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,mBAAmB,GAAG,MAAM,CAQjH;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAQxF;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAEnF"}
@@ -0,0 +1,106 @@
1
+ import { ValidationError } from "../error/pithyError.js";
2
+ import { NAMESPACE_LIMITS } from "./limits.js";
3
+ import { assertValidProjectName, fitSegment, kebab } from "./resource.js";
4
+ //#region src/naming/feature.ts
5
+ /** Every feature resource kind, for tests and for callers that enumerate. */
6
+ const FEATURE_RESOURCE_KINDS = [
7
+ "d1",
8
+ "kv",
9
+ "r2"
10
+ ];
11
+ /** Below this the slug segment isn't worth keeping legible; the trailing segment is truncated to give it room. */
12
+ const MIN_SLUG_BUDGET = 3;
13
+ /**
14
+ * Refuse an issue number the budgets were not derived against.
15
+ *
16
+ * `MAX_ISSUE_DIGITS` is a term in {@link FEATURE_DERIVED_PROJECT_NAME}, so an eight-digit issue would
17
+ * quietly spend a character every project name was already accepted against. It is also the cheapest
18
+ * possible check that the value is an issue number at all — `featureResourceName` used to interpolate
19
+ * whatever string it was handed straight into a Cloudflare name.
20
+ */
21
+ function assertIssue(issue) {
22
+ if (new RegExp(`^[0-9]{1,6}$`).test(issue)) return;
23
+ throw new ValidationError({
24
+ message: `"${issue}" is not an issue number.`,
25
+ action: `Use the issue's digits, up to 6 of them.`,
26
+ detail: `Feature resource names reserve 6 digits, and every project-name budget is derived against that.`
27
+ });
28
+ }
29
+ /** The `<project>-f<issue>` head both feature shapes share, with the project held to the one project rule. */
30
+ function head(identity) {
31
+ assertValidProjectName(identity.project);
32
+ assertIssue(identity.issue);
33
+ return `${kebab(identity.project)}-f${identity.issue}`;
34
+ }
35
+ /**
36
+ * Fit `<head>-<slug>-<tail><fixed>` into `budget`, truncating the slug first and the tail only if the
37
+ * tail is what is eating the name. `fixed` is never touched — it carries the kind suffix, which is the
38
+ * only thing telling a `DB` bucket from a `DB` database.
39
+ *
40
+ * Deterministic in its inputs and hash-disambiguated on both variable segments, so two long inputs
41
+ * sharing a prefix still produce two different names.
42
+ */
43
+ function composeFeatureName(headSegment, slug, tail, fixed, budget) {
44
+ for (const [role, value] of [["slug", slug], ["binding or worker", tail]]) {
45
+ if (value) continue;
46
+ throw new ValidationError({
47
+ message: `A feature resource name needs a ${role}.`,
48
+ action: "Use letters, digits, and hyphens — something that survives kebabbing.",
49
+ detail: `composeFeatureName received an empty ${role} segment for "${headSegment}".`
50
+ });
51
+ }
52
+ const inner = budget - fixed.length;
53
+ let tailSegment = tail;
54
+ let slugBudget = inner - headSegment.length - 1 - (1 + tailSegment.length);
55
+ if (slugBudget < MIN_SLUG_BUDGET) {
56
+ const tailBudget = inner - headSegment.length - 1 - MIN_SLUG_BUDGET - 1;
57
+ tailSegment = fitSegment(tailSegment, Math.max(1, tailBudget));
58
+ slugBudget = inner - headSegment.length - 1 - (1 + tailSegment.length);
59
+ }
60
+ return `${headSegment}-${fitSegment(slug, Math.max(1, slugBudget))}-${tailSegment}${fixed}`;
61
+ }
62
+ /**
63
+ * The full Cloudflare resource name for a feature's binding — `<project>-f<issue>-<slug>-<binding>-<kind>`.
64
+ *
65
+ * Held to **R2's 63**, the strictest of the three kinds a feature provisions, so one shape is legal for
66
+ * all of them: lowercase, hyphenated, alphanumeric at both ends. A D1 or KV name could be longer, but a
67
+ * feature that provisions a bucket and a database wants them recognizably the same name.
68
+ */
69
+ function featureResourceName(identity, binding, kind) {
70
+ return composeFeatureName(head(identity), kebab(identity.slug), kebab(binding), `-${kind}`, NAMESPACE_LIMITS.r2.maxLength);
71
+ }
72
+ /**
73
+ * The CF Secrets Store entry name holding one of a feature's **environment-scoped** secrets —
74
+ * `<project>-f<issue>-<slug>-<secret>`.
75
+ *
76
+ * A Cloudflare account has one Secrets Store, flat and unpartitionable, so the entry name is the only
77
+ * partition there is. A feature therefore needs its own names for the same reason it needs its own
78
+ * database: without them a branch would adopt staging's master key, and its teardown would delete it.
79
+ *
80
+ * Held to the **Secrets Store** limit rather than R2's 63, like every other entry name — an entry has
81
+ * no documented Cloudflare cap, and holding it to the strictest kind's would hash
82
+ * `secrets-encryption-keys` down to nothing for no reason.
83
+ *
84
+ * A `global` secret is not named here at all: it is one account-level value every environment binds,
85
+ * so a feature binds the project's `<project>-global-<secret>` rather than minting a second copy.
86
+ */
87
+ function featureSecretEntryName(identity, secret) {
88
+ return composeFeatureName(head(identity), kebab(identity.slug), kebab(secret), "", NAMESPACE_LIMITS.secretEntry.maxLength);
89
+ }
90
+ /**
91
+ * The Worker **script name** for one of a feature's workers — `<project>-f<issue>-<slug>-<worker>`.
92
+ *
93
+ * This is the name the feature's Workers deploy under, and therefore the name a sibling's `service`
94
+ * binding must target, so RPC inside a feature environment reaches that feature's deployment rather than
95
+ * production's.
96
+ *
97
+ * Held to the **Worker rule** ({@link NAMESPACE_LIMITS.worker}), which is the bug this used to have: it
98
+ * floored the slug at one character and never touched the worker segment, so it ran unbounded — 69
99
+ * characters for a worker called `collaboration-realtime-gateway`, 109 for a 70-character one. `apps/<name>`
100
+ * has a charset rule and no length rule, so nothing upstream was going to stop it either.
101
+ */
102
+ function featureWorkerName(identity, worker) {
103
+ return composeFeatureName(head(identity), kebab(identity.slug), kebab(worker), "", NAMESPACE_LIMITS.worker.maxLength);
104
+ }
105
+ //#endregion
106
+ export { FEATURE_RESOURCE_KINDS, featureResourceName, featureSecretEntryName, featureWorkerName };