@pithy-sh/core 0.7.2 → 0.7.4

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 (47) hide show
  1. package/dist/capability/capability.d.ts +22 -0
  2. package/dist/capability/capability.d.ts.map +1 -1
  3. package/dist/controlPlane/capability.d.ts.map +1 -1
  4. package/dist/controlPlane/capability.js +3 -1
  5. package/dist/controlPlane/discovery/adminRoute.d.ts +10 -0
  6. package/dist/controlPlane/discovery/adminRoute.d.ts.map +1 -1
  7. package/dist/controlPlane/discovery/adminRoute.js +10 -3
  8. package/dist/controlPlane/http/handlers.d.ts +1 -0
  9. package/dist/controlPlane/http/handlers.d.ts.map +1 -1
  10. package/dist/naming/domains.d.ts +43 -3
  11. package/dist/naming/domains.d.ts.map +1 -1
  12. package/dist/naming/domains.js +62 -4
  13. package/dist/naming/feature.d.ts +126 -11
  14. package/dist/naming/feature.d.ts.map +1 -1
  15. package/dist/naming/feature.js +184 -30
  16. package/dist/naming/limits.d.ts +23 -7
  17. package/dist/naming/limits.d.ts.map +1 -1
  18. package/dist/naming/limits.js +22 -9
  19. package/dist/naming/provisionScope.d.ts +25 -11
  20. package/dist/naming/provisionScope.d.ts.map +1 -1
  21. package/dist/naming/provisionScope.js +28 -11
  22. package/dist/seed/seed.d.ts +11 -7
  23. package/dist/seed/seed.d.ts.map +1 -1
  24. package/dist/version.generated.d.ts +1 -1
  25. package/dist/version.generated.js +1 -1
  26. package/dist/worker/identity.d.ts +9 -0
  27. package/dist/worker/identity.d.ts.map +1 -1
  28. package/dist/worker/identity.js +10 -1
  29. package/dist/workflow/host.d.ts +7 -0
  30. package/dist/workflow/host.d.ts.map +1 -1
  31. package/dist/workflow/host.js +5 -3
  32. package/dist/workflow/naming.d.ts +8 -0
  33. package/dist/workflow/naming.d.ts.map +1 -1
  34. package/dist/workflow/naming.js +15 -0
  35. package/package.json +1 -1
  36. package/src/capability/capability.ts +22 -0
  37. package/src/controlPlane/capability.ts +11 -1
  38. package/src/controlPlane/discovery/adminRoute.ts +23 -3
  39. package/src/naming/domains.ts +75 -5
  40. package/src/naming/feature.ts +274 -38
  41. package/src/naming/limits.ts +25 -8
  42. package/src/naming/provisionScope.ts +39 -14
  43. package/src/seed/seed.ts +11 -7
  44. package/src/version.generated.ts +1 -1
  45. package/src/worker/identity.ts +10 -0
  46. package/src/workflow/host.ts +15 -3
  47. package/src/workflow/naming.ts +25 -0
@@ -462,6 +462,28 @@ export interface Capability<Databases extends DatabaseSpecMap = DatabaseSpecMap,
462
462
  * question.
463
463
  */
464
464
  version?: string | null;
