@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.
Files changed (261) hide show
  1. package/AUTHORING.md +367 -9
  2. package/CHANGELOG.md +8 -0
  3. package/LICENSE +21 -0
  4. package/README.md +1 -0
  5. package/bin/apifuse-check.ts +84 -2
  6. package/bin/apifuse-dev.ts +11 -31
  7. package/bin/apifuse-migrate-deployment.ts +200 -0
  8. package/bin/apifuse-pack-check.ts +11 -4
  9. package/bin/apifuse-pack-types.ts +187 -4
  10. package/bin/apifuse-record.ts +44 -32
  11. package/bin/apifuse-submit-check.ts +9 -5
  12. package/bin/apifuse-test.ts +3 -1
  13. package/dist/cli/commands.d.ts +1 -1
  14. package/dist/cli/commands.js +11 -0
  15. package/dist/cli/deployment-intent.d.ts +178 -0
  16. package/dist/cli/deployment-intent.js +277 -0
  17. package/dist/cli/migrate-deployment-intent.d.ts +132 -0
  18. package/dist/cli/migrate-deployment-intent.js +1552 -0
  19. package/dist/cli/migrate-provider-shape.d.ts +5 -1
  20. package/dist/cli/migrate-provider-shape.js +4 -2
  21. package/dist/cli/resolver-runtime.d.ts +21 -0
  22. package/dist/cli/resolver-runtime.js +31 -0
  23. package/dist/define.d.ts +3 -0
  24. package/dist/define.js +5 -0
  25. package/dist/dev.d.ts +3 -1
  26. package/dist/dev.js +6 -1
  27. package/dist/engine.d.ts +35 -1
  28. package/dist/engine.js +21 -2
  29. package/dist/error-resolution.d.ts +1 -0
  30. package/dist/error-resolution.js +54 -0
  31. package/dist/errors.d.ts +39 -1
  32. package/dist/errors.js +50 -0
  33. package/dist/handle-meta.d.ts +36 -1
  34. package/dist/handle-meta.js +16 -0
  35. package/dist/handle.d.ts +23 -5
  36. package/dist/handle.js +54 -8
  37. package/dist/health-scenario.d.ts +38 -0
  38. package/dist/i18n/error-messages.d.ts +86 -0
  39. package/dist/i18n/error-messages.js +192 -0
  40. package/dist/i18n/index.d.ts +1 -0
  41. package/dist/i18n/index.js +1 -0
  42. package/dist/i18n/keys.d.ts +12 -0
  43. package/dist/i18n/keys.js +76 -5
  44. package/dist/index.d.ts +11 -3
  45. package/dist/index.js +9 -1
  46. package/dist/lint.d.ts +15 -0
  47. package/dist/lint.js +469 -82
  48. package/dist/provider.d.ts +6 -6
  49. package/dist/provider.js +3 -3
  50. package/dist/runtime/browser-telemetry-binding.d.ts +5 -0
  51. package/dist/runtime/browser-telemetry-binding.js +83 -0
  52. package/dist/runtime/browser-telemetry.d.ts +76 -0
  53. package/dist/runtime/browser-telemetry.js +165 -0
  54. package/dist/runtime/browser.d.ts +3 -0
  55. package/dist/runtime/browser.js +208 -47
  56. package/dist/runtime/cache-telemetry.d.ts +71 -0
  57. package/dist/runtime/cache-telemetry.js +177 -0
  58. package/dist/runtime/cache.js +67 -7
  59. package/dist/runtime/capability-response.d.ts +3 -0
  60. package/dist/runtime/capability-response.js +56 -0
  61. package/dist/runtime/capability-telemetry.d.ts +46 -0
  62. package/dist/runtime/capability-telemetry.js +68 -0
  63. package/dist/runtime/diagnostic-env.d.ts +3 -0
  64. package/dist/runtime/diagnostic-env.js +10 -1
  65. package/dist/runtime/engine-mode.d.ts +74 -0
  66. package/dist/runtime/engine-mode.js +152 -0
  67. package/dist/runtime/handle.js +9 -1
  68. package/dist/runtime/http-telemetry-guard.d.ts +10 -0
  69. package/dist/runtime/http-telemetry-guard.js +62 -10
  70. package/dist/runtime/http.js +141 -35
  71. package/dist/runtime/instrumentation.js +92 -13
  72. package/dist/runtime/ocr-telemetry.d.ts +82 -0
  73. package/dist/runtime/ocr-telemetry.js +192 -0
  74. package/dist/runtime/ocr.js +35 -8
  75. package/dist/runtime/request-telemetry.d.ts +2 -0
  76. package/dist/runtime/request-telemetry.js +2 -1
  77. package/dist/runtime/resolver-runtime-options.d.ts +14 -0
  78. package/dist/runtime/resolver-runtime-options.js +33 -0
  79. package/dist/runtime/resolver-shared.d.ts +3 -2
  80. package/dist/runtime/resolver-shared.js +4 -4
  81. package/dist/runtime/resolver-telemetry.d.ts +1 -0
  82. package/dist/runtime/resolver-telemetry.js +1 -1
  83. package/dist/runtime/resolver-vendors/browser.d.ts +2 -0
  84. package/dist/runtime/resolver-vendors/browser.js +46 -0
  85. package/dist/runtime/resolver-vendors/hosts.d.ts +21 -0
  86. package/dist/runtime/resolver-vendors/hosts.js +35 -0
  87. package/dist/runtime/resolver-vendors/hypersolutions.js +2 -2
  88. package/dist/runtime/resolver-vendors/types.d.ts +5 -4
  89. package/dist/runtime/resolver.d.ts +4 -1
  90. package/dist/runtime/resolver.js +46 -13
  91. package/dist/runtime/state-telemetry.d.ts +79 -0
  92. package/dist/runtime/state-telemetry.js +194 -0
  93. package/dist/runtime/state.d.ts +3 -0
  94. package/dist/runtime/state.js +26 -7
  95. package/dist/runtime/stealth-cookies.d.ts +5 -1
  96. package/dist/runtime/stealth-cookies.js +21 -1
  97. package/dist/runtime/stealth-owned-headers.d.ts +26 -0
  98. package/dist/runtime/stealth-owned-headers.js +37 -0
  99. package/dist/runtime/stealth-telemetry.d.ts +92 -0
  100. package/dist/runtime/stealth-telemetry.js +179 -0
  101. package/dist/runtime/stealth.d.ts +4 -1
  102. package/dist/runtime/stealth.js +228 -85
  103. package/dist/runtime/stt-telemetry.d.ts +77 -0
  104. package/dist/runtime/stt-telemetry.js +176 -0
  105. package/dist/runtime/stt.js +15 -6
  106. package/dist/runtime-boundary-lint.d.ts +132 -0
  107. package/dist/runtime-boundary-lint.js +586 -0
  108. package/dist/server/error-stack-frames.d.ts +35 -0
  109. package/dist/server/error-stack-frames.js +219 -0
  110. package/dist/server/index.d.ts +7 -0
  111. package/dist/server/index.js +4 -0
  112. package/dist/server/sdk-version.d.ts +3 -0
  113. package/dist/server/sdk-version.js +19 -0
  114. package/dist/server/self-test.js +1 -18
  115. package/dist/server/sensitive-values.d.ts +1 -1
  116. package/dist/server/sensitive-values.js +9 -0
  117. package/dist/server/serve-implementation.d.ts +51 -2
  118. package/dist/server/serve-implementation.js +337 -90
  119. package/dist/server/types.d.ts +1 -0
  120. package/dist/server/types.js +7 -0
  121. package/dist/stateful/stateful-provider-owner-forwarder.js +22 -0
  122. package/dist/stateful/stateful-provider-runtime-context.d.ts +3 -0
  123. package/dist/stateful/stateful-provider-runtime-context.js +1 -0
  124. package/dist/types.d.ts +123 -1
  125. package/dist/typescript-module.d.ts +39 -0
  126. package/dist/typescript-module.js +72 -0
  127. package/package.json +6 -1
  128. package/src/cli/commands.ts +14 -0
  129. package/src/cli/deployment-intent.ts +364 -0
  130. package/src/cli/migrate-deployment-intent.ts +1908 -0
  131. package/src/cli/migrate-provider-shape.ts +10 -4
  132. package/src/cli/resolver-runtime.ts +72 -0
  133. package/src/define.ts +13 -0
  134. package/src/dev.ts +9 -2
  135. package/src/engine.ts +38 -2
  136. package/src/error-resolution.ts +58 -0
  137. package/src/errors.ts +77 -1
  138. package/src/handle-meta.ts +51 -1
  139. package/src/handle.ts +92 -10
  140. package/src/health-scenario.ts +38 -0
  141. package/src/i18n/error-messages.ts +255 -0
  142. package/src/i18n/index.ts +1 -0
  143. package/src/i18n/keys.ts +80 -6
  144. package/src/index.ts +86 -0
  145. package/src/lint.ts +604 -91
  146. package/src/provider.ts +12 -0
  147. package/src/runtime/browser-telemetry-binding.ts +92 -0
  148. package/src/runtime/browser-telemetry.ts +289 -0
  149. package/src/runtime/browser.ts +249 -65
  150. package/src/runtime/cache-telemetry.ts +247 -0
  151. package/src/runtime/cache.ts +68 -17
  152. package/src/runtime/capability-response.ts +53 -0
  153. package/src/runtime/capability-telemetry.ts +101 -0
  154. package/src/runtime/diagnostic-env.ts +11 -1
  155. package/src/runtime/engine-mode.ts +233 -0
  156. package/src/runtime/handle.ts +9 -1
  157. package/src/runtime/http-telemetry-guard.ts +66 -13
  158. package/src/runtime/http.ts +168 -37
  159. package/src/runtime/instrumentation.ts +105 -16
  160. package/src/runtime/ocr-telemetry.ts +308 -0
  161. package/src/runtime/ocr.ts +114 -74
  162. package/src/runtime/request-telemetry.ts +2 -1
  163. package/src/runtime/resolver-runtime-options.ts +50 -0
  164. package/src/runtime/resolver-shared.ts +9 -4
  165. package/src/runtime/resolver-telemetry.ts +1 -1
  166. package/src/runtime/resolver-vendors/browser.ts +65 -7
  167. package/src/runtime/resolver-vendors/hosts.ts +43 -0
  168. package/src/runtime/resolver-vendors/hypersolutions.ts +2 -2
  169. package/src/runtime/resolver-vendors/types.ts +5 -4
  170. package/src/runtime/resolver.ts +64 -21
  171. package/src/runtime/state-telemetry.ts +298 -0
  172. package/src/runtime/state.ts +46 -45
  173. package/src/runtime/stealth-cookies.ts +24 -1
  174. package/src/runtime/stealth-owned-headers.ts +41 -0
  175. package/src/runtime/stealth-telemetry.ts +304 -0
  176. package/src/runtime/stealth.ts +355 -150
  177. package/src/runtime/stt-telemetry.ts +282 -0
  178. package/src/runtime/stt.ts +93 -71
  179. package/src/runtime-boundary-lint.ts +703 -0
  180. package/src/server/error-stack-frames.ts +214 -0
  181. package/src/server/index.ts +53 -0
  182. package/src/server/sdk-version.ts +22 -0
  183. package/src/server/self-test.ts +1 -21
  184. package/src/server/sensitive-values.ts +14 -0
  185. package/src/server/serve-implementation.ts +569 -108
  186. package/src/server/types.ts +7 -0
  187. package/src/stateful/stateful-provider-owner-forwarder.ts +23 -0
  188. package/src/stateful/stateful-provider-runtime-context.ts +4 -0
  189. package/src/types.ts +128 -1
  190. package/src/typescript-module.ts +100 -0
  191. package/src/cli/__tests__/fixtures/migrate-operation-declaration/approval-override.ts.txt +0 -6
  192. package/src/cli/__tests__/fixtures/migrate-operation-declaration/codemod-syntax.ts.txt +0 -3
  193. package/src/cli/__tests__/fixtures/migrate-operation-declaration/connection-precedence.ts.txt +0 -10
  194. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-ambiguous-a.ts.txt +0 -3
  195. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-ambiguous-b.ts.txt +0 -3
  196. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-ambiguous-barrel.ts.txt +0 -2
  197. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-ambiguous-index.ts.txt +0 -3
  198. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-computed.ts.txt +0 -6
  199. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-cycle-a.ts.txt +0 -1
  200. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-cycle-b.ts.txt +0 -1
  201. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-cycle-index.ts.txt +0 -3
  202. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-daiso-barrel.ts.txt +0 -7
  203. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-daiso-catalog.ts.txt +0 -19
  204. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-daiso-docs.ts.txt +0 -6
  205. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-daiso-index.ts.txt +0 -3
  206. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-daiso-stores.ts.txt +0 -8
  207. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-indirect-config.ts.txt +0 -5
  208. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-nonstatic.ts.txt +0 -4
  209. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-package-index.ts.txt +0 -3
  210. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-reexport-index.ts.txt +0 -3
  211. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-reexport-leaf.ts.txt +0 -10
  212. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-reexport-one.ts.txt +0 -1
  213. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-reexport-two.ts.txt +0 -1
  214. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-runtime-composed-index.ts.txt +0 -3
  215. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-runtime-composed-one.ts.txt +0 -6
  216. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-runtime-composed-two.ts.txt +0 -6
  217. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-runtime-mixed-index.ts.txt +0 -14
  218. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-runtime-registry.ts.txt +0 -3
  219. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-star-package-barrel.ts.txt +0 -2
  220. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-star-package-index.ts.txt +0 -3
  221. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-star-package-leaf.ts.txt +0 -3
  222. package/src/cli/__tests__/fixtures/migrate-operation-declaration/docs-conflict.ts.txt +0 -8
  223. package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-hoisted-array-spread.ts.txt +0 -15
  224. package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-imported-array-spread.ts.txt +0 -9
  225. package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-kebab-operation-id.ts.txt +0 -17
  226. package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-map.ts.txt +0 -5
  227. package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-mixed-array-spread.ts.txt +0 -24
  228. package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-operation.ts.txt +0 -16
  229. package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-snake-operation-id.ts.txt +0 -21
  230. package/src/cli/__tests__/fixtures/migrate-operation-declaration/factory-duplicate-provider.ts.txt +0 -11
  231. package/src/cli/__tests__/fixtures/migrate-operation-declaration/factory-id-unresolved.ts.txt +0 -7
  232. package/src/cli/__tests__/fixtures/migrate-operation-declaration/factory-map.ts.txt +0 -3
  233. package/src/cli/__tests__/fixtures/migrate-operation-declaration/factory-one-to-many.ts.txt +0 -18
  234. package/src/cli/__tests__/fixtures/migrate-operation-declaration/factory-one-to-one.ts.txt +0 -13
  235. package/src/cli/__tests__/fixtures/migrate-operation-declaration/hoist-all.ts.txt +0 -31
  236. package/src/cli/__tests__/fixtures/migrate-operation-declaration/hoisted-const.ts.txt +0 -11
  237. package/src/cli/__tests__/fixtures/migrate-operation-declaration/imported-spread.ts.txt +0 -11
  238. package/src/cli/__tests__/fixtures/migrate-operation-declaration/inline-map.ts.txt +0 -11
  239. package/src/cli/__tests__/fixtures/migrate-operation-declaration/inline-spread-cast-tail.ts.txt +0 -21
  240. package/src/cli/__tests__/fixtures/migrate-operation-declaration/inline-spread-ekitan.ts.txt +0 -11
  241. package/src/cli/__tests__/fixtures/migrate-operation-declaration/inline-spread-override.ts.txt +0 -14
  242. package/src/cli/__tests__/fixtures/migrate-operation-declaration/locale-canonical-en.json +0 -1
  243. package/src/cli/__tests__/fixtures/migrate-operation-declaration/locale-canonical-ja.json +0 -1
  244. package/src/cli/__tests__/fixtures/migrate-operation-declaration/locale-canonical-ko.json +0 -1
  245. package/src/cli/__tests__/fixtures/migrate-operation-declaration/locale-existing-operation-namespace-en.json +0 -8
  246. package/src/cli/__tests__/fixtures/migrate-operation-declaration/missing-english-locale.ts.txt +0 -7
  247. package/src/cli/__tests__/fixtures/migrate-operation-declaration/no-safety.ts.txt +0 -6
  248. package/src/cli/__tests__/fixtures/migrate-operation-declaration/non-literal.ts.txt +0 -7
  249. package/src/cli/__tests__/fixtures/migrate-operation-declaration/redundant-approval.ts.txt +0 -6
  250. package/src/cli/__tests__/fixtures/migrate-operation-declaration/registry-binding-ambiguous.ts.txt +0 -12
  251. package/src/cli/__tests__/fixtures/migrate-operation-declaration/registry-imported-other.ts.txt +0 -7
  252. package/src/cli/__tests__/fixtures/migrate-operation-declaration/registry-imported.ts.txt +0 -5
  253. package/src/cli/__tests__/fixtures/migrate-operation-declaration/registry-key-ambiguous.ts.txt +0 -19
  254. package/src/cli/__tests__/fixtures/migrate-operation-declaration/registry-shorthand.ts.txt +0 -9
  255. package/src/cli/__tests__/fixtures/migrate-operation-declaration/registry-typed.ts.txt +0 -11
  256. package/src/cli/__tests__/fixtures/migrate-operation-declaration/registry-unrelated.ts.txt +0 -13
  257. package/src/cli/__tests__/fixtures/migrate-operation-declaration/safety-conflict.ts.txt +0 -7
  258. package/src/cli/__tests__/fixtures/migrate-operation-declaration/stream.ts.txt +0 -7
  259. package/src/cli/__tests__/fixtures/migrate-operation-declaration/tool-router-spread.ts.txt +0 -15
  260. package/src/cli/__tests__/fixtures/migrate-operation-declaration/unparseable.ts.txt +0 -4
  261. 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
+ }