@pithy-sh/core 0.10.0 → 0.12.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 (270) 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/audit/auditEvent.d.ts +5 -0
  17. package/dist/audit/auditEvent.d.ts.map +1 -1
  18. package/dist/audit/auditEvent.js +2 -0
  19. package/dist/audit/recorder.d.ts +10 -2
  20. package/dist/audit/recorder.d.ts.map +1 -1
  21. package/dist/capability/bindings.d.ts +47 -2
  22. package/dist/capability/bindings.d.ts.map +1 -1
  23. package/dist/capability/bindings.js +117 -7
  24. package/dist/capability/capability.d.ts +110 -28
  25. package/dist/capability/capability.d.ts.map +1 -1
  26. package/dist/capability/capability.js +2 -0
  27. package/dist/capability/manifest.d.ts +2 -1
  28. package/dist/capability/manifest.d.ts.map +1 -1
  29. package/dist/capability/manifest.js +1 -1
  30. package/dist/capability/purge.d.ts +80 -0
  31. package/dist/capability/purge.d.ts.map +1 -0
  32. package/dist/capability/purge.js +76 -0
  33. package/dist/capability/settings.d.ts +86 -2
  34. package/dist/capability/settings.d.ts.map +1 -1
  35. package/dist/capability/settings.js +80 -1
  36. package/dist/cli/commandCatalog.generated.d.ts +238 -24
  37. package/dist/cli/commandCatalog.generated.d.ts.map +1 -1
  38. package/dist/cli/commandCatalog.generated.js +251 -22
  39. package/dist/cli/commandFacts.d.ts +2 -1
  40. package/dist/cli/commandFacts.d.ts.map +1 -1
  41. package/dist/cli/commandFacts.js +2 -0
  42. package/dist/controlPlane/capability.d.ts.map +1 -1
  43. package/dist/controlPlane/capability.js +19 -2
  44. package/dist/controlPlane/context.d.ts +5 -0
  45. package/dist/controlPlane/context.d.ts.map +1 -1
  46. package/dist/controlPlane/context.js +3 -1
  47. package/dist/controlPlane/data/connection.d.ts +49 -12
  48. package/dist/controlPlane/data/connection.d.ts.map +1 -1
  49. package/dist/controlPlane/data/connection.js +56 -2
  50. package/dist/controlPlane/discovery/adminRoute.d.ts +29 -0
  51. package/dist/controlPlane/discovery/adminRoute.d.ts.map +1 -1
  52. package/dist/controlPlane/discovery/adminRoute.js +3 -0
  53. package/dist/controlPlane/error/errors.d.ts +4 -3
  54. package/dist/controlPlane/error/errors.d.ts.map +1 -1
  55. package/dist/controlPlane/http/cors.d.ts +4 -4
  56. package/dist/controlPlane/http/cors.js +4 -4
  57. package/dist/controlPlane/http/guard.d.ts +11 -0
  58. package/dist/controlPlane/http/guard.d.ts.map +1 -1
  59. package/dist/controlPlane/http/guard.js +17 -1
  60. package/dist/controlPlane/http/handlers.d.ts +139 -0
  61. package/dist/controlPlane/http/handlers.d.ts.map +1 -1
  62. package/dist/controlPlane/http/handlers.js +67 -4
  63. package/dist/controlPlane/http/readiness.d.ts +33 -0
  64. package/dist/controlPlane/http/readiness.d.ts.map +1 -0
  65. package/dist/controlPlane/http/readiness.js +39 -0
  66. package/dist/controlPlane/http/responses.d.ts +245 -0
  67. package/dist/controlPlane/http/responses.d.ts.map +1 -1
  68. package/dist/controlPlane/http/responses.js +101 -4
  69. package/dist/controlPlane/http/routes.d.ts +2 -0
  70. package/dist/controlPlane/http/routes.d.ts.map +1 -1
  71. package/dist/controlPlane/http/routes.js +31 -3
  72. package/dist/controlPlane/http/verify.d.ts.map +1 -1
  73. package/dist/controlPlane/http/verify.js +2 -1
  74. package/dist/controlPlane/migrationState/migrationState.d.ts +102 -0
  75. package/dist/controlPlane/migrationState/migrationState.d.ts.map +1 -0
  76. package/dist/controlPlane/migrationState/migrationState.js +142 -0
  77. package/dist/controlPlane/migrations/0003_connection_principal.d.ts +21 -0
  78. package/dist/controlPlane/migrations/0003_connection_principal.d.ts.map +1 -0
  79. package/dist/controlPlane/migrations/0003_connection_principal.js +33 -0
  80. package/dist/controlPlane/scope/scope.d.ts +23 -2
  81. package/dist/controlPlane/scope/scope.d.ts.map +1 -1
  82. package/dist/controlPlane/scope/scope.js +24 -3
  83. package/dist/createBackend.d.ts.map +1 -1
  84. package/dist/createBackend.js +9 -2
  85. package/dist/createEntrypoint.d.ts.map +1 -1
  86. package/dist/createEntrypoint.js +5 -3
  87. package/dist/crypto/envelope.d.ts +86 -0
  88. package/dist/crypto/envelope.d.ts.map +1 -0
  89. package/dist/crypto/envelope.js +129 -0
  90. package/dist/crypto/fieldKeys.d.ts +41 -0
  91. package/dist/crypto/fieldKeys.d.ts.map +1 -0
  92. package/dist/crypto/fieldKeys.js +56 -0
  93. package/dist/data/codecs.d.ts +23 -10
  94. package/dist/data/codecs.d.ts.map +1 -1
  95. package/dist/data/codecs.js +50 -36
  96. package/dist/entitlement/allowance.d.ts +25 -0
  97. package/dist/entitlement/allowance.d.ts.map +1 -0
  98. package/dist/entitlement/allowance.js +80 -0
  99. package/dist/entitlement/entitlement.d.ts +41 -0
  100. package/dist/entitlement/entitlement.d.ts.map +1 -1
  101. package/dist/entitlement/meter.d.ts +57 -0
  102. package/dist/entitlement/meter.d.ts.map +1 -0
  103. package/dist/entitlement/meter.js +13 -0
  104. package/dist/error/extend.d.ts +6 -5
  105. package/dist/error/extend.d.ts.map +1 -1
  106. package/dist/error/http.d.ts +168 -0
  107. package/dist/error/http.d.ts.map +1 -1
  108. package/dist/error/payload.d.ts +336 -0
  109. package/dist/error/payload.d.ts.map +1 -1
  110. package/dist/error/payload.js +116 -4
  111. package/dist/error/pithyError.d.ts +18 -7
  112. package/dist/error/pithyError.d.ts.map +1 -1
  113. package/dist/error/pithyError.js +6 -0
  114. package/dist/http/authContext.d.ts +7 -0
  115. package/dist/http/authContext.d.ts.map +1 -1
  116. package/dist/http/authContext.js +8 -1
  117. package/dist/http/scopeGates.d.ts +20 -0
  118. package/dist/http/scopeGates.d.ts.map +1 -0
  119. package/dist/http/scopeGates.js +36 -0
  120. package/dist/index.d.ts +1 -1
  121. package/dist/index.d.ts.map +1 -1
  122. package/dist/index.js +2 -2
  123. package/dist/logger/logger.d.ts +12 -5
  124. package/dist/logger/logger.d.ts.map +1 -1
  125. package/dist/logger/record.d.ts +98 -0
  126. package/dist/logger/record.d.ts.map +1 -1
  127. package/dist/migrations/capabilitySets.d.ts +23 -0
  128. package/dist/migrations/capabilitySets.d.ts.map +1 -0
  129. package/dist/migrations/capabilitySets.js +74 -0
  130. package/dist/migrations/registry.d.ts +26 -0
  131. package/dist/migrations/registry.d.ts.map +1 -1
  132. package/dist/migrations/registry.js +38 -7
  133. package/dist/migrations/state.d.ts +60 -0
  134. package/dist/migrations/state.d.ts.map +1 -0
  135. package/dist/migrations/state.js +46 -0
  136. package/dist/naming/feature.d.ts +27 -5
  137. package/dist/naming/feature.d.ts.map +1 -1
  138. package/dist/naming/feature.js +36 -6
  139. package/dist/naming/limits.d.ts +47 -3
  140. package/dist/naming/limits.d.ts.map +1 -1
  141. package/dist/naming/limits.js +47 -3
  142. package/dist/naming/provisionScope.d.ts +6 -0
  143. package/dist/naming/provisionScope.d.ts.map +1 -1
  144. package/dist/naming/provisionScope.js +8 -3
  145. package/dist/naming/resourceNames.d.ts +21 -2
  146. package/dist/naming/resourceNames.d.ts.map +1 -1
  147. package/dist/naming/resourceNames.js +21 -3
  148. package/dist/privacy/blindIndex.d.ts +27 -0
  149. package/dist/privacy/blindIndex.d.ts.map +1 -0
  150. package/dist/privacy/blindIndex.js +57 -0
  151. package/dist/privacy/classes.d.ts +142 -0
  152. package/dist/privacy/classes.d.ts.map +1 -0
  153. package/dist/privacy/classes.js +209 -0
  154. package/dist/privacy/classify.d.ts +336 -0
  155. package/dist/privacy/classify.d.ts.map +1 -0
  156. package/dist/privacy/classify.js +1024 -0
  157. package/dist/privacy/encrypted.d.ts +210 -0
  158. package/dist/privacy/encrypted.d.ts.map +1 -0
  159. package/dist/privacy/encrypted.js +350 -0
  160. package/dist/privacy/encryptedColumns.d.ts +64 -0
  161. package/dist/privacy/encryptedColumns.d.ts.map +1 -0
  162. package/dist/privacy/encryptedColumns.js +134 -0
  163. package/dist/privacy/fieldRotation.d.ts +34 -0
  164. package/dist/privacy/fieldRotation.d.ts.map +1 -0
  165. package/dist/privacy/fieldRotation.js +186 -0
  166. package/dist/privacy/inventory.d.ts +12 -0
  167. package/dist/privacy/inventory.d.ts.map +1 -0
  168. package/dist/privacy/inventory.js +154 -0
  169. package/dist/privacy/posture.d.ts +114 -0
  170. package/dist/privacy/posture.d.ts.map +1 -0
  171. package/dist/privacy/posture.js +136 -0
  172. package/dist/privacy/redact.d.ts +7 -0
  173. package/dist/privacy/redact.d.ts.map +1 -0
  174. package/dist/privacy/redact.js +404 -0
  175. package/dist/privacy/report.d.ts +58 -0
  176. package/dist/privacy/report.d.ts.map +1 -0
  177. package/dist/privacy/report.js +15 -0
  178. package/dist/privacy/retirement.d.ts +68 -0
  179. package/dist/privacy/retirement.d.ts.map +1 -0
  180. package/dist/privacy/retirement.js +72 -0
  181. package/dist/privacy/sealed.d.ts +87 -0
  182. package/dist/privacy/sealed.d.ts.map +1 -0
  183. package/dist/privacy/sealed.js +221 -0
  184. package/dist/privacy/types.d.ts +559 -0
  185. package/dist/privacy/types.d.ts.map +1 -0
  186. package/dist/privacy/types.js +2 -0
  187. package/dist/seed/writeD1.d.ts +7 -1
  188. package/dist/seed/writeD1.d.ts.map +1 -1
  189. package/dist/seed/writeD1.js +9 -3
  190. package/dist/seed/writeKv.d.ts +2 -0
  191. package/dist/seed/writeKv.d.ts.map +1 -1
  192. package/dist/seed/writeKv.js +6 -2
  193. package/dist/version.generated.d.ts +1 -1
  194. package/dist/version.generated.js +1 -1
  195. package/dist/worker/serviceLoopback.d.ts +36 -0
  196. package/dist/worker/serviceLoopback.d.ts.map +1 -0
  197. package/dist/worker/serviceLoopback.js +74 -0
  198. package/dist/workflow/faults.d.ts +12 -1
  199. package/dist/workflow/faults.d.ts.map +1 -1
  200. package/dist/workflow/faults.js +22 -15
  201. package/package.json +2 -2
  202. package/src/access/access.ts +298 -0
  203. package/src/access/acting.ts +59 -0
  204. package/src/access/policyCheck.ts +77 -0
  205. package/src/access/usage.ts +141 -0
  206. package/src/access/visibility.ts +109 -0
  207. package/src/audit/auditEvent.ts +4 -0
  208. package/src/audit/recorder.ts +10 -2
  209. package/src/capability/bindings.ts +159 -13
  210. package/src/capability/capability.ts +114 -29
  211. package/src/capability/purge.ts +124 -0
  212. package/src/capability/settings.ts +146 -2
  213. package/src/cli/commandCatalog.generated.ts +271 -22
  214. package/src/cli/commandFacts.ts +2 -0
  215. package/src/controlPlane/capability.ts +36 -1
  216. package/src/controlPlane/context.ts +4 -0
  217. package/src/controlPlane/data/connection.ts +90 -0
  218. package/src/controlPlane/discovery/adminRoute.ts +11 -0
  219. package/src/controlPlane/error/errors.ts +4 -3
  220. package/src/controlPlane/http/cors.ts +4 -4
  221. package/src/controlPlane/http/guard.ts +21 -0
  222. package/src/controlPlane/http/handlers.ts +98 -4
  223. package/src/controlPlane/http/readiness.ts +50 -0
  224. package/src/controlPlane/http/responses.ts +240 -2
  225. package/src/controlPlane/http/routes.ts +55 -1
  226. package/src/controlPlane/http/verify.ts +2 -0
  227. package/src/controlPlane/migrationState/migrationState.ts +200 -0
  228. package/src/controlPlane/migrations/0003_connection_principal.ts +37 -0
  229. package/src/controlPlane/scope/scope.ts +25 -2
  230. package/src/controlPlane/token/base64url.ts +1 -1
  231. package/src/createBackend.ts +28 -6
  232. package/src/createEntrypoint.ts +9 -3
  233. package/src/crypto/envelope.ts +196 -0
  234. package/src/crypto/fieldKeys.ts +76 -0
  235. package/src/data/codecs.ts +69 -37
  236. package/src/entitlement/allowance.ts +85 -0
  237. package/src/entitlement/entitlement.ts +38 -0
  238. package/src/entitlement/meter.ts +69 -0
  239. package/src/error/extend.ts +6 -5
  240. package/src/error/payload.ts +253 -4
  241. package/src/error/pithyError.ts +21 -7
  242. package/src/http/authContext.ts +13 -1
  243. package/src/http/scopeGates.ts +34 -0
  244. package/src/index.ts +2 -0
  245. package/src/logger/logger.ts +12 -5
  246. package/src/migrations/capabilitySets.ts +87 -0
  247. package/src/migrations/registry.ts +46 -2
  248. package/src/migrations/state.ts +94 -0
  249. package/src/naming/feature.ts +46 -7
  250. package/src/naming/limits.ts +35 -3
  251. package/src/naming/provisionScope.ts +19 -1
  252. package/src/naming/resourceNames.ts +52 -5
  253. package/src/privacy/blindIndex.ts +81 -0
  254. package/src/privacy/classes.ts +277 -0
  255. package/src/privacy/classify.ts +1447 -0
  256. package/src/privacy/encrypted.ts +513 -0
  257. package/src/privacy/encryptedColumns.ts +185 -0
  258. package/src/privacy/fieldRotation.ts +234 -0
  259. package/src/privacy/inventory.ts +147 -0
  260. package/src/privacy/posture.ts +205 -0
  261. package/src/privacy/redact.ts +423 -0
  262. package/src/privacy/report.ts +64 -0
  263. package/src/privacy/retirement.ts +102 -0
  264. package/src/privacy/sealed.ts +323 -0
  265. package/src/privacy/types.ts +804 -0
  266. package/src/seed/writeD1.ts +13 -4
  267. package/src/seed/writeKv.ts +6 -2
  268. package/src/version.generated.ts +1 -1
  269. package/src/worker/serviceLoopback.ts +105 -0
  270. package/src/workflow/faults.ts +51 -24