465
+ /**
466
+ * The npm **package** that supplies this capability, or `null` where there is none.
467
+ *
468
+ * The join key {@link Capability.version} is useless without. A version joins against a release feed
469
+ * by package name, and this manifest reported the version alone until #626 — so a client had to guess
470
+ * `@pithy-sh/${name}`, which is right for most capabilities and wrong for the one that matters:
471
+ * `controlplane` ships inside `@pithy-sh/core`, so the guess reaches a package that has never been
472
+ * published and the join comes back empty, which is indistinguishable from "up to date".
473
+ *
474
+ * **A capability name and a package name are different kinds of thing.** One is what a composition
475
+ * calls a concept; the other is how that concept is distributed. A package may ship more than one
476
+ * capability — `core` already ships `controlplane` — so no convention makes the second derivable from
477
+ * the first, and the relationship is a fact only the producing package knows. Which is why **the
478
+ * framework never fills this in from `name`**: deriving it here would re-create the bug inside the fix.
479
+ *
480
+ * Every `@pithy-sh/*` capability sets it from its generated `PACKAGE_NAME` constant
481
+ * (`scripts/stampVersions.ts`), beside the version off the same stamp. **Nullable for the same reason
482
+ * `version` is**, and null in the same breath: the adopter's own `app` capability has a name, no
483
+ * package and no version. Neither alone is a state worth having — a package with no version says
484
+ * nothing joinable, and a version with no package is the defect this field closes.
485
+ */
486
+ package?: string | null;
465
487
  /**
466
488
  * Optional startup hook, called once when {@link createBackend} assembles the backend, with every
467
489
  * composed capability. Runs after binding and `dependsOn` validation, before middleware and routes
@@ -1 +1 @@
1
- {"version":3,"file":"capability.d.ts","sourceRoot":"","sources":["../../src/capability/capability.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"capability.d.ts","sourceRoot":"","sources":["../../src/capability/capability.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"capability.d.ts","sourceRoot":"","sources":["../../src/controlPlane/capability.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,KAAK,UAAU,EAAuC,MAAM,0BAA0B,CAAC;AAChG,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,KAAK,EAAE,kBAAkB,EAAc,MAAM,kBAAkB,CAAC;AAGvE,OAAO,EAAE,kBAAkB,EAAE,KAAK,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAWnF,OAAO,EACL,wBAAwB,EAIzB,MAAM,kBAAkB,CAAC;AAE1B;;;;;;;;;GASG;AACH,eAAO,MAAM,4BAA4B,OAAO,CAAC;AAEjD,MAAM,MAAM,mBAAmB,GAAG,uBAAuB,CAAC;AAE1D,uEAAuE;AACvE,MAAM,WAAW,sBAAuB,SAAQ,UAAU,CAAC,eAAe,EAAE,kBAAkB,EAAE,cAAc,CAAC;IAC7G,gCAAgC;IAChC,kBAAkB,EAAE,kBAAkB,CAAC;CACxC;AAED,qGAAqG;AACrG,wBAAgB,wBAAwB,CAAC,UAAU,EAAE,UAAU,GAAG,UAAU,IAAI,sBAAsB,CAErG;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,YAAY,CAAC,OAAO,GAAE,mBAAwB,GAAG,sBAAsB,CAyJtF;AAED,4GAA4G;AAC5G,OAAO,EAAE,wBAAwB,EAAE,CAAC"}
1
+ {"version":3,"file":"capability.d.ts","sourceRoot":"","sources":["../../src/controlPlane/capability.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,KAAK,UAAU,EAAuC,MAAM,0BAA0B,CAAC;AAChG,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,KAAK,EAAE,kBAAkB,EAAc,MAAM,kBAAkB,CAAC;AAGvE,OAAO,EAAE,kBAAkB,EAAE,KAAK,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAWnF,OAAO,EACL,wBAAwB,EAIzB,MAAM,kBAAkB,CAAC;AAE1B;;;;;;;;;GASG;AACH,eAAO,MAAM,4BAA4B,OAAO,CAAC;AAEjD,MAAM,MAAM,mBAAmB,GAAG,uBAAuB,CAAC;AAE1D,uEAAuE;AACvE,MAAM,WAAW,sBAAuB,SAAQ,UAAU,CAAC,eAAe,EAAE,kBAAkB,EAAE,cAAc,CAAC;IAC7G,gCAAgC;IAChC,kBAAkB,EAAE,kBAAkB,CAAC;CACxC;AAED,qGAAqG;AACrG,wBAAgB,wBAAwB,CAAC,UAAU,EAAE,UAAU,GAAG,UAAU,IAAI,sBAAsB,CAErG;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,YAAY,CAAC,OAAO,GAAE,mBAAwB,GAAG,sBAAsB,CAmKtF;AAED,4GAA4G;AAC5G,OAAO,EAAE,wBAAwB,EAAE,CAAC"}
@@ -1,7 +1,7 @@
1
1
  // SPDX-FileCopyrightText: 2026 Pithy
2
2
  // SPDX-License-Identifier: MIT
3
3
  import { defineCapability } from "../capability/capability.js";
4
- import { PACKAGE_VERSION } from "../version.generated.js";
4
+ import { PACKAGE_NAME, PACKAGE_VERSION } from "../version.generated.js";
5
5
  import "../worker/identity.js";
6
6
  import { ControlPlaneConfig } from "./config/config.js";
7
7
  import { ControlPlaneConnection } from "./data/connection.js";
@@ -87,6 +87,7 @@ function controlplane(options = {}) {
87
87
  ...defineCapability({
88
88
  name: "controlplane",
89
89
  version: PACKAGE_VERSION,
90
+ package: PACKAGE_NAME,
90
91
  requiredBindings,
91
92
  config: ControlPlaneConfig,
92
93
  databases: { app: {
@@ -101,6 +102,7 @@ function controlplane(options = {}) {
101
102
  composed = capabilities.map((cap) => ({
102
103
  name: cap.name,
103
104
  version: cap.version ?? null,
105
+ package: cap.package ?? null,
104
106
  adminRoutes: [...cap.adminRoutes ?? []],
105
107
  healthKeys: [...cap.health?.keys ?? []],
106
108
  configKeys: [...cap.manifestConfig?.keys ?? []],
@@ -52,6 +52,7 @@ export type AdminRoute = z.infer<typeof AdminRoute>;
52
52
  export declare const CapabilityDeclaration: z.ZodObject<{
53
53
  name: z.ZodString;
54
54
  version: z.ZodNullable<z.ZodString>;
55
+ package: z.ZodDefault<z.ZodNullable<z.ZodString>>;
55
56
  adminRoutes: z.ZodArray<z.ZodObject<{
56
57
  method: z.ZodEnum<{
57
58
  DELETE: "DELETE";
@@ -93,6 +94,7 @@ export type CapabilityDeclaration = z.infer<typeof CapabilityDeclaration>;
93
94
  export declare const CapabilityDescriptor: z.ZodCodec<z.ZodObject<{
94
95
  name: z.ZodString;
95
96
  version: z.ZodNullable<z.ZodString>;
97
+ package: z.ZodDefault<z.ZodNullable<z.ZodString>>;
96
98
  adminRoutes: z.ZodArray<z.ZodObject<{
97
99
  method: z.ZodEnum<{
98
100
  DELETE: "DELETE";
@@ -134,6 +136,7 @@ export declare const CapabilityDescriptor: z.ZodCodec<z.ZodObject<{
134
136
  }, z.core.$strip>, z.ZodObject<{
135
137
  name: z.ZodString;
136
138
  version: z.ZodNullable<z.ZodString>;
139
+ package: z.ZodDefault<z.ZodNullable<z.ZodString>>;
137
140
  adminRoutes: z.ZodArray<z.ZodObject<{
138
141
  method: z.ZodEnum<{
139
142
  DELETE: "DELETE";
@@ -190,6 +193,11 @@ export type CapabilityDescriptor = z.infer<typeof CapabilityDescriptor>;
190
193
  * now — so a capability that changed its paths or its scopes reports the change directly, and a schema
191
194
  * version would be a second source of truth to keep in sync with the first. That reasoning is unchanged.
192
195
  *
196
+ * **And `capabilities[].version` travels with the package it belongs to** (#626). A version is only
197
+ * actionable joined against a release feed, a feed is keyed by package name, and a capability name is
198
+ * not one — `controlplane` ships inside `@pithy-sh/core`. Reporting the version alone left every client
199
+ * guessing the key, and the guess failed silently on the most frequently released package there is.
200
+ *
193
201
  * What the manifest does carry is **identity**, not schema, and it carries two of them because they
194
202
  * answer questions neither can answer alone. `version` is Cloudflare's opaque per-deploy id: it says
195
203
  * *exactly which build* is running, which is what forensics needs, what reproduces a report, and what
@@ -213,6 +221,7 @@ export declare const ControlPlaneManifest: z.ZodObject<{
213
221
  capabilities: z.ZodArray<z.ZodCodec<z.ZodObject<{
214
222
  name: z.ZodString;
215
223
  version: z.ZodNullable<z.ZodString>;
224
+ package: z.ZodDefault<z.ZodNullable<z.ZodString>>;
216
225
  adminRoutes: z.ZodArray<z.ZodObject<{
217
226
  method: z.ZodEnum<{
218
227
  DELETE: "DELETE";
@@ -254,6 +263,7 @@ export declare const ControlPlaneManifest: z.ZodObject<{
254
263
  }, z.core.$strip>, z.ZodObject<{
255
264
  name: z.ZodString;
256
265
  version: z.ZodNullable<z.ZodString>;
266
+ package: z.ZodDefault<z.ZodNullable<z.ZodString>>;
257
267
  adminRoutes: z.ZodArray<z.ZodObject<{
258
268
  method: z.ZodEnum<{
259
269
  DELETE: "DELETE";
@@ -1 +1 @@
1
- {"version":3,"file":"adminRoute.d.ts","sourceRoot":"","sources":["../../../src/controlPlane/discovery/adminRoute.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAKxB;;;;;;;;;;;;;;;;;GAiBG;AAEH,+CAA+C;AAC/C,eAAO,MAAM,gBAAgB;;;;;;EAE8B,CAAC;AAC5D,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAEhE,oFAAoF;AACpF,eAAO,MAAM,UAAU;;;;;;;;;;;iBAmBpB,CAAC;AACJ,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC;AAEpD;;;;;;GAMG;AACH,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA2C/B,CAAC;AACJ,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAiD1E,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAiC9B,CAAC;AACJ,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAExE;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAuBkE,CAAC;AACpG,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAExE;;;;;;GAMG;AACH,MAAM,MAAM,wBAAwB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC"}
1
+ {"version":3,"file":"adminRoute.d.ts","sourceRoot":"","sources":["../../../src/controlPlane/discovery/adminRoute.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAKxB;;;;;;;;;;;;;;;;;GAiBG;AAEH,+CAA+C;AAC/C,eAAO,MAAM,gBAAgB;;;;;;EAE8B,CAAC;AAC5D,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAEhE,oFAAoF;AACpF,eAAO,MAAM,UAAU;;;;;;;;;;;iBAmBpB,CAAC;AACJ,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC;AAEpD;;;;;;GAMG;AACH,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAqD/B,CAAC;AACJ,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAiD1E,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAsC9B,CAAC;AACJ,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAExE;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAuBkE,CAAC;AACpG,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAExE;;;;;;GAMG;AACH,MAAM,MAAM,wBAAwB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC"}
@@ -47,12 +47,13 @@ const AdminRoute = z.object({
47
47
  */
