@ankhorage/infra 7.2.11 → 7.3.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 (35) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +2 -1
  3. package/dist/features/environment-lifecycle/application/use-cases/prepareInfraOperationAsync.d.ts.map +1 -1
  4. package/dist/features/environment-lifecycle/application/use-cases/prepareInfraOperationAsync.js +9 -1
  5. package/dist/features/environment-lifecycle/application/use-cases/prepareInfraOperationAsync.js.map +1 -1
  6. package/dist/features/environment-lifecycle/application/use-cases/upInfraEnvironmentAsync.d.ts.map +1 -1
  7. package/dist/features/environment-lifecycle/application/use-cases/upInfraEnvironmentAsync.js +13 -5
  8. package/dist/features/environment-lifecycle/application/use-cases/upInfraEnvironmentAsync.js.map +1 -1
  9. package/dist/features/environment-lifecycle/domain/resolveInfraWorkloadApiComposition.d.ts +5 -0
  10. package/dist/features/environment-lifecycle/domain/resolveInfraWorkloadApiComposition.d.ts.map +1 -0
  11. package/dist/features/environment-lifecycle/domain/resolveInfraWorkloadApiComposition.js +55 -0
  12. package/dist/features/environment-lifecycle/domain/resolveInfraWorkloadApiComposition.js.map +1 -0
  13. package/dist/index.d.ts +2 -0
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +1 -0
  16. package/dist/index.js.map +1 -1
  17. package/dist/types/infraWorkloadApiComposition.d.ts +6 -0
  18. package/dist/types/infraWorkloadApiComposition.d.ts.map +1 -0
  19. package/dist/types/infraWorkloadApiComposition.js +2 -0
  20. package/dist/types/infraWorkloadApiComposition.js.map +1 -0
  21. package/package.json +14 -14
  22. package/src/environmentLifecycle.test.ts +4 -4
  23. package/src/features/environment-lifecycle/application/use-cases/prepareInfraOperationAsync.ts +11 -1
  24. package/src/features/environment-lifecycle/application/use-cases/upInfraEnvironmentAsync.ts +14 -4
  25. package/src/features/environment-lifecycle/domain/resolveInfraWorkloadApiComposition.ts +75 -0
  26. package/src/hetznerK3sProduction.test.ts +3 -3
  27. package/src/hetznerK3sSupabaseR2.test.ts +9 -5
  28. package/src/index.test.ts +1 -0
  29. package/src/index.ts +2 -0
  30. package/src/infraOperations.test.ts +27 -0
  31. package/src/infraUp.test.ts +35 -4
  32. package/src/localK3sSupabase.test.ts +3 -3
  33. package/src/productionServerRuntimeEnvironment.integration.test.ts +2 -2
  34. package/src/resolveInfraWorkloadApiComposition.test.ts +87 -0
  35. package/src/types/infraWorkloadApiComposition.ts +5 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @ankhorage/infra
2
2
 
3
+ ## 7.3.0
4
+
5
+ ### Minor Changes
6
+
7
+ - b182936: Validate declarative internal API-to-workload assignments before planning or infrastructure mutation.
8
+
9
+ ## 7.2.12
10
+
11
+ ### Patch Changes
12
+
13
+ - e2ec6f6: Update dependencies: `@ankhorage/contracts`, `@ankhorage/utility`.
14
+
3
15
  ## 7.2.11
4
16
 
5
17
  ### Patch Changes
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
 
4
4
  # INFRA
5
5
 
6
- ![license: MIT](././paradox/badges/license.svg) ![npm: v7.2.11](././paradox/badges/npm.svg) ![runtime: bun](././paradox/badges/runtime.svg) ![typescript: strict](././paradox/badges/typescript.svg) ![eslint: checked](././paradox/badges/eslint.svg) ![prettier: checked](././paradox/badges/prettier.svg) ![build: checked](././paradox/badges/build.svg) ![tests: checked](././paradox/badges/tests.svg) ![paradox: canonical](././paradox/badges/docs.svg)
6
+ ![license: MIT](././paradox/badges/license.svg) ![npm: v7.3.0](././paradox/badges/npm.svg) ![runtime: bun](././paradox/badges/runtime.svg) ![typescript: strict](././paradox/badges/typescript.svg) ![eslint: checked](././paradox/badges/eslint.svg) ![prettier: checked](././paradox/badges/prettier.svg) ![build: checked](././paradox/badges/build.svg) ![tests: checked](././paradox/badges/tests.svg) ![paradox: canonical](././paradox/badges/docs.svg)
7
7
 
8
8
  Executable infra provider and standalone CLI for Ankhorage project workflows.
9
9
 
@@ -43,3 +43,4 @@ import { createInfraRuntimeProvider } from '@ankhorage/infra/cli';
43
43
  - [getInfraEnvironmentOutputs sequence](././paradox/diagrams/sequences/get-infra-environment-outputs.mmd)
44
44
  - [orderInfraPlanActions sequence](././paradox/diagrams/sequences/order-infra-plan-actions.mmd)
45
45
  - [resolveInfraEnvironment sequence](././paradox/diagrams/sequences/resolve-infra-environment.mmd)