@@ -123,6 +123,47 @@ export declare const NAMESPACE_LIMITS: {
123
123
  readonly policy: "truncate";
124
124
  readonly source: "cloudflare";
125
125
  };
126
+ /**
127
+ * A Queue name: **1 to 63 characters**, lowercase letters, digits and dashes, starting and ending with a letter
128
+ * or number ([Queues · Get started](https://developers.cloudflare.com/queues/get-started/), verified
129
+ * 2026-10-18). Truncated rather than refused: Pithy recomputes a queue's name from its binding on every run,
130
+ * as it does a bucket's, so a deterministic hash is safe (#662). The same 63 as R2, so a feature's queue fits
131
+ * the shape {@link FEATURE_DERIVED_PROJECT_NAME} is derived against and moves no budget.
132
+ */
133
+ readonly queue: {
134
+ readonly label: "a Queue name";
135
+ readonly maxLength: 63;
136
+ readonly minLength: 1;
137
+ readonly policy: "truncate";
138
+ readonly source: "cloudflare";
139
+ };
140
+ /**
141
+ * An Artifacts namespace: **2 to 63 characters**, starting with a letter or digit, the rest letters, digits, `.`,
142
+ * `_` and `-` ([Artifacts · Limits](https://developers.cloudflare.com/artifacts/platform/limits/), verified
143
+ * 2026-10-18). Truncated rather than refused: the kit derives it from the project and the environment on every
144
+ * run, like an R2 bucket name, and the longest project and environment compose 34 characters, so it never is (#673).
145
+ */
146
+ readonly artifactsNamespace: {
147
+ readonly label: "an Artifacts namespace name";
148
+ readonly maxLength: 63;
149
+ readonly minLength: 2;
150
+ readonly policy: "truncate";
151
+ readonly source: "cloudflare";
152
+ };
153
+ /**
154
+ * An Artifacts repo name. **The charset is Cloudflare's** — a letter or digit, then letters, digits, `.`, `_` and
155
+ * `-` ([Artifacts · Limits](https://developers.cloudflare.com/artifacts/platform/limits/), verified 2026-10-18) —
156
+ * and **the length is ours**: that page states one for a namespace and none for a repo, so 2 to 63 mirrors the
157
+ * namespace rather than inventing a second number. Refused, never truncated: a repo name is either a UUID or a
158
+ * name somebody chose, and a name somebody chose is never silently shortened (#673).
159
+ */
160
+ readonly artifactsRepo: {
161
+ readonly label: "an Artifacts repo name";
162
+ readonly maxLength: 63;
163
+ readonly minLength: 2;
164
+ readonly policy: "refuse";
165
+ readonly source: "pithy";
166
+ };
126
167
  /**
127
168
  * A D1 database name: `^[a-zA-Z0-9][a-zA-Z0-9_-]*$` in the OpenAPI schema, and **no documented
128
169
  * length cap** — the limits page has no row for it. So the ceiling here is {@link MAX_PITHY_NAME}.
@@ -211,7 +252,10 @@ export declare const FEATURE_TAIL_SEPARATOR = "--";
211
252
  * and teardown recomputes the name rather than matching a prefix.
212
253
  */