48
48
  const CapabilityDeclaration = z.object({
49
49
  name: z.string().min(1).describe("The capability's name — the same token used as its migration namespace and error domain."),
50
- version: z.string().nullable().describe("The npm version of the package supplying this capability, or null where there is none — the adopter's own `app` capability has a name and no package. Reported per capability and never aggregated: the package name is the join key against a release feed, and a project composes some capabilities and not others, so only the intersection of what it composes and what changed is worth reporting."),
50
+ version: z.string().nullable().describe("The npm version of the package supplying this capability, or null where there is none — the adopter's own `app` capability has a name and no package. Reported per capability and never aggregated: a project composes some capabilities and not others, so only the intersection of what it composes and what changed is worth reporting. Joined against a release feed on the `package` beside it, never on `name` — the two are different kinds of thing."),
51
+ package: z.string().nullable().default(null).describe("The npm package supplying this capability — the join key against a release feed — or null where there is none, which is the adopter's own `app` capability and a Worker that predates this field. **Never derivable from `name`:** `controlplane` ships inside `@pithy-sh/core`, so a name-derived `@pithy-sh/<name>` reaches a package that has never been published and the join comes back empty, which reads exactly like up to date. Null together with `version`: a package with no version says nothing joinable, and a version with no package is the reason this field exists."),
51
52
  adminRoutes: z.array(AdminRoute).describe("Every admin route this capability contributes, or empty when it contributes none. Most capabilities are empty: having no management surface is the normal case, and saying so explicitly is what lets a client render a capability it cannot act on."),
52
53
  healthKeys: z.array(HealthSummaryKey).default([]).describe("The closed vocabulary of scalars this capability may report about its own state, or empty when it reports none — including when the Worker predates this field and says nothing at all. Declared alongside the routes so a client can render a key it has never heard of from what the Worker says about it — and so a *withheld* number is visible as a key with no value, rather than as silence."),
53
54
  configKeys: z.array(ManifestConfigKey).default([]).describe("The configured facts this capability states, or empty when it states none — including when the Worker predates this field. Declared alongside the routes, because a fact is what a client needs to *call* one: `POST /billing/entitlements/grant` names a holder and never assumes it, so a client that cannot learn what this project bills can only guess — and a guess writes a row nothing reads."),
54
55
  config: ManifestConfigValues.default({}).describe("What each declared fact resolved to for this deployment. The same answer for every caller, read off the capability's parsed config at assembly: there is no producer here, so no failure state, no per-request cost, and no staleness beyond the manifest itself.")
55
- }).describe("One capability this Worker composes, the version it is at, the admin surface it exposes, the summary it may report, and the configured facts a client must respect to call any of it.");
56
+ }).describe("One capability this Worker composes, the package and version it is at, the admin surface it exposes, the summary it may report, and the configured facts a client must respect to call any of it.");
56
57
  /**
57
58
  * One composed capability's manifest entry **as it goes over the wire**.
58
59
  *
@@ -92,8 +93,9 @@ const CapabilityDescriptor = z.codec(CapabilityDescriptorWire, CapabilityDescrip
92
93
  healthUnavailable
93
94
  })
94
95
  }),
95
- encode: ({ health, healthKeys, configKeys, config, ...declaration }) => ({
96
+ encode: ({ health, healthKeys, configKeys, config, package: suppliedBy, ...declaration }) => ({
96
97
  ...declaration,
98
+ package: suppliedBy ?? null,
97
99
  healthKeys: (healthKeys ?? []).map((key) => ({
98
100
  ...key,
99
101
  nominal: key.nominal ?? null
@@ -111,6 +113,11 @@ const CapabilityDescriptor = z.codec(CapabilityDescriptorWire, CapabilityDescrip
111
113
  * now — so a capability that changed its paths or its scopes reports the change directly, and a schema
112
114
  * version would be a second source of truth to keep in sync with the first. That reasoning is unchanged.
113
115
  *
116
+ * **And `capabilities[].version` travels with the package it belongs to** (#626). A version is only
117
+ * actionable joined against a release feed, a feed is keyed by package name, and a capability name is
118
+ * not one — `controlplane` ships inside `@pithy-sh/core`. Reporting the version alone left every client
119
+ * guessing the key, and the guess failed silently on the most frequently released package there is.
120
+ *
114
121
  * What the manifest does carry is **identity**, not schema, and it carries two of them because they
115
122
  * answer questions neither can answer alone. `version` is Cloudflare's opaque per-deploy id: it says
116
123
  * *exactly which build* is running, which is what forensics needs, what reproduces a report, and what
@@ -78,6 +78,7 @@ export declare function manifestHandler(deps: ControlPlaneHandlerDeps): (c: Cont
78
78
  healthUnavailable: boolean;
79
79
  name: string;
80
80
  version: string | null;
81
+ package: string | null;
81
82
  adminRoutes: {
82
83
  method: "DELETE" | "GET" | "PATCH" | "POST" | "PUT";
83
84
  path: string;
@@ -1 +1 @@
1
- {"version":3,"file":"handlers.d.ts","sourceRoot":"","sources":["../../../src/controlPlane/http/handlers.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AACpC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAGhE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAI3D,OAAO,EAAmC,KAAK,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAC5F,OAAO,KAAK,EAAE,qBAAqB,EAA4B,MAAM,yBAAyB,CAAC;AAC/F,OAAO,EAAE,KAAK,sBAAsB,EAAwB,MAAM,qBAAqB,CAAC;AAUxF,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAuBvF,yGAAyG;AACzG,MAAM,WAAW,uBAAuB;IACtC,yDAAyD;IACzD,MAAM,EAAE,kBAAkB,CAAC;IAC3B,oEAAoE;IACpE,QAAQ,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,KAAK,oBAAoB,CAAC;IAC7D;;;;;;;;OAQG;IACH,oBAAoB,EAAE,MAAM,SAAS,qBAAqB,EAAE,CAAC;IAC7D;;;;;;OAMG;IACH,cAAc,EAAE,MAAM,WAAW,CAAC,MAAM,EAAE,sBAAsB,CAAC,CAAC;IAClE,8DAA8D;IAC9D,GAAG,EAAE,MAAM,IAAI,CAAC;CACjB;AAgED;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,uBAAuB,OAC5C,OAAO,CAAC,YAAY,CAAC;;;;;;kEAUjC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,uBAAuB,OAC1C,OAAO,CAAC,YAAY,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mEAyBvC;AAED,+FAA+F;AAC/F,wBAAgB,eAAe,CAAC,IAAI,EAAE,uBAAuB,OAC1C,OAAO,CAAC,YAAY,CAAC;;;;;;;;;;;;;;mEASvC;AAED;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,uBAAuB,OAC7C,OAAO,CAAC,YAAY,CAAC,QAAQ,kBAAkB;;;;;;;;;;;;;;iBAoDjE;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,uBAAuB,OAC3C,OAAO,CAAC,YAAY,CAAC,UAAU,eAAe,QAAQ,gBAAgB;;;;;;;;;;;;;;mEA8CxF"}
1
+ {"version":3,"file":"handlers.d.ts","sourceRoot":"","sources":["../../../src/controlPlane/http/handlers.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AACpC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAGhE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAI3D,OAAO,EAAmC,KAAK,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAC5F,OAAO,KAAK,EAAE,qBAAqB,EAA4B,MAAM,yBAAyB,CAAC;AAC/F,OAAO,EAAE,KAAK,sBAAsB,EAAwB,MAAM,qBAAqB,CAAC;AAUxF,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAuBvF,yGAAyG;AACzG,MAAM,WAAW,uBAAuB;IACtC,yDAAyD;IACzD,MAAM,EAAE,kBAAkB,CAAC;IAC3B,oEAAoE;IACpE,QAAQ,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,KAAK,oBAAoB,CAAC;IAC7D;;;;;;;;OAQG;IACH,oBAAoB,EAAE,MAAM,SAAS,qBAAqB,EAAE,CAAC;IAC7D;;;;;;OAMG;IACH,cAAc,EAAE,MAAM,WAAW,CAAC,MAAM,EAAE,sBAAsB,CAAC,CAAC;IAClE,8DAA8D;IAC9D,GAAG,EAAE,MAAM,IAAI,CAAC;CACjB;AAgED;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,uBAAuB,OAC5C,OAAO,CAAC,YAAY,CAAC;;;;;;kEAUjC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,uBAAuB,OAC1C,OAAO,CAAC,YAAY,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mEAyBvC;AAED,+FAA+F;AAC/F,wBAAgB,eAAe,CAAC,IAAI,EAAE,uBAAuB,OAC1C,OAAO,CAAC,YAAY,CAAC;;;;;;;;;;;;;;mEASvC;AAED;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,uBAAuB,OAC7C,OAAO,CAAC,YAAY,CAAC,QAAQ,kBAAkB;;;;;;;;;;;;;;iBAoDjE;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,uBAAuB,OAC3C,OAAO,CAAC,YAAY,CAAC,UAAU,eAAe,QAAQ,gBAAgB;;;;;;;;;;;;;;mEA8CxF"}
@@ -1,6 +1,16 @@
1
1
  // SPDX-FileCopyrightText: 2026 Pithy
2
2
  // SPDX-License-Identifier: MIT
3
3
  import { z } from "zod";
4
+ import { type AmbientEnv } from "../env/ambient";
5
+ /**
6
+ * Whether a string is a public hostname: dot-separated DNS labels, at least two, lowercase. No scheme, no
7
+ * port, no trailing dot, and nothing a URL parser decoded into one — `%2e%2e` is `..` once parsed, which is an
8
+ * empty label rather than a host (#643). `localhost` is not one: it has a single label, and it is this machine.
9
+ *
10
+ * The one test for "is this a host" that an origin read from a stamp and an origin typed on a command line
11
+ * both pass, so the two cannot accept different things.
12
+ */
13
+ export declare function isPublicHostname(hostname: string): boolean;
4
14
  /** The environments a domain may be declared for — every managed one, never `dev`. */
