@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";
|
|
@@ -12,14 +12,22 @@ import { z } from "zod";
|
|
|
12
12
|
import { AuthAbortError, createAuthFlowHelpers } from "../auth.js";
|
|
13
13
|
import { validateFailClosedDeclaration } from "../declaration-validation.js";
|
|
14
14
|
import {
|
|
15
|
-
createInProcessProviderEngine,
|
|
16
15
|
ENGINE_OWNED_PROXY_CREDENTIAL_ENV_NAMES,
|
|
17
16
|
isEngineOwnedEnvName,
|
|
18
17
|
type ProviderEngine,
|
|
19
18
|
type ProviderEngineBindingCandidates,
|
|
19
|
+
type ProviderEngineMode,
|
|
20
20
|
readEngineProxyCredentials,
|
|
21
21
|
} from "../engine.js";
|
|
22
|
+
import {
|
|
23
|
+
createEngineForMode,
|
|
24
|
+
isUnavailableProviderEngine,
|
|
25
|
+
type ProviderEngineModeResolution,
|
|
26
|
+
resolveProviderEngineMode,
|
|
27
|
+
} from "../runtime/engine-mode.js";
|
|
28
|
+
import { SDK_VERSION } from "./sdk-version.js";
|
|
22
29
|
import { safeProviderErrorObservability } from "../error-observability.js";
|
|
30
|
+
import { providerErrorStackFrames } from "./error-stack-frames.js";
|
|
23
31
|
import {
|
|
24
32
|
SDK_OWNED_PROVIDER_ERROR_CODES,
|
|
25
33
|
SDK_RUNTIME_OWNED_ERROR_CODES,
|
|
@@ -42,6 +50,15 @@ import {
|
|
|
42
50
|
localizeAuthTurn,
|
|
43
51
|
type ProviderLocaleCatalogMap,
|
|
44
52
|
} from "../i18n/catalog.js";
|
|
53
|
+
import {
|
|
54
|
+
acceptLanguageHeaderValue,
|
|
55
|
+
DEFAULT_PROVIDER_ERROR_LOCALE,
|
|
56
|
+
localizeProviderErrorText,
|
|
57
|
+
PROVIDER_ERROR_LOCALES,
|
|
58
|
+
type ProviderErrorMessageParams,
|
|
59
|
+
type ProviderErrorTextCandidate,
|
|
60
|
+
resolveProviderErrorLocale,
|
|
61
|
+
} from "../i18n/error-messages.js";
|
|
45
62
|
import type { ProviderLocale } from "../i18n/keys.js";
|
|
46
63
|
import {
|
|
47
64
|
categoryForStatus,
|
|
@@ -54,6 +71,7 @@ import {
|
|
|
54
71
|
import { createScratchpad } from "../runtime/auth-flow.js";
|
|
55
72
|
import type * as BrowserRuntimeModule from "../runtime/browser.js";
|
|
56
73
|
import { createProviderCache } from "../runtime/cache.js";
|
|
74
|
+
import { bindCacheTelemetry, CacheTelemetryCollector } from "../runtime/cache-telemetry.js";
|
|
57
75
|
import type { HandleTelemetryEvent } from "../handle.js";
|
|
58
76
|
import { createHandleContext } from "../runtime/handle.js";
|
|
59
77
|
import { createCredentialContext } from "../runtime/credential.js";
|
|
@@ -77,9 +95,17 @@ import {
|
|
|
77
95
|
import { createEnvContext } from "../runtime/env.js";
|
|
78
96
|
import { executeOperation } from "../runtime/executor.js";
|
|
79
97
|
import { createHttpClient } from "../runtime/http.js";
|
|
98
|
+
import { NativeTelemetryCollector } from "../runtime/native-telemetry.js";
|
|
99
|
+
import {
|
|
100
|
+
bindHttpTelemetry,
|
|
101
|
+
isSupportedHttpBinding,
|
|
102
|
+
warnUnsupportedHttpBinding,
|
|
103
|
+
} from "../runtime/http-telemetry-binding.js";
|
|
104
|
+
import { HttpTelemetryCollector, type HttpTelemetryLogPayload } from "../runtime/http-telemetry.js";
|
|
80
105
|
import { wrapWithInstrumentation } from "../runtime/instrumentation.js";
|
|
81
106
|
import type * as NativeNetworkRuntimeModule from "../runtime/native-network.js";
|
|
82
107
|
import { createOcrClientFromEnv } from "../runtime/ocr.js";
|
|
108
|
+
import { bindOcrTelemetry, OcrTelemetryCollector } from "../runtime/ocr-telemetry.js";
|
|
83
109
|
import { getProviderBaseUrl } from "../runtime/provider.js";
|
|
84
110
|
import {
|
|
85
111
|
PROXY_AUTH_IP_DENIED_CODE,
|
|
@@ -110,6 +136,10 @@ import {
|
|
|
110
136
|
ResolverTelemetryCollector,
|
|
111
137
|
type ResolverTelemetryLogPayload,
|
|
112
138
|
} from "../runtime/resolver-telemetry.js";
|
|
139
|
+
import {
|
|
140
|
+
createResolverRuntimeOptions,
|
|
141
|
+
resolveNativeProxyPolicy,
|
|
142
|
+
} from "../runtime/resolver-runtime-options.js";
|
|
113
143
|
import {
|
|
114
144
|
assertRequiredSecretsPresent,
|
|
115
145
|
listMissingRequiredSecrets,
|
|
@@ -119,10 +149,22 @@ import {
|
|
|
119
149
|
createProviderRuntimeStateFromEnv,
|
|
120
150
|
createUnsupportedProviderRuntimeState,
|
|
121
151
|
} from "../runtime/state.js";
|
|
152
|
+
import {
|
|
153
|
+
StateTelemetryCollector,
|
|
154
|
+
instrumentProviderRuntimeState,
|
|
155
|
+
} from "../runtime/state-telemetry.js";
|
|
122
156
|
import type * as StealthRuntimeModule from "../runtime/stealth.js";
|
|
123
157
|
import type { StealthChallengeRuntime } from "../runtime/stealth-akamai-sbsd.js";
|
|
124
158
|
import { StealthCookieJar } from "../runtime/stealth-cookies.js";
|
|
159
|
+
import { StealthTelemetryCollector } from "../runtime/stealth-telemetry.js";
|
|
160
|
+
import { BrowserTelemetryCollector } from "../runtime/browser-telemetry.js";
|
|
161
|
+
import {
|
|
162
|
+
bindBrowserTelemetry,
|
|
163
|
+
isSupportedBrowserBinding,
|
|
164
|
+
warnUnsupportedBrowserBinding,
|
|
165
|
+
} from "../runtime/browser-telemetry-binding.js";
|
|
125
166
|
import { createSttClientFromEnv } from "../runtime/stt.js";
|
|
167
|
+
import { bindSttTelemetry, SttTelemetryCollector } from "../runtime/stt-telemetry.js";
|
|
126
168
|
import {
|
|
127
169
|
createTraceContext,
|
|
128
170
|
getTraceRecorder,
|
|
@@ -153,7 +195,6 @@ import type {
|
|
|
153
195
|
BrowserClient,
|
|
154
196
|
FlowContext,
|
|
155
197
|
FlowContextStore,
|
|
156
|
-
HttpRetrySummary,
|
|
157
198
|
OcrContext,
|
|
158
199
|
OperationDefinition,
|
|
159
200
|
OperationErrorCode,
|
|
@@ -164,8 +205,8 @@ import type {
|
|
|
164
205
|
ProviderDefinition,
|
|
165
206
|
ProviderErrorStatus,
|
|
166
207
|
ProviderFilesContext,
|
|
167
|
-
ProviderProxyPolicy,
|
|
168
208
|
ProviderRuntimeState,
|
|
209
|
+
ProviderRuntimeTarget,
|
|
169
210
|
ProviderStreamEvent,
|
|
170
211
|
ResolverContext,
|
|
171
212
|
StealthClient,
|
|
@@ -233,30 +274,7 @@ function providerErrorCode(error: unknown): string | undefined {
|
|
|
233
274
|
}
|
|
234
275
|
}
|
|
235
276
|
|
|
236
|
-
const
|
|
237
|
-
const retryResponseMeta = new WeakMap<ProviderContext, HttpRetrySummary>();
|
|
238
|
-
type RetryLastErrorCode =
|
|
239
|
-
// runtime/http.ts:178-179 preserves ambient cancellation.
|
|
240
|
-
| "transport_cancelled"
|
|
241
|
-
// runtime/http.ts:180-196 normalizes timeout aborts and timeout-shaped errors.
|
|
242
|
-
| "transport_timeout"
|
|
243
|
-
// runtime/http.ts:198-225 normalizes other native fetch failures.
|
|
244
|
-
| "transport_network_error"
|
|
245
|
-
// runtime/http.ts:119-126,763-771,829-837 creates upstream status errors.
|
|
246
|
-
| "upstream_http_error"
|
|
247
|
-
// runtime/proxy-retry-policy.ts:64-84 forwards other coded TransportErrors; the builder closes them.
|
|
248
|
-
| "other";
|
|
249
|
-
const RETRY_LAST_ERROR_CODES = [
|
|
250
|
-
"transport_cancelled",
|
|
251
|
-
"transport_timeout",
|
|
252
|
-
"transport_network_error",
|
|
253
|
-
"upstream_http_error",
|
|
254
|
-
"other",
|
|
255
|
-
] as const satisfies readonly RetryLastErrorCode[];
|
|
256
|
-
|
|
257
|
-
function tenantRetryLastErrorCode(value: string): RetryLastErrorCode {
|
|
258
|
-
return RETRY_LAST_ERROR_CODES.find((candidate) => candidate === value) ?? "other";
|
|
259
|
-
}
|
|
277
|
+
const retryResponseMeta = new WeakMap<ProviderContext, HttpTelemetryCollector>();
|
|
260
278
|
const STATEFUL_INTERNAL_OPERATIONS_ROUTE = "/__apifuse/stateful/operations";
|
|
261
279
|
const STATEFUL_FORWARDING_SOURCE_POD_HEADER = "x-apifuse-stateful-source-pod";
|
|
262
280
|
const DEFAULT_STATEFUL_FORWARDING_MAX_SKEW_MS = 5 * 60_000;
|
|
@@ -289,6 +307,22 @@ export const ProviderServerStatefulForwardEnvelopeSchema = z
|
|
|
289
307
|
})
|
|
290
308
|
.strict();
|
|
291
309
|
|
|
310
|
+
// Inbound compatibility boundary for rolling deploys, mirroring the outbound
|
|
311
|
+
// one in stateful-provider-owner-forwarder.ts: emitted envelopes stay strict
|
|
312
|
+
// (above), while a received envelope's nested operation request keeps the
|
|
313
|
+
// gateway route's tolerance and strips unknown keys. A source pod on a newer
|
|
314
|
+
// SDK carrying an additive OperationRequestSchema field (tenantId, #164) must
|
|
315
|
+
// not be rejected with STATEFUL_FORWARDING_ENVELOPE_INVALID by an owner pod
|
|
316
|
+
// that predates the field: the rejection is terminal for the operation and the
|
|
317
|
+
// two pods coexist for the whole rolling update. The envelope's own keys stay
|
|
318
|
+
// strict on both sides, because they are protocol rather than payload.
|
|
319
|
+
const ReceivedStatefulForwardEnvelopeSchema =
|
|
320
|
+
ProviderServerStatefulForwardEnvelopeSchema.extend({
|
|
321
|
+
operationRequest: z.object(
|
|
322
|
+
ProviderServerStatefulForwardEnvelopeSchema.shape.operationRequest.shape,
|
|
323
|
+
),
|
|
324
|
+
});
|
|
325
|
+
|
|
292
326
|
export type ProviderServerStatefulForwardEnvelope = Readonly<
|
|
293
327
|
z.infer<typeof ProviderServerStatefulForwardEnvelopeSchema>
|
|
294
328
|
>;
|
|
@@ -616,36 +650,6 @@ function getProviderStealthProfile(provider: ProviderDefinition) {
|
|
|
616
650
|
|
|
617
651
|
type ResolverRuntimeOptions = ResolverRuntimeModule.ResolverRuntimeOptions;
|
|
618
652
|
|
|
619
|
-
/** One option set for ctx.resolver and the automatic SBSD solve, so both share identity scope, cache, proxy intent, and telemetry. */
|
|
620
|
-
function createResolverRuntimeOptions(
|
|
621
|
-
provider: ProviderDefinition,
|
|
622
|
-
cache: ReturnType<typeof createProviderCache>,
|
|
623
|
-
identityScope: string,
|
|
624
|
-
proxyPolicy: ProviderProxyPolicy | undefined,
|
|
625
|
-
proxyClientOptions: Omit<
|
|
626
|
-
NonNullable<ResolverRuntimeOptions["proxyIntent"]>,
|
|
627
|
-
"mode" | "userAgent"
|
|
628
|
-
>,
|
|
629
|
-
stealthProfile: { readonly userAgent: string } | undefined,
|
|
630
|
-
telemetry: ResolverTelemetryCollector,
|
|
631
|
-
): ResolverRuntimeOptions {
|
|
632
|
-
return {
|
|
633
|
-
allowedHosts: provider.allowedHosts,
|
|
634
|
-
cache,
|
|
635
|
-
telemetry,
|
|
636
|
-
identityScope,
|
|
637
|
-
...(proxyPolicy
|
|
638
|
-
? {
|
|
639
|
-
proxyIntent: {
|
|
640
|
-
mode: proxyPolicy.mode,
|
|
641
|
-
...proxyClientOptions,
|
|
642
|
-
...(stealthProfile ? { userAgent: stealthProfile.userAgent } : {}),
|
|
643
|
-
},
|
|
644
|
-
}
|
|
645
|
-
: {}),
|
|
646
|
-
};
|
|
647
|
-
}
|
|
648
|
-
|
|
649
653
|
/** Shared predicate for the SBSD challenge runtime and the ceremony egress lease. */
|
|
650
654
|
function declaresAkamaiSbsdResolver(provider: ProviderDefinition): boolean {
|
|
651
655
|
return provider.resolver?.kinds.some((kind) => kind === "akamai_sbsd") === true;
|
|
@@ -677,12 +681,17 @@ function createStealthChallengeDetection(
|
|
|
677
681
|
async solve(challenge, transport, solveSignal) {
|
|
678
682
|
const resolver = resolverRuntime.bindResolverSignal(
|
|
679
683
|
(resolverOverride
|
|
680
|
-
? bindResolverTelemetry(
|
|
684
|
+
? bindResolverTelemetry(
|
|
685
|
+
resolverOverride,
|
|
686
|
+
resolverOptions.telemetry,
|
|
687
|
+
resolverOptions.browserTelemetry,
|
|
688
|
+
)
|
|
681
689
|
: undefined) ??
|
|
682
690
|
resolverRuntime.createResolverClientFromEnv(provider.resolver, undefined, {
|
|
683
691
|
allowedHosts: resolverOptions.allowedHosts,
|
|
684
692
|
cache: resolverOptions.cache,
|
|
685
693
|
telemetry: resolverOptions.telemetry,
|
|
694
|
+
browserTelemetry: resolverOptions.browserTelemetry,
|
|
686
695
|
identityScope: resolverOptions.identityScope,
|
|
687
696
|
// No proxyIntent: the transport is already bound to the initiating
|
|
688
697
|
// request's lease, and a second identity resolution would allocate
|
|
@@ -817,20 +826,18 @@ function resolveOperationConnectionId(
|
|
|
817
826
|
// absent so it can never override a valid id or key a real scope. Requests
|
|
818
827
|
// without any usable id fall back to the documented missing-connection
|
|
819
828
|
// sentinel scope instead of scoping context/affinity/state under "".
|
|
820
|
-
return (
|
|
821
|
-
normalizeConnectionId(request.connection?.id) ?? normalizeConnectionId(request.connectionId)
|
|
822
|
-
);
|
|
829
|
+
return normalizeIdentifier(request.connection?.id) ?? normalizeIdentifier(request.connectionId);
|
|
823
830
|
}
|
|
824
831
|
|
|
825
|
-
function
|
|
826
|
-
|
|
832
|
+
function resolveOperationTenantId(request: Pick<OperationRequest, "tenantId">): string | undefined {
|
|
833
|
+
// Only the gateway-asserted envelope field is consulted; caller headers can
|
|
834
|
+
// never populate the principal scope. Same rule as connection ids: "" is a
|
|
835
|
+
// malformed identifier, not a principal.
|
|
836
|
+
return normalizeIdentifier(request.tenantId);
|
|
827
837
|
}
|
|
828
838
|
|
|
829
|
-
function
|
|
830
|
-
|
|
831
|
-
if (provider.proxy === true) return { mode: "optional" };
|
|
832
|
-
if (provider.proxy === false) return { mode: "disabled" };
|
|
833
|
-
return undefined;
|
|
839
|
+
function normalizeIdentifier(id: string | undefined): string | undefined {
|
|
840
|
+
return id === "" ? undefined : id;
|
|
834
841
|
}
|
|
835
842
|
|
|
836
843
|
function providerSecretNames(provider: ProviderDefinition): string[] {
|
|
@@ -840,11 +847,19 @@ function providerSecretNames(provider: ProviderDefinition): string[] {
|
|
|
840
847
|
}
|
|
841
848
|
|
|
842
849
|
type RequestScopeContext = {
|
|
850
|
+
run<T>(fn: () => Promise<T>): Promise<T>;
|
|
843
851
|
redact: DiagnosticRedactor;
|
|
844
852
|
trace: RuntimeTraceContext;
|
|
845
853
|
telemetry: RequestTelemetry;
|
|
846
854
|
resolverTelemetry: ResolverTelemetryCollector;
|
|
847
855
|
nativeTelemetry: NativeTelemetryCollector;
|
|
856
|
+
httpTelemetry: HttpTelemetryCollector;
|
|
857
|
+
stealthTelemetry: StealthTelemetryCollector;
|
|
858
|
+
ocrTelemetry: OcrTelemetryCollector;
|
|
859
|
+
sttTelemetry: SttTelemetryCollector;
|
|
860
|
+
browserTelemetry: BrowserTelemetryCollector;
|
|
861
|
+
cacheTelemetry: CacheTelemetryCollector;
|
|
862
|
+
stateTelemetry: StateTelemetryCollector;
|
|
848
863
|
};
|
|
849
864
|
|
|
850
865
|
function createProviderContext(
|
|
@@ -872,8 +887,9 @@ function createProviderContext(
|
|
|
872
887
|
proxyClientOptions.affinityKey,
|
|
873
888
|
request.requestId,
|
|
874
889
|
);
|
|
875
|
-
const cache = createProviderCache(
|
|
876
|
-
|
|
890
|
+
const cache = createProviderCache(
|
|
891
|
+
bindCacheTelemetry({ providerId: provider.id }, scope.cacheTelemetry),
|
|
892
|
+
);
|
|
877
893
|
const { capabilityModules } = options;
|
|
878
894
|
const resolverOptions = createResolverRuntimeOptions(
|
|
879
895
|
provider,
|
|
@@ -883,6 +899,7 @@ function createProviderContext(
|
|
|
883
899
|
proxyClientOptions,
|
|
884
900
|
stealthProfile,
|
|
885
901
|
scope.resolverTelemetry,
|
|
902
|
+
scope.browserTelemetry,
|
|
886
903
|
);
|
|
887
904
|
const challengeRuntime = createStealthChallengeDetection(
|
|
888
905
|
provider,
|
|
@@ -893,6 +910,7 @@ function createProviderContext(
|
|
|
893
910
|
signal,
|
|
894
911
|
);
|
|
895
912
|
const stealthClientOptions = {
|
|
913
|
+
stealthTelemetry: scope.stealthTelemetry,
|
|
896
914
|
upstream: proxyClientOptions.upstream,
|
|
897
915
|
affinityKey: proxyClientOptions.affinityKey,
|
|
898
916
|
telemetry: scope.telemetry.proxy,
|
|
@@ -928,11 +946,19 @@ function createProviderContext(
|
|
|
928
946
|
scopes: request.connection?.scopes,
|
|
929
947
|
values: request.connection?.secrets,
|
|
930
948
|
});
|
|
949
|
+
const operationTenantId = resolveOperationTenantId(request);
|
|
931
950
|
const requestContext = {
|
|
932
951
|
connectionId: resolveOperationConnectionId(request),
|
|
952
|
+
// Absent stays absent: an own property with an undefined value would
|
|
953
|
+
// change the shape of ctx.request for every existing consumer.
|
|
954
|
+
...(operationTenantId !== undefined ? { tenantId: operationTenantId } : {}),
|
|
933
955
|
headers: request.headers ?? {},
|
|
934
956
|
};
|
|
935
|
-
const requestState =
|
|
957
|
+
const requestState = instrumentProviderRuntimeState(
|
|
958
|
+
state,
|
|
959
|
+
scope.stateTelemetry,
|
|
960
|
+
scope.redact,
|
|
961
|
+
).forConnection(requestContext.connectionId);
|
|
936
962
|
const bindings: ProviderEngineBindingCandidates = {
|
|
937
963
|
env,
|
|
938
964
|
credential,
|
|
@@ -941,10 +967,7 @@ function createProviderContext(
|
|
|
941
967
|
http: createHttpClient(baseUrl, {
|
|
942
968
|
...proxyClientOptions,
|
|
943
969
|
...(signal ? { signal } : {}),
|
|
944
|
-
|
|
945
|
-
if (summary.attempts <= 1 || !wrappedContext) return;
|
|
946
|
-
retryResponseMeta.set(wrappedContext, summary);
|
|
947
|
-
},
|
|
970
|
+
httpTelemetry: scope.httpTelemetry,
|
|
948
971
|
}),
|
|
949
972
|
cache,
|
|
950
973
|
state: requestState,
|
|
@@ -962,6 +985,7 @@ function createProviderContext(
|
|
|
962
985
|
requireCdpPool: isProductionProviderBrowserMode(provider),
|
|
963
986
|
stealth: true,
|
|
964
987
|
engine: provider.browser?.engine,
|
|
988
|
+
telemetry: scope.browserTelemetry,
|
|
965
989
|
})
|
|
966
990
|
: createBrowserStub(),
|
|
967
991
|
...(provider.native
|
|
@@ -982,12 +1006,22 @@ function createProviderContext(
|
|
|
982
1006
|
: {}),
|
|
983
1007
|
trace: scope.trace,
|
|
984
1008
|
auth: createAuthStub(),
|
|
985
|
-
ocr:
|
|
986
|
-
|
|
1009
|
+
ocr: bindCapabilityRoot(
|
|
1010
|
+
bindOcrTelemetry(options.ocr ?? createOcrClientFromEnv(provider.ocr), scope.ocrTelemetry),
|
|
1011
|
+
scope.run,
|
|
1012
|
+
),
|
|
1013
|
+
stt: bindCapabilityRoot(
|
|
1014
|
+
bindSttTelemetry(options.stt ?? createSttClientFromEnv(provider.stt), scope.sttTelemetry),
|
|
1015
|
+
scope.run,
|
|
1016
|
+
),
|
|
987
1017
|
resolver: capabilityModules.resolver
|
|
988
1018
|
? capabilityModules.resolver.bindResolverSignal(
|
|
989
1019
|
(options.resolver
|
|
990
|
-
? bindResolverTelemetry(
|
|
1020
|
+
? bindResolverTelemetry(
|
|
1021
|
+
options.resolver,
|
|
1022
|
+
resolverOptions.telemetry,
|
|
1023
|
+
resolverOptions.browserTelemetry,
|
|
1024
|
+
)
|
|
991
1025
|
: undefined) ??
|
|
992
1026
|
capabilityModules.resolver.createResolverClientFromEnv(
|
|
993
1027
|
provider.resolver,
|
|
@@ -998,7 +1032,11 @@ function createProviderContext(
|
|
|
998
1032
|
)
|
|
999
1033
|
: bindResolverSignalWithoutRuntime(
|
|
1000
1034
|
(options.resolver
|
|
1001
|
-
? bindResolverTelemetry(
|
|
1035
|
+
? bindResolverTelemetry(
|
|
1036
|
+
options.resolver,
|
|
1037
|
+
resolverOptions.telemetry,
|
|
1038
|
+
resolverOptions.browserTelemetry,
|
|
1039
|
+
)
|
|
1002
1040
|
: undefined) ??
|
|
1003
1041
|
createUnsupportedResolverClient("Provider does not declare resolver capability"),
|
|
1004
1042
|
signal,
|
|
@@ -1015,10 +1053,43 @@ function createProviderContext(
|
|
|
1015
1053
|
}),
|
|
1016
1054
|
}),
|
|
1017
1055
|
};
|
|
1018
|
-
const
|
|
1019
|
-
|
|
1020
|
-
);
|
|
1021
|
-
|
|
1056
|
+
const candidateHttp = bindings.http;
|
|
1057
|
+
const candidateBrowser = bindings.browser;
|
|
1058
|
+
const attached = options.engine.attach({ provider, bindings }) as ProviderContext;
|
|
1059
|
+
const hostHttp = provider.http ? attached.http : undefined;
|
|
1060
|
+
const supportedHostHttp = provider.http && isSupportedHttpBinding(hostHttp);
|
|
1061
|
+
if (provider.http && !supportedHostHttp) warnUnsupportedHttpBinding();
|
|
1062
|
+
const observedHttp =
|
|
1063
|
+
supportedHostHttp && hostHttp !== candidateHttp
|
|
1064
|
+
? bindHttpTelemetry(hostHttp, scope.httpTelemetry)
|
|
1065
|
+
: undefined;
|
|
1066
|
+
const hostBrowser = provider.runtime === "browser" ? attached.browser : undefined;
|
|
1067
|
+
const supportedHostBrowser =
|
|
1068
|
+
provider.runtime === "browser" && isSupportedBrowserBinding(hostBrowser);
|
|
1069
|
+
if (provider.runtime === "browser" && !supportedHostBrowser) warnUnsupportedBrowserBinding();
|
|
1070
|
+
const observedBrowser =
|
|
1071
|
+
supportedHostBrowser && hostBrowser !== candidateBrowser
|
|
1072
|
+
? bindBrowserTelemetry(hostBrowser, scope.browserTelemetry)
|
|
1073
|
+
: undefined;
|
|
1074
|
+
const contextTarget =
|
|
1075
|
+
observedBrowser || observedHttp
|
|
1076
|
+
? new Proxy(attached, {
|
|
1077
|
+
get(target, property, receiver) {
|
|
1078
|
+
if (property === "http" && observedHttp) return observedHttp;
|
|
1079
|
+
if (property === "http" && provider.http && !supportedHostHttp) return undefined;
|
|
1080
|
+
if (property === "browser" && observedBrowser) return observedBrowser;
|
|
1081
|
+
return Reflect.get(target, property, receiver);
|
|
1082
|
+
},
|
|
1083
|
+
})
|
|
1084
|
+
: provider.http && !supportedHostHttp
|
|
1085
|
+
? new Proxy(attached, {
|
|
1086
|
+
get(target, property, receiver) {
|
|
1087
|
+
return property === "http" ? undefined : Reflect.get(target, property, receiver);
|
|
1088
|
+
},
|
|
1089
|
+
})
|
|
1090
|
+
: attached;
|
|
1091
|
+
const context = wrapWithInstrumentation(contextTarget);
|
|
1092
|
+
retryResponseMeta.set(context, scope.httpTelemetry);
|
|
1022
1093
|
return context;
|
|
1023
1094
|
}
|
|
1024
1095
|
|
|
@@ -1133,7 +1204,9 @@ function createAuthFlowContext(
|
|
|
1133
1204
|
proxyClientOptions.affinityKey,
|
|
1134
1205
|
request.requestId,
|
|
1135
1206
|
);
|
|
1136
|
-
const cache = createProviderCache(
|
|
1207
|
+
const cache = createProviderCache(
|
|
1208
|
+
bindCacheTelemetry({ providerId: provider.id }, scope.cacheTelemetry),
|
|
1209
|
+
);
|
|
1137
1210
|
const { capabilityModules } = options;
|
|
1138
1211
|
const resolverOptions = createResolverRuntimeOptions(
|
|
1139
1212
|
provider,
|
|
@@ -1143,6 +1216,7 @@ function createAuthFlowContext(
|
|
|
1143
1216
|
proxyClientOptions,
|
|
1144
1217
|
stealthProfile,
|
|
1145
1218
|
scope.resolverTelemetry,
|
|
1219
|
+
scope.browserTelemetry,
|
|
1146
1220
|
);
|
|
1147
1221
|
const challengeRuntime = createStealthChallengeDetection(
|
|
1148
1222
|
provider,
|
|
@@ -1153,6 +1227,7 @@ function createAuthFlowContext(
|
|
|
1153
1227
|
signal,
|
|
1154
1228
|
);
|
|
1155
1229
|
const stealthClientOptions = {
|
|
1230
|
+
stealthTelemetry: scope.stealthTelemetry,
|
|
1156
1231
|
upstream: proxyClientOptions.upstream,
|
|
1157
1232
|
affinityKey: proxyClientOptions.affinityKey,
|
|
1158
1233
|
telemetry: scope.telemetry.proxy,
|
|
@@ -1199,13 +1274,30 @@ function createAuthFlowContext(
|
|
|
1199
1274
|
http: createHttpClient(baseUrl, {
|
|
1200
1275
|
...proxyClientOptions,
|
|
1201
1276
|
...(signal ? { signal } : {}),
|
|
1277
|
+
httpTelemetry: scope.httpTelemetry,
|
|
1202
1278
|
}),
|
|
1203
|
-
state:
|
|
1279
|
+
state: instrumentProviderRuntimeState(
|
|
1280
|
+
state,
|
|
1281
|
+
scope.stateTelemetry,
|
|
1282
|
+
scope.redact,
|
|
1283
|
+
).forConnection(resolveOperationConnectionId(request)),
|
|
1204
1284
|
stealth: stealthBaseUrl
|
|
1205
1285
|
? capabilityModules.stealth
|
|
1206
1286
|
? capabilityModules.stealth.createStealthClient(stealthBaseUrl, stealthClientOptions)
|
|
1207
1287
|
: createLazyStealthClient(logStealthCleanupError, stealthBaseUrl, stealthClientOptions)
|
|
1208
1288
|
: createStealthStub(),
|
|
1289
|
+
browser:
|
|
1290
|
+
provider.runtime === "browser"
|
|
1291
|
+
? capabilityModules.browser!.createBrowserClient({
|
|
1292
|
+
allowedHosts: provider.allowedHosts,
|
|
1293
|
+
cdpUrl: readDiagnosticEnv("APIFUSE__CDP_POOL__URL"),
|
|
1294
|
+
headless: true,
|
|
1295
|
+
requireCdpPool: isProductionProviderBrowserMode(provider),
|
|
1296
|
+
stealth: true,
|
|
1297
|
+
engine: provider.browser?.engine,
|
|
1298
|
+
telemetry: scope.browserTelemetry,
|
|
1299
|
+
})
|
|
1300
|
+
: createBrowserStub(),
|
|
1209
1301
|
...(provider.native
|
|
1210
1302
|
? {
|
|
1211
1303
|
native: {
|
|
@@ -1228,12 +1320,22 @@ function createAuthFlowContext(
|
|
|
1228
1320
|
]),
|
|
1229
1321
|
credential,
|
|
1230
1322
|
context: flowContextStore.context,
|
|
1231
|
-
ocr:
|
|
1232
|
-
|
|
1323
|
+
ocr: bindCapabilityRoot(
|
|
1324
|
+
bindOcrTelemetry(options.ocr ?? createOcrClientFromEnv(provider.ocr), scope.ocrTelemetry),
|
|
1325
|
+
scope.run,
|
|
1326
|
+
),
|
|
1327
|
+
stt: bindCapabilityRoot(
|
|
1328
|
+
bindSttTelemetry(options.stt ?? createSttClientFromEnv(provider.stt), scope.sttTelemetry),
|
|
1329
|
+
scope.run,
|
|
1330
|
+
),
|
|
1233
1331
|
resolver: capabilityModules.resolver
|
|
1234
1332
|
? capabilityModules.resolver.bindResolverSignal(
|
|
1235
1333
|
(options.resolver
|
|
1236
|
-
? bindResolverTelemetry(
|
|
1334
|
+
? bindResolverTelemetry(
|
|
1335
|
+
options.resolver,
|
|
1336
|
+
resolverOptions.telemetry,
|
|
1337
|
+
resolverOptions.browserTelemetry,
|
|
1338
|
+
)
|
|
1237
1339
|
: undefined) ??
|
|
1238
1340
|
capabilityModules.resolver.createResolverClientFromEnv(
|
|
1239
1341
|
provider.resolver,
|
|
@@ -1244,7 +1346,11 @@ function createAuthFlowContext(
|
|
|
1244
1346
|
)
|
|
1245
1347
|
: bindResolverSignalWithoutRuntime(
|
|
1246
1348
|
(options.resolver
|
|
1247
|
-
? bindResolverTelemetry(
|
|
1349
|
+
? bindResolverTelemetry(
|
|
1350
|
+
options.resolver,
|
|
1351
|
+
resolverOptions.telemetry,
|
|
1352
|
+
resolverOptions.browserTelemetry,
|
|
1353
|
+
)
|
|
1248
1354
|
: undefined) ??
|
|
1249
1355
|
createUnsupportedResolverClient("Provider does not declare resolver capability"),
|
|
1250
1356
|
signal,
|
|
@@ -1288,6 +1394,7 @@ type ProviderServerLogEventBase = ProviderRequestCost & {
|
|
|
1288
1394
|
status: number;
|
|
1289
1395
|
proxy?: ProxyTelemetryLogPayload;
|
|
1290
1396
|
resolver?: ResolverTelemetryLogPayload;
|
|
1397
|
+
http?: HttpTelemetryLogPayload;
|
|
1291
1398
|
};
|
|
1292
1399
|
|
|
1293
1400
|
export type ProviderServerLogEvent =
|
|
@@ -1307,6 +1414,7 @@ export type ProviderServerLogEvent =
|
|
|
1307
1414
|
retryable?: boolean;
|
|
1308
1415
|
providerObservability?: ProviderErrorObservability;
|
|
1309
1416
|
causeChain?: ProviderErrorCauseFrame[];
|
|
1417
|
+
stack?: string[];
|
|
1310
1418
|
signal?: "unregistered_provider_error_code";
|
|
1311
1419
|
signalFix?: string;
|
|
1312
1420
|
issues?: Array<{ path: string; code: string; message: string }>;
|
|
@@ -1317,6 +1425,13 @@ export type ProviderServerLogEvent =
|
|
|
1317
1425
|
providerId: string;
|
|
1318
1426
|
missingSecrets: string[];
|
|
1319
1427
|
}
|
|
1428
|
+
| {
|
|
1429
|
+
/** Emitted once at boot when `locales/en.json` exists but cannot be read. */
|
|
1430
|
+
level: "warn";
|
|
1431
|
+
event: "provider_locale_catalogs_unavailable";
|
|
1432
|
+
providerId: string;
|
|
1433
|
+
reason: string;
|
|
1434
|
+
}
|
|
1320
1435
|
| ({
|
|
1321
1436
|
level: "info";
|
|
1322
1437
|
event: "provider_handle";
|
|
@@ -1340,6 +1455,27 @@ export type ProviderServerLogEvent =
|
|
|
1340
1455
|
errorClass: string;
|
|
1341
1456
|
message: string;
|
|
1342
1457
|
}
|
|
1458
|
+
| {
|
|
1459
|
+
/** `warn` when the requested attachment could not be honoured, otherwise `info`. */
|
|
1460
|
+
level: "info" | "warn";
|
|
1461
|
+
event: "provider_engine_mode";
|
|
1462
|
+
providerId: string;
|
|
1463
|
+
/** Resolved attachment; `custom` for an opaque host engine. */
|
|
1464
|
+
mode: ProviderEngineMode | "custom";
|
|
1465
|
+
source: "engine" | "env" | "option" | "default";
|
|
1466
|
+
/** True for every non-remote attachment, whose removal is scheduled by ADR-0011. */
|
|
1467
|
+
deprecated: boolean;
|
|
1468
|
+
/** False when the engine refuses to attach; `/readyz` reports 503. */
|
|
1469
|
+
attached: boolean;
|
|
1470
|
+
sdkVersion: string;
|
|
1471
|
+
runtimeTarget?: ProviderRuntimeTarget;
|
|
1472
|
+
/**
|
|
1473
|
+
* Non-fatal selection findings, e.g. `invalid_env_value`,
|
|
1474
|
+
* `env_overrode_option`, `engine_object_overrode_requested_mode`,
|
|
1475
|
+
* `engine_env_ignored`. Absent when the selection was unambiguous.
|
|
1476
|
+
*/
|
|
1477
|
+
warnings?: readonly string[];
|
|
1478
|
+
}
|
|
1343
1479
|
| SelfTestCancellationLogEvent;
|
|
1344
1480
|
|
|
1345
1481
|
export type ProviderServerLogger = (event: ProviderServerLogEvent) => void;
|
|
@@ -1348,6 +1484,17 @@ export type ProviderServerOptions<TContext extends Partial<ProviderContext> = Pr
|
|
|
1348
1484
|
logger?: ProviderServerLogger;
|
|
1349
1485
|
/** Capability attachment boundary. Defaults to the local in-process engine. */
|
|
1350
1486
|
engine?: ProviderEngine;
|
|
1487
|
+
/**
|
|
1488
|
+
* Explicit engine attachment mode. Lowest precedence of the three inputs:
|
|
1489
|
+
* an explicit `engine` object wins, then `APIFUSE__ENGINE__MODE` (the
|
|
1490
|
+
* deployment outranks provider code), then this option, then the in-process
|
|
1491
|
+
* default. Disagreement is never fatal — it is reported on the
|
|
1492
|
+
* `provider_engine_mode` boot event (`env_overrode_option`,
|
|
1493
|
+
* `engine_object_overrode_requested_mode`), which only `serve` emits. A value
|
|
1494
|
+
* that is not a known mode throws `ValidationError`, because unlike the env it
|
|
1495
|
+
* can only come from calling code.
|
|
1496
|
+
*/
|
|
1497
|
+
engineMode?: ProviderEngineMode;
|
|
1351
1498
|
/** Request-file resolver supplied by the engine host when `files` is declared. */
|
|
1352
1499
|
files?: ProviderFilesContext;
|
|
1353
1500
|
/** Optional provider-specific operation executor. Stateful providers use this to preserve provider-local runtime semantics. */
|
|
@@ -1371,6 +1518,15 @@ export type ProviderServerOptions<TContext extends Partial<ProviderContext> = Pr
|
|
|
1371
1518
|
* resolver is reported as one custom invocation; its internal vendor work is not exposed.
|
|
1372
1519
|
*/
|
|
1373
1520
|
resolver?: ResolverContext;
|
|
1521
|
+
/**
|
|
1522
|
+
* Pre-loaded provider locale catalogs used to localize auth turns and the
|
|
1523
|
+
* client-facing `message`/`fix` of provider errors.
|
|
1524
|
+
*
|
|
1525
|
+
* Defaults to reading `locales/{en,ko,ja}.json` from the provider directory
|
|
1526
|
+
* once at server construction. Supply this to bundle catalogs instead of
|
|
1527
|
+
* probing the filesystem, or to pin catalogs in tests.
|
|
1528
|
+
*/
|
|
1529
|
+
localeCatalogs?: ProviderLocaleCatalogMap;
|
|
1374
1530
|
/** Optional runtime state override for tests or custom hosts. Production resolves Redis from env and fails closed when unavailable. */
|
|
1375
1531
|
state?: ProviderRuntimeState;
|
|
1376
1532
|
/** Allow process-local runtime state only for local development and tests. */
|
|
@@ -1465,11 +1621,116 @@ function publicErrorSource(error: unknown, category: ProviderErrorCategory): Pro
|
|
|
1465
1621
|
return sourceForCategory(category);
|
|
1466
1622
|
}
|
|
1467
1623
|
|
|
1624
|
+
/**
|
|
1625
|
+
* Serve-time inputs for localizing the client-facing error envelope.
|
|
1626
|
+
*
|
|
1627
|
+
* Only `error.message` and `error.fix` of the response body are localized.
|
|
1628
|
+
* Logs, cause frames, OTLP attributes and the error-observability header keep
|
|
1629
|
+
* the English literal the provider threw.
|
|
1630
|
+
*/
|
|
1631
|
+
type ErrorEnvelopeLocalization = {
|
|
1632
|
+
readonly catalogs?: ProviderLocaleCatalogMap;
|
|
1633
|
+
readonly locale: ProviderLocale;
|
|
1634
|
+
readonly declaredErrorCode?: OperationErrorCode;
|
|
1635
|
+
};
|
|
1636
|
+
|
|
1637
|
+
function localeKeyOption(error: unknown, key: "messageKey" | "fixKey"): string | undefined {
|
|
1638
|
+
const value = providerErrorOption(error, key);
|
|
1639
|
+
return typeof value === "string" ? value : undefined;
|
|
1640
|
+
}
|
|
1641
|
+
|
|
1642
|
+
function declaredLocaleKey(
|
|
1643
|
+
declaration: OperationErrorCode | undefined,
|
|
1644
|
+
key: "messageKey" | "fixKey",
|
|
1645
|
+
): string | undefined {
|
|
1646
|
+
const value = declaration?.[key];
|
|
1647
|
+
return typeof value === "string" ? value : undefined;
|
|
1648
|
+
}
|
|
1649
|
+
|
|
1650
|
+
function providerErrorParams(error: unknown): ProviderErrorMessageParams | undefined {
|
|
1651
|
+
const params = providerErrorOption(error, "params");
|
|
1652
|
+
return params !== null && typeof params === "object" && !Array.isArray(params)
|
|
1653
|
+
? (params as ProviderErrorMessageParams)
|
|
1654
|
+
: undefined;
|
|
1655
|
+
}
|
|
1656
|
+
|
|
1657
|
+
/**
|
|
1658
|
+
* Resolution order for one localized envelope field:
|
|
1659
|
+
*
|
|
1660
|
+
* 1. the throw site's `messageKey`/`fixKey`;
|
|
1661
|
+
* 2. the declared `errorCodes[]` entry's key for the thrown code;
|
|
1662
|
+
* 3. the derived `errors.<code>.<field>` convention;
|
|
1663
|
+
* 4. the English literal the provider threw.
|
|
1664
|
+
*
|
|
1665
|
+
* Steps 2 and 3 are skipped for SDK-owned failures (transport, Zod, stateful
|
|
1666
|
+
* deadline, SDK runtime codes): their text is the SDK's contract, and a
|
|
1667
|
+
* provider catalog entry must not be able to relabel one. An explicit key on
|
|
1668
|
+
* the thrown error still wins, because that is the provider deliberately
|
|
1669
|
+
* describing an error it constructed itself.
|
|
1670
|
+
*/
|
|
1671
|
+
function localizedErrorField(
|
|
1672
|
+
error: unknown,
|
|
1673
|
+
field: "message" | "fix",
|
|
1674
|
+
literal: string | undefined,
|
|
1675
|
+
localization: ErrorEnvelopeLocalization | undefined,
|
|
1676
|
+
): string | undefined {
|
|
1677
|
+
if (!localization?.catalogs) return literal;
|
|
1678
|
+
const optionKey = field === "message" ? "messageKey" : "fixKey";
|
|
1679
|
+
const candidates: ProviderErrorTextCandidate[] = [
|
|
1680
|
+
{ kind: "key", key: localeKeyOption(error, optionKey) },
|
|
1681
|
+
];
|
|
1682
|
+
if (!sdkOwnsErrorResolution(error)) {
|
|
1683
|
+
candidates.push(
|
|
1684
|
+
{ kind: "key", key: declaredLocaleKey(localization.declaredErrorCode, optionKey) },
|
|
1685
|
+
{ kind: "derived", code: providerErrorCode(error), field },
|
|
1686
|
+
);
|
|
1687
|
+
}
|
|
1688
|
+
return localizeProviderErrorText({
|
|
1689
|
+
catalogs: localization.catalogs,
|
|
1690
|
+
locale: localization.locale,
|
|
1691
|
+
candidates,
|
|
1692
|
+
params: providerErrorParams(error),
|
|
1693
|
+
fallback: literal,
|
|
1694
|
+
});
|
|
1695
|
+
}
|
|
1696
|
+
|
|
1697
|
+
/**
|
|
1698
|
+
* Reads `accept-language` out of the request envelope's `headers` map.
|
|
1699
|
+
*
|
|
1700
|
+
* The gateway stamps the negotiated locale there for auth flows (it persists
|
|
1701
|
+
* the start locale on the flow state so continue/poll turns stay in one
|
|
1702
|
+
* language), and the operation route merges HTTP headers into the same map, so
|
|
1703
|
+
* the envelope is the higher-precedence source.
|
|
1704
|
+
*/
|
|
1705
|
+
function requestBodyAcceptLanguage(rawBody: unknown): string | undefined {
|
|
1706
|
+
if (!rawBody || typeof rawBody !== "object") return undefined;
|
|
1707
|
+
const headers = Object.getOwnPropertyDescriptor(rawBody, "headers")?.value;
|
|
1708
|
+
if (!headers || typeof headers !== "object" || Array.isArray(headers)) return undefined;
|
|
1709
|
+
return acceptLanguageHeaderValue(headers as Record<string, unknown>);
|
|
1710
|
+
}
|
|
1711
|
+
|
|
1712
|
+
function errorEnvelopeLocalization(options: {
|
|
1713
|
+
readonly catalogs: ProviderLocaleCatalogMap | undefined;
|
|
1714
|
+
readonly requestHeaders?: Headers;
|
|
1715
|
+
readonly rawBody?: unknown;
|
|
1716
|
+
readonly declaredErrorCode?: OperationErrorCode;
|
|
1717
|
+
}): ErrorEnvelopeLocalization {
|
|
1718
|
+
return {
|
|
1719
|
+
...(options.catalogs ? { catalogs: options.catalogs } : {}),
|
|
1720
|
+
locale: resolveProviderErrorLocale(
|
|
1721
|
+
requestBodyAcceptLanguage(options.rawBody),
|
|
1722
|
+
options.requestHeaders?.get("accept-language"),
|
|
1723
|
+
),
|
|
1724
|
+
...(options.declaredErrorCode ? { declaredErrorCode: options.declaredErrorCode } : {}),
|
|
1725
|
+
};
|
|
1726
|
+
}
|
|
1727
|
+
|
|
1468
1728
|
function toErrorResponse(
|
|
1469
1729
|
error: unknown,
|
|
1470
1730
|
requestId: string | undefined,
|
|
1471
1731
|
observabilityDetails: ErrorObservabilityDetails,
|
|
1472
1732
|
redact?: DiagnosticRedactor,
|
|
1733
|
+
localization?: ErrorEnvelopeLocalization,
|
|
1473
1734
|
): OperationErrorResponse {
|
|
1474
1735
|
const observability = observabilityDetails;
|
|
1475
1736
|
const source = publicErrorSource(error, observability.category);
|
|
@@ -1487,14 +1748,23 @@ function toErrorResponse(
|
|
|
1487
1748
|
|
|
1488
1749
|
if (isProviderError(error)) {
|
|
1489
1750
|
const details = providerErrorOption(error, "details");
|
|
1751
|
+
const literalFix = providerErrorOption(error, "fix");
|
|
1752
|
+
const fix = localizedErrorField(
|
|
1753
|
+
error,
|
|
1754
|
+
"fix",
|
|
1755
|
+
typeof literalFix === "string" && literalFix.length > 0 ? literalFix : undefined,
|
|
1756
|
+
localization,
|
|
1757
|
+
);
|
|
1758
|
+
const literalMessage = publicProviderErrorMessage(error);
|
|
1490
1759
|
return {
|
|
1491
1760
|
error: {
|
|
1492
1761
|
code: providerErrorCode(error) ?? "provider_error",
|
|
1493
|
-
message:
|
|
1762
|
+
message:
|
|
1763
|
+
localizedErrorField(error, "message", literalMessage, localization) ?? literalMessage,
|
|
1494
1764
|
...(requestId ? { requestId } : {}),
|
|
1495
1765
|
retryable: observability.retryable,
|
|
1496
1766
|
source,
|
|
1497
|
-
...(
|
|
1767
|
+
...(fix ? { fix } : {}),
|
|
1498
1768
|
...(details !== undefined ? { details } : {}),
|
|
1499
1769
|
},
|
|
1500
1770
|
};
|
|
@@ -1761,6 +2031,11 @@ function publicProviderErrorMessage(error: ProviderError): string {
|
|
|
1761
2031
|
}
|
|
1762
2032
|
if (providerErrorCode(error) === "transport_timeout") return "Request timed out";
|
|
1763
2033
|
if (providerErrorCode(error) === "transport_network_error") return "Network error";
|
|
2034
|
+
// Provider-local fault: no request was issued, so an upstream-flavored
|
|
2035
|
+
// message would misattribute it (category is provider_error).
|
|
2036
|
+
if (providerErrorCode(error) === "http_header_factory_failed") {
|
|
2037
|
+
return "Request header preparation failed";
|
|
2038
|
+
}
|
|
1764
2039
|
if (providerErrorCode(error) === "upstream_http_error" && error.status) {
|
|
1765
2040
|
return `Upstream request failed with status ${error.status}`;
|
|
1766
2041
|
}
|
|
@@ -1931,6 +2206,11 @@ function logProviderError(
|
|
|
1931
2206
|
const errorClass = error instanceof Error ? error.name : typeof error;
|
|
1932
2207
|
const message = error instanceof Error ? error.message : String(error);
|
|
1933
2208
|
const causeChain = providerErrorCauseChain(error, redact);
|
|
2209
|
+
// Masked 500 bodies carry only errorClass, so the log is the sole channel
|
|
2210
|
+
// that can locate the throw. Declared 5xx (502/504) are included too: their
|
|
2211
|
+
// body carries code+message, but frame[0] is still the throw site inside
|
|
2212
|
+
// provider code. 4xx failures keep the lean record.
|
|
2213
|
+
const stack = status >= 500 ? providerErrorStackFrames(error, redact) : undefined;
|
|
1934
2214
|
const details = observabilityDetails;
|
|
1935
2215
|
const isUnregisteredProviderErrorCode =
|
|
1936
2216
|
status === 500 &&
|
|
@@ -1973,6 +2253,7 @@ function logProviderError(
|
|
|
1973
2253
|
errorClass: redactDiagnosticText(errorClass, redact),
|
|
1974
2254
|
message: sanitizeDiagnosticText(redactDiagnosticText(message, redact)),
|
|
1975
2255
|
...(causeChain ? { causeChain } : {}),
|
|
2256
|
+
...(stack ? { stack } : {}),
|
|
1976
2257
|
...(providerObservability ? { providerObservability } : {}),
|
|
1977
2258
|
...(details.upstreamStatus ? { upstreamStatus: details.upstreamStatus } : {}),
|
|
1978
2259
|
errorCategory: details.category,
|
|
@@ -2245,6 +2526,7 @@ function createRequestScope(input: {
|
|
|
2245
2526
|
),
|
|
2246
2527
|
finished: false,
|
|
2247
2528
|
staticValues: input.staticSensitiveValues,
|
|
2529
|
+
register: sensitiveRegistry.add,
|
|
2248
2530
|
};
|
|
2249
2531
|
const traceConfig = withDiagnosticEnv(observeEnv, () => resolveTraceConfigFromEnv());
|
|
2250
2532
|
const details = {
|
|
@@ -2276,10 +2558,24 @@ function createRequestScope(input: {
|
|
|
2276
2558
|
const proxyCollector = new ProxyTelemetryCollector();
|
|
2277
2559
|
const resolverCollector = new ResolverTelemetryCollector({ redact });
|
|
2278
2560
|
const nativeCollector = new NativeTelemetryCollector({ redact });
|
|
2561
|
+
const httpCollector = new HttpTelemetryCollector({ redact });
|
|
2562
|
+
const stealthCollector = new StealthTelemetryCollector({ redact });
|
|
2563
|
+
const ocrCollector = new OcrTelemetryCollector({ redact });
|
|
2564
|
+
const sttCollector = new SttTelemetryCollector({ redact });
|
|
2565
|
+
const browserCollector = new BrowserTelemetryCollector({ redact });
|
|
2566
|
+
const cacheTelemetry = new CacheTelemetryCollector({ redact });
|
|
2567
|
+
const stateTelemetry = new StateTelemetryCollector({ redact });
|
|
2279
2568
|
const telemetry = new RequestTelemetry(trace);
|
|
2280
2569
|
telemetry.register(proxyCollector);
|
|
2281
2570
|
telemetry.register(resolverCollector);
|
|
2282
2571
|
telemetry.register(nativeCollector);
|
|
2572
|
+
telemetry.register(httpCollector);
|
|
2573
|
+
telemetry.register(stealthCollector);
|
|
2574
|
+
telemetry.register(browserCollector);
|
|
2575
|
+
telemetry.register(ocrCollector);
|
|
2576
|
+
telemetry.register(sttCollector);
|
|
2577
|
+
telemetry.register(cacheTelemetry);
|
|
2578
|
+
telemetry.register(stateTelemetry);
|
|
2283
2579
|
let rootRunner: <T>(fn: () => Promise<T>) => Promise<T> = (fn) => fn();
|
|
2284
2580
|
let resolveRoot!: (outcome: RequestTerminalOutcome) => void;
|
|
2285
2581
|
const rootTerminal = new Promise<RequestTerminalOutcome>((resolve) => {
|
|
@@ -2362,6 +2658,13 @@ function createRequestScope(input: {
|
|
|
2362
2658
|
telemetry,
|
|
2363
2659
|
resolverTelemetry: resolverCollector,
|
|
2364
2660
|
nativeTelemetry: nativeCollector,
|
|
2661
|
+
httpTelemetry: httpCollector,
|
|
2662
|
+
stealthTelemetry: stealthCollector,
|
|
2663
|
+
ocrTelemetry: ocrCollector,
|
|
2664
|
+
sttTelemetry: sttCollector,
|
|
2665
|
+
browserTelemetry: browserCollector,
|
|
2666
|
+
cacheTelemetry,
|
|
2667
|
+
stateTelemetry,
|
|
2365
2668
|
seedCredentials(rawBody, kind): void {
|
|
2366
2669
|
const harvested = rawRequestCredentials(rawBody, kind);
|
|
2367
2670
|
sensitiveRegistry.add(harvested.values);
|
|
@@ -2581,21 +2884,13 @@ function toJsonSuccessResponse(
|
|
|
2581
2884
|
}
|
|
2582
2885
|
|
|
2583
2886
|
const cacheMeta = ctx && "cache" in ctx ? ctx.cache.responseMeta() : undefined;
|
|
2584
|
-
const
|
|
2887
|
+
const retry = ctx ? retryResponseMeta.get(ctx)?.toTenantRetryPayload() : undefined;
|
|
2585
2888
|
type TenantCacheMeta = {
|
|
2586
2889
|
hit: boolean;
|
|
2587
2890
|
stale: boolean;
|
|
2588
2891
|
keys: TenantOpaqueKeys;
|
|
2589
2892
|
source?: ClosedEnum<NonNullable<ProviderCacheResponseMeta["source"]>>;
|
|
2590
2893
|
};
|
|
2591
|
-
type TenantRetryMeta = {
|
|
2592
|
-
attempts: number;
|
|
2593
|
-
retries: number;
|
|
2594
|
-
preset?: ClosedEnum<NonNullable<HttpRetrySummary["preset"]>>;
|
|
2595
|
-
transport: ClosedEnum<HttpRetrySummary["transport"]>;
|
|
2596
|
-
lastErrorCode?: ClosedEnum<RetryLastErrorCode>;
|
|
2597
|
-
lastStatus?: number;
|
|
2598
|
-
};
|
|
2599
2894
|
const cache: TenantNeutral<TenantCacheMeta> | undefined = cacheMeta
|
|
2600
2895
|
? {
|
|
2601
2896
|
hit: cacheMeta.hit,
|
|
@@ -2605,22 +2900,8 @@ function toJsonSuccessResponse(
|
|
|
2605
2900
|
...(cacheMeta.source ? { source: closedEnum(cacheMeta.source) } : {}),
|
|
2606
2901
|
}
|
|
2607
2902
|
: undefined;
|
|
2608
|
-
const retry: TenantNeutral<TenantRetryMeta> | undefined = retryMeta
|
|
2609
|
-
? {
|
|
2610
|
-
attempts: retryMeta.attempts,
|
|
2611
|
-
retries: retryMeta.retries,
|
|
2612
|
-
...(retryMeta.preset ? { preset: closedEnum(retryMeta.preset) } : {}),
|
|
2613
|
-
transport: closedEnum(retryMeta.transport),
|
|
2614
|
-
...(retryMeta.lastErrorCode
|
|
2615
|
-
? {
|
|
2616
|
-
lastErrorCode: closedEnum(tenantRetryLastErrorCode(retryMeta.lastErrorCode)),
|
|
2617
|
-
}
|
|
2618
|
-
: {}),
|
|
2619
|
-
...(retryMeta.lastStatus ? { lastStatus: retryMeta.lastStatus } : {}),
|
|
2620
|
-
}
|
|
2621
|
-
: undefined;
|
|
2622
2903
|
const meta =
|
|
2623
|
-
cacheMeta ||
|
|
2904
|
+
cacheMeta || retry
|
|
2624
2905
|
? {
|
|
2625
2906
|
...(cache
|
|
2626
2907
|
? {
|
|
@@ -2942,42 +3223,44 @@ function toAuthFlowResponse(
|
|
|
2942
3223
|
};
|
|
2943
3224
|
}
|
|
2944
3225
|
|
|
2945
|
-
function authFlowLocaleFromHeaders(headers?: Record<string, string>): ProviderLocale {
|
|
2946
|
-
const header = Object.entries(headers ?? {}).find(
|
|
2947
|
-
([key]) => key.toLowerCase() === "accept-language",
|
|
2948
|
-
)?.[1];
|
|
2949
|
-
for (const token of (header ?? "").split(",")) {
|
|
2950
|
-
const language = token.trim().split(";")[0]?.split("-")[0]?.toLowerCase();
|
|
2951
|
-
if (isAuthFlowLocale(language)) {
|
|
2952
|
-
return language;
|
|
2953
|
-
}
|
|
2954
|
-
}
|
|
2955
|
-
return "en";
|
|
2956
|
-
}
|
|
2957
|
-
|
|
2958
|
-
function isAuthFlowLocale(value: string | undefined): value is ProviderLocale {
|
|
2959
|
-
return value === "en" || value === "ko" || value === "ja";
|
|
2960
|
-
}
|
|
2961
|
-
|
|
2962
3226
|
function isAuthTurn(value: unknown): value is AuthTurn {
|
|
2963
3227
|
return !!value && typeof value === "object" && "kind" in value && "turnId" in value;
|
|
2964
3228
|
}
|
|
2965
3229
|
|
|
2966
|
-
|
|
3230
|
+
/**
|
|
3231
|
+
* Loads the provider's locale catalogs once, at `createServerApp` time.
|
|
3232
|
+
*
|
|
3233
|
+
* Only the locales that actually exist on disk are read: a provider with
|
|
3234
|
+
* `en`+`ko` but no `ja.json` used to make the whole load throw and silently
|
|
3235
|
+
* disable localization for every turn. Absence of catalogs is not an error —
|
|
3236
|
+
* every localized field falls back to its English literal — but a catalog that
|
|
3237
|
+
* exists and cannot be read is logged once so a packaging mistake is visible at
|
|
3238
|
+
* boot instead of per request.
|
|
3239
|
+
*/
|
|
3240
|
+
function resolveProviderLocaleCatalogs(
|
|
2967
3241
|
provider: ProviderDefinition,
|
|
3242
|
+
options: ProviderServerOptions,
|
|
3243
|
+
logger: ProviderServerLogger,
|
|
2968
3244
|
): ProviderLocaleCatalogMap | undefined {
|
|
3245
|
+
if (options.localeCatalogs) return options.localeCatalogs;
|
|
2969
3246
|
for (const providerDir of [
|
|
2970
3247
|
process.cwd(),
|
|
2971
3248
|
join(process.cwd(), "providers", provider.id),
|
|
2972
3249
|
join(process.cwd(), "providers-staging", provider.id),
|
|
2973
3250
|
]) {
|
|
2974
|
-
|
|
3251
|
+
const locales = PROVIDER_ERROR_LOCALES.filter((locale) =>
|
|
3252
|
+
existsSync(join(providerDir, "locales", `${locale}.json`)),
|
|
3253
|
+
);
|
|
3254
|
+
if (!locales.includes(DEFAULT_PROVIDER_ERROR_LOCALE)) continue;
|
|
2975
3255
|
try {
|
|
2976
|
-
return loadProviderLocaleCatalogs({
|
|
2977
|
-
|
|
2978
|
-
|
|
3256
|
+
return loadProviderLocaleCatalogs({ providerDir, locales });
|
|
3257
|
+
} catch (error) {
|
|
3258
|
+
logger({
|
|
3259
|
+
level: "warn",
|
|
3260
|
+
event: "provider_locale_catalogs_unavailable",
|
|
3261
|
+
providerId: provider.id,
|
|
3262
|
+
reason: sanitizeDiagnosticText(error instanceof Error ? error.message : String(error)),
|
|
2979
3263
|
});
|
|
2980
|
-
} catch {
|
|
2981
3264
|
return undefined;
|
|
2982
3265
|
}
|
|
2983
3266
|
}
|
|
@@ -2985,16 +3268,12 @@ function loadAuthFlowLocaleCatalogs(
|
|
|
2985
3268
|
}
|
|
2986
3269
|
|
|
2987
3270
|
function materializeAuthFlowTurn(
|
|
2988
|
-
provider: ProviderDefinition,
|
|
2989
|
-
request: AuthFlowRequest,
|
|
2990
3271
|
turn: AuthTurn,
|
|
3272
|
+
catalogs: ProviderLocaleCatalogMap | undefined,
|
|
3273
|
+
locale: ProviderLocale,
|
|
2991
3274
|
): AuthTurn {
|
|
2992
|
-
const catalogs = loadAuthFlowLocaleCatalogs(provider);
|
|
2993
3275
|
if (!catalogs) return turn;
|
|
2994
|
-
return localizeAuthTurn(turn, {
|
|
2995
|
-
catalogs,
|
|
2996
|
-
locale: authFlowLocaleFromHeaders(request.headers),
|
|
2997
|
-
});
|
|
3276
|
+
return localizeAuthTurn(turn, { catalogs, locale });
|
|
2998
3277
|
}
|
|
2999
3278
|
|
|
3000
3279
|
function withAuthRequestHeaders(request: AuthFlowRequest, headers: Headers): AuthFlowRequest {
|
|
@@ -3105,6 +3384,14 @@ async function handleAuthFlow(
|
|
|
3105
3384
|
options: ProviderServerRuntimeOptions,
|
|
3106
3385
|
state: ProviderRuntimeState,
|
|
3107
3386
|
scope: RequestScopeContext,
|
|
3387
|
+
/**
|
|
3388
|
+
* Negotiated once per request by the route so a successful turn and an error
|
|
3389
|
+
* envelope from the same request can never disagree. `withAuthRequestHeaders`
|
|
3390
|
+
* lets the HTTP header shadow the envelope's `Accept-Language`, but the
|
|
3391
|
+
* gateway stamps the flow's start locale into the envelope precisely so
|
|
3392
|
+
* continue/poll turns stay in one language, so the envelope wins here.
|
|
3393
|
+
*/
|
|
3394
|
+
locale: ProviderLocale,
|
|
3108
3395
|
signal?: AbortSignal,
|
|
3109
3396
|
): Promise<Response | AuthFlowResponse> {
|
|
3110
3397
|
const flow = provider.auth?.flow;
|
|
@@ -3161,7 +3448,7 @@ async function handleAuthFlow(
|
|
|
3161
3448
|
!(result instanceof Response) &&
|
|
3162
3449
|
!(result instanceof ReadableStream) &&
|
|
3163
3450
|
isAuthTurn(result)
|
|
3164
|
-
? materializeAuthFlowTurn(
|
|
3451
|
+
? materializeAuthFlowTurn(result, options.localeCatalogs, locale)
|
|
3165
3452
|
: result;
|
|
3166
3453
|
return toAuthFlowResponse(materializedResult, getPatch(), getEngineState());
|
|
3167
3454
|
} catch (error) {
|
|
@@ -3307,7 +3594,7 @@ function parseStatefulForwardingEnvelope(
|
|
|
3307
3594
|
rawBody: unknown,
|
|
3308
3595
|
redact: DiagnosticRedactor,
|
|
3309
3596
|
): ProviderServerStatefulForwardEnvelope {
|
|
3310
|
-
const parsed =
|
|
3597
|
+
const parsed = ReceivedStatefulForwardEnvelopeSchema.safeParse(rawBody);
|
|
3311
3598
|
if (parsed.success) return parsed.data;
|
|
3312
3599
|
throw new ProviderError("Stateful forwarding envelope is invalid.", {
|
|
3313
3600
|
code: "STATEFUL_FORWARDING_ENVELOPE_INVALID",
|
|
@@ -3357,16 +3644,78 @@ export function createServerApp<TContext extends Partial<ProviderContext> = Prov
|
|
|
3357
3644
|
);
|
|
3358
3645
|
}
|
|
3359
3646
|
|
|
3647
|
+
/**
|
|
3648
|
+
* Resolve the engine once at boot, before any route exists. An explicit engine
|
|
3649
|
+
* object is used as given; otherwise the resolved mode selects the engine.
|
|
3650
|
+
*
|
|
3651
|
+
* Deliberately never throws for deployment-authored input: a mode this release
|
|
3652
|
+
* cannot serve yields an engine that fails every request and a `/readyz` 503,
|
|
3653
|
+
* because a boot crash would trade a structured signal for CrashLoopBackOff
|
|
3654
|
+
* across the fleet (same trade-off as `provider_secrets_missing` above).
|
|
3655
|
+
*/
|
|
3656
|
+
function resolveServerEngine(serverOptions: ProviderServerOptions): {
|
|
3657
|
+
engine: ProviderEngine;
|
|
3658
|
+
resolution: ProviderEngineModeResolution;
|
|
3659
|
+
} {
|
|
3660
|
+
const resolution = resolveProviderEngineMode({
|
|
3661
|
+
option: serverOptions.engineMode,
|
|
3662
|
+
engine: serverOptions.engine,
|
|
3663
|
+
});
|
|
3664
|
+
if (serverOptions.engine !== undefined) return { engine: serverOptions.engine, resolution };
|
|
3665
|
+
return { engine: createEngineForMode(resolution.mode), resolution };
|
|
3666
|
+
}
|
|
3667
|
+
|
|
3668
|
+
/**
|
|
3669
|
+
* `engine_env_ignored` is expected for the whole migration window: the manifest
|
|
3670
|
+
* generator projects the remote engine env fleet-wide before any mode flips, so
|
|
3671
|
+
* escalating on it would turn every healthy boot into a warn line. The
|
|
3672
|
+
* disagreement warnings still escalate.
|
|
3673
|
+
*/
|
|
3674
|
+
const ENGINE_MODE_INFO_WARNINGS: ReadonlySet<string> = new Set(["engine_env_ignored"]);
|
|
3675
|
+
|
|
3676
|
+
function engineModeLogEvent(
|
|
3677
|
+
provider: ProviderDefinition,
|
|
3678
|
+
resolution: ProviderEngineModeResolution,
|
|
3679
|
+
attached: boolean,
|
|
3680
|
+
): ProviderServerLogEvent {
|
|
3681
|
+
const escalating = resolution.warnings.filter(
|
|
3682
|
+
(warning) => !ENGINE_MODE_INFO_WARNINGS.has(warning),
|
|
3683
|
+
);
|
|
3684
|
+
return {
|
|
3685
|
+
level: attached && escalating.length === 0 ? "info" : "warn",
|
|
3686
|
+
event: "provider_engine_mode",
|
|
3687
|
+
providerId: provider.id,
|
|
3688
|
+
mode: resolution.mode,
|
|
3689
|
+
source: resolution.source,
|
|
3690
|
+
deprecated: resolution.deprecated,
|
|
3691
|
+
attached,
|
|
3692
|
+
sdkVersion: SDK_VERSION,
|
|
3693
|
+
...(provider.runtimeTarget === undefined ? {} : { runtimeTarget: provider.runtimeTarget }),
|
|
3694
|
+
...(resolution.warnings.length === 0 ? {} : { warnings: resolution.warnings }),
|
|
3695
|
+
};
|
|
3696
|
+
}
|
|
3697
|
+
|
|
3360
3698
|
function createServerAppWithCapabilityModules(
|
|
3361
3699
|
provider: ProviderDefinition,
|
|
3362
3700
|
serverOptions: ProviderServerOptions,
|
|
3363
3701
|
capabilityModules: ProviderCapabilityModules,
|
|
3364
3702
|
): Hono {
|
|
3703
|
+
const engineSelection = resolveServerEngine(serverOptions);
|
|
3704
|
+
// Read the provider's locale catalogs once here rather than per turn: the
|
|
3705
|
+
// previous auth-flow loader re-read all three JSON files on every turn and
|
|
3706
|
+
// threw the whole map away when one locale file was absent.
|
|
3707
|
+
const localeCatalogs = resolveProviderLocaleCatalogs(
|
|
3708
|
+
provider,
|
|
3709
|
+
serverOptions,
|
|
3710
|
+
serverOptions.logger ?? defaultProviderServerLogger,
|
|
3711
|
+
);
|
|
3365
3712
|
const options: ProviderServerRuntimeOptions = {
|
|
3366
3713
|
...serverOptions,
|
|
3714
|
+
...(localeCatalogs ? { localeCatalogs } : {}),
|
|
3367
3715
|
capabilityModules,
|
|
3368
|
-
engine:
|
|
3716
|
+
engine: engineSelection.engine,
|
|
3369
3717
|
};
|
|
3718
|
+
const engineAttached = !isUnavailableProviderEngine(options.engine);
|
|
3370
3719
|
const app = new Hono();
|
|
3371
3720
|
// Compile the startup inventory once, composed with the shared outside-context fallback.
|
|
3372
3721
|
const staticSensitiveValues = compileProcessDiagnosticSensitiveValues(
|
|
@@ -3409,11 +3758,27 @@ function createServerAppWithCapabilityModules(
|
|
|
3409
3758
|
const error = new ProviderError("Not found", { code: "not_found", retryable: false });
|
|
3410
3759
|
const observabilityDetails = errorObservabilityDetails(error);
|
|
3411
3760
|
return responseWithErrorObservability(
|
|
3412
|
-
c.json(
|
|
3761
|
+
c.json(
|
|
3762
|
+
toErrorResponse(
|
|
3763
|
+
error,
|
|
3764
|
+
undefined,
|
|
3765
|
+
observabilityDetails,
|
|
3766
|
+
undefined,
|
|
3767
|
+
errorEnvelopeLocalization({
|
|
3768
|
+
catalogs: localeCatalogs,
|
|
3769
|
+
requestHeaders: c.req.raw.headers,
|
|
3770
|
+
}),
|
|
3771
|
+
),
|
|
3772
|
+
404,
|
|
3773
|
+
),
|
|
3413
3774
|
observabilityDetails,
|
|
3414
3775
|
);
|
|
3415
3776
|
});
|
|
3416
3777
|
|
|
3778
|
+
// Liveness. Deliberately static and engine-blind: generated manifests point
|
|
3779
|
+
// liveness at this route, so making it depend on engine reachability would
|
|
3780
|
+
// restart every provider pod on one engine incident. Engine state belongs to
|
|
3781
|
+
// /readyz, which manifests wire to readiness and startup only.
|
|
3417
3782
|
app.get("/health", (c) =>
|
|
3418
3783
|
c.json({
|
|
3419
3784
|
status: "ok",
|
|
@@ -3422,6 +3787,25 @@ function createServerAppWithCapabilityModules(
|
|
|
3422
3787
|
}),
|
|
3423
3788
|
);
|
|
3424
3789
|
|
|
3790
|
+
// Readiness. Reports the boot-resolved attachment; does not probe the engine
|
|
3791
|
+
// per call, so it cannot amplify an engine incident into request load.
|
|
3792
|
+
app.get("/readyz", (c) =>
|
|
3793
|
+
c.json(
|
|
3794
|
+
{
|
|
3795
|
+
status: engineAttached ? "ok" : "unavailable",
|
|
3796
|
+
provider: provider.id,
|
|
3797
|
+
version: provider.version,
|
|
3798
|
+
engine: {
|
|
3799
|
+
// The resolution, not the raw engine `kind`: normalized, so this
|
|
3800
|
+
// body and the `provider_engine_mode` boot event never disagree.
|
|
3801
|
+
mode: engineSelection.resolution.mode,
|
|
3802
|
+
attached: engineAttached,
|
|
3803
|
+
},
|
|
3804
|
+
},
|
|
3805
|
+
engineAttached ? 200 : 503,
|
|
3806
|
+
),
|
|
3807
|
+
);
|
|
3808
|
+
|
|
3425
3809
|
app.post(STATEFUL_INTERNAL_OPERATIONS_ROUTE, async (c) => {
|
|
3426
3810
|
let rawBodyText = "";
|
|
3427
3811
|
let rawBody: unknown;
|
|
@@ -3552,7 +3936,10 @@ function createServerAppWithCapabilityModules(
|
|
|
3552
3936
|
requestId: request.requestId,
|
|
3553
3937
|
operationId,
|
|
3554
3938
|
headers: request.headers ?? {},
|
|
3555
|
-
correlation: {
|
|
3939
|
+
correlation: {
|
|
3940
|
+
connectionId: envelope.connectionId,
|
|
3941
|
+
tenantId: resolveOperationTenantId(request),
|
|
3942
|
+
},
|
|
3556
3943
|
});
|
|
3557
3944
|
const response = await requestScope.run(async () => {
|
|
3558
3945
|
const ctx = createProviderContext(
|
|
@@ -3597,7 +3984,18 @@ function createServerAppWithCapabilityModules(
|
|
|
3597
3984
|
});
|
|
3598
3985
|
const observabilityDetails = errorObservabilityDetails(error, declaredErrorCode);
|
|
3599
3986
|
const response = c.json(
|
|
3600
|
-
toErrorResponse(
|
|
3987
|
+
toErrorResponse(
|
|
3988
|
+
error,
|
|
3989
|
+
requestId,
|
|
3990
|
+
observabilityDetails,
|
|
3991
|
+
requestScope.redact,
|
|
3992
|
+
errorEnvelopeLocalization({
|
|
3993
|
+
catalogs: localeCatalogs,
|
|
3994
|
+
requestHeaders: c.req.raw.headers,
|
|
3995
|
+
rawBody,
|
|
3996
|
+
...(declaredErrorCode ? { declaredErrorCode } : {}),
|
|
3997
|
+
}),
|
|
3998
|
+
),
|
|
3601
3999
|
status,
|
|
3602
4000
|
);
|
|
3603
4001
|
return finalizeRequestResponse(requestScope, response, {
|
|
@@ -3634,7 +4032,10 @@ function createServerAppWithCapabilityModules(
|
|
|
3634
4032
|
requestScope.enrich({
|
|
3635
4033
|
requestId: body.requestId,
|
|
3636
4034
|
headers: body.headers,
|
|
3637
|
-
correlation: {
|
|
4035
|
+
correlation: {
|
|
4036
|
+
connectionId: resolveOperationConnectionId(body),
|
|
4037
|
+
tenantId: resolveOperationTenantId(body),
|
|
4038
|
+
},
|
|
3638
4039
|
});
|
|
3639
4040
|
const streaming = provider.operations[operation]?.transport?.kind
|
|
3640
4041
|
? provider.operations[operation]?.transport?.kind !== "json"
|
|
@@ -3675,7 +4076,18 @@ function createServerAppWithCapabilityModules(
|
|
|
3675
4076
|
requestScope.enrich({ ...(requestId ? { requestId } : {}) });
|
|
3676
4077
|
const observabilityDetails = errorObservabilityDetails(error, declaredErrorCode);
|
|
3677
4078
|
const response = c.json(
|
|
3678
|
-
toErrorResponse(
|
|
4079
|
+
toErrorResponse(
|
|
4080
|
+
error,
|
|
4081
|
+
requestId,
|
|
4082
|
+
observabilityDetails,
|
|
4083
|
+
requestScope.redact,
|
|
4084
|
+
errorEnvelopeLocalization({
|
|
4085
|
+
catalogs: localeCatalogs,
|
|
4086
|
+
requestHeaders: c.req.raw.headers,
|
|
4087
|
+
rawBody,
|
|
4088
|
+
...(declaredErrorCode ? { declaredErrorCode } : {}),
|
|
4089
|
+
}),
|
|
4090
|
+
),
|
|
3679
4091
|
status,
|
|
3680
4092
|
);
|
|
3681
4093
|
return finalizeRequestResponse(requestScope, response, {
|
|
@@ -3701,6 +4113,12 @@ function createServerAppWithCapabilityModules(
|
|
|
3701
4113
|
for (const { path, flowRoute, logRoute } of authRoutes) {
|
|
3702
4114
|
app.post(path, async (c) => {
|
|
3703
4115
|
let rawBody: unknown;
|
|
4116
|
+
// Negotiated once and shared by the success and failure paths so an
|
|
4117
|
+
// auth turn and an error envelope from the same request agree.
|
|
4118
|
+
let localization = errorEnvelopeLocalization({
|
|
4119
|
+
catalogs: localeCatalogs,
|
|
4120
|
+
requestHeaders: c.req.raw.headers,
|
|
4121
|
+
});
|
|
3704
4122
|
const requestScope = createRequestScope({
|
|
3705
4123
|
provider,
|
|
3706
4124
|
staticSensitiveValues,
|
|
@@ -3715,6 +4133,11 @@ function createServerAppWithCapabilityModules(
|
|
|
3715
4133
|
.clone()
|
|
3716
4134
|
.json()
|
|
3717
4135
|
.catch(() => undefined);
|
|
4136
|
+
localization = errorEnvelopeLocalization({
|
|
4137
|
+
catalogs: localeCatalogs,
|
|
4138
|
+
requestHeaders: c.req.raw.headers,
|
|
4139
|
+
rawBody,
|
|
4140
|
+
});
|
|
3718
4141
|
requestScope.seedCredentials(rawBody, "auth");
|
|
3719
4142
|
const body = withAuthRequestHeaders(
|
|
3720
4143
|
AuthFlowRequestSchema.parse(rawBody),
|
|
@@ -3727,7 +4150,7 @@ function createServerAppWithCapabilityModules(
|
|
|
3727
4150
|
correlation: {
|
|
3728
4151
|
connectionId: resolveOperationConnectionId(body),
|
|
3729
4152
|
flowId: body.flowId,
|
|
3730
|
-
tenantId: body.tenantId,
|
|
4153
|
+
tenantId: normalizeIdentifier(body.tenantId),
|
|
3731
4154
|
requestedProviderId:
|
|
3732
4155
|
body.providerId !== undefined && body.providerId !== provider.id
|
|
3733
4156
|
? body.providerId
|
|
@@ -3742,6 +4165,7 @@ function createServerAppWithCapabilityModules(
|
|
|
3742
4165
|
options,
|
|
3743
4166
|
state,
|
|
3744
4167
|
requestScope as RequestScope,
|
|
4168
|
+
localization.locale,
|
|
3745
4169
|
c.req.raw.signal,
|
|
3746
4170
|
);
|
|
3747
4171
|
return handled instanceof Response ? handled : c.json(handled);
|
|
@@ -3756,7 +4180,13 @@ function createServerAppWithCapabilityModules(
|
|
|
3756
4180
|
requestScope.enrich({ ...(requestId ? { requestId } : {}) });
|
|
3757
4181
|
const observabilityDetails = errorObservabilityDetails(error);
|
|
3758
4182
|
const response = c.json(
|
|
3759
|
-
toErrorResponse(
|
|
4183
|
+
toErrorResponse(
|
|
4184
|
+
error,
|
|
4185
|
+
requestId,
|
|
4186
|
+
observabilityDetails,
|
|
4187
|
+
requestScope.redact,
|
|
4188
|
+
localization,
|
|
4189
|
+
),
|
|
3760
4190
|
status,
|
|
3761
4191
|
);
|
|
3762
4192
|
return finalizeRequestResponse(requestScope, response, {
|
|
@@ -3893,13 +4323,18 @@ export async function serve<TContext extends Partial<ProviderContext> = Provider
|
|
|
3893
4323
|
);
|
|
3894
4324
|
const configuredSignals = resolveShutdownSignals(options.shutdown?.signals ?? true);
|
|
3895
4325
|
const selfTestSecrets = resolveSelfTestMasterSecrets();
|
|
4326
|
+
// Resolve the engine here so the boot event reports the truthful source
|
|
4327
|
+
// (option/env/default) and the app is handed the engine already selected —
|
|
4328
|
+
// one resolution per process, not one per app construction.
|
|
4329
|
+
const engineSelection = resolveServerEngine(options as unknown as ProviderServerOptions);
|
|
3896
4330
|
const serverAppOptions: ProviderServerOptions<TContext> = {
|
|
3897
4331
|
logger: options.logger,
|
|
3898
|
-
engine:
|
|
4332
|
+
engine: engineSelection.engine,
|
|
3899
4333
|
files: options.files,
|
|
3900
4334
|
ocr: options.ocr,
|
|
3901
4335
|
stt: options.stt,
|
|
3902
4336
|
resolver: options.resolver,
|
|
4337
|
+
localeCatalogs: options.localeCatalogs,
|
|
3903
4338
|
state: options.state,
|
|
3904
4339
|
allowMemoryStateFallback: options.allowMemoryStateFallback,
|
|
3905
4340
|
operationExecutor: options.operationExecutor,
|
|
@@ -3910,6 +4345,15 @@ export async function serve<TContext extends Partial<ProviderContext> = Provider
|
|
|
3910
4345
|
createServerAppAsync(provider, serverAppOptions),
|
|
3911
4346
|
selfTestSecrets ? import("./self-test.js") : undefined,
|
|
3912
4347
|
]);
|
|
4348
|
+
// One structured line per process boot, before the listener binds: the fleet
|
|
4349
|
+
// audit (ADR-0011 migration) counts these.
|
|
4350
|
+
logger(
|
|
4351
|
+
engineModeLogEvent(
|
|
4352
|
+
provider as unknown as ProviderDefinition,
|
|
4353
|
+
engineSelection.resolution,
|
|
4354
|
+
!isUnavailableProviderEngine(engineSelection.engine),
|
|
4355
|
+
),
|
|
4356
|
+
);
|
|
3913
4357
|
|
|
3914
4358
|
const servers: BunServerHandle[] = [];
|
|
3915
4359
|
try {
|