213
254
  export declare const MIN_VERBATIM_BINDING = 12;
214
- /** The longest feature resource kind — `d1`, `kv`, `r2` are all two characters. `feature.test.ts` pins it. */
255
+ /**
256
+ * The longest feature resource kind segment — `d1`, `kv`, `r2`, and a queue's `q` (#662). `feature.test.ts` pins
257
+ * every kind's `FEATURE_KIND_SEGMENT` to it, so a longer segment fails rather than shrinking every project's budget.
258
+ */
215
259
  export declare const MAX_FEATURE_KIND = 2;
216
260
  /**
217
261
  * What the longest Workflow name leaves a project.
@@ -229,7 +273,7 @@ export declare const MAX_FEATURE_KIND = 2;
229
273
  export declare const WORKFLOW_DERIVED_PROJECT_NAME: number;
230
274
  /**
231
275
  * What the longest feature resource name leaves a project — `<project>-f<issue>-<slug>--<binding>-<kind>`,
232
- * against R2's 63 (the strictest of the three kinds a feature provisions, and the same number a
276
+ * against R2's 63 (the strictest of the kinds a feature provisions, a queue's number too, and the same number a
233
277
  * feature Worker script gets).
234
278
  *
235
279
  * ```
@@ -238,7 +282,7 @@ export declare const WORKFLOW_DERIVED_PROJECT_NAME: number;
238
282
  * - 6 the issue number, six digits — `f999999` (MAX_ISSUE_DIGITS)
239
283
  * - 12 `-` + a legible slug (MIN_LEGIBLE_SLUG)
240
284
  * - 14 `--` + a binding kept whole (FEATURE_TAIL_SEPARATOR, MIN_VERBATIM_BINDING)
241
- * - 3 `-` + the kind, `d1` | `kv` | `r2`
285
+ * - 3 `-` + the kind, `d1` | `kv` | `r2` | `q`
242
286
  * ----
243
287
  * 26
244
288
  * ```
@@ -1 +1 @@
1
- {"version":3,"file":"limits.d.ts","sourceRoot":"","sources":["../../src/naming/limits.ts"],"names":[],"mappings":"AAKA;;;;;;;;;;;;;;GAcG;AAEH,sFAAsF;AACtF,MAAM,MAAM,WAAW,GAAG,YAAY,GAAG,OAAO,CAAC;AAEjD;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,UAAU,CAAC;AAEnD,8FAA8F;AAC9F,MAAM,WAAW,cAAc;IAC7B,2EAA2E;IAC3E,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,kGAAkG;IAClG,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,4FAA4F;IAC5F,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,kEAAkE;IAClE,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC;IAChC,mEAAmE;IACnE,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;CAC9B;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,cAAc,MAAM,CAAC;AAElC;;;GAGG;AACH,eAAO,MAAM,gBAAgB;IAC3B;;;OAGG;;iBACS,KAAK,EAAE,iBAAiB;iBAAE,SAAS,EAAE,EAAE;iBAAE,SAAS,EAAE,CAAC;iBAAE,MAAM,EAAE,QAAQ;iBAAE,MAAM,EAAE,YAAY;;IACzG;;;;;;OAMG;;iBACO,KAAK,EAAE,sBAAsB;iBAAE,SAAS,EAAE,EAAE;iBAAE,SAAS,EAAE,CAAC;iBAAE,MAAM,EAAE,QAAQ;iBAAE,MAAM,EAAE,YAAY;;IAC5G;;;OAGG;;iBACG,KAAK,EAAE,mBAAmB;iBAAE,SAAS,EAAE,EAAE;iBAAE,SAAS,EAAE,CAAC;iBAAE,MAAM,EAAE,UAAU;iBAAE,MAAM,EAAE,YAAY;;IACvG;;;;OAIG;;iBAED,KAAK,EAAE,wBAAwB;iBAC/B,SAAS,EAAE,EAAE;iBACb,SAAS,EAAE,CAAC;iBACZ,MAAM,EAAE,QAAQ;iBAChB,MAAM,EAAE,YAAY;;IAEtB;;;;;OAKG;;iBACG,KAAK,EAAE,sBAAsB;iBAAE,SAAS,EAAE,GAAG;iBAAE,SAAS,EAAE,CAAC;iBAAE,MAAM,EAAE,UAAU;iBAAE,MAAM,EAAE,YAAY;;IAC3G;;;OAGG;;iBACG,KAAK,EAAE,oBAAoB;iBAAE,SAAS;iBAAkB,SAAS,EAAE,CAAC;iBAAE,MAAM,EAAE,UAAU;iBAAE,MAAM,EAAE,OAAO;;IAC/G;;;OAGG;;iBAED,KAAK,EAAE,4BAA4B;iBACnC,SAAS;iBACT,SAAS,EAAE,CAAC;iBACZ,MAAM,EAAE,UAAU;iBAClB,MAAM,EAAE,OAAO;;IAEjB;;;OAGG;;iBAED,KAAK,EAAE,6BAA6B;iBACpC,SAAS;iBACT,SAAS,EAAE,CAAC;iBACZ,MAAM,EAAE,UAAU;iBAClB,MAAM,EAAE,OAAO;;CAEgC,CAAC;AAEpD,0DAA0D;AAC1D,MAAM,MAAM,SAAS,GAAG,MAAM,OAAO,gBAAgB,CAAC;AAEtD;;;;;;;;;GASG;AACH,eAAO,MAAM,kBAAkB,QAAkC,CAAC;AAElE;;;;;;;;GAQG;AACH,eAAO,MAAM,gBAAgB,IAAI,CAAC;AAElC;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,KAAK,CAAC;AAEnC;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,sBAAsB,OAAO,CAAC;AAE3C;;;;;;;;GAQG;AACH,eAAO,MAAM,oBAAoB,KAAK,CAAC;AAEvC,8GAA8G;AAC9G,eAAO,MAAM,gBAAgB,IAAI,CAAC;AAElC;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,6BAA6B,QAC+C,CAAC;AAE1F;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,4BAA4B,QAMjB,CAAC"}
1
+ {"version":3,"file":"limits.d.ts","sourceRoot":"","sources":["../../src/naming/limits.ts"],"names":[],"mappings":"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"}
@@ -80,6 +80,47 @@ const NAMESPACE_LIMITS = {
80
80
  source: "cloudflare"
81
81
  },
82
82
  /**
83
+ * A Queue name: **1 to 63 characters**, lowercase letters, digits and dashes, starting and ending with a letter
84
+ * or number ([Queues · Get started](https://developers.cloudflare.com/queues/get-started/), verified
85
+ * 2026-10-18). Truncated rather than refused: Pithy recomputes a queue's name from its binding on every run,
86
+ * as it does a bucket's, so a deterministic hash is safe (#662). The same 63 as R2, so a feature's queue fits
87
+ * the shape {@link FEATURE_DERIVED_PROJECT_NAME} is derived against and moves no budget.
88
+ */
89
+ queue: {
90
+ label: "a Queue name",
91
+ maxLength: 63,
92
+ minLength: 1,
93
+ policy: "truncate",
94
+ source: "cloudflare"
95
+ },
96
+ /**
97
+ * An Artifacts namespace: **2 to 63 characters**, starting with a letter or digit, the rest letters, digits, `.`,
98
+ * `_` and `-` ([Artifacts · Limits](https://developers.cloudflare.com/artifacts/platform/limits/), verified
99
+ * 2026-10-18). Truncated rather than refused: the kit derives it from the project and the environment on every
100
+ * run, like an R2 bucket name, and the longest project and environment compose 34 characters, so it never is (#673).
101
+ */
102
+ artifactsNamespace: {
103
+ label: "an Artifacts namespace name",
104
+ maxLength: 63,
105
+ minLength: 2,
106
+ policy: "truncate",
107
+ source: "cloudflare"
108
+ },
109
+ /**
110
+ * An Artifacts repo name. **The charset is Cloudflare's** — a letter or digit, then letters, digits, `.`, `_` and
111
+ * `-` ([Artifacts · Limits](https://developers.cloudflare.com/artifacts/platform/limits/), verified 2026-10-18) —
112
+ * and **the length is ours**: that page states one for a namespace and none for a repo, so 2 to 63 mirrors the
113
+ * namespace rather than inventing a second number. Refused, never truncated: a repo name is either a UUID or a
114
+ * name somebody chose, and a name somebody chose is never silently shortened (#673).
115
+ */
116
+ artifactsRepo: {
117
+ label: "an Artifacts repo name",
118
+ maxLength: 63,
119
+ minLength: 2,
120
+ policy: "refuse",
121
+ source: "pithy"
122
+ },
123
+ /**
83
124
  * A D1 database name: `^[a-zA-Z0-9][a-zA-Z0-9_-]*$` in the OpenAPI schema, and **no documented
84
125
  * length cap** — the limits page has no row for it. So the ceiling here is {@link MAX_PITHY_NAME}.
85
126
  */
