@apifuse/provider-sdk 2.2.0-beta.2 → 2.2.0-beta.4
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 +14 -0
- package/CHANGELOG.md +8 -0
- package/bin/apifuse-check.ts +18 -58
- package/bin/apifuse-create.ts +1 -1
- package/bin/apifuse-dev.ts +27 -52
- package/bin/apifuse-pack-smoke.ts +22 -81
- package/bin/apifuse-pack-types.ts +266 -0
- package/bin/apifuse-perf.ts +45 -127
- package/bin/apifuse-record.ts +23 -61
- package/bin/apifuse-submit-check.ts +73 -30
- package/bin/apifuse.ts +1 -1
- package/bin/submit-check-xml-semantics.ts +204 -0
- package/bin/submit-check-xml.ts +134 -0
- package/dist/auth-turn/index.d.ts +1 -1
- package/dist/auth.d.ts +2 -2
- package/dist/auth.js +9 -18
- package/dist/ceremonies/index.d.ts +1 -1
- package/dist/ceremonies/index.js +8 -18
- package/dist/config/loader.d.ts +2 -2
- package/dist/config/loader.js +19 -42
- package/dist/contract-serialization.d.ts +2 -2
- package/dist/contract-serialization.js +3 -6
- package/dist/contract-types.d.ts +2 -2
- package/dist/contract.d.ts +3 -3
- package/dist/contract.js +4 -6
- package/dist/define.d.ts +8 -1
- package/dist/define.js +57 -115
- package/dist/dev.d.ts +1 -1
- package/dist/dev.js +1 -1
- package/dist/errors.d.ts +4 -1
- package/dist/errors.js +48 -0
- package/dist/i18n/catalog.d.ts +2 -2
- package/dist/i18n/catalog.js +4 -10
- package/dist/i18n/index.d.ts +2 -2
- package/dist/i18n/index.js +2 -2
- package/dist/i18n/keys.d.ts +2 -2
- package/dist/index.d.ts +43 -42
- package/dist/index.js +38 -37
- package/dist/lint.js +8 -15
- package/dist/provider.d.ts +11 -11
- package/dist/provider.js +9 -9
- package/dist/public-schema-field-lint.d.ts +1 -1
- package/dist/recipes/gov-api.js +1 -1
- package/dist/runtime/auth-flow.d.ts +1 -1
- package/dist/runtime/auth-flow.js +3 -3
- package/dist/runtime/browser.d.ts +1 -1
- package/dist/runtime/browser.js +15 -29
- package/dist/runtime/cache.d.ts +1 -1
- package/dist/runtime/cache.js +4 -8
- package/dist/runtime/choice.d.ts +1 -1
- package/dist/runtime/choice.js +12 -34
- package/dist/runtime/credential.d.ts +1 -1
- package/dist/runtime/credential.js +1 -1
- package/dist/runtime/env.d.ts +1 -1
- package/dist/runtime/executor.d.ts +1 -1
- package/dist/runtime/executor.js +8 -3
- package/dist/runtime/http.d.ts +2 -2
- package/dist/runtime/http.js +5 -5
- package/dist/runtime/insights.d.ts +1 -1
- package/dist/runtime/insights.js +6 -13
- package/dist/runtime/instrumentation.d.ts +2 -2
- package/dist/runtime/instrumentation.js +5 -15
- package/dist/runtime/keyring.js +1 -1
- package/dist/runtime/namespace.js +1 -1
- package/dist/runtime/otlp.d.ts +1 -1
- package/dist/runtime/perf.d.ts +1 -1
- package/dist/runtime/provider.d.ts +1 -1
- package/dist/runtime/provider.js +1 -2
- package/dist/runtime/proxy-errors.d.ts +1 -1
- package/dist/runtime/proxy-errors.js +3 -5
- package/dist/runtime/proxy-retry-policy.d.ts +2 -2
- package/dist/runtime/proxy-retry-policy.js +2 -2
- package/dist/runtime/proxy-telemetry.d.ts +1 -1
- package/dist/runtime/proxy-telemetry.js +16 -48
- package/dist/runtime/redis.d.ts +1 -1
- package/dist/runtime/redis.js +2 -4
- package/dist/runtime/request-options.d.ts +1 -1
- package/dist/runtime/state.d.ts +2 -2
- package/dist/runtime/state.js +3 -3
- package/dist/runtime/stealth.d.ts +2 -2
- package/dist/runtime/stealth.js +6 -6
- package/dist/runtime/stt.d.ts +1 -1
- package/dist/runtime/stt.js +11 -15
- package/dist/runtime/trace.d.ts +2 -2
- package/dist/runtime/trace.js +2 -4
- package/dist/runtime/waterfall.d.ts +1 -1
- package/dist/schema.d.ts +1 -1
- package/dist/schema.js +7 -15
- package/dist/serve.d.ts +1 -1
- package/dist/serve.js +1 -1
- package/dist/server/index.d.ts +7 -7
- package/dist/server/index.js +6 -6
- package/dist/server/self-test-redaction.d.ts +1 -1
- package/dist/server/self-test-redaction.js +1 -1
- package/dist/server/self-test.d.ts +3 -3
- package/dist/server/self-test.js +3 -3
- package/dist/server/serve.d.ts +3 -3
- package/dist/server/serve.js +49 -57
- package/dist/server/types.js +2 -7
- package/dist/stealth/profiles.d.ts +1 -1
- package/dist/stealth/profiles.js +5 -14
- package/dist/stream.d.ts +1 -1
- package/dist/testing/index.d.ts +2 -2
- package/dist/testing/index.js +2 -2
- package/dist/testing/run.d.ts +1 -1
- package/dist/testing/run.js +7 -14
- package/dist/types.d.ts +57 -0
- package/dist/user-input.d.ts +30 -0
- package/dist/user-input.js +66 -0
- package/package.json +7 -4
- package/src/auth-turn/index.ts +1 -1
- package/src/auth.ts +28 -86
- package/src/ceremonies/index.ts +24 -78
- package/src/config/loader.ts +58 -176
- package/src/contract-serialization.ts +5 -11
- package/src/contract-types.ts +2 -2
- package/src/contract.ts +12 -28
- package/src/define.ts +174 -495
- package/src/dev.ts +4 -9
- package/src/errors.ts +58 -5
- package/src/i18n/catalog.ts +10 -32
- package/src/i18n/index.ts +2 -2
- package/src/i18n/keys.ts +5 -11
- package/src/index.ts +44 -42
- package/src/lint.ts +89 -159
- package/src/provider.ts +15 -11
- package/src/public-schema-field-lint.ts +7 -33
- package/src/recipes/gov-api.ts +2 -5
- package/src/runtime/auth-flow.ts +5 -7
- package/src/runtime/browser.ts +64 -187
- package/src/runtime/cache.ts +20 -67
- package/src/runtime/choice.ts +55 -129
- package/src/runtime/credential.ts +2 -2
- package/src/runtime/env.ts +1 -1
- package/src/runtime/executor.ts +15 -20
- package/src/runtime/http.ts +7 -7
- package/src/runtime/insights.ts +15 -53
- package/src/runtime/instrumentation.ts +19 -58
- package/src/runtime/keyring.ts +7 -19
- package/src/runtime/namespace.ts +2 -7
- package/src/runtime/otlp.ts +12 -23
- package/src/runtime/perf.ts +1 -1
- package/src/runtime/provider.ts +4 -9
- package/src/runtime/proxy-errors.ts +17 -38
- package/src/runtime/proxy-retry-policy.ts +3 -3
- package/src/runtime/proxy-telemetry.ts +23 -72
- package/src/runtime/redis.ts +4 -12
- package/src/runtime/request-options.ts +4 -13
- package/src/runtime/state.ts +30 -109
- package/src/runtime/stealth.ts +8 -8
- package/src/runtime/stt.ts +38 -94
- package/src/runtime/trace.ts +14 -44
- package/src/runtime/waterfall.ts +5 -18
- package/src/schema.ts +23 -84
- package/src/serve.ts +1 -1
- package/src/server/index.ts +7 -7
- package/src/server/self-test-redaction.ts +2 -2
- package/src/server/self-test.ts +5 -5
- package/src/server/serve.ts +103 -252
- package/src/server/types.ts +6 -19
- package/src/stealth/profiles.ts +10 -26
- package/src/stream.ts +8 -19
- package/src/testing/index.ts +2 -2
- package/src/testing/run.ts +19 -67
- package/src/types.ts +58 -0
- package/src/user-input.ts +118 -0
package/src/server/serve.ts
CHANGED
|
@@ -3,62 +3,60 @@ import { join } from "node:path";
|
|
|
3
3
|
|
|
4
4
|
import { Hono } from "hono";
|
|
5
5
|
import { z } from "zod";
|
|
6
|
-
import { AuthAbortError, createAuthFlowHelpers } from "../auth";
|
|
6
|
+
import { AuthAbortError, createAuthFlowHelpers } from "../auth.js";
|
|
7
7
|
import {
|
|
8
8
|
AuthError,
|
|
9
|
+
isProviderError,
|
|
10
|
+
isSessionExpiredError,
|
|
11
|
+
isTransportError,
|
|
9
12
|
ProviderError,
|
|
10
|
-
|
|
11
|
-
TransportError,
|
|
12
|
-
} from "../errors";
|
|
13
|
+
} from "../errors.js";
|
|
13
14
|
import {
|
|
14
15
|
loadProviderLocaleCatalogs,
|
|
15
16
|
localizeAuthTurn,
|
|
16
17
|
type ProviderLocaleCatalogMap,
|
|
17
|
-
} from "../i18n/catalog";
|
|
18
|
-
import type { ProviderLocale } from "../i18n/keys";
|
|
18
|
+
} from "../i18n/catalog.js";
|
|
19
|
+
import type { ProviderLocale } from "../i18n/keys.js";
|
|
19
20
|
import {
|
|
20
21
|
categoryForStatus,
|
|
21
22
|
isRetryableCategory,
|
|
22
23
|
PROVIDER_OBSERVABILITY_TAXONOMY_VERSION,
|
|
23
24
|
type ProviderErrorCategory,
|
|
24
|
-
} from "../observability";
|
|
25
|
-
import { createScratchpad } from "../runtime/auth-flow";
|
|
26
|
-
import { createBrowserClient } from "../runtime/browser";
|
|
27
|
-
import { createProviderCache } from "../runtime/cache";
|
|
25
|
+
} from "../observability.js";
|
|
26
|
+
import { createScratchpad } from "../runtime/auth-flow.js";
|
|
27
|
+
import { createBrowserClient } from "../runtime/browser.js";
|
|
28
|
+
import { createProviderCache } from "../runtime/cache.js";
|
|
28
29
|
import {
|
|
29
30
|
createProviderChoiceContext,
|
|
30
31
|
PROVIDER_RUNTIME_CHOICE_TOKEN_MASTER_SECRET_ENV,
|
|
31
|
-
} from "../runtime/choice";
|
|
32
|
-
import { createCredentialContext } from "../runtime/credential";
|
|
33
|
-
import { createEnvContext } from "../runtime/env";
|
|
34
|
-
import { executeOperation } from "../runtime/executor";
|
|
35
|
-
import { createHttpClient } from "../runtime/http";
|
|
36
|
-
import { wrapWithInstrumentation } from "../runtime/instrumentation";
|
|
37
|
-
import { getProviderBaseUrl } from "../runtime/provider";
|
|
32
|
+
} from "../runtime/choice.js";
|
|
33
|
+
import { createCredentialContext } from "../runtime/credential.js";
|
|
34
|
+
import { createEnvContext } from "../runtime/env.js";
|
|
35
|
+
import { executeOperation } from "../runtime/executor.js";
|
|
36
|
+
import { createHttpClient } from "../runtime/http.js";
|
|
37
|
+
import { wrapWithInstrumentation } from "../runtime/instrumentation.js";
|
|
38
|
+
import { getProviderBaseUrl } from "../runtime/provider.js";
|
|
38
39
|
import {
|
|
39
40
|
PROXY_AUTH_IP_DENIED_CODE,
|
|
40
41
|
PROXY_EDGE_AUTH_REJECTED_CODE,
|
|
41
42
|
PROXY_POOL_EXHAUSTED_CODE,
|
|
42
|
-
} from "../runtime/proxy-errors";
|
|
43
|
-
import {
|
|
44
|
-
PROVIDER_TELEMETRY_HEADER,
|
|
45
|
-
ProxyTelemetryCollector,
|
|
46
|
-
} from "../runtime/proxy-telemetry";
|
|
43
|
+
} from "../runtime/proxy-errors.js";
|
|
44
|
+
import { PROVIDER_TELEMETRY_HEADER, ProxyTelemetryCollector } from "../runtime/proxy-telemetry.js";
|
|
47
45
|
import {
|
|
48
46
|
createProviderRuntimeStateFromEnv,
|
|
49
47
|
createUnsupportedProviderRuntimeState,
|
|
50
|
-
} from "../runtime/state";
|
|
51
|
-
import { createStealthClient } from "../runtime/stealth";
|
|
52
|
-
import { createSttClientFromEnv } from "../runtime/stt";
|
|
53
|
-
import { createTraceContext } from "../runtime/trace";
|
|
54
|
-
import { parseSchema } from "../schema";
|
|
55
|
-
import { getStealthProfile } from "../stealth/profiles";
|
|
48
|
+
} from "../runtime/state.js";
|
|
49
|
+
import { createStealthClient } from "../runtime/stealth.js";
|
|
50
|
+
import { createSttClientFromEnv } from "../runtime/stt.js";
|
|
51
|
+
import { createTraceContext } from "../runtime/trace.js";
|
|
52
|
+
import { parseSchema } from "../schema.js";
|
|
53
|
+
import { getStealthProfile } from "../stealth/profiles.js";
|
|
56
54
|
import {
|
|
57
55
|
APIFUSE_STREAM_DONE_EVENT,
|
|
58
56
|
APIFUSE_STREAM_ERROR_EVENT,
|
|
59
57
|
encodeSseEvent,
|
|
60
58
|
error as streamError,
|
|
61
|
-
} from "../stream";
|
|
59
|
+
} from "../stream.js";
|
|
62
60
|
import type {
|
|
63
61
|
AuthContext,
|
|
64
62
|
AuthTurn,
|
|
@@ -75,13 +73,9 @@ import type {
|
|
|
75
73
|
ProviderStreamEvent,
|
|
76
74
|
StealthClient,
|
|
77
75
|
SttContext,
|
|
78
|
-
} from "../types";
|
|
79
|
-
import {
|
|
80
|
-
|
|
81
|
-
createSelfTestInvoke,
|
|
82
|
-
resolveSelfTestPort,
|
|
83
|
-
} from "./self-test";
|
|
84
|
-
import { resolveSelfTestMasterSecrets } from "./self-test-token";
|
|
76
|
+
} from "../types.js";
|
|
77
|
+
import { createSelfTestApp, createSelfTestInvoke, resolveSelfTestPort } from "./self-test.js";
|
|
78
|
+
import { resolveSelfTestMasterSecrets } from "./self-test-token.js";
|
|
85
79
|
import {
|
|
86
80
|
type AuthFlowRequest,
|
|
87
81
|
AuthFlowRequestSchema,
|
|
@@ -92,7 +86,7 @@ import {
|
|
|
92
86
|
OperationRequestSchema,
|
|
93
87
|
type OperationResponse,
|
|
94
88
|
type OperationSuccessResponse,
|
|
95
|
-
} from "./types";
|
|
89
|
+
} from "./types.js";
|
|
96
90
|
|
|
97
91
|
const DEFAULT_HOST = "0.0.0.0";
|
|
98
92
|
const DEFAULT_PORT = 3000;
|
|
@@ -156,9 +150,7 @@ function createStealthStub(): StealthClient {
|
|
|
156
150
|
};
|
|
157
151
|
}
|
|
158
152
|
|
|
159
|
-
function getProviderStealthBaseUrl(
|
|
160
|
-
provider: ProviderDefinition,
|
|
161
|
-
): string | undefined {
|
|
153
|
+
function getProviderStealthBaseUrl(provider: ProviderDefinition): string | undefined {
|
|
162
154
|
const baseUrl = getProviderBaseUrl(provider);
|
|
163
155
|
if (baseUrl) {
|
|
164
156
|
return baseUrl;
|
|
@@ -168,15 +160,10 @@ function getProviderStealthBaseUrl(
|
|
|
168
160
|
}
|
|
169
161
|
|
|
170
162
|
function getProviderStealthProfile(provider: ProviderDefinition) {
|
|
171
|
-
return provider.stealth?.profile
|
|
172
|
-
? getStealthProfile(provider.stealth.profile)
|
|
173
|
-
: undefined;
|
|
163
|
+
return provider.stealth?.profile ? getStealthProfile(provider.stealth.profile) : undefined;
|
|
174
164
|
}
|
|
175
165
|
|
|
176
|
-
function isProductionProviderBrowserMode(
|
|
177
|
-
provider: ProviderDefinition,
|
|
178
|
-
env = process.env,
|
|
179
|
-
): boolean {
|
|
166
|
+
function isProductionProviderBrowserMode(provider: ProviderDefinition, env = process.env): boolean {
|
|
180
167
|
if (provider.runtime !== "browser") {
|
|
181
168
|
return false;
|
|
182
169
|
}
|
|
@@ -185,9 +172,7 @@ function isProductionProviderBrowserMode(
|
|
|
185
172
|
return true;
|
|
186
173
|
}
|
|
187
174
|
|
|
188
|
-
return
|
|
189
|
-
env.NODE_ENV === "production" && env.APIFUSE__PROVIDER__ID === provider.id
|
|
190
|
-
);
|
|
175
|
+
return env.NODE_ENV === "production" && env.APIFUSE__PROVIDER__ID === provider.id;
|
|
191
176
|
}
|
|
192
177
|
|
|
193
178
|
export function resolveProviderProxyAffinityKey(
|
|
@@ -195,21 +180,16 @@ export function resolveProviderProxyAffinityKey(
|
|
|
195
180
|
request: OperationRequest,
|
|
196
181
|
operationId: string,
|
|
197
182
|
): string {
|
|
198
|
-
const connectionKey =
|
|
199
|
-
resolveOperationConnectionId(request) ?? request.connection?.externalRef;
|
|
183
|
+
const connectionKey = resolveOperationConnectionId(request) ?? request.connection?.externalRef;
|
|
200
184
|
const affinity =
|
|
201
|
-
typeof provider.proxy === "object"
|
|
202
|
-
? provider.proxy.session?.affinity
|
|
203
|
-
: undefined;
|
|
185
|
+
typeof provider.proxy === "object" ? provider.proxy.session?.affinity : undefined;
|
|
204
186
|
if (affinity === "operation") {
|
|
205
187
|
return `${provider.id}/${operationId}`;
|
|
206
188
|
}
|
|
207
189
|
return connectionKey ?? provider.id;
|
|
208
190
|
}
|
|
209
191
|
|
|
210
|
-
function resolveOperationConnectionId(
|
|
211
|
-
request: OperationRequest,
|
|
212
|
-
): string | undefined {
|
|
192
|
+
function resolveOperationConnectionId(request: OperationRequest): string | undefined {
|
|
213
193
|
return request.connection?.id ?? request.connectionId;
|
|
214
194
|
}
|
|
215
195
|
|
|
@@ -226,11 +206,7 @@ function createProviderContext(
|
|
|
226
206
|
const stealthProfile = getProviderStealthProfile(provider);
|
|
227
207
|
const proxyClientOptions = {
|
|
228
208
|
upstream: { proxy: provider.proxy },
|
|
229
|
-
affinityKey: resolveProviderProxyAffinityKey(
|
|
230
|
-
provider,
|
|
231
|
-
request,
|
|
232
|
-
operationId,
|
|
233
|
-
),
|
|
209
|
+
affinityKey: resolveProviderProxyAffinityKey(provider, request, operationId),
|
|
234
210
|
telemetry: proxyTelemetry,
|
|
235
211
|
};
|
|
236
212
|
let wrappedContext: ProviderContext | undefined;
|
|
@@ -269,11 +245,7 @@ function createProviderContext(
|
|
|
269
245
|
state,
|
|
270
246
|
stealth: stealthBaseUrl
|
|
271
247
|
? stealthProfile
|
|
272
|
-
? createStealthClient(
|
|
273
|
-
stealthBaseUrl,
|
|
274
|
-
stealthProfile.name,
|
|
275
|
-
stealthClientOptions,
|
|
276
|
-
)
|
|
248
|
+
? createStealthClient(stealthBaseUrl, stealthProfile.name, stealthClientOptions)
|
|
277
249
|
: createStealthClient(stealthBaseUrl, stealthClientOptions)
|
|
278
250
|
: createStealthStub(),
|
|
279
251
|
browser:
|
|
@@ -386,11 +358,7 @@ function createAuthFlowContext(
|
|
|
386
358
|
http: createHttpClient(baseUrl, proxyClientOptions),
|
|
387
359
|
stealth: stealthBaseUrl
|
|
388
360
|
? stealthProfile
|
|
389
|
-
? createStealthClient(
|
|
390
|
-
stealthBaseUrl,
|
|
391
|
-
stealthProfile.name,
|
|
392
|
-
stealthClientOptions,
|
|
393
|
-
)
|
|
361
|
+
? createStealthClient(stealthBaseUrl, stealthProfile.name, stealthClientOptions)
|
|
394
362
|
: createStealthClient(stealthBaseUrl, stealthClientOptions)
|
|
395
363
|
: createStealthStub(),
|
|
396
364
|
env: createEnvContext(provider.secrets?.map((secret) => secret.name)),
|
|
@@ -503,11 +471,8 @@ function zodDetails(error: z.ZodError): Array<{
|
|
|
503
471
|
}));
|
|
504
472
|
}
|
|
505
473
|
|
|
506
|
-
function toErrorResponse(
|
|
507
|
-
error
|
|
508
|
-
requestId?: string,
|
|
509
|
-
): OperationErrorResponse {
|
|
510
|
-
if (error instanceof ProviderError) {
|
|
474
|
+
function toErrorResponse(error: unknown, requestId?: string): OperationErrorResponse {
|
|
475
|
+
if (isProviderError(error)) {
|
|
511
476
|
const details = publicProviderErrorDetails(error);
|
|
512
477
|
return {
|
|
513
478
|
error: {
|
|
@@ -563,7 +528,12 @@ function isPlainRecord(value: unknown): value is Record<string, unknown> {
|
|
|
563
528
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
564
529
|
}
|
|
565
530
|
|
|
566
|
-
|
|
531
|
+
// Accepts `unknown` so the branded guards narrow cleanly from the top: the
|
|
532
|
+
// subtype error classes are structurally compatible with ProviderError, so
|
|
533
|
+
// narrowing from a ProviderError-typed value would collapse the negative branch
|
|
534
|
+
// to `never`. Narrowing from unknown avoids that while still recognizing errors
|
|
535
|
+
// from a duplicate SDK module instance.
|
|
536
|
+
function providerObservabilityDetails(error: unknown):
|
|
567
537
|
| {
|
|
568
538
|
category: ProviderErrorCategory;
|
|
569
539
|
taxonomyVersion: string;
|
|
@@ -576,14 +546,14 @@ function providerObservabilityDetails(error: ProviderError):
|
|
|
576
546
|
// operation (see design.md §4.3 D3). Without this branch the auth error would
|
|
577
547
|
// serialize as a bare 401 with no retryable/category, losing the refresh
|
|
578
548
|
// signal for exactly the retryOnAuthRefresh operations it is meant to enable.
|
|
579
|
-
if (error
|
|
549
|
+
if (isSessionExpiredError(error)) {
|
|
580
550
|
return {
|
|
581
551
|
category: error.options?.category ?? "credential_expired",
|
|
582
552
|
taxonomyVersion: PROVIDER_OBSERVABILITY_TAXONOMY_VERSION,
|
|
583
553
|
retryable: error.options?.retryable ?? false,
|
|
584
554
|
};
|
|
585
555
|
}
|
|
586
|
-
if (!(error
|
|
556
|
+
if (!isTransportError(error)) {
|
|
587
557
|
return undefined;
|
|
588
558
|
}
|
|
589
559
|
const isProxyPoolCode =
|
|
@@ -616,7 +586,7 @@ function providerObservabilityDetails(error: ProviderError):
|
|
|
616
586
|
}
|
|
617
587
|
|
|
618
588
|
function publicProviderErrorMessage(error: ProviderError): string {
|
|
619
|
-
if (error
|
|
589
|
+
if (isTransportError(error)) {
|
|
620
590
|
if (error.code === PROXY_AUTH_IP_DENIED_CODE) {
|
|
621
591
|
return error.message;
|
|
622
592
|
}
|
|
@@ -639,18 +609,16 @@ function publicProviderErrorMessage(error: ProviderError): string {
|
|
|
639
609
|
return error.message;
|
|
640
610
|
}
|
|
641
611
|
|
|
642
|
-
function toStatusCode(
|
|
643
|
-
error: unknown,
|
|
644
|
-
): 400 | 401 | 404 | 429 | 500 | 502 | 503 | 504 {
|
|
612
|
+
function toStatusCode(error: unknown): 400 | 401 | 404 | 429 | 500 | 502 | 503 | 504 {
|
|
645
613
|
if (error instanceof z.ZodError) {
|
|
646
614
|
return 400;
|
|
647
615
|
}
|
|
648
616
|
|
|
649
|
-
if (error
|
|
617
|
+
if (isTransportError(error)) {
|
|
650
618
|
return error.code === "transport_timeout" ? 504 : 502;
|
|
651
619
|
}
|
|
652
620
|
|
|
653
|
-
if (error
|
|
621
|
+
if (isProviderError(error)) {
|
|
654
622
|
switch (error.code) {
|
|
655
623
|
case "AUTH_REQUIRED":
|
|
656
624
|
case "reauth_required":
|
|
@@ -696,20 +664,15 @@ function logProviderError(
|
|
|
696
664
|
status: number,
|
|
697
665
|
cost: ProviderRequestCost,
|
|
698
666
|
): void {
|
|
699
|
-
const code =
|
|
700
|
-
error
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
: "internal_error";
|
|
667
|
+
const code = isProviderError(error)
|
|
668
|
+
? (error.code ?? "provider_error")
|
|
669
|
+
: error instanceof z.ZodError
|
|
670
|
+
? "invalid_request"
|
|
671
|
+
: "internal_error";
|
|
705
672
|
const errorClass = error instanceof Error ? error.name : typeof error;
|
|
706
673
|
const message = error instanceof Error ? error.message : String(error);
|
|
707
|
-
const details =
|
|
708
|
-
|
|
709
|
-
? providerObservabilityDetails(error)
|
|
710
|
-
: undefined;
|
|
711
|
-
const emit =
|
|
712
|
-
typeof logger === "function" ? logger : defaultProviderServerLogger;
|
|
674
|
+
const details = isProviderError(error) ? providerObservabilityDetails(error) : undefined;
|
|
675
|
+
const emit = typeof logger === "function" ? logger : defaultProviderServerLogger;
|
|
713
676
|
emit({
|
|
714
677
|
level: status >= 500 ? "error" : "warn",
|
|
715
678
|
event: "provider_request_failed",
|
|
@@ -722,7 +685,7 @@ function logProviderError(
|
|
|
722
685
|
code,
|
|
723
686
|
errorClass,
|
|
724
687
|
message,
|
|
725
|
-
...(error
|
|
688
|
+
...(isTransportError(error) && error.upstreamStatus
|
|
726
689
|
? { upstreamStatus: error.upstreamStatus }
|
|
727
690
|
: {}),
|
|
728
691
|
...(details
|
|
@@ -744,8 +707,7 @@ function logProviderCleanupError(
|
|
|
744
707
|
resource: "browser" | "stealth",
|
|
745
708
|
error: unknown,
|
|
746
709
|
): void {
|
|
747
|
-
const emit =
|
|
748
|
-
typeof logger === "function" ? logger : defaultProviderServerLogger;
|
|
710
|
+
const emit = typeof logger === "function" ? logger : defaultProviderServerLogger;
|
|
749
711
|
const errorClass = error instanceof Error ? error.name : typeof error;
|
|
750
712
|
const message = error instanceof Error ? error.message : String(error);
|
|
751
713
|
emit({
|
|
@@ -770,8 +732,7 @@ function logProviderSuccess(
|
|
|
770
732
|
status: number,
|
|
771
733
|
cost: ProviderRequestCost,
|
|
772
734
|
): void {
|
|
773
|
-
const emit =
|
|
774
|
-
typeof logger === "function" ? logger : defaultProviderServerLogger;
|
|
735
|
+
const emit = typeof logger === "function" ? logger : defaultProviderServerLogger;
|
|
775
736
|
emit({
|
|
776
737
|
level: "info",
|
|
777
738
|
event: "provider_request_completed",
|
|
@@ -817,18 +778,13 @@ function toJsonSuccessResponse(
|
|
|
817
778
|
};
|
|
818
779
|
}
|
|
819
780
|
|
|
820
|
-
function isAsyncIterable<T = unknown>(
|
|
821
|
-
value: unknown,
|
|
822
|
-
): value is AsyncIterable<T> {
|
|
781
|
+
function isAsyncIterable<T = unknown>(value: unknown): value is AsyncIterable<T> {
|
|
823
782
|
if (!value || typeof value !== "object") return false;
|
|
824
783
|
const iterator = Reflect.get(value, Symbol.asyncIterator);
|
|
825
784
|
return typeof iterator === "function";
|
|
826
785
|
}
|
|
827
786
|
|
|
828
|
-
function responseWithCleanup(
|
|
829
|
-
response: Response,
|
|
830
|
-
cleanup: RequestCleanup,
|
|
831
|
-
): Response {
|
|
787
|
+
function responseWithCleanup(response: Response, cleanup: RequestCleanup): Response {
|
|
832
788
|
if (!response.body) {
|
|
833
789
|
void cleanup();
|
|
834
790
|
return response;
|
|
@@ -877,10 +833,7 @@ async function validateSseEvent(
|
|
|
877
833
|
const transport = getSseTransport(operation);
|
|
878
834
|
const schema = transport?.events?.[event.event];
|
|
879
835
|
if (!schema) {
|
|
880
|
-
if (
|
|
881
|
-
event.event === APIFUSE_STREAM_ERROR_EVENT ||
|
|
882
|
-
event.event === APIFUSE_STREAM_DONE_EVENT
|
|
883
|
-
) {
|
|
836
|
+
if (event.event === APIFUSE_STREAM_ERROR_EVENT || event.event === APIFUSE_STREAM_DONE_EVENT) {
|
|
884
837
|
return event;
|
|
885
838
|
}
|
|
886
839
|
throw new ProviderError(
|
|
@@ -893,11 +846,7 @@ async function validateSseEvent(
|
|
|
893
846
|
},
|
|
894
847
|
);
|
|
895
848
|
}
|
|
896
|
-
const data = await parseSchema(
|
|
897
|
-
schema,
|
|
898
|
-
event.data,
|
|
899
|
-
`transport.events.${event.event}`,
|
|
900
|
-
);
|
|
849
|
+
const data = await parseSchema(schema, event.data, `transport.events.${event.event}`);
|
|
901
850
|
return { ...event, data };
|
|
902
851
|
}
|
|
903
852
|
|
|
@@ -917,8 +866,7 @@ function assertStreamPayloadWithinLimit(
|
|
|
917
866
|
throw new ProviderError(
|
|
918
867
|
`Stream ${kind} exceeded declared byte limit (${actualBytes} > ${maxBytes}).`,
|
|
919
868
|
{
|
|
920
|
-
code:
|
|
921
|
-
kind === "event" ? "STREAM_EVENT_TOO_LARGE" : "STREAM_CHUNK_TOO_LARGE",
|
|
869
|
+
code: kind === "event" ? "STREAM_EVENT_TOO_LARGE" : "STREAM_CHUNK_TOO_LARGE",
|
|
922
870
|
retryable: false,
|
|
923
871
|
category: "input_validation",
|
|
924
872
|
fix:
|
|
@@ -963,15 +911,10 @@ function toSseResponse(
|
|
|
963
911
|
const validated = await validateSseEvent(operation, next.value);
|
|
964
912
|
const encodedEvent = encodeSseEvent(validated);
|
|
965
913
|
const encodedBytes = encoder.encode(encodedEvent);
|
|
966
|
-
assertStreamPayloadWithinLimit(
|
|
967
|
-
encodedBytes.byteLength,
|
|
968
|
-
transport?.maxEventBytes,
|
|
969
|
-
"event",
|
|
970
|
-
);
|
|
914
|
+
assertStreamPayloadWithinLimit(encodedBytes.byteLength, transport?.maxEventBytes, "event");
|
|
971
915
|
controller.enqueue(encodedBytes);
|
|
972
916
|
} catch (error) {
|
|
973
|
-
const message =
|
|
974
|
-
error instanceof Error ? error.message : "Stream failed";
|
|
917
|
+
const message = error instanceof Error ? error.message : "Stream failed";
|
|
975
918
|
controller.enqueue(
|
|
976
919
|
encoder.encode(
|
|
977
920
|
encodeSseEvent(
|
|
@@ -1018,11 +961,7 @@ function enforceStreamChunkLimit(
|
|
|
1018
961
|
return;
|
|
1019
962
|
}
|
|
1020
963
|
if (value) {
|
|
1021
|
-
assertStreamPayloadWithinLimit(
|
|
1022
|
-
byteLength(value),
|
|
1023
|
-
maxChunkBytes,
|
|
1024
|
-
"chunk",
|
|
1025
|
-
);
|
|
964
|
+
assertStreamPayloadWithinLimit(byteLength(value), maxChunkBytes, "chunk");
|
|
1026
965
|
controller.enqueue(value);
|
|
1027
966
|
}
|
|
1028
967
|
} catch (error) {
|
|
@@ -1042,10 +981,7 @@ function toStreamingResponse(
|
|
|
1042
981
|
requestId?: string,
|
|
1043
982
|
): Response {
|
|
1044
983
|
const transport = operation.transport?.kind ?? "json";
|
|
1045
|
-
if (
|
|
1046
|
-
transport === "sse" &&
|
|
1047
|
-
(result instanceof Response || result instanceof ReadableStream)
|
|
1048
|
-
) {
|
|
984
|
+
if (transport === "sse" && (result instanceof Response || result instanceof ReadableStream)) {
|
|
1049
985
|
void cleanup();
|
|
1050
986
|
throw new ProviderError(
|
|
1051
987
|
"SSE operations must return an AsyncIterable of typed stream.event(...) values.",
|
|
@@ -1059,20 +995,13 @@ function toStreamingResponse(
|
|
|
1059
995
|
}
|
|
1060
996
|
if (result instanceof Response) {
|
|
1061
997
|
const httpTransport = getHttpStreamTransport(operation);
|
|
1062
|
-
if (
|
|
1063
|
-
httpTransport &&
|
|
1064
|
-
result.body &&
|
|
1065
|
-
httpTransport?.maxChunkBytes !== undefined
|
|
1066
|
-
) {
|
|
998
|
+
if (httpTransport && result.body && httpTransport?.maxChunkBytes !== undefined) {
|
|
1067
999
|
return responseWithCleanup(
|
|
1068
|
-
new Response(
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
statusText: result.statusText,
|
|
1074
|
-
},
|
|
1075
|
-
),
|
|
1000
|
+
new Response(enforceStreamChunkLimit(result.body, httpTransport.maxChunkBytes), {
|
|
1001
|
+
headers: result.headers,
|
|
1002
|
+
status: result.status,
|
|
1003
|
+
statusText: result.statusText,
|
|
1004
|
+
}),
|
|
1076
1005
|
cleanup,
|
|
1077
1006
|
);
|
|
1078
1007
|
}
|
|
@@ -1092,8 +1021,7 @@ function toStreamingResponse(
|
|
|
1092
1021
|
: {
|
|
1093
1022
|
"Content-Type":
|
|
1094
1023
|
operation.transport?.kind === "http-stream"
|
|
1095
|
-
? (operation.transport.contentType ??
|
|
1096
|
-
"application/octet-stream")
|
|
1024
|
+
? (operation.transport.contentType ?? "application/octet-stream")
|
|
1097
1025
|
: "application/octet-stream",
|
|
1098
1026
|
},
|
|
1099
1027
|
}),
|
|
@@ -1113,18 +1041,14 @@ function toStreamingResponse(
|
|
|
1113
1041
|
);
|
|
1114
1042
|
}
|
|
1115
1043
|
|
|
1116
|
-
function getSseTransport(
|
|
1117
|
-
operation: OperationDefinition,
|
|
1118
|
-
): OperationSseTransport | undefined {
|
|
1044
|
+
function getSseTransport(operation: OperationDefinition): OperationSseTransport | undefined {
|
|
1119
1045
|
return operation.transport?.kind === "sse" ? operation.transport : undefined;
|
|
1120
1046
|
}
|
|
1121
1047
|
|
|
1122
1048
|
function getHttpStreamTransport(
|
|
1123
1049
|
operation: OperationDefinition,
|
|
1124
1050
|
): OperationHttpStreamTransport | undefined {
|
|
1125
|
-
return operation.transport?.kind === "http-stream"
|
|
1126
|
-
? operation.transport
|
|
1127
|
-
: undefined;
|
|
1051
|
+
return operation.transport?.kind === "http-stream" ? operation.transport : undefined;
|
|
1128
1052
|
}
|
|
1129
1053
|
|
|
1130
1054
|
function toAuthFlowResponse(
|
|
@@ -1145,9 +1069,7 @@ function toAuthFlowResponse(
|
|
|
1145
1069
|
};
|
|
1146
1070
|
}
|
|
1147
1071
|
|
|
1148
|
-
function authFlowLocaleFromHeaders(
|
|
1149
|
-
headers?: Record<string, string>,
|
|
1150
|
-
): ProviderLocale {
|
|
1072
|
+
function authFlowLocaleFromHeaders(headers?: Record<string, string>): ProviderLocale {
|
|
1151
1073
|
const header = Object.entries(headers ?? {}).find(
|
|
1152
1074
|
([key]) => key.toLowerCase() === "accept-language",
|
|
1153
1075
|
)?.[1];
|
|
@@ -1165,9 +1087,7 @@ function isAuthFlowLocale(value: string | undefined): value is ProviderLocale {
|
|
|
1165
1087
|
}
|
|
1166
1088
|
|
|
1167
1089
|
function isAuthTurn(value: unknown): value is AuthTurn {
|
|
1168
|
-
return
|
|
1169
|
-
!!value && typeof value === "object" && "kind" in value && "turnId" in value
|
|
1170
|
-
);
|
|
1090
|
+
return !!value && typeof value === "object" && "kind" in value && "turnId" in value;
|
|
1171
1091
|
}
|
|
1172
1092
|
|
|
1173
1093
|
function loadAuthFlowLocaleCatalogs(
|
|
@@ -1204,10 +1124,7 @@ function materializeAuthFlowTurn(
|
|
|
1204
1124
|
});
|
|
1205
1125
|
}
|
|
1206
1126
|
|
|
1207
|
-
function withAuthRequestHeaders(
|
|
1208
|
-
request: AuthFlowRequest,
|
|
1209
|
-
headers: Headers,
|
|
1210
|
-
): AuthFlowRequest {
|
|
1127
|
+
function withAuthRequestHeaders(request: AuthFlowRequest, headers: Headers): AuthFlowRequest {
|
|
1211
1128
|
return {
|
|
1212
1129
|
...request,
|
|
1213
1130
|
headers: {
|
|
@@ -1225,17 +1142,9 @@ async function handleOperation(
|
|
|
1225
1142
|
state: ProviderRuntimeState = createUnsupportedProviderRuntimeState(),
|
|
1226
1143
|
proxyTelemetry?: ProxyTelemetryCollector,
|
|
1227
1144
|
): Promise<Response | OperationResponse> {
|
|
1228
|
-
const ctx = createProviderContext(
|
|
1229
|
-
provider,
|
|
1230
|
-
request,
|
|
1231
|
-
operationId,
|
|
1232
|
-
options,
|
|
1233
|
-
state,
|
|
1234
|
-
proxyTelemetry,
|
|
1235
|
-
);
|
|
1145
|
+
const ctx = createProviderContext(provider, request, operationId, options, state, proxyTelemetry);
|
|
1236
1146
|
const operation = provider.operations[operationId];
|
|
1237
|
-
const streaming =
|
|
1238
|
-
operation?.transport?.kind && operation.transport.kind !== "json";
|
|
1147
|
+
const streaming = operation?.transport?.kind && operation.transport.kind !== "json";
|
|
1239
1148
|
let cleanupCalled = false;
|
|
1240
1149
|
const cleanup = async () => {
|
|
1241
1150
|
if (cleanupCalled) return;
|
|
@@ -1266,12 +1175,7 @@ async function handleOperation(
|
|
|
1266
1175
|
}
|
|
1267
1176
|
};
|
|
1268
1177
|
try {
|
|
1269
|
-
const result = await executeOperation(
|
|
1270
|
-
provider,
|
|
1271
|
-
operationId,
|
|
1272
|
-
ctx,
|
|
1273
|
-
request.input,
|
|
1274
|
-
);
|
|
1178
|
+
const result = await executeOperation(provider, operationId, ctx, request.input);
|
|
1275
1179
|
if (streaming && operation) {
|
|
1276
1180
|
return toStreamingResponse(operation, result, cleanup, request.requestId);
|
|
1277
1181
|
}
|
|
@@ -1315,12 +1219,7 @@ async function handleAuthFlow(
|
|
|
1315
1219
|
});
|
|
1316
1220
|
}
|
|
1317
1221
|
|
|
1318
|
-
const { context, getPatch } = createAuthFlowContext(
|
|
1319
|
-
provider,
|
|
1320
|
-
request,
|
|
1321
|
-
options,
|
|
1322
|
-
signal,
|
|
1323
|
-
);
|
|
1222
|
+
const { context, getPatch } = createAuthFlowContext(provider, request, options, signal);
|
|
1324
1223
|
try {
|
|
1325
1224
|
const result =
|
|
1326
1225
|
route === "start"
|
|
@@ -1468,17 +1367,8 @@ export function createServerApp(
|
|
|
1468
1367
|
.clone()
|
|
1469
1368
|
.json()
|
|
1470
1369
|
.catch(() => undefined);
|
|
1471
|
-
const body = withAuthRequestHeaders(
|
|
1472
|
-
|
|
1473
|
-
c.req.raw.headers,
|
|
1474
|
-
);
|
|
1475
|
-
const response = await handleAuthFlow(
|
|
1476
|
-
provider,
|
|
1477
|
-
body,
|
|
1478
|
-
"start",
|
|
1479
|
-
options,
|
|
1480
|
-
c.req.raw.signal,
|
|
1481
|
-
);
|
|
1370
|
+
const body = withAuthRequestHeaders(AuthFlowRequestSchema.parse(rawBody), c.req.raw.headers);
|
|
1371
|
+
const response = await handleAuthFlow(provider, body, "start", options, c.req.raw.signal);
|
|
1482
1372
|
logProviderSuccess(
|
|
1483
1373
|
logger,
|
|
1484
1374
|
provider,
|
|
@@ -1514,17 +1404,8 @@ export function createServerApp(
|
|
|
1514
1404
|
.clone()
|
|
1515
1405
|
.json()
|
|
1516
1406
|
.catch(() => undefined);
|
|
1517
|
-
const body = withAuthRequestHeaders(
|
|
1518
|
-
|
|
1519
|
-
c.req.raw.headers,
|
|
1520
|
-
);
|
|
1521
|
-
const response = await handleAuthFlow(
|
|
1522
|
-
provider,
|
|
1523
|
-
body,
|
|
1524
|
-
"continue",
|
|
1525
|
-
options,
|
|
1526
|
-
c.req.raw.signal,
|
|
1527
|
-
);
|
|
1407
|
+
const body = withAuthRequestHeaders(AuthFlowRequestSchema.parse(rawBody), c.req.raw.headers);
|
|
1408
|
+
const response = await handleAuthFlow(provider, body, "continue", options, c.req.raw.signal);
|
|
1528
1409
|
logProviderSuccess(
|
|
1529
1410
|
logger,
|
|
1530
1411
|
provider,
|
|
@@ -1560,17 +1441,8 @@ export function createServerApp(
|
|
|
1560
1441
|
.clone()
|
|
1561
1442
|
.json()
|
|
1562
1443
|
.catch(() => undefined);
|
|
1563
|
-
const body = withAuthRequestHeaders(
|
|
1564
|
-
|
|
1565
|
-
c.req.raw.headers,
|
|
1566
|
-
);
|
|
1567
|
-
const response = await handleAuthFlow(
|
|
1568
|
-
provider,
|
|
1569
|
-
body,
|
|
1570
|
-
"poll",
|
|
1571
|
-
options,
|
|
1572
|
-
c.req.raw.signal,
|
|
1573
|
-
);
|
|
1444
|
+
const body = withAuthRequestHeaders(AuthFlowRequestSchema.parse(rawBody), c.req.raw.headers);
|
|
1445
|
+
const response = await handleAuthFlow(provider, body, "poll", options, c.req.raw.signal);
|
|
1574
1446
|
logProviderSuccess(
|
|
1575
1447
|
logger,
|
|
1576
1448
|
provider,
|
|
@@ -1606,17 +1478,8 @@ export function createServerApp(
|
|
|
1606
1478
|
.clone()
|
|
1607
1479
|
.json()
|
|
1608
1480
|
.catch(() => undefined);
|
|
1609
|
-
const body = withAuthRequestHeaders(
|
|
1610
|
-
|
|
1611
|
-
c.req.raw.headers,
|
|
1612
|
-
);
|
|
1613
|
-
const response = await handleAuthFlow(
|
|
1614
|
-
provider,
|
|
1615
|
-
body,
|
|
1616
|
-
"refresh",
|
|
1617
|
-
options,
|
|
1618
|
-
c.req.raw.signal,
|
|
1619
|
-
);
|
|
1481
|
+
const body = withAuthRequestHeaders(AuthFlowRequestSchema.parse(rawBody), c.req.raw.headers);
|
|
1482
|
+
const response = await handleAuthFlow(provider, body, "refresh", options, c.req.raw.signal);
|
|
1620
1483
|
logProviderSuccess(
|
|
1621
1484
|
logger,
|
|
1622
1485
|
provider,
|
|
@@ -1652,17 +1515,8 @@ export function createServerApp(
|
|
|
1652
1515
|
.clone()
|
|
1653
1516
|
.json()
|
|
1654
1517
|
.catch(() => undefined);
|
|
1655
|
-
const body = withAuthRequestHeaders(
|
|
1656
|
-
|
|
1657
|
-
c.req.raw.headers,
|
|
1658
|
-
);
|
|
1659
|
-
const response = await handleAuthFlow(
|
|
1660
|
-
provider,
|
|
1661
|
-
body,
|
|
1662
|
-
"abort",
|
|
1663
|
-
options,
|
|
1664
|
-
c.req.raw.signal,
|
|
1665
|
-
);
|
|
1518
|
+
const body = withAuthRequestHeaders(AuthFlowRequestSchema.parse(rawBody), c.req.raw.headers);
|
|
1519
|
+
const response = await handleAuthFlow(provider, body, "abort", options, c.req.raw.signal);
|
|
1666
1520
|
logProviderSuccess(
|
|
1667
1521
|
logger,
|
|
1668
1522
|
provider,
|
|
@@ -1737,12 +1591,9 @@ export async function serve(
|
|
|
1737
1591
|
const bunRuntime = getBunServeRuntime();
|
|
1738
1592
|
|
|
1739
1593
|
if (bunRuntime === undefined) {
|
|
1740
|
-
throw new ProviderError(
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
code: "RUNTIME_UNSUPPORTED",
|
|
1744
|
-
},
|
|
1745
|
-
);
|
|
1594
|
+
throw new ProviderError("Bun runtime is required to start the provider server", {
|
|
1595
|
+
code: "RUNTIME_UNSUPPORTED",
|
|
1596
|
+
});
|
|
1746
1597
|
}
|
|
1747
1598
|
|
|
1748
1599
|
const app = createServerApp(provider, {
|