5
15
  export declare const DOMAIN_ENVIRONMENTS: ("prod" | "staging")[];
6
16
  /** One environment's public address for one Worker. */
@@ -58,9 +68,32 @@ export interface ResolvedOrigin {
58
68
  origin: string;
59
69
  /** The hostname alone: a route pattern, and what Turnstile binds a widget to. */
60
70
  hostname: string;
61
- /** Whether `domains` declared it. `false` means {@link LOCAL_ORIGIN} was substituted. */
71
+ /**
72
+ * Whether `domains` declared it. `false` means {@link LOCAL_ORIGIN} was substituted, or — for a feature
73
+ * environment only — that the address is the `workers.dev` one provisioning stamped ({@link featureOrigin}).
74
+ */
62
75
  declared: boolean;
63
76
  }
77
+ /**
78
+ * **A feature environment's origin: the `workers.dev` address provisioning stamped, or null (#643).**
79
+ *
80
+ * A feature Worker answers on `https://<script>.<account subdomain>.workers.dev`. Nothing can declare that —
81
+ * `domains` has no feature key, because a branch's hostname is composed from the branch — and the Worker cannot
82
+ * derive it either, because the subdomain is the account's and only the Cloudflare API knows it. So `pithy
83
+ * provision --feature` derives it, through the CLI's one address resolver, and stamps it as the feature
84
+ * stanza's {@link BASE_URL_VAR}. This is the one reader of that stamp.
85
+ *
86
+ * **Only a `workers.dev` https origin is accepted, and that is the guard rather than a tidiness check.** A
87
+ * feature stanza is generated from the Worker's top level, so a `BASE_URL` somebody set by hand up there
88
+ * arrives in the feature too — and it is another environment's origin, most likely production's. Honoring it
89
+ * would send a branch's magic links into production, which is the one thing {@link resolveOrigin} exists to
90
+ * make impossible. A feature's address is `workers.dev` by construction, so anything else is not one and
91
+ * resolves to nothing. No path, no port, no credentials: an origin, or null.
92
+ */
93
+ export declare function featureOrigin(stamped: string | undefined): {
94
+ origin: string;
95
+ hostname: string;
96
+ } | null;
64
97
  /**
65
98
  * **Where is this Worker publicly reachable, in this environment?** — asked once, answered once.
66
99
  *
@@ -86,8 +119,10 @@ export interface ResolvedOrigin {
86
119
  * checks that stored value, so a per-environment issuer would make a connection minted in staging
87
120
  * unverifiable in production. That may be the better isolation, but it is a decision about trust rather
88
121
  * than about reachability, and a helper whose job is "where am I reachable" must not sweep it up.
122
+ *
123
+ * **`stamped` is a feature environment's `BASE_URL`, and is read for nothing else** — see {@link featureOrigin}.
89
124
  */