@@ -165,7 +206,10 @@ const FEATURE_TAIL_SEPARATOR = "--";
165
206
  * and teardown recomputes the name rather than matching a prefix.
166
207
  */
167
208
  const MIN_VERBATIM_BINDING = 12;
168
- /** The longest feature resource kind — `d1`, `kv`, `r2` are all two characters. `feature.test.ts` pins it. */
209
+ /**
210
+ * The longest feature resource kind segment — `d1`, `kv`, `r2`, and a queue's `q` (#662). `feature.test.ts` pins
211
+ * every kind's `FEATURE_KIND_SEGMENT` to it, so a longer segment fails rather than shrinking every project's budget.
212
+ */
169
213
  const MAX_FEATURE_KIND = 2;
170
214
  /**
171
215
  * What the longest Workflow name leaves a project.
@@ -183,7 +227,7 @@ const MAX_FEATURE_KIND = 2;
183
227
  const WORKFLOW_DERIVED_PROJECT_NAME = NAMESPACE_LIMITS.workflow.maxLength - 1 - MAX_ENVIRONMENT_NAME - 1 - 22;
184
228
  /**
185
229
  * What the longest feature resource name leaves a project — `<project>-f<issue>-<slug>--<binding>-<kind>`,
186
- * against R2's 63 (the strictest of the three kinds a feature provisions, and the same number a
230
+ * against R2's 63 (the strictest of the kinds a feature provisions, a queue's number too, and the same number a
187
231
  * feature Worker script gets).
188
232
  *
189
233
  * ```
@@ -192,7 +236,7 @@ const WORKFLOW_DERIVED_PROJECT_NAME = NAMESPACE_LIMITS.workflow.maxLength - 1 -
192
236
  * - 6 the issue number, six digits — `f999999` (MAX_ISSUE_DIGITS)
193
237
  * - 12 `-` + a legible slug (MIN_LEGIBLE_SLUG)
194
238
  * - 14 `--` + a binding kept whole (FEATURE_TAIL_SEPARATOR, MIN_VERBATIM_BINDING)
195
- * - 3 `-` + the kind, `d1` | `kv` | `r2`
239
+ * - 3 `-` + the kind, `d1` | `kv` | `r2` | `q`
196
240
  * ----
197
241
  * 26
198
242
  * ```
@@ -171,6 +171,12 @@ export interface ProvisionScope {
171
171
  * `<project>-feature-email-send` would be one Workflow every open branch deployed over every other's.
172
172
  */
173
173
  readonly workflowHost: Omit<WorkflowHostNameParts, "capability">;
