@apifuse/provider-sdk 2.2.0-beta.62 → 2.2.0-beta.64
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.
- package/AUTHORING.md +367 -9
- package/CHANGELOG.md +8 -0
- package/LICENSE +21 -0
- package/README.md +1 -0
- package/bin/apifuse-check.ts +84 -2
- package/bin/apifuse-dev.ts +11 -31
- package/bin/apifuse-migrate-deployment.ts +200 -0
- package/bin/apifuse-pack-check.ts +11 -4
- package/bin/apifuse-pack-types.ts +187 -4
- package/bin/apifuse-record.ts +44 -32
- package/bin/apifuse-submit-check.ts +9 -5
- package/bin/apifuse-test.ts +3 -1
- package/dist/cli/commands.d.ts +1 -1
- package/dist/cli/commands.js +11 -0
- package/dist/cli/deployment-intent.d.ts +178 -0
- package/dist/cli/deployment-intent.js +277 -0
- package/dist/cli/migrate-deployment-intent.d.ts +132 -0
- package/dist/cli/migrate-deployment-intent.js +1552 -0
- package/dist/cli/migrate-provider-shape.d.ts +5 -1
- package/dist/cli/migrate-provider-shape.js +4 -2
- package/dist/cli/resolver-runtime.d.ts +21 -0
- package/dist/cli/resolver-runtime.js +31 -0
- package/dist/define.d.ts +3 -0
- package/dist/define.js +5 -0
- package/dist/dev.d.ts +3 -1
- package/dist/dev.js +6 -1
- package/dist/engine.d.ts +35 -1
- package/dist/engine.js +21 -2
- package/dist/error-resolution.d.ts +1 -0
- package/dist/error-resolution.js +54 -0
- package/dist/errors.d.ts +39 -1
- package/dist/errors.js +50 -0
- package/dist/handle-meta.d.ts +36 -1
- package/dist/handle-meta.js +16 -0
- package/dist/handle.d.ts +23 -5
- package/dist/handle.js +54 -8
- package/dist/health-scenario.d.ts +38 -0
- package/dist/i18n/error-messages.d.ts +86 -0
- package/dist/i18n/error-messages.js +192 -0
- package/dist/i18n/index.d.ts +1 -0
- package/dist/i18n/index.js +1 -0
- package/dist/i18n/keys.d.ts +12 -0
- package/dist/i18n/keys.js +76 -5
- package/dist/index.d.ts +11 -3
- package/dist/index.js +9 -1
- package/dist/lint.d.ts +15 -0
- package/dist/lint.js +469 -82
- package/dist/provider.d.ts +6 -6
- package/dist/provider.js +3 -3
- package/dist/runtime/browser-telemetry-binding.d.ts +5 -0
- package/dist/runtime/browser-telemetry-binding.js +83 -0
- package/dist/runtime/browser-telemetry.d.ts +76 -0
- package/dist/runtime/browser-telemetry.js +165 -0
- package/dist/runtime/browser.d.ts +3 -0
- package/dist/runtime/browser.js +208 -47
- package/dist/runtime/cache-telemetry.d.ts +71 -0
- package/dist/runtime/cache-telemetry.js +177 -0
- package/dist/runtime/cache.js +67 -7
- package/dist/runtime/capability-response.d.ts +3 -0
- package/dist/runtime/capability-response.js +56 -0
- package/dist/runtime/capability-telemetry.d.ts +46 -0
- package/dist/runtime/capability-telemetry.js +68 -0
- package/dist/runtime/diagnostic-env.d.ts +3 -0
- package/dist/runtime/diagnostic-env.js +10 -1
- package/dist/runtime/engine-mode.d.ts +74 -0
- package/dist/runtime/engine-mode.js +152 -0
- package/dist/runtime/handle.js +9 -1
- package/dist/runtime/http-telemetry-guard.d.ts +10 -0
- package/dist/runtime/http-telemetry-guard.js +62 -10
- package/dist/runtime/http.js +141 -35
- package/dist/runtime/instrumentation.js +92 -13
- package/dist/runtime/ocr-telemetry.d.ts +82 -0
- package/dist/runtime/ocr-telemetry.js +192 -0
- package/dist/runtime/ocr.js +35 -8
- package/dist/runtime/request-telemetry.d.ts +2 -0
- package/dist/runtime/request-telemetry.js +2 -1
- package/dist/runtime/resolver-runtime-options.d.ts +14 -0
- package/dist/runtime/resolver-runtime-options.js +33 -0
- package/dist/runtime/resolver-shared.d.ts +3 -2
- package/dist/runtime/resolver-shared.js +4 -4
- package/dist/runtime/resolver-telemetry.d.ts +1 -0
- package/dist/runtime/resolver-telemetry.js +1 -1
- package/dist/runtime/resolver-vendors/browser.d.ts +2 -0
- package/dist/runtime/resolver-vendors/browser.js +46 -0
- package/dist/runtime/resolver-vendors/hosts.d.ts +21 -0
- package/dist/runtime/resolver-vendors/hosts.js +35 -0
- package/dist/runtime/resolver-vendors/hypersolutions.js +2 -2
- package/dist/runtime/resolver-vendors/types.d.ts +5 -4
- package/dist/runtime/resolver.d.ts +4 -1
- package/dist/runtime/resolver.js +46 -13
- package/dist/runtime/state-telemetry.d.ts +79 -0
- package/dist/runtime/state-telemetry.js +194 -0
- package/dist/runtime/state.d.ts +3 -0
- package/dist/runtime/state.js +26 -7
- package/dist/runtime/stealth-cookies.d.ts +5 -1
- package/dist/runtime/stealth-cookies.js +21 -1
- package/dist/runtime/stealth-owned-headers.d.ts +26 -0
- package/dist/runtime/stealth-owned-headers.js +37 -0
- package/dist/runtime/stealth-telemetry.d.ts +92 -0
- package/dist/runtime/stealth-telemetry.js +179 -0
- package/dist/runtime/stealth.d.ts +4 -1
- package/dist/runtime/stealth.js +228 -85
- package/dist/runtime/stt-telemetry.d.ts +77 -0
- package/dist/runtime/stt-telemetry.js +176 -0
- package/dist/runtime/stt.js +15 -6
- package/dist/runtime-boundary-lint.d.ts +132 -0
- package/dist/runtime-boundary-lint.js +586 -0
- package/dist/server/error-stack-frames.d.ts +35 -0
- package/dist/server/error-stack-frames.js +219 -0
- package/dist/server/index.d.ts +7 -0
- package/dist/server/index.js +4 -0
- package/dist/server/sdk-version.d.ts +3 -0
- package/dist/server/sdk-version.js +19 -0
- package/dist/server/self-test.js +1 -18
- package/dist/server/sensitive-values.d.ts +1 -1
- package/dist/server/sensitive-values.js +9 -0
- package/dist/server/serve-implementation.d.ts +51 -2
- package/dist/server/serve-implementation.js +337 -90
- package/dist/server/types.d.ts +1 -0
- package/dist/server/types.js +7 -0
- package/dist/stateful/stateful-provider-owner-forwarder.js +22 -0
- package/dist/stateful/stateful-provider-runtime-context.d.ts +3 -0
- package/dist/stateful/stateful-provider-runtime-context.js +1 -0
- package/dist/types.d.ts +123 -1
- package/dist/typescript-module.d.ts +39 -0
- package/dist/typescript-module.js +72 -0
- package/package.json +6 -1
- package/src/cli/commands.ts +14 -0
- package/src/cli/deployment-intent.ts +364 -0
- package/src/cli/migrate-deployment-intent.ts +1908 -0
- package/src/cli/migrate-provider-shape.ts +10 -4
- package/src/cli/resolver-runtime.ts +72 -0
- package/src/define.ts +13 -0
- package/src/dev.ts +9 -2
- package/src/engine.ts +38 -2
- package/src/error-resolution.ts +58 -0
- package/src/errors.ts +77 -1
- package/src/handle-meta.ts +51 -1
- package/src/handle.ts +92 -10
- package/src/health-scenario.ts +38 -0
- package/src/i18n/error-messages.ts +255 -0
- package/src/i18n/index.ts +1 -0
- package/src/i18n/keys.ts +80 -6
- package/src/index.ts +86 -0
- package/src/lint.ts +604 -91
- package/src/provider.ts +12 -0
- package/src/runtime/browser-telemetry-binding.ts +92 -0
- package/src/runtime/browser-telemetry.ts +289 -0
- package/src/runtime/browser.ts +249 -65
- package/src/runtime/cache-telemetry.ts +247 -0
- package/src/runtime/cache.ts +68 -17
- package/src/runtime/capability-response.ts +53 -0
- package/src/runtime/capability-telemetry.ts +101 -0
- package/src/runtime/diagnostic-env.ts +11 -1
- package/src/runtime/engine-mode.ts +233 -0
- package/src/runtime/handle.ts +9 -1
- package/src/runtime/http-telemetry-guard.ts +66 -13
- package/src/runtime/http.ts +168 -37
- package/src/runtime/instrumentation.ts +105 -16
- package/src/runtime/ocr-telemetry.ts +308 -0
- package/src/runtime/ocr.ts +114 -74
- package/src/runtime/request-telemetry.ts +2 -1
- package/src/runtime/resolver-runtime-options.ts +50 -0
- package/src/runtime/resolver-shared.ts +9 -4
- package/src/runtime/resolver-telemetry.ts +1 -1
- package/src/runtime/resolver-vendors/browser.ts +65 -7
- package/src/runtime/resolver-vendors/hosts.ts +43 -0
- package/src/runtime/resolver-vendors/hypersolutions.ts +2 -2
- package/src/runtime/resolver-vendors/types.ts +5 -4
- package/src/runtime/resolver.ts +64 -21
- package/src/runtime/state-telemetry.ts +298 -0
- package/src/runtime/state.ts +46 -45
- package/src/runtime/stealth-cookies.ts +24 -1
- package/src/runtime/stealth-owned-headers.ts +41 -0
- package/src/runtime/stealth-telemetry.ts +304 -0
- package/src/runtime/stealth.ts +355 -150
- package/src/runtime/stt-telemetry.ts +282 -0
- package/src/runtime/stt.ts +93 -71
- package/src/runtime-boundary-lint.ts +703 -0
- package/src/server/error-stack-frames.ts +214 -0
- package/src/server/index.ts +53 -0
- package/src/server/sdk-version.ts +22 -0
- package/src/server/self-test.ts +1 -21
- package/src/server/sensitive-values.ts +14 -0
- package/src/server/serve-implementation.ts +569 -108
- package/src/server/types.ts +7 -0
- package/src/stateful/stateful-provider-owner-forwarder.ts +23 -0
- package/src/stateful/stateful-provider-runtime-context.ts +4 -0
- package/src/types.ts +128 -1
- package/src/typescript-module.ts +100 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/approval-override.ts.txt +0 -6
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/codemod-syntax.ts.txt +0 -3
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/connection-precedence.ts.txt +0 -10
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-ambiguous-a.ts.txt +0 -3
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-ambiguous-b.ts.txt +0 -3
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-ambiguous-barrel.ts.txt +0 -2
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-ambiguous-index.ts.txt +0 -3
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-computed.ts.txt +0 -6
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-cycle-a.ts.txt +0 -1
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-cycle-b.ts.txt +0 -1
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-cycle-index.ts.txt +0 -3
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-daiso-barrel.ts.txt +0 -7
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-daiso-catalog.ts.txt +0 -19
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-daiso-docs.ts.txt +0 -6
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-daiso-index.ts.txt +0 -3
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-daiso-stores.ts.txt +0 -8
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-indirect-config.ts.txt +0 -5
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-nonstatic.ts.txt +0 -4
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-package-index.ts.txt +0 -3
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-reexport-index.ts.txt +0 -3
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-reexport-leaf.ts.txt +0 -10
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-reexport-one.ts.txt +0 -1
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-reexport-two.ts.txt +0 -1
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-runtime-composed-index.ts.txt +0 -3
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-runtime-composed-one.ts.txt +0 -6
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-runtime-composed-two.ts.txt +0 -6
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-runtime-mixed-index.ts.txt +0 -14
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-runtime-registry.ts.txt +0 -3
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-star-package-barrel.ts.txt +0 -2
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-star-package-index.ts.txt +0 -3
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-star-package-leaf.ts.txt +0 -3
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/docs-conflict.ts.txt +0 -8
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-hoisted-array-spread.ts.txt +0 -15
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-imported-array-spread.ts.txt +0 -9
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-kebab-operation-id.ts.txt +0 -17
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-map.ts.txt +0 -5
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-mixed-array-spread.ts.txt +0 -24
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-operation.ts.txt +0 -16
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-snake-operation-id.ts.txt +0 -21
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/factory-duplicate-provider.ts.txt +0 -11
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/factory-id-unresolved.ts.txt +0 -7
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/factory-map.ts.txt +0 -3
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/factory-one-to-many.ts.txt +0 -18
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/factory-one-to-one.ts.txt +0 -13
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/hoist-all.ts.txt +0 -31
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/hoisted-const.ts.txt +0 -11
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/imported-spread.ts.txt +0 -11
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/inline-map.ts.txt +0 -11
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/inline-spread-cast-tail.ts.txt +0 -21
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/inline-spread-ekitan.ts.txt +0 -11
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/inline-spread-override.ts.txt +0 -14
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/locale-canonical-en.json +0 -1
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/locale-canonical-ja.json +0 -1
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/locale-canonical-ko.json +0 -1
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/locale-existing-operation-namespace-en.json +0 -8
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/missing-english-locale.ts.txt +0 -7
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/no-safety.ts.txt +0 -6
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/non-literal.ts.txt +0 -7
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/redundant-approval.ts.txt +0 -6
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/registry-binding-ambiguous.ts.txt +0 -12
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/registry-imported-other.ts.txt +0 -7
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/registry-imported.ts.txt +0 -5
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/registry-key-ambiguous.ts.txt +0 -19
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/registry-shorthand.ts.txt +0 -9
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/registry-typed.ts.txt +0 -11
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/registry-unrelated.ts.txt +0 -13
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/safety-conflict.ts.txt +0 -7
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/stream.ts.txt +0 -7
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/tool-router-spread.ts.txt +0 -15
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/unparseable.ts.txt +0 -4
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/verbatim-template.ts.txt +0 -12
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
import type { ProviderDeploymentOverrides } from "../types.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Deployment-intent helpers shared by `apifuse check` and
|
|
7
|
+
* `apifuse migrate-deployment`.
|
|
8
|
+
*
|
|
9
|
+
* Deployment intent has exactly one authored home: the optional `deployment`
|
|
10
|
+
* key on `defineProvider()`. The APIFuse registry builder is the only reader;
|
|
11
|
+
* it resolves omitted fields from a runtime profile and rejects anything it
|
|
12
|
+
* cannot resolve. This module mirrors that profile table so the CLI can
|
|
13
|
+
* (a) subtract profile defaults when hoisting a legacy `deploy.ts` into the
|
|
14
|
+
* key and (b) warn when a declared key restates a default. The registry
|
|
15
|
+
* stays canonical — keep the constants below identical to
|
|
16
|
+
* `resolveProviderDeploymentConfig` in `@apifuse/provider-registry`.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
export type DeploymentRuntime = "shared" | "dedicated" | "browser";
|
|
20
|
+
export type DeploymentLanguage = "typescript" | "python";
|
|
21
|
+
|
|
22
|
+
export interface DeploymentResources {
|
|
23
|
+
cpu: string;
|
|
24
|
+
memory: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface DeploymentHpa {
|
|
28
|
+
enabled: boolean;
|
|
29
|
+
minReplicas?: number;
|
|
30
|
+
maxReplicas?: number;
|
|
31
|
+
targetCPUUtilizationPercentage?: number;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Fully resolved deployment view: what the platform materializes for a provider. */
|
|
35
|
+
export interface ResolvedDeploymentIntent {
|
|
36
|
+
runtime: DeploymentRuntime;
|
|
37
|
+
language: DeploymentLanguage;
|
|
38
|
+
replicas: number;
|
|
39
|
+
hpa: DeploymentHpa;
|
|
40
|
+
resources: DeploymentResources;
|
|
41
|
+
cache?: { redis?: { enabled: boolean; url?: string } };
|
|
42
|
+
network?: { additionalTcpPorts: number[] };
|
|
43
|
+
buildContext: string;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Runtime-profile defaults. `dedicated` carries no resource profile and
|
|
48
|
+
* therefore always needs explicit `resources`.
|
|
49
|
+
*/
|
|
50
|
+
export const DEPLOYMENT_PROFILE_DEFAULTS = Object.freeze({
|
|
51
|
+
runtime: "shared" as DeploymentRuntime,
|
|
52
|
+
language: "typescript" as DeploymentLanguage,
|
|
53
|
+
replicas: 1,
|
|
54
|
+
hpa: Object.freeze({
|
|
55
|
+
enabled: true,
|
|
56
|
+
minReplicas: 1,
|
|
57
|
+
maxReplicas: 1,
|
|
58
|
+
targetCPUUtilizationPercentage: 70,
|
|
59
|
+
}) as Readonly<DeploymentHpa>,
|
|
60
|
+
buildContext: ".",
|
|
61
|
+
resources: Object.freeze({
|
|
62
|
+
shared: Object.freeze({ cpu: "25m", memory: "128Mi" }),
|
|
63
|
+
browser: Object.freeze({ cpu: "200m", memory: "256Mi" }),
|
|
64
|
+
dedicated: undefined,
|
|
65
|
+
}) as Readonly<Record<DeploymentRuntime, Readonly<DeploymentResources> | undefined>>,
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
const BUILD_CONTEXT_CONSTRAINT =
|
|
69
|
+
'only "." is accepted; external provider images always build from the materialized checkout root';
|
|
70
|
+
|
|
71
|
+
const hpaSchema = z
|
|
72
|
+
.object({
|
|
73
|
+
enabled: z.boolean(),
|
|
74
|
+
minReplicas: z.number().int().positive().optional(),
|
|
75
|
+
maxReplicas: z.number().int().positive().optional(),
|
|
76
|
+
targetCPUUtilizationPercentage: z.number().int().positive().optional(),
|
|
77
|
+
})
|
|
78
|
+
.strict();
|
|
79
|
+
|
|
80
|
+
const resourcesSchema = z.object({ cpu: z.string().min(1), memory: z.string().min(1) }).strict();
|
|
81
|
+
|
|
82
|
+
const cacheSchema = z
|
|
83
|
+
.object({
|
|
84
|
+
redis: z
|
|
85
|
+
.object({ enabled: z.boolean(), url: z.string().min(1).optional() })
|
|
86
|
+
.strict()
|
|
87
|
+
.optional(),
|
|
88
|
+
})
|
|
89
|
+
.strict();
|
|
90
|
+
|
|
91
|
+
const networkSchema = z
|
|
92
|
+
.object({
|
|
93
|
+
additionalTcpPorts: z.array(z.number().int().min(1).max(65535)).default([]),
|
|
94
|
+
})
|
|
95
|
+
.strict();
|
|
96
|
+
|
|
97
|
+
const buildContextSchema = z
|
|
98
|
+
.string()
|
|
99
|
+
.min(1)
|
|
100
|
+
.refine((value) => value === ".", { message: BUILD_CONTEXT_CONSTRAINT });
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* The standalone `deploy.ts` contract (legacy). Mirrors the registry's
|
|
104
|
+
* `ProviderDeploymentConfigSchema`, including its own defaults — note that a
|
|
105
|
+
* legacy file without `hpa` resolved to `{ enabled: false }`, not the
|
|
106
|
+
* profile HPA, so the two default tables are deliberately different.
|
|
107
|
+
*/
|
|
108
|
+
export const legacyDeploymentConfigSchema = z
|
|
109
|
+
.object({
|
|
110
|
+
runtime: z.enum(["shared", "dedicated", "browser"]),
|
|
111
|
+
language: z.enum(["typescript", "python"]),
|
|
112
|
+
replicas: z.number().int().positive().default(1),
|
|
113
|
+
hpa: hpaSchema.default({ enabled: false }),
|
|
114
|
+
resources: resourcesSchema,
|
|
115
|
+
cache: cacheSchema.optional(),
|
|
116
|
+
network: networkSchema.optional(),
|
|
117
|
+
buildContext: buildContextSchema.optional(),
|
|
118
|
+
})
|
|
119
|
+
.strict();
|
|
120
|
+
|
|
121
|
+
/** The authored `deployment` key: every field optional (registry `ProviderDeploymentOverridesSchema`). */
|
|
122
|
+
export const deploymentOverridesSchema = z
|
|
123
|
+
.object({
|
|
124
|
+
runtime: z.enum(["shared", "dedicated", "browser"]).optional(),
|
|
125
|
+
language: z.enum(["typescript", "python"]).optional(),
|
|
126
|
+
replicas: z.number().int().positive().optional(),
|
|
127
|
+
hpa: hpaSchema.optional(),
|
|
128
|
+
resources: resourcesSchema.optional(),
|
|
129
|
+
cache: cacheSchema.optional(),
|
|
130
|
+
network: networkSchema.optional(),
|
|
131
|
+
buildContext: buildContextSchema.optional(),
|
|
132
|
+
})
|
|
133
|
+
.strict();
|
|
134
|
+
|
|
135
|
+
export type DeploymentParse<T> =
|
|
136
|
+
| { readonly ok: true; readonly value: T }
|
|
137
|
+
| { readonly ok: false; readonly reason: string };
|
|
138
|
+
|
|
139
|
+
function formatZodIssues(error: z.ZodError): string {
|
|
140
|
+
return error.issues
|
|
141
|
+
.map((issue) => {
|
|
142
|
+
const path = issue.path.map(String).join(".");
|
|
143
|
+
return path ? `${path}: ${issue.message}` : issue.message;
|
|
144
|
+
})
|
|
145
|
+
.join("; ");
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/** Parse a legacy `deploy.ts` default export into the resolved view. */
|
|
149
|
+
export function normalizeLegacyDeployment(
|
|
150
|
+
value: unknown,
|
|
151
|
+
): DeploymentParse<ResolvedDeploymentIntent> {
|
|
152
|
+
const parsed = legacyDeploymentConfigSchema.safeParse(value);
|
|
153
|
+
if (!parsed.success) {
|
|
154
|
+
return {
|
|
155
|
+
ok: false,
|
|
156
|
+
reason: `deploy.ts is not a valid deployment config: ${formatZodIssues(parsed.error)}`,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
const legacy = parsed.data;
|
|
160
|
+
return {
|
|
161
|
+
ok: true,
|
|
162
|
+
value: {
|
|
163
|
+
runtime: legacy.runtime,
|
|
164
|
+
language: legacy.language,
|
|
165
|
+
replicas: legacy.replicas,
|
|
166
|
+
hpa: legacy.hpa,
|
|
167
|
+
resources: legacy.resources,
|
|
168
|
+
...(legacy.cache ? { cache: legacy.cache } : {}),
|
|
169
|
+
...(legacy.network ? { network: legacy.network } : {}),
|
|
170
|
+
buildContext: legacy.buildContext ?? DEPLOYMENT_PROFILE_DEFAULTS.buildContext,
|
|
171
|
+
},
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/** Parse an authored `deployment` key against the strict overrides contract. */
|
|
176
|
+
export function parseDeploymentIntent(
|
|
177
|
+
value: unknown,
|
|
178
|
+
): DeploymentParse<ProviderDeploymentOverrides> {
|
|
179
|
+
const parsed = deploymentOverridesSchema.safeParse(value);
|
|
180
|
+
if (!parsed.success) {
|
|
181
|
+
return { ok: false, reason: `deployment key is invalid: ${formatZodIssues(parsed.error)}` };
|
|
182
|
+
}
|
|
183
|
+
return { ok: true, value: parsed.data };
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Resolve an authored key against the runtime profiles, per field;
|
|
188
|
+
* `resources` and `hpa` resolve as whole objects (never deep-merged).
|
|
189
|
+
* Throws when the intent cannot resolve (dedicated runtime without
|
|
190
|
+
* resources, non-default buildContext) — the registry rejects the same.
|
|
191
|
+
*/
|
|
192
|
+
export function resolveDeploymentIntent(
|
|
193
|
+
intent: ProviderDeploymentOverrides = {},
|
|
194
|
+
): ResolvedDeploymentIntent {
|
|
195
|
+
const runtime = intent.runtime ?? DEPLOYMENT_PROFILE_DEFAULTS.runtime;
|
|
196
|
+
const resources = intent.resources ?? DEPLOYMENT_PROFILE_DEFAULTS.resources[runtime];
|
|
197
|
+
if (!resources) {
|
|
198
|
+
throw new Error(
|
|
199
|
+
`resources: runtime "${runtime}" has no profile default; declare explicit resources { cpu, memory }`,
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
if (
|
|
203
|
+
intent.buildContext !== undefined &&
|
|
204
|
+
intent.buildContext !== DEPLOYMENT_PROFILE_DEFAULTS.buildContext
|
|
205
|
+
) {
|
|
206
|
+
throw new Error(`buildContext: ${BUILD_CONTEXT_CONSTRAINT}`);
|
|
207
|
+
}
|
|
208
|
+
return {
|
|
209
|
+
runtime,
|
|
210
|
+
language: intent.language ?? DEPLOYMENT_PROFILE_DEFAULTS.language,
|
|
211
|
+
replicas: intent.replicas ?? DEPLOYMENT_PROFILE_DEFAULTS.replicas,
|
|
212
|
+
hpa: intent.hpa ? { ...intent.hpa } : { ...DEPLOYMENT_PROFILE_DEFAULTS.hpa },
|
|
213
|
+
resources: { ...resources },
|
|
214
|
+
...(intent.cache ? { cache: cloneJson(intent.cache) } : {}),
|
|
215
|
+
...(intent.network
|
|
216
|
+
? { network: { additionalTcpPorts: [...(intent.network.additionalTcpPorts ?? [])] } }
|
|
217
|
+
: {}),
|
|
218
|
+
buildContext: intent.buildContext ?? DEPLOYMENT_PROFILE_DEFAULTS.buildContext,
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Two resolved views describe the same deployment when they are deep-equal
|
|
224
|
+
* after dropping empty shells (`network: { additionalTcpPorts: [] }`,
|
|
225
|
+
* `cache: {}`) that carry no intent — the manifests they render are
|
|
226
|
+
* byte-identical.
|
|
227
|
+
*/
|
|
228
|
+
export function deploymentIntentsEquivalent(
|
|
229
|
+
left: ResolvedDeploymentIntent,
|
|
230
|
+
right: ResolvedDeploymentIntent,
|
|
231
|
+
): boolean {
|
|
232
|
+
return deepEqual(canonicalResolvedDeployment(left), canonicalResolvedDeployment(right));
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
export function canonicalResolvedDeployment(
|
|
236
|
+
resolved: ResolvedDeploymentIntent,
|
|
237
|
+
): ResolvedDeploymentIntent {
|
|
238
|
+
const { cache, network, ...rest } = resolved;
|
|
239
|
+
const canonical: ResolvedDeploymentIntent = { ...rest };
|
|
240
|
+
if (cache?.redis !== undefined) canonical.cache = { redis: { ...cache.redis } };
|
|
241
|
+
if (network !== undefined && network.additionalTcpPorts.length > 0) {
|
|
242
|
+
canonical.network = { additionalTcpPorts: [...network.additionalTcpPorts] };
|
|
243
|
+
}
|
|
244
|
+
return canonical;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* The smallest `deployment` key that resolves to `resolved`: profile
|
|
249
|
+
* defaults are omitted, `hpa`/`resources` are kept whole when they differ
|
|
250
|
+
* (the resolver never deep-merges them), and empty shells are dropped.
|
|
251
|
+
* Callers prove the result with
|
|
252
|
+
* `deploymentIntentsEquivalent(resolveDeploymentIntent(key), resolved)`.
|
|
253
|
+
*/
|
|
254
|
+
export function subtractDeploymentDefaults(
|
|
255
|
+
resolved: ResolvedDeploymentIntent,
|
|
256
|
+
): ProviderDeploymentOverrides {
|
|
257
|
+
const intent: ProviderDeploymentOverrides = {};
|
|
258
|
+
if (resolved.runtime !== DEPLOYMENT_PROFILE_DEFAULTS.runtime) intent.runtime = resolved.runtime;
|
|
259
|
+
if (resolved.language !== DEPLOYMENT_PROFILE_DEFAULTS.language)
|
|
260
|
+
intent.language = resolved.language;
|
|
261
|
+
if (resolved.replicas !== DEPLOYMENT_PROFILE_DEFAULTS.replicas)
|
|
262
|
+
intent.replicas = resolved.replicas;
|
|
263
|
+
if (!deepEqual(resolved.hpa, DEPLOYMENT_PROFILE_DEFAULTS.hpa)) intent.hpa = { ...resolved.hpa };
|
|
264
|
+
const profileResources = DEPLOYMENT_PROFILE_DEFAULTS.resources[resolved.runtime];
|
|
265
|
+
if (profileResources === undefined || !deepEqual(resolved.resources, profileResources)) {
|
|
266
|
+
intent.resources = { ...resolved.resources };
|
|
267
|
+
}
|
|
268
|
+
if (resolved.cache?.redis !== undefined) intent.cache = { redis: { ...resolved.cache.redis } };
|
|
269
|
+
if (resolved.network !== undefined && resolved.network.additionalTcpPorts.length > 0) {
|
|
270
|
+
intent.network = { additionalTcpPorts: [...resolved.network.additionalTcpPorts] };
|
|
271
|
+
}
|
|
272
|
+
// buildContext: "." is the only accepted value, so it is never authored.
|
|
273
|
+
return intent;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/** Authored order for emitted keys: the registry contract's field order. */
|
|
277
|
+
export const DEPLOYMENT_INTENT_FIELD_ORDER: readonly (keyof ProviderDeploymentOverrides)[] = [
|
|
278
|
+
"runtime",
|
|
279
|
+
"language",
|
|
280
|
+
"replicas",
|
|
281
|
+
"hpa",
|
|
282
|
+
"resources",
|
|
283
|
+
"cache",
|
|
284
|
+
"network",
|
|
285
|
+
"buildContext",
|
|
286
|
+
];
|
|
287
|
+
|
|
288
|
+
export interface RedundantDeploymentField {
|
|
289
|
+
/** Dotted path under `deployment`, e.g. `hpa` or `network.additionalTcpPorts`. */
|
|
290
|
+
readonly path: string;
|
|
291
|
+
readonly message: string;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Fields of an authored `deployment` key that restate what the runtime
|
|
296
|
+
* profile already resolves (ADR-0009 "derived values are not re-stated").
|
|
297
|
+
* Accepts an unvalidated value because `apifuse check` inspects the loaded
|
|
298
|
+
* definition, whose key the SDK passes through verbatim.
|
|
299
|
+
*/
|
|
300
|
+
export function redundantDeploymentDefaults(intent: unknown): RedundantDeploymentField[] {
|
|
301
|
+
if (!isRecord(intent)) return [];
|
|
302
|
+
const redundant: RedundantDeploymentField[] = [];
|
|
303
|
+
const flag = (path: string, detail: string): void => {
|
|
304
|
+
redundant.push({ path, message: `deployment.${path} ${detail}; omit it` });
|
|
305
|
+
};
|
|
306
|
+
if (intent.runtime === DEPLOYMENT_PROFILE_DEFAULTS.runtime) {
|
|
307
|
+
flag("runtime", `restates the profile default ("${DEPLOYMENT_PROFILE_DEFAULTS.runtime}")`);
|
|
308
|
+
}
|
|
309
|
+
if (intent.language === DEPLOYMENT_PROFILE_DEFAULTS.language) {
|
|
310
|
+
flag("language", `restates the profile default ("${DEPLOYMENT_PROFILE_DEFAULTS.language}")`);
|
|
311
|
+
}
|
|
312
|
+
if (intent.replicas === DEPLOYMENT_PROFILE_DEFAULTS.replicas) {
|
|
313
|
+
flag("replicas", `restates the profile default (${DEPLOYMENT_PROFILE_DEFAULTS.replicas})`);
|
|
314
|
+
}
|
|
315
|
+
if (deepEqual(intent.hpa, DEPLOYMENT_PROFILE_DEFAULTS.hpa)) {
|
|
316
|
+
flag("hpa", "restates the profile default (enabled, 1-1 replicas, 70% CPU target)");
|
|
317
|
+
}
|
|
318
|
+
const runtime =
|
|
319
|
+
typeof intent.runtime === "string" && intent.runtime in DEPLOYMENT_PROFILE_DEFAULTS.resources
|
|
320
|
+
? (intent.runtime as DeploymentRuntime)
|
|
321
|
+
: DEPLOYMENT_PROFILE_DEFAULTS.runtime;
|
|
322
|
+
const profileResources = DEPLOYMENT_PROFILE_DEFAULTS.resources[runtime];
|
|
323
|
+
if (profileResources !== undefined && deepEqual(intent.resources, profileResources)) {
|
|
324
|
+
flag(
|
|
325
|
+
"resources",
|
|
326
|
+
`restates the "${runtime}" profile default (${profileResources.cpu}/${profileResources.memory})`,
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
if (intent.buildContext === DEPLOYMENT_PROFILE_DEFAULTS.buildContext) {
|
|
330
|
+
flag("buildContext", 'restates the only accepted value (".")');
|
|
331
|
+
}
|
|
332
|
+
if (isRecord(intent.network)) {
|
|
333
|
+
const ports = intent.network.additionalTcpPorts;
|
|
334
|
+
if (ports === undefined || (Array.isArray(ports) && ports.length === 0)) {
|
|
335
|
+
flag("network", "declares no additional TCP ports (empty shell)");
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
if (isRecord(intent.cache) && intent.cache.redis === undefined) {
|
|
339
|
+
flag("cache", "declares no redis intent (empty shell)");
|
|
340
|
+
}
|
|
341
|
+
return redundant;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/** Structural equality for JSON-shaped values, key order insensitive. */
|
|
345
|
+
export function deepEqual(left: unknown, right: unknown): boolean {
|
|
346
|
+
if (left === right) return true;
|
|
347
|
+
if (Array.isArray(left) || Array.isArray(right)) {
|
|
348
|
+
if (!Array.isArray(left) || !Array.isArray(right) || left.length !== right.length) return false;
|
|
349
|
+
return left.every((item, index) => deepEqual(item, right[index]));
|
|
350
|
+
}
|
|
351
|
+
if (!isRecord(left) || !isRecord(right)) return false;
|
|
352
|
+
const leftKeys = Object.keys(left).filter((key) => left[key] !== undefined);
|
|
353
|
+
const rightKeys = Object.keys(right).filter((key) => right[key] !== undefined);
|
|
354
|
+
if (leftKeys.length !== rightKeys.length) return false;
|
|
355
|
+
return leftKeys.every((key) => Object.hasOwn(right, key) && deepEqual(left[key], right[key]));
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
359
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
function cloneJson<T>(value: T): T {
|
|
363
|
+
return JSON.parse(JSON.stringify(value)) as T;
|
|
364
|
+
}
|