90
- export declare function resolveOrigin(environment: string | undefined, domains: WorkerDomains | undefined): ResolvedOrigin;
125
+ export declare function resolveOrigin(environment: string | undefined, domains: WorkerDomains | undefined, stamped?: string | undefined): ResolvedOrigin;
91
126
  /**
92
127
  * The one line an adopter writes: this Worker's public origin for the environment it is running in.
93
128
  *
@@ -105,6 +140,11 @@ export declare function resolveOrigin(environment: string | undefined, domains:
105
140
  * `AUTH_BASE_URL`, which is part of why `email` and `payments` were missed for days: the constant read
106
141
  * as auth's private business when it is the Worker's address, and every capability that needs an origin
107
142
  * needs this one.
143
+ *
144
+ * **A feature deployment is answered from its own vars (#643).** `env` is the ambient environment — inside a
145
+ * Worker, its own vars — and it is read for one thing: the `BASE_URL` a feature's provisioning stamped. So the
146
+ * same scaffolded line gives auth's base URL, email's links and payments' return URLs a feature's real
147
+ * `workers.dev` origin, rather than `http://localhost`, with nothing added to the adopter's config.
108
148
  */
109
- export declare function originFor(environment: string | undefined, domains: WorkerDomains | undefined): string;
149
+ export declare function originFor(environment: string | undefined, domains: WorkerDomains | undefined, env?: AmbientEnv): string;
110
150
  //# sourceMappingURL=domains.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"domains.d.ts","sourceRoot":"","sources":["../../src/naming/domains.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAsCxB,sFAAsF;AACtF,eAAO,MAAM,mBAAmB,wBAA8D,CAAC;AAE/F,uDAAuD;AACvD,eAAO,MAAM,YAAY;;;iBA+BrB,CAAC;AACL,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AAExD;;;;;GAKG;AACH,eAAO,MAAM,aAAa;;;;;;;;;iBAOvB,CAAC;AACJ,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAE1D;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,aAAa,GAAG,SAAS,EAAE,WAAW,EAAE,MAAM,GAAG,SAAS,GAAG,YAAY,GAAG,IAAI,CAKlH;AAED,gHAAgH;AAChH,wBAAgB,UAAU,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAEvD;AAED;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,YAAY,qBAAqB,CAAC;AAE/C,yFAAyF;AACzF,MAAM,WAAW,cAAc;IAC7B,qEAAqE;IACrE,MAAM,EAAE,MAAM,CAAC;IACf,iFAAiF;IACjF,QAAQ,EAAE,MAAM,CAAC;IACjB,yFAAyF;IACzF,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,EAAE,OAAO,EAAE,aAAa,GAAG,SAAS,GAAG,cAAc,CAIjH;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,SAAS,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,EAAE,OAAO,EAAE,aAAa,GAAG,SAAS,GAAG,MAAM,CAErG"}
