@apifuse/provider-sdk 2.2.0-beta.1 → 2.2.0-beta.10
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 +106 -0
- package/CHANGELOG.md +37 -0
- package/README.md +5 -1
- package/SUBMISSION.md +1 -1
- package/bin/apifuse-check.ts +44 -59
- package/bin/apifuse-create.ts +1 -1
- package/bin/apifuse-dev.ts +27 -52
- package/bin/apifuse-pack-check.ts +14 -0
- 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 +53 -70
- package/bin/apifuse-submit-check.ts +510 -45
- package/bin/apifuse-sync-assets.ts +117 -0
- package/bin/apifuse.ts +1 -1
- package/bin/submit-check-delimited-text.ts +50 -0
- package/bin/submit-check-xml-semantics.ts +204 -0
- package/bin/submit-check-xml.ts +134 -0
- package/dist/auth-turn/index.d.ts +3 -3
- 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/cli/commands.d.ts +1 -1
- package/dist/cli/commands.js +8 -0
- package/dist/cli/create.d.ts +3 -0
- package/dist/cli/create.js +34 -35
- package/dist/cli/prompt-assets.d.ts +80 -0
- package/dist/cli/prompt-assets.js +743 -0
- package/dist/cli/templates/provider/AGENTS.md.tpl +17 -8
- package/dist/config/loader.d.ts +149 -8
- package/dist/config/loader.js +378 -89
- 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 +119 -122
- 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 +44 -42
- package/dist/index.js +39 -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 +31 -35
- 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 +15 -3
- package/dist/runtime/http.d.ts +2 -2
- package/dist/runtime/http.js +144 -348
- 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 +56 -19
- 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 +9 -7
- package/dist/runtime/proxy-nodemaven.d.ts +35 -0
- package/dist/runtime/proxy-nodemaven.js +128 -0
- package/dist/runtime/proxy-retry-policy.d.ts +40 -0
- package/dist/runtime/proxy-retry-policy.js +326 -0
- package/dist/runtime/proxy-telemetry.d.ts +2 -1
- package/dist/runtime/proxy-telemetry.js +55 -52
- 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/secrets.d.ts +27 -0
- package/dist/runtime/secrets.js +51 -0
- package/dist/runtime/state.d.ts +2 -2
- package/dist/runtime/state.js +15 -4
- package/dist/runtime/stealth.d.ts +2 -2
- package/dist/runtime/stealth.js +108 -212
- 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 +104 -3
- package/dist/server/self-test.js +673 -115
- package/dist/server/serve.d.ts +8 -3
- package/dist/server/serve.js +105 -59
- package/dist/server/types.d.ts +10 -9
- package/dist/server/types.js +3 -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 +87 -1
- package/dist/user-input.d.ts +30 -0
- package/dist/user-input.js +66 -0
- package/package.json +9 -5
- package/src/auth-turn/index.ts +1 -1
- package/src/auth.ts +28 -86
- package/src/ceremonies/index.ts +24 -78
- package/src/cli/commands.ts +10 -0
- package/src/cli/create.ts +42 -35
- package/src/cli/prompt-assets.ts +865 -0
- package/src/cli/templates/provider/AGENTS.md.tpl +17 -8
- package/src/config/loader.ts +543 -208
- package/src/contract-serialization.ts +5 -11
- package/src/contract-types.ts +2 -2
- package/src/contract.ts +12 -28
- package/src/define.ts +247 -505
- 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 +49 -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 +79 -132
- package/src/runtime/credential.ts +2 -2
- package/src/runtime/env.ts +1 -1
- package/src/runtime/executor.ts +23 -20
- package/src/runtime/http.ts +195 -559
- package/src/runtime/insights.ts +15 -53
- package/src/runtime/instrumentation.ts +78 -65
- 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 +29 -42
- package/src/runtime/proxy-nodemaven.ts +178 -0
- package/src/runtime/proxy-retry-policy.ts +469 -0
- package/src/runtime/proxy-telemetry.ts +79 -77
- package/src/runtime/redis.ts +4 -12
- package/src/runtime/request-options.ts +4 -13
- package/src/runtime/secrets.ts +64 -0
- package/src/runtime/state.ts +41 -110
- package/src/runtime/stealth.ts +162 -377
- 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 +12 -7
- package/src/server/self-test-redaction.ts +2 -2
- package/src/server/self-test.ts +857 -132
- package/src/server/serve.ts +172 -245
- package/src/server/types.ts +7 -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 +88 -1
- package/src/user-input.ts +118 -0
- package/dist/cli/templates/provider/CLAUDE.md.tpl +0 -1
- package/src/cli/templates/provider/CLAUDE.md.tpl +0 -1
- /package/dist/cli/templates/provider/{skills → .agents/skills}/fixtures-and-recording/SKILL.md.tpl +0 -0
- /package/dist/cli/templates/provider/{skills → .agents/skills}/health-checks-and-fail-closed/SKILL.md.tpl +0 -0
- /package/dist/cli/templates/provider/{skills → .agents/skills}/normalization-standards/SKILL.md.tpl +0 -0
- /package/dist/cli/templates/provider/{skills → .agents/skills}/pagination-and-counts/SKILL.md.tpl +0 -0
- /package/dist/cli/templates/provider/{skills → .agents/skills}/upstream-contract-verification/SKILL.md.tpl +0 -0
- /package/dist/cli/templates/provider/{skills → .agents/skills}/upstream-notes/README.md.tpl +0 -0
- /package/src/cli/templates/provider/{skills → .agents/skills}/fixtures-and-recording/SKILL.md.tpl +0 -0
- /package/src/cli/templates/provider/{skills → .agents/skills}/health-checks-and-fail-closed/SKILL.md.tpl +0 -0
- /package/src/cli/templates/provider/{skills → .agents/skills}/normalization-standards/SKILL.md.tpl +0 -0
- /package/src/cli/templates/provider/{skills → .agents/skills}/pagination-and-counts/SKILL.md.tpl +0 -0
- /package/src/cli/templates/provider/{skills → .agents/skills}/upstream-contract-verification/SKILL.md.tpl +0 -0
- /package/src/cli/templates/provider/{skills → .agents/skills}/upstream-notes/README.md.tpl +0 -0
package/dist/server/serve.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Hono } from "hono";
|
|
2
|
-
import { type ProviderErrorCategory } from "../observability";
|
|
3
|
-
import type { ProviderDefinition, ProviderRuntimeState, SttContext } from "../types";
|
|
4
|
-
import { type OperationRequest } from "./types";
|
|
2
|
+
import { type ProviderErrorCategory } from "../observability.js";
|
|
3
|
+
import type { ProviderDefinition, ProviderRuntimeState, SttContext } from "../types.js";
|
|
4
|
+
import { type OperationRequest } from "./types.js";
|
|
5
5
|
export declare function resolveProviderProxyAffinityKey(provider: ProviderDefinition, request: OperationRequest, operationId: string): string;
|
|
6
6
|
type ProviderRequestCost = {
|
|
7
7
|
durationMs: number;
|
|
@@ -35,6 +35,11 @@ export type ProviderServerLogEvent = (ProviderServerLogEventBase & {
|
|
|
35
35
|
message: string;
|
|
36
36
|
}>;
|
|
37
37
|
}) | {
|
|
38
|
+
level: "warn";
|
|
39
|
+
event: "provider_secrets_missing";
|
|
40
|
+
providerId: string;
|
|
41
|
+
missingSecrets: string[];
|
|
42
|
+
} | {
|
|
38
43
|
level: "warn";
|
|
39
44
|
event: "provider_cleanup_failed";
|
|
40
45
|
providerId: string;
|
package/dist/server/serve.js
CHANGED
|
@@ -2,32 +2,33 @@ import { existsSync } from "node:fs";
|
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { Hono } from "hono";
|
|
4
4
|
import { z } from "zod";
|
|
5
|
-
import { AuthAbortError, createAuthFlowHelpers } from "../auth";
|
|
6
|
-
import { AuthError,
|
|
7
|
-
import { loadProviderLocaleCatalogs, localizeAuthTurn, } from "../i18n/catalog";
|
|
8
|
-
import { categoryForStatus, isRetryableCategory, PROVIDER_OBSERVABILITY_TAXONOMY_VERSION, } from "../observability";
|
|
9
|
-
import { createScratchpad } from "../runtime/auth-flow";
|
|
10
|
-
import { createBrowserClient } from "../runtime/browser";
|
|
11
|
-
import { createProviderCache } from "../runtime/cache";
|
|
12
|
-
import { createProviderChoiceContext, PROVIDER_RUNTIME_CHOICE_TOKEN_MASTER_SECRET_ENV, } from "../runtime/choice";
|
|
13
|
-
import { createCredentialContext } from "../runtime/credential";
|
|
14
|
-
import { createEnvContext } from "../runtime/env";
|
|
15
|
-
import { executeOperation } from "../runtime/executor";
|
|
16
|
-
import { createHttpClient } from "../runtime/http";
|
|
17
|
-
import { wrapWithInstrumentation } from "../runtime/instrumentation";
|
|
18
|
-
import { getProviderBaseUrl } from "../runtime/provider";
|
|
19
|
-
import { PROXY_AUTH_IP_DENIED_CODE, PROXY_EDGE_AUTH_REJECTED_CODE, PROXY_POOL_EXHAUSTED_CODE, } from "../runtime/proxy-errors";
|
|
20
|
-
import { PROVIDER_TELEMETRY_HEADER, ProxyTelemetryCollector
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
24
|
-
import {
|
|
25
|
-
import {
|
|
26
|
-
import {
|
|
27
|
-
import {
|
|
28
|
-
import {
|
|
29
|
-
import {
|
|
30
|
-
import {
|
|
5
|
+
import { AuthAbortError, createAuthFlowHelpers } from "../auth.js";
|
|
6
|
+
import { AuthError, isProviderError, isSessionExpiredError, isTransportError, ProviderError, } from "../errors.js";
|
|
7
|
+
import { loadProviderLocaleCatalogs, localizeAuthTurn, } from "../i18n/catalog.js";
|
|
8
|
+
import { categoryForStatus, isRetryableCategory, PROVIDER_OBSERVABILITY_TAXONOMY_VERSION, } from "../observability.js";
|
|
9
|
+
import { createScratchpad } from "../runtime/auth-flow.js";
|
|
10
|
+
import { createBrowserClient } from "../runtime/browser.js";
|
|
11
|
+
import { createProviderCache } from "../runtime/cache.js";
|
|
12
|
+
import { createProviderChoiceContext, PROVIDER_RUNTIME_CHOICE_TOKEN_MASTER_SECRET_ENV, } from "../runtime/choice.js";
|
|
13
|
+
import { createCredentialContext } from "../runtime/credential.js";
|
|
14
|
+
import { createEnvContext } from "../runtime/env.js";
|
|
15
|
+
import { executeOperation } from "../runtime/executor.js";
|
|
16
|
+
import { createHttpClient } from "../runtime/http.js";
|
|
17
|
+
import { wrapWithInstrumentation } from "../runtime/instrumentation.js";
|
|
18
|
+
import { getProviderBaseUrl } from "../runtime/provider.js";
|
|
19
|
+
import { PROXY_AUTH_IP_DENIED_CODE, PROXY_EDGE_AUTH_REJECTED_CODE, PROXY_POOL_EXHAUSTED_CODE, } from "../runtime/proxy-errors.js";
|
|
20
|
+
import { PROVIDER_TELEMETRY_HEADER, ProxyTelemetryCollector } from "../runtime/proxy-telemetry.js";
|
|
21
|
+
import { assertRequiredSecretsPresent, listMissingRequiredSecrets, MISSING_SECRET_CODE, } from "../runtime/secrets.js";
|
|
22
|
+
import { createProviderRuntimeStateFromEnv, createUnsupportedProviderRuntimeState, } from "../runtime/state.js";
|
|
23
|
+
import { createStealthClient } from "../runtime/stealth.js";
|
|
24
|
+
import { createSttClientFromEnv } from "../runtime/stt.js";
|
|
25
|
+
import { createTraceContext } from "../runtime/trace.js";
|
|
26
|
+
import { parseSchema } from "../schema.js";
|
|
27
|
+
import { getStealthProfile } from "../stealth/profiles.js";
|
|
28
|
+
import { APIFUSE_STREAM_DONE_EVENT, APIFUSE_STREAM_ERROR_EVENT, encodeSseEvent, error as streamError, } from "../stream.js";
|
|
29
|
+
import { createSelfTestApp, createSelfTestAuthFlowInvoke, createSelfTestInvoke, resolveSelfTestPort, } from "./self-test.js";
|
|
30
|
+
import { resolveSelfTestMasterSecrets } from "./self-test-token.js";
|
|
31
|
+
import { AuthFlowRequestSchema, OperationRequestSchema, } from "./types.js";
|
|
31
32
|
const DEFAULT_HOST = "0.0.0.0";
|
|
32
33
|
const DEFAULT_PORT = 3000;
|
|
33
34
|
const AUTH_FLOW_LOCALES = ["en", "ko", "ja"];
|
|
@@ -93,9 +94,7 @@ function getProviderStealthBaseUrl(provider) {
|
|
|
93
94
|
return firstHost ? `https://${firstHost}` : undefined;
|
|
94
95
|
}
|
|
95
96
|
function getProviderStealthProfile(provider) {
|
|
96
|
-
return provider.stealth?.profile
|
|
97
|
-
? getStealthProfile(provider.stealth.profile)
|
|
98
|
-
: undefined;
|
|
97
|
+
return provider.stealth?.profile ? getStealthProfile(provider.stealth.profile) : undefined;
|
|
99
98
|
}
|
|
100
99
|
function isProductionProviderBrowserMode(provider, env = process.env) {
|
|
101
100
|
if (provider.runtime !== "browser") {
|
|
@@ -104,18 +103,19 @@ function isProductionProviderBrowserMode(provider, env = process.env) {
|
|
|
104
103
|
if (env.APIFUSE__PROVIDER__RUNTIME === "browser") {
|
|
105
104
|
return true;
|
|
106
105
|
}
|
|
107
|
-
return
|
|
106
|
+
return env.NODE_ENV === "production" && env.APIFUSE__PROVIDER__ID === provider.id;
|
|
108
107
|
}
|
|
109
108
|
export function resolveProviderProxyAffinityKey(provider, request, operationId) {
|
|
110
|
-
const connectionKey = request
|
|
111
|
-
const affinity = typeof provider.proxy === "object"
|
|
112
|
-
? provider.proxy.session?.affinity
|
|
113
|
-
: undefined;
|
|
109
|
+
const connectionKey = resolveOperationConnectionId(request) ?? request.connection?.externalRef;
|
|
110
|
+
const affinity = typeof provider.proxy === "object" ? provider.proxy.session?.affinity : undefined;
|
|
114
111
|
if (affinity === "operation") {
|
|
115
112
|
return `${provider.id}/${operationId}`;
|
|
116
113
|
}
|
|
117
114
|
return connectionKey ?? provider.id;
|
|
118
115
|
}
|
|
116
|
+
function resolveOperationConnectionId(request) {
|
|
117
|
+
return request.connection?.id ?? request.connectionId;
|
|
118
|
+
}
|
|
119
119
|
function createProviderContext(provider, request, operationId, options = {}, state = createUnsupportedProviderRuntimeState(), proxyTelemetry) {
|
|
120
120
|
const baseUrl = getProviderBaseUrl(provider);
|
|
121
121
|
const stealthBaseUrl = getProviderStealthBaseUrl(provider);
|
|
@@ -142,7 +142,7 @@ function createProviderContext(provider, request, operationId, options = {}, sta
|
|
|
142
142
|
values: request.connection?.secrets,
|
|
143
143
|
});
|
|
144
144
|
const requestContext = {
|
|
145
|
-
connectionId: request
|
|
145
|
+
connectionId: resolveOperationConnectionId(request),
|
|
146
146
|
headers: request.headers ?? {},
|
|
147
147
|
};
|
|
148
148
|
const context = wrapWithInstrumentation({
|
|
@@ -290,7 +290,7 @@ function zodDetails(error) {
|
|
|
290
290
|
}));
|
|
291
291
|
}
|
|
292
292
|
function toErrorResponse(error, requestId) {
|
|
293
|
-
if (error
|
|
293
|
+
if (isProviderError(error)) {
|
|
294
294
|
const details = publicProviderErrorDetails(error);
|
|
295
295
|
return {
|
|
296
296
|
error: {
|
|
@@ -312,11 +312,22 @@ function toErrorResponse(error, requestId) {
|
|
|
312
312
|
},
|
|
313
313
|
};
|
|
314
314
|
}
|
|
315
|
+
// A masked internal error MUST NOT be advertised as retryable: without an
|
|
316
|
+
// explicit retryable:false the hub (bori provider-backed engine) defaults 5xx
|
|
317
|
+
// to retryable:true, which turns a deterministic pre-upstream crash into an
|
|
318
|
+
// infinite START->CONTINUE->restart loop (2026-07-22 catchtable reserve RCA).
|
|
319
|
+
// We still refuse to leak message/stack — only the error class name (or the
|
|
320
|
+
// primitive type for non-Error throwables) is surfaced for ops triage.
|
|
315
321
|
return {
|
|
316
322
|
error: {
|
|
317
323
|
code: "internal_error",
|
|
318
324
|
message: "Internal error",
|
|
319
325
|
...(requestId ? { requestId } : {}),
|
|
326
|
+
details: {
|
|
327
|
+
retryable: false,
|
|
328
|
+
category: "internal_error",
|
|
329
|
+
errorClass: error instanceof Error ? error.name : typeof error,
|
|
330
|
+
},
|
|
320
331
|
},
|
|
321
332
|
};
|
|
322
333
|
}
|
|
@@ -340,20 +351,37 @@ function publicProviderErrorDetails(error) {
|
|
|
340
351
|
function isPlainRecord(value) {
|
|
341
352
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
342
353
|
}
|
|
354
|
+
// Accepts `unknown` so the branded guards narrow cleanly from the top: the
|
|
355
|
+
// subtype error classes are structurally compatible with ProviderError, so
|
|
356
|
+
// narrowing from a ProviderError-typed value would collapse the negative branch
|
|
357
|
+
// to `never`. Narrowing from unknown avoids that while still recognizing errors
|
|
358
|
+
// from a duplicate SDK module instance.
|
|
343
359
|
function providerObservabilityDetails(error) {
|
|
344
360
|
// Session-expiry surfaces the credential_expired category + the opt-in
|
|
345
361
|
// retryable signal so Gateway/Credential Service can refresh and re-drive the
|
|
346
362
|
// operation (see design.md §4.3 D3). Without this branch the auth error would
|
|
347
363
|
// serialize as a bare 401 with no retryable/category, losing the refresh
|
|
348
364
|
// signal for exactly the retryOnAuthRefresh operations it is meant to enable.
|
|
349
|
-
if (error
|
|
365
|
+
if (isSessionExpiredError(error)) {
|
|
350
366
|
return {
|
|
351
367
|
category: error.options?.category ?? "credential_expired",
|
|
352
368
|
taxonomyVersion: PROVIDER_OBSERVABILITY_TAXONOMY_VERSION,
|
|
353
369
|
retryable: error.options?.retryable ?? false,
|
|
354
370
|
};
|
|
355
371
|
}
|
|
356
|
-
|
|
372
|
+
// Missing-secret errors carry the canonical credential_unavailable category
|
|
373
|
+
// so Gateway/observability can attribute the failure to provisioning, not
|
|
374
|
+
// the upstream. Matched by code (not constructor) so both the SDK-owned
|
|
375
|
+
// runtime gate and any not-yet-migrated provider-thrown MISSING_SECRET
|
|
376
|
+
// serialize identically, including across duplicate SDK module instances.
|
|
377
|
+
if (isProviderError(error) && error.code === MISSING_SECRET_CODE) {
|
|
378
|
+
return {
|
|
379
|
+
category: error.options?.category ?? "credential_unavailable",
|
|
380
|
+
taxonomyVersion: PROVIDER_OBSERVABILITY_TAXONOMY_VERSION,
|
|
381
|
+
retryable: error.options?.retryable ?? false,
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
if (!isTransportError(error)) {
|
|
357
385
|
return undefined;
|
|
358
386
|
}
|
|
359
387
|
const isProxyPoolCode = error.code === PROXY_POOL_EXHAUSTED_CODE ||
|
|
@@ -382,7 +410,7 @@ function providerObservabilityDetails(error) {
|
|
|
382
410
|
};
|
|
383
411
|
}
|
|
384
412
|
function publicProviderErrorMessage(error) {
|
|
385
|
-
if (error
|
|
413
|
+
if (isTransportError(error)) {
|
|
386
414
|
if (error.code === PROXY_AUTH_IP_DENIED_CODE) {
|
|
387
415
|
return error.message;
|
|
388
416
|
}
|
|
@@ -410,14 +438,18 @@ function toStatusCode(error) {
|
|
|
410
438
|
if (error instanceof z.ZodError) {
|
|
411
439
|
return 400;
|
|
412
440
|
}
|
|
413
|
-
if (error
|
|
441
|
+
if (isTransportError(error)) {
|
|
414
442
|
return error.code === "transport_timeout" ? 504 : 502;
|
|
415
443
|
}
|
|
416
|
-
if (error
|
|
444
|
+
if (isProviderError(error)) {
|
|
417
445
|
switch (error.code) {
|
|
418
446
|
case "AUTH_REQUIRED":
|
|
419
447
|
case "reauth_required":
|
|
420
448
|
return 401;
|
|
449
|
+
// Unprovisioned declared secret: a deployment/config defect, never an
|
|
450
|
+
// upstream failure — explicit 400 (was only reached via fallthrough).
|
|
451
|
+
case MISSING_SECRET_CODE:
|
|
452
|
+
return 400;
|
|
421
453
|
case "NOT_FOUND":
|
|
422
454
|
case "not_found":
|
|
423
455
|
case "NO_DATA":
|
|
@@ -445,16 +477,14 @@ function extractRequestId(raw) {
|
|
|
445
477
|
return typeof value === "string" ? value : undefined;
|
|
446
478
|
}
|
|
447
479
|
function logProviderError(logger, provider, kind, route, requestId, error, status, cost) {
|
|
448
|
-
const code = error
|
|
480
|
+
const code = isProviderError(error)
|
|
449
481
|
? (error.code ?? "provider_error")
|
|
450
482
|
: error instanceof z.ZodError
|
|
451
483
|
? "invalid_request"
|
|
452
484
|
: "internal_error";
|
|
453
485
|
const errorClass = error instanceof Error ? error.name : typeof error;
|
|
454
486
|
const message = error instanceof Error ? error.message : String(error);
|
|
455
|
-
const details = error
|
|
456
|
-
? providerObservabilityDetails(error)
|
|
457
|
-
: undefined;
|
|
487
|
+
const details = isProviderError(error) ? providerObservabilityDetails(error) : undefined;
|
|
458
488
|
const emit = typeof logger === "function" ? logger : defaultProviderServerLogger;
|
|
459
489
|
emit({
|
|
460
490
|
level: status >= 500 ? "error" : "warn",
|
|
@@ -468,7 +498,7 @@ function logProviderError(logger, provider, kind, route, requestId, error, statu
|
|
|
468
498
|
code,
|
|
469
499
|
errorClass,
|
|
470
500
|
message,
|
|
471
|
-
...(error
|
|
501
|
+
...(isTransportError(error) && error.upstreamStatus
|
|
472
502
|
? { upstreamStatus: error.upstreamStatus }
|
|
473
503
|
: {}),
|
|
474
504
|
...(details
|
|
@@ -591,8 +621,7 @@ async function validateSseEvent(operation, event) {
|
|
|
591
621
|
const transport = getSseTransport(operation);
|
|
592
622
|
const schema = transport?.events?.[event.event];
|
|
593
623
|
if (!schema) {
|
|
594
|
-
if (event.event === APIFUSE_STREAM_ERROR_EVENT ||
|
|
595
|
-
event.event === APIFUSE_STREAM_DONE_EVENT) {
|
|
624
|
+
if (event.event === APIFUSE_STREAM_ERROR_EVENT || event.event === APIFUSE_STREAM_DONE_EVENT) {
|
|
596
625
|
return event;
|
|
597
626
|
}
|
|
598
627
|
throw new ProviderError(`SSE event "${event.event}" is not declared in operation transport.events.`, {
|
|
@@ -711,8 +740,7 @@ function enforceStreamChunkLimit(body, maxChunkBytes) {
|
|
|
711
740
|
}
|
|
712
741
|
function toStreamingResponse(operation, result, cleanup, requestId) {
|
|
713
742
|
const transport = operation.transport?.kind ?? "json";
|
|
714
|
-
if (transport === "sse" &&
|
|
715
|
-
(result instanceof Response || result instanceof ReadableStream)) {
|
|
743
|
+
if (transport === "sse" && (result instanceof Response || result instanceof ReadableStream)) {
|
|
716
744
|
void cleanup();
|
|
717
745
|
throw new ProviderError("SSE operations must return an AsyncIterable of typed stream.event(...) values.", {
|
|
718
746
|
code: "SSE_RESULT_UNSUPPORTED",
|
|
@@ -723,9 +751,7 @@ function toStreamingResponse(operation, result, cleanup, requestId) {
|
|
|
723
751
|
}
|
|
724
752
|
if (result instanceof Response) {
|
|
725
753
|
const httpTransport = getHttpStreamTransport(operation);
|
|
726
|
-
if (httpTransport &&
|
|
727
|
-
result.body &&
|
|
728
|
-
httpTransport?.maxChunkBytes !== undefined) {
|
|
754
|
+
if (httpTransport && result.body && httpTransport?.maxChunkBytes !== undefined) {
|
|
729
755
|
return responseWithCleanup(new Response(enforceStreamChunkLimit(result.body, httpTransport.maxChunkBytes), {
|
|
730
756
|
headers: result.headers,
|
|
731
757
|
status: result.status,
|
|
@@ -744,8 +770,7 @@ function toStreamingResponse(operation, result, cleanup, requestId) {
|
|
|
744
770
|
? { "Content-Type": "text/event-stream; charset=utf-8" }
|
|
745
771
|
: {
|
|
746
772
|
"Content-Type": operation.transport?.kind === "http-stream"
|
|
747
|
-
? (operation.transport.contentType ??
|
|
748
|
-
"application/octet-stream")
|
|
773
|
+
? (operation.transport.contentType ?? "application/octet-stream")
|
|
749
774
|
: "application/octet-stream",
|
|
750
775
|
},
|
|
751
776
|
}), cleanup);
|
|
@@ -763,9 +788,7 @@ function getSseTransport(operation) {
|
|
|
763
788
|
return operation.transport?.kind === "sse" ? operation.transport : undefined;
|
|
764
789
|
}
|
|
765
790
|
function getHttpStreamTransport(operation) {
|
|
766
|
-
return operation.transport?.kind === "http-stream"
|
|
767
|
-
? operation.transport
|
|
768
|
-
: undefined;
|
|
791
|
+
return operation.transport?.kind === "http-stream" ? operation.transport : undefined;
|
|
769
792
|
}
|
|
770
793
|
function toAuthFlowResponse(result, contextPatch) {
|
|
771
794
|
if (result instanceof Response) {
|
|
@@ -793,7 +816,7 @@ function isAuthFlowLocale(value) {
|
|
|
793
816
|
return value === "en" || value === "ko" || value === "ja";
|
|
794
817
|
}
|
|
795
818
|
function isAuthTurn(value) {
|
|
796
|
-
return
|
|
819
|
+
return !!value && typeof value === "object" && "kind" in value && "turnId" in value;
|
|
797
820
|
}
|
|
798
821
|
function loadAuthFlowLocaleCatalogs(provider) {
|
|
799
822
|
for (const providerDir of [
|
|
@@ -890,8 +913,16 @@ async function handleAuthFlow(provider, request, route, options = {}, signal) {
|
|
|
890
913
|
code: "AUTH_FLOW_NOT_CONFIGURED",
|
|
891
914
|
});
|
|
892
915
|
}
|
|
916
|
+
// Same SDK-owned gate as executeOperation: OAuth/credentials ceremonies
|
|
917
|
+
// depend on declared secrets (client ids/secrets), so fail structured before
|
|
918
|
+
// any flow code runs instead of at whatever point the ceremony first reads
|
|
919
|
+
// the env. `abort` stays exempt: a user must always be able to cancel a
|
|
920
|
+
// stranded flow even when provisioning is broken.
|
|
893
921
|
const { context, getPatch } = createAuthFlowContext(provider, request, options, signal);
|
|
894
922
|
try {
|
|
923
|
+
if (route !== "abort") {
|
|
924
|
+
assertRequiredSecretsPresent(provider, context.env);
|
|
925
|
+
}
|
|
895
926
|
const result = route === "start"
|
|
896
927
|
? await flow.start(context)
|
|
897
928
|
: route === "continue"
|
|
@@ -938,6 +969,20 @@ export function createServerApp(provider, options = {}) {
|
|
|
938
969
|
providerId: provider.id,
|
|
939
970
|
allowMemoryFallback: options.allowMemoryStateFallback === true,
|
|
940
971
|
});
|
|
972
|
+
// Boot-time visibility for unprovisioned declared secrets: emit a structured
|
|
973
|
+
// warn so deploy tooling/alerting sees the gap the moment the pod boots,
|
|
974
|
+
// instead of discovering it request-by-request. Deliberately log-only — a
|
|
975
|
+
// boot crash would trade a structured MISSING_SECRET signal for
|
|
976
|
+
// CrashLoopBackOff. Requests still fail closed via the executeOperation gate.
|
|
977
|
+
const missingSecretsAtBoot = listMissingRequiredSecrets(provider, createEnvContext(provider.secrets?.map((secret) => secret.name)));
|
|
978
|
+
if (missingSecretsAtBoot.length > 0) {
|
|
979
|
+
logger({
|
|
980
|
+
level: "warn",
|
|
981
|
+
event: "provider_secrets_missing",
|
|
982
|
+
providerId: provider.id,
|
|
983
|
+
missingSecrets: missingSecretsAtBoot,
|
|
984
|
+
});
|
|
985
|
+
}
|
|
941
986
|
app.notFound((c) => c.json({
|
|
942
987
|
error: {
|
|
943
988
|
code: "not_found",
|
|
@@ -1124,6 +1169,7 @@ export async function serve(provider, options = {}) {
|
|
|
1124
1169
|
const selfTestApp = createSelfTestApp(provider, {
|
|
1125
1170
|
secrets: selfTestSecrets,
|
|
1126
1171
|
invoke: createSelfTestInvoke(app),
|
|
1172
|
+
authFlow: createSelfTestAuthFlowInvoke(app),
|
|
1127
1173
|
});
|
|
1128
1174
|
bunRuntime.serve({
|
|
1129
1175
|
port: options.selfTestPort ?? resolveSelfTestPort(),
|
package/dist/server/types.d.ts
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
export declare const ConnectionModeSchema: z.ZodEnum<{
|
|
3
|
-
credentials: "credentials";
|
|
4
3
|
none: "none";
|
|
4
|
+
credentials: "credentials";
|
|
5
5
|
oauth2: "oauth2";
|
|
6
6
|
"platform-managed": "platform-managed";
|
|
7
7
|
}>;
|
|
8
8
|
export declare const OperationConnectionSchema: z.ZodObject<{
|
|
9
9
|
id: z.ZodString;
|
|
10
10
|
mode: z.ZodEnum<{
|
|
11
|
-
credentials: "credentials";
|
|
12
11
|
none: "none";
|
|
12
|
+
credentials: "credentials";
|
|
13
13
|
oauth2: "oauth2";
|
|
14
14
|
"platform-managed": "platform-managed";
|
|
15
15
|
}>;
|
|
@@ -21,11 +21,12 @@ export declare const OperationConnectionSchema: z.ZodObject<{
|
|
|
21
21
|
export declare const OperationRequestSchema: z.ZodObject<{
|
|
22
22
|
requestId: z.ZodString;
|
|
23
23
|
input: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
24
|
+
connectionId: z.ZodOptional<z.ZodString>;
|
|
24
25
|
connection: z.ZodOptional<z.ZodObject<{
|
|
25
26
|
id: z.ZodString;
|
|
26
27
|
mode: z.ZodEnum<{
|
|
27
|
-
credentials: "credentials";
|
|
28
28
|
none: "none";
|
|
29
|
+
credentials: "credentials";
|
|
29
30
|
oauth2: "oauth2";
|
|
30
31
|
"platform-managed": "platform-managed";
|
|
31
32
|
}>;
|
|
@@ -54,21 +55,21 @@ export declare const OperationSuccessResponseSchema: z.ZodObject<{
|
|
|
54
55
|
stale: z.ZodBoolean;
|
|
55
56
|
keys: z.ZodArray<z.ZodString>;
|
|
56
57
|
source: z.ZodOptional<z.ZodEnum<{
|
|
57
|
-
loader: "loader";
|
|
58
|
-
memory: "memory";
|
|
59
58
|
mixed: "mixed";
|
|
60
59
|
redis: "redis";
|
|
60
|
+
memory: "memory";
|
|
61
|
+
loader: "loader";
|
|
61
62
|
}>>;
|
|
62
63
|
}, z.core.$strip>>;
|
|
63
64
|
retry: z.ZodOptional<z.ZodObject<{
|
|
64
65
|
attempts: z.ZodNumber;
|
|
65
66
|
retries: z.ZodNumber;
|
|
66
67
|
preset: z.ZodOptional<z.ZodEnum<{
|
|
67
|
-
aggressive_read: "aggressive_read";
|
|
68
68
|
off: "off";
|
|
69
|
-
rate_limit_aware: "rate_limit_aware";
|
|
70
|
-
safe_read: "safe_read";
|
|
71
69
|
transport_transient: "transport_transient";
|
|
70
|
+
safe_read: "safe_read";
|
|
71
|
+
aggressive_read: "aggressive_read";
|
|
72
|
+
rate_limit_aware: "rate_limit_aware";
|
|
72
73
|
}>>;
|
|
73
74
|
transport: z.ZodEnum<{
|
|
74
75
|
native: "native";
|
|
@@ -100,8 +101,8 @@ export declare const AuthFlowRequestSchema: z.ZodObject<{
|
|
|
100
101
|
connection: z.ZodOptional<z.ZodObject<{
|
|
101
102
|
id: z.ZodString;
|
|
102
103
|
mode: z.ZodEnum<{
|
|
103
|
-
credentials: "credentials";
|
|
104
104
|
none: "none";
|
|
105
|
+
credentials: "credentials";
|
|
105
106
|
oauth2: "oauth2";
|
|
106
107
|
"platform-managed": "platform-managed";
|
|
107
108
|
}>;
|
package/dist/server/types.js
CHANGED
|
@@ -1,11 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { HttpRetryPreset } from "../types";
|
|
3
|
-
export const ConnectionModeSchema = z.enum([
|
|
4
|
-
"oauth2",
|
|
5
|
-
"credentials",
|
|
6
|
-
"platform-managed",
|
|
7
|
-
"none",
|
|
8
|
-
]);
|
|
2
|
+
import { HttpRetryPreset } from "../types.js";
|
|
3
|
+
export const ConnectionModeSchema = z.enum(["oauth2", "credentials", "platform-managed", "none"]);
|
|
9
4
|
export const OperationConnectionSchema = z.object({
|
|
10
5
|
id: z.string(),
|
|
11
6
|
mode: ConnectionModeSchema,
|
|
@@ -17,6 +12,7 @@ export const OperationConnectionSchema = z.object({
|
|
|
17
12
|
export const OperationRequestSchema = z.object({
|
|
18
13
|
requestId: z.string(),
|
|
19
14
|
input: z.record(z.string(), z.unknown()),
|
|
15
|
+
connectionId: z.string().optional(),
|
|
20
16
|
connection: OperationConnectionSchema.optional(),
|
|
21
17
|
headers: z.record(z.string(), z.string()).optional(),
|
|
22
18
|
trace: z.record(z.string(), z.string()).optional(),
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { StealthProfile } from "../types";
|
|
1
|
+
import type { StealthProfile } from "../types.js";
|
|
2
2
|
export declare function generateLayer2Headers(profile: StealthProfile): Record<string, string>;
|
|
3
3
|
export declare function getStealthProfile(name: string): StealthProfile;
|
|
4
4
|
export declare function listStealthProfiles(): string[];
|
package/dist/stealth/profiles.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { SDKError } from "../errors";
|
|
1
|
+
import { SDKError } from "../errors.js";
|
|
2
2
|
const CHROMIUM_HEADER_ORDER = [
|
|
3
3
|
":method",
|
|
4
4
|
":authority",
|
|
@@ -74,12 +74,8 @@ function createProfile(name, definition) {
|
|
|
74
74
|
tlsClientIdentifier: definition.tlsClientIdentifier,
|
|
75
75
|
ja3: definition.ja3,
|
|
76
76
|
ja4: definition.ja4,
|
|
77
|
-
h2Settings: definition.h2Settings
|
|
78
|
-
|
|
79
|
-
: undefined,
|
|
80
|
-
headerOrder: definition.headerOrder
|
|
81
|
-
? [...definition.headerOrder]
|
|
82
|
-
: undefined,
|
|
77
|
+
h2Settings: definition.h2Settings ? { ...definition.h2Settings } : undefined,
|
|
78
|
+
headerOrder: definition.headerOrder ? [...definition.headerOrder] : undefined,
|
|
83
79
|
};
|
|
84
80
|
}
|
|
85
81
|
function extractBrowserMajorVersion(profile) {
|
|
@@ -120,9 +116,7 @@ export function generateLayer2Headers(profile) {
|
|
|
120
116
|
: `"Chromium";v="${majorVersion}", "Google Chrome";v="${majorVersion}", "Not)A;Brand";v="99"`;
|
|
121
117
|
headers["Sec-Ch-Ua-Platform"] = toPlatformHeaderValue(profile.platform);
|
|
122
118
|
headers["Sec-Ch-Ua-Mobile"] =
|
|
123
|
-
profile.platform === "android" || profile.platform === "ios"
|
|
124
|
-
? "?1"
|
|
125
|
-
: "?0";
|
|
119
|
+
profile.platform === "android" || profile.platform === "ios" ? "?1" : "?0";
|
|
126
120
|
}
|
|
127
121
|
return headers;
|
|
128
122
|
}
|
|
@@ -252,8 +246,5 @@ export function getStealthProfile(name) {
|
|
|
252
246
|
};
|
|
253
247
|
}
|
|
254
248
|
export function listStealthProfiles() {
|
|
255
|
-
return [
|
|
256
|
-
...Object.keys(STEALTH_PROFILES),
|
|
257
|
-
...Object.keys(STEALTH_PROFILE_ALIASES),
|
|
258
|
-
];
|
|
249
|
+
return [...Object.keys(STEALTH_PROFILES), ...Object.keys(STEALTH_PROFILE_ALIASES)];
|
|
259
250
|
}
|
package/dist/stream.d.ts
CHANGED
package/dist/testing/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { describeTransform, snapshotTransform, toMatchShape } from "./helpers";
|
|
2
|
-
export { runStandardTests } from "./run";
|
|
1
|
+
export { describeTransform, snapshotTransform, toMatchShape } from "./helpers.js";
|
|
2
|
+
export { runStandardTests } from "./run.js";
|
package/dist/testing/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { describeTransform, snapshotTransform, toMatchShape } from "./helpers";
|
|
2
|
-
export { runStandardTests } from "./run";
|
|
1
|
+
export { describeTransform, snapshotTransform, toMatchShape } from "./helpers.js";
|
|
2
|
+
export { runStandardTests } from "./run.js";
|
package/dist/testing/run.d.ts
CHANGED
package/dist/testing/run.js
CHANGED
|
@@ -1,19 +1,14 @@
|
|
|
1
1
|
import { describe, expect, it } from "bun:test";
|
|
2
|
-
import { createProviderCache } from "../runtime/cache";
|
|
3
|
-
import { createTestProviderChoiceContext } from "../runtime/choice";
|
|
4
|
-
import { createMemoryProviderRuntimeState } from "../runtime/state";
|
|
5
|
-
import { createUnsupportedSttClient } from "../runtime/stt";
|
|
6
|
-
import { safeParseSchemaSync } from "../schema";
|
|
2
|
+
import { createProviderCache } from "../runtime/cache.js";
|
|
3
|
+
import { createTestProviderChoiceContext } from "../runtime/choice.js";
|
|
4
|
+
import { createMemoryProviderRuntimeState } from "../runtime/state.js";
|
|
5
|
+
import { createUnsupportedSttClient } from "../runtime/stt.js";
|
|
6
|
+
import { safeParseSchemaSync } from "../schema.js";
|
|
7
7
|
// Mirrors CONNECTOR_ID_REGEX in ../define.ts, which defineProvider() enforces.
|
|
8
8
|
// A single lowercase segment (no hyphen) is a valid id, so the trailing group
|
|
9
9
|
// is optional (`*`), matching providers like `kakaomap`, `kstartup`, `triple`.
|
|
10
10
|
const CONNECTOR_ID_REGEX = /^[a-z][a-z0-9]*(-[a-z][a-z0-9]*)*$/;
|
|
11
|
-
const VALID_AUTH_MODES = [
|
|
12
|
-
"none",
|
|
13
|
-
"platform-managed",
|
|
14
|
-
"credentials",
|
|
15
|
-
"oauth2",
|
|
16
|
-
];
|
|
11
|
+
const VALID_AUTH_MODES = ["none", "platform-managed", "credentials", "oauth2"];
|
|
17
12
|
const UPDATE_SNAPSHOT_ARGS = new Set(["-u", "--update-snapshots"]);
|
|
18
13
|
function isFixtureEnvelope(value) {
|
|
19
14
|
return (value !== null &&
|
|
@@ -60,9 +55,7 @@ function inferFixtureDir(providerId) {
|
|
|
60
55
|
.map((line) => line.match(/\(?((?:file:\/\/)?[^():]+\.test\.ts)/)?.[1])
|
|
61
56
|
.find((file) => file !== undefined);
|
|
62
57
|
if (testFile) {
|
|
63
|
-
const pathname = testFile.startsWith("file://")
|
|
64
|
-
? new URL(testFile).pathname
|
|
65
|
-
: testFile;
|
|
58
|
+
const pathname = testFile.startsWith("file://") ? new URL(testFile).pathname : testFile;
|
|
66
59
|
return `${pathname.replace(/\/[^/]+$/, "")}/../__fixtures__`;
|
|
67
60
|
}
|
|
68
61
|
return `providers/${providerId}/__fixtures__`;
|