@apifuse/provider-sdk 2.2.0-beta.61 → 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 +8 -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 +191 -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/handle-meta.d.ts +18 -2
- package/dist/handle-meta.js +30 -2
- package/dist/handle.d.ts +33 -12
- package/dist/handle.js +69 -24
- 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 +10 -2
- package/dist/index.js +9 -1
- package/dist/lint.d.ts +10 -0
- package/dist/lint.js +440 -57
- package/dist/provider.d.ts +7 -5
- package/dist/provider.js +4 -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/handle.js +15 -10
- package/dist/runtime/http-telemetry-binding.d.ts +10 -0
- package/dist/runtime/http-telemetry-binding.js +152 -0
- package/dist/runtime/http-telemetry-guard.d.ts +16 -0
- package/dist/runtime/http-telemetry-guard.js +202 -0
- package/dist/runtime/http-telemetry.d.ts +104 -0
- package/dist/runtime/http-telemetry.js +190 -0
- package/dist/runtime/http.d.ts +31 -0
- package/dist/runtime/http.js +356 -174
- 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 +9 -0
- package/dist/server/index.js +5 -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 +53 -2
- package/dist/server/serve-implementation.js +369 -123
- 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/handle-meta.ts +39 -4
- package/src/handle.ts +121 -38
- 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 +91 -0
- package/src/lint.ts +552 -57
- package/src/provider.ts +20 -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 +20 -10
- package/src/runtime/http-telemetry-binding.ts +162 -0
- package/src/runtime/http-telemetry-guard.ts +208 -0
- package/src/runtime/http-telemetry.ts +292 -0
- package/src/runtime/http.ts +457 -204
- 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 +66 -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 +601 -157
- 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
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { bindCapabilityRoot } from "../runtime/capability-telemetry.js";
|
|
2
2
|
import { STATUS_CODES } from "node:http";
|
|
3
3
|
import { readDiagnosticEnv, withDiagnosticEnv } from "../runtime/diagnostic-env.js";
|
|
4
4
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
@@ -10,15 +10,20 @@ import { Hono } from "hono";
|
|
|
10
10
|
import { z } from "zod";
|
|
11
11
|
import { AuthAbortError, createAuthFlowHelpers } from "../auth.js";
|
|
12
12
|
import { validateFailClosedDeclaration } from "../declaration-validation.js";
|
|
13
|
-
import {
|
|
13
|
+
import { ENGINE_OWNED_PROXY_CREDENTIAL_ENV_NAMES, isEngineOwnedEnvName, readEngineProxyCredentials, } from "../engine.js";
|
|
14
|
+
import { createEngineForMode, isUnavailableProviderEngine, resolveProviderEngineMode, } from "../runtime/engine-mode.js";
|
|
15
|
+
import { SDK_VERSION } from "./sdk-version.js";
|
|
14
16
|
import { safeProviderErrorObservability } from "../error-observability.js";
|
|
17
|
+
import { providerErrorStackFrames } from "./error-stack-frames.js";
|
|
15
18
|
import { SDK_OWNED_PROVIDER_ERROR_CODES, SDK_RUNTIME_OWNED_ERROR_CODES, SDK_STATUS_MAPPED_PROVIDER_ERROR_CODES, } from "../error-resolution.js";
|
|
16
19
|
import { AuthError, isProviderError, isSessionExpiredError, isTransportError, isValidationError, ProviderError, SDKError, } from "../errors.js";
|
|
17
20
|
import { sanitizeDiagnosticText } from "../fixture-sanitization.js";
|
|
18
21
|
import { loadProviderLocaleCatalogs, localizeAuthTurn, } from "../i18n/catalog.js";
|
|
22
|
+
import { acceptLanguageHeaderValue, DEFAULT_PROVIDER_ERROR_LOCALE, localizeProviderErrorText, PROVIDER_ERROR_LOCALES, resolveProviderErrorLocale, } from "../i18n/error-messages.js";
|
|
19
23
|
import { categoryForStatus, isRetryableCategory, PROVIDER_OBSERVABILITY_TAXONOMY_VERSION, sourceForCategory, } from "../observability.js";
|
|
20
24
|
import { createScratchpad } from "../runtime/auth-flow.js";
|
|
21
25
|
import { createProviderCache } from "../runtime/cache.js";
|
|
26
|
+
import { bindCacheTelemetry, CacheTelemetryCollector } from "../runtime/cache-telemetry.js";
|
|
22
27
|
import { createHandleContext } from "../runtime/handle.js";
|
|
23
28
|
import { createCredentialContext } from "../runtime/credential.js";
|
|
24
29
|
import { bindDiagnosticSensitiveRegistry, compileProcessDiagnosticSensitiveValues, createDiagnosticRedactor, REDACTION_FAILED, isDiagnosticRedactionSuppressed, bindCriticalDiagnosticRedactor, redactCriticalDiagnosticText, redactDiagnosticText, registerDiagnosticSensitiveValues, } from "../runtime/diagnostic-redactor.js";
|
|
@@ -26,18 +31,28 @@ import { createCeremonyEgressLeaseRuntime, ENGINE_CEREMONY_EGRESS_LEASE, } from
|
|
|
26
31
|
import { createEnvContext } from "../runtime/env.js";
|
|
27
32
|
import { executeOperation } from "../runtime/executor.js";
|
|
28
33
|
import { createHttpClient } from "../runtime/http.js";
|
|
34
|
+
import { NativeTelemetryCollector } from "../runtime/native-telemetry.js";
|
|
35
|
+
import { bindHttpTelemetry, isSupportedHttpBinding, warnUnsupportedHttpBinding, } from "../runtime/http-telemetry-binding.js";
|
|
36
|
+
import { HttpTelemetryCollector } from "../runtime/http-telemetry.js";
|
|
29
37
|
import { wrapWithInstrumentation } from "../runtime/instrumentation.js";
|
|
30
38
|
import { createOcrClientFromEnv } from "../runtime/ocr.js";
|
|
39
|
+
import { bindOcrTelemetry, OcrTelemetryCollector } from "../runtime/ocr-telemetry.js";
|
|
31
40
|
import { getProviderBaseUrl } from "../runtime/provider.js";
|
|
32
41
|
import { PROXY_AUTH_IP_DENIED_CODE, PROXY_EDGE_AUTH_REJECTED_CODE, PROXY_POOL_EXHAUSTED_CODE, } from "../runtime/proxy-errors.js";
|
|
33
42
|
import { PROVIDER_TELEMETRY_HEADER, ProxyTelemetryCollector, } from "../runtime/proxy-telemetry.js";
|
|
34
43
|
import { closedEnum, RequestTelemetry, tenantOpaqueKeys, } from "../runtime/request-telemetry.js";
|
|
35
44
|
import { bindResolverTelemetry, createUnsupportedResolverClient, inheritResolverTelemetryBinding, } from "../runtime/resolver-shared.js";
|
|
36
45
|
import { ResolverTelemetryCollector, } from "../runtime/resolver-telemetry.js";
|
|
46
|
+
import { createResolverRuntimeOptions, resolveNativeProxyPolicy, } from "../runtime/resolver-runtime-options.js";
|
|
37
47
|
import { assertRequiredSecretsPresent, listMissingRequiredSecrets, MISSING_SECRET_CODE, } from "../runtime/secrets.js";
|
|
38
48
|
import { createProviderRuntimeStateFromEnv, createUnsupportedProviderRuntimeState, } from "../runtime/state.js";
|
|
49
|
+
import { StateTelemetryCollector, instrumentProviderRuntimeState, } from "../runtime/state-telemetry.js";
|
|
39
50
|
import { StealthCookieJar } from "../runtime/stealth-cookies.js";
|
|
51
|
+
import { StealthTelemetryCollector } from "../runtime/stealth-telemetry.js";
|
|
52
|
+
import { BrowserTelemetryCollector } from "../runtime/browser-telemetry.js";
|
|
53
|
+
import { bindBrowserTelemetry, isSupportedBrowserBinding, warnUnsupportedBrowserBinding, } from "../runtime/browser-telemetry-binding.js";
|
|
40
54
|
import { createSttClientFromEnv } from "../runtime/stt.js";
|
|
55
|
+
import { bindSttTelemetry, SttTelemetryCollector } from "../runtime/stt-telemetry.js";
|
|
41
56
|
import { createTraceContext, getTraceRecorder, updateTraceContextExportMetadata, } from "../runtime/trace.js";
|
|
42
57
|
import { resolveTraceConfigFromEnv } from "../runtime/trace-config.js";
|
|
43
58
|
import { parseSchema } from "../schema.js";
|
|
@@ -81,18 +96,7 @@ function providerErrorCode(error) {
|
|
|
81
96
|
return undefined;
|
|
82
97
|
}
|
|
83
98
|
}
|
|
84
|
-
const AUTH_FLOW_LOCALES = ["en", "ko", "ja"];
|
|
85
99
|
const retryResponseMeta = new WeakMap();
|
|
86
|
-
const RETRY_LAST_ERROR_CODES = [
|
|
87
|
-
"transport_cancelled",
|
|
88
|
-
"transport_timeout",
|
|
89
|
-
"transport_network_error",
|
|
90
|
-
"upstream_http_error",
|
|
91
|
-
"other",
|
|
92
|
-
];
|
|
93
|
-
function tenantRetryLastErrorCode(value) {
|
|
94
|
-
return RETRY_LAST_ERROR_CODES.find((candidate) => candidate === value) ?? "other";
|
|
95
|
-
}
|
|
96
100
|
const STATEFUL_INTERNAL_OPERATIONS_ROUTE = "/__apifuse/stateful/operations";
|
|
97
101
|
const STATEFUL_FORWARDING_SOURCE_POD_HEADER = "x-apifuse-stateful-source-pod";
|
|
98
102
|
const DEFAULT_STATEFUL_FORWARDING_MAX_SKEW_MS = 5 * 60_000;
|
|
@@ -121,6 +125,18 @@ export const ProviderServerStatefulForwardEnvelopeSchema = z
|
|
|
121
125
|
}).strict(),
|
|
122
126
|
})
|
|
123
127
|
.strict();
|
|
128
|
+
// Inbound compatibility boundary for rolling deploys, mirroring the outbound
|
|
129
|
+
// one in stateful-provider-owner-forwarder.ts: emitted envelopes stay strict
|
|
130
|
+
// (above), while a received envelope's nested operation request keeps the
|
|
131
|
+
// gateway route's tolerance and strips unknown keys. A source pod on a newer
|
|
132
|
+
// SDK carrying an additive OperationRequestSchema field (tenantId, #164) must
|
|
133
|
+
// not be rejected with STATEFUL_FORWARDING_ENVELOPE_INVALID by an owner pod
|
|
134
|
+
// that predates the field: the rejection is terminal for the operation and the
|
|
135
|
+
// two pods coexist for the whole rolling update. The envelope's own keys stay
|
|
136
|
+
// strict on both sides, because they are protocol rather than payload.
|
|
137
|
+
const ReceivedStatefulForwardEnvelopeSchema = ProviderServerStatefulForwardEnvelopeSchema.extend({
|
|
138
|
+
operationRequest: z.object(ProviderServerStatefulForwardEnvelopeSchema.shape.operationRequest.shape),
|
|
139
|
+
});
|
|
124
140
|
const require = createRequire(import.meta.url);
|
|
125
141
|
function normalizedCapabilityCause(error) {
|
|
126
142
|
if (error instanceof Error)
|
|
@@ -335,24 +351,6 @@ function getProviderStealthBaseUrl(provider) {
|
|
|
335
351
|
function getProviderStealthProfile(provider) {
|
|
336
352
|
return provider.stealth ? getStealthProfile(provider.stealth) : undefined;
|
|
337
353
|
}
|
|
338
|
-
/** One option set for ctx.resolver and the automatic SBSD solve, so both share identity scope, cache, proxy intent, and telemetry. */
|
|
339
|
-
function createResolverRuntimeOptions(provider, cache, identityScope, proxyPolicy, proxyClientOptions, stealthProfile, telemetry) {
|
|
340
|
-
return {
|
|
341
|
-
allowedHosts: provider.allowedHosts,
|
|
342
|
-
cache,
|
|
343
|
-
telemetry,
|
|
344
|
-
identityScope,
|
|
345
|
-
...(proxyPolicy
|
|
346
|
-
? {
|
|
347
|
-
proxyIntent: {
|
|
348
|
-
mode: proxyPolicy.mode,
|
|
349
|
-
...proxyClientOptions,
|
|
350
|
-
...(stealthProfile ? { userAgent: stealthProfile.userAgent } : {}),
|
|
351
|
-
},
|
|
352
|
-
}
|
|
353
|
-
: {}),
|
|
354
|
-
};
|
|
355
|
-
}
|
|
356
354
|
/** Shared predicate for the SBSD challenge runtime and the ceremony egress lease. */
|
|
357
355
|
function declaresAkamaiSbsdResolver(provider) {
|
|
358
356
|
return provider.resolver?.kinds.some((kind) => kind === "akamai_sbsd") === true;
|
|
@@ -375,12 +373,13 @@ function createStealthChallengeDetection(provider, resolverRuntime, resolverOver
|
|
|
375
373
|
? {
|
|
376
374
|
async solve(challenge, transport, solveSignal) {
|
|
377
375
|
const resolver = resolverRuntime.bindResolverSignal((resolverOverride
|
|
378
|
-
? bindResolverTelemetry(resolverOverride, resolverOptions.telemetry)
|
|
376
|
+
? bindResolverTelemetry(resolverOverride, resolverOptions.telemetry, resolverOptions.browserTelemetry)
|
|
379
377
|
: undefined) ??
|
|
380
378
|
resolverRuntime.createResolverClientFromEnv(provider.resolver, undefined, {
|
|
381
379
|
allowedHosts: resolverOptions.allowedHosts,
|
|
382
380
|
cache: resolverOptions.cache,
|
|
383
381
|
telemetry: resolverOptions.telemetry,
|
|
382
|
+
browserTelemetry: resolverOptions.browserTelemetry,
|
|
384
383
|
identityScope: resolverOptions.identityScope,
|
|
385
384
|
// No proxyIntent: the transport is already bound to the initiating
|
|
386
385
|
// request's lease, and a second identity resolution would allocate
|
|
@@ -473,19 +472,16 @@ function resolveOperationConnectionId(request) {
|
|
|
473
472
|
// absent so it can never override a valid id or key a real scope. Requests
|
|
474
473
|
// without any usable id fall back to the documented missing-connection
|
|
475
474
|
// sentinel scope instead of scoping context/affinity/state under "".
|
|
476
|
-
return (
|
|
475
|
+
return normalizeIdentifier(request.connection?.id) ?? normalizeIdentifier(request.connectionId);
|
|
477
476
|
}
|
|
478
|
-
function
|
|
479
|
-
|
|
477
|
+
function resolveOperationTenantId(request) {
|
|
478
|
+
// Only the gateway-asserted envelope field is consulted; caller headers can
|
|
479
|
+
// never populate the principal scope. Same rule as connection ids: "" is a
|
|
480
|
+
// malformed identifier, not a principal.
|
|
481
|
+
return normalizeIdentifier(request.tenantId);
|
|
480
482
|
}
|
|
481
|
-
function
|
|
482
|
-
|
|
483
|
-
return provider.proxy;
|
|
484
|
-
if (provider.proxy === true)
|
|
485
|
-
return { mode: "optional" };
|
|
486
|
-
if (provider.proxy === false)
|
|
487
|
-
return { mode: "disabled" };
|
|
488
|
-
return undefined;
|
|
483
|
+
function normalizeIdentifier(id) {
|
|
484
|
+
return id === "" ? undefined : id;
|
|
489
485
|
}
|
|
490
486
|
function providerSecretNames(provider) {
|
|
491
487
|
return (provider.secrets?.map((secret) => secret.name) ?? []).filter((name) => !isEngineOwnedEnvName(name));
|
|
@@ -503,12 +499,12 @@ function createProviderContext(provider, request, operationId, options, state =
|
|
|
503
499
|
engineCredentials: engineProxyCredentials,
|
|
504
500
|
};
|
|
505
501
|
const resolverIdentityScope = resolveProviderResolverIdentityScope(provider, proxyClientOptions.affinityKey, request.requestId);
|
|
506
|
-
const cache = createProviderCache({ providerId: provider.id });
|
|
507
|
-
let wrappedContext;
|
|
502
|
+
const cache = createProviderCache(bindCacheTelemetry({ providerId: provider.id }, scope.cacheTelemetry));
|
|
508
503
|
const { capabilityModules } = options;
|
|
509
|
-
const resolverOptions = createResolverRuntimeOptions(provider, cache, resolverIdentityScope, proxyPolicy, proxyClientOptions, stealthProfile, scope.resolverTelemetry);
|
|
504
|
+
const resolverOptions = createResolverRuntimeOptions(provider, cache, resolverIdentityScope, proxyPolicy, proxyClientOptions, stealthProfile, scope.resolverTelemetry, scope.browserTelemetry);
|
|
510
505
|
const challengeRuntime = createStealthChallengeDetection(provider, capabilityModules.resolver, options.resolver, resolverOptions, scope.trace, signal);
|
|
511
506
|
const stealthClientOptions = {
|
|
507
|
+
stealthTelemetry: scope.stealthTelemetry,
|
|
512
508
|
upstream: proxyClientOptions.upstream,
|
|
513
509
|
affinityKey: proxyClientOptions.affinityKey,
|
|
514
510
|
telemetry: scope.telemetry.proxy,
|
|
@@ -533,11 +529,15 @@ function createProviderContext(provider, request, operationId, options, state =
|
|
|
533
529
|
scopes: request.connection?.scopes,
|
|
534
530
|
values: request.connection?.secrets,
|
|
535
531
|
});
|
|
532
|
+
const operationTenantId = resolveOperationTenantId(request);
|
|
536
533
|
const requestContext = {
|
|
537
534
|
connectionId: resolveOperationConnectionId(request),
|
|
535
|
+
// Absent stays absent: an own property with an undefined value would
|
|
536
|
+
// change the shape of ctx.request for every existing consumer.
|
|
537
|
+
...(operationTenantId !== undefined ? { tenantId: operationTenantId } : {}),
|
|
538
538
|
headers: request.headers ?? {},
|
|
539
539
|
};
|
|
540
|
-
const requestState = state.forConnection(requestContext.connectionId);
|
|
540
|
+
const requestState = instrumentProviderRuntimeState(state, scope.stateTelemetry, scope.redact).forConnection(requestContext.connectionId);
|
|
541
541
|
const bindings = {
|
|
542
542
|
env,
|
|
543
543
|
credential,
|
|
@@ -546,11 +546,7 @@ function createProviderContext(provider, request, operationId, options, state =
|
|
|
546
546
|
http: createHttpClient(baseUrl, {
|
|
547
547
|
...proxyClientOptions,
|
|
548
548
|
...(signal ? { signal } : {}),
|
|
549
|
-
|
|
550
|
-
if (summary.attempts <= 1 || !wrappedContext)
|
|
551
|
-
return;
|
|
552
|
-
retryResponseMeta.set(wrappedContext, summary);
|
|
553
|
-
},
|
|
549
|
+
httpTelemetry: scope.httpTelemetry,
|
|
554
550
|
}),
|
|
555
551
|
cache,
|
|
556
552
|
state: requestState,
|
|
@@ -567,6 +563,7 @@ function createProviderContext(provider, request, operationId, options, state =
|
|
|
567
563
|
requireCdpPool: isProductionProviderBrowserMode(provider),
|
|
568
564
|
stealth: true,
|
|
569
565
|
engine: provider.browser?.engine,
|
|
566
|
+
telemetry: scope.browserTelemetry,
|
|
570
567
|
})
|
|
571
568
|
: createBrowserStub(),
|
|
572
569
|
...(provider.native
|
|
@@ -585,15 +582,15 @@ function createProviderContext(provider, request, operationId, options, state =
|
|
|
585
582
|
: {}),
|
|
586
583
|
trace: scope.trace,
|
|
587
584
|
auth: createAuthStub(),
|
|
588
|
-
ocr: options.ocr ?? createOcrClientFromEnv(provider.ocr),
|
|
589
|
-
stt: options.stt ?? createSttClientFromEnv(provider.stt),
|
|
585
|
+
ocr: bindCapabilityRoot(bindOcrTelemetry(options.ocr ?? createOcrClientFromEnv(provider.ocr), scope.ocrTelemetry), scope.run),
|
|
586
|
+
stt: bindCapabilityRoot(bindSttTelemetry(options.stt ?? createSttClientFromEnv(provider.stt), scope.sttTelemetry), scope.run),
|
|
590
587
|
resolver: capabilityModules.resolver
|
|
591
588
|
? capabilityModules.resolver.bindResolverSignal((options.resolver
|
|
592
|
-
? bindResolverTelemetry(options.resolver, resolverOptions.telemetry)
|
|
589
|
+
? bindResolverTelemetry(options.resolver, resolverOptions.telemetry, resolverOptions.browserTelemetry)
|
|
593
590
|
: undefined) ??
|
|
594
591
|
capabilityModules.resolver.createResolverClientFromEnv(provider.resolver, undefined, resolverOptions), signal)
|
|
595
592
|
: bindResolverSignalWithoutRuntime((options.resolver
|
|
596
|
-
? bindResolverTelemetry(options.resolver, resolverOptions.telemetry)
|
|
593
|
+
? bindResolverTelemetry(options.resolver, resolverOptions.telemetry, resolverOptions.browserTelemetry)
|
|
597
594
|
: undefined) ??
|
|
598
595
|
createUnsupportedResolverClient("Provider does not declare resolver capability"), signal),
|
|
599
596
|
handle: createHandleContext({
|
|
@@ -607,8 +604,44 @@ function createProviderContext(provider, request, operationId, options, state =
|
|
|
607
604
|
}),
|
|
608
605
|
}),
|
|
609
606
|
};
|
|
610
|
-
const
|
|
611
|
-
|
|
607
|
+
const candidateHttp = bindings.http;
|
|
608
|
+
const candidateBrowser = bindings.browser;
|
|
609
|
+
const attached = options.engine.attach({ provider, bindings });
|
|
610
|
+
const hostHttp = provider.http ? attached.http : undefined;
|
|
611
|
+
const supportedHostHttp = provider.http && isSupportedHttpBinding(hostHttp);
|
|
612
|
+
if (provider.http && !supportedHostHttp)
|
|
613
|
+
warnUnsupportedHttpBinding();
|
|
614
|
+
const observedHttp = supportedHostHttp && hostHttp !== candidateHttp
|
|
615
|
+
? bindHttpTelemetry(hostHttp, scope.httpTelemetry)
|
|
616
|
+
: undefined;
|
|
617
|
+
const hostBrowser = provider.runtime === "browser" ? attached.browser : undefined;
|
|
618
|
+
const supportedHostBrowser = provider.runtime === "browser" && isSupportedBrowserBinding(hostBrowser);
|
|
619
|
+
if (provider.runtime === "browser" && !supportedHostBrowser)
|
|
620
|
+
warnUnsupportedBrowserBinding();
|
|
621
|
+
const observedBrowser = supportedHostBrowser && hostBrowser !== candidateBrowser
|
|
622
|
+
? bindBrowserTelemetry(hostBrowser, scope.browserTelemetry)
|
|
623
|
+
: undefined;
|
|
624
|
+
const contextTarget = observedBrowser || observedHttp
|
|
625
|
+
? new Proxy(attached, {
|
|
626
|
+
get(target, property, receiver) {
|
|
627
|
+
if (property === "http" && observedHttp)
|
|
628
|
+
return observedHttp;
|
|
629
|
+
if (property === "http" && provider.http && !supportedHostHttp)
|
|
630
|
+
return undefined;
|
|
631
|
+
if (property === "browser" && observedBrowser)
|
|
632
|
+
return observedBrowser;
|
|
633
|
+
return Reflect.get(target, property, receiver);
|
|
634
|
+
},
|
|
635
|
+
})
|
|
636
|
+
: provider.http && !supportedHostHttp
|
|
637
|
+
? new Proxy(attached, {
|
|
638
|
+
get(target, property, receiver) {
|
|
639
|
+
return property === "http" ? undefined : Reflect.get(target, property, receiver);
|
|
640
|
+
},
|
|
641
|
+
})
|
|
642
|
+
: attached;
|
|
643
|
+
const context = wrapWithInstrumentation(contextTarget);
|
|
644
|
+
retryResponseMeta.set(context, scope.httpTelemetry);
|
|
612
645
|
return context;
|
|
613
646
|
}
|
|
614
647
|
function createFlowContextStore(allowedKeys, initialContext = {}) {
|
|
@@ -678,11 +711,12 @@ function createAuthFlowContext(provider, request, route, options, state, scope,
|
|
|
678
711
|
throw new SDKError("An egress lease was supplied for an auth flow that does not use a ceremony lease", { code: "EGRESS_LEASE_INVALID" });
|
|
679
712
|
}
|
|
680
713
|
const resolverIdentityScope = resolveProviderResolverIdentityScope(provider, proxyClientOptions.affinityKey, request.requestId);
|
|
681
|
-
const cache = createProviderCache({ providerId: provider.id });
|
|
714
|
+
const cache = createProviderCache(bindCacheTelemetry({ providerId: provider.id }, scope.cacheTelemetry));
|
|
682
715
|
const { capabilityModules } = options;
|
|
683
|
-
const resolverOptions = createResolverRuntimeOptions(provider, cache, resolverIdentityScope, proxyPolicy, proxyClientOptions, stealthProfile, scope.resolverTelemetry);
|
|
716
|
+
const resolverOptions = createResolverRuntimeOptions(provider, cache, resolverIdentityScope, proxyPolicy, proxyClientOptions, stealthProfile, scope.resolverTelemetry, scope.browserTelemetry);
|
|
684
717
|
const challengeRuntime = createStealthChallengeDetection(provider, capabilityModules.resolver, options.resolver, resolverOptions, scope.trace, signal);
|
|
685
718
|
const stealthClientOptions = {
|
|
719
|
+
stealthTelemetry: scope.stealthTelemetry,
|
|
686
720
|
upstream: proxyClientOptions.upstream,
|
|
687
721
|
affinityKey: proxyClientOptions.affinityKey,
|
|
688
722
|
telemetry: scope.telemetry.proxy,
|
|
@@ -719,13 +753,25 @@ function createAuthFlowContext(provider, request, route, options, state, scope,
|
|
|
719
753
|
http: createHttpClient(baseUrl, {
|
|
720
754
|
...proxyClientOptions,
|
|
721
755
|
...(signal ? { signal } : {}),
|
|
756
|
+
httpTelemetry: scope.httpTelemetry,
|
|
722
757
|
}),
|
|
723
|
-
state: state.forConnection(resolveOperationConnectionId(request)),
|
|
758
|
+
state: instrumentProviderRuntimeState(state, scope.stateTelemetry, scope.redact).forConnection(resolveOperationConnectionId(request)),
|
|
724
759
|
stealth: stealthBaseUrl
|
|
725
760
|
? capabilityModules.stealth
|
|
726
761
|
? capabilityModules.stealth.createStealthClient(stealthBaseUrl, stealthClientOptions)
|
|
727
762
|
: createLazyStealthClient(logStealthCleanupError, stealthBaseUrl, stealthClientOptions)
|
|
728
763
|
: createStealthStub(),
|
|
764
|
+
browser: provider.runtime === "browser"
|
|
765
|
+
? capabilityModules.browser.createBrowserClient({
|
|
766
|
+
allowedHosts: provider.allowedHosts,
|
|
767
|
+
cdpUrl: readDiagnosticEnv("APIFUSE__CDP_POOL__URL"),
|
|
768
|
+
headless: true,
|
|
769
|
+
requireCdpPool: isProductionProviderBrowserMode(provider),
|
|
770
|
+
stealth: true,
|
|
771
|
+
engine: provider.browser?.engine,
|
|
772
|
+
telemetry: scope.browserTelemetry,
|
|
773
|
+
})
|
|
774
|
+
: createBrowserStub(),
|
|
729
775
|
...(provider.native
|
|
730
776
|
? {
|
|
731
777
|
native: {
|
|
@@ -746,15 +792,15 @@ function createAuthFlowContext(provider, request, route, options, state, scope,
|
|
|
746
792
|
]),
|
|
747
793
|
credential,
|
|
748
794
|
context: flowContextStore.context,
|
|
749
|
-
ocr: options.ocr ?? createOcrClientFromEnv(provider.ocr),
|
|
750
|
-
stt: options.stt ?? createSttClientFromEnv(provider.stt),
|
|
795
|
+
ocr: bindCapabilityRoot(bindOcrTelemetry(options.ocr ?? createOcrClientFromEnv(provider.ocr), scope.ocrTelemetry), scope.run),
|
|
796
|
+
stt: bindCapabilityRoot(bindSttTelemetry(options.stt ?? createSttClientFromEnv(provider.stt), scope.sttTelemetry), scope.run),
|
|
751
797
|
resolver: capabilityModules.resolver
|
|
752
798
|
? capabilityModules.resolver.bindResolverSignal((options.resolver
|
|
753
|
-
? bindResolverTelemetry(options.resolver, resolverOptions.telemetry)
|
|
799
|
+
? bindResolverTelemetry(options.resolver, resolverOptions.telemetry, resolverOptions.browserTelemetry)
|
|
754
800
|
: undefined) ??
|
|
755
801
|
capabilityModules.resolver.createResolverClientFromEnv(provider.resolver, undefined, resolverOptions), signal)
|
|
756
802
|
: bindResolverSignalWithoutRuntime((options.resolver
|
|
757
|
-
? bindResolverTelemetry(options.resolver, resolverOptions.telemetry)
|
|
803
|
+
? bindResolverTelemetry(options.resolver, resolverOptions.telemetry, resolverOptions.browserTelemetry)
|
|
758
804
|
: undefined) ??
|
|
759
805
|
createUnsupportedResolverClient("Provider does not declare resolver capability"), signal),
|
|
760
806
|
auth: createAuthFlowHelpers({ signal }),
|
|
@@ -818,7 +864,76 @@ function publicErrorSource(error, category) {
|
|
|
818
864
|
}
|
|
819
865
|
return sourceForCategory(category);
|
|
820
866
|
}
|
|
821
|
-
function
|
|
867
|
+
function localeKeyOption(error, key) {
|
|
868
|
+
const value = providerErrorOption(error, key);
|
|
869
|
+
return typeof value === "string" ? value : undefined;
|
|
870
|
+
}
|
|
871
|
+
function declaredLocaleKey(declaration, key) {
|
|
872
|
+
const value = declaration?.[key];
|
|
873
|
+
return typeof value === "string" ? value : undefined;
|
|
874
|
+
}
|
|
875
|
+
function providerErrorParams(error) {
|
|
876
|
+
const params = providerErrorOption(error, "params");
|
|
877
|
+
return params !== null && typeof params === "object" && !Array.isArray(params)
|
|
878
|
+
? params
|
|
879
|
+
: undefined;
|
|
880
|
+
}
|
|
881
|
+
/**
|
|
882
|
+
* Resolution order for one localized envelope field:
|
|
883
|
+
*
|
|
884
|
+
* 1. the throw site's `messageKey`/`fixKey`;
|
|
885
|
+
* 2. the declared `errorCodes[]` entry's key for the thrown code;
|
|
886
|
+
* 3. the derived `errors.<code>.<field>` convention;
|
|
887
|
+
* 4. the English literal the provider threw.
|
|
888
|
+
*
|
|
889
|
+
* Steps 2 and 3 are skipped for SDK-owned failures (transport, Zod, stateful
|
|
890
|
+
* deadline, SDK runtime codes): their text is the SDK's contract, and a
|
|
891
|
+
* provider catalog entry must not be able to relabel one. An explicit key on
|
|
892
|
+
* the thrown error still wins, because that is the provider deliberately
|
|
893
|
+
* describing an error it constructed itself.
|
|
894
|
+
*/
|
|
895
|
+
function localizedErrorField(error, field, literal, localization) {
|
|
896
|
+
if (!localization?.catalogs)
|
|
897
|
+
return literal;
|
|
898
|
+
const optionKey = field === "message" ? "messageKey" : "fixKey";
|
|
899
|
+
const candidates = [
|
|
900
|
+
{ kind: "key", key: localeKeyOption(error, optionKey) },
|
|
901
|
+
];
|
|
902
|
+
if (!sdkOwnsErrorResolution(error)) {
|
|
903
|
+
candidates.push({ kind: "key", key: declaredLocaleKey(localization.declaredErrorCode, optionKey) }, { kind: "derived", code: providerErrorCode(error), field });
|
|
904
|
+
}
|
|
905
|
+
return localizeProviderErrorText({
|
|
906
|
+
catalogs: localization.catalogs,
|
|
907
|
+
locale: localization.locale,
|
|
908
|
+
candidates,
|
|
909
|
+
params: providerErrorParams(error),
|
|
910
|
+
fallback: literal,
|
|
911
|
+
});
|
|
912
|
+
}
|
|
913
|
+
/**
|
|
914
|
+
* Reads `accept-language` out of the request envelope's `headers` map.
|
|
915
|
+
*
|
|
916
|
+
* The gateway stamps the negotiated locale there for auth flows (it persists
|
|
917
|
+
* the start locale on the flow state so continue/poll turns stay in one
|
|
918
|
+
* language), and the operation route merges HTTP headers into the same map, so
|
|
919
|
+
* the envelope is the higher-precedence source.
|
|
920
|
+
*/
|
|
921
|
+
function requestBodyAcceptLanguage(rawBody) {
|
|
922
|
+
if (!rawBody || typeof rawBody !== "object")
|
|
923
|
+
return undefined;
|
|
924
|
+
const headers = Object.getOwnPropertyDescriptor(rawBody, "headers")?.value;
|
|
925
|
+
if (!headers || typeof headers !== "object" || Array.isArray(headers))
|
|
926
|
+
return undefined;
|
|
927
|
+
return acceptLanguageHeaderValue(headers);
|
|
928
|
+
}
|
|
929
|
+
function errorEnvelopeLocalization(options) {
|
|
930
|
+
return {
|
|
931
|
+
...(options.catalogs ? { catalogs: options.catalogs } : {}),
|
|
932
|
+
locale: resolveProviderErrorLocale(requestBodyAcceptLanguage(options.rawBody), options.requestHeaders?.get("accept-language")),
|
|
933
|
+
...(options.declaredErrorCode ? { declaredErrorCode: options.declaredErrorCode } : {}),
|
|
934
|
+
};
|
|
935
|
+
}
|
|
936
|
+
function toErrorResponse(error, requestId, observabilityDetails, redact, localization) {
|
|
822
937
|
const observability = observabilityDetails;
|
|
823
938
|
const source = publicErrorSource(error, observability.category);
|
|
824
939
|
if (error instanceof StatefulRoutingDeadlineError) {
|
|
@@ -834,14 +949,17 @@ function toErrorResponse(error, requestId, observabilityDetails, redact) {
|
|
|
834
949
|
}
|
|
835
950
|
if (isProviderError(error)) {
|
|
836
951
|
const details = providerErrorOption(error, "details");
|
|
952
|
+
const literalFix = providerErrorOption(error, "fix");
|
|
953
|
+
const fix = localizedErrorField(error, "fix", typeof literalFix === "string" && literalFix.length > 0 ? literalFix : undefined, localization);
|
|
954
|
+
const literalMessage = publicProviderErrorMessage(error);
|
|
837
955
|
return {
|
|
838
956
|
error: {
|
|
839
957
|
code: providerErrorCode(error) ?? "provider_error",
|
|
840
|
-
message:
|
|
958
|
+
message: localizedErrorField(error, "message", literalMessage, localization) ?? literalMessage,
|
|
841
959
|
...(requestId ? { requestId } : {}),
|
|
842
960
|
retryable: observability.retryable,
|
|
843
961
|
source,
|
|
844
|
-
...(
|
|
962
|
+
...(fix ? { fix } : {}),
|
|
845
963
|
...(details !== undefined ? { details } : {}),
|
|
846
964
|
},
|
|
847
965
|
};
|
|
@@ -1087,6 +1205,11 @@ function publicProviderErrorMessage(error) {
|
|
|
1087
1205
|
return "Request timed out";
|
|
1088
1206
|
if (providerErrorCode(error) === "transport_network_error")
|
|
1089
1207
|
return "Network error";
|
|
1208
|
+
// Provider-local fault: no request was issued, so an upstream-flavored
|
|
1209
|
+
// message would misattribute it (category is provider_error).
|
|
1210
|
+
if (providerErrorCode(error) === "http_header_factory_failed") {
|
|
1211
|
+
return "Request header preparation failed";
|
|
1212
|
+
}
|
|
1090
1213
|
if (providerErrorCode(error) === "upstream_http_error" && error.status) {
|
|
1091
1214
|
return `Upstream request failed with status ${error.status}`;
|
|
1092
1215
|
}
|
|
@@ -1210,6 +1333,11 @@ function logProviderError(logger, provider, kind, route, requestId, error, statu
|
|
|
1210
1333
|
const errorClass = error instanceof Error ? error.name : typeof error;
|
|
1211
1334
|
const message = error instanceof Error ? error.message : String(error);
|
|
1212
1335
|
const causeChain = providerErrorCauseChain(error, redact);
|
|
1336
|
+
// Masked 500 bodies carry only errorClass, so the log is the sole channel
|
|
1337
|
+
// that can locate the throw. Declared 5xx (502/504) are included too: their
|
|
1338
|
+
// body carries code+message, but frame[0] is still the throw site inside
|
|
1339
|
+
// provider code. 4xx failures keep the lean record.
|
|
1340
|
+
const stack = status >= 500 ? providerErrorStackFrames(error, redact) : undefined;
|
|
1213
1341
|
const details = observabilityDetails;
|
|
1214
1342
|
const isUnregisteredProviderErrorCode = status === 500 &&
|
|
1215
1343
|
isProviderError(error) &&
|
|
@@ -1251,6 +1379,7 @@ function logProviderError(logger, provider, kind, route, requestId, error, statu
|
|
|
1251
1379
|
errorClass: redactDiagnosticText(errorClass, redact),
|
|
1252
1380
|
message: sanitizeDiagnosticText(redactDiagnosticText(message, redact)),
|
|
1253
1381
|
...(causeChain ? { causeChain } : {}),
|
|
1382
|
+
...(stack ? { stack } : {}),
|
|
1254
1383
|
...(providerObservability ? { providerObservability } : {}),
|
|
1255
1384
|
...(details.upstreamStatus ? { upstreamStatus: details.upstreamStatus } : {}),
|
|
1256
1385
|
errorCategory: details.category,
|
|
@@ -1452,6 +1581,7 @@ function createRequestScope(input) {
|
|
|
1452
1581
|
observe: createDiagnosticEnvObserver(input.provider, sensitiveRegistry.add, sensitiveRegistry.has),
|
|
1453
1582
|
finished: false,
|
|
1454
1583
|
staticValues: input.staticSensitiveValues,
|
|
1584
|
+
register: sensitiveRegistry.add,
|
|
1455
1585
|
};
|
|
1456
1586
|
const traceConfig = withDiagnosticEnv(observeEnv, () => resolveTraceConfigFromEnv());
|
|
1457
1587
|
const details = {
|
|
@@ -1481,10 +1611,24 @@ function createRequestScope(input) {
|
|
|
1481
1611
|
const proxyCollector = new ProxyTelemetryCollector();
|
|
1482
1612
|
const resolverCollector = new ResolverTelemetryCollector({ redact });
|
|
1483
1613
|
const nativeCollector = new NativeTelemetryCollector({ redact });
|
|
1614
|
+
const httpCollector = new HttpTelemetryCollector({ redact });
|
|
1615
|
+
const stealthCollector = new StealthTelemetryCollector({ redact });
|
|
1616
|
+
const ocrCollector = new OcrTelemetryCollector({ redact });
|
|
1617
|
+
const sttCollector = new SttTelemetryCollector({ redact });
|
|
1618
|
+
const browserCollector = new BrowserTelemetryCollector({ redact });
|
|
1619
|
+
const cacheTelemetry = new CacheTelemetryCollector({ redact });
|
|
1620
|
+
const stateTelemetry = new StateTelemetryCollector({ redact });
|
|
1484
1621
|
const telemetry = new RequestTelemetry(trace);
|
|
1485
1622
|
telemetry.register(proxyCollector);
|
|
1486
1623
|
telemetry.register(resolverCollector);
|
|
1487
1624
|
telemetry.register(nativeCollector);
|
|
1625
|
+
telemetry.register(httpCollector);
|
|
1626
|
+
telemetry.register(stealthCollector);
|
|
1627
|
+
telemetry.register(browserCollector);
|
|
1628
|
+
telemetry.register(ocrCollector);
|
|
1629
|
+
telemetry.register(sttCollector);
|
|
1630
|
+
telemetry.register(cacheTelemetry);
|
|
1631
|
+
telemetry.register(stateTelemetry);
|
|
1488
1632
|
let rootRunner = (fn) => fn();
|
|
1489
1633
|
let resolveRoot;
|
|
1490
1634
|
const rootTerminal = new Promise((resolve) => {
|
|
@@ -1562,6 +1706,13 @@ function createRequestScope(input) {
|
|
|
1562
1706
|
telemetry,
|
|
1563
1707
|
resolverTelemetry: resolverCollector,
|
|
1564
1708
|
nativeTelemetry: nativeCollector,
|
|
1709
|
+
httpTelemetry: httpCollector,
|
|
1710
|
+
stealthTelemetry: stealthCollector,
|
|
1711
|
+
ocrTelemetry: ocrCollector,
|
|
1712
|
+
sttTelemetry: sttCollector,
|
|
1713
|
+
browserTelemetry: browserCollector,
|
|
1714
|
+
cacheTelemetry,
|
|
1715
|
+
stateTelemetry,
|
|
1565
1716
|
seedCredentials(rawBody, kind) {
|
|
1566
1717
|
const harvested = rawRequestCredentials(rawBody, kind);
|
|
1567
1718
|
sensitiveRegistry.add(harvested.values);
|
|
@@ -1743,7 +1894,7 @@ function toJsonSuccessResponse(result, ctx) {
|
|
|
1743
1894
|
return new Response(result);
|
|
1744
1895
|
}
|
|
1745
1896
|
const cacheMeta = ctx && "cache" in ctx ? ctx.cache.responseMeta() : undefined;
|
|
1746
|
-
const
|
|
1897
|
+
const retry = ctx ? retryResponseMeta.get(ctx)?.toTenantRetryPayload() : undefined;
|
|
1747
1898
|
const cache = cacheMeta
|
|
1748
1899
|
? {
|
|
1749
1900
|
hit: cacheMeta.hit,
|
|
@@ -1753,21 +1904,7 @@ function toJsonSuccessResponse(result, ctx) {
|
|
|
1753
1904
|
...(cacheMeta.source ? { source: closedEnum(cacheMeta.source) } : {}),
|
|
1754
1905
|
}
|
|
1755
1906
|
: undefined;
|
|
1756
|
-
const
|
|
1757
|
-
? {
|
|
1758
|
-
attempts: retryMeta.attempts,
|
|
1759
|
-
retries: retryMeta.retries,
|
|
1760
|
-
...(retryMeta.preset ? { preset: closedEnum(retryMeta.preset) } : {}),
|
|
1761
|
-
transport: closedEnum(retryMeta.transport),
|
|
1762
|
-
...(retryMeta.lastErrorCode
|
|
1763
|
-
? {
|
|
1764
|
-
lastErrorCode: closedEnum(tenantRetryLastErrorCode(retryMeta.lastErrorCode)),
|
|
1765
|
-
}
|
|
1766
|
-
: {}),
|
|
1767
|
-
...(retryMeta.lastStatus ? { lastStatus: retryMeta.lastStatus } : {}),
|
|
1768
|
-
}
|
|
1769
|
-
: undefined;
|
|
1770
|
-
const meta = cacheMeta || retryMeta
|
|
1907
|
+
const meta = cacheMeta || retry
|
|
1771
1908
|
? {
|
|
1772
1909
|
...(cache
|
|
1773
1910
|
? {
|
|
@@ -2016,50 +2153,49 @@ function toAuthFlowResponse(result, contextPatch, engine) {
|
|
|
2016
2153
|
...(engine ? { engine } : {}),
|
|
2017
2154
|
};
|
|
2018
2155
|
}
|
|
2019
|
-
function authFlowLocaleFromHeaders(headers) {
|
|
2020
|
-
const header = Object.entries(headers ?? {}).find(([key]) => key.toLowerCase() === "accept-language")?.[1];
|
|
2021
|
-
for (const token of (header ?? "").split(",")) {
|
|
2022
|
-
const language = token.trim().split(";")[0]?.split("-")[0]?.toLowerCase();
|
|
2023
|
-
if (isAuthFlowLocale(language)) {
|
|
2024
|
-
return language;
|
|
2025
|
-
}
|
|
2026
|
-
}
|
|
2027
|
-
return "en";
|
|
2028
|
-
}
|
|
2029
|
-
function isAuthFlowLocale(value) {
|
|
2030
|
-
return value === "en" || value === "ko" || value === "ja";
|
|
2031
|
-
}
|
|
2032
2156
|
function isAuthTurn(value) {
|
|
2033
2157
|
return !!value && typeof value === "object" && "kind" in value && "turnId" in value;
|
|
2034
2158
|
}
|
|
2035
|
-
|
|
2159
|
+
/**
|
|
2160
|
+
* Loads the provider's locale catalogs once, at `createServerApp` time.
|
|
2161
|
+
*
|
|
2162
|
+
* Only the locales that actually exist on disk are read: a provider with
|
|
2163
|
+
* `en`+`ko` but no `ja.json` used to make the whole load throw and silently
|
|
2164
|
+
* disable localization for every turn. Absence of catalogs is not an error —
|
|
2165
|
+
* every localized field falls back to its English literal — but a catalog that
|
|
2166
|
+
* exists and cannot be read is logged once so a packaging mistake is visible at
|
|
2167
|
+
* boot instead of per request.
|
|
2168
|
+
*/
|
|
2169
|
+
function resolveProviderLocaleCatalogs(provider, options, logger) {
|
|
2170
|
+
if (options.localeCatalogs)
|
|
2171
|
+
return options.localeCatalogs;
|
|
2036
2172
|
for (const providerDir of [
|
|
2037
2173
|
process.cwd(),
|
|
2038
2174
|
join(process.cwd(), "providers", provider.id),
|
|
2039
2175
|
join(process.cwd(), "providers-staging", provider.id),
|
|
2040
2176
|
]) {
|
|
2041
|
-
|
|
2177
|
+
const locales = PROVIDER_ERROR_LOCALES.filter((locale) => existsSync(join(providerDir, "locales", `${locale}.json`)));
|
|
2178
|
+
if (!locales.includes(DEFAULT_PROVIDER_ERROR_LOCALE))
|
|
2042
2179
|
continue;
|
|
2043
2180
|
try {
|
|
2044
|
-
return loadProviderLocaleCatalogs({
|
|
2045
|
-
providerDir,
|
|
2046
|
-
locales: AUTH_FLOW_LOCALES,
|
|
2047
|
-
});
|
|
2181
|
+
return loadProviderLocaleCatalogs({ providerDir, locales });
|
|
2048
2182
|
}
|
|
2049
|
-
catch {
|
|
2183
|
+
catch (error) {
|
|
2184
|
+
logger({
|
|
2185
|
+
level: "warn",
|
|
2186
|
+
event: "provider_locale_catalogs_unavailable",
|
|
2187
|
+
providerId: provider.id,
|
|
2188
|
+
reason: sanitizeDiagnosticText(error instanceof Error ? error.message : String(error)),
|
|
2189
|
+
});
|
|
2050
2190
|
return undefined;
|
|
2051
2191
|
}
|
|
2052
2192
|
}
|
|
2053
2193
|
return undefined;
|
|
2054
2194
|
}
|
|
2055
|
-
function materializeAuthFlowTurn(
|
|
2056
|
-
const catalogs = loadAuthFlowLocaleCatalogs(provider);
|
|
2195
|
+
function materializeAuthFlowTurn(turn, catalogs, locale) {
|
|
2057
2196
|
if (!catalogs)
|
|
2058
2197
|
return turn;
|
|
2059
|
-
return localizeAuthTurn(turn, {
|
|
2060
|
-
catalogs,
|
|
2061
|
-
locale: authFlowLocaleFromHeaders(request.headers),
|
|
2062
|
-
});
|
|
2198
|
+
return localizeAuthTurn(turn, { catalogs, locale });
|
|
2063
2199
|
}
|
|
2064
2200
|
function withAuthRequestHeaders(request, headers) {
|
|
2065
2201
|
return {
|
|
@@ -2138,7 +2274,15 @@ async function handleOperation(provider, request, operationId, options, state =
|
|
|
2138
2274
|
await cleanup();
|
|
2139
2275
|
}
|
|
2140
2276
|
}
|
|
2141
|
-
async function handleAuthFlow(provider, request, route, options, state, scope,
|
|
2277
|
+
async function handleAuthFlow(provider, request, route, options, state, scope,
|
|
2278
|
+
/**
|
|
2279
|
+
* Negotiated once per request by the route so a successful turn and an error
|
|
2280
|
+
* envelope from the same request can never disagree. `withAuthRequestHeaders`
|
|
2281
|
+
* lets the HTTP header shadow the envelope's `Accept-Language`, but the
|
|
2282
|
+
* gateway stamps the flow's start locale into the envelope precisely so
|
|
2283
|
+
* continue/poll turns stay in one language, so the envelope wins here.
|
|
2284
|
+
*/
|
|
2285
|
+
locale, signal) {
|
|
2142
2286
|
const flow = provider.auth?.flow;
|
|
2143
2287
|
if (!flow) {
|
|
2144
2288
|
throw new ProviderError("Auth flow is not configured", {
|
|
@@ -2180,7 +2324,7 @@ async function handleAuthFlow(provider, request, route, options, state, scope, s
|
|
|
2180
2324
|
!(result instanceof Response) &&
|
|
2181
2325
|
!(result instanceof ReadableStream) &&
|
|
2182
2326
|
isAuthTurn(result)
|
|
2183
|
-
? materializeAuthFlowTurn(
|
|
2327
|
+
? materializeAuthFlowTurn(result, options.localeCatalogs, locale)
|
|
2184
2328
|
: result;
|
|
2185
2329
|
return toAuthFlowResponse(materializedResult, getPatch(), getEngineState());
|
|
2186
2330
|
}
|
|
@@ -2302,7 +2446,7 @@ function operationRequestFromForwardingEnvelope(envelope) {
|
|
|
2302
2446
|
};
|
|
2303
2447
|
}
|
|
2304
2448
|
function parseStatefulForwardingEnvelope(rawBody, redact) {
|
|
2305
|
-
const parsed =
|
|
2449
|
+
const parsed = ReceivedStatefulForwardEnvelopeSchema.safeParse(rawBody);
|
|
2306
2450
|
if (parsed.success)
|
|
2307
2451
|
return parsed.data;
|
|
2308
2452
|
throw new ProviderError("Stateful forwarding envelope is invalid.", {
|
|
@@ -2334,12 +2478,59 @@ export function createServerApp(provider, options = {}) {
|
|
|
2334
2478
|
validateStatefulServerConfig(runtimeOptions);
|
|
2335
2479
|
return createServerAppWithCapabilityModules(runtimeProvider, runtimeOptions, loadProviderCapabilityModulesSync(runtimeProvider));
|
|
2336
2480
|
}
|
|
2481
|
+
/**
|
|
2482
|
+
* Resolve the engine once at boot, before any route exists. An explicit engine
|
|
2483
|
+
* object is used as given; otherwise the resolved mode selects the engine.
|
|
2484
|
+
*
|
|
2485
|
+
* Deliberately never throws for deployment-authored input: a mode this release
|
|
2486
|
+
* cannot serve yields an engine that fails every request and a `/readyz` 503,
|
|
2487
|
+
* because a boot crash would trade a structured signal for CrashLoopBackOff
|
|
2488
|
+
* across the fleet (same trade-off as `provider_secrets_missing` above).
|
|
2489
|
+
*/
|
|
2490
|
+
function resolveServerEngine(serverOptions) {
|
|
2491
|
+
const resolution = resolveProviderEngineMode({
|
|
2492
|
+
option: serverOptions.engineMode,
|
|
2493
|
+
engine: serverOptions.engine,
|
|
2494
|
+
});
|
|
2495
|
+
if (serverOptions.engine !== undefined)
|
|
2496
|
+
return { engine: serverOptions.engine, resolution };
|
|
2497
|
+
return { engine: createEngineForMode(resolution.mode), resolution };
|
|
2498
|
+
}
|
|
2499
|
+
/**
|
|
2500
|
+
* `engine_env_ignored` is expected for the whole migration window: the manifest
|
|
2501
|
+
* generator projects the remote engine env fleet-wide before any mode flips, so
|
|
2502
|
+
* escalating on it would turn every healthy boot into a warn line. The
|
|
2503
|
+
* disagreement warnings still escalate.
|
|
2504
|
+
*/
|
|
2505
|
+
const ENGINE_MODE_INFO_WARNINGS = new Set(["engine_env_ignored"]);
|
|
2506
|
+
function engineModeLogEvent(provider, resolution, attached) {
|
|
2507
|
+
const escalating = resolution.warnings.filter((warning) => !ENGINE_MODE_INFO_WARNINGS.has(warning));
|
|
2508
|
+
return {
|
|
2509
|
+
level: attached && escalating.length === 0 ? "info" : "warn",
|
|
2510
|
+
event: "provider_engine_mode",
|
|
2511
|
+
providerId: provider.id,
|
|
2512
|
+
mode: resolution.mode,
|
|
2513
|
+
source: resolution.source,
|
|
2514
|
+
deprecated: resolution.deprecated,
|
|
2515
|
+
attached,
|
|
2516
|
+
sdkVersion: SDK_VERSION,
|
|
2517
|
+
...(provider.runtimeTarget === undefined ? {} : { runtimeTarget: provider.runtimeTarget }),
|
|
2518
|
+
...(resolution.warnings.length === 0 ? {} : { warnings: resolution.warnings }),
|
|
2519
|
+
};
|
|
2520
|
+
}
|
|
2337
2521
|
function createServerAppWithCapabilityModules(provider, serverOptions, capabilityModules) {
|
|
2522
|
+
const engineSelection = resolveServerEngine(serverOptions);
|
|
2523
|
+
// Read the provider's locale catalogs once here rather than per turn: the
|
|
2524
|
+
// previous auth-flow loader re-read all three JSON files on every turn and
|
|
2525
|
+
// threw the whole map away when one locale file was absent.
|
|
2526
|
+
const localeCatalogs = resolveProviderLocaleCatalogs(provider, serverOptions, serverOptions.logger ?? defaultProviderServerLogger);
|
|
2338
2527
|
const options = {
|
|
2339
2528
|
...serverOptions,
|
|
2529
|
+
...(localeCatalogs ? { localeCatalogs } : {}),
|
|
2340
2530
|
capabilityModules,
|
|
2341
|
-
engine:
|
|
2531
|
+
engine: engineSelection.engine,
|
|
2342
2532
|
};
|
|
2533
|
+
const engineAttached = !isUnavailableProviderEngine(options.engine);
|
|
2343
2534
|
const app = new Hono();
|
|
2344
2535
|
// Compile the startup inventory once, composed with the shared outside-context fallback.
|
|
2345
2536
|
const staticSensitiveValues = compileProcessDiagnosticSensitiveValues(collectStaticDiagnosticSensitiveValues(provider, {
|
|
@@ -2371,13 +2562,33 @@ function createServerAppWithCapabilityModules(provider, serverOptions, capabilit
|
|
|
2371
2562
|
app.notFound((c) => {
|
|
2372
2563
|
const error = new ProviderError("Not found", { code: "not_found", retryable: false });
|
|
2373
2564
|
const observabilityDetails = errorObservabilityDetails(error);
|
|
2374
|
-
return responseWithErrorObservability(c.json(toErrorResponse(error, undefined, observabilityDetails
|
|
2565
|
+
return responseWithErrorObservability(c.json(toErrorResponse(error, undefined, observabilityDetails, undefined, errorEnvelopeLocalization({
|
|
2566
|
+
catalogs: localeCatalogs,
|
|
2567
|
+
requestHeaders: c.req.raw.headers,
|
|
2568
|
+
})), 404), observabilityDetails);
|
|
2375
2569
|
});
|
|
2570
|
+
// Liveness. Deliberately static and engine-blind: generated manifests point
|
|
2571
|
+
// liveness at this route, so making it depend on engine reachability would
|
|
2572
|
+
// restart every provider pod on one engine incident. Engine state belongs to
|
|
2573
|
+
// /readyz, which manifests wire to readiness and startup only.
|
|
2376
2574
|
app.get("/health", (c) => c.json({
|
|
2377
2575
|
status: "ok",
|
|
2378
2576
|
provider: provider.id,
|
|
2379
2577
|
version: provider.version,
|
|
2380
2578
|
}));
|
|
2579
|
+
// Readiness. Reports the boot-resolved attachment; does not probe the engine
|
|
2580
|
+
// per call, so it cannot amplify an engine incident into request load.
|
|
2581
|
+
app.get("/readyz", (c) => c.json({
|
|
2582
|
+
status: engineAttached ? "ok" : "unavailable",
|
|
2583
|
+
provider: provider.id,
|
|
2584
|
+
version: provider.version,
|
|
2585
|
+
engine: {
|
|
2586
|
+
// The resolution, not the raw engine `kind`: normalized, so this
|
|
2587
|
+
// body and the `provider_engine_mode` boot event never disagree.
|
|
2588
|
+
mode: engineSelection.resolution.mode,
|
|
2589
|
+
attached: engineAttached,
|
|
2590
|
+
},
|
|
2591
|
+
}, engineAttached ? 200 : 503));
|
|
2381
2592
|
app.post(STATEFUL_INTERNAL_OPERATIONS_ROUTE, async (c) => {
|
|
2382
2593
|
let rawBodyText = "";
|
|
2383
2594
|
let rawBody;
|
|
@@ -2483,7 +2694,10 @@ function createServerAppWithCapabilityModules(provider, serverOptions, capabilit
|
|
|
2483
2694
|
requestId: request.requestId,
|
|
2484
2695
|
operationId,
|
|
2485
2696
|
headers: request.headers ?? {},
|
|
2486
|
-
correlation: {
|
|
2697
|
+
correlation: {
|
|
2698
|
+
connectionId: envelope.connectionId,
|
|
2699
|
+
tenantId: resolveOperationTenantId(request),
|
|
2700
|
+
},
|
|
2487
2701
|
});
|
|
2488
2702
|
const response = await requestScope.run(async () => {
|
|
2489
2703
|
const ctx = createProviderContext(provider, request, operationId, options, state, requestScope, signal);
|
|
@@ -2518,7 +2732,12 @@ function createServerAppWithCapabilityModules(provider, serverOptions, capabilit
|
|
|
2518
2732
|
...(requestId ? { requestId } : {}),
|
|
2519
2733
|
});
|
|
2520
2734
|
const observabilityDetails = errorObservabilityDetails(error, declaredErrorCode);
|
|
2521
|
-
const response = c.json(toErrorResponse(error, requestId, observabilityDetails, requestScope.redact
|
|
2735
|
+
const response = c.json(toErrorResponse(error, requestId, observabilityDetails, requestScope.redact, errorEnvelopeLocalization({
|
|
2736
|
+
catalogs: localeCatalogs,
|
|
2737
|
+
requestHeaders: c.req.raw.headers,
|
|
2738
|
+
rawBody,
|
|
2739
|
+
...(declaredErrorCode ? { declaredErrorCode } : {}),
|
|
2740
|
+
})), status);
|
|
2522
2741
|
return finalizeRequestResponse(requestScope, response, {
|
|
2523
2742
|
kind: "failed",
|
|
2524
2743
|
status,
|
|
@@ -2552,7 +2771,10 @@ function createServerAppWithCapabilityModules(provider, serverOptions, capabilit
|
|
|
2552
2771
|
requestScope.enrich({
|
|
2553
2772
|
requestId: body.requestId,
|
|
2554
2773
|
headers: body.headers,
|
|
2555
|
-
correlation: {
|
|
2774
|
+
correlation: {
|
|
2775
|
+
connectionId: resolveOperationConnectionId(body),
|
|
2776
|
+
tenantId: resolveOperationTenantId(body),
|
|
2777
|
+
},
|
|
2556
2778
|
});
|
|
2557
2779
|
const streaming = provider.operations[operation]?.transport?.kind
|
|
2558
2780
|
? provider.operations[operation]?.transport?.kind !== "json"
|
|
@@ -2582,7 +2804,12 @@ function createServerAppWithCapabilityModules(provider, serverOptions, capabilit
|
|
|
2582
2804
|
const requestId = extractRequestId(rawBody);
|
|
2583
2805
|
requestScope.enrich({ ...(requestId ? { requestId } : {}) });
|
|
2584
2806
|
const observabilityDetails = errorObservabilityDetails(error, declaredErrorCode);
|
|
2585
|
-
const response = c.json(toErrorResponse(error, requestId, observabilityDetails, requestScope.redact
|
|
2807
|
+
const response = c.json(toErrorResponse(error, requestId, observabilityDetails, requestScope.redact, errorEnvelopeLocalization({
|
|
2808
|
+
catalogs: localeCatalogs,
|
|
2809
|
+
requestHeaders: c.req.raw.headers,
|
|
2810
|
+
rawBody,
|
|
2811
|
+
...(declaredErrorCode ? { declaredErrorCode } : {}),
|
|
2812
|
+
})), status);
|
|
2586
2813
|
return finalizeRequestResponse(requestScope, response, {
|
|
2587
2814
|
kind: "failed",
|
|
2588
2815
|
status,
|
|
@@ -2600,6 +2827,12 @@ function createServerAppWithCapabilityModules(provider, serverOptions, capabilit
|
|
|
2600
2827
|
for (const { path, flowRoute, logRoute } of authRoutes) {
|
|
2601
2828
|
app.post(path, async (c) => {
|
|
2602
2829
|
let rawBody;
|
|
2830
|
+
// Negotiated once and shared by the success and failure paths so an
|
|
2831
|
+
// auth turn and an error envelope from the same request agree.
|
|
2832
|
+
let localization = errorEnvelopeLocalization({
|
|
2833
|
+
catalogs: localeCatalogs,
|
|
2834
|
+
requestHeaders: c.req.raw.headers,
|
|
2835
|
+
});
|
|
2603
2836
|
const requestScope = createRequestScope({
|
|
2604
2837
|
provider,
|
|
2605
2838
|
staticSensitiveValues,
|
|
@@ -2614,6 +2847,11 @@ function createServerAppWithCapabilityModules(provider, serverOptions, capabilit
|
|
|
2614
2847
|
.clone()
|
|
2615
2848
|
.json()
|
|
2616
2849
|
.catch(() => undefined);
|
|
2850
|
+
localization = errorEnvelopeLocalization({
|
|
2851
|
+
catalogs: localeCatalogs,
|
|
2852
|
+
requestHeaders: c.req.raw.headers,
|
|
2853
|
+
rawBody,
|
|
2854
|
+
});
|
|
2617
2855
|
requestScope.seedCredentials(rawBody, "auth");
|
|
2618
2856
|
const body = withAuthRequestHeaders(AuthFlowRequestSchema.parse(rawBody), c.req.raw.headers);
|
|
2619
2857
|
requestScope.enrich({
|
|
@@ -2623,14 +2861,14 @@ function createServerAppWithCapabilityModules(provider, serverOptions, capabilit
|
|
|
2623
2861
|
correlation: {
|
|
2624
2862
|
connectionId: resolveOperationConnectionId(body),
|
|
2625
2863
|
flowId: body.flowId,
|
|
2626
|
-
tenantId: body.tenantId,
|
|
2864
|
+
tenantId: normalizeIdentifier(body.tenantId),
|
|
2627
2865
|
requestedProviderId: body.providerId !== undefined && body.providerId !== provider.id
|
|
2628
2866
|
? body.providerId
|
|
2629
2867
|
: undefined,
|
|
2630
2868
|
},
|
|
2631
2869
|
});
|
|
2632
2870
|
const response = await requestScope.run(async () => {
|
|
2633
|
-
const handled = await handleAuthFlow(provider, body, flowRoute, options, state, requestScope, c.req.raw.signal);
|
|
2871
|
+
const handled = await handleAuthFlow(provider, body, flowRoute, options, state, requestScope, localization.locale, c.req.raw.signal);
|
|
2634
2872
|
return handled instanceof Response ? handled : c.json(handled);
|
|
2635
2873
|
});
|
|
2636
2874
|
return finalizeRequestResponse(requestScope, response, {
|
|
@@ -2643,7 +2881,7 @@ function createServerAppWithCapabilityModules(provider, serverOptions, capabilit
|
|
|
2643
2881
|
const requestId = extractRequestId(rawBody);
|
|
2644
2882
|
requestScope.enrich({ ...(requestId ? { requestId } : {}) });
|
|
2645
2883
|
const observabilityDetails = errorObservabilityDetails(error);
|
|
2646
|
-
const response = c.json(toErrorResponse(error, requestId, observabilityDetails, requestScope.redact), status);
|
|
2884
|
+
const response = c.json(toErrorResponse(error, requestId, observabilityDetails, requestScope.redact, localization), status);
|
|
2647
2885
|
return finalizeRequestResponse(requestScope, response, {
|
|
2648
2886
|
kind: "failed",
|
|
2649
2887
|
status,
|
|
@@ -2705,13 +2943,18 @@ export async function serve(provider, options = {}) {
|
|
|
2705
2943
|
const configuredTimeoutMs = shutdownTimeout(options.shutdown?.timeoutMs ?? DEFAULT_SHUTDOWN_TIMEOUT_MS);
|
|
2706
2944
|
const configuredSignals = resolveShutdownSignals(options.shutdown?.signals ?? true);
|
|
2707
2945
|
const selfTestSecrets = resolveSelfTestMasterSecrets();
|
|
2946
|
+
// Resolve the engine here so the boot event reports the truthful source
|
|
2947
|
+
// (option/env/default) and the app is handed the engine already selected —
|
|
2948
|
+
// one resolution per process, not one per app construction.
|
|
2949
|
+
const engineSelection = resolveServerEngine(options);
|
|
2708
2950
|
const serverAppOptions = {
|
|
2709
2951
|
logger: options.logger,
|
|
2710
|
-
engine:
|
|
2952
|
+
engine: engineSelection.engine,
|
|
2711
2953
|
files: options.files,
|
|
2712
2954
|
ocr: options.ocr,
|
|
2713
2955
|
stt: options.stt,
|
|
2714
2956
|
resolver: options.resolver,
|
|
2957
|
+
localeCatalogs: options.localeCatalogs,
|
|
2715
2958
|
state: options.state,
|
|
2716
2959
|
allowMemoryStateFallback: options.allowMemoryStateFallback,
|
|
2717
2960
|
operationExecutor: options.operationExecutor,
|
|
@@ -2722,6 +2965,9 @@ export async function serve(provider, options = {}) {
|
|
|
2722
2965
|
createServerAppAsync(provider, serverAppOptions),
|
|
2723
2966
|
selfTestSecrets ? import("./self-test.js") : undefined,
|
|
2724
2967
|
]);
|
|
2968
|
+
// One structured line per process boot, before the listener binds: the fleet
|
|
2969
|
+
// audit (ADR-0011 migration) counts these.
|
|
2970
|
+
logger(engineModeLogEvent(provider, engineSelection.resolution, !isUnavailableProviderEngine(engineSelection.engine)));
|
|
2725
2971
|
const servers = [];
|
|
2726
2972
|
try {
|
|
2727
2973
|
servers.push(bunRuntime.serve({
|