@apifuse/provider-sdk 2.2.0-beta.62 → 2.2.0-beta.63
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 +268 -9
- package/CHANGELOG.md +4 -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-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/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 +7 -1
- package/dist/index.js +6 -0
- package/dist/lint.d.ts +10 -0
- package/dist/lint.js +412 -49
- package/dist/provider.d.ts +5 -5
- package/dist/provider.js +2 -2
- 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/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 +30 -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/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 +94 -1
- 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/i18n/error-messages.ts +255 -0
- package/src/i18n/index.ts +1 -0
- package/src/i18n/keys.ts +80 -6
- package/src/index.ts +72 -0
- package/src/lint.ts +522 -50
- package/src/provider.ts +10 -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/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 +109 -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/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 +99 -1
- 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
package/AUTHORING.md
CHANGED
|
@@ -137,6 +137,73 @@ variable, the Cloudflare OCR/STT tokens and account identifier,
|
|
|
137
137
|
`APIFUSE__OCR__API_KEY`, and `APIFUSE__CACHE__KEY_PEPPER`. `defineProvider`
|
|
138
138
|
rejects them in `secrets`; the engine reads them from its own environment.
|
|
139
139
|
|
|
140
|
+
### Engine mode
|
|
141
|
+
|
|
142
|
+
Provider processes attach their capability bindings through an engine. Today the
|
|
143
|
+
default is the in-process engine; ADR-0011 replaces it with the authenticated
|
|
144
|
+
remote engine in a phased migration, and `serve` logs every boot as
|
|
145
|
+
`provider_engine_mode` (`deprecated: true` for in-process). You do not need to
|
|
146
|
+
change anything now.
|
|
147
|
+
|
|
148
|
+
A deployment names the mode with the engine-owned `APIFUSE__ENGINE__MODE`
|
|
149
|
+
variable (`in-process` or `remote`; trimmed and case-folded, blank means unset);
|
|
150
|
+
hosts may pass `engineMode` to `serve`/`createServerApp`/`startDevServer`
|
|
151
|
+
instead. The manifest wins when the two disagree, so provider code cannot
|
|
152
|
+
silently downgrade what the deployment asked for; the disagreement is reported in
|
|
153
|
+
the boot event's `warnings`. An unrecognized value is warned about and ignored —
|
|
154
|
+
it never crashes the process. `APIFUSE__ENGINE__MODE` cannot be declared in
|
|
155
|
+
`secrets` (`APIFUSE__ENGINE__*` is engine-owned).
|
|
156
|
+
|
|
157
|
+
`remote` has no client in this release: the server attaches an engine that fails
|
|
158
|
+
every request with `PROVIDER_ENGINE_MODE_UNSUPPORTED` and reports `503` on
|
|
159
|
+
`/readyz`. It never falls back to in-process. `apifuse dev` and `apifuse record`
|
|
160
|
+
refuse to start instead.
|
|
161
|
+
|
|
162
|
+
`GET /health` is liveness only and stays engine-blind; `GET /readyz` reports the
|
|
163
|
+
resolved engine attachment and is what readiness/startup probes should use — but
|
|
164
|
+
only for a provider already pinned to an SDK release that serves the route.
|
|
165
|
+
Earlier pins answer `404`, and a failing readiness/startup probe is not
|
|
166
|
+
self-healing, so manifests move to `/readyz` after the pin wave, never before.
|
|
167
|
+
|
|
168
|
+
### Deployment intent
|
|
169
|
+
|
|
170
|
+
Deployment intent (runtime profile, resources, HPA, Redis, extra TCP ports) has
|
|
171
|
+
exactly one authored home: the optional `deployment` key inside
|
|
172
|
+
`defineProvider({...})`. The SDK passes it through verbatim; the APIFuse
|
|
173
|
+
registry resolves omitted fields from the runtime profile and rejects anything
|
|
174
|
+
it cannot resolve.
|
|
175
|
+
|
|
176
|
+
- Declare only what differs from the profile. Profile defaults:
|
|
177
|
+
`runtime: "shared"`, `language: "typescript"`, `replicas: 1`,
|
|
178
|
+
`hpa: { enabled: true, minReplicas: 1, maxReplicas: 1, targetCPUUtilizationPercentage: 70 }`,
|
|
179
|
+
resources `25m/128Mi` (shared) or `200m/256Mi` (browser), `buildContext: "."`.
|
|
180
|
+
Most providers therefore declare no `deployment` key at all.
|
|
181
|
+
- The deployment `runtime` axis (`shared | dedicated | browser`) is distinct
|
|
182
|
+
from the execution `runtime` (`standard | shared | browser`) declared next to
|
|
183
|
+
it.
|
|
184
|
+
- Do not author a standalone `deploy.ts`, a `deployment` extra on a spread
|
|
185
|
+
default export (`export default { ...provider, deployment }`), or a
|
|
186
|
+
re-exported config module. A repository that still carries `deploy.ts`
|
|
187
|
+
migrates with `apifuse migrate-deployment .`: it hoists the file's non-default
|
|
188
|
+
values into the declaration, proves the result resolves identically, and
|
|
189
|
+
deletes the file. The `/deploy.ts` CODEOWNERS entry stays until the platform
|
|
190
|
+
stops honoring a re-added legacy file (`--drop-codeowners-lock` then). A
|
|
191
|
+
refusal names the manual step.
|
|
192
|
+
- `apifuse check` warns during the migration window
|
|
193
|
+
(`deployment/legacy-deploy-file`, `deployment/spread-export`,
|
|
194
|
+
`deployment/redundant-default`); the rules become errors once no repository
|
|
195
|
+
carries the legacy file.
|
|
196
|
+
|
|
197
|
+
```ts
|
|
198
|
+
const buildProvider = defineProvider({
|
|
199
|
+
id: "example",
|
|
200
|
+
version: "1.0.0",
|
|
201
|
+
runtime: "standard",
|
|
202
|
+
deployment: { cache: { redis: { enabled: true } } },
|
|
203
|
+
// ...
|
|
204
|
+
});
|
|
205
|
+
```
|
|
206
|
+
|
|
140
207
|
### Factored operations
|
|
141
208
|
|
|
142
209
|
`defineProvider(declaration)` returns the builder that accepts `operations`, so
|
|
@@ -536,18 +603,20 @@ operation:
|
|
|
536
603
|
|
|
537
604
|
```ts
|
|
538
605
|
errorCodes: [{
|
|
539
|
-
code: "
|
|
540
|
-
status:
|
|
541
|
-
retryable:
|
|
542
|
-
description: "The upstream
|
|
606
|
+
code: "ITEM_SOLD_OUT",
|
|
607
|
+
status: 409,
|
|
608
|
+
retryable: false,
|
|
609
|
+
description: "The upstream sold the item before checkout completed.",
|
|
543
610
|
}],
|
|
544
611
|
handler: async () => {
|
|
545
|
-
throw new ProviderError("
|
|
546
|
-
code: "UPSTREAM_SCHEMA_ERROR",
|
|
547
|
-
});
|
|
612
|
+
throw new ProviderError("Item sold out", { code: "ITEM_SOLD_OUT" });
|
|
548
613
|
},
|
|
549
614
|
```
|
|
550
615
|
|
|
616
|
+
Declare only codes the SDK does not already register. `UPSTREAM_SCHEMA_ERROR`,
|
|
617
|
+
`UPSTREAM_AUTH_ERROR`, and `INVALID_REQUEST` are registered below, so throwing
|
|
618
|
+
them needs no declaration at all.
|
|
619
|
+
|
|
551
620
|
`defineProvider` accepts only statuses the server can emit: 400, 401, 404, 429,
|
|
552
621
|
500, 502, 503, and 504. Invalid declared statuses fail provider definition,
|
|
553
622
|
not a live request. Status selection uses this order:
|
|
@@ -567,10 +636,10 @@ The registered mappings are:
|
|
|
567
636
|
| Error code or fallback | HTTP status |
|
|
568
637
|
| --- | ---: |
|
|
569
638
|
| `AUTH_REQUIRED`, `reauth_required` | 401 |
|
|
570
|
-
| `MISSING_SECRET` | 400 |
|
|
639
|
+
| `MISSING_SECRET`, `UPSTREAM_AUTH_ERROR`, `INVALID_REQUEST` | 400 |
|
|
571
640
|
| `NOT_FOUND`, `not_found`, `NO_DATA` | 404 |
|
|
572
641
|
| `RATE_LIMITED`, `UPSTREAM_RATE_LIMIT`, `LIMITED_NUMBER_OF_SERVICE_REQUESTS_EXCEEDS_ERROR` | 429 |
|
|
573
|
-
| `UPSTREAM_ERROR`, `BLOCKED` | 502 |
|
|
642
|
+
| `UPSTREAM_ERROR`, `BLOCKED`, `UPSTREAM_SCHEMA_ERROR` | 502 |
|
|
574
643
|
| `STT_UNAVAILABLE`, `UNSUPPORTED_STT_BACKEND`, `STATEFUL_FORWARDING_REPLAY_CACHE_FULL` | 503 |
|
|
575
644
|
| Unregistered input `ValidationError` code | 400 |
|
|
576
645
|
| Other unregistered `ProviderError` code | 500 |
|
|
@@ -587,6 +656,154 @@ Throw the domain `ProviderError` directly. Subclassing or wrapping it as a
|
|
|
587
656
|
domain code's `status` instead. Genuine `TransportError` values remain
|
|
588
657
|
SDK-owned and keep their 502/504 mapping.
|
|
589
658
|
|
|
659
|
+
#### Three fleet-consensus codes are registered
|
|
660
|
+
|
|
661
|
+
`UPSTREAM_AUTH_ERROR` (400), `UPSTREAM_SCHEMA_ERROR` (502), and
|
|
662
|
+
`INVALID_REQUEST` (400) are registered mappings. All three are non-retryable.
|
|
663
|
+
|
|
664
|
+
- `UPSTREAM_AUTH_ERROR` is 400, not 401 or 502, because it means the upstream
|
|
665
|
+
refused a *platform-managed* service key. The caller holds no credential, so
|
|
666
|
+
401 ("re-authenticate") asks for something the caller cannot do, and 502
|
|
667
|
+
("the upstream is sick") promises a recovery that will never arrive. It is a
|
|
668
|
+
deployment/config defect, exactly like `MISSING_SECRET`.
|
|
669
|
+
- `UPSTREAM_SCHEMA_ERROR` is non-retryable: a retry returns the same payload
|
|
670
|
+
the provider already could not normalize.
|
|
671
|
+
- `INVALID_REQUEST` is the spelling for caller-side bad input. `INVALID_INPUT`,
|
|
672
|
+
`VALIDATION_ERROR`, and `BAD_REQUEST` are not registered.
|
|
673
|
+
|
|
674
|
+
Like `UPSTREAM_ERROR` and `BLOCKED`, these are thrown by provider code rather
|
|
675
|
+
than by the SDK, so they are registered mappings and not SDK-owned codes: an
|
|
676
|
+
existing operation declaration still wins at runtime (step 2 above). Registering
|
|
677
|
+
them therefore never rewrites a status you already declared. What it does do is
|
|
678
|
+
let you delete the declaration — and when your declaration disagrees with the
|
|
679
|
+
registered mapping, `apifuse check` reports it rather than leaving the fleet
|
|
680
|
+
answering one code two ways:
|
|
681
|
+
|
|
682
|
+
- `error-code-status-conflicts-sdk` — declared `status` differs from the
|
|
683
|
+
registered status.
|
|
684
|
+
- `error-code-retryable-conflicts-sdk` — declared `retryable` differs from the
|
|
685
|
+
registered retryability.
|
|
686
|
+
|
|
687
|
+
Both are warnings. Drop the conflicting field so the SDK supplies the value, or,
|
|
688
|
+
if your operation genuinely means something else, throw a distinct code that
|
|
689
|
+
says so.
|
|
690
|
+
|
|
691
|
+
### Localized error messages (`messageKey` / `fixKey`)
|
|
692
|
+
|
|
693
|
+
The positional `ProviderError` message stays **English**. It is what
|
|
694
|
+
`error.message`, cause frames, the `provider_request_failed` log, OTLP
|
|
695
|
+
attributes and tests see. Only two fields of the response body are localized:
|
|
696
|
+
`error.message` and `error.fix`. Nothing else — not `code`, not `details`, not
|
|
697
|
+
`errorCodes[].description`, not the `X-ApiFuse-Error-Observability` header.
|
|
698
|
+
|
|
699
|
+
Attach locale keys instead of parsing `Accept-Language` yourself:
|
|
700
|
+
|
|
701
|
+
```ts
|
|
702
|
+
// locales/en.json
|
|
703
|
+
{ "errors": { "upstreamSchema": {
|
|
704
|
+
"message": "{provider} returned an unexpected response.",
|
|
705
|
+
"fix": "Retry in a few minutes."
|
|
706
|
+
} } }
|
|
707
|
+
```
|
|
708
|
+
|
|
709
|
+
```ts
|
|
710
|
+
throw new ProviderError("The culture data service returned an unexpected response.", {
|
|
711
|
+
code: "UPSTREAM_SCHEMA_ERROR",
|
|
712
|
+
messageKey: "errors.upstreamSchema.message",
|
|
713
|
+
fixKey: "errors.upstreamSchema.fix",
|
|
714
|
+
params: { provider: "Culture Data" },
|
|
715
|
+
});
|
|
716
|
+
```
|
|
717
|
+
|
|
718
|
+
Keys can also live on the declaration, which localizes **every** site that
|
|
719
|
+
throws that code without touching the throw sites:
|
|
720
|
+
|
|
721
|
+
```ts
|
|
722
|
+
errorCodes: [{
|
|
723
|
+
code: "UPSTREAM_SCHEMA_ERROR",
|
|
724
|
+
description: "The upstream response no longer matches its schema.",
|
|
725
|
+
messageKey: "errors.upstreamSchema.message",
|
|
726
|
+
fixKey: "errors.upstreamSchema.fix",
|
|
727
|
+
}],
|
|
728
|
+
```
|
|
729
|
+
|
|
730
|
+
`status` and `retryable` are omitted because `UPSTREAM_SCHEMA_ERROR` is a
|
|
731
|
+
registered code: the SDK already supplies 502 and non-retryable, and declaring
|
|
732
|
+
a different value trips `error-code-status-conflicts-sdk` /
|
|
733
|
+
`error-code-retryable-conflicts-sdk`. A declaration that carries only keys is
|
|
734
|
+
still worth writing — it localizes every site that throws the code.
|
|
735
|
+
|
|
736
|
+
Resolution order per field, evaluated at serve time:
|
|
737
|
+
|
|
738
|
+
1. the throw site's `messageKey` / `fixKey`;
|
|
739
|
+
2. the matching `errorCodes[]` entry's `messageKey` / `fixKey`;
|
|
740
|
+
3. the derived `errors.<code>.message` / `errors.<code>.fix` (the code is used
|
|
741
|
+
verbatim as a catalog segment, so `errors.UPSTREAM_SCHEMA_ERROR.message`
|
|
742
|
+
works with no declaration at all);
|
|
743
|
+
4. the English literal passed to the constructor.
|
|
744
|
+
|
|
745
|
+
Each candidate is resolved in the caller's locale first and then in `en`, so a
|
|
746
|
+
more specific key always beats a more specific locale. A missing key, a
|
|
747
|
+
malformed key, a non-string catalog value or an absent catalog is a miss, never
|
|
748
|
+
a throw: the caller always gets text. Steps 2 and 3 do not apply to SDK-owned
|
|
749
|
+
failures (transport, Zod, stateful deadline, SDK runtime codes) — a provider
|
|
750
|
+
catalog must not be able to relabel `Request timed out`.
|
|
751
|
+
|
|
752
|
+
**Key grammar.** A locale key is dot-separated segments. Every segment is
|
|
753
|
+
camelCase (`upstreamSchema`, `whenToUse`) or an array index (`0`), and the
|
|
754
|
+
first segment is always a camelCase namespace. The one exception is the segment
|
|
755
|
+
directly under `errors`, which may also be the error code spelled the way it is
|
|
756
|
+
thrown — `errors.UPSTREAM_SCHEMA_ERROR.message`, `errors.BLOCKED.fix`,
|
|
757
|
+
`errors.reauth_required.message`. Both spellings work, so
|
|
758
|
+
`errors.upstreamSchema.message` and `errors.UPSTREAM_SCHEMA_ERROR.message` are
|
|
759
|
+
equally legal keys; write the code form when you want the key to match the
|
|
760
|
+
derived path in step 3. Nothing else loosens: a space, a dot inside a segment,
|
|
761
|
+
an empty segment, a mixed-case or hyphenated code, and a leading digit are all
|
|
762
|
+
still malformed.
|
|
763
|
+
|
|
764
|
+
Locale negotiation reads `Accept-Language` from the request envelope's
|
|
765
|
+
`headers` map first (the gateway stamps the flow's locale there) and then from
|
|
766
|
+
the HTTP header, honours quality values, matches on the primary subtag, and
|
|
767
|
+
supports `en`, `ko`, `ja` with `en` as the default. A request with no
|
|
768
|
+
`Accept-Language` therefore keeps serving the `en` catalog value, so existing
|
|
769
|
+
contract fixtures do not churn.
|
|
770
|
+
|
|
771
|
+
**Interpolation.** `{name}` placeholders, `{{` and `}}` for literal braces.
|
|
772
|
+
`params` accepts strings and finite numbers only. A placeholder with no usable
|
|
773
|
+
param is left verbatim so the authoring gap is visible rather than silently
|
|
774
|
+
deleting text.
|
|
775
|
+
|
|
776
|
+
**Params are untrusted.** Upstream error text routinely ends up in `params`, so
|
|
777
|
+
every string value is scrubbed before substitution: secrets and e-mail
|
|
778
|
+
addresses redacted, control/bidi/newline characters encoded or collapsed,
|
|
779
|
+
`<`, `>` and backticks removed, HTML-entity ampersands (`<`) neutralized
|
|
780
|
+
while ordinary ampersands survive, and the value capped at 200 characters.
|
|
781
|
+
Substituted text is never rescanned, so a value containing `{other}` cannot
|
|
782
|
+
trigger a second interpolation round. Prefer `details` when you need to relay
|
|
783
|
+
an upstream body verbatim; `params` is for short, bounded values.
|
|
784
|
+
|
|
785
|
+
**Catalog loading.** Catalogs are read once when the server is built (from the
|
|
786
|
+
provider directory's `locales/{en,ko,ja}.json`, loading only the locales that
|
|
787
|
+
exist). Pass `serve(provider, { localeCatalogs })` to bundle them instead. A
|
|
788
|
+
catalog that exists but cannot be parsed logs one
|
|
789
|
+
`provider_locale_catalogs_unavailable` warn at boot and every field falls back
|
|
790
|
+
to its English literal.
|
|
791
|
+
|
|
792
|
+
**Lint.** `apifuse check` reports:
|
|
793
|
+
|
|
794
|
+
- `error-locale-key-missing` / `error-locale-key-malformed` (**error**) — a
|
|
795
|
+
literal `messageKey`/`fixKey`, on a throw site or on an `errorCodes[]` entry,
|
|
796
|
+
that `locales/en.json` does not resolve to non-empty text, or that is not a
|
|
797
|
+
legal locale dot path (see **Key grammar** above).
|
|
798
|
+
- `thrown-error-message-not-localized` (**warn**) — a throw site whose declared
|
|
799
|
+
code has no key anywhere and no `errors.<code>.message` in `en`, so it is
|
|
800
|
+
served untranslated. Warning during the migration; promoted to error per
|
|
801
|
+
provider as each wave lands.
|
|
802
|
+
|
|
803
|
+
`ko`/`ja` coverage needs no new rule: `en` is the baseline, so the existing
|
|
804
|
+
catalog parity and placeholder checks already fail an `errors.*` key that is
|
|
805
|
+
missing or untranslated in `ko`/`ja`.
|
|
806
|
+
|
|
590
807
|
### Declared secrets are SDK-enforced
|
|
591
808
|
|
|
592
809
|
Environment/secret presence validation is single-sourced in the SDK. Declare
|
|
@@ -678,6 +895,48 @@ redacted. If a login flow must consume a rotated credential from `Location`,
|
|
|
678
895
|
inspect it inside `stopWhen`; that callback receives the real hop while callback
|
|
679
896
|
failures are sanitized before propagation.
|
|
680
897
|
|
|
898
|
+
### Request-bound proof headers
|
|
899
|
+
|
|
900
|
+
When an upstream requires a single-use, request-bound header (DPoP proofs,
|
|
901
|
+
HTTP message signatures, HMAC nonces), pass `headers` as a factory instead of a
|
|
902
|
+
record. `ctx.http` calls it once per issued attempt with the resolved URL and
|
|
903
|
+
method, so a managed retry sends a fresh proof rather than replaying the first
|
|
904
|
+
one, and the request keeps proxy failover and the transient retry taxonomy:
|
|
905
|
+
|
|
906
|
+
```ts
|
|
907
|
+
const response = await ctx.http.get(requestUrl, {
|
|
908
|
+
headers: async ({ url, method }) => {
|
|
909
|
+
// RFC 9449 §4.2: `htu` is the target URI without query and fragment.
|
|
910
|
+
const htu = new URL(url);
|
|
911
|
+
htu.search = "";
|
|
912
|
+
htu.hash = "";
|
|
913
|
+
return {
|
|
914
|
+
...commonHeaders,
|
|
915
|
+
dpop: await createDpopProof(htu.toString(), method),
|
|
916
|
+
};
|
|
917
|
+
},
|
|
918
|
+
});
|
|
919
|
+
```
|
|
920
|
+
|
|
921
|
+
`url` is the exact URL the attempt is issued against, including `params` and
|
|
922
|
+
`sensitiveParams`. Bind to it instead of a URL you rebuild, but strip the query
|
|
923
|
+
and fragment for fields that are defined without them (DPoP `htu`), and never
|
|
924
|
+
log it or copy it verbatim into a signed field a verifier logs.
|
|
925
|
+
|
|
926
|
+
Keep the factory fast and local. It runs after proxy resolution, so an already
|
|
927
|
+
allocated egress endpoint and its lease are held while it is awaited; a remote
|
|
928
|
+
signing round trip there can outlive the lease TTL. Mint locally, and set
|
|
929
|
+
`timeout` on the call so a hung signer cannot hold the endpoint indefinitely.
|
|
930
|
+
|
|
931
|
+
The factory is not invoked for redirect hops (they reuse the attempt's
|
|
932
|
+
headers), for skipped duplicate proxy offsets, or for an attempt whose proxy
|
|
933
|
+
allocation failed — those build no request, so `attempt` counts only the
|
|
934
|
+
requests that are actually issued. A factory that throws fails the request with
|
|
935
|
+
the non-retryable `TransportError` code `http_header_factory_failed`, which is
|
|
936
|
+
classified as a provider-side fault (`provider_error`), not an upstream
|
|
937
|
+
failure. `ctx.stealth` has its own `headers` option and does not accept a
|
|
938
|
+
factory.
|
|
939
|
+
|
|
681
940
|
### Public local debugging checklist
|
|
682
941
|
|
|
683
942
|
- Operation smoke requests use the provider server envelope:
|
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
|
@@ -78,6 +78,7 @@ The operation request body is the same envelope used by the APIFuse gateway:
|
|
|
78
78
|
| `headers` | no | Extra caller headers to expose through `ctx.request.headers`. |
|
|
79
79
|
| `connectionId` | no | Connection identity only; it does not include credentials. The gateway sends it for `optional` connection mode, and only when the caller supplied a connection that passed authorization. Exposed as `ctx.request.connectionId`. |
|
|
80
80
|
| `connection` | no | Credential-bearing connection data. The gateway sends it for `required` connection mode; omit it for no-auth/public (`none` mode) operations. For local debugging, pass an object with `id`, `mode`, `secrets`, `metadata`, and `externalRef`. Do not pass `null`. |
|
|
81
|
+
| `tenantId` | no | Gateway-asserted principal scope: the organization id for customer subjects, the service-account id for platform service accounts. Sent for every connection mode, including `none`; absent on direct calls and self-test. Exposed as `ctx.request.tenantId` (`undefined` when absent or empty). Caller headers never populate it. |
|
|
81
82
|
|
|
82
83
|
The gateway sends only `connection` for `required` mode, only `connectionId`
|
|
83
84
|
for `optional` mode (and nothing when the caller passed no connection), and
|
package/bin/apifuse-check.ts
CHANGED
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
DECLARATION_INVALID_CODE,
|
|
16
16
|
validateFailClosedDeclaration,
|
|
17
17
|
} from "../src/declaration-validation.js";
|
|
18
|
+
import { redundantDeploymentDefaults } from "../src/cli/deployment-intent.js";
|
|
18
19
|
import { isProviderError } from "../src/errors.js";
|
|
19
20
|
import type { ProviderDefinition } from "../src/index.js";
|
|
20
21
|
import { lintProviderWithInformation, type ProviderLintMode } from "../src/lint.js";
|
|
@@ -140,6 +141,7 @@ export async function runChecks(
|
|
|
140
141
|
}
|
|
141
142
|
const provider = assertProviderDefinition(providerModule?.default);
|
|
142
143
|
const providerSourceFiles = collectProviderSourceFiles(providerRoot);
|
|
144
|
+
const localeCatalogEn = readEnglishLocaleCatalog(providerRoot);
|
|
143
145
|
|
|
144
146
|
return [
|
|
145
147
|
checkIndex(indexPath, provider),
|
|
@@ -147,8 +149,9 @@ export async function runChecks(
|
|
|
147
149
|
checkOperations(provider),
|
|
148
150
|
checkFixtures(provider),
|
|
149
151
|
checkSchemas(provider),
|
|
150
|
-
checkAuthoringLint(provider, providerSourceFiles, options.lintMode),
|
|
152
|
+
checkAuthoringLint(provider, providerSourceFiles, localeCatalogEn, options.lintMode),
|
|
151
153
|
checkProviderMetadata(provider),
|
|
154
|
+
await checkDeploymentIntent(providerRoot, provider),
|
|
152
155
|
checkDockerfile(dockerfilePath),
|
|
153
156
|
checkPackageJson(packageJsonPath),
|
|
154
157
|
checkProviderJson(providerJsonPath, packageJsonPath),
|
|
@@ -367,9 +370,30 @@ function checkSchemas(provider: ProviderDefinition | undefined): CheckResult {
|
|
|
367
370
|
};
|
|
368
371
|
}
|
|
369
372
|
|
|
373
|
+
/**
|
|
374
|
+
* Reads `locales/en.json` so the error-localization lint rules can resolve
|
|
375
|
+
* declared `messageKey`/`fixKey` values. An absent or unreadable catalog is
|
|
376
|
+
* reported as an empty catalog rather than as "no catalog": a provider that
|
|
377
|
+
* references locale keys without shipping `en.json` must still fail the
|
|
378
|
+
* missing-key rule instead of silently skipping it.
|
|
379
|
+
*/
|
|
380
|
+
function readEnglishLocaleCatalog(providerRoot: string): Record<string, unknown> {
|
|
381
|
+
const catalogPath = resolve(providerRoot, "locales", "en.json");
|
|
382
|
+
if (!existsSync(catalogPath)) return {};
|
|
383
|
+
try {
|
|
384
|
+
const parsed: unknown = JSON.parse(readFileSync(catalogPath, "utf8"));
|
|
385
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed)
|
|
386
|
+
? (parsed as Record<string, unknown>)
|
|
387
|
+
: {};
|
|
388
|
+
} catch {
|
|
389
|
+
return {};
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
370
393
|
function checkAuthoringLint(
|
|
371
394
|
provider: ProviderDefinition | undefined,
|
|
372
395
|
providerSourceFiles: Record<string, string>,
|
|
396
|
+
localeCatalogEn: Record<string, unknown>,
|
|
373
397
|
lintMode: ProviderLintMode = "official",
|
|
374
398
|
): CheckResult {
|
|
375
399
|
if (!provider) {
|
|
@@ -380,7 +404,7 @@ function checkAuthoringLint(
|
|
|
380
404
|
}
|
|
381
405
|
|
|
382
406
|
const { diagnostics, information } = lintProviderWithInformation(
|
|
383
|
-
{ ...provider, providerSourceFiles },
|
|
407
|
+
{ ...provider, providerSourceFiles, localeCatalogEn },
|
|
384
408
|
{ mode: lintMode },
|
|
385
409
|
);
|
|
386
410
|
const errors = diagnostics.filter((diagnostic) => diagnostic.level === "error");
|
|
@@ -448,6 +472,64 @@ function checkProviderMetadata(provider: ProviderDefinition | undefined): CheckR
|
|
|
448
472
|
};
|
|
449
473
|
}
|
|
450
474
|
|
|
475
|
+
export const DEPLOYMENT_INTENT_CHECK_MESSAGE =
|
|
476
|
+
"Deployment intent lives only in defineProvider({ deployment }) without restating profile defaults";
|
|
477
|
+
export const LEGACY_DEPLOY_FILE_RULE = "deployment/legacy-deploy-file";
|
|
478
|
+
export const REDUNDANT_DEPLOYMENT_DEFAULT_RULE = "deployment/redundant-default";
|
|
479
|
+
export const SPREAD_EXPORT_DEPLOYMENT_RULE = "deployment/spread-export";
|
|
480
|
+
|
|
481
|
+
/**
|
|
482
|
+
* Deployment intent has one authored home: the `deployment` key on
|
|
483
|
+
* `defineProvider()`, carrying only the fields that differ from the runtime
|
|
484
|
+
* profile. A standalone `deploy.ts` is a legacy surface and a field that
|
|
485
|
+
* restates a profile default is noise the platform derives anyway. Both are
|
|
486
|
+
* reported as warnings while the fleet migrates (`apifuse
|
|
487
|
+
* migrate-deployment`); they become errors once no repository carries the
|
|
488
|
+
* legacy file.
|
|
489
|
+
*/
|
|
490
|
+
async function checkDeploymentIntent(
|
|
491
|
+
providerRoot: string,
|
|
492
|
+
provider: ProviderDefinition | undefined,
|
|
493
|
+
): Promise<CheckResult> {
|
|
494
|
+
const details: string[] = [];
|
|
495
|
+
if (existsSync(resolve(providerRoot, "deploy.ts"))) {
|
|
496
|
+
details.push(
|
|
497
|
+
`WARN ${LEGACY_DEPLOY_FILE_RULE}: deploy.ts is a legacy deployment surface; run \`apifuse migrate-deployment .\` to move its non-default values into defineProvider({ deployment }) and delete the file.`,
|
|
498
|
+
);
|
|
499
|
+
}
|
|
500
|
+
// A `deployment` extra on `export default { ...provider, deployment }` is
|
|
501
|
+
// indistinguishable from a declaration key once the module is loaded, so
|
|
502
|
+
// inspect the source. The inspector needs the TypeScript compiler (a
|
|
503
|
+
// devDependency of provider repositories); without it this rule is skipped.
|
|
504
|
+
const indexPath = resolve(providerRoot, "index.ts");
|
|
505
|
+
if (existsSync(indexPath)) {
|
|
506
|
+
try {
|
|
507
|
+
const { findSpreadExportDeploymentProperty } = await import(
|
|
508
|
+
"../src/cli/migrate-deployment-intent.js"
|
|
509
|
+
);
|
|
510
|
+
const spread = findSpreadExportDeploymentProperty(readFileSync(indexPath, "utf8"), indexPath);
|
|
511
|
+
if (spread !== undefined) {
|
|
512
|
+
details.push(
|
|
513
|
+
`WARN ${SPREAD_EXPORT_DEPLOYMENT_RULE}: index.ts:${spread.line} attaches \`deployment\` to a spread default export; the only authored surface is the \`deployment\` key inside defineProvider({...}). Run \`apifuse migrate-deployment .\` to hoist it.`,
|
|
514
|
+
);
|
|
515
|
+
}
|
|
516
|
+
} catch {
|
|
517
|
+
// TypeScript unavailable: the source-shape rule cannot run here.
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
if (provider?.deployment !== undefined) {
|
|
521
|
+
for (const redundant of redundantDeploymentDefaults(provider.deployment)) {
|
|
522
|
+
details.push(`WARN ${REDUNDANT_DEPLOYMENT_DEFAULT_RULE}: ${redundant.message}`);
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
return {
|
|
526
|
+
message: DEPLOYMENT_INTENT_CHECK_MESSAGE,
|
|
527
|
+
// Warning-only during the fleet migration window.
|
|
528
|
+
passed: true,
|
|
529
|
+
details,
|
|
530
|
+
};
|
|
531
|
+
}
|
|
532
|
+
|
|
451
533
|
function checkDockerfile(dockerfilePath: string): CheckResult {
|
|
452
534
|
return {
|
|
453
535
|
message: "Dockerfile exists",
|
package/bin/apifuse-dev.ts
CHANGED
|
@@ -11,20 +11,18 @@ import {
|
|
|
11
11
|
createProviderCache,
|
|
12
12
|
createHandleContext,
|
|
13
13
|
createProviderEnvironment,
|
|
14
|
-
createUnsupportedResolverClient,
|
|
15
14
|
createSttClientFromEnv,
|
|
16
|
-
readEngineProxyCredentials,
|
|
17
15
|
type ProviderDefinition,
|
|
18
16
|
type ProviderEngineBindingCandidates,
|
|
19
17
|
ProviderError,
|
|
20
|
-
type ProviderProxyPolicy,
|
|
21
18
|
} from "../src/index.js";
|
|
19
|
+
import { createCliResolverRuntime } from "../src/cli/resolver-runtime.js";
|
|
20
|
+
import { assertProcessEngineModeSupported } from "../src/runtime/engine-mode.js";
|
|
22
21
|
import { createBrowserClient } from "../src/runtime/browser.js";
|
|
23
|
-
import { createResolverClientFromEnv } from "../src/runtime/resolver.js";
|
|
24
22
|
import { createMemoryProviderRuntimeState } from "../src/runtime/state.js";
|
|
25
23
|
import { createStealthClient } from "../src/runtime/stealth.js";
|
|
24
|
+
import type { ResolverTelemetryCollector } from "../src/runtime/resolver-telemetry.js";
|
|
26
25
|
import { createTraceContext } from "../src/runtime/trace.js";
|
|
27
|
-
import { getStealthProfile } from "../src/stealth/profiles.js";
|
|
28
26
|
import type { BrowserClient, ProviderContext } from "../src/types.js";
|
|
29
27
|
|
|
30
28
|
const HELP_TEXT = `Usage: apifuse dev [path]
|
|
@@ -43,6 +41,9 @@ export async function main() {
|
|
|
43
41
|
const providerModule = await import(resolve(providerPath, "index.ts"));
|
|
44
42
|
const provider = assertProviderDefinition(providerModule.default, providerPath);
|
|
45
43
|
|
|
44
|
+
// Fail fast before the server starts: this is a developer tool, and an
|
|
45
|
+
// explicit remote request must never silently fall back to in-process.
|
|
46
|
+
assertProcessEngineModeSupported();
|
|
46
47
|
const { startDevServer } = await import("../src/dev.js");
|
|
47
48
|
const port = Number(process.env.APIFUSE__RUNTIME__PORT) || 3900;
|
|
48
49
|
|
|
@@ -50,6 +51,7 @@ export async function main() {
|
|
|
50
51
|
|
|
51
52
|
console.log("\nEndpoints:");
|
|
52
53
|
console.log(` GET http://localhost:${port}/health`);
|
|
54
|
+
console.log(` GET http://localhost:${port}/readyz`);
|
|
53
55
|
|
|
54
56
|
for (const operationId of Object.keys(provider.operations)) {
|
|
55
57
|
console.log(` POST http://localhost:${port}/v1/${operationId}`);
|
|
@@ -81,18 +83,17 @@ export async function main() {
|
|
|
81
83
|
|
|
82
84
|
export function createProviderContext(provider: ProviderDefinition): {
|
|
83
85
|
ctx: ProviderContext;
|
|
86
|
+
resolverTelemetry: ResolverTelemetryCollector;
|
|
84
87
|
} {
|
|
85
88
|
const providerEnvironment = createProviderEnvironment(
|
|
86
89
|
process.env,
|
|
87
90
|
provider.secrets?.map((secret) => secret.name) ?? [],
|
|
88
91
|
);
|
|
89
92
|
const providerEnv = { get: (key: string) => readDiagnosticEnv(key, providerEnvironment) };
|
|
90
|
-
const engineCredentials = readEngineProxyCredentials();
|
|
91
93
|
const credential = createCredentialContext();
|
|
92
94
|
const state = createMemoryProviderRuntimeState();
|
|
93
95
|
const cache = createProviderCache({ providerId: provider.id });
|
|
94
|
-
const
|
|
95
|
-
const stealthProfile = provider.stealth ? getStealthProfile(provider.stealth) : undefined;
|
|
96
|
+
const { resolver, resolverTelemetry } = createCliResolverRuntime(provider, cache);
|
|
96
97
|
const candidates: ProviderEngineBindingCandidates = {
|
|
97
98
|
env: providerEnv,
|
|
98
99
|
credential,
|
|
@@ -113,21 +114,7 @@ export function createProviderContext(provider: ProviderDefinition): {
|
|
|
113
114
|
}),
|
|
114
115
|
ocr: createOcrClientFromEnv(provider.ocr),
|
|
115
116
|
stt: createSttClientFromEnv(provider.stt),
|
|
116
|
-
resolver
|
|
117
|
-
? createResolverClientFromEnv(provider.resolver, engineCredentials, {
|
|
118
|
-
allowedHosts: provider.allowedHosts,
|
|
119
|
-
cache,
|
|
120
|
-
...(proxyPolicy
|
|
121
|
-
? {
|
|
122
|
-
proxyIntent: {
|
|
123
|
-
mode: proxyPolicy.mode,
|
|
124
|
-
upstream: { proxy: provider.proxy },
|
|
125
|
-
...(stealthProfile ? { userAgent: stealthProfile.userAgent } : {}),
|
|
126
|
-
},
|
|
127
|
-
}
|
|
128
|
-
: {}),
|
|
129
|
-
})
|
|
130
|
-
: createUnsupportedResolverClient("Provider does not declare resolver capability"),
|
|
117
|
+
resolver,
|
|
131
118
|
handle: createHandleContext({
|
|
132
119
|
providerId: provider.id,
|
|
133
120
|
// Local dev is a single-user session: one stable connection scope so
|
|
@@ -141,14 +128,7 @@ export function createProviderContext(provider: ProviderDefinition): {
|
|
|
141
128
|
bindings: candidates,
|
|
142
129
|
}) as ProviderContext;
|
|
143
130
|
|
|
144
|
-
return { ctx };
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
function resolveNativeProxyPolicy(provider: ProviderDefinition): ProviderProxyPolicy | undefined {
|
|
148
|
-
if (typeof provider.proxy === "object") return provider.proxy;
|
|
149
|
-
if (provider.proxy === true) return { mode: "optional" };
|
|
150
|
-
if (provider.proxy === false) return { mode: "disabled" };
|
|
151
|
-
return undefined;
|
|
131
|
+
return { ctx, resolverTelemetry };
|
|
152
132
|
}
|
|
153
133
|
|
|
154
134
|
function normalizeArgs(argv: string[]): string[] {
|