@apifuse/provider-sdk 2.2.0-beta.53 → 2.2.0-beta.55
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/CHANGELOG.md +8 -0
- package/dist/cli/migrate-operation-declaration.d.ts +5 -1
- package/dist/cli/migrate-operation-declaration.js +645 -46
- package/dist/runtime/auth-flow.d.ts +1 -0
- package/dist/runtime/auth-flow.js +2 -0
- package/dist/runtime/trace.d.ts +7 -0
- package/dist/runtime/trace.js +24 -1
- package/dist/server/serve-implementation.d.ts +4 -0
- package/dist/server/serve-implementation.js +576 -321
- package/dist/types.d.ts +1 -0
- package/package.json +1 -1
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-ambiguous-a.ts.txt +3 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-ambiguous-b.ts.txt +3 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-ambiguous-barrel.ts.txt +2 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-ambiguous-index.ts.txt +3 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-computed.ts.txt +6 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-cycle-a.ts.txt +1 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-cycle-b.ts.txt +1 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-cycle-index.ts.txt +3 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-daiso-barrel.ts.txt +7 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-daiso-catalog.ts.txt +19 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-daiso-docs.ts.txt +6 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-daiso-index.ts.txt +3 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-daiso-stores.ts.txt +8 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-indirect-config.ts.txt +5 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-nonstatic.ts.txt +4 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-package-index.ts.txt +3 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-reexport-index.ts.txt +3 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-reexport-leaf.ts.txt +10 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-reexport-one.ts.txt +1 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-reexport-two.ts.txt +1 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-star-package-barrel.ts.txt +2 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-star-package-index.ts.txt +3 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-star-package-leaf.ts.txt +3 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/factory-duplicate-provider.ts.txt +11 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/factory-id-unresolved.ts.txt +7 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/factory-one-to-many.ts.txt +18 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/factory-one-to-one.ts.txt +13 -0
- package/src/cli/migrate-operation-declaration.ts +844 -49
- package/src/runtime/auth-flow.ts +3 -0
- package/src/runtime/trace.ts +37 -1
- package/src/server/serve-implementation.ts +759 -586
- package/src/types.ts +1 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
3
|
import { existsSync } from "node:fs";
|
|
3
4
|
import { createRequire } from "node:module";
|
|
4
5
|
import { join } from "node:path";
|
|
@@ -30,12 +31,13 @@ import { assertRequiredSecretsPresent, listMissingRequiredSecrets, MISSING_SECRE
|
|
|
30
31
|
import { createProviderRuntimeStateFromEnv, createUnsupportedProviderRuntimeState, } from "../runtime/state.js";
|
|
31
32
|
import { StealthCookieJar } from "../runtime/stealth-cookies.js";
|
|
32
33
|
import { createSttClientFromEnv } from "../runtime/stt.js";
|
|
33
|
-
import { createTraceContext } from "../runtime/trace.js";
|
|
34
|
+
import { createTraceContext, updateTraceContextExportMetadata, } from "../runtime/trace.js";
|
|
34
35
|
import { resolveTraceConfigFromEnv } from "../runtime/trace-config.js";
|
|
35
36
|
import { parseSchema } from "../schema.js";
|
|
36
37
|
import { STATEFUL_NONCE_HEADER as STATEFUL_FORWARDING_NONCE_HEADER, STATEFUL_SIGNATURE_HEADER as STATEFUL_FORWARDING_SIGNATURE_HEADER, STATEFUL_TIMESTAMP_HEADER as STATEFUL_FORWARDING_TIMESTAMP_HEADER, verifyStatefulRequestSignature, } from "../stateful-signing.js";
|
|
37
38
|
import { StatefulRoutingDeadlineError } from "../stateful/errors.js";
|
|
38
39
|
import { getStealthProfile } from "../stealth/profiles.js";
|
|
40
|
+
import { sanitizeTraceAttributes } from "../trace-sanitization.js";
|
|
39
41
|
import { APIFUSE_STREAM_DONE_EVENT, APIFUSE_STREAM_ERROR_EVENT, encodeSseEvent, error as streamError, } from "../stream.js";
|
|
40
42
|
import { VALID_OPERATION_ERROR_STATUSES } from "../types.js";
|
|
41
43
|
import { resolveSelfTestMasterSecrets } from "./self-test-token.js";
|
|
@@ -398,8 +400,7 @@ function resolveNativeProxyPolicy(provider) {
|
|
|
398
400
|
function providerSecretNames(provider) {
|
|
399
401
|
return (provider.secrets?.map((secret) => secret.name) ?? []).filter((name) => !isEngineOwnedEnvName(name));
|
|
400
402
|
}
|
|
401
|
-
function createProviderContext(provider, request, operationId, options, state = createUnsupportedProviderRuntimeState(),
|
|
402
|
-
const traceConfig = resolveTraceConfigFromEnv();
|
|
403
|
+
function createProviderContext(provider, request, operationId, options, state = createUnsupportedProviderRuntimeState(), scope, signal) {
|
|
403
404
|
const baseUrl = getProviderBaseUrl(provider);
|
|
404
405
|
const stealthBaseUrl = getProviderStealthBaseUrl(provider);
|
|
405
406
|
const stealthProfile = getProviderStealthProfile(provider);
|
|
@@ -408,7 +409,7 @@ function createProviderContext(provider, request, operationId, options, state =
|
|
|
408
409
|
const proxyClientOptions = {
|
|
409
410
|
upstream: { proxy: provider.proxy },
|
|
410
411
|
affinityKey: resolveProviderProxyAffinityKey(provider, request, operationId),
|
|
411
|
-
telemetry: proxyTelemetry,
|
|
412
|
+
telemetry: scope.proxyTelemetry,
|
|
412
413
|
engineCredentials: engineProxyCredentials,
|
|
413
414
|
};
|
|
414
415
|
const resolverIdentityScope = resolveProviderResolverIdentityScope(provider, proxyClientOptions.affinityKey, request.requestId);
|
|
@@ -416,7 +417,7 @@ function createProviderContext(provider, request, operationId, options, state =
|
|
|
416
417
|
const stealthClientOptions = {
|
|
417
418
|
upstream: proxyClientOptions.upstream,
|
|
418
419
|
affinityKey: proxyClientOptions.affinityKey,
|
|
419
|
-
telemetry: proxyTelemetry,
|
|
420
|
+
telemetry: scope.proxyTelemetry,
|
|
420
421
|
engineCredentials: engineProxyCredentials,
|
|
421
422
|
...(signal ? { signal } : {}),
|
|
422
423
|
...(provider.stealth ? { stealth: provider.stealth } : {}),
|
|
@@ -482,13 +483,7 @@ function createProviderContext(provider, request, operationId, options, state =
|
|
|
482
483
|
},
|
|
483
484
|
}
|
|
484
485
|
: {}),
|
|
485
|
-
trace:
|
|
486
|
-
? createTraceContext(resolveServerTraceContextOptions(traceConfig, {
|
|
487
|
-
request_id: request.requestId,
|
|
488
|
-
provider_id: provider.id,
|
|
489
|
-
operation_id: operationId,
|
|
490
|
-
}))
|
|
491
|
-
: createTraceContext(),
|
|
486
|
+
trace: scope.trace,
|
|
492
487
|
auth: createAuthStub(),
|
|
493
488
|
ocr: options.ocr ?? createOcrClientFromEnv(provider.ocr),
|
|
494
489
|
stt: options.stt ?? createSttClientFromEnv(provider.stt),
|
|
@@ -558,7 +553,7 @@ export function resolveAuthFlowProxyAffinityKey(provider, request) {
|
|
|
558
553
|
request.providerId ??
|
|
559
554
|
provider.id);
|
|
560
555
|
}
|
|
561
|
-
function createAuthFlowContext(provider, request, options, state,
|
|
556
|
+
function createAuthFlowContext(provider, request, options, state, scope, signal) {
|
|
562
557
|
const baseUrl = getProviderBaseUrl(provider);
|
|
563
558
|
const stealthBaseUrl = getProviderStealthBaseUrl(provider);
|
|
564
559
|
const stealthProfile = getProviderStealthProfile(provider);
|
|
@@ -569,14 +564,14 @@ function createAuthFlowContext(provider, request, options, state, proxyTelemetry
|
|
|
569
564
|
const proxyClientOptions = {
|
|
570
565
|
upstream: { proxy: provider.proxy },
|
|
571
566
|
affinityKey: resolveAuthFlowProxyAffinityKey(provider, request),
|
|
572
|
-
telemetry: proxyTelemetry,
|
|
567
|
+
telemetry: scope.proxyTelemetry,
|
|
573
568
|
engineCredentials: engineProxyCredentials,
|
|
574
569
|
};
|
|
575
570
|
const resolverIdentityScope = resolveProviderResolverIdentityScope(provider, proxyClientOptions.affinityKey, request.requestId);
|
|
576
571
|
const stealthClientOptions = {
|
|
577
572
|
upstream: proxyClientOptions.upstream,
|
|
578
573
|
affinityKey: proxyClientOptions.affinityKey,
|
|
579
|
-
telemetry: proxyTelemetry,
|
|
574
|
+
telemetry: scope.proxyTelemetry,
|
|
580
575
|
engineCredentials: engineProxyCredentials,
|
|
581
576
|
...(signal ? { signal } : {}),
|
|
582
577
|
...(provider.stealth ? { stealth: provider.stealth } : {}),
|
|
@@ -592,63 +587,65 @@ function createAuthFlowContext(provider, request, options, state, proxyTelemetry
|
|
|
592
587
|
})
|
|
593
588
|
: undefined;
|
|
594
589
|
const cache = createProviderCache({ providerId: provider.id });
|
|
590
|
+
const context = wrapWithInstrumentation({
|
|
591
|
+
flowId: request.flowId,
|
|
592
|
+
connectionId: resolveOperationConnectionId(request),
|
|
593
|
+
externalRef: request.externalRef,
|
|
594
|
+
tenantId: request.tenantId ?? "",
|
|
595
|
+
providerId: request.providerId ?? provider.id,
|
|
596
|
+
trace: scope.trace,
|
|
597
|
+
http: createHttpClient(baseUrl, {
|
|
598
|
+
...proxyClientOptions,
|
|
599
|
+
...(signal ? { signal } : {}),
|
|
600
|
+
}),
|
|
601
|
+
state: state.forConnection(resolveOperationConnectionId(request)),
|
|
602
|
+
stealth: stealthBaseUrl
|
|
603
|
+
? capabilityModules.stealth
|
|
604
|
+
? capabilityModules.stealth.createStealthClient(stealthBaseUrl, stealthClientOptions)
|
|
605
|
+
: createLazyStealthClient(logStealthCleanupError, stealthBaseUrl, stealthClientOptions)
|
|
606
|
+
: createStealthStub(),
|
|
607
|
+
...(provider.native
|
|
608
|
+
? {
|
|
609
|
+
native: {
|
|
610
|
+
network: capabilityModules.nativeNetwork.createNativeNetworkClient({
|
|
611
|
+
egress: provider.native.network,
|
|
612
|
+
proxyPolicy: resolveNativeProxyPolicy(provider),
|
|
613
|
+
affinityKey: proxyClientOptions.affinityKey,
|
|
614
|
+
credentials: capabilityModules.nativeNetwork.createEnvVendorCredentialResolver(createEnvContext([...ENGINE_OWNED_PROXY_CREDENTIAL_ENV_NAMES])),
|
|
615
|
+
}),
|
|
616
|
+
},
|
|
617
|
+
}
|
|
618
|
+
: {}),
|
|
619
|
+
env: createEnvContext([
|
|
620
|
+
...providerSecretNames(provider),
|
|
621
|
+
...(provider.auth?.mode === "oauth2_proxied" ? ["APIFUSE__AUTH_PROXY__URL"] : []),
|
|
622
|
+
]),
|
|
623
|
+
credential,
|
|
624
|
+
context: flowContextStore.context,
|
|
625
|
+
ocr: options.ocr ?? createOcrClientFromEnv(provider.ocr),
|
|
626
|
+
stt: options.stt ?? createSttClientFromEnv(provider.stt),
|
|
627
|
+
resolver: capabilityModules.resolver
|
|
628
|
+
? capabilityModules.resolver.bindResolverSignal(options.resolver ??
|
|
629
|
+
capabilityModules.resolver.createResolverClientFromEnv(provider.resolver, undefined, {
|
|
630
|
+
allowedHosts: provider.allowedHosts,
|
|
631
|
+
cache,
|
|
632
|
+
identityScope: resolverIdentityScope,
|
|
633
|
+
...(proxyPolicy
|
|
634
|
+
? {
|
|
635
|
+
proxyIntent: {
|
|
636
|
+
mode: proxyPolicy.mode,
|
|
637
|
+
...proxyClientOptions,
|
|
638
|
+
...(stealthProfile ? { userAgent: stealthProfile.userAgent } : {}),
|
|
639
|
+
},
|
|
640
|
+
}
|
|
641
|
+
: {}),
|
|
642
|
+
}), signal)
|
|
643
|
+
: bindResolverSignalWithoutRuntime(options.resolver ??
|
|
644
|
+
createUnsupportedResolverClient("Provider does not declare resolver capability"), signal),
|
|
645
|
+
auth: createAuthFlowHelpers({ signal }),
|
|
646
|
+
});
|
|
595
647
|
return {
|
|
596
|
-
context
|
|
597
|
-
flowId: request.flowId,
|
|
598
|
-
connectionId: resolveOperationConnectionId(request),
|
|
599
|
-
externalRef: request.externalRef,
|
|
600
|
-
tenantId: request.tenantId ?? "",
|
|
601
|
-
providerId: request.providerId ?? provider.id,
|
|
602
|
-
http: createHttpClient(baseUrl, {
|
|
603
|
-
...proxyClientOptions,
|
|
604
|
-
...(signal ? { signal } : {}),
|
|
605
|
-
}),
|
|
606
|
-
state: state.forConnection(resolveOperationConnectionId(request)),
|
|
607
|
-
stealth: stealthBaseUrl
|
|
608
|
-
? capabilityModules.stealth
|
|
609
|
-
? capabilityModules.stealth.createStealthClient(stealthBaseUrl, stealthClientOptions)
|
|
610
|
-
: createLazyStealthClient(logStealthCleanupError, stealthBaseUrl, stealthClientOptions)
|
|
611
|
-
: createStealthStub(),
|
|
612
|
-
...(provider.native
|
|
613
|
-
? {
|
|
614
|
-
native: {
|
|
615
|
-
network: capabilityModules.nativeNetwork.createNativeNetworkClient({
|
|
616
|
-
egress: provider.native.network,
|
|
617
|
-
proxyPolicy: resolveNativeProxyPolicy(provider),
|
|
618
|
-
affinityKey: proxyClientOptions.affinityKey,
|
|
619
|
-
credentials: capabilityModules.nativeNetwork.createEnvVendorCredentialResolver(createEnvContext([...ENGINE_OWNED_PROXY_CREDENTIAL_ENV_NAMES])),
|
|
620
|
-
}),
|
|
621
|
-
},
|
|
622
|
-
}
|
|
623
|
-
: {}),
|
|
624
|
-
env: createEnvContext([
|
|
625
|
-
...providerSecretNames(provider),
|
|
626
|
-
...(provider.auth?.mode === "oauth2_proxied" ? ["APIFUSE__AUTH_PROXY__URL"] : []),
|
|
627
|
-
]),
|
|
628
|
-
credential,
|
|
629
|
-
context: flowContextStore.context,
|
|
630
|
-
ocr: options.ocr ?? createOcrClientFromEnv(provider.ocr),
|
|
631
|
-
stt: options.stt ?? createSttClientFromEnv(provider.stt),
|
|
632
|
-
resolver: capabilityModules.resolver
|
|
633
|
-
? capabilityModules.resolver.bindResolverSignal(options.resolver ??
|
|
634
|
-
capabilityModules.resolver.createResolverClientFromEnv(provider.resolver, undefined, {
|
|
635
|
-
allowedHosts: provider.allowedHosts,
|
|
636
|
-
cache,
|
|
637
|
-
identityScope: resolverIdentityScope,
|
|
638
|
-
...(proxyPolicy
|
|
639
|
-
? {
|
|
640
|
-
proxyIntent: {
|
|
641
|
-
mode: proxyPolicy.mode,
|
|
642
|
-
...proxyClientOptions,
|
|
643
|
-
...(stealthProfile ? { userAgent: stealthProfile.userAgent } : {}),
|
|
644
|
-
},
|
|
645
|
-
}
|
|
646
|
-
: {}),
|
|
647
|
-
}), signal)
|
|
648
|
-
: bindResolverSignalWithoutRuntime(options.resolver ??
|
|
649
|
-
createUnsupportedResolverClient("Provider does not declare resolver capability"), signal),
|
|
650
|
-
auth: createAuthFlowHelpers({ signal }),
|
|
651
|
-
},
|
|
648
|
+
context,
|
|
652
649
|
getPatch: flowContextStore.getPatch,
|
|
653
650
|
};
|
|
654
651
|
}
|
|
@@ -1017,7 +1014,7 @@ function providerErrorCauseChain(error) {
|
|
|
1017
1014
|
}
|
|
1018
1015
|
return frames.length > 0 ? frames : undefined;
|
|
1019
1016
|
}
|
|
1020
|
-
function logProviderError(logger, provider, kind, route, requestId, error, status, cost, declaredErrorCode, proxyTelemetry, observabilityDetails) {
|
|
1017
|
+
function logProviderError(logger, provider, kind, route, requestId, error, status, cost, declaredErrorCode, proxyTelemetry, observabilityDetails, correlation = {}) {
|
|
1021
1018
|
const providerCode = isProviderError(error) ? providerErrorCode(error) : undefined;
|
|
1022
1019
|
const code = isProviderError(error)
|
|
1023
1020
|
? (providerCode ?? "provider_error")
|
|
@@ -1051,6 +1048,14 @@ function logProviderError(logger, provider, kind, route, requestId, error, statu
|
|
|
1051
1048
|
kind,
|
|
1052
1049
|
route,
|
|
1053
1050
|
...(requestId ? { requestId } : {}),
|
|
1051
|
+
...(correlation.connectionId !== undefined
|
|
1052
|
+
? { connectionId: correlation.connectionId }
|
|
1053
|
+
: {}),
|
|
1054
|
+
...(correlation.flowId !== undefined ? { flowId: correlation.flowId } : {}),
|
|
1055
|
+
...(correlation.tenantId !== undefined ? { tenantId: correlation.tenantId } : {}),
|
|
1056
|
+
...(correlation.requestedProviderId !== undefined
|
|
1057
|
+
? { requestedProviderId: correlation.requestedProviderId }
|
|
1058
|
+
: {}),
|
|
1054
1059
|
status,
|
|
1055
1060
|
...cost,
|
|
1056
1061
|
...(proxy ? { proxy } : {}),
|
|
@@ -1088,7 +1093,7 @@ function logProviderCleanupError(logger, provider, kind, operationId, requestId,
|
|
|
1088
1093
|
message,
|
|
1089
1094
|
});
|
|
1090
1095
|
}
|
|
1091
|
-
function logProviderSuccess(logger, provider, kind, route, requestId, status, cost, proxyTelemetry) {
|
|
1096
|
+
function logProviderSuccess(logger, provider, kind, route, requestId, status, cost, proxyTelemetry, correlation = {}) {
|
|
1092
1097
|
const proxy = proxyTelemetry?.toLogPayload();
|
|
1093
1098
|
const emit = typeof logger === "function" ? logger : defaultProviderServerLogger;
|
|
1094
1099
|
emit({
|
|
@@ -1098,11 +1103,301 @@ function logProviderSuccess(logger, provider, kind, route, requestId, status, co
|
|
|
1098
1103
|
kind,
|
|
1099
1104
|
route,
|
|
1100
1105
|
...(requestId ? { requestId } : {}),
|
|
1106
|
+
...(correlation.connectionId !== undefined
|
|
1107
|
+
? { connectionId: correlation.connectionId }
|
|
1108
|
+
: {}),
|
|
1109
|
+
...(correlation.flowId !== undefined ? { flowId: correlation.flowId } : {}),
|
|
1110
|
+
...(correlation.tenantId !== undefined ? { tenantId: correlation.tenantId } : {}),
|
|
1111
|
+
...(correlation.requestedProviderId !== undefined
|
|
1112
|
+
? { requestedProviderId: correlation.requestedProviderId }
|
|
1113
|
+
: {}),
|
|
1101
1114
|
status,
|
|
1102
1115
|
...cost,
|
|
1103
1116
|
...(proxy ? { proxy } : {}),
|
|
1104
1117
|
});
|
|
1105
1118
|
}
|
|
1119
|
+
const STREAM_CLEANUP_TIMEOUT_MS = 100;
|
|
1120
|
+
const CLIENT_CANCELLED_STATUS = 400;
|
|
1121
|
+
function clientCancelledError() {
|
|
1122
|
+
return new ProviderError("Request stream was cancelled by the client.", {
|
|
1123
|
+
code: "client_cancelled",
|
|
1124
|
+
category: "client_cancelled",
|
|
1125
|
+
retryable: false,
|
|
1126
|
+
});
|
|
1127
|
+
}
|
|
1128
|
+
async function runBestEffortWithTimeout(cleanup) {
|
|
1129
|
+
let timeout;
|
|
1130
|
+
const work = Promise.resolve()
|
|
1131
|
+
.then(cleanup)
|
|
1132
|
+
.catch(() => undefined);
|
|
1133
|
+
try {
|
|
1134
|
+
await Promise.race([
|
|
1135
|
+
work,
|
|
1136
|
+
new Promise((resolve) => {
|
|
1137
|
+
timeout = setTimeout(resolve, STREAM_CLEANUP_TIMEOUT_MS);
|
|
1138
|
+
}),
|
|
1139
|
+
]);
|
|
1140
|
+
}
|
|
1141
|
+
finally {
|
|
1142
|
+
if (timeout !== undefined)
|
|
1143
|
+
clearTimeout(timeout);
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
function traceIdFromTraceparent(headers) {
|
|
1147
|
+
const traceparent = Object.entries(headers).find(([name]) => name.toLowerCase() === "traceparent")?.[1];
|
|
1148
|
+
if (!traceparent)
|
|
1149
|
+
return undefined;
|
|
1150
|
+
const match = /^(00)-([0-9a-f]{32})-([0-9a-f]{16})-([0-9a-f]{2})$/.exec(traceparent);
|
|
1151
|
+
if (!match)
|
|
1152
|
+
return undefined;
|
|
1153
|
+
const traceId = match[2];
|
|
1154
|
+
const parentId = match[3];
|
|
1155
|
+
if (!traceId || !parentId || /^0+$/.test(traceId) || /^0+$/.test(parentId))
|
|
1156
|
+
return undefined;
|
|
1157
|
+
return traceId;
|
|
1158
|
+
}
|
|
1159
|
+
function requestTraceId(headers, requestId) {
|
|
1160
|
+
return (traceIdFromTraceparent(headers) ??
|
|
1161
|
+
(requestId
|
|
1162
|
+
? createHash("sha256").update(requestId, "utf8").digest("hex").slice(0, 32)
|
|
1163
|
+
: undefined));
|
|
1164
|
+
}
|
|
1165
|
+
function createRequestScope(input) {
|
|
1166
|
+
const requestCost = startRequestCost();
|
|
1167
|
+
const traceConfig = resolveTraceConfigFromEnv();
|
|
1168
|
+
const proxyTelemetry = new ProxyTelemetryCollector();
|
|
1169
|
+
const details = {
|
|
1170
|
+
route: input.route,
|
|
1171
|
+
requestId: input.requestId,
|
|
1172
|
+
operationId: input.operationId,
|
|
1173
|
+
flowId: input.flowId,
|
|
1174
|
+
headers: input.headers,
|
|
1175
|
+
correlation: { ...(input.correlation ?? {}) },
|
|
1176
|
+
};
|
|
1177
|
+
const traceAttributes = {
|
|
1178
|
+
provider_id: input.provider.id,
|
|
1179
|
+
...(input.operationId ? { operation_id: input.operationId } : {}),
|
|
1180
|
+
...(input.flowId ? { flow_id: input.flowId } : {}),
|
|
1181
|
+
...(input.requestId ? { request_id: input.requestId } : {}),
|
|
1182
|
+
route: input.route,
|
|
1183
|
+
};
|
|
1184
|
+
const initialTraceId = requestTraceId(input.headers, input.requestId);
|
|
1185
|
+
const trace = traceConfig
|
|
1186
|
+
? createTraceContext({
|
|
1187
|
+
...resolveServerTraceContextOptions(traceConfig, traceAttributes),
|
|
1188
|
+
...(initialTraceId ? { traceId: initialTraceId } : {}),
|
|
1189
|
+
})
|
|
1190
|
+
: createTraceContext();
|
|
1191
|
+
let rootRunner = (fn) => fn();
|
|
1192
|
+
let resolveRoot;
|
|
1193
|
+
const rootTerminal = new Promise((resolve) => {
|
|
1194
|
+
resolveRoot = resolve;
|
|
1195
|
+
});
|
|
1196
|
+
let streamingSetupPending = false;
|
|
1197
|
+
let rootSettled = false;
|
|
1198
|
+
let headersSnapshotted = false;
|
|
1199
|
+
let terminalOutcome;
|
|
1200
|
+
let finishedResult;
|
|
1201
|
+
let abortSignal;
|
|
1202
|
+
let abortListener;
|
|
1203
|
+
const streamCleanups = [];
|
|
1204
|
+
const terminalError = (outcome) => {
|
|
1205
|
+
if (outcome.kind === "failed")
|
|
1206
|
+
return outcome.error;
|
|
1207
|
+
if (outcome.kind === "cancelled")
|
|
1208
|
+
return clientCancelledError();
|
|
1209
|
+
return undefined;
|
|
1210
|
+
};
|
|
1211
|
+
const root = trace.span(`request:${input.kind}:${input.route}`, async () => {
|
|
1212
|
+
rootRunner = AsyncLocalStorage.snapshot();
|
|
1213
|
+
const outcome = await rootTerminal;
|
|
1214
|
+
const error = terminalError(outcome);
|
|
1215
|
+
if (error !== undefined)
|
|
1216
|
+
throw error;
|
|
1217
|
+
});
|
|
1218
|
+
void root.catch(() => undefined);
|
|
1219
|
+
const settleRoot = () => {
|
|
1220
|
+
if (!terminalOutcome || streamingSetupPending || rootSettled)
|
|
1221
|
+
return;
|
|
1222
|
+
rootSettled = true;
|
|
1223
|
+
if (abortSignal && abortListener) {
|
|
1224
|
+
abortSignal.removeEventListener("abort", abortListener);
|
|
1225
|
+
}
|
|
1226
|
+
resolveRoot(terminalOutcome);
|
|
1227
|
+
};
|
|
1228
|
+
const runCleanupEntry = (entry) => {
|
|
1229
|
+
entry.promise ??= runBestEffortWithTimeout(entry.cleanup);
|
|
1230
|
+
return entry.promise;
|
|
1231
|
+
};
|
|
1232
|
+
const cleanupStream = async () => {
|
|
1233
|
+
await Promise.all(streamCleanups.map(runCleanupEntry));
|
|
1234
|
+
};
|
|
1235
|
+
const headerSnapshot = (error) => {
|
|
1236
|
+
const providerTelemetryHeader = proxyTelemetry.toHeaderValue();
|
|
1237
|
+
const declaredErrorCode = error === undefined ? undefined : input.declaredErrorCode?.(error);
|
|
1238
|
+
const errorObservability = error === undefined ? undefined : errorObservabilityDetails(error, declaredErrorCode);
|
|
1239
|
+
return {
|
|
1240
|
+
...(providerTelemetryHeader ? { providerTelemetryHeader } : {}),
|
|
1241
|
+
...(errorObservability ? { errorObservability } : {}),
|
|
1242
|
+
};
|
|
1243
|
+
};
|
|
1244
|
+
const updateTraceMetadata = () => {
|
|
1245
|
+
if (details.requestId)
|
|
1246
|
+
traceAttributes.request_id = details.requestId;
|
|
1247
|
+
if (details.operationId)
|
|
1248
|
+
traceAttributes.operation_id = details.operationId;
|
|
1249
|
+
if (details.flowId)
|
|
1250
|
+
traceAttributes.flow_id = details.flowId;
|
|
1251
|
+
traceAttributes.route = details.route;
|
|
1252
|
+
const traceId = requestTraceId(details.headers, details.requestId);
|
|
1253
|
+
const sanitizedAttributes = Object.fromEntries(Object.entries(sanitizeTraceAttributes(traceAttributes)).map(([key, value]) => [
|
|
1254
|
+
key,
|
|
1255
|
+
String(value),
|
|
1256
|
+
]));
|
|
1257
|
+
updateTraceContextExportMetadata(trace, {
|
|
1258
|
+
...(traceId ? { traceId } : {}),
|
|
1259
|
+
resourceAttributes: sanitizedAttributes,
|
|
1260
|
+
});
|
|
1261
|
+
};
|
|
1262
|
+
const scope = {
|
|
1263
|
+
trace,
|
|
1264
|
+
proxyTelemetry,
|
|
1265
|
+
enrich(enrichment) {
|
|
1266
|
+
if (terminalOutcome)
|
|
1267
|
+
return;
|
|
1268
|
+
if (enrichment.route !== undefined)
|
|
1269
|
+
details.route = enrichment.route;
|
|
1270
|
+
if (enrichment.requestId !== undefined)
|
|
1271
|
+
details.requestId = enrichment.requestId;
|
|
1272
|
+
if (enrichment.operationId !== undefined)
|
|
1273
|
+
details.operationId = enrichment.operationId;
|
|
1274
|
+
if (enrichment.flowId !== undefined)
|
|
1275
|
+
details.flowId = enrichment.flowId;
|
|
1276
|
+
if (enrichment.headers !== undefined)
|
|
1277
|
+
details.headers = enrichment.headers;
|
|
1278
|
+
if (enrichment.correlation !== undefined) {
|
|
1279
|
+
Object.assign(details.correlation, enrichment.correlation);
|
|
1280
|
+
}
|
|
1281
|
+
updateTraceMetadata();
|
|
1282
|
+
},
|
|
1283
|
+
run(fn) {
|
|
1284
|
+
return rootRunner(fn);
|
|
1285
|
+
},
|
|
1286
|
+
async runStreaming(fn) {
|
|
1287
|
+
streamingSetupPending = true;
|
|
1288
|
+
try {
|
|
1289
|
+
return await rootRunner(fn);
|
|
1290
|
+
}
|
|
1291
|
+
finally {
|
|
1292
|
+
streamingSetupPending = false;
|
|
1293
|
+
settleRoot();
|
|
1294
|
+
}
|
|
1295
|
+
},
|
|
1296
|
+
runStreamStep(fn) {
|
|
1297
|
+
return rootRunner(fn);
|
|
1298
|
+
},
|
|
1299
|
+
watchAbort(signal) {
|
|
1300
|
+
if (terminalOutcome)
|
|
1301
|
+
return;
|
|
1302
|
+
abortSignal = signal;
|
|
1303
|
+
abortListener = () => {
|
|
1304
|
+
scope.terminalize({ kind: "cancelled", status: CLIENT_CANCELLED_STATUS });
|
|
1305
|
+
void cleanupStream();
|
|
1306
|
+
};
|
|
1307
|
+
if (signal.aborted)
|
|
1308
|
+
abortListener();
|
|
1309
|
+
else
|
|
1310
|
+
signal.addEventListener("abort", abortListener, { once: true });
|
|
1311
|
+
},
|
|
1312
|
+
registerStreamCleanup(cleanup) {
|
|
1313
|
+
const entry = { cleanup };
|
|
1314
|
+
streamCleanups.push(entry);
|
|
1315
|
+
if (terminalOutcome)
|
|
1316
|
+
void runCleanupEntry(entry);
|
|
1317
|
+
},
|
|
1318
|
+
cleanupStream,
|
|
1319
|
+
snapshotHeaders(error) {
|
|
1320
|
+
headersSnapshotted = true;
|
|
1321
|
+
return headerSnapshot(error);
|
|
1322
|
+
},
|
|
1323
|
+
terminalize(outcome) {
|
|
1324
|
+
if (finishedResult)
|
|
1325
|
+
return finishedResult;
|
|
1326
|
+
terminalOutcome = outcome;
|
|
1327
|
+
const error = terminalError(outcome);
|
|
1328
|
+
finishedResult = {};
|
|
1329
|
+
try {
|
|
1330
|
+
const declaredErrorCode = error === undefined ? undefined : input.declaredErrorCode?.(error);
|
|
1331
|
+
const status = outcome.status ??
|
|
1332
|
+
(error === undefined ? 200 : toStatusCode(error, declaredErrorCode));
|
|
1333
|
+
finishedResult = headerSnapshot(error);
|
|
1334
|
+
const cost = finishRequestCost(requestCost);
|
|
1335
|
+
try {
|
|
1336
|
+
if (error === undefined) {
|
|
1337
|
+
logProviderSuccess(input.logger, input.provider, input.kind, details.route, details.requestId, status, cost, proxyTelemetry, details.correlation);
|
|
1338
|
+
}
|
|
1339
|
+
else {
|
|
1340
|
+
logProviderError(input.logger, input.provider, input.kind, details.route, details.requestId, error, status, cost, declaredErrorCode, proxyTelemetry, finishedResult.errorObservability, details.correlation);
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1343
|
+
catch {
|
|
1344
|
+
// Observer callbacks cannot hold the request root open or change the response.
|
|
1345
|
+
}
|
|
1346
|
+
if (!headersSnapshotted) {
|
|
1347
|
+
if (finishedResult.providerTelemetryHeader) {
|
|
1348
|
+
try {
|
|
1349
|
+
input.setHeader?.(PROVIDER_TELEMETRY_HEADER, finishedResult.providerTelemetryHeader);
|
|
1350
|
+
}
|
|
1351
|
+
catch {
|
|
1352
|
+
// Header observers are isolated independently from request settlement.
|
|
1353
|
+
}
|
|
1354
|
+
}
|
|
1355
|
+
if (finishedResult.errorObservability) {
|
|
1356
|
+
try {
|
|
1357
|
+
input.setHeader?.(ERROR_OBSERVABILITY_HEADER, JSON.stringify(finishedResult.errorObservability));
|
|
1358
|
+
}
|
|
1359
|
+
catch {
|
|
1360
|
+
// Header observers are isolated independently from request settlement.
|
|
1361
|
+
}
|
|
1362
|
+
}
|
|
1363
|
+
}
|
|
1364
|
+
}
|
|
1365
|
+
finally {
|
|
1366
|
+
settleRoot();
|
|
1367
|
+
void cleanupStream();
|
|
1368
|
+
}
|
|
1369
|
+
return finishedResult;
|
|
1370
|
+
},
|
|
1371
|
+
};
|
|
1372
|
+
return scope;
|
|
1373
|
+
}
|
|
1374
|
+
function responseWithRequestScopeHeaders(response, finished) {
|
|
1375
|
+
const headers = new Headers(response.headers);
|
|
1376
|
+
headers.delete(PROVIDER_TELEMETRY_HEADER);
|
|
1377
|
+
if (finished.providerTelemetryHeader) {
|
|
1378
|
+
headers.set(PROVIDER_TELEMETRY_HEADER, finished.providerTelemetryHeader);
|
|
1379
|
+
}
|
|
1380
|
+
if (finished.errorObservability) {
|
|
1381
|
+
headers.set(ERROR_OBSERVABILITY_HEADER, JSON.stringify(finished.errorObservability));
|
|
1382
|
+
}
|
|
1383
|
+
return new Response(response.body, {
|
|
1384
|
+
headers,
|
|
1385
|
+
status: response.status,
|
|
1386
|
+
statusText: response.statusText,
|
|
1387
|
+
});
|
|
1388
|
+
}
|
|
1389
|
+
function finalizeRequestResponse(scope, response, outcome) {
|
|
1390
|
+
try {
|
|
1391
|
+
const error = outcome.kind === "failed" ? outcome.error : undefined;
|
|
1392
|
+
const finalResponse = responseWithRequestScopeHeaders(response, scope.snapshotHeaders(error));
|
|
1393
|
+
scope.terminalize(outcome);
|
|
1394
|
+
return finalResponse;
|
|
1395
|
+
}
|
|
1396
|
+
catch (error) {
|
|
1397
|
+
scope.terminalize({ kind: "failed", status: 500, error });
|
|
1398
|
+
throw error;
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1106
1401
|
function toJsonSuccessResponse(result, ctx) {
|
|
1107
1402
|
if (result instanceof Response) {
|
|
1108
1403
|
return result;
|
|
@@ -1135,45 +1430,40 @@ function isAsyncIterable(value) {
|
|
|
1135
1430
|
const iterator = Reflect.get(value, Symbol.asyncIterator);
|
|
1136
1431
|
return typeof iterator === "function";
|
|
1137
1432
|
}
|
|
1138
|
-
function responseWithCleanup(response,
|
|
1433
|
+
function responseWithCleanup(response, lifecycle) {
|
|
1139
1434
|
if (!response.body) {
|
|
1140
|
-
|
|
1435
|
+
lifecycle.terminalize({ kind: "completed", status: response.status });
|
|
1436
|
+
void lifecycle.cleanup();
|
|
1141
1437
|
return response;
|
|
1142
1438
|
}
|
|
1143
1439
|
const reader = response.body.getReader();
|
|
1144
|
-
let
|
|
1145
|
-
|
|
1146
|
-
if (cleaned)
|
|
1147
|
-
return;
|
|
1148
|
-
cleaned = true;
|
|
1149
|
-
await cleanup();
|
|
1150
|
-
};
|
|
1440
|
+
let cleanupReason = "request terminalized";
|
|
1441
|
+
lifecycle.registerCleanup(() => lifecycle.runStep(() => reader.cancel(cleanupReason)));
|
|
1151
1442
|
const body = new ReadableStream({
|
|
1152
1443
|
async pull(controller) {
|
|
1153
1444
|
try {
|
|
1154
|
-
const { done, value } = await reader.read();
|
|
1445
|
+
const { done, value } = await lifecycle.runStep(() => reader.read());
|
|
1155
1446
|
if (done) {
|
|
1156
1447
|
controller.close();
|
|
1157
|
-
|
|
1448
|
+
lifecycle.terminalize({ kind: "completed", status: response.status });
|
|
1449
|
+
await lifecycle.cleanup();
|
|
1158
1450
|
return;
|
|
1159
1451
|
}
|
|
1160
1452
|
if (value)
|
|
1161
1453
|
controller.enqueue(value);
|
|
1162
1454
|
}
|
|
1163
1455
|
catch (error) {
|
|
1164
|
-
|
|
1456
|
+
lifecycle.terminalize({ kind: "failed", error });
|
|
1457
|
+
await lifecycle.cleanup();
|
|
1165
1458
|
controller.error(error);
|
|
1166
1459
|
}
|
|
1167
1460
|
},
|
|
1168
1461
|
async cancel(reason) {
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
finally {
|
|
1173
|
-
await runCleanup();
|
|
1174
|
-
}
|
|
1462
|
+
cleanupReason = reason;
|
|
1463
|
+
lifecycle.terminalize({ kind: "cancelled", status: CLIENT_CANCELLED_STATUS });
|
|
1464
|
+
await lifecycle.cleanup();
|
|
1175
1465
|
},
|
|
1176
|
-
});
|
|
1466
|
+
}, { highWaterMark: 0 });
|
|
1177
1467
|
return new Response(body, {
|
|
1178
1468
|
headers: response.headers,
|
|
1179
1469
|
status: response.status,
|
|
@@ -1215,37 +1505,39 @@ function assertStreamPayloadWithinLimit(actualBytes, maxBytes, kind) {
|
|
|
1215
1505
|
: "Emit smaller stream chunks or increase transport.maxChunkBytes.",
|
|
1216
1506
|
});
|
|
1217
1507
|
}
|
|
1218
|
-
function toSseResponse(operation, result,
|
|
1508
|
+
function toSseResponse(operation, result, lifecycle, requestId) {
|
|
1219
1509
|
const encoder = new TextEncoder();
|
|
1220
1510
|
const iterator = result[Symbol.asyncIterator]();
|
|
1511
|
+
let cleanupReason;
|
|
1512
|
+
lifecycle.registerCleanup(() => lifecycle.runStep(async () => {
|
|
1513
|
+
await iterator.return?.(cleanupReason);
|
|
1514
|
+
}));
|
|
1221
1515
|
const transport = getSseTransport(operation);
|
|
1222
1516
|
let done = false;
|
|
1223
|
-
let cleaned = false;
|
|
1224
|
-
const runCleanup = async () => {
|
|
1225
|
-
if (cleaned)
|
|
1226
|
-
return;
|
|
1227
|
-
cleaned = true;
|
|
1228
|
-
await cleanup();
|
|
1229
|
-
};
|
|
1230
1517
|
const body = new ReadableStream({
|
|
1231
1518
|
async pull(controller) {
|
|
1232
1519
|
try {
|
|
1233
1520
|
if (done) {
|
|
1234
1521
|
controller.close();
|
|
1235
|
-
|
|
1522
|
+
lifecycle.terminalize({ kind: "completed", status: 200 });
|
|
1523
|
+
await lifecycle.cleanup();
|
|
1236
1524
|
return;
|
|
1237
1525
|
}
|
|
1238
|
-
const next = await iterator.next();
|
|
1526
|
+
const next = await lifecycle.runStep(() => iterator.next());
|
|
1239
1527
|
if (next.done) {
|
|
1240
1528
|
done = true;
|
|
1241
1529
|
controller.close();
|
|
1242
|
-
|
|
1530
|
+
lifecycle.terminalize({ kind: "completed", status: 200 });
|
|
1531
|
+
await lifecycle.cleanup();
|
|
1243
1532
|
return;
|
|
1244
1533
|
}
|
|
1245
|
-
const
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1534
|
+
const encodedBytes = await lifecycle.runStep(async () => {
|
|
1535
|
+
const validated = await validateSseEvent(operation, next.value);
|
|
1536
|
+
const encodedEvent = encodeSseEvent(validated);
|
|
1537
|
+
const bytes = encoder.encode(encodedEvent);
|
|
1538
|
+
assertStreamPayloadWithinLimit(bytes.byteLength, transport?.maxEventBytes, "event");
|
|
1539
|
+
return bytes;
|
|
1540
|
+
});
|
|
1249
1541
|
controller.enqueue(encodedBytes);
|
|
1250
1542
|
}
|
|
1251
1543
|
catch (error) {
|
|
@@ -1255,18 +1547,16 @@ function toSseResponse(operation, result, cleanup, requestId) {
|
|
|
1255
1547
|
}))));
|
|
1256
1548
|
controller.close();
|
|
1257
1549
|
done = true;
|
|
1258
|
-
|
|
1550
|
+
lifecycle.terminalize({ kind: "failed", error });
|
|
1551
|
+
await lifecycle.cleanup();
|
|
1259
1552
|
}
|
|
1260
1553
|
},
|
|
1261
1554
|
async cancel(reason) {
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
finally {
|
|
1266
|
-
await runCleanup();
|
|
1267
|
-
}
|
|
1555
|
+
cleanupReason = reason;
|
|
1556
|
+
lifecycle.terminalize({ kind: "cancelled", status: CLIENT_CANCELLED_STATUS });
|
|
1557
|
+
await lifecycle.cleanup();
|
|
1268
1558
|
},
|
|
1269
|
-
});
|
|
1559
|
+
}, { highWaterMark: 0 });
|
|
1270
1560
|
return new Response(body, {
|
|
1271
1561
|
headers: {
|
|
1272
1562
|
"Cache-Control": "no-cache, no-transform",
|
|
@@ -1301,10 +1591,9 @@ function enforceStreamChunkLimit(body, maxChunkBytes) {
|
|
|
1301
1591
|
},
|
|
1302
1592
|
});
|
|
1303
1593
|
}
|
|
1304
|
-
function toStreamingResponse(operation, result,
|
|
1594
|
+
function toStreamingResponse(operation, result, lifecycle, requestId) {
|
|
1305
1595
|
const transport = operation.transport?.kind ?? "json";
|
|
1306
1596
|
if (transport === "sse" && (result instanceof Response || result instanceof ReadableStream)) {
|
|
1307
|
-
void cleanup();
|
|
1308
1597
|
throw new ProviderError("SSE operations must return an AsyncIterable of typed stream.event(...) values.", {
|
|
1309
1598
|
code: "SSE_RESULT_UNSUPPORTED",
|
|
1310
1599
|
category: "output_validation",
|
|
@@ -1319,9 +1608,9 @@ function toStreamingResponse(operation, result, cleanup, requestId) {
|
|
|
1319
1608
|
headers: result.headers,
|
|
1320
1609
|
status: result.status,
|
|
1321
1610
|
statusText: result.statusText,
|
|
1322
|
-
}),
|
|
1611
|
+
}), lifecycle);
|
|
1323
1612
|
}
|
|
1324
|
-
return responseWithCleanup(result,
|
|
1613
|
+
return responseWithCleanup(result, lifecycle);
|
|
1325
1614
|
}
|
|
1326
1615
|
if (result instanceof ReadableStream) {
|
|
1327
1616
|
const httpTransport = getHttpStreamTransport(operation);
|
|
@@ -1336,12 +1625,11 @@ function toStreamingResponse(operation, result, cleanup, requestId) {
|
|
|
1336
1625
|
? (operation.transport.contentType ?? "application/octet-stream")
|
|
1337
1626
|
: "application/octet-stream",
|
|
1338
1627
|
},
|
|
1339
|
-
}),
|
|
1628
|
+
}), lifecycle);
|
|
1340
1629
|
}
|
|
1341
1630
|
if (transport === "sse" && isAsyncIterable(result)) {
|
|
1342
|
-
return toSseResponse(operation, result,
|
|
1631
|
+
return toSseResponse(operation, result, lifecycle, requestId);
|
|
1343
1632
|
}
|
|
1344
|
-
void cleanup();
|
|
1345
1633
|
throw new ProviderError(`Streaming operation returned unsupported result for transport "${transport}"`, {
|
|
1346
1634
|
code: "STREAM_RESULT_UNSUPPORTED",
|
|
1347
1635
|
fix: "Return an AsyncIterable of stream.event(...) values, a ReadableStream, or a Response from streaming operations.",
|
|
@@ -1419,8 +1707,8 @@ function withAuthRequestHeaders(request, headers) {
|
|
|
1419
1707
|
},
|
|
1420
1708
|
};
|
|
1421
1709
|
}
|
|
1422
|
-
async function handleOperation(provider, request, operationId, options, state = createUnsupportedProviderRuntimeState(),
|
|
1423
|
-
const ctx = createProviderContext(provider, request, operationId, options, state,
|
|
1710
|
+
async function handleOperation(provider, request, operationId, options, state = createUnsupportedProviderRuntimeState(), scope, signal) {
|
|
1711
|
+
const ctx = createProviderContext(provider, request, operationId, options, state, scope, signal);
|
|
1424
1712
|
const operation = provider.operations[operationId];
|
|
1425
1713
|
const streaming = operation?.transport?.kind && operation.transport.kind !== "json";
|
|
1426
1714
|
let cleanupCalled = false;
|
|
@@ -1445,6 +1733,21 @@ async function handleOperation(provider, request, operationId, options, state =
|
|
|
1445
1733
|
}
|
|
1446
1734
|
}
|
|
1447
1735
|
};
|
|
1736
|
+
const streamLifecycle = {
|
|
1737
|
+
runStep(fn) {
|
|
1738
|
+
return scope.runStreamStep(fn);
|
|
1739
|
+
},
|
|
1740
|
+
registerCleanup(streamCleanup) {
|
|
1741
|
+
scope.registerStreamCleanup(streamCleanup);
|
|
1742
|
+
},
|
|
1743
|
+
terminalize(outcome) {
|
|
1744
|
+
scope.terminalize(outcome);
|
|
1745
|
+
},
|
|
1746
|
+
cleanup() {
|
|
1747
|
+
return scope.cleanupStream();
|
|
1748
|
+
},
|
|
1749
|
+
};
|
|
1750
|
+
scope.registerStreamCleanup(() => scope.runStreamStep(cleanup));
|
|
1448
1751
|
try {
|
|
1449
1752
|
const result = options.operationExecutor
|
|
1450
1753
|
? await options.operationExecutor({
|
|
@@ -1458,7 +1761,7 @@ async function handleOperation(provider, request, operationId, options, state =
|
|
|
1458
1761
|
env: createEnvContext(providerSecretNames(provider)),
|
|
1459
1762
|
});
|
|
1460
1763
|
if (streaming && operation) {
|
|
1461
|
-
return toStreamingResponse(operation, result,
|
|
1764
|
+
return toStreamingResponse(operation, result, streamLifecycle, request.requestId);
|
|
1462
1765
|
}
|
|
1463
1766
|
return toJsonSuccessResponse(result, ctx);
|
|
1464
1767
|
}
|
|
@@ -1471,19 +1774,7 @@ async function handleOperation(provider, request, operationId, options, state =
|
|
|
1471
1774
|
await cleanup();
|
|
1472
1775
|
}
|
|
1473
1776
|
}
|
|
1474
|
-
function
|
|
1475
|
-
const headerValue = proxyTelemetry?.toHeaderValue();
|
|
1476
|
-
const headers = new Headers(response.headers);
|
|
1477
|
-
headers.delete(PROVIDER_TELEMETRY_HEADER);
|
|
1478
|
-
if (headerValue)
|
|
1479
|
-
headers.set(PROVIDER_TELEMETRY_HEADER, headerValue);
|
|
1480
|
-
return new Response(response.body, {
|
|
1481
|
-
headers,
|
|
1482
|
-
status: response.status,
|
|
1483
|
-
statusText: response.statusText,
|
|
1484
|
-
});
|
|
1485
|
-
}
|
|
1486
|
-
async function handleAuthFlow(provider, request, route, options, state, proxyTelemetry, signal) {
|
|
1777
|
+
async function handleAuthFlow(provider, request, route, options, state, scope, signal) {
|
|
1487
1778
|
const flow = provider.auth?.flow;
|
|
1488
1779
|
if (!flow) {
|
|
1489
1780
|
throw new ProviderError("Auth flow is not configured", {
|
|
@@ -1495,7 +1786,7 @@ async function handleAuthFlow(provider, request, route, options, state, proxyTel
|
|
|
1495
1786
|
// any flow code runs instead of at whatever point the ceremony first reads
|
|
1496
1787
|
// the env. `abort` stays exempt: a user must always be able to cancel a
|
|
1497
1788
|
// stranded flow even when provisioning is broken.
|
|
1498
|
-
const { context, getPatch } = createAuthFlowContext(provider, request, options, state,
|
|
1789
|
+
const { context, getPatch } = createAuthFlowContext(provider, request, options, state, scope, signal);
|
|
1499
1790
|
try {
|
|
1500
1791
|
if (route !== "abort") {
|
|
1501
1792
|
assertRequiredSecretsPresent(provider, context.env);
|
|
@@ -1723,7 +2014,15 @@ function createServerAppWithCapabilityModules(provider, serverOptions, capabilit
|
|
|
1723
2014
|
let rawBody;
|
|
1724
2015
|
let operationId;
|
|
1725
2016
|
const operation = "stateful-internal";
|
|
1726
|
-
const
|
|
2017
|
+
const requestScope = createRequestScope({
|
|
2018
|
+
provider,
|
|
2019
|
+
kind: "operation",
|
|
2020
|
+
route: operation,
|
|
2021
|
+
headers: Object.fromEntries(c.req.raw.headers.entries()),
|
|
2022
|
+
logger,
|
|
2023
|
+
setHeader: (name, value) => c.header(name, value),
|
|
2024
|
+
declaredErrorCode: (error) => declaredErrorCodeFor(error, operationId, operationErrorCodes),
|
|
2025
|
+
});
|
|
1727
2026
|
try {
|
|
1728
2027
|
if (!options.internalOperationExecutor) {
|
|
1729
2028
|
throw new ProviderError("Stateful internal operation executor is not configured.", {
|
|
@@ -1808,20 +2107,32 @@ function createServerAppWithCapabilityModules(provider, serverOptions, capabilit
|
|
|
1808
2107
|
}
|
|
1809
2108
|
const request = operationRequestFromForwardingEnvelope(envelope);
|
|
1810
2109
|
operationId = envelope.operationId;
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
}
|
|
1815
|
-
const output = await options.internalOperationExecutor({
|
|
1816
|
-
provider,
|
|
2110
|
+
requestScope.enrich({
|
|
2111
|
+
route: operationId,
|
|
2112
|
+
requestId: request.requestId,
|
|
1817
2113
|
operationId,
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
2114
|
+
headers: request.headers ?? {},
|
|
2115
|
+
correlation: { connectionId: envelope.connectionId },
|
|
2116
|
+
});
|
|
2117
|
+
const response = await requestScope.run(async () => {
|
|
2118
|
+
const ctx = createProviderContext(provider, request, operationId, options, state, requestScope, signal);
|
|
2119
|
+
if (deadlineAtMs !== undefined && deadlineAtMs <= Date.now()) {
|
|
2120
|
+
throw new StatefulRoutingDeadlineError(envelope.requestId, envelope.deadlineAt);
|
|
2121
|
+
}
|
|
2122
|
+
const output = await options.internalOperationExecutor({
|
|
2123
|
+
provider,
|
|
2124
|
+
operationId: operationId,
|
|
2125
|
+
ctx,
|
|
2126
|
+
request,
|
|
2127
|
+
internalStatefulForward: envelope,
|
|
2128
|
+
signal,
|
|
2129
|
+
});
|
|
2130
|
+
return c.json({ data: output });
|
|
2131
|
+
});
|
|
2132
|
+
return finalizeRequestResponse(requestScope, response, {
|
|
2133
|
+
kind: "completed",
|
|
2134
|
+
status: 200,
|
|
1822
2135
|
});
|
|
1823
|
-
logProviderSuccess(logger, provider, "operation", operationId || operation, request.requestId, 200, finishRequestCost(requestCost));
|
|
1824
|
-
return c.json({ data: output });
|
|
1825
2136
|
}
|
|
1826
2137
|
catch (error) {
|
|
1827
2138
|
const declaredErrorCode = declaredErrorCodeFor(error, operationId, operationErrorCodes);
|
|
@@ -1831,16 +2142,32 @@ function createServerAppWithCapabilityModules(provider, serverOptions, capabilit
|
|
|
1831
2142
|
c.header("Retry-After", String(STATEFUL_FORWARDING_REPLAY_RETRY_AFTER_SECONDS));
|
|
1832
2143
|
}
|
|
1833
2144
|
const requestId = extractRequestId(rawBody);
|
|
2145
|
+
requestScope.enrich({
|
|
2146
|
+
...(operationId ? { route: operationId, operationId } : {}),
|
|
2147
|
+
...(requestId ? { requestId } : {}),
|
|
2148
|
+
});
|
|
1834
2149
|
const observabilityDetails = errorObservabilityDetails(error, declaredErrorCode);
|
|
1835
|
-
|
|
1836
|
-
return
|
|
2150
|
+
const response = c.json(toErrorResponse(error, requestId, observabilityDetails), status);
|
|
2151
|
+
return finalizeRequestResponse(requestScope, response, {
|
|
2152
|
+
kind: "failed",
|
|
2153
|
+
status,
|
|
2154
|
+
error,
|
|
2155
|
+
});
|
|
1837
2156
|
}
|
|
1838
2157
|
});
|
|
1839
2158
|
app.post("/v1/:operation", async (c) => {
|
|
1840
2159
|
let rawBody;
|
|
1841
2160
|
const operation = c.req.param("operation");
|
|
1842
|
-
const
|
|
1843
|
-
|
|
2161
|
+
const requestScope = createRequestScope({
|
|
2162
|
+
provider,
|
|
2163
|
+
kind: "operation",
|
|
2164
|
+
route: operation,
|
|
2165
|
+
operationId: operation,
|
|
2166
|
+
headers: Object.fromEntries(c.req.raw.headers.entries()),
|
|
2167
|
+
logger,
|
|
2168
|
+
setHeader: (name, value) => c.header(name, value),
|
|
2169
|
+
declaredErrorCode: (error) => declaredErrorCodeFor(error, operation, operationErrorCodes),
|
|
2170
|
+
});
|
|
1844
2171
|
try {
|
|
1845
2172
|
rawBody = await c.req.raw
|
|
1846
2173
|
.clone()
|
|
@@ -1849,179 +2176,107 @@ function createServerAppWithCapabilityModules(provider, serverOptions, capabilit
|
|
|
1849
2176
|
const body = OperationRequestSchema.parse(rawBody);
|
|
1850
2177
|
const requestHeaders = Object.fromEntries(c.req.raw.headers.entries());
|
|
1851
2178
|
body.headers = { ...requestHeaders, ...body.headers };
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
}
|
|
1857
|
-
const
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
2179
|
+
requestScope.enrich({
|
|
2180
|
+
requestId: body.requestId,
|
|
2181
|
+
headers: body.headers,
|
|
2182
|
+
correlation: { connectionId: resolveOperationConnectionId(body) },
|
|
2183
|
+
});
|
|
2184
|
+
const streaming = provider.operations[operation]?.transport?.kind
|
|
2185
|
+
? provider.operations[operation]?.transport?.kind !== "json"
|
|
2186
|
+
: false;
|
|
2187
|
+
if (streaming)
|
|
2188
|
+
requestScope.watchAbort(c.req.raw.signal);
|
|
2189
|
+
const execute = async () => {
|
|
2190
|
+
const handled = await handleOperation(provider, body, operation, options, state, requestScope, c.req.raw.signal);
|
|
2191
|
+
const response = handled instanceof Response ? handled : c.json(handled);
|
|
2192
|
+
return streaming
|
|
2193
|
+
? responseWithRequestScopeHeaders(response, requestScope.snapshotHeaders())
|
|
2194
|
+
: response;
|
|
2195
|
+
};
|
|
2196
|
+
const response = streaming
|
|
2197
|
+
? await requestScope.runStreaming(execute)
|
|
2198
|
+
: await requestScope.run(execute);
|
|
2199
|
+
if (streaming)
|
|
2200
|
+
return response;
|
|
2201
|
+
return finalizeRequestResponse(requestScope, response, {
|
|
2202
|
+
kind: "completed",
|
|
2203
|
+
status: response.status,
|
|
2204
|
+
});
|
|
1862
2205
|
}
|
|
1863
2206
|
catch (error) {
|
|
1864
2207
|
const declaredErrorCode = declaredErrorCodeFor(error, operation, operationErrorCodes);
|
|
1865
2208
|
const status = toStatusCode(error, declaredErrorCode);
|
|
1866
2209
|
const requestId = extractRequestId(rawBody);
|
|
2210
|
+
requestScope.enrich({ ...(requestId ? { requestId } : {}) });
|
|
1867
2211
|
const observabilityDetails = errorObservabilityDetails(error, declaredErrorCode);
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
});
|
|
1875
|
-
app.post("/auth/start", async (c) => {
|
|
1876
|
-
let rawBody;
|
|
1877
|
-
const proxyTelemetry = new ProxyTelemetryCollector();
|
|
1878
|
-
const requestCost = startRequestCost();
|
|
1879
|
-
try {
|
|
1880
|
-
rawBody = await c.req.raw
|
|
1881
|
-
.clone()
|
|
1882
|
-
.json()
|
|
1883
|
-
.catch(() => undefined);
|
|
1884
|
-
const body = withAuthRequestHeaders(AuthFlowRequestSchema.parse(rawBody), c.req.raw.headers);
|
|
1885
|
-
const response = await handleAuthFlow(provider, body, "start", options, state, proxyTelemetry, c.req.raw.signal);
|
|
1886
|
-
logProviderSuccess(logger, provider, "auth", "start", body.requestId, response instanceof Response ? response.status : 200, finishRequestCost(requestCost), proxyTelemetry);
|
|
1887
|
-
if (response instanceof Response)
|
|
1888
|
-
return responseWithProviderTelemetry(response, proxyTelemetry);
|
|
1889
|
-
const telemetryHeader = proxyTelemetry.toHeaderValue();
|
|
1890
|
-
if (telemetryHeader)
|
|
1891
|
-
c.header(PROVIDER_TELEMETRY_HEADER, telemetryHeader);
|
|
1892
|
-
return c.json(response);
|
|
1893
|
-
}
|
|
1894
|
-
catch (error) {
|
|
1895
|
-
const status = toStatusCode(error);
|
|
1896
|
-
const requestId = extractRequestId(rawBody);
|
|
1897
|
-
const observabilityDetails = errorObservabilityDetails(error);
|
|
1898
|
-
logProviderError(logger, provider, "auth", "start", requestId, error, status, finishRequestCost(requestCost), undefined, proxyTelemetry, observabilityDetails);
|
|
1899
|
-
const telemetryHeader = proxyTelemetry.toHeaderValue();
|
|
1900
|
-
if (telemetryHeader)
|
|
1901
|
-
c.header(PROVIDER_TELEMETRY_HEADER, telemetryHeader);
|
|
1902
|
-
return responseWithErrorObservability(c.json(toErrorResponse(error, requestId, observabilityDetails), status), observabilityDetails);
|
|
1903
|
-
}
|
|
1904
|
-
});
|
|
1905
|
-
app.post("/auth/continue", async (c) => {
|
|
1906
|
-
let rawBody;
|
|
1907
|
-
const proxyTelemetry = new ProxyTelemetryCollector();
|
|
1908
|
-
const requestCost = startRequestCost();
|
|
1909
|
-
try {
|
|
1910
|
-
rawBody = await c.req.raw
|
|
1911
|
-
.clone()
|
|
1912
|
-
.json()
|
|
1913
|
-
.catch(() => undefined);
|
|
1914
|
-
const body = withAuthRequestHeaders(AuthFlowRequestSchema.parse(rawBody), c.req.raw.headers);
|
|
1915
|
-
const response = await handleAuthFlow(provider, body, "continue", options, state, proxyTelemetry, c.req.raw.signal);
|
|
1916
|
-
logProviderSuccess(logger, provider, "auth", "continue", body.requestId, response instanceof Response ? response.status : 200, finishRequestCost(requestCost), proxyTelemetry);
|
|
1917
|
-
if (response instanceof Response)
|
|
1918
|
-
return responseWithProviderTelemetry(response, proxyTelemetry);
|
|
1919
|
-
const telemetryHeader = proxyTelemetry.toHeaderValue();
|
|
1920
|
-
if (telemetryHeader)
|
|
1921
|
-
c.header(PROVIDER_TELEMETRY_HEADER, telemetryHeader);
|
|
1922
|
-
return c.json(response);
|
|
1923
|
-
}
|
|
1924
|
-
catch (error) {
|
|
1925
|
-
const status = toStatusCode(error);
|
|
1926
|
-
const requestId = extractRequestId(rawBody);
|
|
1927
|
-
const observabilityDetails = errorObservabilityDetails(error);
|
|
1928
|
-
logProviderError(logger, provider, "auth", "continue", requestId, error, status, finishRequestCost(requestCost), undefined, proxyTelemetry, observabilityDetails);
|
|
1929
|
-
const telemetryHeader = proxyTelemetry.toHeaderValue();
|
|
1930
|
-
if (telemetryHeader)
|
|
1931
|
-
c.header(PROVIDER_TELEMETRY_HEADER, telemetryHeader);
|
|
1932
|
-
return responseWithErrorObservability(c.json(toErrorResponse(error, requestId, observabilityDetails), status), observabilityDetails);
|
|
1933
|
-
}
|
|
1934
|
-
});
|
|
1935
|
-
app.post("/auth/poll", async (c) => {
|
|
1936
|
-
let rawBody;
|
|
1937
|
-
const proxyTelemetry = new ProxyTelemetryCollector();
|
|
1938
|
-
const requestCost = startRequestCost();
|
|
1939
|
-
try {
|
|
1940
|
-
rawBody = await c.req.raw
|
|
1941
|
-
.clone()
|
|
1942
|
-
.json()
|
|
1943
|
-
.catch(() => undefined);
|
|
1944
|
-
const body = withAuthRequestHeaders(AuthFlowRequestSchema.parse(rawBody), c.req.raw.headers);
|
|
1945
|
-
const response = await handleAuthFlow(provider, body, "poll", options, state, proxyTelemetry, c.req.raw.signal);
|
|
1946
|
-
logProviderSuccess(logger, provider, "auth", "poll", body.requestId, response instanceof Response ? response.status : 200, finishRequestCost(requestCost), proxyTelemetry);
|
|
1947
|
-
if (response instanceof Response)
|
|
1948
|
-
return responseWithProviderTelemetry(response, proxyTelemetry);
|
|
1949
|
-
const telemetryHeader = proxyTelemetry.toHeaderValue();
|
|
1950
|
-
if (telemetryHeader)
|
|
1951
|
-
c.header(PROVIDER_TELEMETRY_HEADER, telemetryHeader);
|
|
1952
|
-
return c.json(response);
|
|
1953
|
-
}
|
|
1954
|
-
catch (error) {
|
|
1955
|
-
const status = toStatusCode(error);
|
|
1956
|
-
const requestId = extractRequestId(rawBody);
|
|
1957
|
-
const observabilityDetails = errorObservabilityDetails(error);
|
|
1958
|
-
logProviderError(logger, provider, "auth", "poll", requestId, error, status, finishRequestCost(requestCost), undefined, proxyTelemetry, observabilityDetails);
|
|
1959
|
-
const telemetryHeader = proxyTelemetry.toHeaderValue();
|
|
1960
|
-
if (telemetryHeader)
|
|
1961
|
-
c.header(PROVIDER_TELEMETRY_HEADER, telemetryHeader);
|
|
1962
|
-
return responseWithErrorObservability(c.json(toErrorResponse(error, requestId, observabilityDetails), status), observabilityDetails);
|
|
1963
|
-
}
|
|
1964
|
-
});
|
|
1965
|
-
app.post("/auth/refresh", async (c) => {
|
|
1966
|
-
let rawBody;
|
|
1967
|
-
const proxyTelemetry = new ProxyTelemetryCollector();
|
|
1968
|
-
const requestCost = startRequestCost();
|
|
1969
|
-
try {
|
|
1970
|
-
rawBody = await c.req.raw
|
|
1971
|
-
.clone()
|
|
1972
|
-
.json()
|
|
1973
|
-
.catch(() => undefined);
|
|
1974
|
-
const body = withAuthRequestHeaders(AuthFlowRequestSchema.parse(rawBody), c.req.raw.headers);
|
|
1975
|
-
const response = await handleAuthFlow(provider, body, "refresh", options, state, proxyTelemetry, c.req.raw.signal);
|
|
1976
|
-
logProviderSuccess(logger, provider, "auth", "refresh", body.requestId, response instanceof Response ? response.status : 200, finishRequestCost(requestCost), proxyTelemetry);
|
|
1977
|
-
if (response instanceof Response)
|
|
1978
|
-
return responseWithProviderTelemetry(response, proxyTelemetry);
|
|
1979
|
-
const telemetryHeader = proxyTelemetry.toHeaderValue();
|
|
1980
|
-
if (telemetryHeader)
|
|
1981
|
-
c.header(PROVIDER_TELEMETRY_HEADER, telemetryHeader);
|
|
1982
|
-
return c.json(response);
|
|
1983
|
-
}
|
|
1984
|
-
catch (error) {
|
|
1985
|
-
const status = toStatusCode(error);
|
|
1986
|
-
const requestId = extractRequestId(rawBody);
|
|
1987
|
-
const observabilityDetails = errorObservabilityDetails(error);
|
|
1988
|
-
logProviderError(logger, provider, "auth", "refresh", requestId, error, status, finishRequestCost(requestCost), undefined, proxyTelemetry, observabilityDetails);
|
|
1989
|
-
const telemetryHeader = proxyTelemetry.toHeaderValue();
|
|
1990
|
-
if (telemetryHeader)
|
|
1991
|
-
c.header(PROVIDER_TELEMETRY_HEADER, telemetryHeader);
|
|
1992
|
-
return responseWithErrorObservability(c.json(toErrorResponse(error, requestId, observabilityDetails), status), observabilityDetails);
|
|
1993
|
-
}
|
|
1994
|
-
});
|
|
1995
|
-
app.post("/auth/disconnect", async (c) => {
|
|
1996
|
-
let rawBody;
|
|
1997
|
-
const proxyTelemetry = new ProxyTelemetryCollector();
|
|
1998
|
-
const requestCost = startRequestCost();
|
|
1999
|
-
try {
|
|
2000
|
-
rawBody = await c.req.raw
|
|
2001
|
-
.clone()
|
|
2002
|
-
.json()
|
|
2003
|
-
.catch(() => undefined);
|
|
2004
|
-
const body = withAuthRequestHeaders(AuthFlowRequestSchema.parse(rawBody), c.req.raw.headers);
|
|
2005
|
-
const response = await handleAuthFlow(provider, body, "abort", options, state, proxyTelemetry, c.req.raw.signal);
|
|
2006
|
-
logProviderSuccess(logger, provider, "auth", "disconnect", body.requestId, response instanceof Response ? response.status : 200, finishRequestCost(requestCost), proxyTelemetry);
|
|
2007
|
-
if (response instanceof Response)
|
|
2008
|
-
return responseWithProviderTelemetry(response, proxyTelemetry);
|
|
2009
|
-
const telemetryHeader = proxyTelemetry.toHeaderValue();
|
|
2010
|
-
if (telemetryHeader)
|
|
2011
|
-
c.header(PROVIDER_TELEMETRY_HEADER, telemetryHeader);
|
|
2012
|
-
return c.json(response);
|
|
2013
|
-
}
|
|
2014
|
-
catch (error) {
|
|
2015
|
-
const status = toStatusCode(error);
|
|
2016
|
-
const requestId = extractRequestId(rawBody);
|
|
2017
|
-
const observabilityDetails = errorObservabilityDetails(error);
|
|
2018
|
-
logProviderError(logger, provider, "auth", "disconnect", requestId, error, status, finishRequestCost(requestCost), undefined, proxyTelemetry, observabilityDetails);
|
|
2019
|
-
const telemetryHeader = proxyTelemetry.toHeaderValue();
|
|
2020
|
-
if (telemetryHeader)
|
|
2021
|
-
c.header(PROVIDER_TELEMETRY_HEADER, telemetryHeader);
|
|
2022
|
-
return responseWithErrorObservability(c.json(toErrorResponse(error, requestId, observabilityDetails), status), observabilityDetails);
|
|
2212
|
+
const response = c.json(toErrorResponse(error, requestId, observabilityDetails), status);
|
|
2213
|
+
return finalizeRequestResponse(requestScope, response, {
|
|
2214
|
+
kind: "failed",
|
|
2215
|
+
status,
|
|
2216
|
+
error,
|
|
2217
|
+
});
|
|
2023
2218
|
}
|
|
2024
2219
|
});
|
|
2220
|
+
const authRoutes = [
|
|
2221
|
+
{ path: "/auth/start", flowRoute: "start", logRoute: "start" },
|
|
2222
|
+
{ path: "/auth/continue", flowRoute: "continue", logRoute: "continue" },
|
|
2223
|
+
{ path: "/auth/poll", flowRoute: "poll", logRoute: "poll" },
|
|
2224
|
+
{ path: "/auth/refresh", flowRoute: "refresh", logRoute: "refresh" },
|
|
2225
|
+
{ path: "/auth/disconnect", flowRoute: "abort", logRoute: "disconnect" },
|
|
2226
|
+
];
|
|
2227
|
+
for (const { path, flowRoute, logRoute } of authRoutes) {
|
|
2228
|
+
app.post(path, async (c) => {
|
|
2229
|
+
let rawBody;
|
|
2230
|
+
const requestScope = createRequestScope({
|
|
2231
|
+
provider,
|
|
2232
|
+
kind: "auth",
|
|
2233
|
+
route: logRoute,
|
|
2234
|
+
headers: Object.fromEntries(c.req.raw.headers.entries()),
|
|
2235
|
+
logger,
|
|
2236
|
+
setHeader: (name, value) => c.header(name, value),
|
|
2237
|
+
});
|
|
2238
|
+
try {
|
|
2239
|
+
rawBody = await c.req.raw
|
|
2240
|
+
.clone()
|
|
2241
|
+
.json()
|
|
2242
|
+
.catch(() => undefined);
|
|
2243
|
+
const body = withAuthRequestHeaders(AuthFlowRequestSchema.parse(rawBody), c.req.raw.headers);
|
|
2244
|
+
requestScope.enrich({
|
|
2245
|
+
requestId: body.requestId,
|
|
2246
|
+
flowId: body.flowId,
|
|
2247
|
+
headers: body.headers ?? {},
|
|
2248
|
+
correlation: {
|
|
2249
|
+
connectionId: resolveOperationConnectionId(body),
|
|
2250
|
+
flowId: body.flowId,
|
|
2251
|
+
tenantId: body.tenantId,
|
|
2252
|
+
requestedProviderId: body.providerId !== undefined && body.providerId !== provider.id
|
|
2253
|
+
? body.providerId
|
|
2254
|
+
: undefined,
|
|
2255
|
+
},
|
|
2256
|
+
});
|
|
2257
|
+
const response = await requestScope.run(async () => {
|
|
2258
|
+
const handled = await handleAuthFlow(provider, body, flowRoute, options, state, requestScope, c.req.raw.signal);
|
|
2259
|
+
return handled instanceof Response ? handled : c.json(handled);
|
|
2260
|
+
});
|
|
2261
|
+
return finalizeRequestResponse(requestScope, response, {
|
|
2262
|
+
kind: "completed",
|
|
2263
|
+
status: response.status,
|
|
2264
|
+
});
|
|
2265
|
+
}
|
|
2266
|
+
catch (error) {
|
|
2267
|
+
const status = toStatusCode(error);
|
|
2268
|
+
const requestId = extractRequestId(rawBody);
|
|
2269
|
+
requestScope.enrich({ ...(requestId ? { requestId } : {}) });
|
|
2270
|
+
const observabilityDetails = errorObservabilityDetails(error);
|
|
2271
|
+
const response = c.json(toErrorResponse(error, requestId, observabilityDetails), status);
|
|
2272
|
+
return finalizeRequestResponse(requestScope, response, {
|
|
2273
|
+
kind: "failed",
|
|
2274
|
+
status,
|
|
2275
|
+
error,
|
|
2276
|
+
});
|
|
2277
|
+
}
|
|
2278
|
+
});
|
|
2279
|
+
}
|
|
2025
2280
|
return app;
|
|
2026
2281
|
}
|
|
2027
2282
|
function validateStatefulServerConfig(options) {
|