174
+ /**
175
+ * The Artifacts namespace an `artifacts` binding names in this scope (#673): `<project>-<env>` in a declared
176
+ * environment, `<project>-f<issue>-<slug>--artifacts` in a feature. On the scope for the reason every other name
177
+ * is: the stanza a run writes and the namespace it writes into it are one decision.
178
+ */
179
+ readonly artifactsNamespace: string;
174
180
  }
175
181
  /**
176
182
  * The same branch, one line down, for a provisioned resource: a `global` binding resolves to the
@@ -1 +1 @@
1
- {"version":3,"file":"provisionScope.d.ts","sourceRoot":"","sources":["../../src/naming/provisionScope.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAEhE,OAAO,EACL,KAAK,eAAe,EACpB,KAAK,mBAAmB,EAIzB,MAAM,WAAW,CAAC;AAInB;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH;;;;;;GAMG;AACH,MAAM,MAAM,eAAe,GAAG,aAAa,GAAG,QAAQ,CAAC;AAEvD;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,aAAa;IAC5B;;;;;OAKG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,eAAe,CAAC;IACjC;;;OAGG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,mBAAoB,SAAQ,aAAa;IACxD,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC;CAC1B;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,oBAAoB;IACnC,2GAA2G;IAC3G,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,oGAAoG;IACpG,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,uFAAuF;AACvF,MAAM,WAAW,cAAc;IAC7B,6FAA6F;IAC7F,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB;;;;;;;;;;;;;;OAcG;IACH,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB;;;;;;;;;;;OAWG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,mBAAmB,EAAE,MAAM,EAAE,aAAa,GAAG,MAAM,CAAC;IACpF;;;;;;;;;OASG;IACH,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;IAC/B;;;;;;;;;;OAUG;IACH,MAAM,CAAC,MAAM,EAAE,oBAAoB,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAChE;;;;;;;OAOG;IACH,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,eAAe,GAAG,MAAM,CAAC;IAClE;;;;;;;;OAQG;IACH,QAAQ,CAAC,YAAY,EAAE,IAAI,CAAC,qBAAqB,EAAE,YAAY,CAAC,CAAC;CAClE;AAiBD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,mBAAmB,EACzB,MAAM,EAAE,mBAAmB,GAC1B,MAAM,CAAC;AACV,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,mBAAmB,EACzB,MAAM,EAAE,aAAa,EACrB,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,GAChC,MAAM,CAAC;AAsBV;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAKpE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,cAAc,CA8BrF;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,eAAe,GAAG,cAAc,CAiBtE;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,eAAe,EAAE,MAAM,EAAE,oBAAoB,GAAG,MAAM,EAAE,CAY1G"}
1
+ {"version":3,"file":"provisionScope.d.ts","sourceRoot":"","sources":["../../src/naming/provisionScope.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAEhE,OAAO,EACL,KAAK,eAAe,EACpB,KAAK,mBAAmB,EAKzB,MAAM,WAAW,CAAC;AAInB;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH;;;;;;GAMG;AACH,MAAM,MAAM,eAAe,GAAG,aAAa,GAAG,QAAQ,CAAC;AAEvD;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,aAAa;IAC5B;;;;;OAKG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,eAAe,CAAC;IACjC;;;OAGG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,mBAAoB,SAAQ,aAAa;IACxD,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC;CAC1B;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,oBAAoB;IACnC,2GAA2G;IAC3G,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,oGAAoG;IACpG,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,uFAAuF;AACvF,MAAM,WAAW,cAAc;IAC7B,6FAA6F;IAC7F,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB;;;;;;;;;;;;;;OAcG;IACH,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB;;;;;;;;;;;OAWG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,mBAAmB,EAAE,MAAM,EAAE,aAAa,GAAG,MAAM,CAAC;IACpF;;;;;;;;;OASG;IACH,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;IAC/B;;;;;;;;;;OAUG;IACH,MAAM,CAAC,MAAM,EAAE,oBAAoB,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAChE;;;;;;;OAOG;IACH,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,eAAe,GAAG,MAAM,CAAC;IAClE;;;;;;;;OAQG;IACH,QAAQ,CAAC,YAAY,EAAE,IAAI,CAAC,qBAAqB,EAAE,YAAY,CAAC,CAAC;IACjE;;;;OAIG;IACH,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;CACrC;AAsBD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,mBAAmB,EACzB,MAAM,EAAE,mBAAmB,GAC1B,MAAM,CAAC;AACV,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,mBAAmB,EACzB,MAAM,EAAE,aAAa,EACrB,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,GAChC,MAAM,CAAC;AAsBV;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAKpE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,cAAc,CA+BrF;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,eAAe,GAAG,cAAc,CAsBtE;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,eAAe,EAAE,MAAM,EAAE,oBAAoB,GAAG,MAAM,EAAE,CAY1G"}
@@ -4,14 +4,15 @@ import { InternalError, ValidationError } from "../error/pithyError.js";
4
4
  import { commandName } from "../cli/command.js";
5
5
  import { FEATURE_ENVIRONMENT, assertValidEnvironment, isFeatureMarker } from "./environment.js";
6
6
  import { kebab } from "./resource.js";
7
- import { featureResourceName, featureSecretEntryName, featureWorkerName } from "./feature.js";
7
+ import { featureArtifactsNamespace, featureResourceName, featureSecretEntryName, featureWorkerName } from "./feature.js";
8
8
  import { resourceNames } from "./resourceNames.js";
9
9
  //#region src/naming/provisionScope.ts
10
10
  /** The wrangler binding array a resource kind's name is composed for. */
11
11
  const KIND_NAMER = {
12
12
  d1: "d1",
13
13
  kv: "kv",
14
- r2: "r2"
14
+ r2: "r2",
15
+ queue: "queue"
15
16
  };
16
17
  /**
17
18
  * The one branch every scope shares: a `global` secret resolves to the project's single
@@ -102,7 +103,8 @@ function environmentScope(project, environment) {
102
103
  workflowHost: {
103
104
  project,
104
105
  env: environment
105
- }
106
+ },
107
+ artifactsNamespace: names.artifactsNamespace()
106
108
  };
107
109
  }
108
110
  /**
@@ -145,6 +147,9 @@ function featureScope(identity) {
145
147
  project: identity.project,
146
148
  env: FEATURE_ENVIRONMENT,
147
149
  feature: identity
150
+ },
151
+ get artifactsNamespace() {
152
+ return featureArtifactsNamespace(identity);
148
153
  }
149
154
  };
150
155
  }
@@ -49,6 +49,8 @@ export interface ScopedNames {
49
49
  kv(binding: string): string;
50
50
  /** An R2 bucket. Cloudflare's 63 — the one namespace where that number was always right. */
51
51
  r2(binding: string): string;
52
+ /** A Queue. Cloudflare's 63, truncated with a stable hash like a bucket (#662). */
53
+ queue(binding: string): string;
52
54
  /** A Vectorize index. 64 bytes, refused rather than truncated: a renamed index has no vectors. */
53
55
  vectorizeIndex(index: string): string;
54
56
  /** A Secrets Store entry. No Cloudflare cap; Pithy's ceiling. */
@@ -56,22 +58,39 @@ export interface ScopedNames {
56
58
  /** A Cloudflare API token label. No Cloudflare cap; Pithy's ceiling. */
57
59
  apiToken(profile: string): string;
58
60
  }
61
+ /**
62
+ * The names in one declared environment: everything a scope has, and the one name that exists per environment
63
+ * and never at `global`.
64
+ */
65
+ export interface EnvironmentNames extends ScopedNames {
66
+ /**
67
+ * The Artifacts namespace — `<project>-<env>`, with no `<thing>` (#673). One per environment, so a feature's
68
+ * repos never sit beside production's and the per-namespace rate limit splits per environment. There is no
69
+ * global one, because a namespace shared across environments is exactly that mix.
70
+ */
71
+ artifactsNamespace(): string;
72
+ }
59
73
  /** The names a feature environment provisions under, with the project already bound. */
