@pithy-sh/core 0.9.0 → 0.11.0

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 (298) hide show
  1. package/dist/access/access.d.ts +172 -0
  2. package/dist/access/access.d.ts.map +1 -0
  3. package/dist/access/access.js +147 -0
  4. package/dist/access/acting.d.ts +49 -0
  5. package/dist/access/acting.d.ts.map +1 -0
  6. package/dist/access/acting.js +21 -0
  7. package/dist/access/policyCheck.d.ts +33 -0
  8. package/dist/access/policyCheck.d.ts.map +1 -0
  9. package/dist/access/policyCheck.js +48 -0
  10. package/dist/access/usage.d.ts +83 -0
  11. package/dist/access/usage.d.ts.map +1 -0
  12. package/dist/access/usage.js +104 -0
  13. package/dist/access/visibility.d.ts +66 -0
  14. package/dist/access/visibility.d.ts.map +1 -0
  15. package/dist/access/visibility.js +86 -0
  16. package/dist/asyncapi/document.d.ts +46 -0
  17. package/dist/asyncapi/document.d.ts.map +1 -0
  18. package/dist/asyncapi/document.js +115 -0
  19. package/dist/audit/auditEvent.d.ts +5 -0
  20. package/dist/audit/auditEvent.d.ts.map +1 -1
  21. package/dist/audit/auditEvent.js +2 -0
  22. package/dist/capability/bindings.d.ts +47 -2
  23. package/dist/capability/bindings.d.ts.map +1 -1
  24. package/dist/capability/bindings.js +117 -7
  25. package/dist/capability/capability.d.ts +105 -28
  26. package/dist/capability/capability.d.ts.map +1 -1
  27. package/dist/capability/composition.d.ts.map +1 -1
  28. package/dist/capability/composition.js +5 -1
  29. package/dist/capability/manifest.d.ts +2 -1
  30. package/dist/capability/manifest.d.ts.map +1 -1
  31. package/dist/capability/manifest.js +1 -1
  32. package/dist/capability/settings.d.ts +96 -4
  33. package/dist/capability/settings.d.ts.map +1 -1
  34. package/dist/capability/settings.js +85 -4
  35. package/dist/cli/command.d.ts +364 -0
  36. package/dist/cli/command.d.ts.map +1 -0
  37. package/dist/cli/command.js +418 -0
  38. package/dist/cli/commandCatalog.generated.d.ts +2437 -0
  39. package/dist/cli/commandCatalog.generated.d.ts.map +1 -0
  40. package/dist/cli/commandCatalog.generated.js +2564 -0
  41. package/dist/cli/commandFacts.d.ts +164 -0
  42. package/dist/cli/commandFacts.d.ts.map +1 -0
  43. package/dist/cli/commandFacts.js +216 -0
  44. package/dist/controlPlane/capability.d.ts.map +1 -1
  45. package/dist/controlPlane/capability.js +12 -2
  46. package/dist/controlPlane/context.d.ts +5 -0
  47. package/dist/controlPlane/context.d.ts.map +1 -1
  48. package/dist/controlPlane/context.js +3 -1
  49. package/dist/controlPlane/data/connection.d.ts +47 -0
  50. package/dist/controlPlane/data/connection.d.ts.map +1 -1
  51. package/dist/controlPlane/data/connection.js +55 -1
  52. package/dist/controlPlane/error/errors.d.ts.map +1 -1
  53. package/dist/controlPlane/error/errors.js +4 -3
  54. package/dist/controlPlane/http/cors.d.ts +4 -4
  55. package/dist/controlPlane/http/cors.js +4 -4
  56. package/dist/controlPlane/http/guard.d.ts +11 -0
  57. package/dist/controlPlane/http/guard.d.ts.map +1 -1
  58. package/dist/controlPlane/http/guard.js +28 -2
  59. package/dist/controlPlane/http/handlers.d.ts +60 -0
  60. package/dist/controlPlane/http/handlers.d.ts.map +1 -1
  61. package/dist/controlPlane/http/handlers.js +31 -2
  62. package/dist/controlPlane/http/readiness.d.ts +33 -0
  63. package/dist/controlPlane/http/readiness.d.ts.map +1 -0
  64. package/dist/controlPlane/http/readiness.js +39 -0
  65. package/dist/controlPlane/http/responses.d.ts +147 -0
  66. package/dist/controlPlane/http/responses.d.ts.map +1 -1
  67. package/dist/controlPlane/http/responses.js +61 -4
  68. package/dist/controlPlane/http/routes.d.ts +2 -1
  69. package/dist/controlPlane/http/routes.d.ts.map +1 -1
  70. package/dist/controlPlane/http/routes.js +50 -9
  71. package/dist/controlPlane/http/verify.d.ts.map +1 -1
  72. package/dist/controlPlane/http/verify.js +2 -1
  73. package/dist/controlPlane/migrationState/migrationState.d.ts +102 -0
  74. package/dist/controlPlane/migrationState/migrationState.d.ts.map +1 -0
  75. package/dist/controlPlane/migrationState/migrationState.js +142 -0
  76. package/dist/controlPlane/migrations/0003_connection_principal.d.ts +21 -0
  77. package/dist/controlPlane/migrations/0003_connection_principal.d.ts.map +1 -0
  78. package/dist/controlPlane/migrations/0003_connection_principal.js +33 -0
  79. package/dist/controlPlane/scope/scope.d.ts +13 -2
  80. package/dist/controlPlane/scope/scope.d.ts.map +1 -1
  81. package/dist/controlPlane/scope/scope.js +14 -3
  82. package/dist/createBackend.d.ts +7 -0
  83. package/dist/createBackend.d.ts.map +1 -1
  84. package/dist/createBackend.js +41 -6
  85. package/dist/createEntrypoint.d.ts.map +1 -1
  86. package/dist/createEntrypoint.js +5 -3
  87. package/dist/entitlement/allowance.d.ts +25 -0
  88. package/dist/entitlement/allowance.d.ts.map +1 -0
  89. package/dist/entitlement/allowance.js +80 -0
  90. package/dist/entitlement/entitlement.d.ts +41 -0
  91. package/dist/entitlement/entitlement.d.ts.map +1 -1
  92. package/dist/entitlement/meter.d.ts +57 -0
  93. package/dist/entitlement/meter.d.ts.map +1 -0
  94. package/dist/entitlement/meter.js +13 -0
  95. package/dist/entitlement/require.js +1 -1
  96. package/dist/error/cause.d.ts +16 -116
  97. package/dist/error/cause.d.ts.map +1 -1
  98. package/dist/error/cause.js +19 -153
  99. package/dist/error/http.d.ts +184 -0
  100. package/dist/error/http.d.ts.map +1 -1
  101. package/dist/error/payload.d.ts +368 -0
  102. package/dist/error/payload.d.ts.map +1 -1
  103. package/dist/error/payload.js +126 -4
  104. package/dist/error/pithyError.d.ts +44 -0
  105. package/dist/error/pithyError.d.ts.map +1 -1
  106. package/dist/error/pithyError.js +56 -1
  107. package/dist/error/unresolved.d.ts +265 -0
  108. package/dist/error/unresolved.d.ts.map +1 -0
  109. package/dist/error/unresolved.js +599 -0
  110. package/dist/error/wire.d.ts +20 -0
  111. package/dist/error/wire.d.ts.map +1 -0
  112. package/dist/error/wire.js +19 -0
  113. package/dist/http/authContext.d.ts +7 -0
  114. package/dist/http/authContext.d.ts.map +1 -1
  115. package/dist/http/authContext.js +8 -1
  116. package/dist/http/oidcWebhook.d.ts +3 -3
  117. package/dist/http/oidcWebhook.js +3 -3
  118. package/dist/http/routeContract.js +1 -1
  119. package/dist/http/routeSpec.d.ts +66 -0
  120. package/dist/http/routeSpec.d.ts.map +1 -0
  121. package/dist/http/routeSpec.js +40 -0
  122. package/dist/http/sameOrigin.d.ts +1 -1
  123. package/dist/http/sameOrigin.js +1 -1
  124. package/dist/http/scopeGates.d.ts +20 -0
  125. package/dist/http/scopeGates.d.ts.map +1 -0
  126. package/dist/http/scopeGates.js +36 -0
  127. package/dist/http/signedWebhook.d.ts +3 -3
  128. package/dist/http/signedWebhook.js +3 -3
  129. package/dist/http/socket.d.ts +52 -0
  130. package/dist/http/socket.d.ts.map +1 -0
  131. package/dist/http/socket.js +2 -0
  132. package/dist/http/subprotocol.d.ts +29 -0
  133. package/dist/http/subprotocol.d.ts.map +1 -0
  134. package/dist/http/subprotocol.js +37 -0
  135. package/dist/http/validation.d.ts +40 -8
  136. package/dist/http/validation.d.ts.map +1 -1
  137. package/dist/http/validation.js +25 -1
  138. package/dist/index.d.ts +1 -1
  139. package/dist/index.d.ts.map +1 -1
  140. package/dist/index.js +4 -4
  141. package/dist/logger/record.d.ts +107 -0
  142. package/dist/logger/record.d.ts.map +1 -1
  143. package/dist/migrations/bookkeeping.d.ts +21 -0
  144. package/dist/migrations/bookkeeping.d.ts.map +1 -1
  145. package/dist/migrations/bookkeeping.js +32 -3
  146. package/dist/migrations/capabilitySets.d.ts +23 -0
  147. package/dist/migrations/capabilitySets.d.ts.map +1 -0
  148. package/dist/migrations/capabilitySets.js +74 -0
  149. package/dist/migrations/groups.d.ts +140 -0
  150. package/dist/migrations/groups.d.ts.map +1 -0
  151. package/dist/migrations/groups.js +191 -0
  152. package/dist/migrations/migrateStep.d.ts +13 -0
  153. package/dist/migrations/migrateStep.d.ts.map +1 -0
  154. package/dist/migrations/migrateStep.js +20 -0
  155. package/dist/migrations/owner.d.ts +7 -0
  156. package/dist/migrations/owner.d.ts.map +1 -1
  157. package/dist/migrations/owner.js +11 -7
  158. package/dist/migrations/registry.d.ts +26 -0
  159. package/dist/migrations/registry.d.ts.map +1 -1
  160. package/dist/migrations/registry.js +38 -7
  161. package/dist/migrations/retained.js +2 -2
  162. package/dist/migrations/runner.d.ts +76 -5
  163. package/dist/migrations/runner.d.ts.map +1 -1
  164. package/dist/migrations/runner.js +130 -19
  165. package/dist/migrations/state.d.ts +60 -0
  166. package/dist/migrations/state.d.ts.map +1 -0
  167. package/dist/migrations/state.js +46 -0
  168. package/dist/naming/domains.d.ts +17 -0
  169. package/dist/naming/domains.d.ts.map +1 -1
  170. package/dist/naming/domains.js +62 -4
  171. package/dist/naming/environment.d.ts.map +1 -1
  172. package/dist/naming/environment.js +2 -1
  173. package/dist/naming/feature.d.ts +27 -5
  174. package/dist/naming/feature.d.ts.map +1 -1
  175. package/dist/naming/feature.js +36 -6
  176. package/dist/naming/limits.d.ts +47 -3
  177. package/dist/naming/limits.d.ts.map +1 -1
  178. package/dist/naming/limits.js +47 -3
  179. package/dist/naming/packageName.d.ts +17 -0
  180. package/dist/naming/packageName.d.ts.map +1 -0
  181. package/dist/naming/packageName.js +22 -0
  182. package/dist/naming/provisionScope.d.ts +6 -0
  183. package/dist/naming/provisionScope.d.ts.map +1 -1
  184. package/dist/naming/provisionScope.js +10 -4
  185. package/dist/naming/resourceNames.d.ts +21 -2
  186. package/dist/naming/resourceNames.d.ts.map +1 -1
  187. package/dist/naming/resourceNames.js +21 -3
  188. package/dist/openapi/document.d.ts +106 -0
  189. package/dist/openapi/document.d.ts.map +1 -0
  190. package/dist/openapi/document.js +314 -0
  191. package/dist/openapi/routeGroups.d.ts +8 -0
  192. package/dist/openapi/routeGroups.d.ts.map +1 -0
  193. package/dist/openapi/routeGroups.js +23 -0
  194. package/dist/seed/writeD1.d.ts +7 -1
  195. package/dist/seed/writeD1.d.ts.map +1 -1
  196. package/dist/seed/writeD1.js +9 -3
  197. package/dist/seed/writeKv.d.ts +2 -0
  198. package/dist/seed/writeKv.d.ts.map +1 -1
  199. package/dist/seed/writeKv.js +6 -2
  200. package/dist/text/comments.js +66 -4
  201. package/dist/text/literals.d.ts +43 -0
  202. package/dist/text/literals.d.ts.map +1 -0
  203. package/dist/text/literals.js +293 -0
  204. package/dist/version.generated.d.ts +1 -1
  205. package/dist/version.generated.d.ts.map +1 -1
  206. package/dist/version.generated.js +1 -1
  207. package/dist/worker/serviceLoopback.d.ts +36 -0
  208. package/dist/worker/serviceLoopback.d.ts.map +1 -0
  209. package/dist/worker/serviceLoopback.js +74 -0
  210. package/dist/workflow/dispatchRoute.d.ts.map +1 -1
  211. package/dist/workflow/dispatchRoute.js +12 -6
  212. package/dist/workflow/faults.d.ts +12 -1
  213. package/dist/workflow/faults.d.ts.map +1 -1
  214. package/dist/workflow/faults.js +24 -18
  215. package/dist/workflow/hostEnv.d.ts +1 -0
  216. package/dist/workflow/hostEnv.d.ts.map +1 -1
  217. package/dist/workflow/hostEnv.js +4 -2
  218. package/dist/workflow/loopback.d.ts.map +1 -1
  219. package/dist/workflow/loopback.js +2 -1
  220. package/package.json +4 -4
  221. package/src/access/access.ts +298 -0
  222. package/src/access/acting.ts +59 -0
  223. package/src/access/policyCheck.ts +77 -0
  224. package/src/access/usage.ts +141 -0
  225. package/src/access/visibility.ts +109 -0
  226. package/src/asyncapi/document.ts +164 -0
  227. package/src/audit/auditEvent.ts +4 -0
  228. package/src/capability/bindings.ts +159 -13
  229. package/src/capability/capability.ts +106 -29
  230. package/src/capability/composition.ts +2 -1
  231. package/src/capability/settings.ts +165 -4
  232. package/src/cli/command.ts +865 -0
  233. package/src/cli/commandCatalog.generated.ts +2723 -0
  234. package/src/cli/commandFacts.ts +220 -0
  235. package/src/controlPlane/capability.ts +25 -1
  236. package/src/controlPlane/context.ts +4 -0
  237. package/src/controlPlane/data/connection.ts +90 -0
  238. package/src/controlPlane/error/errors.ts +10 -3
  239. package/src/controlPlane/http/cors.ts +4 -4
  240. package/src/controlPlane/http/guard.ts +35 -2
  241. package/src/controlPlane/http/handlers.ts +41 -0
  242. package/src/controlPlane/http/readiness.ts +50 -0
  243. package/src/controlPlane/http/responses.ts +140 -2
  244. package/src/controlPlane/http/routes.ts +82 -10
  245. package/src/controlPlane/http/verify.ts +2 -0
  246. package/src/controlPlane/migrationState/migrationState.ts +200 -0
  247. package/src/controlPlane/migrations/0003_connection_principal.ts +37 -0
  248. package/src/controlPlane/scope/scope.ts +14 -2
  249. package/src/createBackend.ts +59 -8
  250. package/src/createEntrypoint.ts +9 -3
  251. package/src/entitlement/allowance.ts +85 -0
  252. package/src/entitlement/entitlement.ts +38 -0
  253. package/src/entitlement/meter.ts +69 -0
  254. package/src/entitlement/require.ts +1 -1
  255. package/src/error/cause.ts +18 -181
  256. package/src/error/payload.ts +270 -4
  257. package/src/error/pithyError.ts +76 -0
  258. package/src/error/unresolved.ts +853 -0
  259. package/src/error/wire.ts +40 -0
  260. package/src/http/authContext.ts +13 -1
  261. package/src/http/oidcWebhook.ts +3 -3
  262. package/src/http/routeContract.ts +1 -1
  263. package/src/http/routeSpec.ts +81 -0
  264. package/src/http/sameOrigin.ts +1 -1
  265. package/src/http/scopeGates.ts +34 -0
  266. package/src/http/signedWebhook.ts +3 -3
  267. package/src/http/socket.ts +55 -0
  268. package/src/http/subprotocol.ts +47 -0
  269. package/src/http/validation.ts +50 -8
  270. package/src/index.ts +1 -0
  271. package/src/migrations/bookkeeping.ts +39 -2
  272. package/src/migrations/capabilitySets.ts +87 -0
  273. package/src/migrations/groups.ts +295 -0
  274. package/src/migrations/migrateStep.ts +19 -0
  275. package/src/migrations/owner.ts +10 -6
  276. package/src/migrations/registry.ts +46 -2
  277. package/src/migrations/retained.ts +2 -2
  278. package/src/migrations/runner.ts +214 -22
  279. package/src/migrations/state.ts +94 -0
  280. package/src/naming/domains.ts +75 -16
  281. package/src/naming/environment.ts +2 -1
  282. package/src/naming/feature.ts +46 -7
  283. package/src/naming/limits.ts +35 -3
  284. package/src/naming/packageName.ts +20 -0
  285. package/src/naming/provisionScope.ts +21 -2
  286. package/src/naming/resourceNames.ts +52 -5
  287. package/src/openapi/document.ts +412 -0
  288. package/src/openapi/routeGroups.ts +25 -0
  289. package/src/seed/writeD1.ts +13 -4
  290. package/src/seed/writeKv.ts +6 -2
  291. package/src/text/comments.ts +56 -4
  292. package/src/text/literals.ts +294 -0
  293. package/src/version.generated.ts +1 -1
  294. package/src/worker/serviceLoopback.ts +105 -0
  295. package/src/workflow/dispatchRoute.ts +13 -6
  296. package/src/workflow/faults.ts +56 -27
  297. package/src/workflow/hostEnv.ts +9 -1
  298. package/src/workflow/loopback.ts +2 -1
