@apifuse/provider-sdk 2.2.0-beta.59 → 2.2.0-beta.60
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 +16 -0
- package/CHANGELOG.md +4 -0
- package/bin/apifuse-dev.ts +2 -1
- package/bin/apifuse-perf.ts +41 -4
- package/bin/apifuse-record.ts +3 -2
- package/dist/config/loader.js +13 -12
- package/dist/engine.js +3 -2
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -1
- package/dist/lint.js +36 -15
- package/dist/provider.d.ts +1 -1
- package/dist/provider.js +1 -1
- package/dist/runtime/browser.js +2 -1
- package/dist/runtime/cache.js +3 -2
- package/dist/runtime/diagnostic-env-values.d.ts +5 -0
- package/dist/runtime/diagnostic-env-values.js +57 -0
- package/dist/runtime/diagnostic-env.d.ts +11 -0
- package/dist/runtime/diagnostic-env.js +35 -0
- package/dist/runtime/diagnostic-redactor.d.ts +49 -0
- package/dist/runtime/diagnostic-redactor.js +569 -0
- package/dist/runtime/egress-lease.d.ts +2 -0
- package/dist/runtime/egress-lease.js +4 -1
- package/dist/runtime/env.js +2 -1
- package/dist/runtime/instrumentation.js +3 -0
- package/dist/runtime/ocr.js +2 -1
- package/dist/runtime/otlp.js +5 -4
- package/dist/runtime/proxy-nodemaven.js +3 -2
- package/dist/runtime/request-options.d.ts +7 -0
- package/dist/runtime/request-options.js +62 -22
- package/dist/runtime/resolver-vendors/browser.js +0 -1
- package/dist/runtime/resolver.js +3 -2
- package/dist/runtime/stt.js +3 -2
- package/dist/runtime/trace.d.ts +5 -0
- package/dist/runtime/trace.js +115 -20
- package/dist/schema.d.ts +11 -0
- package/dist/schema.js +56 -2
- package/dist/server/self-test-redaction.js +8 -33
- package/dist/server/sensitive-values.d.ts +26 -0
- package/dist/server/sensitive-values.js +131 -0
- package/dist/server/serve-implementation.js +219 -78
- package/dist/server/trace-output.d.ts +2 -1
- package/dist/server/trace-output.js +46 -14
- package/dist/trace-sanitization.d.ts +4 -3
- package/dist/trace-sanitization.js +45 -22
- package/package.json +2 -1
- package/src/config/loader.ts +13 -12
- package/src/engine.ts +3 -2
- package/src/index.ts +2 -0
- package/src/lint.ts +42 -16
- package/src/provider.ts +1 -0
- package/src/runtime/browser.ts +2 -1
- package/src/runtime/cache.ts +3 -2
- package/src/runtime/diagnostic-env-values.ts +56 -0
- package/src/runtime/diagnostic-env.ts +55 -0
- package/src/runtime/diagnostic-redactor.ts +662 -0
- package/src/runtime/egress-lease.ts +6 -1
- package/src/runtime/env.ts +2 -1
- package/src/runtime/instrumentation.ts +3 -0
- package/src/runtime/ocr.ts +2 -1
- package/src/runtime/otlp.ts +5 -4
- package/src/runtime/proxy-nodemaven.ts +3 -2
- package/src/runtime/request-options.ts +76 -28
- package/src/runtime/resolver-vendors/browser.ts +0 -1
- package/src/runtime/resolver.ts +3 -2
- package/src/runtime/stt.ts +3 -2
- package/src/runtime/trace.ts +168 -26
- package/src/schema.ts +63 -2
- package/src/server/self-test-redaction.ts +10 -40
- package/src/server/sensitive-values.ts +200 -0
- package/src/server/serve-implementation.ts +301 -96
- package/src/server/trace-output.ts +77 -17
- package/src/trace-sanitization.ts +70 -23
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { STATUS_CODES } from "node:http";
|
|
2
|
+
import { readDiagnosticEnv, withDiagnosticEnv } from "../runtime/diagnostic-env.js";
|
|
2
3
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
4
|
+
import { createHash } from "node:crypto";
|
|
3
5
|
import { existsSync } from "node:fs";
|
|
4
6
|
import { createRequire } from "node:module";
|
|
5
7
|
import { join } from "node:path";
|
|
@@ -8,15 +10,15 @@ import { Hono } from "hono";
|
|
|
8
10
|
import { z } from "zod";
|
|
9
11
|
import { AuthAbortError, createAuthFlowHelpers } from "../auth.js";
|
|
10
12
|
import { validateFailClosedDeclaration } from "../declaration-validation.js";
|
|
11
|
-
import { safeProviderErrorObservability } from "../error-observability.js";
|
|
12
13
|
import {
|
|
13
14
|
createInProcessProviderEngine,
|
|
14
15
|
ENGINE_OWNED_PROXY_CREDENTIAL_ENV_NAMES,
|
|
15
16
|
isEngineOwnedEnvName,
|
|
16
|
-
readEngineProxyCredentials,
|
|
17
17
|
type ProviderEngine,
|
|
18
18
|
type ProviderEngineBindingCandidates,
|
|
19
|
+
readEngineProxyCredentials,
|
|
19
20
|
} from "../engine.js";
|
|
21
|
+
import { safeProviderErrorObservability } from "../error-observability.js";
|
|
20
22
|
import {
|
|
21
23
|
SDK_OWNED_PROVIDER_ERROR_CODES,
|
|
22
24
|
SDK_RUNTIME_OWNED_ERROR_CODES,
|
|
@@ -42,11 +44,11 @@ import {
|
|
|
42
44
|
import type { ProviderLocale } from "../i18n/keys.js";
|
|
43
45
|
import {
|
|
44
46
|
categoryForStatus,
|
|
45
|
-
type ProviderErrorSource,
|
|
46
|
-
sourceForCategory,
|
|
47
47
|
isRetryableCategory,
|
|
48
48
|
PROVIDER_OBSERVABILITY_TAXONOMY_VERSION,
|
|
49
49
|
type ProviderErrorCategory,
|
|
50
|
+
type ProviderErrorSource,
|
|
51
|
+
sourceForCategory,
|
|
50
52
|
} from "../observability.js";
|
|
51
53
|
import { createScratchpad } from "../runtime/auth-flow.js";
|
|
52
54
|
import type * as BrowserRuntimeModule from "../runtime/browser.js";
|
|
@@ -56,6 +58,19 @@ import {
|
|
|
56
58
|
PROVIDER_RUNTIME_CHOICE_TOKEN_MASTER_SECRET_ENV,
|
|
57
59
|
} from "../runtime/choice.js";
|
|
58
60
|
import { createCredentialContext } from "../runtime/credential.js";
|
|
61
|
+
import {
|
|
62
|
+
bindDiagnosticSensitiveRegistry,
|
|
63
|
+
type CompiledDiagnosticSensitiveValues,
|
|
64
|
+
compileProcessDiagnosticSensitiveValues,
|
|
65
|
+
createDiagnosticRedactor,
|
|
66
|
+
type DiagnosticRedactor,
|
|
67
|
+
REDACTION_FAILED,
|
|
68
|
+
isDiagnosticRedactionSuppressed,
|
|
69
|
+
bindCriticalDiagnosticRedactor,
|
|
70
|
+
redactCriticalDiagnosticText,
|
|
71
|
+
redactDiagnosticText,
|
|
72
|
+
registerDiagnosticSensitiveValues,
|
|
73
|
+
} from "../runtime/diagnostic-redactor.js";
|
|
59
74
|
import {
|
|
60
75
|
createCeremonyEgressLeaseRuntime,
|
|
61
76
|
ENGINE_CEREMONY_EGRESS_LEASE,
|
|
@@ -65,8 +80,8 @@ import { executeOperation } from "../runtime/executor.js";
|
|
|
65
80
|
import { createHttpClient } from "../runtime/http.js";
|
|
66
81
|
import { wrapWithInstrumentation } from "../runtime/instrumentation.js";
|
|
67
82
|
import type * as NativeNetworkRuntimeModule from "../runtime/native-network.js";
|
|
68
|
-
import { getProviderBaseUrl } from "../runtime/provider.js";
|
|
69
83
|
import { createOcrClientFromEnv } from "../runtime/ocr.js";
|
|
84
|
+
import { getProviderBaseUrl } from "../runtime/provider.js";
|
|
70
85
|
import {
|
|
71
86
|
PROXY_AUTH_IP_DENIED_CODE,
|
|
72
87
|
PROXY_EDGE_AUTH_REJECTED_CODE,
|
|
@@ -78,24 +93,24 @@ import {
|
|
|
78
93
|
type ProxyTelemetryLogPayload,
|
|
79
94
|
} from "../runtime/proxy-telemetry.js";
|
|
80
95
|
import {
|
|
96
|
+
type ClosedEnum,
|
|
81
97
|
closedEnum,
|
|
82
98
|
RequestTelemetry,
|
|
83
|
-
tenantOpaqueKeys,
|
|
84
|
-
type ClosedEnum,
|
|
85
99
|
type TenantNeutral,
|
|
86
100
|
type TenantOpaqueKeys,
|
|
101
|
+
tenantOpaqueKeys,
|
|
87
102
|
} from "../runtime/request-telemetry.js";
|
|
88
|
-
import {
|
|
89
|
-
ResolverTelemetryCollector,
|
|
90
|
-
type ResolverTelemetryLogPayload,
|
|
91
|
-
} from "../runtime/resolver-telemetry.js";
|
|
92
103
|
import type * as ResolverRuntimeModule from "../runtime/resolver.js";
|
|
93
104
|
import {
|
|
94
|
-
createUnsupportedResolverClient,
|
|
95
105
|
bindResolverTelemetry,
|
|
106
|
+
createUnsupportedResolverClient,
|
|
96
107
|
inheritResolverTelemetryBinding,
|
|
97
108
|
type ResolverSolveWithRecorder,
|
|
98
109
|
} from "../runtime/resolver-shared.js";
|
|
110
|
+
import {
|
|
111
|
+
ResolverTelemetryCollector,
|
|
112
|
+
type ResolverTelemetryLogPayload,
|
|
113
|
+
} from "../runtime/resolver-telemetry.js";
|
|
99
114
|
import {
|
|
100
115
|
assertRequiredSecretsPresent,
|
|
101
116
|
listMissingRequiredSecrets,
|
|
@@ -105,9 +120,9 @@ import {
|
|
|
105
120
|
createProviderRuntimeStateFromEnv,
|
|
106
121
|
createUnsupportedProviderRuntimeState,
|
|
107
122
|
} from "../runtime/state.js";
|
|
108
|
-
import { StealthCookieJar } from "../runtime/stealth-cookies.js";
|
|
109
123
|
import type * as StealthRuntimeModule from "../runtime/stealth.js";
|
|
110
124
|
import type { StealthChallengeRuntime } from "../runtime/stealth-akamai-sbsd.js";
|
|
125
|
+
import { StealthCookieJar } from "../runtime/stealth-cookies.js";
|
|
111
126
|
import { createSttClientFromEnv } from "../runtime/stt.js";
|
|
112
127
|
import {
|
|
113
128
|
createTraceContext,
|
|
@@ -118,21 +133,21 @@ import {
|
|
|
118
133
|
} from "../runtime/trace.js";
|
|
119
134
|
import { resolveTraceConfigFromEnv } from "../runtime/trace-config.js";
|
|
120
135
|
import { parseSchema } from "../schema.js";
|
|
136
|
+
import { StatefulRoutingDeadlineError } from "../stateful/errors.js";
|
|
121
137
|
import {
|
|
122
138
|
STATEFUL_NONCE_HEADER as STATEFUL_FORWARDING_NONCE_HEADER,
|
|
123
139
|
STATEFUL_SIGNATURE_HEADER as STATEFUL_FORWARDING_SIGNATURE_HEADER,
|
|
124
140
|
STATEFUL_TIMESTAMP_HEADER as STATEFUL_FORWARDING_TIMESTAMP_HEADER,
|
|
125
141
|
verifyStatefulRequestSignature,
|
|
126
142
|
} from "../stateful-signing.js";
|
|
127
|
-
import { StatefulRoutingDeadlineError } from "../stateful/errors.js";
|
|
128
143
|
import { getStealthProfile } from "../stealth/profiles.js";
|
|
129
|
-
import { sanitizeTraceAttributes } from "../trace-sanitization.js";
|
|
130
144
|
import {
|
|
131
145
|
APIFUSE_STREAM_DONE_EVENT,
|
|
132
146
|
APIFUSE_STREAM_ERROR_EVENT,
|
|
133
147
|
encodeSseEvent,
|
|
134
148
|
error as streamError,
|
|
135
149
|
} from "../stream.js";
|
|
150
|
+
import { sanitizeSpanNameForOutput, sanitizeTraceAttributes } from "../trace-sanitization.js";
|
|
136
151
|
import type {
|
|
137
152
|
AuthContext,
|
|
138
153
|
AuthTurn,
|
|
@@ -140,15 +155,15 @@ import type {
|
|
|
140
155
|
FlowContext,
|
|
141
156
|
FlowContextStore,
|
|
142
157
|
HttpRetrySummary,
|
|
158
|
+
OcrContext,
|
|
143
159
|
OperationDefinition,
|
|
144
160
|
OperationErrorCode,
|
|
145
161
|
OperationHttpStreamTransport,
|
|
146
162
|
OperationSseTransport,
|
|
147
|
-
OcrContext,
|
|
148
|
-
ProviderErrorStatus,
|
|
149
|
-
ProviderContext,
|
|
150
163
|
ProviderCacheResponseMeta,
|
|
164
|
+
ProviderContext,
|
|
151
165
|
ProviderDefinition,
|
|
166
|
+
ProviderErrorStatus,
|
|
152
167
|
ProviderFilesContext,
|
|
153
168
|
ProviderProxyPolicy,
|
|
154
169
|
ProviderRuntimeState,
|
|
@@ -162,6 +177,10 @@ import type {
|
|
|
162
177
|
import { VALID_OPERATION_ERROR_STATUSES } from "../types.js";
|
|
163
178
|
import type { SelfTestCancellationLogEvent } from "./self-test.js";
|
|
164
179
|
import { resolveSelfTestMasterSecrets } from "./self-test-token.js";
|
|
180
|
+
import {
|
|
181
|
+
collectStaticDiagnosticSensitiveValues,
|
|
182
|
+
createDiagnosticEnvObserver,
|
|
183
|
+
} from "./sensitive-values.js";
|
|
165
184
|
import { resolveServerTraceContextOptions } from "./trace-output.js";
|
|
166
185
|
import {
|
|
167
186
|
type AuthFlowRequest,
|
|
@@ -307,9 +326,7 @@ export type ProviderServerOperationExecutorInput<
|
|
|
307
326
|
|
|
308
327
|
export type ProviderServerOperationExecutor<
|
|
309
328
|
TContext extends Partial<ProviderContext> = ProviderContext,
|
|
310
|
-
> = (
|
|
311
|
-
input: ProviderServerOperationExecutorInput<TContext>,
|
|
312
|
-
) => Promise<unknown>;
|
|
329
|
+
> = (input: ProviderServerOperationExecutorInput<TContext>) => Promise<unknown>;
|
|
313
330
|
|
|
314
331
|
type RequestTerminalOutcome =
|
|
315
332
|
| { kind: "completed"; status: number }
|
|
@@ -317,6 +334,7 @@ type RequestTerminalOutcome =
|
|
|
317
334
|
| { kind: "cancelled"; status?: number };
|
|
318
335
|
|
|
319
336
|
type RequestStreamLifecycle = {
|
|
337
|
+
redact: DiagnosticRedactor;
|
|
320
338
|
runStep<T>(fn: () => Promise<T>): Promise<T>;
|
|
321
339
|
registerCleanup(cleanup: () => void | Promise<void>): void;
|
|
322
340
|
terminalize(outcome: RequestTerminalOutcome): void;
|
|
@@ -823,6 +841,7 @@ function providerSecretNames(provider: ProviderDefinition): string[] {
|
|
|
823
841
|
}
|
|
824
842
|
|
|
825
843
|
type RequestScopeContext = {
|
|
844
|
+
redact: DiagnosticRedactor;
|
|
826
845
|
trace: RuntimeTraceContext;
|
|
827
846
|
telemetry: RequestTelemetry;
|
|
828
847
|
resolverTelemetry: ResolverTelemetryCollector;
|
|
@@ -899,6 +918,7 @@ function createProviderContext(
|
|
|
899
918
|
request.requestId,
|
|
900
919
|
"stealth",
|
|
901
920
|
error,
|
|
921
|
+
scope.redact,
|
|
902
922
|
);
|
|
903
923
|
|
|
904
924
|
const env = createEnvContext([
|
|
@@ -940,7 +960,7 @@ function createProviderContext(
|
|
|
940
960
|
provider.runtime === "browser"
|
|
941
961
|
? capabilityModules.browser!.createBrowserClient({
|
|
942
962
|
allowedHosts: provider.allowedHosts,
|
|
943
|
-
cdpUrl:
|
|
963
|
+
cdpUrl: readDiagnosticEnv("APIFUSE__CDP_POOL__URL"),
|
|
944
964
|
headless: true,
|
|
945
965
|
requireCdpPool: isProductionProviderBrowserMode(provider),
|
|
946
966
|
stealth: true,
|
|
@@ -1101,6 +1121,7 @@ function createAuthFlowContext(
|
|
|
1101
1121
|
providerId: provider.id,
|
|
1102
1122
|
flowId: request.flowId,
|
|
1103
1123
|
affinityKey: proxyClientOptions.affinityKey,
|
|
1124
|
+
onHandle: (handle) => registerDiagnosticSensitiveValues(scope.trace, [handle]),
|
|
1104
1125
|
...(request.engine?.egressLease ? { handle: request.engine.egressLease } : {}),
|
|
1105
1126
|
})
|
|
1106
1127
|
: undefined;
|
|
@@ -1161,6 +1182,7 @@ function createAuthFlowContext(
|
|
|
1161
1182
|
request.requestId,
|
|
1162
1183
|
"stealth",
|
|
1163
1184
|
error,
|
|
1185
|
+
scope.redact,
|
|
1164
1186
|
);
|
|
1165
1187
|
const credential = request.connection
|
|
1166
1188
|
? createCredentialContext({
|
|
@@ -1418,15 +1440,18 @@ function finishRequestCost(input: {
|
|
|
1418
1440
|
};
|
|
1419
1441
|
}
|
|
1420
1442
|
|
|
1421
|
-
function zodDetails(
|
|
1443
|
+
function zodDetails(
|
|
1444
|
+
error: z.ZodError,
|
|
1445
|
+
redact?: DiagnosticRedactor,
|
|
1446
|
+
): Array<{
|
|
1422
1447
|
path: string;
|
|
1423
1448
|
code: string;
|
|
1424
1449
|
message: string;
|
|
1425
1450
|
}> {
|
|
1426
1451
|
return error.issues.map((issue) => ({
|
|
1427
|
-
path: issue.path.join("."),
|
|
1428
|
-
code: issue.code,
|
|
1429
|
-
message: issue.message,
|
|
1452
|
+
path: sanitizeSpanNameForOutput(issue.path.join("."), redact),
|
|
1453
|
+
code: sanitizeSpanNameForOutput(issue.code, redact),
|
|
1454
|
+
message: sanitizeSpanNameForOutput(issue.message, redact),
|
|
1430
1455
|
}));
|
|
1431
1456
|
}
|
|
1432
1457
|
|
|
@@ -1453,6 +1478,7 @@ function toErrorResponse(
|
|
|
1453
1478
|
error: unknown,
|
|
1454
1479
|
requestId: string | undefined,
|
|
1455
1480
|
observabilityDetails: ErrorObservabilityDetails,
|
|
1481
|
+
redact?: DiagnosticRedactor,
|
|
1456
1482
|
): OperationErrorResponse {
|
|
1457
1483
|
const observability = observabilityDetails;
|
|
1458
1484
|
const source = publicErrorSource(error, observability.category);
|
|
@@ -1491,7 +1517,7 @@ function toErrorResponse(
|
|
|
1491
1517
|
...(requestId ? { requestId } : {}),
|
|
1492
1518
|
retryable: observability.retryable,
|
|
1493
1519
|
source,
|
|
1494
|
-
details: zodDetails(error),
|
|
1520
|
+
details: zodDetails(error, redact),
|
|
1495
1521
|
},
|
|
1496
1522
|
};
|
|
1497
1523
|
}
|
|
@@ -1787,14 +1813,17 @@ export type ProviderErrorCauseFrame = {
|
|
|
1787
1813
|
const MAX_PROVIDER_ERROR_CAUSE_FRAMES = 5;
|
|
1788
1814
|
const MAX_PROVIDER_ERROR_CAUSE_MESSAGE_LENGTH = 300;
|
|
1789
1815
|
|
|
1790
|
-
function providerErrorCauseMessage(message: string): string {
|
|
1791
|
-
const sanitized = sanitizeDiagnosticText(message);
|
|
1816
|
+
function providerErrorCauseMessage(message: string, redact?: DiagnosticRedactor): string {
|
|
1817
|
+
const sanitized = sanitizeDiagnosticText(redactDiagnosticText(message, redact));
|
|
1792
1818
|
return sanitized.length > MAX_PROVIDER_ERROR_CAUSE_MESSAGE_LENGTH
|
|
1793
1819
|
? `${sanitized.slice(0, MAX_PROVIDER_ERROR_CAUSE_MESSAGE_LENGTH)}… [truncated]`
|
|
1794
1820
|
: sanitized;
|
|
1795
1821
|
}
|
|
1796
1822
|
|
|
1797
|
-
function providerErrorCauseChain(
|
|
1823
|
+
function providerErrorCauseChain(
|
|
1824
|
+
error: unknown,
|
|
1825
|
+
redact?: DiagnosticRedactor,
|
|
1826
|
+
): ProviderErrorCauseFrame[] | undefined {
|
|
1798
1827
|
if (!(error instanceof Error) && !isProviderError(error)) return undefined;
|
|
1799
1828
|
|
|
1800
1829
|
const seen = new Set<object>([error]);
|
|
@@ -1811,9 +1840,9 @@ function providerErrorCauseChain(error: unknown): ProviderErrorCauseFrame[] | un
|
|
|
1811
1840
|
const providerObservability = safeProviderErrorObservability(cause);
|
|
1812
1841
|
const causeCode = providerErrorCode(cause);
|
|
1813
1842
|
frames.push({
|
|
1814
|
-
errorClass: cause.name,
|
|
1815
|
-
...(causeCode !== undefined ? { code: causeCode } : {}),
|
|
1816
|
-
message: providerErrorCauseMessage(message),
|
|
1843
|
+
errorClass: redactDiagnosticText(cause.name, redact),
|
|
1844
|
+
...(causeCode !== undefined ? { code: redactDiagnosticText(causeCode, redact) } : {}),
|
|
1845
|
+
message: providerErrorCauseMessage(message, redact),
|
|
1817
1846
|
messageLength: message.length,
|
|
1818
1847
|
messageFingerprint: createHash("sha256").update(message).digest("hex").slice(0, 12),
|
|
1819
1848
|
...(providerObservability ? { providerObservability } : {}),
|
|
@@ -1837,6 +1866,7 @@ function logProviderError(
|
|
|
1837
1866
|
telemetry: RequestTelemetry | undefined,
|
|
1838
1867
|
observabilityDetails: ErrorObservabilityDetails,
|
|
1839
1868
|
correlation: RequestCorrelationIds = {},
|
|
1869
|
+
redact?: DiagnosticRedactor,
|
|
1840
1870
|
): void {
|
|
1841
1871
|
const providerCode = isProviderError(error) ? providerErrorCode(error) : undefined;
|
|
1842
1872
|
const code = isProviderError(error)
|
|
@@ -1848,7 +1878,7 @@ function logProviderError(
|
|
|
1848
1878
|
: "internal_error";
|
|
1849
1879
|
const errorClass = error instanceof Error ? error.name : typeof error;
|
|
1850
1880
|
const message = error instanceof Error ? error.message : String(error);
|
|
1851
|
-
const causeChain = providerErrorCauseChain(error);
|
|
1881
|
+
const causeChain = providerErrorCauseChain(error, redact);
|
|
1852
1882
|
const details = observabilityDetails;
|
|
1853
1883
|
const isUnregisteredProviderErrorCode =
|
|
1854
1884
|
status === 500 &&
|
|
@@ -1868,24 +1898,28 @@ function logProviderError(
|
|
|
1868
1898
|
emit({
|
|
1869
1899
|
level: status >= 500 ? "error" : "warn",
|
|
1870
1900
|
event: "provider_request_failed",
|
|
1871
|
-
providerId: provider.id,
|
|
1901
|
+
providerId: redactDiagnosticText(provider.id, redact),
|
|
1872
1902
|
kind,
|
|
1873
|
-
route,
|
|
1874
|
-
...(requestId ? { requestId } : {}),
|
|
1903
|
+
route: redactCriticalDiagnosticText(route, redact),
|
|
1904
|
+
...(requestId ? { requestId: redactCriticalDiagnosticText(requestId, redact) } : {}),
|
|
1875
1905
|
...(correlation.connectionId !== undefined
|
|
1876
|
-
? { connectionId: correlation.connectionId }
|
|
1906
|
+
? { connectionId: redactDiagnosticText(correlation.connectionId, redact) }
|
|
1907
|
+
: {}),
|
|
1908
|
+
...(correlation.flowId !== undefined
|
|
1909
|
+
? { flowId: redactDiagnosticText(correlation.flowId, redact) }
|
|
1910
|
+
: {}),
|
|
1911
|
+
...(correlation.tenantId !== undefined
|
|
1912
|
+
? { tenantId: redactDiagnosticText(correlation.tenantId, redact) }
|
|
1877
1913
|
: {}),
|
|
1878
|
-
...(correlation.flowId !== undefined ? { flowId: correlation.flowId } : {}),
|
|
1879
|
-
...(correlation.tenantId !== undefined ? { tenantId: correlation.tenantId } : {}),
|
|
1880
1914
|
...(correlation.requestedProviderId !== undefined
|
|
1881
|
-
? { requestedProviderId: correlation.requestedProviderId }
|
|
1915
|
+
? { requestedProviderId: redactDiagnosticText(correlation.requestedProviderId, redact) }
|
|
1882
1916
|
: {}),
|
|
1883
1917
|
status,
|
|
1884
1918
|
...cost,
|
|
1885
1919
|
...(telemetryPayload ?? {}),
|
|
1886
|
-
code,
|
|
1887
|
-
errorClass,
|
|
1888
|
-
message,
|
|
1920
|
+
code: sanitizeSpanNameForOutput(redactCriticalDiagnosticText(code, redact)),
|
|
1921
|
+
errorClass: redactDiagnosticText(errorClass, redact),
|
|
1922
|
+
message: sanitizeDiagnosticText(redactDiagnosticText(message, redact)),
|
|
1889
1923
|
...(causeChain ? { causeChain } : {}),
|
|
1890
1924
|
...(providerObservability ? { providerObservability } : {}),
|
|
1891
1925
|
...(details.upstreamStatus ? { upstreamStatus: details.upstreamStatus } : {}),
|
|
@@ -1899,7 +1933,7 @@ function logProviderError(
|
|
|
1899
1933
|
"Declare this code (with status and retryable) in the operation's errorCodes so it serves its intended status instead of 500.",
|
|
1900
1934
|
}
|
|
1901
1935
|
: {}),
|
|
1902
|
-
...(error instanceof z.ZodError ? { issues: zodDetails(error) } : {}),
|
|
1936
|
+
...(error instanceof z.ZodError ? { issues: zodDetails(error, redact) } : {}),
|
|
1903
1937
|
});
|
|
1904
1938
|
}
|
|
1905
1939
|
|
|
@@ -1911,6 +1945,7 @@ function logProviderCleanupError(
|
|
|
1911
1945
|
requestId: string | undefined,
|
|
1912
1946
|
resource: "browser" | "stealth",
|
|
1913
1947
|
error: unknown,
|
|
1948
|
+
redact?: DiagnosticRedactor,
|
|
1914
1949
|
): void {
|
|
1915
1950
|
const emit = typeof logger === "function" ? logger : defaultProviderServerLogger;
|
|
1916
1951
|
const errorClass = error instanceof Error ? error.name : typeof error;
|
|
@@ -1918,13 +1953,13 @@ function logProviderCleanupError(
|
|
|
1918
1953
|
emit({
|
|
1919
1954
|
level: "warn",
|
|
1920
1955
|
event: "provider_cleanup_failed",
|
|
1921
|
-
providerId: provider.id,
|
|
1956
|
+
providerId: redactDiagnosticText(provider.id, redact),
|
|
1922
1957
|
kind,
|
|
1923
|
-
route: operationId,
|
|
1924
|
-
...(requestId ? { requestId } : {}),
|
|
1958
|
+
route: redactCriticalDiagnosticText(operationId, redact),
|
|
1959
|
+
...(requestId ? { requestId: redactCriticalDiagnosticText(requestId, redact) } : {}),
|
|
1925
1960
|
resource,
|
|
1926
|
-
errorClass,
|
|
1927
|
-
message,
|
|
1961
|
+
errorClass: redactDiagnosticText(errorClass, redact),
|
|
1962
|
+
message: sanitizeDiagnosticText(redactDiagnosticText(message, redact)),
|
|
1928
1963
|
});
|
|
1929
1964
|
}
|
|
1930
1965
|
|
|
@@ -1938,23 +1973,28 @@ function logProviderSuccess(
|
|
|
1938
1973
|
cost: ProviderRequestCost,
|
|
1939
1974
|
telemetry?: RequestTelemetry,
|
|
1940
1975
|
correlation: RequestCorrelationIds = {},
|
|
1976
|
+
redact?: DiagnosticRedactor,
|
|
1941
1977
|
): void {
|
|
1942
1978
|
const telemetryPayload = telemetry?.toLogPayload();
|
|
1943
1979
|
const emit = typeof logger === "function" ? logger : defaultProviderServerLogger;
|
|
1944
1980
|
emit({
|
|
1945
1981
|
level: "info",
|
|
1946
1982
|
event: "provider_request_completed",
|
|
1947
|
-
providerId: provider.id,
|
|
1983
|
+
providerId: redactDiagnosticText(provider.id, redact),
|
|
1948
1984
|
kind,
|
|
1949
|
-
route,
|
|
1950
|
-
...(requestId ? { requestId } : {}),
|
|
1985
|
+
route: redactCriticalDiagnosticText(route, redact),
|
|
1986
|
+
...(requestId ? { requestId: redactCriticalDiagnosticText(requestId, redact) } : {}),
|
|
1951
1987
|
...(correlation.connectionId !== undefined
|
|
1952
|
-
? { connectionId: correlation.connectionId }
|
|
1988
|
+
? { connectionId: redactDiagnosticText(correlation.connectionId, redact) }
|
|
1989
|
+
: {}),
|
|
1990
|
+
...(correlation.flowId !== undefined
|
|
1991
|
+
? { flowId: redactDiagnosticText(correlation.flowId, redact) }
|
|
1992
|
+
: {}),
|
|
1993
|
+
...(correlation.tenantId !== undefined
|
|
1994
|
+
? { tenantId: redactDiagnosticText(correlation.tenantId, redact) }
|
|
1953
1995
|
: {}),
|
|
1954
|
-
...(correlation.flowId !== undefined ? { flowId: correlation.flowId } : {}),
|
|
1955
|
-
...(correlation.tenantId !== undefined ? { tenantId: correlation.tenantId } : {}),
|
|
1956
1996
|
...(correlation.requestedProviderId !== undefined
|
|
1957
|
-
? { requestedProviderId: correlation.requestedProviderId }
|
|
1997
|
+
? { requestedProviderId: redactDiagnosticText(correlation.requestedProviderId, redact) }
|
|
1958
1998
|
: {}),
|
|
1959
1999
|
status,
|
|
1960
2000
|
...cost,
|
|
@@ -1968,6 +2008,7 @@ type RequestScopeFinishResult = {
|
|
|
1968
2008
|
};
|
|
1969
2009
|
|
|
1970
2010
|
type RequestScope = RequestScopeContext & {
|
|
2011
|
+
seedCredentials(rawBody: unknown, kind: "operation" | "auth" | "stateful"): void;
|
|
1971
2012
|
enrich(input: {
|
|
1972
2013
|
route?: string;
|
|
1973
2014
|
requestId?: string;
|
|
@@ -2039,8 +2080,91 @@ function requestTraceId(
|
|
|
2039
2080
|
);
|
|
2040
2081
|
}
|
|
2041
2082
|
|
|
2083
|
+
/** Harvest only credential subtrees, iteratively and before schema diagnostics. */
|
|
2084
|
+
function rawRequestCredentials(
|
|
2085
|
+
rawBody: unknown,
|
|
2086
|
+
kind: "operation" | "auth" | "stateful",
|
|
2087
|
+
): { values: string[]; exhausted: boolean } {
|
|
2088
|
+
const record = (value: unknown): Record<string, unknown> | undefined =>
|
|
2089
|
+
value !== null && typeof value === "object" ? (value as Record<string, unknown>) : undefined;
|
|
2090
|
+
const own = (value: unknown, key: string): unknown => {
|
|
2091
|
+
const object = record(value);
|
|
2092
|
+
return object && Object.hasOwn(object, key) ? object[key] : undefined;
|
|
2093
|
+
};
|
|
2094
|
+
const body = kind === "stateful" ? own(rawBody, "operationRequest") : rawBody;
|
|
2095
|
+
const values: string[] = [];
|
|
2096
|
+
let remainingBytes = 64 * 1024;
|
|
2097
|
+
let exhausted = false;
|
|
2098
|
+
const add = (value: unknown): void => {
|
|
2099
|
+
if (typeof value !== "string") return;
|
|
2100
|
+
const bytes = Buffer.byteLength(value, "utf8");
|
|
2101
|
+
if (bytes > remainingBytes) {
|
|
2102
|
+
exhausted = true;
|
|
2103
|
+
return;
|
|
2104
|
+
}
|
|
2105
|
+
remainingBytes -= bytes;
|
|
2106
|
+
if (value) values.push(value);
|
|
2107
|
+
const trimmed = value.trim();
|
|
2108
|
+
if (trimmed && trimmed !== value) values.push(trimmed);
|
|
2109
|
+
};
|
|
2110
|
+
function* entries(object: Record<string, unknown>) {
|
|
2111
|
+
for (const key in object) {
|
|
2112
|
+
if (Object.hasOwn(object, key)) yield [key, object[key]] as const;
|
|
2113
|
+
}
|
|
2114
|
+
}
|
|
2115
|
+
const visit = (root: unknown): void => {
|
|
2116
|
+
const object = record(root);
|
|
2117
|
+
if (!object) {
|
|
2118
|
+
add(root);
|
|
2119
|
+
return;
|
|
2120
|
+
}
|
|
2121
|
+
// Connection inventories are usually flat. Walk the root directly and only
|
|
2122
|
+
// allocate iterator frames when a nested credential container is encountered.
|
|
2123
|
+
for (const key in object) {
|
|
2124
|
+
if (!Object.hasOwn(object, key)) continue;
|
|
2125
|
+
const value = object[key];
|
|
2126
|
+
add(key);
|
|
2127
|
+
if (exhausted) break;
|
|
2128
|
+
const child = record(value);
|
|
2129
|
+
if (!child) {
|
|
2130
|
+
add(value);
|
|
2131
|
+
if (exhausted) break;
|
|
2132
|
+
continue;
|
|
2133
|
+
}
|
|
2134
|
+
const stack = [{ iterator: entries(child), depth: 1 }];
|
|
2135
|
+
while (stack.length && !exhausted) {
|
|
2136
|
+
const frame = stack[stack.length - 1];
|
|
2137
|
+
const entry = frame.iterator.next();
|
|
2138
|
+
if (entry.done) {
|
|
2139
|
+
stack.pop();
|
|
2140
|
+
continue;
|
|
2141
|
+
}
|
|
2142
|
+
const [key, value] = entry.value;
|
|
2143
|
+
add(key);
|
|
2144
|
+
if (exhausted) break;
|
|
2145
|
+
const child = record(value);
|
|
2146
|
+
if (child) {
|
|
2147
|
+
if (frame.depth >= 8) {
|
|
2148
|
+
exhausted = true;
|
|
2149
|
+
break;
|
|
2150
|
+
}
|
|
2151
|
+
stack.push({ iterator: entries(child), depth: frame.depth + 1 });
|
|
2152
|
+
} else add(value);
|
|
2153
|
+
}
|
|
2154
|
+
if (exhausted) break;
|
|
2155
|
+
}
|
|
2156
|
+
};
|
|
2157
|
+
visit(own(own(body, "connection"), "secrets"));
|
|
2158
|
+
if (kind === "auth" && !exhausted) {
|
|
2159
|
+
add(own(own(body, "engine"), "egressLease"));
|
|
2160
|
+
if (!exhausted) visit(own(body, "input"));
|
|
2161
|
+
}
|
|
2162
|
+
return { values, exhausted };
|
|
2163
|
+
}
|
|
2164
|
+
|
|
2042
2165
|
function createRequestScope(input: {
|
|
2043
2166
|
provider: ProviderDefinition;
|
|
2167
|
+
staticSensitiveValues: CompiledDiagnosticSensitiveValues;
|
|
2044
2168
|
kind: "operation" | "auth";
|
|
2045
2169
|
route: string;
|
|
2046
2170
|
requestId?: string;
|
|
@@ -2052,8 +2176,25 @@ function createRequestScope(input: {
|
|
|
2052
2176
|
setHeader?: (name: string, value: string) => void;
|
|
2053
2177
|
declaredErrorCode?: (error: unknown) => OperationErrorCode | undefined;
|
|
2054
2178
|
}): RequestScope {
|
|
2179
|
+
const sensitiveRegistry = createDiagnosticRedactor([], input.staticSensitiveValues);
|
|
2180
|
+
const redact = (text: string) => redactDiagnosticText(text, sensitiveRegistry.redact);
|
|
2181
|
+
bindCriticalDiagnosticRedactor(
|
|
2182
|
+
redact,
|
|
2183
|
+
sensitiveRegistry.redactCritical,
|
|
2184
|
+
sensitiveRegistry.redactStructured,
|
|
2185
|
+
() => sensitiveRegistry.suppressed,
|
|
2186
|
+
);
|
|
2055
2187
|
const requestCost = startRequestCost();
|
|
2056
|
-
const
|
|
2188
|
+
const observeEnv = {
|
|
2189
|
+
observe: createDiagnosticEnvObserver(
|
|
2190
|
+
input.provider,
|
|
2191
|
+
sensitiveRegistry.add,
|
|
2192
|
+
sensitiveRegistry.has,
|
|
2193
|
+
),
|
|
2194
|
+
finished: false,
|
|
2195
|
+
staticValues: input.staticSensitiveValues,
|
|
2196
|
+
};
|
|
2197
|
+
const traceConfig = withDiagnosticEnv(observeEnv, () => resolveTraceConfigFromEnv());
|
|
2057
2198
|
const details = {
|
|
2058
2199
|
route: input.route,
|
|
2059
2200
|
requestId: input.requestId,
|
|
@@ -2072,12 +2213,16 @@ function createRequestScope(input: {
|
|
|
2072
2213
|
const initialTraceId = requestTraceId(input.headers, input.requestId);
|
|
2073
2214
|
const trace: RuntimeTraceContext = traceConfig
|
|
2074
2215
|
? createTraceContext({
|
|
2075
|
-
...
|
|
2216
|
+
...withDiagnosticEnv(observeEnv, () =>
|
|
2217
|
+
resolveServerTraceContextOptions(traceConfig, traceAttributes, undefined, redact),
|
|
2218
|
+
),
|
|
2219
|
+
redact,
|
|
2076
2220
|
...(initialTraceId ? { traceId: initialTraceId } : {}),
|
|
2077
2221
|
})
|
|
2078
|
-
: createTraceContext();
|
|
2222
|
+
: createTraceContext({ redact });
|
|
2223
|
+
bindDiagnosticSensitiveRegistry(trace, sensitiveRegistry);
|
|
2079
2224
|
const proxyCollector = new ProxyTelemetryCollector();
|
|
2080
|
-
const resolverCollector = new ResolverTelemetryCollector();
|
|
2225
|
+
const resolverCollector = new ResolverTelemetryCollector({ redact });
|
|
2081
2226
|
const telemetry = new RequestTelemetry(trace);
|
|
2082
2227
|
telemetry.register(proxyCollector);
|
|
2083
2228
|
telemetry.register(resolverCollector);
|
|
@@ -2104,12 +2249,14 @@ function createRequestScope(input: {
|
|
|
2104
2249
|
if (outcome.kind === "cancelled") return clientCancelledError();
|
|
2105
2250
|
return undefined;
|
|
2106
2251
|
};
|
|
2107
|
-
const root =
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2252
|
+
const root = withDiagnosticEnv(observeEnv, () =>
|
|
2253
|
+
trace.span(`request:${input.kind}:${input.route}`, async () => {
|
|
2254
|
+
rootRunner = AsyncLocalStorage.snapshot();
|
|
2255
|
+
const outcome = await rootTerminal;
|
|
2256
|
+
const error = terminalError(outcome);
|
|
2257
|
+
if (error !== undefined) throw error;
|
|
2258
|
+
}),
|
|
2259
|
+
);
|
|
2113
2260
|
void root.catch(() => undefined);
|
|
2114
2261
|
|
|
2115
2262
|
const settleRoot = (): void => {
|
|
@@ -2144,7 +2291,7 @@ function createRequestScope(input: {
|
|
|
2144
2291
|
traceAttributes.route = details.route;
|
|
2145
2292
|
const traceId = requestTraceId(details.headers, details.requestId);
|
|
2146
2293
|
const sanitizedAttributes = Object.fromEntries(
|
|
2147
|
-
Object.entries(sanitizeTraceAttributes(traceAttributes)).map(([key, value]) => [
|
|
2294
|
+
Object.entries(sanitizeTraceAttributes(traceAttributes, redact)).map(([key, value]) => [
|
|
2148
2295
|
key,
|
|
2149
2296
|
String(value),
|
|
2150
2297
|
]),
|
|
@@ -2156,9 +2303,16 @@ function createRequestScope(input: {
|
|
|
2156
2303
|
};
|
|
2157
2304
|
|
|
2158
2305
|
const scope: RequestScope = {
|
|
2306
|
+
redact,
|
|
2159
2307
|
trace,
|
|
2160
2308
|
telemetry,
|
|
2161
2309
|
resolverTelemetry: resolverCollector,
|
|
2310
|
+
seedCredentials(rawBody, kind): void {
|
|
2311
|
+
const harvested = rawRequestCredentials(rawBody, kind);
|
|
2312
|
+
sensitiveRegistry.add(harvested.values);
|
|
2313
|
+
// An incomplete inventory cannot certify free text: emit less.
|
|
2314
|
+
if (harvested.exhausted) sensitiveRegistry.suppress();
|
|
2315
|
+
},
|
|
2162
2316
|
enrich(enrichment): void {
|
|
2163
2317
|
if (terminalOutcome) return;
|
|
2164
2318
|
if (enrichment.route !== undefined) details.route = enrichment.route;
|
|
@@ -2208,6 +2362,7 @@ function createRequestScope(input: {
|
|
|
2208
2362
|
},
|
|
2209
2363
|
terminalize(outcome): RequestScopeFinishResult {
|
|
2210
2364
|
if (finishedResult) return finishedResult;
|
|
2365
|
+
observeEnv.finished = true;
|
|
2211
2366
|
terminalOutcome = outcome;
|
|
2212
2367
|
const error = terminalError(outcome);
|
|
2213
2368
|
finishedResult = {};
|
|
@@ -2215,8 +2370,7 @@ function createRequestScope(input: {
|
|
|
2215
2370
|
const declaredErrorCode =
|
|
2216
2371
|
error === undefined ? undefined : input.declaredErrorCode?.(error);
|
|
2217
2372
|
const status =
|
|
2218
|
-
outcome.status ??
|
|
2219
|
-
(error === undefined ? 200 : toStatusCode(error, declaredErrorCode));
|
|
2373
|
+
outcome.status ?? (error === undefined ? 200 : toStatusCode(error, declaredErrorCode));
|
|
2220
2374
|
finishedResult = headerSnapshot(error);
|
|
2221
2375
|
const cost = finishRequestCost(requestCost);
|
|
2222
2376
|
try {
|
|
@@ -2231,6 +2385,7 @@ function createRequestScope(input: {
|
|
|
2231
2385
|
cost,
|
|
2232
2386
|
telemetry,
|
|
2233
2387
|
details.correlation,
|
|
2388
|
+
redact,
|
|
2234
2389
|
);
|
|
2235
2390
|
} else {
|
|
2236
2391
|
logProviderError(
|
|
@@ -2246,6 +2401,7 @@ function createRequestScope(input: {
|
|
|
2246
2401
|
telemetry,
|
|
2247
2402
|
finishedResult.errorObservability as ErrorObservabilityDetails,
|
|
2248
2403
|
details.correlation,
|
|
2404
|
+
redact,
|
|
2249
2405
|
);
|
|
2250
2406
|
}
|
|
2251
2407
|
} catch {
|
|
@@ -2281,8 +2437,9 @@ function createRequestScope(input: {
|
|
|
2281
2437
|
return scope;
|
|
2282
2438
|
}
|
|
2283
2439
|
|
|
2284
|
-
// A raw `Response` result skips `toErrorResponse
|
|
2285
|
-
// provider-owned
|
|
2440
|
+
// A raw `Response` result skips `toErrorResponse`. Its payload shape stays
|
|
2441
|
+
// provider-owned; known credentials in error payloads still need redaction.
|
|
2442
|
+
// The observability header is SDK-owned: derive it
|
|
2286
2443
|
// from the status. Only timeouts, rate limits, and 5xx are retryable;
|
|
2287
2444
|
// `isRetryableCategory` would otherwise mark every other 4xx (`upstream_http`)
|
|
2288
2445
|
// as retryable.
|
|
@@ -2297,11 +2454,12 @@ function rawResponseErrorObservability(status: number): ErrorObservabilityDetail
|
|
|
2297
2454
|
// `rawStatusFallback` is decided by the caller from the outcome, not from a
|
|
2298
2455
|
// missing snapshot: a failed outcome whose thrown value is `undefined` also has
|
|
2299
2456
|
// no `errorObservability`, and its 500 carries the SDK envelope, not a raw body.
|
|
2300
|
-
function responseWithRequestScopeHeaders(
|
|
2457
|
+
async function responseWithRequestScopeHeaders(
|
|
2301
2458
|
response: Response,
|
|
2302
2459
|
finished: RequestScopeFinishResult,
|
|
2303
2460
|
rawStatusFallback: boolean,
|
|
2304
|
-
|
|
2461
|
+
redact: DiagnosticRedactor,
|
|
2462
|
+
): Promise<Response> {
|
|
2305
2463
|
const headers = new Headers(response.headers);
|
|
2306
2464
|
headers.delete(PROVIDER_TELEMETRY_HEADER);
|
|
2307
2465
|
if (finished.providerTelemetryHeader) {
|
|
@@ -2316,24 +2474,36 @@ function responseWithRequestScopeHeaders(
|
|
|
2316
2474
|
if (errorObservability) {
|
|
2317
2475
|
headers.set(ERROR_OBSERVABILITY_HEADER, JSON.stringify(errorObservability));
|
|
2318
2476
|
}
|
|
2319
|
-
|
|
2477
|
+
// Raw Response bodies are provider-owned. Preserve their stream and representation.
|
|
2478
|
+
const body = response.body;
|
|
2479
|
+
const statusText =
|
|
2480
|
+
rawStatusFallback && response.status >= 400
|
|
2481
|
+
? redactDiagnosticText(response.statusText, redact)
|
|
2482
|
+
: response.statusText;
|
|
2483
|
+
return new Response(body, {
|
|
2320
2484
|
headers,
|
|
2321
2485
|
status: response.status,
|
|
2322
|
-
|
|
2486
|
+
// When free text cannot be certified, use the SDK's standard HTTP reason
|
|
2487
|
+
// phrase. It preserves the status contract without trusting provider text.
|
|
2488
|
+
statusText:
|
|
2489
|
+
statusText === REDACTION_FAILED && isDiagnosticRedactionSuppressed(redact)
|
|
2490
|
+
? (STATUS_CODES[response.status] ?? "")
|
|
2491
|
+
: statusText,
|
|
2323
2492
|
});
|
|
2324
2493
|
}
|
|
2325
2494
|
|
|
2326
|
-
function finalizeRequestResponse(
|
|
2495
|
+
async function finalizeRequestResponse(
|
|
2327
2496
|
scope: RequestScope,
|
|
2328
2497
|
response: Response,
|
|
2329
2498
|
outcome: RequestTerminalOutcome,
|
|
2330
|
-
): Response {
|
|
2499
|
+
): Promise<Response> {
|
|
2331
2500
|
try {
|
|
2332
2501
|
const error = outcome.kind === "failed" ? outcome.error : undefined;
|
|
2333
|
-
const finalResponse = responseWithRequestScopeHeaders(
|
|
2502
|
+
const finalResponse = await responseWithRequestScopeHeaders(
|
|
2334
2503
|
response,
|
|
2335
2504
|
scope.snapshotHeaders(error),
|
|
2336
2505
|
outcome.kind === "completed",
|
|
2506
|
+
scope.redact,
|
|
2337
2507
|
);
|
|
2338
2508
|
scope.terminalize(outcome);
|
|
2339
2509
|
return finalResponse;
|
|
@@ -2552,11 +2722,7 @@ function toSseResponse(
|
|
|
2552
2722
|
const validated = await validateSseEvent(operation, next.value);
|
|
2553
2723
|
const encodedEvent = encodeSseEvent(validated);
|
|
2554
2724
|
const bytes = encoder.encode(encodedEvent);
|
|
2555
|
-
assertStreamPayloadWithinLimit(
|
|
2556
|
-
bytes.byteLength,
|
|
2557
|
-
transport?.maxEventBytes,
|
|
2558
|
-
"event",
|
|
2559
|
-
);
|
|
2725
|
+
assertStreamPayloadWithinLimit(bytes.byteLength, transport?.maxEventBytes, "event");
|
|
2560
2726
|
return bytes;
|
|
2561
2727
|
});
|
|
2562
2728
|
controller.enqueue(encodedBytes);
|
|
@@ -2565,9 +2731,13 @@ function toSseResponse(
|
|
|
2565
2731
|
controller.enqueue(
|
|
2566
2732
|
encoder.encode(
|
|
2567
2733
|
encodeSseEvent(
|
|
2568
|
-
streamError(
|
|
2569
|
-
|
|
2570
|
-
|
|
2734
|
+
streamError(
|
|
2735
|
+
"stream_error",
|
|
2736
|
+
sanitizeDiagnosticText(redactDiagnosticText(message, lifecycle.redact)),
|
|
2737
|
+
{
|
|
2738
|
+
...(requestId ? { requestId } : {}),
|
|
2739
|
+
},
|
|
2740
|
+
),
|
|
2571
2741
|
),
|
|
2572
2742
|
),
|
|
2573
2743
|
);
|
|
@@ -2810,6 +2980,7 @@ async function handleOperation(
|
|
|
2810
2980
|
request.requestId,
|
|
2811
2981
|
"stealth",
|
|
2812
2982
|
error,
|
|
2983
|
+
scope.redact,
|
|
2813
2984
|
);
|
|
2814
2985
|
}
|
|
2815
2986
|
}
|
|
@@ -2825,11 +2996,13 @@ async function handleOperation(
|
|
|
2825
2996
|
request.requestId,
|
|
2826
2997
|
"browser",
|
|
2827
2998
|
error,
|
|
2999
|
+
scope.redact,
|
|
2828
3000
|
);
|
|
2829
3001
|
}
|
|
2830
3002
|
}
|
|
2831
3003
|
};
|
|
2832
3004
|
const streamLifecycle: RequestStreamLifecycle = {
|
|
3005
|
+
redact: scope.redact,
|
|
2833
3006
|
runStep<T>(fn: () => Promise<T>): Promise<T> {
|
|
2834
3007
|
return scope.runStreamStep(fn);
|
|
2835
3008
|
},
|
|
@@ -2953,6 +3126,7 @@ async function handleAuthFlow(
|
|
|
2953
3126
|
request.requestId,
|
|
2954
3127
|
"stealth",
|
|
2955
3128
|
error,
|
|
3129
|
+
scope.redact,
|
|
2956
3130
|
);
|
|
2957
3131
|
}
|
|
2958
3132
|
}
|
|
@@ -3074,12 +3248,15 @@ function operationRequestFromForwardingEnvelope(
|
|
|
3074
3248
|
};
|
|
3075
3249
|
}
|
|
3076
3250
|
|
|
3077
|
-
function parseStatefulForwardingEnvelope(
|
|
3251
|
+
function parseStatefulForwardingEnvelope(
|
|
3252
|
+
rawBody: unknown,
|
|
3253
|
+
redact: DiagnosticRedactor,
|
|
3254
|
+
): ProviderServerStatefulForwardEnvelope {
|
|
3078
3255
|
const parsed = ProviderServerStatefulForwardEnvelopeSchema.safeParse(rawBody);
|
|
3079
3256
|
if (parsed.success) return parsed.data;
|
|
3080
3257
|
throw new ProviderError("Stateful forwarding envelope is invalid.", {
|
|
3081
3258
|
code: "STATEFUL_FORWARDING_ENVELOPE_INVALID",
|
|
3082
|
-
details: zodDetails(parsed.error),
|
|
3259
|
+
details: zodDetails(parsed.error, redact),
|
|
3083
3260
|
});
|
|
3084
3261
|
}
|
|
3085
3262
|
|
|
@@ -3087,7 +3264,9 @@ function parseStatefulForwardingEnvelope(rawBody: unknown): ProviderServerStatef
|
|
|
3087
3264
|
* Primary, cross-runtime app factory. Declared capability ESM is preloaded
|
|
3088
3265
|
* asynchronously, so this path works on Bun and every supported Node release.
|
|
3089
3266
|
*/
|
|
3090
|
-
export async function createServerAppAsync<
|
|
3267
|
+
export async function createServerAppAsync<
|
|
3268
|
+
TContext extends Partial<ProviderContext> = ProviderContext,
|
|
3269
|
+
>(
|
|
3091
3270
|
provider: ProviderDefinition<TContext>,
|
|
3092
3271
|
options: ProviderServerOptions<TContext> = {},
|
|
3093
3272
|
): Promise<Hono> {
|
|
@@ -3134,6 +3313,12 @@ function createServerAppWithCapabilityModules(
|
|
|
3134
3313
|
engine: serverOptions.engine ?? createInProcessProviderEngine(),
|
|
3135
3314
|
};
|
|
3136
3315
|
const app = new Hono();
|
|
3316
|
+
// Compile the startup inventory once, composed with the shared outside-context fallback.
|
|
3317
|
+
const staticSensitiveValues = compileProcessDiagnosticSensitiveValues(
|
|
3318
|
+
collectStaticDiagnosticSensitiveValues(provider, {
|
|
3319
|
+
statefulForwardingSecret: options.statefulForwarding?.secret,
|
|
3320
|
+
}),
|
|
3321
|
+
);
|
|
3137
3322
|
const logger = options.logger ?? defaultProviderServerLogger;
|
|
3138
3323
|
const operationErrorCodes = buildOperationErrorCodeLookup(provider);
|
|
3139
3324
|
const statefulForwardingReplayCache = new StatefulForwardingReplayCache(
|
|
@@ -3189,6 +3374,7 @@ function createServerAppWithCapabilityModules(
|
|
|
3189
3374
|
const operation = "stateful-internal";
|
|
3190
3375
|
const requestScope = createRequestScope({
|
|
3191
3376
|
provider,
|
|
3377
|
+
staticSensitiveValues,
|
|
3192
3378
|
kind: "operation",
|
|
3193
3379
|
route: operation,
|
|
3194
3380
|
headers: Object.fromEntries(c.req.raw.headers.entries()),
|
|
@@ -3218,7 +3404,8 @@ function createServerAppWithCapabilityModules(
|
|
|
3218
3404
|
code: "STATEFUL_FORWARDING_ENVELOPE_INVALID",
|
|
3219
3405
|
});
|
|
3220
3406
|
}
|
|
3221
|
-
|
|
3407
|
+
requestScope.seedCredentials(rawBody, "stateful");
|
|
3408
|
+
const envelope = parseStatefulForwardingEnvelope(rawBody, requestScope.redact);
|
|
3222
3409
|
if (envelope.providerId !== provider.id) {
|
|
3223
3410
|
throw new ProviderError(
|
|
3224
3411
|
"Stateful forwarding envelope providerId does not match the served provider.",
|
|
@@ -3354,7 +3541,10 @@ function createServerAppWithCapabilityModules(
|
|
|
3354
3541
|
...(requestId ? { requestId } : {}),
|
|
3355
3542
|
});
|
|
3356
3543
|
const observabilityDetails = errorObservabilityDetails(error, declaredErrorCode);
|
|
3357
|
-
const response = c.json(
|
|
3544
|
+
const response = c.json(
|
|
3545
|
+
toErrorResponse(error, requestId, observabilityDetails, requestScope.redact),
|
|
3546
|
+
status,
|
|
3547
|
+
);
|
|
3358
3548
|
return finalizeRequestResponse(requestScope, response, {
|
|
3359
3549
|
kind: "failed",
|
|
3360
3550
|
status,
|
|
@@ -3368,6 +3558,7 @@ function createServerAppWithCapabilityModules(
|
|
|
3368
3558
|
const operation = c.req.param("operation");
|
|
3369
3559
|
const requestScope = createRequestScope({
|
|
3370
3560
|
provider,
|
|
3561
|
+
staticSensitiveValues,
|
|
3371
3562
|
kind: "operation",
|
|
3372
3563
|
route: operation,
|
|
3373
3564
|
operationId: operation,
|
|
@@ -3381,6 +3572,7 @@ function createServerAppWithCapabilityModules(
|
|
|
3381
3572
|
.clone()
|
|
3382
3573
|
.json()
|
|
3383
3574
|
.catch(() => undefined);
|
|
3575
|
+
requestScope.seedCredentials(rawBody, "operation");
|
|
3384
3576
|
const body = OperationRequestSchema.parse(rawBody);
|
|
3385
3577
|
const requestHeaders = Object.fromEntries(c.req.raw.headers.entries());
|
|
3386
3578
|
body.headers = { ...requestHeaders, ...body.headers };
|
|
@@ -3405,7 +3597,12 @@ function createServerAppWithCapabilityModules(
|
|
|
3405
3597
|
);
|
|
3406
3598
|
const response = handled instanceof Response ? handled : c.json(handled);
|
|
3407
3599
|
return streaming
|
|
3408
|
-
? responseWithRequestScopeHeaders(
|
|
3600
|
+
? responseWithRequestScopeHeaders(
|
|
3601
|
+
response,
|
|
3602
|
+
requestScope.snapshotHeaders(),
|
|
3603
|
+
true,
|
|
3604
|
+
requestScope.redact,
|
|
3605
|
+
)
|
|
3409
3606
|
: response;
|
|
3410
3607
|
};
|
|
3411
3608
|
const response = streaming
|
|
@@ -3422,7 +3619,10 @@ function createServerAppWithCapabilityModules(
|
|
|
3422
3619
|
const requestId = extractRequestId(rawBody);
|
|
3423
3620
|
requestScope.enrich({ ...(requestId ? { requestId } : {}) });
|
|
3424
3621
|
const observabilityDetails = errorObservabilityDetails(error, declaredErrorCode);
|
|
3425
|
-
const response = c.json(
|
|
3622
|
+
const response = c.json(
|
|
3623
|
+
toErrorResponse(error, requestId, observabilityDetails, requestScope.redact),
|
|
3624
|
+
status,
|
|
3625
|
+
);
|
|
3426
3626
|
return finalizeRequestResponse(requestScope, response, {
|
|
3427
3627
|
kind: "failed",
|
|
3428
3628
|
status,
|
|
@@ -3448,6 +3648,7 @@ function createServerAppWithCapabilityModules(
|
|
|
3448
3648
|
let rawBody: unknown;
|
|
3449
3649
|
const requestScope = createRequestScope({
|
|
3450
3650
|
provider,
|
|
3651
|
+
staticSensitiveValues,
|
|
3451
3652
|
kind: "auth",
|
|
3452
3653
|
route: logRoute,
|
|
3453
3654
|
headers: Object.fromEntries(c.req.raw.headers.entries()),
|
|
@@ -3459,6 +3660,7 @@ function createServerAppWithCapabilityModules(
|
|
|
3459
3660
|
.clone()
|
|
3460
3661
|
.json()
|
|
3461
3662
|
.catch(() => undefined);
|
|
3663
|
+
requestScope.seedCredentials(rawBody, "auth");
|
|
3462
3664
|
const body = withAuthRequestHeaders(
|
|
3463
3665
|
AuthFlowRequestSchema.parse(rawBody),
|
|
3464
3666
|
c.req.raw.headers,
|
|
@@ -3498,7 +3700,10 @@ function createServerAppWithCapabilityModules(
|
|
|
3498
3700
|
const requestId = extractRequestId(rawBody);
|
|
3499
3701
|
requestScope.enrich({ ...(requestId ? { requestId } : {}) });
|
|
3500
3702
|
const observabilityDetails = errorObservabilityDetails(error);
|
|
3501
|
-
const response = c.json(
|
|
3703
|
+
const response = c.json(
|
|
3704
|
+
toErrorResponse(error, requestId, observabilityDetails, requestScope.redact),
|
|
3705
|
+
status,
|
|
3706
|
+
);
|
|
3502
3707
|
return finalizeRequestResponse(requestScope, response, {
|
|
3503
3708
|
kind: "failed",
|
|
3504
3709
|
status,
|