@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,169 @@
1
+ /**
2
+ * One naming rule, in one module: **`<project>-<env>-<thing>`**, kebab-case.
3
+ *
4
+ * Every Cloudflare namespace Pithy writes into is flat and account-wide — D1, KV, R2, Worker
5
+ * scripts, Workflows, the single Secrets Store, the API-token list. None of them can be
6
+ * partitioned, so the *name* is the partition, and the project segment is the only thing keeping
7
+ * two Pithy projects in one account from adopting or overwriting each other's resources.
8
+ *
9
+ * **Project first** because that is the ownership boundary everything turns on: teardown, the
10
+ * token listing's prefix filter, and the reaper's reservation all key on it. **Environment second**
11
+ * because these end up in listings nobody can filter — `wrangler d1 list`, the account dashboard,
12
+ * the Secrets Store — and sorting by name should group a project's resources and then that
13
+ * project's environments, so everything belonging to prod sits together. Environment last
14
+ * would scatter prod through the listing, interleaved with staging, which is exactly when
15
+ * someone acts on the wrong one.
16
+ *
17
+ * A `global`-scoped thing puts the literal `global` in the environment slot rather than omitting
18
+ * it, so the scheme has no exception to remember.
19
+ *
20
+ * This module is in `core` because it has no dependencies and every layer needs it — capabilities,
21
+ * the CLI, and the Workers runtime alike. It therefore uses **no `node:` builtin**: core is bundled
22
+ * into the adopter's Worker, where `node:crypto` is not free.
23
+ */
24
+ /**
25
+ * The default budget of the un-namespaced {@link resourceName} composer: the strictest cap of any
26
+ * namespace Pithy writes into, which is R2's 63.
27
+ *
28
+ * A caller that knows its namespace does **not** use this — it passes that namespace's own limit
29
+ * from `./limits`, or, better, goes through the facade in `./resourceNames`, which cannot pick the
30
+ * wrong budget. This number is the conservative answer for a call site that has no namespace at all.
31
+ */
32
+ export declare const MAX_RESOURCE_NAME: 63;
33
+ /**
34
+ * The cap on a project name — **derived, not chosen, and derived from two shapes rather than one**.
35
+ *
36
+ * A project name has to survive every name it will ever be the head of, so it gets the *smaller* of
37
+ * the two worst cases (`./limits` carries both derivations with their arithmetic):
38
+ *
39
+ * - {@link WORKFLOW_DERIVED_PROJECT_NAME} — `<project>-<env>-<capability>-<job>` against a Workflow's
40
+ * 64 characters, after the longest environment and the longest `<capability>-<job>`.
41
+ * - {@link FEATURE_DERIVED_PROJECT_NAME} — `<project>-f<issue>-<slug>-<binding>-<kind>` against R2's
42
+ * 63, with room for the reserved issue digits, a legible slug, and a binding kept whole.
43
+ *
44
+ * Neither number is repeated here. Each is a computed constant with its arithmetic in `./limits`, and a
45
+ * copy in this sentence is a copy that goes stale the first time a term of the derivation moves.
46
+ *
47
+ * The feature shape is the binding one, and it used to be invisible: the cap was derived from the
48
+ * Workflow alone, so a project inside it could still compose a feature name whose slug was hashed
49
+ * away to nothing. Taking the minimum is the only version that is true of both.
50
+ *
51
+ * **Why the cap belongs on the project name rather than on each composed name:** without it the
52
+ * refusal lands at the first `pithy <capability> provision`, by which time `pithy add` has created
53
+ * real R2 buckets and D1 databases under a name that fits *their* budget. The project is
54
+ * half-provisioned, and the only documented remedy — renaming — orphans everything already made.
55
+ * Capped here, `pithy init` and `requireProjectName` refuse it before anything is written.
56
+ */
57
+ export declare const MAX_PROJECT_NAME: number;
58
+ /** Lowercase, collapse any run of non-`[a-z0-9]` to one `-`, and trim leading/trailing `-`. */
59
+ export declare function kebab(input: string): string;
60
+ /**
61
+ * A stable 6-hex-character digest, FNV-1a. Pure arithmetic rather than `node:crypto` so this module
62
+ * runs unchanged in a Worker. It is a *disambiguator for truncated names*, never a security
63
+ * primitive — collision resistance beyond "two different capability names look different" is not
64
+ * claimed and must not be relied on.
65
+ */
66
+ export declare function hash6(input: string): string;
67
+ /**
68
+ * Fit a kebab segment into `budget` characters: verbatim when it fits, otherwise a truncated head
69
+ * plus a short stable hash so it stays distinct and within budget. Deterministic in `segment`
70
+ * alone, so two commands computing the same name agree without storing it. The hash shrinks for
71
+ * tiny budgets, and the result never exceeds `budget` nor trails a hyphen.
72
+ */
73
+ export declare function fitSegment(segment: string, budget: number): string;
74
+ /** The parts of a provisioned name. Every field is required — an absent segment is a naming bug, not a default. */
75
+ export interface ResourceNameParts {
76
+ /**
77
+ * The project name — the root `pithy.config.ts` `name`, resolved by `requireProjectName` and
78
+ * **never guessed**. This is the ownership boundary; a wrong value here writes into another
79
+ * project's resources.
80
+ */
81
+ project: string;
82
+ /** The environment (`dev`, `staging`, `prod`), or the literal `global` for a value shared across all of them. */
83
+ env: string;
84
+ /** What this resource is — a capability, a binding, a secret, a token profile. The only segment ever truncated. */
85
+ thing: string;
86
+ }
87
+ /**
88
+ * Compose `<project>-<env>-<thing>`, kebab-cased and fitted to `budget`.
89
+ *
90
+ * **Only `thing` is ever truncated.** The project and environment segments are taken verbatim,
91
+ * because a hashed project segment would let two long project names share a prefix — and the token
92
+ * listing filters on exactly that prefix, so a collision there means one project enumerating and
93
+ * acting on another's credentials. If project and environment alone cannot fit, that is refused
94
+ * with the limit named rather than quietly hashed into an ambiguous name.
95
+ *
96
+ * `budget` defaults to {@link MAX_RESOURCE_NAME}; a namespace with its own limit passes it in.
97
+ *
98
+ * The project segment is held to {@link isValidProjectName} — the same rule the Worker and Workflow
99
+ * namers apply — so a project this accepts is one every namespace accepts. It is a backstop, not the
100
+ * gate: the real refusal belongs at `pithy init` and `requireProjectName`, before anything is written.
101
+ */
102
+ export declare function resourceName(parts: ResourceNameParts, budget?: number): string;
103
+ /**
104
+ * Could every Cloudflare name this project generates be created? The one rule, in one place, so no
105
+ * namespace re-derives a looser version of it.
106
+ *
107
+ * **Read after kebabbing**, for the same reason {@link isReservedProjectName} is: the raw string never
108
+ * reaches an account, only what the composer made of it. `Acme Corp` is legal (it becomes `acme-corp`);
109
+ * `2026 Launch` is not, and neither answer can be read off the raw characters.
110
+ *
111
+ * **Two dimensions, one answer: charset and length.** Both have the same failure mode and so must have
112
+ * the same home — {@link NAME_SEGMENT} for the charset, {@link MAX_PROJECT_NAME} for the number. The
113
+ * charset is the shared segment rule rather than a project-specific one: a project name is the head of
114
+ * every composed name, so it is held to exactly what every other segment is held to.
115
+ *
116
+ * The rule matters at the *source* — `pithy init` and `requireProjectName` — because the namespaces
117
+ * disagree about it. A digit-leading project composes a legal D1 database name, a legal KV title, and a
118
+ * legal bucket name, so `pithy add` provisions all three for real; only the first host-worker deploy
119
+ * refuses it, by which point the documented remedy — rename the project — orphans everything already
120
+ * created. A 33-character project fails exactly the same way, one deploy later. One rule, enforced
121
+ * before anything is written, is the only version of this that is safe.
122
+ */
123
+ export declare function isValidProjectName(name: string): boolean;
124
+ /**
125
+ * Refuse a project name no Cloudflare namespace could carry, as a **`ValidationError`**: a project name
126
+ * is something a human typed, so it is a 400 with an action, never an internal 500 blamed on the toolkit.
127
+ *
128
+ * Length and charset are one rule with two sentences, because they are two different mistakes: a name
129
+ * that is merely long is otherwise perfect, and "use lowercase letters" would be no help at all.
130
+ */
131
+ export declare function assertValidProjectName(name: string): void;
132
+ /**
133
+ * **`pithy-int-` is reserved for Pithy's own live integration tests, on any account.**
134
+ *
135
+ * Every Cloudflare name a live test creates begins with it, no real project's name may, and the
136
+ * test-debris reaper deletes nothing without it. The reservation is what makes automatic reaping safe
137
+ * in both directions: without it the reaper is guessing — either it fails to recognize the names a
138
+ * suite invented, and debris accumulates silently on a real account, or it matches loosely enough to
139
+ * one day delete a customer's database. With it, membership is decidable from the name alone.
140
+ *
141
+ * It lives here, next to the composer, because both halves of the reservation are naming facts.
142
+ * `<project>-<env>-<thing>` puts the project first and takes it **verbatim**, so the prefix of every
143
+ * generated name is decided by the project name and nothing else — which is what lets one guard, on
144
+ * `pithy init`, keep the two sets from ever overlapping. See {@link isReservedProjectName}.
145
+ *
146
+ * This is deliberately narrower than the product's own `pithy` prefix. A project may legitimately be
147
+ * named `pithy-app`, and everything it provisions is real; `pithy-int-…` is debris by definition.
148
+ */
149
+ export declare const RESERVED_TEST_PREFIX = "pithy-int-";
150
+ /**
151
+ * Would a project of this name generate resource names inside the reservation? The one rule
152
+ * `pithy init` enforces, stated once so no call site re-derives it.
153
+ *
154
+ * Two traps make the obvious `name.startsWith(RESERVED_TEST_PREFIX)` wrong, and both are reachable
155
+ * with a name a human would type.
156
+ *
157
+ * **Kebabbing.** The composer kebabs the project before using it, so the raw string is not what
158
+ * Cloudflare sees. `"Pithy Int Ruse"` is not prefixed by `pithy-int-`, and it normalizes straight into
159
+ * the reservation.
160
+ *
161
+ * **The trailing hyphen.** The composer appends one after the project. A project named exactly
162
+ * `pithy-int` is not prefixed by `pithy-int-` either, and it generates `pithy-int-dev-db` — inside the
163
+ * namespace the reaper deletes.
164
+ *
165
+ * Testing `` `${kebab(name)}-` `` against the prefix closes both: it compares the form the composer
166
+ * actually uses, including the separator it actually adds.
167
+ */
168
+ export declare function isReservedProjectName(name: string): boolean;
169
+ //# sourceMappingURL=resource.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resource.d.ts","sourceRoot":"","sources":["../../src/naming/resource.ts"],"names":[],"mappings":"AAaA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH;;;;;;;GAOG;AACH,eAAO,MAAM,iBAAiB,IAAgC,CAAC;AAK/D;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,gBAAgB,QAAwE,CAAC;AAEtG,+FAA+F;AAC/F,wBAAgB,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAK3C;AAED;;;;;GAKG;AACH,wBAAgB,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAO3C;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAOlE;AAED,mHAAmH;AACnH,MAAM,WAAW,iBAAiB;IAChC;;;;OAIG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB,iHAAiH;IACjH,GAAG,EAAE,MAAM,CAAC;IACZ,mHAAmH;IACnH,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,EAAE,MAAM,GAAE,MAA0B,GAAG,MAAM,CAgCjG;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAGxD;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAezD;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,oBAAoB,eAAe,CAAC;AAEjD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE3D"}
@@ -0,0 +1,220 @@
1
+ import { NAME_SEGMENT } from "./segment.js";
2
+ import { InternalError, ValidationError } from "../error/pithyError.js";
3
+ import { MAX_ENVIRONMENT_NAME } from "./environment.js";
4
+ import { FEATURE_DERIVED_PROJECT_NAME, NAMESPACE_LIMITS, WORKFLOW_DERIVED_PROJECT_NAME } from "./limits.js";
5
+ //#region src/naming/resource.ts
6
+ /**
7
+ * One naming rule, in one module: **`<project>-<env>-<thing>`**, kebab-case.
8
+ *
9
+ * Every Cloudflare namespace Pithy writes into is flat and account-wide — D1, KV, R2, Worker
10
+ * scripts, Workflows, the single Secrets Store, the API-token list. None of them can be
11
+ * partitioned, so the *name* is the partition, and the project segment is the only thing keeping
12
+ * two Pithy projects in one account from adopting or overwriting each other's resources.
13
+ *
14
+ * **Project first** because that is the ownership boundary everything turns on: teardown, the
15
+ * token listing's prefix filter, and the reaper's reservation all key on it. **Environment second**
16
+ * because these end up in listings nobody can filter — `wrangler d1 list`, the account dashboard,
17
+ * the Secrets Store — and sorting by name should group a project's resources and then that
18
+ * project's environments, so everything belonging to prod sits together. Environment last
19
+ * would scatter prod through the listing, interleaved with staging, which is exactly when
20
+ * someone acts on the wrong one.
21
+ *
22
+ * A `global`-scoped thing puts the literal `global` in the environment slot rather than omitting
23
+ * it, so the scheme has no exception to remember.
24
+ *
25
+ * This module is in `core` because it has no dependencies and every layer needs it — capabilities,
26
+ * the CLI, and the Workers runtime alike. It therefore uses **no `node:` builtin**: core is bundled
27
+ * into the adopter's Worker, where `node:crypto` is not free.
28
+ */
29
+ /**
30
+ * The default budget of the un-namespaced {@link resourceName} composer: the strictest cap of any
31
+ * namespace Pithy writes into, which is R2's 63.
32
+ *
33
+ * A caller that knows its namespace does **not** use this — it passes that namespace's own limit
34
+ * from `./limits`, or, better, goes through the facade in `./resourceNames`, which cannot pick the
35
+ * wrong budget. This number is the conservative answer for a call site that has no namespace at all.
36
+ */
37
+ const MAX_RESOURCE_NAME = NAMESPACE_LIMITS.r2.maxLength;
38
+ /** Below this the trailing segment is not worth keeping legible, so a name this tight is refused instead. */
39
+ const MIN_THING_BUDGET = 1;
40
+ /**
41
+ * The cap on a project name — **derived, not chosen, and derived from two shapes rather than one**.
42
+ *
43
+ * A project name has to survive every name it will ever be the head of, so it gets the *smaller* of
44
+ * the two worst cases (`./limits` carries both derivations with their arithmetic):
45
+ *
46
+ * - {@link WORKFLOW_DERIVED_PROJECT_NAME} — `<project>-<env>-<capability>-<job>` against a Workflow's
47
+ * 64 characters, after the longest environment and the longest `<capability>-<job>`.
48
+ * - {@link FEATURE_DERIVED_PROJECT_NAME} — `<project>-f<issue>-<slug>-<binding>-<kind>` against R2's
49
+ * 63, with room for the reserved issue digits, a legible slug, and a binding kept whole.
50
+ *
51
+ * Neither number is repeated here. Each is a computed constant with its arithmetic in `./limits`, and a
52
+ * copy in this sentence is a copy that goes stale the first time a term of the derivation moves.
53
+ *
54
+ * The feature shape is the binding one, and it used to be invisible: the cap was derived from the
55
+ * Workflow alone, so a project inside it could still compose a feature name whose slug was hashed
56
+ * away to nothing. Taking the minimum is the only version that is true of both.
57
+ *
58
+ * **Why the cap belongs on the project name rather than on each composed name:** without it the
59
+ * refusal lands at the first `pithy <capability> provision`, by which time `pithy add` has created
60
+ * real R2 buckets and D1 databases under a name that fits *their* budget. The project is
61
+ * half-provisioned, and the only documented remedy — renaming — orphans everything already made.
62
+ * Capped here, `pithy init` and `requireProjectName` refuse it before anything is written.
63
+ */
64
+ const MAX_PROJECT_NAME = Math.min(WORKFLOW_DERIVED_PROJECT_NAME, FEATURE_DERIVED_PROJECT_NAME);
65
+ /** Lowercase, collapse any run of non-`[a-z0-9]` to one `-`, and trim leading/trailing `-`. */
66
+ function kebab(input) {
67
+ return input.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
68
+ }
69
+ /**
70
+ * A stable 6-hex-character digest, FNV-1a. Pure arithmetic rather than `node:crypto` so this module
71
+ * runs unchanged in a Worker. It is a *disambiguator for truncated names*, never a security
72
+ * primitive — collision resistance beyond "two different capability names look different" is not
73
+ * claimed and must not be relied on.
74
+ */
75
+ function hash6(input) {
76
+ let h = 2166136261;
77
+ for (let i = 0; i < input.length; i += 1) {
78
+ h ^= input.charCodeAt(i);
79
+ h = Math.imul(h, 16777619) >>> 0;
80
+ }
81
+ return h.toString(16).padStart(8, "0").slice(0, 6);
82
+ }
83
+ /**
84
+ * Fit a kebab segment into `budget` characters: verbatim when it fits, otherwise a truncated head
85
+ * plus a short stable hash so it stays distinct and within budget. Deterministic in `segment`
86
+ * alone, so two commands computing the same name agree without storing it. The hash shrinks for
87
+ * tiny budgets, and the result never exceeds `budget` nor trails a hyphen.
88
+ */
89
+ function fitSegment(segment, budget) {
90
+ if (budget <= 0) return "";
91
+ if (segment.length <= budget) return segment;
92
+ if (budget < 4) return hash6(segment).slice(0, budget);
93
+ const hashLen = Math.min(6, Math.max(2, budget - 2));
94
+ const headLen = Math.max(1, budget - hashLen - 1);
95
+ return `${segment.slice(0, headLen).replace(/-+$/, "")}-${hash6(segment).slice(0, hashLen)}`;
96
+ }
97
+ /**
98
+ * Compose `<project>-<env>-<thing>`, kebab-cased and fitted to `budget`.
99
+ *
100
+ * **Only `thing` is ever truncated.** The project and environment segments are taken verbatim,
101
+ * because a hashed project segment would let two long project names share a prefix — and the token
102
+ * listing filters on exactly that prefix, so a collision there means one project enumerating and
103
+ * acting on another's credentials. If project and environment alone cannot fit, that is refused
104
+ * with the limit named rather than quietly hashed into an ambiguous name.
105
+ *
106
+ * `budget` defaults to {@link MAX_RESOURCE_NAME}; a namespace with its own limit passes it in.
107
+ *
108
+ * The project segment is held to {@link isValidProjectName} — the same rule the Worker and Workflow
109
+ * namers apply — so a project this accepts is one every namespace accepts. It is a backstop, not the
110
+ * gate: the real refusal belongs at `pithy init` and `requireProjectName`, before anything is written.
111
+ */
112
+ function resourceName(parts, budget = MAX_RESOURCE_NAME) {
113
+ const project = kebab(parts.project);
114
+ const env = kebab(parts.env);
115
+ const thing = kebab(parts.thing);
116
+ for (const [role, value] of [
117
+ ["project", project],
118
+ ["environment", env],
119
+ ["thing", thing]
120
+ ]) if (!value) throw new ValidationError({
121
+ message: `A resource name needs a ${role}.`,
122
+ action: role === "project" ? "Set `name` in pithy.config.ts." : void 0,
123
+ detail: `resourceName received an empty ${role} segment (raw: ${JSON.stringify(parts)})`
124
+ });
125
+ assertValidProjectName(parts.project);
126
+ const head = `${project}-${env}`;
127
+ const thingBudget = budget - head.length - 1;
128
+ if (thingBudget < MIN_THING_BUDGET) throw new InternalError({
129
+ message: `The project and environment names alone exceed the ${budget}-character limit for this resource.`,
130
+ action: "Shorten `name` in pithy.config.ts.",
131
+ detail: `"${head}" leaves ${thingBudget} characters for "${thing}"`
132
+ });
133
+ return `${head}-${fitSegment(thing, thingBudget)}`;
134
+ }
135
+ /**
136
+ * Could every Cloudflare name this project generates be created? The one rule, in one place, so no
137
+ * namespace re-derives a looser version of it.
138
+ *
139
+ * **Read after kebabbing**, for the same reason {@link isReservedProjectName} is: the raw string never
140
+ * reaches an account, only what the composer made of it. `Acme Corp` is legal (it becomes `acme-corp`);
141
+ * `2026 Launch` is not, and neither answer can be read off the raw characters.
142
+ *
143
+ * **Two dimensions, one answer: charset and length.** Both have the same failure mode and so must have
144
+ * the same home — {@link NAME_SEGMENT} for the charset, {@link MAX_PROJECT_NAME} for the number. The
145
+ * charset is the shared segment rule rather than a project-specific one: a project name is the head of
146
+ * every composed name, so it is held to exactly what every other segment is held to.
147
+ *
148
+ * The rule matters at the *source* — `pithy init` and `requireProjectName` — because the namespaces
149
+ * disagree about it. A digit-leading project composes a legal D1 database name, a legal KV title, and a
150
+ * legal bucket name, so `pithy add` provisions all three for real; only the first host-worker deploy
151
+ * refuses it, by which point the documented remedy — rename the project — orphans everything already
152
+ * created. A 33-character project fails exactly the same way, one deploy later. One rule, enforced
153
+ * before anything is written, is the only version of this that is safe.
154
+ */
155
+ function isValidProjectName(name) {
156
+ const normalized = kebab(name);
157
+ return normalized.length <= MAX_PROJECT_NAME && NAME_SEGMENT.test(normalized);
158
+ }
159
+ /**
160
+ * Refuse a project name no Cloudflare namespace could carry, as a **`ValidationError`**: a project name
161
+ * is something a human typed, so it is a 400 with an action, never an internal 500 blamed on the toolkit.
162
+ *
163
+ * Length and charset are one rule with two sentences, because they are two different mistakes: a name
164
+ * that is merely long is otherwise perfect, and "use lowercase letters" would be no help at all.
165
+ */
166
+ function assertValidProjectName(name) {
167
+ if (isValidProjectName(name)) return;
168
+ const normalized = kebab(name);
169
+ if (normalized.length > MAX_PROJECT_NAME && NAME_SEGMENT.test(normalized)) throw new ValidationError({
170
+ message: `"${name}" is ${normalized.length} characters. A project name stops at ${MAX_PROJECT_NAME}.`,
171
+ action: `Shorten \`name\` in pithy.config.ts to ${MAX_PROJECT_NAME} characters or fewer.`,
172
+ detail: `${MAX_PROJECT_NAME} is the smaller of what a 64-character Workflow name leaves after a ${MAX_ENVIRONMENT_NAME}-character environment and a 22-character <capability>-<job> (${WORKFLOW_DERIVED_PROJECT_NAME}), and what a 63-character feature resource name leaves after a six-digit issue, a legible slug, and a binding (${FEATURE_DERIVED_PROJECT_NAME}).`
173
+ });
174
+ throw new ValidationError({
175
+ message: `"${name}" can't be a project name.`,
176
+ action: "Use lowercase letters, digits, and single hyphens, starting with a letter.",
177
+ detail: `"${name}" kebabs to "${normalized}", which does not match ${NAME_SEGMENT.source}.`
178
+ });
179
+ }
180
+ /**
181
+ * **`pithy-int-` is reserved for Pithy's own live integration tests, on any account.**
182
+ *
183
+ * Every Cloudflare name a live test creates begins with it, no real project's name may, and the
184
+ * test-debris reaper deletes nothing without it. The reservation is what makes automatic reaping safe
185
+ * in both directions: without it the reaper is guessing — either it fails to recognize the names a
186
+ * suite invented, and debris accumulates silently on a real account, or it matches loosely enough to
187
+ * one day delete a customer's database. With it, membership is decidable from the name alone.
188
+ *
189
+ * It lives here, next to the composer, because both halves of the reservation are naming facts.
190
+ * `<project>-<env>-<thing>` puts the project first and takes it **verbatim**, so the prefix of every
191
+ * generated name is decided by the project name and nothing else — which is what lets one guard, on
192
+ * `pithy init`, keep the two sets from ever overlapping. See {@link isReservedProjectName}.
193
+ *
194
+ * This is deliberately narrower than the product's own `pithy` prefix. A project may legitimately be
195
+ * named `pithy-app`, and everything it provisions is real; `pithy-int-…` is debris by definition.
196
+ */
197
+ const RESERVED_TEST_PREFIX = "pithy-int-";
198
+ /**
199
+ * Would a project of this name generate resource names inside the reservation? The one rule
200
+ * `pithy init` enforces, stated once so no call site re-derives it.
201
+ *
202
+ * Two traps make the obvious `name.startsWith(RESERVED_TEST_PREFIX)` wrong, and both are reachable
203
+ * with a name a human would type.
204
+ *
205
+ * **Kebabbing.** The composer kebabs the project before using it, so the raw string is not what
206
+ * Cloudflare sees. `"Pithy Int Ruse"` is not prefixed by `pithy-int-`, and it normalizes straight into
207
+ * the reservation.
208
+ *
209
+ * **The trailing hyphen.** The composer appends one after the project. A project named exactly
210
+ * `pithy-int` is not prefixed by `pithy-int-` either, and it generates `pithy-int-dev-db` — inside the
211
+ * namespace the reaper deletes.
212
+ *
213
+ * Testing `` `${kebab(name)}-` `` against the prefix closes both: it compares the form the composer
214
+ * actually uses, including the separator it actually adds.
215
+ */
216
+ function isReservedProjectName(name) {
217
+ return `${kebab(name)}-`.startsWith(RESERVED_TEST_PREFIX);
218
+ }
219
+ //#endregion
220
+ export { MAX_PROJECT_NAME, MAX_RESOURCE_NAME, RESERVED_TEST_PREFIX, assertValidProjectName, fitSegment, hash6, isReservedProjectName, isValidProjectName, kebab, resourceName };
@@ -0,0 +1,83 @@
1
+ import { type FeatureIdentity, type FeatureResourceKind } from "./feature";
2
+ /**
3
+ * Every Cloudflare name a project composes, behind one object — **the facade**.
4
+ *
5
+ * The free functions underneath are correct and stay exported, but they share one flaw: each takes a
6
+ * budget, so every call site decides for itself which limit applies. That is how one number, 63, ended
7
+ * up standing in for eight different namespaces. Here the budget is not an argument at all. You pick a
8
+ * *kind of thing* — a bucket, a KV namespace, a Workflow — and the kind carries its own cap and its own
9
+ * refuse-or-truncate policy from `./limits`. There is no wrong budget to pass, because there is no
10
+ * budget to pass.
11
+ *
12
+ * **Shape: project, then scope, then kind.**
13
+ *
14
+ * ```ts
15
+ * const names = resourceNames(config.name); // the project, validated once
16
+ * const prod = names.env("prod"); // the environment, validated once
17
+ * prod.d1("DB"); // acme-prod-db
18
+ * prod.workflow("email", "send"); // acme-prod-email-send
19
+ * names.global.secretEntry("secrets-manager-cf-api-token");
20
+ * names.feature({ issue, slug }).resource("DB", "d1");
21
+ * ```
22
+ *
23
+ * Three reasons for that order rather than a flat `worker(capability, env)`:
24
+ *
25
+ * 1. **Each thing is validated once, at the point it is named.** A bad project fails at
26
+ * `resourceNames`, before a single name exists; a bad environment fails at `env`, not on the fourth
27
+ * getter that happened to use it.
28
+ * 2. **The environment is threaded once, not through every call.** Commands are environment-scoped —
29
+ * `pithy deploy --env prod` names a dozen resources in one environment — so binding it once removes a
30
+ * dozen chances to pass the wrong one.
31
+ * 3. **`global` becomes a property, not a magic string.** It is the same interface as an environment
32
+ * (`names.global.secretEntry(…)`), so no call site special-cases the literal, and no call site can
33
+ * typo it into a real environment named `globl`.
34
+ *
35
+ * The feature shape hangs off the same object for the same reason it exists at all: it is the *other*
36
+ * scope a project's resources live in, and it shares the project segment with everything above.
37
+ */
38
+ /** The names available within one scope — an environment, or the global scope beside them. */
39
+ export interface ScopedNames {
40
+ /** A capability's Worker script — `<project>-<env>-<capability>`. Refused, not truncated, past 63. */
41
+ worker(capability: string): string;
42
+ /** One durable job's Workflow — `<project>-<env>-<capability>-<job>`. Refused, not truncated, past 64. */
43
+ workflow(capability: string, job: string): string;
44
+ /** A D1 database. No Cloudflare cap; Pithy's ceiling, truncated with a stable hash. */
45
+ d1(binding: string): string;
46
+ /** A KV namespace title. Cloudflare's 512, not the 63 this used to be held to. */
47
+ kv(binding: string): string;
48
+ /** An R2 bucket. Cloudflare's 63 — the one namespace where that number was always right. */
49
+ r2(binding: string): string;
50
+ /** A Vectorize index. 64 bytes, refused rather than truncated: a renamed index has no vectors. */
51
+ vectorizeIndex(index: string): string;
52
+ /** A Secrets Store entry. No Cloudflare cap; Pithy's ceiling. */
53
+ secretEntry(secret: string): string;
54
+ /** A Cloudflare API token label. No Cloudflare cap; Pithy's ceiling. */
55
+ apiToken(profile: string): string;
56
+ }
57
+ /** The names a feature environment provisions under, with the project already bound. */
58
+ export interface FeatureNames {
59
+ /** A feature's D1, KV, or R2 resource — `<project>-f<issue>-<slug>-<binding>-<kind>`. */
60
+ resource(binding: string, kind: FeatureResourceKind): string;
61
+ /** A feature's Worker script — `<project>-f<issue>-<slug>-<worker>`. */
62
+ worker(worker: string): string;
63
+ }
64
+ /** Everything a project can be asked to name. Created once, from the project, by {@link resourceNames}. */
65
+ export interface ProjectNames {
66
+ /** The kebabbed project name every name below leads with. */
67
+ readonly project: string;
68
+ /** The names in one environment. Throws if the environment is not one this scheme accepts. */
69
+ env(environment: string): ScopedNames;
70
+ /** The names shared by every environment — the `global` scope, without typing the word. */
71
+ readonly global: ScopedNames;
72
+ /** The names a feature environment provisions under. */
73
+ feature(identity: Omit<FeatureIdentity, "project">): FeatureNames;
74
+ }
75
+ /**
76
+ * Bind a project to every name it can compose.
77
+ *
78
+ * The project is validated here — charset *and* length, the one project rule — so a project that cannot
79
+ * carry every namespace fails before it has provisioned any of them. That is the whole reason the
80
+ * project is supplied once instead of per call.
81
+ */
82
+ export declare function resourceNames(project: string): ProjectNames;
83
+ //# sourceMappingURL=resourceNames.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resourceNames.d.ts","sourceRoot":"","sources":["../../src/naming/resourceNames.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,KAAK,eAAe,EAAE,KAAK,mBAAmB,EAA0C,MAAM,WAAW,CAAC;AAInH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAEH,8FAA8F;AAC9F,MAAM,WAAW,WAAW;IAC1B,sGAAsG;IACtG,MAAM,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAAC;IACnC,0GAA0G;IAC1G,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;IAClD,uFAAuF;IACvF,EAAE,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC;IAC5B,kFAAkF;IAClF,EAAE,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC;IAC5B,4FAA4F;IAC5F,EAAE,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC;IAC5B,kGAAkG;IAClG,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;IACtC,iEAAiE;IACjE,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;IACpC,wEAAwE;IACxE,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC;CACnC;AAED,wFAAwF;AACxF,MAAM,WAAW,YAAY;IAC3B,yFAAyF;IACzF,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,mBAAmB,GAAG,MAAM,CAAC;IAC7D,wEAAwE;IACxE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;CAChC;AAED,2GAA2G;AAC3G,MAAM,WAAW,YAAY;IAC3B,6DAA6D;IAC7D,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,8FAA8F;IAC9F,GAAG,CAAC,WAAW,EAAE,MAAM,GAAG,WAAW,CAAC;IACtC,2FAA2F;IAC3F,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B,wDAAwD;IACxD,OAAO,CAAC,QAAQ,EAAE,IAAI,CAAC,eAAe,EAAE,SAAS,CAAC,GAAG,YAAY,CAAC;CACnE;AA8DD;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,YAAY,CAmB3D"}
@@ -0,0 +1,102 @@
1
+ import { InternalError, ValidationError } from "../error/pithyError.js";
2
+ import { GLOBAL_SCOPE, assertValidEnvironment } from "./environment.js";
3
+ import { NAMESPACE_LIMITS } from "./limits.js";
4
+ import { assertValidProjectName, kebab, resourceName } from "./resource.js";
5
+ import { workflowHostName, workflowScriptName } from "../workflow/naming.js";
6
+ import { featureResourceName, featureWorkerName } from "./feature.js";
7
+ //#region src/naming/resourceNames.ts
8
+ /**
9
+ * Compose `<project>-<scope>-<thing>` under one namespace's rule.
10
+ *
11
+ * A `truncate` namespace hands off to {@link resourceName} with that namespace's cap, so there is still
12
+ * exactly one truncation rule. A `refuse` namespace composes verbatim and throws with the limit named —
13
+ * as an `InternalError`, because by this point the project and scope have both been validated, so an
14
+ * over-long name means author-controlled code asked for one.
15
+ *
16
+ * **The refuse branch here is deliberately not `../workflow/naming`'s**, though both compose
17
+ * `<project>-<scope>-<thing>` and both refuse rather than truncate. They answer different questions: a
18
+ * Workflow or Worker name is assembled from author-controlled capability and job identifiers, so that
19
+ * module *asserts* each segment against `NAME_SEGMENT` and measures the result in **bytes**, the
20
+ * unit Cloudflare states those two limits in. The kinds routed through here are named after a binding
21
+ * or a secret, which is an adopter's string, so it is kebabbed into shape instead. Merging the two would
22
+ * either start mangling a capability name or stop normalizing a binding.
23
+ */
24
+ function scopedName(project, scope, thing, limit) {
25
+ if (limit.policy === "truncate") {
26
+ const name = resourceName({
27
+ project,
28
+ env: scope,
29
+ thing
30
+ }, limit.maxLength);
31
+ if (name.length < limit.minLength) throw new InternalError({
32
+ message: `"${name}" is too short for ${limit.label}.`,
33
+ action: `Use a longer project, environment, or resource name — ${limit.label} needs ${limit.minLength} characters.`
34
+ });
35
+ return name;
36
+ }
37
+ const segment = kebab(thing);
38
+ if (!segment) throw new ValidationError({
39
+ message: `A name needs something to name.`,
40
+ detail: `${limit.label} was asked for with an empty segment (raw: ${JSON.stringify(thing)}).`
41
+ });
42
+ const name = `${kebab(project)}-${scope}-${segment}`;
43
+ if (name.length > limit.maxLength) throw new InternalError({
44
+ message: `"${name}" is ${name.length} characters. ${limit.label[0]?.toUpperCase()}${limit.label.slice(1)} stops at ${limit.maxLength}.`,
45
+ action: "Shorten the project, environment, or resource name.",
46
+ detail: `${limit.label} is refused rather than truncated — renaming it after a deploy orphans what it addresses.`
47
+ });
48
+ return name;
49
+ }
50
+ /** The getters for one scope. `global` and every environment share this, so nothing special-cases either. */
51
+ function scopedNames(project, scope) {
52
+ return {
53
+ worker: (capability) => workflowHostName({
54
+ project,
55
+ capability,
56
+ env: scope
57
+ }),
58
+ workflow: (capability, job) => workflowScriptName({
59
+ project,
60
+ capability,
61
+ job,
62
+ env: scope
63
+ }),
64
+ d1: (binding) => scopedName(project, scope, binding, NAMESPACE_LIMITS.d1),
65
+ kv: (binding) => scopedName(project, scope, binding, NAMESPACE_LIMITS.kv),
66
+ r2: (binding) => scopedName(project, scope, binding, NAMESPACE_LIMITS.r2),
67
+ vectorizeIndex: (index) => scopedName(project, scope, index, NAMESPACE_LIMITS.vectorizeIndex),
68
+ secretEntry: (secret) => scopedName(project, scope, secret, NAMESPACE_LIMITS.secretEntry),
69
+ apiToken: (profile) => scopedName(project, scope, profile, NAMESPACE_LIMITS.apiToken)
70
+ };
71
+ }
72
+ /**
73
+ * Bind a project to every name it can compose.
74
+ *
75
+ * The project is validated here — charset *and* length, the one project rule — so a project that cannot
76
+ * carry every namespace fails before it has provisioned any of them. That is the whole reason the
77
+ * project is supplied once instead of per call.
78
+ */
79
+ function resourceNames(project) {
80
+ assertValidProjectName(project);
81
+ const normalized = kebab(project);
82
+ return {
83
+ project: normalized,
84
+ env(environment) {
85
+ assertValidEnvironment(environment);
86
+ return scopedNames(normalized, environment);
87
+ },
88
+ global: scopedNames(normalized, GLOBAL_SCOPE),
89
+ feature(identity) {
90
+ const full = {
91
+ ...identity,
92
+ project: normalized
93
+ };
94
+ return {
95
+ resource: (binding, kind) => featureResourceName(full, binding, kind),
96
+ worker: (worker) => featureWorkerName(full, worker)
97
+ };
98
+ }
99
+ };
100
+ }
101
+ //#endregion
102
+ export { resourceNames };
@@ -0,0 +1,29 @@
1
+ /**
2
+ * What a single segment of a composed name may look like — one pattern, one home.
3
+ *
4
+ * `<project>-<env>-<thing>` is built out of segments, and every one of them answers to the same
5
+ * shape: a project, an environment, a capability, a job. The pattern lived in three modules,
6
+ * character for character, which is the arrangement where a rule stops being one rule — an edit to
7
+ * the environment copy would silently leave the Workflow copy behind, and the two would disagree
8
+ * about a name the other had already accepted.
9
+ *
10
+ * A capability that genuinely needs a different rule should declare its own pattern beside this one
11
+ * with a comment saying what it allows that this does not, and why. Divergence is a decision, not a
12
+ * stale copy. `scaffoldProject`'s `WORKER_NAME` is the standing example: a Worker *directory* under
13
+ * `apps/` may lead with a digit, because it is a package name rather than a Cloudflare name.
14
+ */
15
+ /**
16
+ * A lowercase letter, then letters, digits, and single inner hyphens — no leading digit, no
17
+ * doubled or trailing hyphen, no underscore, no uppercase.
18
+ *
19
+ * This is the **strictest** rule of any namespace a name reaches, and therefore the rule for all of
20
+ * them. Cloudflare Worker script names, Workflow names, and Vectorize indexes must start with a
21
+ * letter; D1, KV, and R2 would take `1password-clone-dev-db` happily. The loose namespaces are the
22
+ * ones a project touches first, so holding everything to the strict rule is what keeps a name that
23
+ * provisions from being a name that cannot deploy.
24
+ *
25
+ * Not `/g`: `.test()` on a global regex advances `lastIndex`, and one shared instance would answer
26
+ * differently on alternate calls. `segment.test.ts` pins that.
27
+ */
28
+ export declare const NAME_SEGMENT: RegExp;
29
+ //# sourceMappingURL=segment.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"segment.d.ts","sourceRoot":"","sources":["../../src/naming/segment.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;GAaG;AAEH;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,YAAY,QAAkC,CAAC"}
@@ -0,0 +1,31 @@
1
+ //#region src/naming/segment.ts
2
+ /**
3
+ * What a single segment of a composed name may look like — one pattern, one home.
4
+ *
5
+ * `<project>-<env>-<thing>` is built out of segments, and every one of them answers to the same
6
+ * shape: a project, an environment, a capability, a job. The pattern lived in three modules,
7
+ * character for character, which is the arrangement where a rule stops being one rule — an edit to
8
+ * the environment copy would silently leave the Workflow copy behind, and the two would disagree
9
+ * about a name the other had already accepted.
10
+ *
11
+ * A capability that genuinely needs a different rule should declare its own pattern beside this one
12
+ * with a comment saying what it allows that this does not, and why. Divergence is a decision, not a
13
+ * stale copy. `scaffoldProject`'s `WORKER_NAME` is the standing example: a Worker *directory* under
14
+ * `apps/` may lead with a digit, because it is a package name rather than a Cloudflare name.
15
+ */
16
+ /**
17
+ * A lowercase letter, then letters, digits, and single inner hyphens — no leading digit, no
18
+ * doubled or trailing hyphen, no underscore, no uppercase.
19
+ *
20
+ * This is the **strictest** rule of any namespace a name reaches, and therefore the rule for all of
21
+ * them. Cloudflare Worker script names, Workflow names, and Vectorize indexes must start with a
22
+ * letter; D1, KV, and R2 would take `1password-clone-dev-db` happily. The loose namespaces are the
23
+ * ones a project touches first, so holding everything to the strict rule is what keeps a name that
24
+ * provisions from being a name that cannot deploy.
25
+ *
26
+ * Not `/g`: `.test()` on a global regex advances `lastIndex`, and one shared instance would answer
27
+ * differently on alternate calls. `segment.test.ts` pins that.
28
+ */
29
+ const NAME_SEGMENT = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;
30
+ //#endregion
31
+ export { NAME_SEGMENT };