60
74
  export interface FeatureNames {
61
- /** A feature's D1, KV, or R2 resource — `<project>-f<issue>-<slug>-<binding>-<kind>`. */
75
+ /** A feature's D1, KV, R2 or Queue resource — `<project>-f<issue>-<slug>--<binding>-<kind>`. */
62
76
  resource(binding: string, kind: FeatureResourceKind): string;
63
77
  /**
64
78
  * A feature's Worker script — `<project>-f<issue>-<slug>-<app>`, from the `apps/<app>` directory and
65
79
  * never the deploy name, which already leads with the project (#587).
66
80
  */
67
81
  worker(app: string): string;
82
+ /**
83
+ * A feature's Artifacts namespace — `<project>-f<issue>-<slug>--artifacts` (#673). The tail is what makes it a
84
+ * feature name `isFeatureOwnedName` reads back, so teardown reaps this feature's repos and no sibling's.
85
+ */
86
+ artifactsNamespace(): string;
68
87
  }
69
88
  /** Everything a project can be asked to name. Created once, from the project, by {@link resourceNames}. */
70
89
  export interface ProjectNames {
71
90
  /** The kebabbed project name every name below leads with. */
72
91
  readonly project: string;
73
92
  /** The names in one environment. Throws if the environment is not one this scheme accepts. */
74
- env(environment: string): ScopedNames;
93
+ env(environment: string): EnvironmentNames;
75
94
  /** The names shared by every environment — the `global` scope, without typing the word. */
76
95
  readonly global: ScopedNames;
77
96
  /** The names a feature environment provisions under. */
@@ -1 +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;;;OAGG;IACH,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;CAC7B;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"}
1
+ {"version":3,"file":"resourceNames.d.ts","sourceRoot":"","sources":["../../src/naming/resourceNames.ts"],"names":[],"mappings":"AAMA,OAAO,EACL,KAAK,eAAe,EACpB,KAAK,mBAAmB,EAIzB,MAAM,WAAW,CAAC;AAInB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;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,mFAAmF;IACnF,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC;IAC/B,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;;;GAGG;AACH,MAAM,WAAW,gBAAiB,SAAQ,WAAW;IACnD;;;;OAIG;IACH,kBAAkB,IAAI,MAAM,CAAC;CAC9B;AAED,wFAAwF;AACxF,MAAM,WAAW,YAAY;IAC3B,gGAAgG;IAChG,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,mBAAmB,GAAG,MAAM,CAAC;IAC7D;;;OAGG;IACH,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;IAC5B;;;OAGG;IACH,kBAAkB,IAAI,MAAM,CAAC;CAC9B;AAED,2GAA2G;AAC3G,MAAM,WAAW,YAAY;IAC3B,6DAA6D;IAC7D,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,8FAA8F;IAC9F,GAAG,CAAC,WAAW,EAAE,MAAM,GAAG,gBAAgB,CAAC;IAC3C,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;AA+ED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,YAAY,CAuB3D"}
@@ -4,7 +4,7 @@ import { InternalError, ValidationError } from "../error/pithyError.js";
4
4
  import { GLOBAL_SCOPE, assertValidEnvironment } from "./environment.js";
5
5
  import { NAMESPACE_LIMITS } from "./limits.js";
6
6
  import { assertValidProjectName, kebab, resourceName } from "./resource.js";
7
- import { featureResourceName, featureWorkerName } from "./feature.js";
7
+ import { featureArtifactsNamespace, featureResourceName, featureWorkerName } from "./feature.js";
8
8
  import { workflowHostName, workflowScriptName } from "../workflow/naming.js";
9
9
  //#region src/naming/resourceNames.ts
10
10
  /**
@@ -49,6 +49,19 @@ function scopedName(project, scope, thing, limit) {
49
49
  });
50
50
  return name;
51
51
  }
52
+ /**
53
+ * `<project>-<env>`, under the Artifacts namespace rule. The project and environment are both validated by now, and
54
+ * the longest of each compose 34 characters, so the cap is a check rather than a truncation that ever happens.
55
+ */
56
+ function artifactsNamespaceName(project, environment) {
57
+ const limit = NAMESPACE_LIMITS.artifactsNamespace;
58
+ const name = `${project}-${environment}`;
59
+ if (name.length > limit.maxLength || name.length < limit.minLength) throw new InternalError({
60
+ message: `"${name}" does not fit ${limit.label}, which takes ${limit.minLength} to ${limit.maxLength} characters.`,
61
+ action: "Shorten the project or environment name."
62
+ });
63
+ return name;
64
+ }
52
65
  /** The getters for one scope. `global` and every environment share this, so nothing special-cases either. */
53
66
  function scopedNames(project, scope) {
54
67
  return {
@@ -66,6 +79,7 @@ function scopedNames(project, scope) {
66
79
  d1: (binding) => scopedName(project, scope, binding, NAMESPACE_LIMITS.d1),
67
80
  kv: (binding) => scopedName(project, scope, binding, NAMESPACE_LIMITS.kv),
68
81
  r2: (binding) => scopedName(project, scope, binding, NAMESPACE_LIMITS.r2),
82
+ queue: (binding) => scopedName(project, scope, binding, NAMESPACE_LIMITS.queue),
69
83
  vectorizeIndex: (index) => scopedName(project, scope, index, NAMESPACE_LIMITS.vectorizeIndex),
70
84
  secretEntry: (secret) => scopedName(project, scope, secret, NAMESPACE_LIMITS.secretEntry),
71
85
  apiToken: (profile) => scopedName(project, scope, profile, NAMESPACE_LIMITS.apiToken)
@@ -85,7 +99,10 @@ function resourceNames(project) {
85
99
  project: normalized,
86
100
  env(environment) {
87
101
  assertValidEnvironment(environment);
88
- return scopedNames(normalized, environment);
102
+ return {
103
+ ...scopedNames(normalized, environment),
104
+ artifactsNamespace: () => artifactsNamespaceName(normalized, environment)
105
+ };
89
106
  },
90
107
  global: scopedNames(normalized, GLOBAL_SCOPE),
91
108
  feature(identity) {
@@ -95,7 +112,8 @@ function resourceNames(project) {
95
112
  };
96
113
  return {
97
114
  resource: (binding, kind) => featureResourceName(full, binding, kind),
98
- worker: (app) => featureWorkerName(full, app)
115
+ worker: (app) => featureWorkerName(full, app),
116
+ artifactsNamespace: () => featureArtifactsNamespace(full)
99
117
  };
100
118
  }
101
119
  };
@@ -0,0 +1,27 @@
1
+ // SPDX-FileCopyrightText: 2026 Pithy
2
+ // SPDX-License-Identifier: MIT
3
+ import { type EncryptionConfig } from "../crypto/envelope";
4
+ import { type BlindIndexNormalizer } from "./encrypted";
5
+ /**
6
+ * **A blind index: a keyed hash an encrypted column can be found by** (#390).
7
+ *
8
+ * An encrypted column cannot answer `WHERE email = ?`. A sibling column holding `HMAC(key, normalize(value))` can,
9
+ * for equality and nothing else. One rule here is shared by the app Worker that seals, `sealed().match` that looks
10
+ * up, and the rotator that re-indexes inside the manager, so the three cannot hash one value two ways.
11
+ *
12
+ * - **The key is derived, on the field key's own version axis.** Each version's index key is HKDF-SHA256 over that
13
+ * version's field key, with the label `pithy/blind-index/<normalizer>`. One secret, one rotation.
14
+ * - **The value carries its version**: `<version>:<hmac>`, so the stale count reaches an index exactly as it reaches
15
+ * an envelope, by reading the prefix.
16
+ * - **The normalizer is in the label**, so changing a field's normalizer cannot silently miss against old hashes: they
17
+ * are hashes under a different key, and the change reads as a re-index.
18
+ */
19
+ /** The HKDF label one normalizer's index keys are derived under. */
20
+ export declare function blindIndexLabel(normalizer: BlindIndexNormalizer): string;
21
+ /** The HMAC key for one version and normalizer: HKDF-SHA256 over that version's field key. */
22
+ export declare function blindIndexKey(config: EncryptionConfig, version: number, normalizer: BlindIndexNormalizer): Promise<CryptoKey>;
23
+ /** The blind index of `value` under `version`: `<version>:<hmac>`. The value is normalized first. */
24
+ export declare function blindIndexValue(config: EncryptionConfig, version: number, normalizer: BlindIndexNormalizer, value: string): Promise<string>;
25
+ /** The version a blind-index value was written under, or `null` for a value that is not one. */
26
+ export declare function blindIndexVersion(value: string): number | null;
27
+ //# sourceMappingURL=blindIndex.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blindIndex.d.ts","sourceRoot":"","sources":["../../src/privacy/blindIndex.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,gBAAgB,EAAY,MAAM,oBAAoB,CAAC;AAErE,OAAO,EAAE,KAAK,oBAAoB,EAAqB,MAAM,aAAa,CAAC;AAE3E;;;;;;;;;;;;;GAaG;AAEH,oEAAoE;AACpE,wBAAgB,eAAe,CAAC,UAAU,EAAE,oBAAoB,GAAG,MAAM,CAExE;AASD,8FAA8F;AAC9F,wBAAsB,aAAa,CACjC,MAAM,EAAE,gBAAgB,EACxB,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,oBAAoB,GAC/B,OAAO,CAAC,SAAS,CAAC,CAmBpB;AAED,qGAAqG;AACrG,wBAAsB,eAAe,CACnC,MAAM,EAAE,gBAAgB,EACxB,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,oBAAoB,EAChC,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,MAAM,CAAC,CAQjB;AAED,gGAAgG;AAChG,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAG9D"}
@@ -0,0 +1,57 @@
1
+ // SPDX-FileCopyrightText: 2026 Pithy
2
+ // SPDX-License-Identifier: MIT
3
+ import { keyBytes } from "../crypto/envelope.js";
4
+ import { FIELD_KEYS_BINDING } from "../crypto/fieldKeys.js";
5
+ import { normalizeForIndex } from "./encrypted.js";
6
+ //#region src/privacy/blindIndex.ts
7
+ /**
8
+ * **A blind index: a keyed hash an encrypted column can be found by** (#390).
9
+ *
10
+ * An encrypted column cannot answer `WHERE email = ?`. A sibling column holding `HMAC(key, normalize(value))` can,
11
+ * for equality and nothing else. One rule here is shared by the app Worker that seals, `sealed().match` that looks
12
+ * up, and the rotator that re-indexes inside the manager, so the three cannot hash one value two ways.
13
+ *
14
+ * - **The key is derived, on the field key's own version axis.** Each version's index key is HKDF-SHA256 over that
15
+ * version's field key, with the label `pithy/blind-index/<normalizer>`. One secret, one rotation.
16
+ * - **The value carries its version**: `<version>:<hmac>`, so the stale count reaches an index exactly as it reaches
17
+ * an envelope, by reading the prefix.
18
+ * - **The normalizer is in the label**, so changing a field's normalizer cannot silently miss against old hashes: they
19
+ * are hashes under a different key, and the change reads as a re-index.
20
+ */
21
+ /** The HKDF label one normalizer's index keys are derived under. */
22
+ function blindIndexLabel(normalizer) {
23
+ return `pithy/blind-index/${normalizer}`;
24
+ }
25
+ /** Unpadded base64url, the alphabet a blind index is written in. */
26
+ function base64Url(bytes) {
27
+ let binary = "";
28
+ for (const byte of bytes) binary += String.fromCharCode(byte);
29
+ return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
30
+ }
31
+ /** The HMAC key for one version and normalizer: HKDF-SHA256 over that version's field key. */
32
+ async function blindIndexKey(config, version, normalizer) {
33
+ const material = await crypto.subtle.importKey("raw", keyBytes(config, version, { keys: FIELD_KEYS_BINDING }), "HKDF", false, ["deriveBits"]);
34
+ const bits = await crypto.subtle.deriveBits({
35
+ name: "HKDF",
36
+ hash: "SHA-256",
37
+ salt: /* @__PURE__ */ new Uint8Array(0),
38
+ info: Uint8Array.from(new TextEncoder().encode(blindIndexLabel(normalizer)))
39
+ }, material, 256);
40
+ return crypto.subtle.importKey("raw", bits, {
41
+ name: "HMAC",
42
+ hash: "SHA-256"
43
+ }, false, ["sign"]);
44
+ }
45
+ /** The blind index of `value` under `version`: `<version>:<hmac>`. The value is normalized first. */
46
+ async function blindIndexValue(config, version, normalizer, value) {
47
+ const key = await blindIndexKey(config, version, normalizer);
48
+ const mac = await crypto.subtle.sign("HMAC", key, Uint8Array.from(new TextEncoder().encode(normalizeForIndex(normalizer, value))));
49
+ return `${version}:${base64Url(new Uint8Array(mac))}`;
50
+ }
51
+ /** The version a blind-index value was written under, or `null` for a value that is not one. */
52
+ function blindIndexVersion(value) {
53
+ const match = /^(\d+):/.exec(value);
54
+ return match ? Number(match[1]) : null;
55
+ }
56
+ //#endregion
57
+ export { blindIndexKey, blindIndexLabel, blindIndexValue, blindIndexVersion };
@@ -0,0 +1,142 @@
1
+ // SPDX-FileCopyrightText: 2026 Pithy
2
+ // SPDX-License-Identifier: MIT
3
+ import { z } from "zod";
4
+ /**
5
+ * **A field declares its own privacy class, and the declaration drives everything downstream** (#132).
6
+ *
7
+ * One marker, several consumers — the shape `@pithy-sh/vector` already uses, where `.meta({ filterable: true
8
+ * })` drives provisioning, the filter type, the drift check and the boot check. Here the marker drives the
9
+ * type gate on the three internal surfaces (the logger, `AuditEvent.metadata`, `PithyError.detail`), the
10
+ * masked view `redact` returns, the classification `classifyPrivacy` derives, and the inventory the control
11
+ * plane reports.
12
+ *
13
+ * ## What a marker does, in one call
14
+ *
15
+ * `pii(schema)` does two things. It attaches `.meta({ privacy: "pii" })`, which is **introspectable** — that is
16
+ * what `redact`, `classifyPrivacy` and the inventory read. And it applies Zod's `.brand()`, which changes the
17
+ * **inferred type** — that is what makes a log call carrying the value fail to compile. The brand lands on the
18
+ * field, not on the object, so extracting the field does not launder it: `user.email` is as branded as `user`.
19
+ *
20
+ * **A marker over another marker adds its class.** `pii(secret(x))` and `secret(pii(x))` are the same field: it
21
+ * carries both classes, in its `meta` (as a list, strongest first) and in its type (both brands). It is masked with the
22
+ * strongest one's placeholder and listed once per class. The same marker twice is one class.
23
+ *
24
+ * **The brand key is `privacy:<class>`, not the bare class name.** An adopter's own `.brand<"pii">()` for some
25
+ * unrelated purpose must not trip a gate it never opted into, and an adopter's `UserId` brand must not either:
26
+ * the gate reads a brand under the `privacy:` namespace and nothing else, which is the federated-key rule
27
+ * (`auth/invalid_token`, `pithy_auth_users`) applied one more time.
28
+ *
29
+ * **Nothing at runtime changes.** Zod's `.brand()` returns the same schema object — the brand exists only in
30
+ * the type — and `.meta()` returns a clone that parses exactly as its source. `Schema.parse(row)` returns the
31
+ * same data it did before the marker, and no column is added anywhere.
32
+ *
33
+ * ## The set is open
34
+ *
35
+ * Four classes ship named — `secret`, `pii`, `phi`, `pci` — and {@link definePrivacyClass} registers more
36
+ * (`genetic`, `biometric`, whatever a regulator names) without a change to core. A closed union is what #117
37
+ * records the cost of, and a healthcare adopter should not have to wait on us.
38
+ *
39
+ * ## Where `secret` stops and `@pithy-sh/secrets` starts
40
+ *
41
+ * Two different facts, kept apart on purpose. `@pithy-sh/secrets` decides **where a credential the Worker reads
42
+ * is stored** — its registry names a backend, and its reader redacts the value it hands back. `secret()` says
43
+ * **what a field in a data shape is** — a receipt, a magic-link token, a session id, a key hash — values that
44
+ * live in rows and payloads and that no secrets registry ever holds. Merging them would make core depend on a
45
+ * capability (principle 4) and would make a storage decision answer a classification question.
46
+ */
47
+ /** The `meta` key a marker writes and every reader reads. */
48
+ export declare const PRIVACY_META_KEY = "privacy";
49
+ /** The prefix of every privacy brand, so the gate never reads a brand that means something else. */
50
+ export declare const PRIVACY_BRAND_PREFIX = "privacy:";
51
+ /** A class name: a lowercase identifier. It becomes a brand key, a config value and a report column. */
52
+ export declare const PrivacyClassName: z.ZodString;
53
+ export type PrivacyClassName = z.output<typeof PrivacyClassName>;
54
+ /** The four classes the kit names, in the order every report lists them. */
55
+ export declare const BUILT_IN_PRIVACY_CLASSES: readonly ["secret", "pii", "phi", "pci"];
56
+ /** One of the four built-in class names. */
57
+ export type BuiltInPrivacyClass = (typeof BUILT_IN_PRIVACY_CLASSES)[number];
58
+ /** How a class masks a value: the string that stands in its place on every internal surface. */
59
+ export type PrivacyMask = (value: unknown) => string;
60
+ /** One registered class: its name, what it means, and how it masks. */
61
+ export interface PrivacyClass {
62
+ /** The class name — the `meta` value and the brand's suffix. */
63
+ readonly name: string;
64
+ /** What the class means, in one sentence an auditor reads. */
65
+ readonly description: string;
66
+ /** The masked form of a value of this class. */
67
+ readonly mask: PrivacyMask;
68
+ }
69
+ /**
70
+ * The brand a marker puts on a schema's output type. Zod's own brand machinery, under the `privacy:` key.
71
+ *
72
+ * `"out"` on purpose: the **input** side stays unbranded, so `z.input` — the SQLite row shape, the Kysely
73
+ * table type, a KV write's pre-parse value — is exactly what it was. A row read from D1 parses into the
74
+ * branded app shape, and `Schema.encode()` hands Kysely the plain row back.
75
+ */
76
+ export type Marked<S extends z.ZodType, C extends string> = z.core.$ZodBranded<S, `${typeof PRIVACY_BRAND_PREFIX}${C}`, "out">;
77
+ /** A marker for one class: wraps a schema and returns it marked. Carries its class name for a reader. */
78
+ export interface PrivacyMarker<C extends string> {
79
+ <S extends z.ZodType>(schema: S): Marked<S, C>;
80
+ /** The class this marker applies. */
81
+ readonly className: C;
82
+ }
83
+ /**
84
+ * Mask a primary account number to its first six and last four digits — `424242******4242`.
85
+ *
86
+ * **The one class whose mask is not a placeholder, because the masked form is the useful one.** First-six and
87
+ * last-four is the display PCI-DSS permits, and it is what an operator reading a log needs to match a
88
+ * complaint to a card without holding the card. Spaces and dashes are separators and are dropped; anything
89
+ * that is not 13 to 19 digits once they are is not a card number, and masks to `[pci]` rather than to a
90
+ * partial reveal of something else.
91
+ *
92
+ * Every payment rail Pithy ships is hosted, so no PAN ever reaches an adopter's Worker through the kit. The
93
+ * class is here for the adopter's own schemas.
94
+ */
95
+ export declare function maskPan(value: unknown): string;
96
+ /** The class a name refers to, or `undefined` for a name nothing registered. */
97
+ export declare function privacyClass(name: string): PrivacyClass | undefined;
98
+ /** Every registered class, built-ins first in their fixed order, then each adopter class in registration order. */
99
+ export declare function privacyClasses(): PrivacyClass[];
100
+ /**
101
+ * `names` from the strongest to the weakest, each once: `secret`, then `phi`, then `pci`, then each adopter class, then
102
+ * `pii`. An adopter registers a class because its data is more specific than `pii`, so it ranks above it; adopter
103
+ * classes rank among themselves by name. The order a marker records several classes in, whatever order they came.
104
+ */
105
+ export declare function rankPrivacyClasses(names: Iterable<string>): string[];
106
+ /**
107
+ * The strongest of `names`, the class a value is masked with when it carries several and is masked whole. See
108
+ * {@link rankPrivacyClasses}. `undefined` for none.
109
+ */
110
+ export declare function strongestPrivacyClass(names: Iterable<string>): string | undefined;
111
+ /**
112
+ * The classes a schema's own `privacy` meta records: one class as its name, several as a list, strongest first. Read
113
+ * from this exact schema, never through a wrapper. Empty where it records none.
114
+ */
115
+ export declare function ownPrivacyClasses(schema: z.ZodType): string[];
116
+ /** What {@link definePrivacyClass} takes. */
117
+ export interface PrivacyClassSpec<C extends string> {
118
+ /** The class name: a lowercase identifier, unique across the isolate. */
119
+ name: C;
120
+ /** What the class means. Defaults to a sentence naming it as adopter-registered. */
121
+ description?: string;
122
+ /** How a value masks. Defaults to the `[name]` placeholder. */
123
+ mask?: PrivacyMask;
124
+ }
125
+ /**
126
+ * Register a class of the adopter's own — `genetic`, `biometric`, whatever their regulator names — and get its
127
+ * marker back. Usable exactly like the built-ins: the same `meta`, the same brand, the same gate, the same
128
+ * inventory row.
129
+ *
130
+ * **A duplicate is refused at registration**, a built-in name included. Two modules each believing they own
131
+ * `genetic` would disagree about its mask, and the one that loaded second would win silently.
132
+ */
133
+ export declare function definePrivacyClass<const C extends string>(spec: PrivacyClassSpec<C>): PrivacyMarker<C>;
134
+ /** Mark a field as a credential or bearer artifact. See {@link BUILT_INS}. */
135
+ export declare const secret: PrivacyMarker<"secret">;
136
+ /** Mark a field as personal data under GDPR or CCPA. */
137
+ export declare const pii: PrivacyMarker<"pii">;
138
+ /** Mark a field as Protected Health Information under HIPAA. */
139
+ export declare const phi: PrivacyMarker<"phi">;
140
+ /** Mark a field as cardholder data under PCI-DSS. Masks to first six and last four. */
141
+ export declare const pci: PrivacyMarker<"pci">;
142
+ //# sourceMappingURL=classes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"classes.d.ts","sourceRoot":"","sources":["../../src/privacy/classes.ts"],"names":[],"mappings":"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"}