@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
@@ -2,6 +2,7 @@
2
2
  // SPDX-License-Identifier: MIT
3
3
 
4
4
  import { InternalError } from "../error/pithyError";
5
+ import type { FeatureIdentity } from "../naming/feature";
5
6
  import { ENVIRONMENT_VAR, PROJECT_VAR } from "../worker/identity";
6
7
  import { type WorkflowHostNameParts, workflowHostName, workflowScriptName } from "./naming";
7
8
  import type { WorkflowRegistry } from "./spec";
@@ -118,6 +119,12 @@ export interface WorkflowHostParams {
118
119
  capability: string;
119
120
  /** The target environment (`dev` | `staging` | `production`). */
120
121
  env: string;
122
+ /**
123
+ * The feature this host serves, when it serves one (#643). Its name then takes the feature's head —
124
+ * `<project>-f<issue>-<slug>-<capability>` — as the Workflows derived with the same parts do; `env` still
125
+ * says `feature`, and that is what the host's `ENVIRONMENT` var is stamped with.
126
+ */
127
+ feature?: FeatureIdentity;
121
128
  /** D1 binding name → database id. An unlisted binding keeps the template's id. */
122
129
  databaseIds?: Record<string, string>;
123
130
  /**
@@ -195,7 +202,12 @@ export function resolveWorkflowHost(template: WorkflowHostTemplate, params: Work
195
202
  const resolved = structuredClone(template);
196
203
  const remote = new Set(params.remoteBindings ?? []);
197
204
 
198
- resolved.name = workflowHostName({ project: params.project, capability: params.capability, env: params.env });
205
+ resolved.name = workflowHostName({
206
+ project: params.project,
207
+ capability: params.capability,
208
+ env: params.env,
209
+ ...(params.feature ? { feature: params.feature } : {}),
210
+ });
199
211
 
200
212
  if (resolved.d1_databases) {
201
213
  resolved.d1_databases = resolved.d1_databases.map((entry) => ({
@@ -293,7 +305,7 @@ export function hostWorkflowsFor(
293
305
  registry: WorkflowRegistry,
294
306
  parts: WorkflowHostNameParts,
295
307
  ): { workflows: HostWorkflowBinding[]; crons: string[] } {
296
- const { project, capability, env } = parts;
308
+ const { project, capability, env, feature } = parts;
297
309
  const owned = Object.values(registry).filter((entry) => entry.capability === capability);
298
310
  const workflows: HostWorkflowBinding[] = [];
299
311
  const crons: string[] = [];
@@ -307,7 +319,7 @@ export function hostWorkflowsFor(
307
319
  }
308
320
  workflows.push({
309
321
  binding: entry.spec.binding,
310
- name: workflowScriptName({ project, capability, job: entry.job, env }),
322
+ name: workflowScriptName({ project, capability, job: entry.job, env, ...(feature ? { feature } : {}) }),
311
323
  class_name: entry.spec.className,
312
324
  });
313
325
  if (entry.spec.schedule) crons.push(entry.spec.schedule);
@@ -2,6 +2,7 @@
2
2
  // SPDX-License-Identifier: MIT
3
3
 
4
4
  import { InternalError } from "../error/pithyError";
5
+ import { type FeatureIdentity, featureWorkerName, featureWorkflowName } from "../naming/feature";
5
6
  import { MAX_CAPABILITY_JOB, NAMESPACE_LIMITS } from "../naming/limits";
6
7
  import { assertValidProjectName, kebab } from "../naming/resource";
7
8
  import { NAME_SEGMENT } from "../naming/segment";
@@ -109,6 +110,21 @@ function head(project: string, env: string): string {
109
110
  return `${kebab(project)}-${env}`;
110
111
  }
111
112
 
113
+ /**
114
+ * The feature a name is composed for, held to the project the parts name — two projects in one set of parts is
115
+ * a name composed for somebody else's feature.
116
+ */
117
+ function featureOf(parts: WorkflowHostNameParts): FeatureIdentity {
118
+ const feature = parts.feature as FeatureIdentity;
119
+ if (feature.project !== parts.project) {
120
+ throw new InternalError({
121
+ message: "A feature's Workflow name was asked for with two different projects.",
122
+ detail: `parts name project "${parts.project}" and a feature of project "${feature.project}".`,
123
+ });
124
+ }
125
+ return feature;
126
+ }
127
+
112
128
  /** The identity of a capability's deployment in one environment. */
113
129
  export interface WorkflowHostNameParts {
114
130
  /** The project name — the root `pithy.config.ts` `name`, resolved by `requireProjectName` and never guessed. */
@@ -117,6 +133,13 @@ export interface WorkflowHostNameParts {
117
133
  capability: string;
118
134
  /** The deployment environment (e.g. `staging`). */
119
135
  env: string;
136
+ /**
137
+ * **The feature this deployment is, when it is one (#643).** A feature's host Worker and Workflows take the
138
+ * feature's head — `<project>-f<issue>-<slug>-…` — not `<project>-feature-…`, which every open branch would
139
+ * share: Worker and Workflow names are account-wide, so a second branch's deploy would replace the first's.
140
+ * `env` stays `feature`, which is what the host's `ENVIRONMENT` var says; only the names change.
141
+ */
142
+ feature?: FeatureIdentity;
120
143
  }
121
144
 
122
145
  /** The identity of one durable job's Workflow in one environment. */
@@ -139,6 +162,7 @@ export function workflowScriptName(parts: WorkflowScriptNameParts): string {
139
162
  assertSegment(parts.capability, "capability");
140
163
  assertSegment(parts.job, "job");
141
164
  assertCapabilityJob(parts.capability, parts.job);
165
+ if (parts.feature) return featureWorkflowName(featureOf(parts), parts.capability, parts.job);
142
166
  return assertLength(`${head(parts.project, parts.env)}-${parts.capability}-${parts.job}`, MAX_WORKFLOW_NAME_BYTES);
143
167
  }
144
168
 
@@ -151,6 +175,7 @@ export function workflowScriptName(parts: WorkflowScriptNameParts): string {
151
175
  */
152
176
  export function workflowHostName(parts: WorkflowHostNameParts): string {
153
177
  assertSegment(parts.capability, "capability");
178
+ if (parts.feature) return featureWorkerName(featureOf(parts), parts.capability);
154
179
  return assertLength(`${head(parts.project, parts.env)}-${parts.capability}`, MAX_WORKER_NAME_BYTES);
155
180
  }
156
181