46
+ - [resolveInfraWorkloadApiComposition sequence](././paradox/diagrams/sequences/resolve-infra-workload-api-composition.mmd)
@@ -1 +1 @@
1
- {"version":3,"file":"prepareInfraOperationAsync.d.ts","sourceRoot":"","sources":["../../../../../src/features/environment-lifecycle/application/use-cases/prepareInfraOperationAsync.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAmB,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAE/E,OAAO,KAAK,EACV,qBAAqB,EACrB,8BAA8B,EAC9B,sBAAsB,EACvB,MAAM,yCAAyC,CAAC;AAQjD,iGAAiG;AACjG,wBAAsB,0BAA0B,CAC9C,OAAO,EAAE,qBAAqB,EAC9B,SAAS,EAAE,UAAU,GAAG,MAAM,GAAG,UAAU,GAAG,QAAQ,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,EACvF,YAAY,EAAE,8BAA8B,GAC3C,OAAO,CAAC,WAAW,CAAC,sBAAsB,CAAC,CAAC,CA6E9C"}
1
+ {"version":3,"file":"prepareInfraOperationAsync.d.ts","sourceRoot":"","sources":["../../../../../src/features/environment-lifecycle/application/use-cases/prepareInfraOperationAsync.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAmB,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAE/E,OAAO,KAAK,EACV,qBAAqB,EACrB,8BAA8B,EAC9B,sBAAsB,EACvB,MAAM,yCAAyC,CAAC;AASjD,iGAAiG;AACjG,wBAAsB,0BAA0B,CAC9C,OAAO,EAAE,qBAAqB,EAC9B,SAAS,EAAE,UAAU,GAAG,MAAM,GAAG,UAAU,GAAG,QAAQ,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,EACvF,YAAY,EAAE,8BAA8B,GAC3C,OAAO,CAAC,WAAW,CAAC,sBAAsB,CAAC,CAAC,CAsF9C"}
@@ -1,4 +1,5 @@
1
1
  import { resolveInfraEnvironment } from '../../domain/resolveInfraEnvironment.js';
2
+ import { resolveInfraWorkloadApiComposition } from '../../domain/resolveInfraWorkloadApiComposition.js';
2
3
  import { appendInfraFailure } from '../../utils/appendInfraFailure.js';
3
4
  import { createInfraExecutionContext } from '../../utils/createInfraExecutionContext.js';
4
5
  import { collectInfraWorkloadsAsync } from './collectInfraWorkloadsAsync.js';
@@ -43,6 +44,13 @@ export async function prepareInfraOperationAsync(request, operation, dependencie
43
44
  if (!workloads.ok)
44
45
  return appendInfraFailure(diagnostics, workloads);
45
46
  diagnostics.push(...workloads.diagnostics);
47
+ const allWorkloads = [
48
+ ...Object.values(environment.value.desired.workloads ?? {}),
49
+ ...workloads.value,
50
+ ];
51
+ const apiComposition = resolveInfraWorkloadApiComposition(environment.value.manifest, allWorkloads);
52
+ if (!apiComposition.ok)
53
+ return appendInfraFailure(diagnostics, apiComposition);
46
54
  const compute = await adapters.value.compute.inspectAsync(context, environment.value.desired.deployment.compute);
47
55
  if (!compute.ok)
48
56
  return appendInfraFailure(diagnostics, compute);
@@ -52,7 +60,7 @@ export async function prepareInfraOperationAsync(request, operation, dependencie
52
60
  targets: compute.value.targets.length > 0
53
61
  ? compute.value.targets
54
62
  : (request.previous?.targets ?? compute.value.targets),
55
- workloads: [...Object.values(environment.value.desired.workloads ?? {}), ...workloads.value],
63
+ workloads: allWorkloads,
56
64
  availableOutputs: mergeOutputs(request.previous?.outputs ?? [], compute.value.outputs),
57
65
  };
58
66
  if (runtimeDesired.targets.length === 0) {
@@ -1 +1 @@
1
- {"version":3,"file":"prepareInfraOperationAsync.js","sourceRoot":"","sources":["../../../../../src/features/environment-lifecycle/application/use-cases/prepareInfraOperationAsync.ts"],"names":[],"mappings":"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","sourcesContent":["import type { InfraDiagnostic, InfraResult } from '@ankhorage/contracts/infra';\n\nimport type {\n InfraOperationRequest,\n InfraOrchestrationDependencies,\n PreparedInfraOperation,\n} from '../../../../types/infraOrchestration.js';\nimport { resolveInfraEnvironment } from '../../domain/resolveInfraEnvironment.js';\nimport { appendInfraFailure } from '../../utils/appendInfraFailure.js';\nimport { createInfraExecutionContext } from '../../utils/createInfraExecutionContext.js';\nimport { collectInfraWorkloadsAsync } from './collectInfraWorkloadsAsync.js';\nimport { resolveInfraAdaptersAsync } from './resolveInfraAdaptersAsync.js';\nimport { validateInfraAdaptersAsync } from './validateInfraAdaptersAsync.js';\n\n/*** Resolve and validate one operation without creating, updating or deleting infrastructure. */\nexport async function prepareInfraOperationAsync(\n request: InfraOperationRequest,\n operation: 'validate' | 'plan' | 'generate' | 'status' | 'outputs' | 'down' | 'destroy',\n dependencies: InfraOrchestrationDependencies,\n): Promise<InfraResult<PreparedInfraOperation>> {\n const diagnostics: InfraDiagnostic[] = [];\n const environment = resolveInfraEnvironment({\n manifest: request.manifest,\n environment: request.environment,\n operation,\n });\n if (!environment.ok) return environment;\n diagnostics.push(...environment.diagnostics);\n if (\n request.previous !== undefined &&\n request.previousDesired !== undefined &&\n !hasSameProviderSelection(request.previousDesired, environment.value.desired)\n ) {\n return {\n ok: false,\n diagnostics: [\n ...diagnostics,\n {\n severity: 'error',\n code: 'infra-provider-selection-changed',\n message:\n 'Stored Infra resources use another provider selection. Destroy that environment before applying the new selection.',\n },\n ],\n };\n }\n const adapters = await resolveInfraAdaptersAsync(\n environment.value.desired,\n dependencies.adapterResolver,\n );\n if (!adapters.ok) return appendInfraFailure(diagnostics, adapters);\n diagnostics.push(...adapters.diagnostics);\n const context = createInfraExecutionContext(request, environment.value, dependencies);\n const validation = await validateInfraAdaptersAsync(context, adapters.value);\n if (!validation.ok) return appendInfraFailure(diagnostics, validation);\n diagnostics.push(...validation.diagnostics);\n const workloads = await collectInfraWorkloadsAsync(context, adapters.value.services);\n if (!workloads.ok) return appendInfraFailure(diagnostics, workloads);\n diagnostics.push(...workloads.diagnostics);\n const compute = await adapters.value.compute.inspectAsync(\n context,\n environment.value.desired.deployment.compute,\n );\n if (!compute.ok) return appendInfraFailure(diagnostics, compute);\n diagnostics.push(...compute.diagnostics);\n const runtimeDesired = {\n selection: environment.value.desired.deployment.runtime,\n targets:\n compute.value.targets.length > 0\n ? compute.value.targets\n : (request.previous?.targets ?? compute.value.targets),\n workloads: [...Object.values(environment.value.desired.workloads ?? {}), ...workloads.value],\n availableOutputs: mergeOutputs(request.previous?.outputs ?? [], compute.value.outputs),\n };\n if (runtimeDesired.targets.length === 0) {\n diagnostics.push({\n severity: 'info',\n code: 'infra-runtime-target-deferred',\n message: 'Runtime validation is deferred until compute exposes a portable target.',\n });\n } else {\n const runtime = await adapters.value.runtime.validateAsync(context, runtimeDesired);\n if (!runtime.ok) return appendInfraFailure(diagnostics, runtime);\n diagnostics.push(...runtime.diagnostics);\n }\n return {\n ok: true,\n value: {\n environment: environment.value,\n context,\n adapters: adapters.value,\n compute: compute.value,\n runtimeDesired,\n },\n diagnostics,\n };\n}\n\n/*** Compare provider identity only; configuration changes remain normal in-place reconciliation. */\nfunction hasSameProviderSelection(\n previous: PreparedInfraOperation['environment']['desired'],\n current: PreparedInfraOperation['environment']['desired'],\n): boolean {\n return providerSelection(previous).join('\\0') === providerSelection(current).join('\\0');\n}\n\n/*** Read the closed provider slots in stable capability order. */\nfunction providerSelection(\n desired: PreparedInfraOperation['environment']['desired'],\n): readonly (string | undefined)[] {\n return [\n desired.deployment.compute.provider,\n desired.deployment.runtime.provider,\n desired.database?.provider,\n desired.objectStorage?.provider,\n desired.auth?.provider,\n desired.authz?.provider,\n desired.secretStore?.provider,\n ];\n}\n\n/*** Prefer freshly inspected outputs while preserving unrelated safe prior values. */\nfunction mergeOutputs(\n previous: PreparedInfraOperation['runtimeDesired']['availableOutputs'],\n current: PreparedInfraOperation['runtimeDesired']['availableOutputs'],\n): PreparedInfraOperation['runtimeDesired']['availableOutputs'] {\n const currentKeys = new Set(current.map(outputKey));\n return [...previous.filter((output) => !currentKeys.has(outputKey(output))), ...current];\n}\n\n/*** Create one stable public-or-secret output identity without reading its value. */\nfunction outputKey(\n output: PreparedInfraOperation['runtimeDesired']['availableOutputs'][number],\n): string {\n const { owner } = output;\n return `${owner.projectId}\\0${owner.environment}\\0${owner.adapter}\\0${owner.resourceId}\\0${output.name}`;\n}\n"]}
1
+ {"version":3,"file":"prepareInfraOperationAsync.js","sourceRoot":"","sources":["../../../../../src/features/environment-lifecycle/application/use-cases/prepareInfraOperationAsync.ts"],"names":[],"mappings":"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","sourcesContent":["import type { InfraDiagnostic, InfraResult } from '@ankhorage/contracts/infra';\n\nimport type {\n InfraOperationRequest,\n InfraOrchestrationDependencies,\n PreparedInfraOperation,\n} from '../../../../types/infraOrchestration.js';\nimport { resolveInfraEnvironment } from '../../domain/resolveInfraEnvironment.js';\nimport { resolveInfraWorkloadApiComposition } from '../../domain/resolveInfraWorkloadApiComposition.js';\nimport { appendInfraFailure } from '../../utils/appendInfraFailure.js';\nimport { createInfraExecutionContext } from '../../utils/createInfraExecutionContext.js';\nimport { collectInfraWorkloadsAsync } from './collectInfraWorkloadsAsync.js';\nimport { resolveInfraAdaptersAsync } from './resolveInfraAdaptersAsync.js';\nimport { validateInfraAdaptersAsync } from './validateInfraAdaptersAsync.js';\n\n/*** Resolve and validate one operation without creating, updating or deleting infrastructure. */\nexport async function prepareInfraOperationAsync(\n request: InfraOperationRequest,\n operation: 'validate' | 'plan' | 'generate' | 'status' | 'outputs' | 'down' | 'destroy',\n dependencies: InfraOrchestrationDependencies,\n): Promise<InfraResult<PreparedInfraOperation>> {\n const diagnostics: InfraDiagnostic[] = [];\n const environment = resolveInfraEnvironment({\n manifest: request.manifest,\n environment: request.environment,\n operation,\n });\n if (!environment.ok) return environment;\n diagnostics.push(...environment.diagnostics);\n if (\n request.previous !== undefined &&\n request.previousDesired !== undefined &&\n !hasSameProviderSelection(request.previousDesired, environment.value.desired)\n ) {\n return {\n ok: false,\n diagnostics: [\n ...diagnostics,\n {\n severity: 'error',\n code: 'infra-provider-selection-changed',\n message:\n 'Stored Infra resources use another provider selection. Destroy that environment before applying the new selection.',\n },\n ],\n };\n }\n const adapters = await resolveInfraAdaptersAsync(\n environment.value.desired,\n dependencies.adapterResolver,\n );\n if (!adapters.ok) return appendInfraFailure(diagnostics, adapters);\n diagnostics.push(...adapters.diagnostics);\n const context = createInfraExecutionContext(request, environment.value, dependencies);\n const validation = await validateInfraAdaptersAsync(context, adapters.value);\n if (!validation.ok) return appendInfraFailure(diagnostics, validation);\n diagnostics.push(...validation.diagnostics);\n const workloads = await collectInfraWorkloadsAsync(context, adapters.value.services);\n if (!workloads.ok) return appendInfraFailure(diagnostics, workloads);\n diagnostics.push(...workloads.diagnostics);\n const allWorkloads = [\n ...Object.values(environment.value.desired.workloads ?? {}),\n ...workloads.value,\n ];\n const apiComposition = resolveInfraWorkloadApiComposition(\n environment.value.manifest,\n allWorkloads,\n );\n if (!apiComposition.ok) return appendInfraFailure(diagnostics, apiComposition);\n const compute = await adapters.value.compute.inspectAsync(\n context,\n environment.value.desired.deployment.compute,\n );\n if (!compute.ok) return appendInfraFailure(diagnostics, compute);\n diagnostics.push(...compute.diagnostics);\n const runtimeDesired = {\n selection: environment.value.desired.deployment.runtime,\n targets:\n compute.value.targets.length > 0\n ? compute.value.targets\n : (request.previous?.targets ?? compute.value.targets),\n workloads: allWorkloads,\n availableOutputs: mergeOutputs(request.previous?.outputs ?? [], compute.value.outputs),\n };\n if (runtimeDesired.targets.length === 0) {\n diagnostics.push({\n severity: 'info',\n code: 'infra-runtime-target-deferred',\n message: 'Runtime validation is deferred until compute exposes a portable target.',\n });\n } else {\n const runtime = await adapters.value.runtime.validateAsync(context, runtimeDesired);\n if (!runtime.ok) return appendInfraFailure(diagnostics, runtime);\n diagnostics.push(...runtime.diagnostics);\n }\n return {\n ok: true,\n value: {\n environment: environment.value,\n context,\n adapters: adapters.value,\n compute: compute.value,\n runtimeDesired,\n },\n diagnostics,\n };\n}\n\n/*** Compare provider identity only; configuration changes remain normal in-place reconciliation. */\nfunction hasSameProviderSelection(\n previous: PreparedInfraOperation['environment']['desired'],\n current: PreparedInfraOperation['environment']['desired'],\n): boolean {\n return providerSelection(previous).join('\\0') === providerSelection(current).join('\\0');\n}\n\n/*** Read the closed provider slots in stable capability order. */\nfunction providerSelection(\n desired: PreparedInfraOperation['environment']['desired'],\n): readonly (string | undefined)[] {\n return [\n desired.deployment.compute.provider,\n desired.deployment.runtime.provider,\n desired.database?.provider,\n desired.objectStorage?.provider,\n desired.auth?.provider,\n desired.authz?.provider,\n desired.secretStore?.provider,\n ];\n}\n\n/*** Prefer freshly inspected outputs while preserving unrelated safe prior values. */\nfunction mergeOutputs(\n previous: PreparedInfraOperation['runtimeDesired']['availableOutputs'],\n current: PreparedInfraOperation['runtimeDesired']['availableOutputs'],\n): PreparedInfraOperation['runtimeDesired']['availableOutputs'] {\n const currentKeys = new Set(current.map(outputKey));\n return [...previous.filter((output) => !currentKeys.has(outputKey(output))), ...current];\n}\n\n/*** Create one stable public-or-secret output identity without reading its value. */\nfunction outputKey(\n output: PreparedInfraOperation['runtimeDesired']['availableOutputs'][number],\n): string {\n const { owner } = output;\n return `${owner.projectId}\\0${owner.environment}\\0${owner.adapter}\\0${owner.resourceId}\\0${output.name}`;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"upInfraEnvironmentAsync.d.ts","sourceRoot":"","sources":["../../../../../src/features/environment-lifecycle/application/use-cases/upInfraEnvironmentAsync.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EACV,8BAA8B,EAC9B,sBAAsB,EACtB,cAAc,EACf,MAAM,yCAAyC,CAAC;AAWjD,sGAAsG;AACtG,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,cAAc,EACvB,YAAY,EAAE,8BAA8B,GAC3C,OAAO,CAAC,sBAAsB,CAAC,CA6EjC"}
1
+ {"version":3,"file":"upInfraEnvironmentAsync.d.ts","sourceRoot":"","sources":["../../../../../src/features/environment-lifecycle/application/use-cases/upInfraEnvironmentAsync.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EACV,8BAA8B,EAC9B,sBAAsB,EACtB,cAAc,EACf,MAAM,yCAAyC,CAAC;AAYjD,sGAAsG;AACtG,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,cAAc,EACvB,YAAY,EAAE,8BAA8B,GAC3C,OAAO,CAAC,sBAAsB,CAAC,CAsFjC"}
@@ -1,4 +1,5 @@
1
1
  import { resolveInfraEnvironment } from '../../domain/resolveInfraEnvironment.js';
2
+ import { resolveInfraWorkloadApiComposition } from '../../domain/resolveInfraWorkloadApiComposition.js';
2
3
  import { appendInfraFailure } from '../../utils/appendInfraFailure.js';
3
4
  import { createInfraExecutionContext } from '../../utils/createInfraExecutionContext.js';
4
5
  import { createInfraLedger } from '../../utils/createInfraLedger.js';
@@ -26,6 +27,17 @@ export async function upInfraEnvironmentAsync(request, dependencies) {
26
27
  if (!validation.ok)
27
28
  return appendInfraFailure(diagnostics, validation);
28
29
  diagnostics.push(...validation.diagnostics);
30
+ const workloads = await collectInfraWorkloadsAsync(context, adapters.value.services);
31
+ if (!workloads.ok)
32
+ return appendInfraFailure(diagnostics, workloads);
33
+ diagnostics.push(...workloads.diagnostics);
34
+ const allWorkloads = [
35
+ ...Object.values(environment.value.desired.workloads ?? {}),
36
+ ...workloads.value,
37
+ ];
38
+ const apiComposition = resolveInfraWorkloadApiComposition(environment.value.manifest, allWorkloads);
39
+ if (!apiComposition.ok)
40
+ return appendInfraFailure(diagnostics, apiComposition);
29
41
  const compute = await adapters.value.compute.ensureAsync(context, environment.value.desired.deployment.compute);
30
42
  if (!compute.ok)
31
43
  return appendInfraFailure(diagnostics, compute);
@@ -34,14 +46,10 @@ export async function upInfraEnvironmentAsync(request, dependencies) {
34
46
  if (!preparation.ok)
35
47
  return appendInfraFailure(diagnostics, preparation);
36
48
  diagnostics.push(...preparation.diagnostics);
37
- const workloads = await collectInfraWorkloadsAsync(context, adapters.value.services);
38
- if (!workloads.ok)
39
- return appendInfraFailure(diagnostics, workloads);
40
- diagnostics.push(...workloads.diagnostics);
41
49
  const runtimeDesired = {
42
50
  selection: environment.value.desired.deployment.runtime,
43
51
  targets: compute.value.targets,
44
- workloads: [...Object.values(environment.value.desired.workloads ?? {}), ...workloads.value],
52
+ workloads: allWorkloads,
45
53
  availableOutputs: mergeInfraOutputs(request.previous?.outputs ?? [], compute.value.outputs),
46
54
  };
47
55
  const runtimeValidation = await adapters.value.runtime.validateAsync(context, runtimeDesired);
@@ -1 +1 @@
1
- {"version":3,"file":"upInfraEnvironmentAsync.js","sourceRoot":"","sources":["../../../../../src/features/environment-lifecycle/application/use-cases/upInfraEnvironmentAsync.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n InfraDiagnostic,\n InfraExecutionContext,\n InfraOutput,\n InfraOwnedResource,\n InfraResult,\n InfraServiceAdapter,\n} from '@ankhorage/contracts/infra';\n\nimport type {\n InfraOrchestrationDependencies,\n InfraUpOperationResult,\n InfraUpRequest,\n} from '../../../../types/infraOrchestration.js';\nimport { resolveInfraEnvironment } from '../../domain/resolveInfraEnvironment.js';\nimport { appendInfraFailure } from '../../utils/appendInfraFailure.js';\nimport { createInfraExecutionContext } from '../../utils/createInfraExecutionContext.js';\nimport { createInfraLedger } from '../../utils/createInfraLedger.js';\nimport { mergeInfraOutputs } from '../../utils/mergeInfraOutputs.js';\nimport { collectInfraWorkloadsAsync } from './collectInfraWorkloadsAsync.js';\nimport { prepareInfraServicesAsync } from './prepareInfraServicesAsync.js';\nimport { resolveInfraAdaptersAsync } from './resolveInfraAdaptersAsync.js';\nimport { validateInfraAdaptersAsync } from './validateInfraAdaptersAsync.js';\n\n/*** Validate and reconcile compute, service preparation, workloads, runtime and services in order. */\nexport async function upInfraEnvironmentAsync(\n request: InfraUpRequest,\n dependencies: InfraOrchestrationDependencies,\n): Promise<InfraUpOperationResult> {\n const environment = resolveInfraEnvironment({\n manifest: request.manifest,\n environment: request.environment,\n operation: 'up',\n });\n if (!environment.ok) return environment;\n const diagnostics: InfraDiagnostic[] = [...environment.diagnostics];\n const adapters = await resolveInfraAdaptersAsync(\n environment.value.desired,\n dependencies.adapterResolver,\n );\n if (!adapters.ok) return appendInfraFailure(diagnostics, adapters);\n diagnostics.push(...adapters.diagnostics);\n const context = createInfraExecutionContext(request, environment.value, dependencies);\n const validation = await validateInfraAdaptersAsync(context, adapters.value);\n if (!validation.ok) return appendInfraFailure(diagnostics, validation);\n diagnostics.push(...validation.diagnostics);\n const compute = await adapters.value.compute.ensureAsync(\n context,\n environment.value.desired.deployment.compute,\n );\n if (!compute.ok) return appendInfraFailure(diagnostics, compute);\n diagnostics.push(...compute.diagnostics);\n const preparation = await prepareInfraServicesAsync(context, adapters.value.services);\n if (!preparation.ok) return appendInfraFailure(diagnostics, preparation);\n diagnostics.push(...preparation.diagnostics);\n const workloads = await collectInfraWorkloadsAsync(context, adapters.value.services);\n if (!workloads.ok) return appendInfraFailure(diagnostics, workloads);\n diagnostics.push(...workloads.diagnostics);\n const runtimeDesired = {\n selection: environment.value.desired.deployment.runtime,\n targets: compute.value.targets,\n workloads: [...Object.values(environment.value.desired.workloads ?? {}), ...workloads.value],\n availableOutputs: mergeInfraOutputs(request.previous?.outputs ?? [], compute.value.outputs),\n };\n const runtimeValidation = await adapters.value.runtime.validateAsync(context, runtimeDesired);\n if (!runtimeValidation.ok) return appendInfraFailure(diagnostics, runtimeValidation);\n diagnostics.push(...runtimeValidation.diagnostics);\n const runtime = await adapters.value.runtime.ensureAsync(context, runtimeDesired);\n if (!runtime.ok) return appendInfraFailure(diagnostics, runtime);\n diagnostics.push(...runtime.diagnostics);\n const services = await reconcileServicesAsync(context, adapters.value.services, [\n ...compute.value.outputs,\n ...runtime.value.outputs,\n ]);\n if (!services.ok) return appendInfraFailure(diagnostics, services);\n diagnostics.push(...services.diagnostics);\n const resources = [\n ...compute.value.resources,\n ...runtime.value.resources,\n ...services.value.resources,\n ];\n const outputs = mergeInfraOutputs(\n compute.value.outputs,\n runtime.value.outputs,\n services.value.outputs,\n );\n const ledger = createInfraLedger({\n projectId: request.projectId,\n environment: environment.value.id,\n targets: compute.value.targets,\n resources,\n outputs,\n previous: request.previous,\n });\n return {\n ok: true,\n value: {\n environment: environment.value.id,\n targets: compute.value.targets,\n resources,\n outputs,\n ledger,\n },\n diagnostics,\n };\n}\n\n/*** Reconcile service-owned external state after its runtime workloads expose outputs. */\nasync function reconcileServicesAsync(\n context: InfraExecutionContext,\n services: readonly InfraServiceAdapter[],\n runtimeOutputs: readonly InfraOutput[],\n): Promise<\n InfraResult<{\n readonly resources: readonly InfraOwnedResource[];\n readonly outputs: readonly InfraOutput[];\n }>\n> {\n const resources: InfraOwnedResource[] = [];\n const outputs: InfraOutput[] = [];\n const diagnostics: InfraDiagnostic[] = [];\n for (const service of services) {\n const result = await service.reconcileAsync(context, runtimeOutputs);\n if (!result.ok) return appendInfraFailure(diagnostics, result);\n resources.push(...result.value.resources);\n outputs.push(...result.value.outputs);\n diagnostics.push(...result.diagnostics);\n }\n return { ok: true, value: { resources, outputs }, diagnostics };\n}\n"]}
1
+ {"version":3,"file":"upInfraEnvironmentAsync.js","sourceRoot":"","sources":["../../../../../src/features/environment-lifecycle/application/use-cases/upInfraEnvironmentAsync.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n InfraDiagnostic,\n InfraExecutionContext,\n InfraOutput,\n InfraOwnedResource,\n InfraResult,\n InfraServiceAdapter,\n} from '@ankhorage/contracts/infra';\n\nimport type {\n InfraOrchestrationDependencies,\n InfraUpOperationResult,\n InfraUpRequest,\n} from '../../../../types/infraOrchestration.js';\nimport { resolveInfraEnvironment } from '../../domain/resolveInfraEnvironment.js';\nimport { resolveInfraWorkloadApiComposition } from '../../domain/resolveInfraWorkloadApiComposition.js';\nimport { appendInfraFailure } from '../../utils/appendInfraFailure.js';\nimport { createInfraExecutionContext } from '../../utils/createInfraExecutionContext.js';\nimport { createInfraLedger } from '../../utils/createInfraLedger.js';\nimport { mergeInfraOutputs } from '../../utils/mergeInfraOutputs.js';\nimport { collectInfraWorkloadsAsync } from './collectInfraWorkloadsAsync.js';\nimport { prepareInfraServicesAsync } from './prepareInfraServicesAsync.js';\nimport { resolveInfraAdaptersAsync } from './resolveInfraAdaptersAsync.js';\nimport { validateInfraAdaptersAsync } from './validateInfraAdaptersAsync.js';\n\n/*** Validate and reconcile compute, service preparation, workloads, runtime and services in order. */\nexport async function upInfraEnvironmentAsync(\n request: InfraUpRequest,\n dependencies: InfraOrchestrationDependencies,\n): Promise<InfraUpOperationResult> {\n const environment = resolveInfraEnvironment({\n manifest: request.manifest,\n environment: request.environment,\n operation: 'up',\n });\n if (!environment.ok) return environment;\n const diagnostics: InfraDiagnostic[] = [...environment.diagnostics];\n const adapters = await resolveInfraAdaptersAsync(\n environment.value.desired,\n dependencies.adapterResolver,\n );\n if (!adapters.ok) return appendInfraFailure(diagnostics, adapters);\n diagnostics.push(...adapters.diagnostics);\n const context = createInfraExecutionContext(request, environment.value, dependencies);\n const validation = await validateInfraAdaptersAsync(context, adapters.value);\n if (!validation.ok) return appendInfraFailure(diagnostics, validation);\n diagnostics.push(...validation.diagnostics);\n const workloads = await collectInfraWorkloadsAsync(context, adapters.value.services);\n if (!workloads.ok) return appendInfraFailure(diagnostics, workloads);\n diagnostics.push(...workloads.diagnostics);\n const allWorkloads = [\n ...Object.values(environment.value.desired.workloads ?? {}),\n ...workloads.value,\n ];\n const apiComposition = resolveInfraWorkloadApiComposition(\n environment.value.manifest,\n allWorkloads,\n );\n if (!apiComposition.ok) return appendInfraFailure(diagnostics, apiComposition);\n const compute = await adapters.value.compute.ensureAsync(\n context,\n environment.value.desired.deployment.compute,\n );\n if (!compute.ok) return appendInfraFailure(diagnostics, compute);\n diagnostics.push(...compute.diagnostics);\n const preparation = await prepareInfraServicesAsync(context, adapters.value.services);\n if (!preparation.ok) return appendInfraFailure(diagnostics, preparation);\n diagnostics.push(...preparation.diagnostics);\n const runtimeDesired = {\n selection: environment.value.desired.deployment.runtime,\n targets: compute.value.targets,\n workloads: allWorkloads,\n availableOutputs: mergeInfraOutputs(request.previous?.outputs ?? [], compute.value.outputs),\n };\n const runtimeValidation = await adapters.value.runtime.validateAsync(context, runtimeDesired);\n if (!runtimeValidation.ok) return appendInfraFailure(diagnostics, runtimeValidation);\n diagnostics.push(...runtimeValidation.diagnostics);\n const runtime = await adapters.value.runtime.ensureAsync(context, runtimeDesired);\n if (!runtime.ok) return appendInfraFailure(diagnostics, runtime);\n diagnostics.push(...runtime.diagnostics);\n const services = await reconcileServicesAsync(context, adapters.value.services, [\n ...compute.value.outputs,\n ...runtime.value.outputs,\n ]);\n if (!services.ok) return appendInfraFailure(diagnostics, services);\n diagnostics.push(...services.diagnostics);\n const resources = [\n ...compute.value.resources,\n ...runtime.value.resources,\n ...services.value.resources,\n ];\n const outputs = mergeInfraOutputs(\n compute.value.outputs,\n runtime.value.outputs,\n services.value.outputs,\n );\n const ledger = createInfraLedger({\n projectId: request.projectId,\n environment: environment.value.id,\n targets: compute.value.targets,\n resources,\n outputs,\n previous: request.previous,\n });\n return {\n ok: true,\n value: {\n environment: environment.value.id,\n targets: compute.value.targets,\n resources,\n outputs,\n ledger,\n },\n diagnostics,\n };\n}\n\n/*** Reconcile service-owned external state after its runtime workloads expose outputs. */\nasync function reconcileServicesAsync(\n context: InfraExecutionContext,\n services: readonly InfraServiceAdapter[],\n runtimeOutputs: readonly InfraOutput[],\n): Promise<\n InfraResult<{\n readonly resources: readonly InfraOwnedResource[];\n readonly outputs: readonly InfraOutput[];\n }>\n> {\n const resources: InfraOwnedResource[] = [];\n const outputs: InfraOutput[] = [];\n const diagnostics: InfraDiagnostic[] = [];\n for (const service of services) {\n const result = await service.reconcileAsync(context, runtimeOutputs);\n if (!result.ok) return appendInfraFailure(diagnostics, result);\n resources.push(...result.value.resources);\n outputs.push(...result.value.outputs);\n diagnostics.push(...result.diagnostics);\n }\n return { ok: true, value: { resources, outputs }, diagnostics };\n}\n"]}
@@ -0,0 +1,5 @@
1
+ import type { InfraManifest, InfraResult, InfraWorkloadSpec } from '@ankhorage/contracts/infra';
2
+ import type { InfraWorkloadApiAssignment } from '../../../types/infraWorkloadApiComposition.js';
3
+ /*** Resolve internal API declarations to exactly one declared workload without selecting HTTP transport. */
4
+ export declare function resolveInfraWorkloadApiComposition(manifest: InfraManifest, workloads: readonly InfraWorkloadSpec[]): InfraResult<readonly InfraWorkloadApiAssignment[]>;
5
+ //# sourceMappingURL=resolveInfraWorkloadApiComposition.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolveInfraWorkloadApiComposition.d.ts","sourceRoot":"","sources":["../../../../src/features/environment-lifecycle/domain/resolveInfraWorkloadApiComposition.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,aAAa,EACb,WAAW,EACX,iBAAiB,EAClB,MAAM,4BAA4B,CAAC;AAEpC,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,+CAA+C,CAAC;AAEhG,4GAA4G;AAC5G,wBAAgB,kCAAkC,CAChD,QAAQ,EAAE,aAAa,EACvB,SAAS,EAAE,SAAS,iBAAiB,EAAE,GACtC,WAAW,CAAC,SAAS,0BAA0B,EAAE,CAAC,CA8BpD"}
@@ -0,0 +1,55 @@
1
+ /*** Resolve internal API declarations to exactly one declared workload without selecting HTTP transport. */
2
+ export function resolveInfraWorkloadApiComposition(manifest, workloads) {
3
+ const diagnostics = [];
4
+ const assignments = [];
5
+ const apiDefinitions = new Map(Object.entries(manifest.apis ?? {}));
6
+ const assignedWorkloads = new Map();
7
+ for (const workload of workloads) {
8
+ for (const apiId of Object.keys(workload.apis?.apiIds ?? {})) {
9
+ const api = apiDefinitions.get(apiId);
10
+ if (api === undefined) {
11
+ diagnostics.push(createMissingApiDiagnostic(workload.id, apiId));
12
+ continue;
13
+ }
14
+ if (api.origin !== 'internal') {
15
+ diagnostics.push(createExternalApiDiagnostic(workload.id, apiId));
16
+ continue;
17
+ }
18
+ const assignedWorkloadId = assignedWorkloads.get(apiId);
19
+ if (assignedWorkloadId !== undefined) {
20
+ diagnostics.push(createDuplicateApiDiagnostic(apiId, assignedWorkloadId, workload.id));
21
+ continue;
22
+ }
23
+ assignedWorkloads.set(apiId, workload.id);
24
+ assignments.push({ apiId, workloadId: workload.id });
25
+ }
26
+ }
27
+ return diagnostics.length === 0
28
+ ? { ok: true, value: assignments, diagnostics }
29
+ : { ok: false, diagnostics };
30
+ }
31
+ /*** Describe an API reference whose registry definition is unavailable to the desired state. */
32
+ function createMissingApiDiagnostic(workloadId, apiId) {
33
+ return {
34
+ severity: 'error',
35
+ code: 'infra-workload-api-missing',
36
+ message: `Workload "${workloadId}" references API "${apiId}", but it is not declared in manifest.apis.`,
37
+ };
38
+ }
39
+ /*** Describe an external API incorrectly claimed as a workload-served API. */
40
+ function createExternalApiDiagnostic(workloadId, apiId) {
41
+ return {
42
+ severity: 'error',
43
+ code: 'infra-workload-api-external',
44
+ message: `Workload "${workloadId}" references external API "${apiId}"; only internal APIs may be served by workloads.`,
45
+ };
46
+ }
47
+ /*** Describe the conflicting second workload for one internal API. */
48
+ function createDuplicateApiDiagnostic(apiId, assignedWorkloadId, workloadId) {
49
+ return {
50
+ severity: 'error',
51
+ code: 'infra-workload-api-duplicate',
52
+ message: `Internal API "${apiId}" is assigned to both workloads "${assignedWorkloadId}" and "${workloadId}".`,
53
+ };
54
+ }
55
+ //# sourceMappingURL=resolveInfraWorkloadApiComposition.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolveInfraWorkloadApiComposition.js","sourceRoot":"","sources":["../../../../src/features/environment-lifecycle/domain/resolveInfraWorkloadApiComposition.ts"],"names":[],"mappings":"AASA,4GAA4G;AAC5G,MAAM,UAAU,kCAAkC,CAChD,QAAuB,EACvB,SAAuC;IAEvC,MAAM,WAAW,GAAsB,EAAE,CAAC;IAC1C,MAAM,WAAW,GAAiC,EAAE,CAAC;IACrD,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC;IACpE,MAAM,iBAAiB,GAAG,IAAI,GAAG,EAAkB,CAAC;IAEpD,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;QACjC,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,IAAI,EAAE,CAAC,EAAE,CAAC;YAC7D,MAAM,GAAG,GAAG,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;YACtC,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;gBACtB,WAAW,CAAC,IAAI,CAAC,0BAA0B,CAAC,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC,CAAC;gBACjE,SAAS;YACX,CAAC;YACD,IAAI,GAAG,CAAC,MAAM,KAAK,UAAU,EAAE,CAAC;gBAC9B,WAAW,CAAC,IAAI,CAAC,2BAA2B,CAAC,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC,CAAC;gBAClE,SAAS;YACX,CAAC;YACD,MAAM,kBAAkB,GAAG,iBAAiB,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;YACxD,IAAI,kBAAkB,KAAK,SAAS,EAAE,CAAC;gBACrC,WAAW,CAAC,IAAI,CAAC,4BAA4B,CAAC,KAAK,EAAE,kBAAkB,EAAE,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC;gBACvF,SAAS;YACX,CAAC;YACD,iBAAiB,CAAC,GAAG,CAAC,KAAK,EAAE,QAAQ,CAAC,EAAE,CAAC,CAAC;YAC1C,WAAW,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,UAAU,EAAE,QAAQ,CAAC,EAAE,EAAE,CAAC,CAAC;QACvD,CAAC;IACH,CAAC;IAED,OAAO,WAAW,CAAC,MAAM,KAAK,CAAC;QAC7B,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE;QAC/C,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC;AACjC,CAAC;AAED,gGAAgG;AAChG,SAAS,0BAA0B,CAAC,UAAkB,EAAE,KAAa;IACnE,OAAO;QACL,QAAQ,EAAE,OAAgB;QAC1B,IAAI,EAAE,4BAA4B;QAClC,OAAO,EAAE,aAAa,UAAU,qBAAqB,KAAK,6CAA6C;KACxG,CAAC;AACJ,CAAC;AAED,8EAA8E;AAC9E,SAAS,2BAA2B,CAAC,UAAkB,EAAE,KAAa;IACpE,OAAO;QACL,QAAQ,EAAE,OAAgB;QAC1B,IAAI,EAAE,6BAA6B;QACnC,OAAO,EAAE,aAAa,UAAU,8BAA8B,KAAK,mDAAmD;KACvH,CAAC;AACJ,CAAC;AAED,sEAAsE;AACtE,SAAS,4BAA4B,CACnC,KAAa,EACb,kBAA0B,EAC1B,UAAkB;IAElB,OAAO;QACL,QAAQ,EAAE,OAAgB;QAC1B,IAAI,EAAE,8BAA8B;QACpC,OAAO,EAAE,iBAAiB,KAAK,oCAAoC,kBAAkB,UAAU,UAAU,IAAI;KAC9G,CAAC;AACJ,CAAC","sourcesContent":["import type {\n InfraDiagnostic,\n InfraManifest,\n InfraResult,\n InfraWorkloadSpec,\n} from '@ankhorage/contracts/infra';\n\nimport type { InfraWorkloadApiAssignment } from '../../../types/infraWorkloadApiComposition.js';\n\n/*** Resolve internal API declarations to exactly one declared workload without selecting HTTP transport. */\nexport function resolveInfraWorkloadApiComposition(\n manifest: InfraManifest,\n workloads: readonly InfraWorkloadSpec[],\n): InfraResult<readonly InfraWorkloadApiAssignment[]> {\n const diagnostics: InfraDiagnostic[] = [];\n const assignments: InfraWorkloadApiAssignment[] = [];\n const apiDefinitions = new Map(Object.entries(manifest.apis ?? {}));\n const assignedWorkloads = new Map<string, string>();\n\n for (const workload of workloads) {\n for (const apiId of Object.keys(workload.apis?.apiIds ?? {})) {\n const api = apiDefinitions.get(apiId);\n if (api === undefined) {\n diagnostics.push(createMissingApiDiagnostic(workload.id, apiId));\n continue;\n }\n if (api.origin !== 'internal') {\n diagnostics.push(createExternalApiDiagnostic(workload.id, apiId));\n continue;\n }\n const assignedWorkloadId = assignedWorkloads.get(apiId);\n if (assignedWorkloadId !== undefined) {\n diagnostics.push(createDuplicateApiDiagnostic(apiId, assignedWorkloadId, workload.id));\n continue;\n }\n assignedWorkloads.set(apiId, workload.id);\n assignments.push({ apiId, workloadId: workload.id });\n }\n }\n\n return diagnostics.length === 0\n ? { ok: true, value: assignments, diagnostics }\n : { ok: false, diagnostics };\n}\n\n/*** Describe an API reference whose registry definition is unavailable to the desired state. */\nfunction createMissingApiDiagnostic(workloadId: string, apiId: string) {\n return {\n severity: 'error' as const,\n code: 'infra-workload-api-missing',\n message: `Workload \"${workloadId}\" references API \"${apiId}\", but it is not declared in manifest.apis.`,\n };\n}\n\n/*** Describe an external API incorrectly claimed as a workload-served API. */\nfunction createExternalApiDiagnostic(workloadId: string, apiId: string) {\n return {\n severity: 'error' as const,\n code: 'infra-workload-api-external',\n message: `Workload \"${workloadId}\" references external API \"${apiId}\"; only internal APIs may be served by workloads.`,\n };\n}\n\n/*** Describe the conflicting second workload for one internal API. */\nfunction createDuplicateApiDiagnostic(\n apiId: string,\n assignedWorkloadId: string,\n workloadId: string,\n) {\n return {\n severity: 'error' as const,\n code: 'infra-workload-api-duplicate',\n message: `Internal API \"${apiId}\" is assigned to both workloads \"${assignedWorkloadId}\" and \"${workloadId}\".`,\n };\n}\n"]}
package/dist/index.d.ts CHANGED
@@ -13,5 +13,7 @@ export { upInfraEnvironmentAsync } from './features/environment-lifecycle/applic
13
13
  export { validateInfraEnvironmentAsync } from './features/environment-lifecycle/application/use-cases/validateInfraEnvironmentAsync.js';
14
14
  export { orderInfraPlanActions } from './features/environment-lifecycle/domain/orderInfraPlanActions.js';
15
15
  export { resolveInfraEnvironment } from './features/environment-lifecycle/domain/resolveInfraEnvironment.js';
16
+ export { resolveInfraWorkloadApiComposition } from './features/environment-lifecycle/domain/resolveInfraWorkloadApiComposition.js';
16
17
  export type { InfraDestroyOperationRequest, InfraDestroyOperationResult, InfraDestroyResult, InfraDownOperationResult, InfraDownResult, InfraGenerateOperationResult, InfraGenerateResult, InfraOperationRequest, InfraOrchestrationDependencies, InfraOutputsOperationResult, InfraOutputsResult, InfraPlanOperationResult, InfraStatusOperationResult, InfraStoredState, InfraUpOperationResult, InfraUpRequest, InfraUpResult, InfraValidateOperationResult, InfraValidateResult, PreparedInfraOperation, ResolvedInfraAdapters, ResolvedInfraEnvironment, } from './types/infraOrchestration.js';
18
+ export type { InfraWorkloadApiAssignment } from './types/infraWorkloadApiComposition.js';
17
19
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oCAAoC,EAAE,MAAM,4FAA4F,CAAC;AAClJ,OAAO,EAAE,gCAAgC,EAAE,MAAM,wFAAwF,CAAC;AAC1I,OAAO,EAAE,qCAAqC,EAAE,MAAM,6FAA6F,CAAC;AACpJ,YAAY,EACV,yBAAyB,EACzB,2BAA2B,GAC5B,MAAM,oFAAoF,CAAC;AAC5F,OAAO,EAAE,4BAA4B,EAAE,MAAM,wFAAwF,CAAC;AACtI,OAAO,EAAE,yBAAyB,EAAE,MAAM,qFAAqF,CAAC;AAChI,OAAO,EAAE,6BAA6B,EAAE,MAAM,yFAAyF,CAAC;AACxI,OAAO,EAAE,0BAA0B,EAAE,MAAM,sFAAsF,CAAC;AAClI,OAAO,EAAE,yBAAyB,EAAE,MAAM,qFAAqF,CAAC;AAChI,OAAO,EAAE,yBAAyB,EAAE,MAAM,qFAAqF,CAAC;AAChI,OAAO,EAAE,2BAA2B,EAAE,MAAM,uFAAuF,CAAC;AACpI,OAAO,EAAE,uBAAuB,EAAE,MAAM,mFAAmF,CAAC;AAC5H,OAAO,EAAE,6BAA6B,EAAE,MAAM,yFAAyF,CAAC;AACxI,OAAO,EAAE,qBAAqB,EAAE,MAAM,kEAAkE,CAAC;AACzG,OAAO,EAAE,uBAAuB,EAAE,MAAM,oEAAoE,CAAC;AAC7G,YAAY,EACV,4BAA4B,EAC5B,2BAA2B,EAC3B,kBAAkB,EAClB,wBAAwB,EACxB,eAAe,EACf,4BAA4B,EAC5B,mBAAmB,EACnB,qBAAqB,EACrB,8BAA8B,EAC9B,2BAA2B,EAC3B,kBAAkB,EAClB,wBAAwB,EACxB,0BAA0B,EAC1B,gBAAgB,EAChB,sBAAsB,EACtB,cAAc,EACd,aAAa,EACb,4BAA4B,EAC5B,mBAAmB,EACnB,sBAAsB,EACtB,qBAAqB,EACrB,wBAAwB,GACzB,MAAM,+BAA+B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oCAAoC,EAAE,MAAM,4FAA4F,CAAC;AAClJ,OAAO,EAAE,gCAAgC,EAAE,MAAM,wFAAwF,CAAC;AAC1I,OAAO,EAAE,qCAAqC,EAAE,MAAM,6FAA6F,CAAC;AACpJ,YAAY,EACV,yBAAyB,EACzB,2BAA2B,GAC5B,MAAM,oFAAoF,CAAC;AAC5F,OAAO,EAAE,4BAA4B,EAAE,MAAM,wFAAwF,CAAC;AACtI,OAAO,EAAE,yBAAyB,EAAE,MAAM,qFAAqF,CAAC;AAChI,OAAO,EAAE,6BAA6B,EAAE,MAAM,yFAAyF,CAAC;AACxI,OAAO,EAAE,0BAA0B,EAAE,MAAM,sFAAsF,CAAC;AAClI,OAAO,EAAE,yBAAyB,EAAE,MAAM,qFAAqF,CAAC;AAChI,OAAO,EAAE,yBAAyB,EAAE,MAAM,qFAAqF,CAAC;AAChI,OAAO,EAAE,2BAA2B,EAAE,MAAM,uFAAuF,CAAC;AACpI,OAAO,EAAE,uBAAuB,EAAE,MAAM,mFAAmF,CAAC;AAC5H,OAAO,EAAE,6BAA6B,EAAE,MAAM,yFAAyF,CAAC;AACxI,OAAO,EAAE,qBAAqB,EAAE,MAAM,kEAAkE,CAAC;AACzG,OAAO,EAAE,uBAAuB,EAAE,MAAM,oEAAoE,CAAC;AAC7G,OAAO,EAAE,kCAAkC,EAAE,MAAM,+EAA+E,CAAC;AACnI,YAAY,EACV,4BAA4B,EAC5B,2BAA2B,EAC3B,kBAAkB,EAClB,wBAAwB,EACxB,eAAe,EACf,4BAA4B,EAC5B,mBAAmB,EACnB,qBAAqB,EACrB,8BAA8B,EAC9B,2BAA2B,EAC3B,kBAAkB,EAClB,wBAAwB,EACxB,0BAA0B,EAC1B,gBAAgB,EAChB,sBAAsB,EACtB,cAAc,EACd,aAAa,EACb,4BAA4B,EAC5B,mBAAmB,EACnB,sBAAsB,EACtB,qBAAqB,EACrB,wBAAwB,GACzB,MAAM,+BAA+B,CAAC;AACvC,YAAY,EAAE,0BAA0B,EAAE,MAAM,wCAAwC,CAAC"}
package/dist/index.js CHANGED
@@ -12,4 +12,5 @@ export { upInfraEnvironmentAsync } from './features/environment-lifecycle/applic
12
12
  export { validateInfraEnvironmentAsync } from './features/environment-lifecycle/application/use-cases/validateInfraEnvironmentAsync.js';
13
13
  export { orderInfraPlanActions } from './features/environment-lifecycle/domain/orderInfraPlanActions.js';
14
14
  export { resolveInfraEnvironment } from './features/environment-lifecycle/domain/resolveInfraEnvironment.js';
15
+ export { resolveInfraWorkloadApiComposition } from './features/environment-lifecycle/domain/resolveInfraWorkloadApiComposition.js';
15
16
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oCAAoC,EAAE,MAAM,4FAA4F,CAAC;AAClJ,OAAO,EAAE,gCAAgC,EAAE,MAAM,wFAAwF,CAAC;AAC1I,OAAO,EAAE,qCAAqC,EAAE,MAAM,6FAA6F,CAAC;AAKpJ,OAAO,EAAE,4BAA4B,EAAE,MAAM,wFAAwF,CAAC;AACtI,OAAO,EAAE,yBAAyB,EAAE,MAAM,qFAAqF,CAAC;AAChI,OAAO,EAAE,6BAA6B,EAAE,MAAM,yFAAyF,CAAC;AACxI,OAAO,EAAE,0BAA0B,EAAE,MAAM,sFAAsF,CAAC;AAClI,OAAO,EAAE,yBAAyB,EAAE,MAAM,qFAAqF,CAAC;AAChI,OAAO,EAAE,yBAAyB,EAAE,MAAM,qFAAqF,CAAC;AAChI,OAAO,EAAE,2BAA2B,EAAE,MAAM,uFAAuF,CAAC;AACpI,OAAO,EAAE,uBAAuB,EAAE,MAAM,mFAAmF,CAAC;AAC5H,OAAO,EAAE,6BAA6B,EAAE,MAAM,yFAAyF,CAAC;AACxI,OAAO,EAAE,qBAAqB,EAAE,MAAM,kEAAkE,CAAC;AACzG,OAAO,EAAE,uBAAuB,EAAE,MAAM,oEAAoE,CAAC","sourcesContent":["export { createEnvironmentInfraCredentialPort } from './features/environment-lifecycle/adapters/outbound/createEnvironmentInfraCredentialPort.js';\nexport { createEnvironmentInfraSecretPort } from './features/environment-lifecycle/adapters/outbound/createEnvironmentInfraSecretPort.js';\nexport { createNodeInfraAdapterPackageResolver } from './features/environment-lifecycle/adapters/outbound/createNodeInfraAdapterPackageResolver.js';\nexport type {\n InfraAdapterPackageModule,\n InfraAdapterPackageResolver,\n} from './features/environment-lifecycle/application/ports/outbound/infraAdapterPackage.js';\nexport { destroyInfraEnvironmentAsync } from './features/environment-lifecycle/application/use-cases/destroyInfraEnvironmentAsync.js';\nexport { downInfraEnvironmentAsync } from './features/environment-lifecycle/application/use-cases/downInfraEnvironmentAsync.js';\nexport { generateInfraEnvironmentAsync } from './features/environment-lifecycle/application/use-cases/generateInfraEnvironmentAsync.js';\nexport { getInfraEnvironmentOutputs } from './features/environment-lifecycle/application/use-cases/getInfraEnvironmentOutputs.js';\nexport { planInfraEnvironmentAsync } from './features/environment-lifecycle/application/use-cases/planInfraEnvironmentAsync.js';\nexport { resolveInfraAdaptersAsync } from './features/environment-lifecycle/application/use-cases/resolveInfraAdaptersAsync.js';\nexport { statusInfraEnvironmentAsync } from './features/environment-lifecycle/application/use-cases/statusInfraEnvironmentAsync.js';\nexport { upInfraEnvironmentAsync } from './features/environment-lifecycle/application/use-cases/upInfraEnvironmentAsync.js';\nexport { validateInfraEnvironmentAsync } from './features/environment-lifecycle/application/use-cases/validateInfraEnvironmentAsync.js';\nexport { orderInfraPlanActions } from './features/environment-lifecycle/domain/orderInfraPlanActions.js';\nexport { resolveInfraEnvironment } from './features/environment-lifecycle/domain/resolveInfraEnvironment.js';\nexport type {\n InfraDestroyOperationRequest,\n InfraDestroyOperationResult,\n InfraDestroyResult,\n InfraDownOperationResult,\n InfraDownResult,\n InfraGenerateOperationResult,\n InfraGenerateResult,\n InfraOperationRequest,\n InfraOrchestrationDependencies,\n InfraOutputsOperationResult,\n InfraOutputsResult,\n InfraPlanOperationResult,\n InfraStatusOperationResult,\n InfraStoredState,\n InfraUpOperationResult,\n InfraUpRequest,\n InfraUpResult,\n InfraValidateOperationResult,\n InfraValidateResult,\n PreparedInfraOperation,\n ResolvedInfraAdapters,\n ResolvedInfraEnvironment,\n} from './types/infraOrchestration.js';\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oCAAoC,EAAE,MAAM,4FAA4F,CAAC;AAClJ,OAAO,EAAE,gCAAgC,EAAE,MAAM,wFAAwF,CAAC;AAC1I,OAAO,EAAE,qCAAqC,EAAE,MAAM,6FAA6F,CAAC;AAKpJ,OAAO,EAAE,4BAA4B,EAAE,MAAM,wFAAwF,CAAC;AACtI,OAAO,EAAE,yBAAyB,EAAE,MAAM,qFAAqF,CAAC;AAChI,OAAO,EAAE,6BAA6B,EAAE,MAAM,yFAAyF,CAAC;AACxI,OAAO,EAAE,0BAA0B,EAAE,MAAM,sFAAsF,CAAC;AAClI,OAAO,EAAE,yBAAyB,EAAE,MAAM,qFAAqF,CAAC;AAChI,OAAO,EAAE,yBAAyB,EAAE,MAAM,qFAAqF,CAAC;AAChI,OAAO,EAAE,2BAA2B,EAAE,MAAM,uFAAuF,CAAC;AACpI,OAAO,EAAE,uBAAuB,EAAE,MAAM,mFAAmF,CAAC;AAC5H,OAAO,EAAE,6BAA6B,EAAE,MAAM,yFAAyF,CAAC;AACxI,OAAO,EAAE,qBAAqB,EAAE,MAAM,kEAAkE,CAAC;AACzG,OAAO,EAAE,uBAAuB,EAAE,MAAM,oEAAoE,CAAC;AAC7G,OAAO,EAAE,kCAAkC,EAAE,MAAM,+EAA+E,CAAC","sourcesContent":["export { createEnvironmentInfraCredentialPort } from './features/environment-lifecycle/adapters/outbound/createEnvironmentInfraCredentialPort.js';\nexport { createEnvironmentInfraSecretPort } from './features/environment-lifecycle/adapters/outbound/createEnvironmentInfraSecretPort.js';\nexport { createNodeInfraAdapterPackageResolver } from './features/environment-lifecycle/adapters/outbound/createNodeInfraAdapterPackageResolver.js';\nexport type {\n InfraAdapterPackageModule,\n InfraAdapterPackageResolver,\n} from './features/environment-lifecycle/application/ports/outbound/infraAdapterPackage.js';\nexport { destroyInfraEnvironmentAsync } from './features/environment-lifecycle/application/use-cases/destroyInfraEnvironmentAsync.js';\nexport { downInfraEnvironmentAsync } from './features/environment-lifecycle/application/use-cases/downInfraEnvironmentAsync.js';\nexport { generateInfraEnvironmentAsync } from './features/environment-lifecycle/application/use-cases/generateInfraEnvironmentAsync.js';\nexport { getInfraEnvironmentOutputs } from './features/environment-lifecycle/application/use-cases/getInfraEnvironmentOutputs.js';\nexport { planInfraEnvironmentAsync } from './features/environment-lifecycle/application/use-cases/planInfraEnvironmentAsync.js';\nexport { resolveInfraAdaptersAsync } from './features/environment-lifecycle/application/use-cases/resolveInfraAdaptersAsync.js';\nexport { statusInfraEnvironmentAsync } from './features/environment-lifecycle/application/use-cases/statusInfraEnvironmentAsync.js';\nexport { upInfraEnvironmentAsync } from './features/environment-lifecycle/application/use-cases/upInfraEnvironmentAsync.js';\nexport { validateInfraEnvironmentAsync } from './features/environment-lifecycle/application/use-cases/validateInfraEnvironmentAsync.js';\nexport { orderInfraPlanActions } from './features/environment-lifecycle/domain/orderInfraPlanActions.js';\nexport { resolveInfraEnvironment } from './features/environment-lifecycle/domain/resolveInfraEnvironment.js';\nexport { resolveInfraWorkloadApiComposition } from './features/environment-lifecycle/domain/resolveInfraWorkloadApiComposition.js';\nexport type {\n InfraDestroyOperationRequest,\n InfraDestroyOperationResult,\n InfraDestroyResult,\n InfraDownOperationResult,\n InfraDownResult,\n InfraGenerateOperationResult,\n InfraGenerateResult,\n InfraOperationRequest,\n InfraOrchestrationDependencies,\n InfraOutputsOperationResult,\n InfraOutputsResult,\n InfraPlanOperationResult,\n InfraStatusOperationResult,\n InfraStoredState,\n InfraUpOperationResult,\n InfraUpRequest,\n InfraUpResult,\n InfraValidateOperationResult,\n InfraValidateResult,\n PreparedInfraOperation,\n ResolvedInfraAdapters,\n ResolvedInfraEnvironment,\n} from './types/infraOrchestration.js';\nexport type { InfraWorkloadApiAssignment } from './types/infraWorkloadApiComposition.js';\n"]}
@@ -0,0 +1,6 @@
1
+ /** One internal API assigned to the workload that serves it. */
2
+ export interface InfraWorkloadApiAssignment {
3
+ readonly apiId: string;
4
+ readonly workloadId: string;
5
+ }
6
+ //# sourceMappingURL=infraWorkloadApiComposition.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"infraWorkloadApiComposition.d.ts","sourceRoot":"","sources":["../../src/types/infraWorkloadApiComposition.ts"],"names":[],"mappings":"AAAA,gEAAgE;AAChE,MAAM,WAAW,0BAA0B;IACzC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=infraWorkloadApiComposition.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"infraWorkloadApiComposition.js","sourceRoot":"","sources":["../../src/types/infraWorkloadApiComposition.ts"],"names":[],"mappings":"","sourcesContent":["/** One internal API assigned to the workload that serves it. */\nexport interface InfraWorkloadApiAssignment {\n readonly apiId: string;\n readonly workloadId: string;\n}\n"]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ankhorage/infra",
3
3
  "type": "module",
4
- "version": "7.2.11",
4
+ "version": "7.3.0",
5
5
  "description": "Project-aware infrastructure generation library and executable Ankh provider for Ankhorage.",
6
6
  "homepage": "https://github.com/ankhorage/infra#readme",
7
7
  "bugs": {
@@ -35,10 +35,10 @@
35
35
  "openapi"
36
36
  ],
37
37
  "dependencies": {
38
- "@ankhorage/ankh": "^0.13.2",
38
+ "@ankhorage/ankh": "^0.13.6",
39
39
  "@ankhorage/capability": "^0.2.2",
40
- "@ankhorage/contracts": "^26.0.1",
41
- "@ankhorage/utility": "^1.9.86"
40
+ "@ankhorage/contracts": "^26.10.0",
41
+ "@ankhorage/utility": "^1.10.0"
42
42
  },
43
43
  "files": [
44
44
  "dist",
@@ -230,19 +230,19 @@
230
230
  "knip:test": "bun run knip:check"
231
231
  },
232
232
  "devDependencies": {
233
- "@ankhorage/cerbos": "^0.2.0",
234
- "@ankhorage/devtools": "^2.3.21",
235
- "@ankhorage/docker-compose": "^0.5.3",
236
- "@ankhorage/hetzner": "^0.3.0",
237
- "@ankhorage/k3s": "^0.4.1",
238
- "@ankhorage/local": "^0.3.22",
239
- "@ankhorage/minikube": "^0.4.124",
233
+ "@ankhorage/infra-cerbos": "^0.2.3",
234
+ "@ankhorage/devtools": "^2.6.0",
235
+ "@ankhorage/infra-docker-compose": "^0.5.6",
236
+ "@ankhorage/infra-hetzner": "^0.3.3",
237
+ "@ankhorage/infra-k3s": "^0.4.4",
238
+ "@ankhorage/infra-local": "^0.3.31",
239
+ "@ankhorage/infra-minikube": "^0.4.132",
240
240
  "@ankhorage/paradox": "^0.3.5",
241
- "@ankhorage/r2": "^0.2.0",
242
- "@ankhorage/supabase": "^0.5.14",
241
+ "@ankhorage/infra-r2": "^0.3.0",
242
+ "@ankhorage/infra-supabase": "^0.6.0",
243
243
  "@ankhorage/supabase-vault": "^0.3.12",
244
244
  "@types/bun": "^1.4.2",
245
- "@types/node": "^26.6.5",
245
+ "@types/node": "^26.6.6",
246
246
  "typescript": "^6.0.3"
247
247
  },
248
248
  "packageManager": "bun@1.4.2"
@@ -54,10 +54,10 @@ describe('selected adapter resolution', () => {
54
54
 
55
55
  expect(result.ok).toBe(true);
56
56
  expect(requested).toEqual([
57
- '@ankhorage/local',
58
- '@ankhorage/docker-compose',
59
- '@ankhorage/supabase',
60
- '@ankhorage/cerbos',
57
+ '@ankhorage/infra-local',
58
+ '@ankhorage/infra-docker-compose',
59
+ '@ankhorage/infra-supabase',
60
+ '@ankhorage/infra-cerbos',
61
61
  ]);
62
62
  if (!result.ok) throw new Error('Expected adapters.');
63
63
  expect(result.value.services.map(({ descriptor }) => descriptor.id)).toEqual([
@@ -6,6 +6,7 @@ import type {
6
6
  PreparedInfraOperation,
7
7
  } from '../../../../types/infraOrchestration.js';
8
8
  import { resolveInfraEnvironment } from '../../domain/resolveInfraEnvironment.js';
9
+ import { resolveInfraWorkloadApiComposition } from '../../domain/resolveInfraWorkloadApiComposition.js';
9
10
  import { appendInfraFailure } from '../../utils/appendInfraFailure.js';
10
11
  import { createInfraExecutionContext } from '../../utils/createInfraExecutionContext.js';
11
12
  import { collectInfraWorkloadsAsync } from './collectInfraWorkloadsAsync.js';
@@ -57,6 +58,15 @@ export async function prepareInfraOperationAsync(
57
58
  const workloads = await collectInfraWorkloadsAsync(context, adapters.value.services);
58
59
  if (!workloads.ok) return appendInfraFailure(diagnostics, workloads);
59
60
  diagnostics.push(...workloads.diagnostics);
61
+ const allWorkloads = [
62
+ ...Object.values(environment.value.desired.workloads ?? {}),
63
+ ...workloads.value,
64
+ ];
65
+ const apiComposition = resolveInfraWorkloadApiComposition(
66
+ environment.value.manifest,
67
+ allWorkloads,
68
+ );
69
+ if (!apiComposition.ok) return appendInfraFailure(diagnostics, apiComposition);
60
70
  const compute = await adapters.value.compute.inspectAsync(
61
71
  context,
62
72
  environment.value.desired.deployment.compute,
@@ -69,7 +79,7 @@ export async function prepareInfraOperationAsync(
69
79
  compute.value.targets.length > 0
70
80
  ? compute.value.targets
71
81
  : (request.previous?.targets ?? compute.value.targets),
72
- workloads: [...Object.values(environment.value.desired.workloads ?? {}), ...workloads.value],
82
+ workloads: allWorkloads,
73
83
  availableOutputs: mergeOutputs(request.previous?.outputs ?? [], compute.value.outputs),
74
84
  };
75
85
  if (runtimeDesired.targets.length === 0) {
@@ -13,6 +13,7 @@ import type {
13
13
  InfraUpRequest,
14
14
  } from '../../../../types/infraOrchestration.js';
15
15
  import { resolveInfraEnvironment } from '../../domain/resolveInfraEnvironment.js';
16
+ import { resolveInfraWorkloadApiComposition } from '../../domain/resolveInfraWorkloadApiComposition.js';
16
17
  import { appendInfraFailure } from '../../utils/appendInfraFailure.js';
17
18
  import { createInfraExecutionContext } from '../../utils/createInfraExecutionContext.js';
18
19
  import { createInfraLedger } from '../../utils/createInfraLedger.js';
@@ -44,6 +45,18 @@ export async function upInfraEnvironmentAsync(
44
45
  const validation = await validateInfraAdaptersAsync(context, adapters.value);
45
46
  if (!validation.ok) return appendInfraFailure(diagnostics, validation);
46
47
  diagnostics.push(...validation.diagnostics);
48
+ const workloads = await collectInfraWorkloadsAsync(context, adapters.value.services);
49
+ if (!workloads.ok) return appendInfraFailure(diagnostics, workloads);
50
+ diagnostics.push(...workloads.diagnostics);
51
+ const allWorkloads = [
52
+ ...Object.values(environment.value.desired.workloads ?? {}),
53
+ ...workloads.value,
54
+ ];
55
+ const apiComposition = resolveInfraWorkloadApiComposition(
56
+ environment.value.manifest,
57
+ allWorkloads,
58
+ );
59
+ if (!apiComposition.ok) return appendInfraFailure(diagnostics, apiComposition);
47
60
  const compute = await adapters.value.compute.ensureAsync(
48
61
  context,
49
62
  environment.value.desired.deployment.compute,
@@ -53,13 +66,10 @@ export async function upInfraEnvironmentAsync(
53
66
  const preparation = await prepareInfraServicesAsync(context, adapters.value.services);
54
67
  if (!preparation.ok) return appendInfraFailure(diagnostics, preparation);
55
68
  diagnostics.push(...preparation.diagnostics);
56
- const workloads = await collectInfraWorkloadsAsync(context, adapters.value.services);
57
- if (!workloads.ok) return appendInfraFailure(diagnostics, workloads);
58
- diagnostics.push(...workloads.diagnostics);
59
69
  const runtimeDesired = {
60
70
  selection: environment.value.desired.deployment.runtime,
61
71
  targets: compute.value.targets,
62
- workloads: [...Object.values(environment.value.desired.workloads ?? {}), ...workloads.value],
72
+ workloads: allWorkloads,
63
73
  availableOutputs: mergeInfraOutputs(request.previous?.outputs ?? [], compute.value.outputs),
64
74
  };
65
75
  const runtimeValidation = await adapters.value.runtime.validateAsync(context, runtimeDesired);
@@ -0,0 +1,75 @@
1
+ import type {
2
+ InfraDiagnostic,
3
+ InfraManifest,
4
+ InfraResult,
5
+ InfraWorkloadSpec,
6
+ } from '@ankhorage/contracts/infra';
7
+
8
+ import type { InfraWorkloadApiAssignment } from '../../../types/infraWorkloadApiComposition.js';
9
+
10
+ /*** Resolve internal API declarations to exactly one declared workload without selecting HTTP transport. */
11
+ export function resolveInfraWorkloadApiComposition(
12
+ manifest: InfraManifest,
13
+ workloads: readonly InfraWorkloadSpec[],
14
+ ): InfraResult<readonly InfraWorkloadApiAssignment[]> {
15
+ const diagnostics: InfraDiagnostic[] = [];
16
+ const assignments: InfraWorkloadApiAssignment[] = [];
17
+ const apiDefinitions = new Map(Object.entries(manifest.apis ?? {}));
18
+ const assignedWorkloads = new Map<string, string>();
19
+
20
+ for (const workload of workloads) {
21
+ for (const apiId of Object.keys(workload.apis?.apiIds ?? {})) {
22
+ const api = apiDefinitions.get(apiId);
23
+ if (api === undefined) {
24
+ diagnostics.push(createMissingApiDiagnostic(workload.id, apiId));
25
+ continue;
26
+ }
27
+ if (api.origin !== 'internal') {
28
+ diagnostics.push(createExternalApiDiagnostic(workload.id, apiId));
29
+ continue;
30
+ }
31
+ const assignedWorkloadId = assignedWorkloads.get(apiId);
32
+ if (assignedWorkloadId !== undefined) {
33
+ diagnostics.push(createDuplicateApiDiagnostic(apiId, assignedWorkloadId, workload.id));
34
+ continue;
35
+ }
36
+ assignedWorkloads.set(apiId, workload.id);
37
+ assignments.push({ apiId, workloadId: workload.id });
38
+ }
39
+ }
40
+
41
+ return diagnostics.length === 0
42
+ ? { ok: true, value: assignments, diagnostics }
43
+ : { ok: false, diagnostics };
44
+ }
45
+
46
+ /*** Describe an API reference whose registry definition is unavailable to the desired state. */
47
+ function createMissingApiDiagnostic(workloadId: string, apiId: string) {
48
+ return {
49
+ severity: 'error' as const,
50
+ code: 'infra-workload-api-missing',
51
+ message: `Workload "${workloadId}" references API "${apiId}", but it is not declared in manifest.apis.`,
52
+ };
53
+ }
54
+
55
+ /*** Describe an external API incorrectly claimed as a workload-served API. */
56
+ function createExternalApiDiagnostic(workloadId: string, apiId: string) {
57
+ return {
58
+ severity: 'error' as const,
59
+ code: 'infra-workload-api-external',
60
+ message: `Workload "${workloadId}" references external API "${apiId}"; only internal APIs may be served by workloads.`,
61
+ };
62
+ }
63
+
64
+ /*** Describe the conflicting second workload for one internal API. */
65
+ function createDuplicateApiDiagnostic(
66
+ apiId: string,
67
+ assignedWorkloadId: string,
68
+ workloadId: string,
69
+ ) {
70
+ return {
71
+ severity: 'error' as const,
72
+ code: 'infra-workload-api-duplicate',
73
+ message: `Internal API "${apiId}" is assigned to both workloads "${assignedWorkloadId}" and "${workloadId}".`,
74
+ };
75
+ }
@@ -15,7 +15,7 @@ import {
15
15
  type HetznerProjectIdentity,
16
16
  type HetznerResourceId,
17
17
  infraAdapterDescriptor as hetznerInfraAdapterDescriptor,
18
- } from '@ankhorage/hetzner';
18
+ } from '@ankhorage/infra-hetzner';
19
19
  import {
20
20
  createInfraAdapter as createK3sInfraAdapter,
21
21
  infraAdapterDescriptor as k3sInfraAdapterDescriptor,
@@ -23,13 +23,13 @@ import {
23
23
  type K3sClusterSpec,
24
24
  type K3sControlPlane,
25
25
  type K3sNodeAccess,
26
- } from '@ankhorage/k3s';
26
+ } from '@ankhorage/infra-k3s';
27
27
  import {
28
28
  createInfraAdapter as createSupabaseInfraAdapter,
29
29
  infraAdapterDescriptor as supabaseInfraAdapterDescriptor,
30
30
  type SupabaseControlPlane,
31
31
  type SupabaseControlPlaneRequest,
32
- } from '@ankhorage/supabase';
32
+ } from '@ankhorage/infra-supabase';
33
33
  import { expect, test } from 'bun:test';
34
34
 
35
35
  import { destroyInfraEnvironmentAsync } from './features/environment-lifecycle/application/use-cases/destroyInfraEnvironmentAsync.js';
@@ -13,7 +13,7 @@ import {
13
13
  type HetznerProjectIdentity,
14
14
  type HetznerResourceId,
15
15
  infraAdapterDescriptor as hetznerInfraAdapterDescriptor,
16
- } from '@ankhorage/hetzner';
16
+ } from '@ankhorage/infra-hetzner';
17
17
  import {
18
18
  createInfraAdapter as createK3sInfraAdapter,
19
19
  infraAdapterDescriptor as k3sInfraAdapterDescriptor,
@@ -21,20 +21,20 @@ import {
21
21
  type K3sClusterSpec,
22
22
  type K3sControlPlane,
23
23
  type K3sNodeAccess,
24
- } from '@ankhorage/k3s';
24
+ } from '@ankhorage/infra-k3s';
25
25
  import {
26
26
  createInfraAdapter as createR2InfraAdapter,
27
27
  infraAdapterDescriptor as r2InfraAdapterDescriptor,
28
28
  type R2BucketControlPlane,
29
29
  type R2BucketObservation,
30
30
  type R2CloudRequest,
31
- } from '@ankhorage/r2';
31
+ } from '@ankhorage/infra-r2';
32
32
  import {
33
33
  createInfraAdapter as createSupabaseInfraAdapter,
34
34
  infraAdapterDescriptor as supabaseInfraAdapterDescriptor,
35
35
  type SupabaseControlPlane,
36
36
  type SupabaseControlPlaneRequest,
37
- } from '@ankhorage/supabase';
37
+ } from '@ankhorage/infra-supabase';
38
38
  import { expect, test } from 'bun:test';
39
39
 
40
40
  import { planInfraEnvironmentAsync } from './features/environment-lifecycle/application/use-cases/planInfraEnvironmentAsync.js';
@@ -57,7 +57,11 @@ const manifest = {
57
57
  },
58
58
  database: { provider: 'supabase', tier: 'prod' },
59
59
  auth: { provider: 'supabase' },
60
- objectStorage: { provider: 'r2', accountId: r2AccountId, buckets: [r2Bucket] },
60
+ objectStorage: {
61
+ provider: 'r2',
62
+ accountId: r2AccountId,
63
+ buckets: { [r2Bucket]: true },
64
+ },
61
65
  networking: {
62
66
  domain,
63
67
  publicBaseUrl,
package/src/index.test.ts CHANGED
@@ -16,6 +16,7 @@ describe('@ankhorage/infra', () => {
16
16
  'planInfraEnvironmentAsync',
17
17
  'resolveInfraAdaptersAsync',
18
18
  'resolveInfraEnvironment',
19
+ 'resolveInfraWorkloadApiComposition',
19
20
  'statusInfraEnvironmentAsync',
20
21
  'upInfraEnvironmentAsync',
21
22
  'validateInfraEnvironmentAsync',
package/src/index.ts CHANGED
@@ -16,6 +16,7 @@ export { upInfraEnvironmentAsync } from './features/environment-lifecycle/applic
16
16
  export { validateInfraEnvironmentAsync } from './features/environment-lifecycle/application/use-cases/validateInfraEnvironmentAsync.js';
17
17
  export { orderInfraPlanActions } from './features/environment-lifecycle/domain/orderInfraPlanActions.js';
18
18
  export { resolveInfraEnvironment } from './features/environment-lifecycle/domain/resolveInfraEnvironment.js';
19
+ export { resolveInfraWorkloadApiComposition } from './features/environment-lifecycle/domain/resolveInfraWorkloadApiComposition.js';
19
20
  export type {
20
21
  InfraDestroyOperationRequest,
21
22
  InfraDestroyOperationResult,
@@ -40,3 +41,4 @@ export type {
40
41
  ResolvedInfraAdapters,
41
42
  ResolvedInfraEnvironment,
42
43
  } from './types/infraOrchestration.js';
44
+ export type { InfraWorkloadApiAssignment } from './types/infraWorkloadApiComposition.js';
@@ -60,6 +60,33 @@ describe('provider-neutral lifecycle operations', () => {
60
60
  expect(calls.some((call) => call.endsWith('.destroy'))).toBe(false);
61
61
  });
62
62
 
63
+ it('refuses an undeclared workload API before provider inspection or planning', async () => {
64
+ const calls: string[] = [];
65
+ const invalidManifest = {
66
+ ...manifest,
67
+ environments: {
68
+ local: {
69
+ ...desired,
70
+ workloads: {
71
+ api: { ...workload('api'), apis: { apiIds: { missing: true } } },
72
+ },
73
+ },
74
+ },
75
+ };
76
+
77
+ const result = await planInfraEnvironmentAsync(
78
+ { projectId: 'sample', manifest: invalidManifest },
79
+ createDependencies(calls),
80
+ );
81
+
82
+ expect(result).toMatchObject({
83
+ ok: false,
84
+ diagnostics: [{ code: 'infra-workload-api-missing' }],
85
+ });
86
+ expect(calls).not.toContain('local.inspect');
87
+ expect(calls.some((call) => call.endsWith('.plan'))).toBe(false);
88
+ });
89
+
63
90
  it('plans fresh remote compute and a deferred runtime without inventing an SSH target', async () => {
64
91
  const calls: string[] = [];
65
92
  const remoteManifest = {
@@ -39,11 +39,11 @@ describe('environment up orchestration', () => {
39
39
  'local.validate',
40
40
  'supabase.validate',
41
41
  'cerbos.validate',
42
+ 'supabase.workloads',
43
+ 'cerbos.workloads',
42
44
  'local.ensure',
43
45
  'supabase.prepare',
44
46
  'cerbos.prepare',
45
- 'supabase.workloads',
46
- 'cerbos.workloads',
47
47
  'docker-compose.validate',
48
48
  'docker-compose.ensure',
49
49
  'supabase.reconcile',
@@ -83,7 +83,36 @@ describe('environment up orchestration', () => {
83
83
  expect(calls).toEqual(['local.validate', 'supabase.validate']);
84
84
  });
85
85
 
86
- it('stops before workload materialization when service preparation fails', async () => {
86
+ it('stops before mutation when workload API composition is invalid', async () => {
87
+ const calls: string[] = [];
88
+ const invalidManifest = {
89
+ ...manifest,
90
+ environments: {
91
+ local: {
92
+ ...environment,
93
+ workloads: {
94
+ api: {
95
+ ...workload('api'),
96
+ apis: { apiIds: { missing: true } },
97
+ },
98
+ },
99
+ },
100
+ },
101
+ };
102
+ const result = await upInfraEnvironmentAsync(
103
+ { projectId: 'sample', manifest: invalidManifest },
104
+ createDependencies(calls, [], []),
105
+ );
106
+
107
+ expect(result).toMatchObject({
108
+ ok: false,
109
+ diagnostics: [{ code: 'infra-workload-api-missing' }],
110
+ });
111
+ expect(calls.some((call) => call.endsWith('.ensure'))).toBe(false);
112
+ expect(calls.some((call) => call.endsWith('.prepare'))).toBe(false);
113
+ });
114
+
115
+ it('stops before runtime reconciliation when service preparation fails', async () => {
87
116
  const calls: string[] = [];
88
117
  const dependencies = createDependencies(calls, [], [], 'supabase.prepare');
89
118
  const result = await upInfraEnvironmentAsync({ projectId: 'sample', manifest }, dependencies);
@@ -97,10 +126,12 @@ describe('environment up orchestration', () => {
97
126
  'local.validate',
98
127
  'supabase.validate',
99
128
  'cerbos.validate',
129
+ 'supabase.workloads',
130
+ 'cerbos.workloads',
100
131
  'local.ensure',
101
132
  'supabase.prepare',
102
133
  ]);
103
- expect(calls).not.toContain('supabase.workloads');
134
+ expect(calls).not.toContain('docker-compose.validate');
104
135
  expect(calls).not.toContain('docker-compose.ensure');
105
136
  });
106
137
 
@@ -16,19 +16,19 @@ import {
16
16
  type K3sClusterSpec,
17
17
  type K3sControlPlane,
18
18
  type K3sNodeAccess,
19
- } from '@ankhorage/k3s';
19
+ } from '@ankhorage/infra-k3s';
20
20
  import {
21
21
  createInfraAdapter as createLocalInfraAdapter,
22
22
  infraAdapterDescriptor as localInfraAdapterDescriptor,
23
23
  type LocalHostObservation,
24
24
  type LocalHostProbe,
25
- } from '@ankhorage/local';
25
+ } from '@ankhorage/infra-local';
26
26
  import {
27
27
  createInfraAdapter as createSupabaseInfraAdapter,
28
28
  infraAdapterDescriptor as supabaseInfraAdapterDescriptor,
29
29
  type SupabaseControlPlane,
30
30
  type SupabaseControlPlaneRequest,
31
- } from '@ankhorage/supabase';
31
+ } from '@ankhorage/infra-supabase';
32
32
  import { expect, test } from 'bun:test';
33
33
 
34
34
  import { destroyInfraEnvironmentAsync } from './features/environment-lifecycle/application/use-cases/destroyInfraEnvironmentAsync.js';
@@ -7,7 +7,7 @@ import type {
7
7
  InfraResourceStatus,
8
8
  InfraResult,
9
9
  } from '@ankhorage/contracts/infra';
10
- import { infraAdapterDescriptor as hetznerInfraAdapterDescriptor } from '@ankhorage/hetzner';
10
+ import { infraAdapterDescriptor as hetznerInfraAdapterDescriptor } from '@ankhorage/infra-hetzner';
11
11
  import {
12
12
  createInfraAdapter as createK3sInfraAdapter,
13
13
  infraAdapterDescriptor as k3sInfraAdapterDescriptor,
@@ -15,7 +15,7 @@ import {
15
15
  type K3sClusterSpec,
16
16
  type K3sControlPlane,
17
17
  type K3sNodeAccess,
18
- } from '@ankhorage/k3s';
18
+ } from '@ankhorage/infra-k3s';
19
19
  import { expect, test } from 'bun:test';
20
20
 
21
21
  import { type InfraOrchestrationDependencies, upInfraEnvironmentAsync } from './index.js';
@@ -0,0 +1,87 @@
1
+ import type { InfraManifest, InfraWorkloadSpec } from '@ankhorage/contracts/infra';
2
+ import { describe, expect, it } from 'bun:test';
3
+
4
+ import { resolveInfraWorkloadApiComposition } from './features/environment-lifecycle/domain/resolveInfraWorkloadApiComposition.js';
5
+
6
+ const environment = {
7
+ deployment: { compute: { provider: 'local' }, runtime: { provider: 'docker-compose' } },
8
+ } as const;
9
+
10
+ describe('resolveInfraWorkloadApiComposition', () => {
11
+ it('assigns every declared internal API to its single workload', () => {
12
+ const result = resolveInfraWorkloadApiComposition(manifest(), [workload('api', ['projects'])]);
13
+
14
+ expect(result).toEqual({
15
+ ok: true,
16
+ value: [{ apiId: 'projects', workloadId: 'api' }],
17
+ diagnostics: [],
18
+ });
19
+ });
20
+
21
+ it('fails closed for missing API declarations and external API targets', () => {
22
+ const result = resolveInfraWorkloadApiComposition(manifest(), [
23
+ workload('api', ['missing', 'third-party']),
24
+ ]);
25
+
26
+ expect(result).toMatchObject({
27
+ ok: false,
28
+ diagnostics: [
29
+ { code: 'infra-workload-api-missing' },
30
+ { code: 'infra-workload-api-external' },
31
+ ],
32
+ });
33
+ });
34
+
35
+ it('fails closed when multiple workloads claim one internal API', () => {
36
+ const result = resolveInfraWorkloadApiComposition(manifest(), [
37
+ workload('api-a', ['projects']),
38
+ workload('api-b', ['projects']),
39
+ ]);
40
+
41
+ expect(result).toMatchObject({
42
+ ok: false,
43
+ diagnostics: [{ code: 'infra-workload-api-duplicate' }],
44
+ });
45
+ });
46
+
47
+ it('fails closed when a workload references APIs without an API registry', () => {
48
+ const { apis: _apis, ...withoutApis } = manifest();
49
+ const result = resolveInfraWorkloadApiComposition(withoutApis, [workload('api', ['projects'])]);
50
+
51
+ expect(result).toMatchObject({
52
+ ok: false,
53
+ diagnostics: [{ code: 'infra-workload-api-missing' }],
54
+ });
55
+ });
56
+ });
57
+
58
+ function manifest(): InfraManifest {
59
+ return {
60
+ environments: { local: environment },
61
+ apis: {
62
+ projects: {
63
+ id: 'projects',
64
+ origin: 'internal',
65
+ protocol: 'rest',
66
+ basePath: '/projects',
67
+ endpoints: {},
68
+ },
69
+ 'third-party': {
70
+ id: 'third-party',
71
+ origin: 'external',
72
+ protocol: 'rest',
73
+ baseUrl: 'https://third-party.example.test',
74
+ endpoints: {},
75
+ },
76
+ },
77
+ modules: {},
78
+ };
79
+ }
80
+
81
+ function workload(id: string, apiIds: readonly string[]): InfraWorkloadSpec {
82
+ return {
83
+ id,
84
+ artifact: { kind: 'image', image: `example/${id}:1` },
85
+ apis: { apiIds: Object.fromEntries(apiIds.map((apiId) => [apiId, true])) },
86
+ };
87
+ }
@@ -0,0 +1,5 @@
1
+ /** One internal API assigned to the workload that serves it. */
2
+ export interface InfraWorkloadApiAssignment {
3
+ readonly apiId: string;
4
+ readonly workloadId: string;
5
+ }