@@ -107,6 +107,35 @@ export const NAMESPACE_LIMITS = {
107
107
  * no longer hashes one at 63.
108
108
  */
109
109
  kv: { label: "a KV namespace title", maxLength: 512, minLength: 1, policy: "truncate", source: "cloudflare" },
110
+ /**
111
+ * A Queue name: **1 to 63 characters**, lowercase letters, digits and dashes, starting and ending with a letter
112
+ * or number ([Queues · Get started](https://developers.cloudflare.com/queues/get-started/), verified
113
+ * 2026-10-18). Truncated rather than refused: Pithy recomputes a queue's name from its binding on every run,
114
+ * as it does a bucket's, so a deterministic hash is safe (#662). The same 63 as R2, so a feature's queue fits
115
+ * the shape {@link FEATURE_DERIVED_PROJECT_NAME} is derived against and moves no budget.
116
+ */
117
+ queue: { label: "a Queue name", maxLength: 63, minLength: 1, policy: "truncate", source: "cloudflare" },
118
+ /**
119
+ * An Artifacts namespace: **2 to 63 characters**, starting with a letter or digit, the rest letters, digits, `.`,
120
+ * `_` and `-` ([Artifacts · Limits](https://developers.cloudflare.com/artifacts/platform/limits/), verified
121
+ * 2026-10-18). Truncated rather than refused: the kit derives it from the project and the environment on every
122
+ * run, like an R2 bucket name, and the longest project and environment compose 34 characters, so it never is (#673).
123
+ */
124
+ artifactsNamespace: {
125
+ label: "an Artifacts namespace name",
126
+ maxLength: 63,
127
+ minLength: 2,
128
+ policy: "truncate",
129
+ source: "cloudflare",
130
+ },
131
+ /**
132
+ * An Artifacts repo name. **The charset is Cloudflare's** — a letter or digit, then letters, digits, `.`, `_` and
133
+ * `-` ([Artifacts · Limits](https://developers.cloudflare.com/artifacts/platform/limits/), verified 2026-10-18) —
134
+ * and **the length is ours**: that page states one for a namespace and none for a repo, so 2 to 63 mirrors the
135
+ * namespace rather than inventing a second number. Refused, never truncated: a repo name is either a UUID or a
136
+ * name somebody chose, and a name somebody chose is never silently shortened (#673).
137
+ */
138
+ artifactsRepo: { label: "an Artifacts repo name", maxLength: 63, minLength: 2, policy: "refuse", source: "pithy" },
110
139
  /**
111
140
  * A D1 database name: `^[a-zA-Z0-9][a-zA-Z0-9_-]*$` in the OpenAPI schema, and **no documented
112
141
  * length cap** — the limits page has no row for it. So the ceiling here is {@link MAX_PITHY_NAME}.
@@ -196,7 +225,10 @@ export const FEATURE_TAIL_SEPARATOR = "--";
196
225
  */
197
226
  export const MIN_VERBATIM_BINDING = 12;
198
227
 
199
- /** The longest feature resource kind — `d1`, `kv`, `r2` are all two characters. `feature.test.ts` pins it. */
228
+ /**
229
+ * The longest feature resource kind segment — `d1`, `kv`, `r2`, and a queue's `q` (#662). `feature.test.ts` pins
230
+ * every kind's `FEATURE_KIND_SEGMENT` to it, so a longer segment fails rather than shrinking every project's budget.
231
+ */
200
232
  export const MAX_FEATURE_KIND = 2;
201
233
 
202
234
  /**
@@ -217,7 +249,7 @@ export const WORKFLOW_DERIVED_PROJECT_NAME =
217
249
 
218
250
  /**
219
251
  * What the longest feature resource name leaves a project — `<project>-f<issue>-<slug>--<binding>-<kind>`,
220
- * against R2's 63 (the strictest of the three kinds a feature provisions, and the same number a
252
+ * against R2's 63 (the strictest of the kinds a feature provisions, a queue's number too, and the same number a
221
253
  * feature Worker script gets).
222
254
  *
223
255
  * ```
@@ -226,7 +258,7 @@ export const WORKFLOW_DERIVED_PROJECT_NAME =
226
258
  * - 6 the issue number, six digits — `f999999` (MAX_ISSUE_DIGITS)
227
259
  * - 12 `-` + a legible slug (MIN_LEGIBLE_SLUG)
228
260
  * - 14 `--` + a binding kept whole (FEATURE_TAIL_SEPARATOR, MIN_VERBATIM_BINDING)
229
- * - 3 `-` + the kind, `d1` | `kv` | `r2`
261
+ * - 3 `-` + the kind, `d1` | `kv` | `r2` | `q`
230
262
  * ----
231
263
  * 26
232
264
  * ```
@@ -0,0 +1,20 @@
1
+ // SPDX-FileCopyrightText: 2026 Pithy
2
+ // SPDX-License-Identifier: MIT
3
+
4
+ /**
5
+ * The package a bare specifier names: `@pithy-sh/payments` from `@pithy-sh/payments/src/capability`, and
6
+ * `stripe` from `stripe/lib/x` — **scope-aware, and the only copy in the kit (#709)**.
7
+ *
8
+ * There were five. Three took the first segment or two depending on a leading `@`; two took the first two
9
+ * segments whatever they were, which is right for every `@pithy-sh/*` name they were written against and
10
+ * wrong for the first unscoped dependency anyone passed them. A rule that small drifts precisely because
11
+ * nobody thinks it needs a home, so it has one. That it is the only copy is a gate, not a claim: `packages/cli/src/ci/packageOfCopies.test.ts` fails on any source that writes the rule out by hand, because the first migration left three behind and said it had not.
12
+ *
13
+ * Total, and deliberately unopinionated about what it is handed: a caller with a relative path, a `#`
14
+ * import or a URL gets the first segment back. Deciding whether a specifier is a package at all is the
15
+ * unresolved-import classifier's job (`error/unresolved.ts`), not this function's.
16
+ */
17
+ export function packageOf(specifier: string): string {
18
+ const parts = specifier.split("/");
19
+ return specifier.startsWith("@") ? parts.slice(0, 2).join("/") : (parts[0] ?? specifier);
20
+ }
@@ -1,12 +1,14 @@
1
1
  // SPDX-FileCopyrightText: 2026 Pithy
2
2
  // SPDX-License-Identifier: MIT
3
3
 
4
+ import { commandName } from "../cli/command";
4
5
  import { InternalError, ValidationError } from "../error/pithyError";
5
6
  import type { WorkflowHostNameParts } from "../workflow/naming";
6
7
  import { assertValidEnvironment, FEATURE_ENVIRONMENT, GLOBAL_SCOPE, isFeatureMarker } from "./environment";
7
8
  import {
8
9
  type FeatureIdentity,
9
10
  type FeatureResourceKind,
11
+ featureArtifactsNamespace,
10
12
  featureResourceName,
11
13
  featureSecretEntryName,
12
14
  featureWorkerName,
@@ -188,10 +190,21 @@ export interface ProvisionScope {
188
190
  * `<project>-feature-email-send` would be one Workflow every open branch deployed over every other's.
189
191
  */
190
192
  readonly workflowHost: Omit<WorkflowHostNameParts, "capability">;
193
+ /**
194
+ * The Artifacts namespace an `artifacts` binding names in this scope (#673): `<project>-<env>` in a declared
195
+ * environment, `<project>-f<issue>-<slug>--artifacts` in a feature. On the scope for the reason every other name
196
+ * is: the stanza a run writes and the namespace it writes into it are one decision.
197
+ */
198
+ readonly artifactsNamespace: string;
191
199
  }
192
200
 
193
201
  /** The wrangler binding array a resource kind's name is composed for. */
194
- const KIND_NAMER: Record<FeatureResourceKind, "d1" | "kv" | "r2"> = { d1: "d1", kv: "kv", r2: "r2" };
202
+ const KIND_NAMER: Record<FeatureResourceKind, "d1" | "kv" | "r2" | "queue"> = {
203
+ d1: "d1",
204
+ kv: "kv",
205
+ r2: "r2",
206
+ queue: "queue",
207
+ };
195
208
 
196
209
  /**
197
210
  * The one branch every scope shares: a `global` secret resolves to the project's single
@@ -336,7 +349,7 @@ export function environmentScope(project: string, environment: string): Provisio
336
349
  throw new ValidationError({
337
350
  message: `The ${environment} Worker "${name}" is named the way a feature's Worker is.`,
338
351
  action: `Rename it in env.${environment}.name, or the Worker's top-level name, so it does not follow ${kebab(project)}- with f and a number.`,
339
- detail: `${declared === undefined ? "Composed by wrangler from the deploy name" : `Declared in env.${environment}.name`}. A feature's Worker is <project>-f<issue>-<slug>-<app>, and \`pithy feature destroy\` deletes it by that exact name.`,
352
+ detail: `${declared === undefined ? "Composed by wrangler from the deploy name" : `Declared in env.${environment}.name`}. A feature's Worker is <project>-f<issue>-<slug>-<app>, and \`${commandName("feature destroy")}\` deletes it by that exact name.`,
340
353
  });
341
354
  }
342
355
  return name;
@@ -344,6 +357,7 @@ export function environmentScope(project: string, environment: string): Provisio
344
357
  secretEntry: (secret, secretScope) =>
345
358
  secretEntryName(project, secret, secretScope, () => names.secretEntry(secret)),
346
359
  workflowHost: { project, env: environment },
360
+ artifactsNamespace: names.artifactsNamespace(),
347
361
  };
348
362
  }
349
363
 
@@ -391,6 +405,11 @@ export function featureScope(identity: FeatureIdentity): ProvisionScope {
391
405
  secretEntry: (secret) => featureSecretEntryName(identity, secret),
392
406
  // `env` is the stanza — what the host's `ENVIRONMENT` var says — and `feature` is what its names take.
393
407
  workflowHost: { project: identity.project, env: FEATURE_ENVIRONMENT, feature: identity },
408
+ // The feature's own, read back as its own by `isFeatureOwnedName`, so teardown reaps no sibling's repos. A
409
+ // getter, so a slug too long for it refuses where the namespace is asked for, never where the scope is made.
410
+ get artifactsNamespace() {
411
+ return featureArtifactsNamespace(identity);
412
+ },
394
413
  };
395
414
  }
396
415
 
@@ -4,7 +4,13 @@
4
4
  import { InternalError, ValidationError } from "../error/pithyError";
5
5
  import { workflowHostName, workflowScriptName } from "../workflow/naming";
6
6
  import { assertValidEnvironment, GLOBAL_SCOPE } from "./environment";
7
- import { type FeatureIdentity, type FeatureResourceKind, featureResourceName, featureWorkerName } from "./feature";
7
+ import {
8
+ type FeatureIdentity,
9
+ type FeatureResourceKind,
10
+ featureArtifactsNamespace,
11
+ featureResourceName,
12
+ featureWorkerName,
13
+ } from "./feature";
8
14
  import { NAMESPACE_LIMITS, type NamespaceLimit } from "./limits";
9
15
  import { assertValidProjectName, kebab, resourceName } from "./resource";
10
16
 
@@ -57,6 +63,8 @@ export interface ScopedNames {
57
63
  kv(binding: string): string;
58
64
  /** An R2 bucket. Cloudflare's 63 — the one namespace where that number was always right. */
59
65
  r2(binding: string): string;
66
+ /** A Queue. Cloudflare's 63, truncated with a stable hash like a bucket (#662). */
67
+ queue(binding: string): string;
60
68
  /** A Vectorize index. 64 bytes, refused rather than truncated: a renamed index has no vectors. */
61
69
  vectorizeIndex(index: string): string;
62
70
  /** A Secrets Store entry. No Cloudflare cap; Pithy's ceiling. */
@@ -65,15 +73,33 @@ export interface ScopedNames {
65
73
  apiToken(profile: string): string;
66
74
  }
67
75
 
76
+ /**
77
+ * The names in one declared environment: everything a scope has, and the one name that exists per environment
78
+ * and never at `global`.
79
+ */
80
+ export interface EnvironmentNames extends ScopedNames {
81
+ /**
82
+ * The Artifacts namespace — `<project>-<env>`, with no `<thing>` (#673). One per environment, so a feature's
83
+ * repos never sit beside production's and the per-namespace rate limit splits per environment. There is no
84
+ * global one, because a namespace shared across environments is exactly that mix.
85
+ */
86
+ artifactsNamespace(): string;
87
+ }
88
+
68
89
  /** The names a feature environment provisions under, with the project already bound. */
69
90
  export interface FeatureNames {
70
- /** A feature's D1, KV, or R2 resource — `<project>-f<issue>-<slug>-<binding>-<kind>`. */
91
+ /** A feature's D1, KV, R2 or Queue resource — `<project>-f<issue>-<slug>--<binding>-<kind>`. */
71
92
  resource(binding: string, kind: FeatureResourceKind): string;
72
93
  /**
73
94
  * A feature's Worker script — `<project>-f<issue>-<slug>-<app>`, from the `apps/<app>` directory and
74
95
  * never the deploy name, which already leads with the project (#587).
75
96
  */
76
97
  worker(app: string): string;
98
+ /**
99
+ * A feature's Artifacts namespace — `<project>-f<issue>-<slug>--artifacts` (#673). The tail is what makes it a
100
+ * feature name `isFeatureOwnedName` reads back, so teardown reaps this feature's repos and no sibling's.
101
+ */
102
+ artifactsNamespace(): string;
77
103
  }
78
104
 
79
105
  /** Everything a project can be asked to name. Created once, from the project, by {@link resourceNames}. */
@@ -81,7 +107,7 @@ export interface ProjectNames {
81
107
  /** The kebabbed project name every name below leads with. */
82
108
  readonly project: string;
83
109
  /** The names in one environment. Throws if the environment is not one this scheme accepts. */
84
- env(environment: string): ScopedNames;
110
+ env(environment: string): EnvironmentNames;
85
111
  /** The names shared by every environment — the `global` scope, without typing the word. */
86
112
  readonly global: ScopedNames;
87
113
  /** The names a feature environment provisions under. */
@@ -134,6 +160,22 @@ function scopedName(project: string, scope: string, thing: string, limit: Namesp
134
160
  return name;
135
161
  }
136
162
 
163
+ /**
164
+ * `<project>-<env>`, under the Artifacts namespace rule. The project and environment are both validated by now, and
165
+ * the longest of each compose 34 characters, so the cap is a check rather than a truncation that ever happens.
166
+ */
167
+ function artifactsNamespaceName(project: string, environment: string): string {
168
+ const limit = NAMESPACE_LIMITS.artifactsNamespace;
169
+ const name = `${project}-${environment}`;
170
+ if (name.length > limit.maxLength || name.length < limit.minLength) {
171
+ throw new InternalError({
172
+ message: `"${name}" does not fit ${limit.label}, which takes ${limit.minLength} to ${limit.maxLength} characters.`,
173
+ action: "Shorten the project or environment name.",
174
+ });
175
+ }
176
+ return name;
177
+ }
178
+
137
179
  /** The getters for one scope. `global` and every environment share this, so nothing special-cases either. */
138
180
  function scopedNames(project: string, scope: string): ScopedNames {
139
181
  return {
@@ -142,6 +184,7 @@ function scopedNames(project: string, scope: string): ScopedNames {
142
184
  d1: (binding) => scopedName(project, scope, binding, NAMESPACE_LIMITS.d1),
143
185
  kv: (binding) => scopedName(project, scope, binding, NAMESPACE_LIMITS.kv),
144
186
  r2: (binding) => scopedName(project, scope, binding, NAMESPACE_LIMITS.r2),
187
+ queue: (binding) => scopedName(project, scope, binding, NAMESPACE_LIMITS.queue),
145
188
  vectorizeIndex: (index) => scopedName(project, scope, index, NAMESPACE_LIMITS.vectorizeIndex),
146
189
  secretEntry: (secret) => scopedName(project, scope, secret, NAMESPACE_LIMITS.secretEntry),
147
190
  apiToken: (profile) => scopedName(project, scope, profile, NAMESPACE_LIMITS.apiToken),
@@ -161,9 +204,12 @@ export function resourceNames(project: string): ProjectNames {
161
204
 
162
205
  return {
163
206
  project: normalized,
164
- env(environment: string): ScopedNames {
207
+ env(environment: string): EnvironmentNames {
165
208
  assertValidEnvironment(environment);
166
- return scopedNames(normalized, environment);
209
+ return {
210
+ ...scopedNames(normalized, environment),
211
+ artifactsNamespace: () => artifactsNamespaceName(normalized, environment),
212
+ };
167
213
  },
168
214
  global: scopedNames(normalized, GLOBAL_SCOPE),
169
215
  feature(identity): FeatureNames {
@@ -171,6 +217,7 @@ export function resourceNames(project: string): ProjectNames {
171
217
  return {
172
218
  resource: (binding, kind) => featureResourceName(full, binding, kind),
173
219
  worker: (app) => featureWorkerName(full, app),
220
+ artifactsNamespace: () => featureArtifactsNamespace(full),
174
221
  };
175
222
  },
176
223
  };