1
+ {"version":3,"file":"domains.d.ts","sourceRoot":"","sources":["../../src/naming/domains.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,KAAK,UAAU,EAAc,MAAM,gBAAgB,CAAC;AAuC7D;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAE1D;AAED,sFAAsF;AACtF,eAAO,MAAM,mBAAmB,wBAA8D,CAAC;AAE/F,uDAAuD;AACvD,eAAO,MAAM,YAAY;;;iBA+BrB,CAAC;AACL,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AAExD;;;;;GAKG;AACH,eAAO,MAAM,aAAa;;;;;;;;;iBAOvB,CAAC;AACJ,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAE1D;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,aAAa,GAAG,SAAS,EAAE,WAAW,EAAE,MAAM,GAAG,SAAS,GAAG,YAAY,GAAG,IAAI,CAKlH;AAED,gHAAgH;AAChH,wBAAgB,UAAU,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAEvD;AAED;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,YAAY,qBAAqB,CAAC;AAE/C,yFAAyF;AACzF,MAAM,WAAW,cAAc;IAC7B,qEAAqE;IACrE,MAAM,EAAE,MAAM,CAAC;IACf,iFAAiF;IACjF,QAAQ,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAetG;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,aAAa,CAC3B,WAAW,EAAE,MAAM,GAAG,SAAS,EAC/B,OAAO,EAAE,aAAa,GAAG,SAAS,EAClC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,GAC3B,cAAc,CAShB;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,SAAS,CACvB,WAAW,EAAE,MAAM,GAAG,SAAS,EAC/B,OAAO,EAAE,aAAa,GAAG,SAAS,EAClC,GAAG,GAAE,UAAyB,GAC7B,MAAM,CAER"}
@@ -1,6 +1,8 @@
1
1
  // SPDX-FileCopyrightText: 2026 Pithy
2
2
  // SPDX-License-Identifier: MIT
3
3
  import { ENVIRONMENTS } from "./environment.js";
4
+ import { BASE_URL_VAR } from "../worker/identity.js";
5
+ import { ambientEnv } from "../env/ambient.js";
4
6
  import { z } from "zod";
5
7
  //#region src/naming/domains.ts
6
8
  /**
@@ -36,6 +38,17 @@ import { z } from "zod";
36
38
  */
37
39
  /** A hostname a Worker answers on. No scheme, no path, no port — wrangler's `routes` pattern is a host. */
38
40
  const HOSTNAME_PATTERN = /^(?!-)[a-z0-9-]{1,63}(?<!-)(\.(?!-)[a-z0-9-]{1,63}(?<!-))+$/;
41
+ /**
42
+ * Whether a string is a public hostname: dot-separated DNS labels, at least two, lowercase. No scheme, no
43
+ * port, no trailing dot, and nothing a URL parser decoded into one — `%2e%2e` is `..` once parsed, which is an
44
+ * empty label rather than a host (#643). `localhost` is not one: it has a single label, and it is this machine.
45
+ *
46
+ * The one test for "is this a host" that an origin read from a stamp and an origin typed on a command line
47
+ * both pass, so the two cannot accept different things.
48
+ */
49
+ function isPublicHostname(hostname) {
50
+ return hostname.length <= 253 && HOSTNAME_PATTERN.test(hostname);
51
+ }
39
52
  /** The environments a domain may be declared for — every managed one, never `dev`. */
40
53
  const DOMAIN_ENVIRONMENTS = ENVIRONMENTS.filter((environment) => environment !== "dev");
41
54
  /** One environment's public address for one Worker. */
@@ -95,6 +108,39 @@ function baseUrlFor(domain) {
95
108
  */
96
109
  const LOCAL_ORIGIN = "http://localhost";
97
110
  /**
111
+ * **A feature environment's origin: the `workers.dev` address provisioning stamped, or null (#643).**
112
+ *
113
+ * A feature Worker answers on `https://<script>.<account subdomain>.workers.dev`. Nothing can declare that —
114
+ * `domains` has no feature key, because a branch's hostname is composed from the branch — and the Worker cannot
115
+ * derive it either, because the subdomain is the account's and only the Cloudflare API knows it. So `pithy
116
+ * provision --feature` derives it, through the CLI's one address resolver, and stamps it as the feature
117
+ * stanza's {@link BASE_URL_VAR}. This is the one reader of that stamp.
118
+ *
119
+ * **Only a `workers.dev` https origin is accepted, and that is the guard rather than a tidiness check.** A
120
+ * feature stanza is generated from the Worker's top level, so a `BASE_URL` somebody set by hand up there
121
+ * arrives in the feature too — and it is another environment's origin, most likely production's. Honoring it
122
+ * would send a branch's magic links into production, which is the one thing {@link resolveOrigin} exists to
123
+ * make impossible. A feature's address is `workers.dev` by construction, so anything else is not one and
124
+ * resolves to nothing. No path, no port, no credentials: an origin, or null.
125
+ */
126
+ function featureOrigin(stamped) {
127
+ if (!stamped) return null;
128
+ let url;
129
+ try {
130
+ url = new URL(stamped);
131
+ } catch {
132
+ return null;
133
+ }
134
+ if (url.protocol !== "https:" || url.port !== "" || url.username !== "" || url.password !== "") return null;
135
+ if (url.pathname !== "/" || url.search !== "" || url.hash !== "") return null;
136
+ if (!url.hostname.endsWith(".workers.dev") || url.hostname.split(".").length < 4) return null;
137
+ if (!isPublicHostname(url.hostname)) return null;
138
+ return {
139
+ origin: url.origin,
140
+ hostname: url.hostname
141
+ };
142
+ }
143
+ /**
98
144
  * **Where is this Worker publicly reachable, in this environment?** — asked once, answered once.
99
145
  *
100
146
  * The two halves already existed ({@link domainFor} and {@link baseUrlFor}) and nothing composed them,
@@ -119,14 +165,21 @@ const LOCAL_ORIGIN = "http://localhost";
119
165
  * checks that stored value, so a per-environment issuer would make a connection minted in staging
120
166
  * unverifiable in production. That may be the better isolation, but it is a decision about trust rather
121
167
  * than about reachability, and a helper whose job is "where am I reachable" must not sweep it up.
168
+ *
169
+ * **`stamped` is a feature environment's `BASE_URL`, and is read for nothing else** — see {@link featureOrigin}.
122
170
  */
123
- function resolveOrigin(environment, domains) {
171
+ function resolveOrigin(environment, domains, stamped) {
124
172
  const domain = domainFor(domains, environment);
125
173
  if (domain) return {
126
174
  origin: baseUrlFor(domain),
127
175
  hostname: domain.pattern,
128
176
  declared: true
129
177
  };
178
+ const feature = environment === "feature" ? featureOrigin(stamped) : null;
179
+ if (feature) return {
180
+ ...feature,
181
+ declared: false
182
+ };
130
183
  return {
131
184
  origin: LOCAL_ORIGIN,
132
185
  hostname: "localhost",
@@ -150,9 +203,14 @@ function resolveOrigin(environment, domains) {
150
203
  * `AUTH_BASE_URL`, which is part of why `email` and `payments` were missed for days: the constant read
151
204
  * as auth's private business when it is the Worker's address, and every capability that needs an origin
152
205
  * needs this one.
206
+ *
207
+ * **A feature deployment is answered from its own vars (#643).** `env` is the ambient environment — inside a
208
+ * Worker, its own vars — and it is read for one thing: the `BASE_URL` a feature's provisioning stamped. So the
209
+ * same scaffolded line gives auth's base URL, email's links and payments' return URLs a feature's real
210
+ * `workers.dev` origin, rather than `http://localhost`, with nothing added to the adopter's config.
153
211
  */
154
- function originFor(environment, domains) {
155
- return resolveOrigin(environment, domains).origin;
212
+ function originFor(environment, domains, env = ambientEnv()) {
213
+ return resolveOrigin(environment, domains, env[BASE_URL_VAR]).origin;
156
214
  }
157
215
  //#endregion
158
- export { DOMAIN_ENVIRONMENTS, LOCAL_ORIGIN, WorkerDomain, WorkerDomains, baseUrlFor, domainFor, originFor, resolveOrigin };
216
+ export { DOMAIN_ENVIRONMENTS, LOCAL_ORIGIN, WorkerDomain, WorkerDomains, baseUrlFor, domainFor, featureOrigin, isPublicHostname, originFor, resolveOrigin };
@@ -1,8 +1,15 @@
1
1
  // SPDX-FileCopyrightText: 2026 Pithy
2
2
  // SPDX-License-Identifier: MIT
3
3
  /**
4
- * The names a feature environment provisions under: `<project>-f<issue>-<slug>-<binding>-<kind>` for a
5
- * resource, `<project>-f<issue>-<slug>-<worker>` for a Worker script.
4
+ * The names a feature environment provisions under: `<project>-f<issue>-<slug>--<binding>-<kind>` for a
5
+ * resource, `<project>-f<issue>-<slug>--<worker>` for a Worker script.
6
+ *
7
+ * **Two hyphens after the slug, and a project with no `f<digits>` segment (#643).** Together they make every
8
+ * feature name parse back to exactly one (project, issue, slug): the double hyphen is something no other name
9
+ * Pithy composes can hold ({@link FEATURE_TAIL_SEPARATOR}), and the first `f<digits>` segment is the issue's,
10
+ * because the project may carry none ({@link assertFeatureProject}). Before them, project `acme-f12-x`'s `prod`
11
+ * and project `acme`'s branch `feature/12-x-prod` composed every name between them, and a branch whose slug
12
+ * was a hyphen-prefix of a sibling's could compose the sibling's database. See {@link parseFeatureName}.
6
13
  *
7
14
  * **No environment segment, and no Worker segment.** A feature *is* an environment, and two Workers that
8
15
  * declare the same binding are meant to share one resource — so the binding name, not the topology, is
@@ -10,9 +17,12 @@
10
17
  *
11
18
  * Every name here is **derived and recomputed**, never stored: `provision` and `destroy` both compute the
12
19
  * same string from `(identity, binding, kind)`, which is what lets teardown reconcile exactly rather than
13
- * scan a prefix a hyphenated sibling slug could ambiguously match. That is also why these names
14
- * **truncate** where a capability's Workflow refuses: a feature name addresses nothing that outlives the
15
- * feature, and failing a CI run because a branch slug was long would be the worse failure.
20
+ * scan a prefix a hyphenated sibling slug could ambiguously match.
21
+ *
22
+ * **Never truncated (#643).** These names used to fit a long slug with a hash, on the reasoning that a feature
23
+ * name addresses nothing that outlives the feature. But a short hash is a slug some sibling branch can have
24
+ * whole, so a fitted name was a name two features could compose. A slug that does not fit is refused instead,
25
+ * naming the maximum ({@link assertFeatureSlugFits}), and every feature name is exactly its feature's.
16
26
  *
17
27
  * It lives in `core`, beside the rest of the naming rule, so the CLI and the facade compose one
18
28
  * implementation rather than two that drift.
@@ -31,7 +41,92 @@ export interface FeatureIdentity {
31
41
  slug: string;
32
42
  }
33
43
  /**
34
- * The full Cloudflare resource name for a feature's binding — `<project>-f<issue>-<slug>-<binding>-<kind>`.
44
+ * **The issue as a name carries it: its digits, without leading zeros (#643).** `feature/0643-foo` and
45
+ * `feature/643-foo` are one issue, so they are one feature and compose one set of names. Read as strings they
46
+ * were two features, and their rate-limit namespaces collided every time. `0` stays `0`.
47
+ */
48
+ export declare function canonicalIssue(issue: string): string;
49
+ /**
50
+ * The `f<digits>` segment a project name carries, or `null` — the shape a feature's issue takes (#643).
51
+ *
52
+ * Read after kebabbing, because the kebab is what reaches an account.
53
+ */
54
+ export declare function featureMarkerInProjectName(project: string): string | null;
55
+ /**
56
+ * **Refuse features for a project whose name carries an `f<digits>` segment (#643).**
57
+ *
58
+ * A feature name is read left to right: the project, then the first `f<digits>` segment, which is the issue.
59
+ * A project named `acme-f12-x` puts that segment inside itself, and then its feature 3 on `y` and project `acme`'s
60
+ * feature 12 on `x-f3-y` are one string. Environment names already obey this rule ({@link isFeatureMarker}), so
61
+ * the project obeys it too, where the two shapes meet.
62
+ *
63
+ * **Refused here, not at `requireProjectName`.** Such a project's staging and prod are untouched: no name they
64
+ * compose holds {@link FEATURE_TAIL_SEPARATOR}, so no feature anywhere can compose one of theirs. Refusing the
65
+ * name everywhere would break every command of a project that is otherwise sound. `pithy init` refuses a new one,
66
+ * and `pithy doctor` names the segment an existing one carries. Nothing is renamed.
67
+ */
68
+ export declare function assertFeatureProject(project: string): void;
69
+ /** A feature name, read back: which project, issue and slug it belongs to, and what it names. */
70
+ export interface ParsedFeatureName {
71
+ /** The project segment, kebabbed. */
72
+ project: string;
73
+ /** The issue, canonical. */
74
+ issue: string;
75
+ /** The slug segment: the whole slug, since no feature name is truncated. */
76
+ slug: string;
77
+ /** Everything after {@link FEATURE_TAIL_SEPARATOR}: the binding and kind, Worker, Workflow or entry. */
78
+ thing: string;
79
+ }
80
+ /**
81
+ * **Read a feature name back into its owner, or `null` when it is not one (#643).**
82
+ *
83
+ * Exact, because the shape leaves one reading: a feature name holds {@link FEATURE_TAIL_SEPARATOR} exactly once,
84
+ * everything before it is `<project>-f<issue>-<slug>`, and the first `f<digits>` segment there is the issue,
85
+ * since a project may carry none. Nothing else Pithy composes holds the separator at all.
86
+ */
87
+ export declare function parseFeatureName(name: string): ParsedFeatureName | null;
88
+ /**
89
+ * **Is this name one this feature composes?** The ownership check the isolation gates ask (#643).
90
+ *
91
+ * Parsed, never prefix-matched, and exact: the name's project, issue and slug are this feature's, character for
92
+ * character. `<project>-f<issue>-` is shared by every branch of one issue, so a prefix match reached siblings; and
93
+ * a fitted form of the slug, which this used to accept, is a short hash some sibling can have as its whole slug
94
+ * (`feature/12-login` owned `feature/12-c`). No feature name is fitted any more ({@link composeFeatureName}), so
95
+ * there is nothing but the slug itself to accept.
96
+ */
97
+ export declare function isFeatureOwnedName(identity: FeatureIdentity, name: string): boolean;
98
+ /** A feature's project and issue: everything its names are derived from but the slug. */
99
+ export type FeatureHead = Pick<FeatureIdentity, "project" | "issue">;
100
+ /**
101
+ * **The longest slug one feature name leaves room for** — `limit` less `<project>-f<issue>-`, the
102
+ * {@link FEATURE_TAIL_SEPARATOR}, and `thing`, which is everything after it. Zero or less when `thing` leaves none.
103
+ */
104
+ export declare function featureSlugRoom(parts: FeatureHead, thing: string, limit: number): number;
105
+ /** One kind of name a feature composes, as far as its slug budget reads it. */
106
+ export interface FeatureNameShape {
107
+ /** How an error names the namespace, e.g. `a Workflow name`. */
108
+ label: string;
109
+ /** The longest name the namespace takes. */
110
+ limit: number;
111
+ /** Everything after {@link FEATURE_TAIL_SEPARATOR}, e.g. `email-suppressions-d1`. */
112
+ thing: string;
113
+ }
114
+ /** The longest slug every one of `shapes` leaves room for, for this project and issue, and the shape that sets it. */
115
+ export declare function tightestFeatureShape(parts: FeatureHead, shapes: readonly FeatureNameShape[]): {
116
+ max: number;
117
+ shape: FeatureNameShape | null;
118
+ };
119
+ /** The longest slug a feature of this project and issue may have, across every name it composes. */
120
+ export declare function maxFeatureSlug(parts: FeatureHead, shapes: readonly FeatureNameShape[]): number;
121
+ /**
122
+ * **Refuse a branch whose slug cannot fit every name its feature composes (#643).** The one refusal
123
+ * `pithy feature create` and `pithy provision --feature` make, before a worktree or a resource exists, with the
124
+ * project's maximum in the message. {@link composeFeatureName} refuses the same slug one name at a time; this says
125
+ * it once, for all of them.
126
+ */
127
+ export declare function assertFeatureSlugFits(identity: FeatureIdentity, shapes: readonly FeatureNameShape[]): void;
128
+ /**
129
+ * The full Cloudflare resource name for a feature's binding — `<project>-f<issue>-<slug>--<binding>-<kind>`.
35
130
  *
36
131
  * Held to **R2's 63**, the strictest of the three kinds a feature provisions, so one shape is legal for
37
132
  * all of them: lowercase, hyphenated, alphanumeric at both ends. A D1 or KV name could be longer, but a
@@ -40,7 +135,7 @@ export interface FeatureIdentity {
40
135
  export declare function featureResourceName(identity: FeatureIdentity, binding: string, kind: FeatureResourceKind): string;
41
136
  /**
42
137
  * The CF Secrets Store entry name holding one of a feature's **environment-scoped** secrets —
43
- * `<project>-f<issue>-<slug>-<secret>`.
138
+ * `<project>-f<issue>-<slug>--<secret>`.
44
139
  *
45
140
  * A Cloudflare account has one Secrets Store, flat and unpartitionable, so the entry name is the only
46
141
  * partition there is. A feature therefore needs its own names for the same reason it needs its own
@@ -50,12 +145,12 @@ export declare function featureResourceName(identity: FeatureIdentity, binding:
50
145
  * no documented Cloudflare cap, and holding it to the strictest kind's would hash
51
146
  * `secrets-encryption-keys` down to nothing for no reason.
52
147
  *
53
- * A `global` secret is not named here at all: it is one account-level value every environment binds,
54
- * so a feature binds the project's `<project>-global-<secret>` rather than minting a second copy.
148
+ * A `global` secret is named here too since #643: a feature shares nothing with any other environment, so
149
+ * its entry for a global secret is its own rather than the project's `<project>-global-<secret>`.
55
150
  */
56
151
  export declare function featureSecretEntryName(identity: FeatureIdentity, secret: string): string;
57
152
  /**
58
- * The Worker **script name** for one of a feature's workers — `<project>-f<issue>-<slug>-<worker>`.
153
+ * The Worker **script name** for one of a feature's workers — `<project>-f<issue>-<slug>--<worker>`.
59
154
  *
60
155
  * This is the name the feature's Workers deploy under, and therefore the name a sibling's `service`
61
156
  * binding must target, so RPC inside a feature environment reaches that feature's deployment rather than
@@ -67,10 +162,30 @@ export declare function featureSecretEntryName(identity: FeatureIdentity, secret
67
162
  * has a charset rule and no length rule, so nothing upstream was going to stop it either.
68
163
  *
69
164
  * **`app` is the `apps/<app>` directory, never the deploy name.** A scaffolded Worker deploys as
70
- * `<project>-<app>`, and handing that here composed `<project>-f<issue>-<slug>-<project>-<app>` — the
165
+ * `<project>-<app>`, and handing that here composed `<project>-f<issue>-<slug>--<project>-<app>` — the
71
166
  * project twice, spent out of the budget above (#587). This function takes a string and cannot tell the
72
167
  * two apart, so the provisioning path reaches it only through `featureScope`, which is handed both names
73
168
  * and picks.
74
169
  */
75
170
  export declare function featureWorkerName(identity: FeatureIdentity, app: string): string;
171
+ /**
172
+ * The deployed name of one of a feature's **Workflows** — `<project>-f<issue>-<slug>--<capability>-<job>` (#643).
173
+ *
174
+ * A Workflow name is account-wide, like a Worker's, so a feature's email host cannot run
175
+ * `<project>-feature-email-send`: every open branch would deploy the same Workflow over every other's. It takes
176
+ * the feature's head instead, exactly as its host Worker does ({@link featureWorkerName}), so the Worker and the
177
+ * Workflows it hosts are recognizably one feature's and teardown recomputes both from the identity.
178
+ *
179
+ * Held to the **Workflow** limit ({@link NAMESPACE_LIMITS.workflow}), and fitted the way every feature name is:
180
+ * the slug gives way first, then the `<capability>-<job>` tail, deterministically.
181
+ */
182
+ export declare function featureWorkflowName(identity: FeatureIdentity, capability: string, job: string): string;
183
+ /**
184
+ * The name of one of a feature's **Vectorize indexes** — `<project>-f<issue>-<slug>--<thing>` (#643).
185
+ *
186
+ * An index name is account-wide, so a feature's vector host cannot bind `<project>-feature-vector-<index>`: every
187
+ * open branch would read and write one index. Held to the Vectorize limit and fitted the way every feature name
188
+ * is, so teardown recomputes it from the identity.
189
+ */
190
+ export declare function featureVectorizeIndexName(identity: FeatureIdentity, thing: string): string;
76
191
  //# sourceMappingURL=feature.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"feature.d.ts","sourceRoot":"","sources":["../../src/naming/feature.ts"],"names":[],"mappings":"AAQA;;;;;;;;;;;;;;;;GAgBG;AAEH,iHAAiH;AACjH,MAAM,MAAM,mBAAmB,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAErD,6EAA6E;AAC7E,eAAO,MAAM,sBAAsB,EAAE,SAAS,mBAAmB,EAAuB,CAAC;AAEzF,6EAA6E;AAC7E,MAAM,WAAW,eAAe;IAC9B,uEAAuE;IACvE,OAAO,EAAE,MAAM,CAAC;IAChB,6FAA6F;IAC7F,KAAK,EAAE,MAAM,CAAC;IACd,uEAAuE;IACvE,IAAI,EAAE,MAAM,CAAC;CACd;AAkED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,mBAAmB,GAAG,MAAM,CAQjH;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAQxF;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,eAAe,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAEhF"}
1
+ {"version":3,"file":"feature.d.ts","sourceRoot":"","sources":["../../src/naming/feature.ts"],"names":[],"mappings":"AAQA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,iHAAiH;AACjH,MAAM,MAAM,mBAAmB,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAErD,6EAA6E;AAC7E,eAAO,MAAM,sBAAsB,EAAE,SAAS,mBAAmB,EAAuB,CAAC;AAEzF,6EAA6E;AAC7E,MAAM,WAAW,eAAe;IAC9B,uEAAuE;IACvE,OAAO,EAAE,MAAM,CAAC;IAChB,6FAA6F;IAC7F,KAAK,EAAE,MAAM,CAAC;IACd,uEAAuE;IACvE,IAAI,EAAE,MAAM,CAAC;CACd;AAmBD;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAGpD;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAEzE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAQ1D;AASD,iGAAiG;AACjG,MAAM,WAAW,iBAAiB;IAChC,qCAAqC;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,4BAA4B;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,4EAA4E;IAC5E,IAAI,EAAE,MAAM,CAAC;IACb,wGAAwG;IACxG,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,GAAG,IAAI,CAcvE;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,eAAe,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAQnF;AAED,yFAAyF;AACzF,MAAM,MAAM,WAAW,GAAG,IAAI,CAAC,eAAe,EAAE,SAAS,GAAG,OAAO,CAAC,CAAC;AAErE;;;GAGG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAExF;AAsDD,+EAA+E;AAC/E,MAAM,WAAW,gBAAgB;IAC/B,gEAAgE;IAChE,KAAK,EAAE,MAAM,CAAC;IACd,4CAA4C;IAC5C,KAAK,EAAE,MAAM,CAAC;IACd,qFAAqF;IACrF,KAAK,EAAE,MAAM,CAAC;CACf;AAED,sHAAsH;AACtH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,WAAW,EAClB,MAAM,EAAE,SAAS,gBAAgB,EAAE,GAClC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,gBAAgB,GAAG,IAAI,CAAA;CAAE,CAWjD;AAED,oGAAoG;AACpG,wBAAgB,cAAc,CAAC,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,SAAS,gBAAgB,EAAE,GAAG,MAAM,CAE9F;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,eAAe,EAAE,MAAM,EAAE,SAAS,gBAAgB,EAAE,GAAG,IAAI,CAkB1G;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,mBAAmB,GAAG,MAAM,CASjH;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CASxF;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,eAAe,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAShF;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAStG;AAED;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,eAAe,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAS1F"}