@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/src/server/serve.ts
CHANGED
|
@@ -3,62 +3,65 @@ 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
|
+
} from "../runtime/proxy-errors.js";
|
|
44
|
+
import { PROVIDER_TELEMETRY_HEADER, ProxyTelemetryCollector } from "../runtime/proxy-telemetry.js";
|
|
43
45
|
import {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
46
|
+
assertRequiredSecretsPresent,
|
|
47
|
+
listMissingRequiredSecrets,
|
|
48
|
+
MISSING_SECRET_CODE,
|
|
49
|
+
} from "../runtime/secrets.js";
|
|
47
50
|
import {
|
|
48
51
|
createProviderRuntimeStateFromEnv,
|
|
49
52
|
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";
|
|
53
|
+
} from "../runtime/state.js";
|
|
54
|
+
import { createStealthClient } from "../runtime/stealth.js";
|
|
55
|
+
import { createSttClientFromEnv } from "../runtime/stt.js";
|
|
56
|
+
import { createTraceContext } from "../runtime/trace.js";
|
|
57
|
+
import { parseSchema } from "../schema.js";
|
|
58
|
+
import { getStealthProfile } from "../stealth/profiles.js";
|
|
56
59
|
import {
|
|
57
60
|
APIFUSE_STREAM_DONE_EVENT,
|
|
58
61
|
APIFUSE_STREAM_ERROR_EVENT,
|
|
59
62
|
encodeSseEvent,
|
|
60
63
|
error as streamError,
|
|
61
|
-
} from "../stream";
|
|
64
|
+
} from "../stream.js";
|
|
62
65
|
import type {
|
|
63
66
|
AuthContext,
|
|
64
67
|
AuthTurn,
|
|
@@ -75,13 +78,14 @@ import type {
|
|
|
75
78
|
ProviderStreamEvent,
|
|
76
79
|
StealthClient,
|
|
77
80
|
SttContext,
|
|
78
|
-
} from "../types";
|
|
81
|
+
} from "../types.js";
|
|
79
82
|
import {
|
|
80
83
|
createSelfTestApp,
|
|
84
|
+
createSelfTestAuthFlowInvoke,
|
|
81
85
|
createSelfTestInvoke,
|
|
82
86
|
resolveSelfTestPort,
|
|
83
|
-
} from "./self-test";
|
|
84
|
-
import { resolveSelfTestMasterSecrets } from "./self-test-token";
|
|
87
|
+
} from "./self-test.js";
|
|
88
|
+
import { resolveSelfTestMasterSecrets } from "./self-test-token.js";
|
|
85
89
|
import {
|
|
86
90
|
type AuthFlowRequest,
|
|
87
91
|
AuthFlowRequestSchema,
|
|
@@ -92,7 +96,7 @@ import {
|
|
|
92
96
|
OperationRequestSchema,
|
|
93
97
|
type OperationResponse,
|
|
94
98
|
type OperationSuccessResponse,
|
|
95
|
-
} from "./types";
|
|
99
|
+
} from "./types.js";
|
|
96
100
|
|
|
97
101
|
const DEFAULT_HOST = "0.0.0.0";
|
|
98
102
|
const DEFAULT_PORT = 3000;
|
|
@@ -156,9 +160,7 @@ function createStealthStub(): StealthClient {
|
|
|
156
160
|
};
|
|
157
161
|
}
|
|
158
162
|
|
|
159
|
-
function getProviderStealthBaseUrl(
|
|
160
|
-
provider: ProviderDefinition,
|
|
161
|
-
): string | undefined {
|
|
163
|
+
function getProviderStealthBaseUrl(provider: ProviderDefinition): string | undefined {
|
|
162
164
|
const baseUrl = getProviderBaseUrl(provider);
|
|
163
165
|
if (baseUrl) {
|
|
164
166
|
return baseUrl;
|
|
@@ -168,15 +170,10 @@ function getProviderStealthBaseUrl(
|
|
|
168
170
|
}
|
|
169
171
|
|
|
170
172
|
function getProviderStealthProfile(provider: ProviderDefinition) {
|
|
171
|
-
return provider.stealth?.profile
|
|
172
|
-
? getStealthProfile(provider.stealth.profile)
|
|
173
|
-
: undefined;
|
|
173
|
+
return provider.stealth?.profile ? getStealthProfile(provider.stealth.profile) : undefined;
|
|
174
174
|
}
|
|
175
175
|
|
|
176
|
-
function isProductionProviderBrowserMode(
|
|
177
|
-
provider: ProviderDefinition,
|
|
178
|
-
env = process.env,
|
|
179
|
-
): boolean {
|
|
176
|
+
function isProductionProviderBrowserMode(provider: ProviderDefinition, env = process.env): boolean {
|
|
180
177
|
if (provider.runtime !== "browser") {
|
|
181
178
|
return false;
|
|
182
179
|
}
|
|
@@ -185,9 +182,7 @@ function isProductionProviderBrowserMode(
|
|
|
185
182
|
return true;
|
|
186
183
|
}
|
|
187
184
|
|
|
188
|
-
return
|
|
189
|
-
env.NODE_ENV === "production" && env.APIFUSE__PROVIDER__ID === provider.id
|
|
190
|
-
);
|
|
185
|
+
return env.NODE_ENV === "production" && env.APIFUSE__PROVIDER__ID === provider.id;
|
|
191
186
|
}
|
|
192
187
|
|
|
193
188
|
export function resolveProviderProxyAffinityKey(
|
|
@@ -195,18 +190,19 @@ export function resolveProviderProxyAffinityKey(
|
|
|
195
190
|
request: OperationRequest,
|
|
196
191
|
operationId: string,
|
|
197
192
|
): string {
|
|
198
|
-
const connectionKey =
|
|
199
|
-
request.connection?.id ?? request.connection?.externalRef;
|
|
193
|
+
const connectionKey = resolveOperationConnectionId(request) ?? request.connection?.externalRef;
|
|
200
194
|
const affinity =
|
|
201
|
-
typeof provider.proxy === "object"
|
|
202
|
-
? provider.proxy.session?.affinity
|
|
203
|
-
: undefined;
|
|
195
|
+
typeof provider.proxy === "object" ? provider.proxy.session?.affinity : undefined;
|
|
204
196
|
if (affinity === "operation") {
|
|
205
197
|
return `${provider.id}/${operationId}`;
|
|
206
198
|
}
|
|
207
199
|
return connectionKey ?? provider.id;
|
|
208
200
|
}
|
|
209
201
|
|
|
202
|
+
function resolveOperationConnectionId(request: OperationRequest): string | undefined {
|
|
203
|
+
return request.connection?.id ?? request.connectionId;
|
|
204
|
+
}
|
|
205
|
+
|
|
210
206
|
function createProviderContext(
|
|
211
207
|
provider: ProviderDefinition,
|
|
212
208
|
request: OperationRequest,
|
|
@@ -220,11 +216,7 @@ function createProviderContext(
|
|
|
220
216
|
const stealthProfile = getProviderStealthProfile(provider);
|
|
221
217
|
const proxyClientOptions = {
|
|
222
218
|
upstream: { proxy: provider.proxy },
|
|
223
|
-
affinityKey: resolveProviderProxyAffinityKey(
|
|
224
|
-
provider,
|
|
225
|
-
request,
|
|
226
|
-
operationId,
|
|
227
|
-
),
|
|
219
|
+
affinityKey: resolveProviderProxyAffinityKey(provider, request, operationId),
|
|
228
220
|
telemetry: proxyTelemetry,
|
|
229
221
|
};
|
|
230
222
|
let wrappedContext: ProviderContext | undefined;
|
|
@@ -245,7 +237,7 @@ function createProviderContext(
|
|
|
245
237
|
values: request.connection?.secrets,
|
|
246
238
|
});
|
|
247
239
|
const requestContext = {
|
|
248
|
-
connectionId: request
|
|
240
|
+
connectionId: resolveOperationConnectionId(request),
|
|
249
241
|
headers: request.headers ?? {},
|
|
250
242
|
};
|
|
251
243
|
const context = wrapWithInstrumentation({
|
|
@@ -263,11 +255,7 @@ function createProviderContext(
|
|
|
263
255
|
state,
|
|
264
256
|
stealth: stealthBaseUrl
|
|
265
257
|
? stealthProfile
|
|
266
|
-
? createStealthClient(
|
|
267
|
-
stealthBaseUrl,
|
|
268
|
-
stealthProfile.name,
|
|
269
|
-
stealthClientOptions,
|
|
270
|
-
)
|
|
258
|
+
? createStealthClient(stealthBaseUrl, stealthProfile.name, stealthClientOptions)
|
|
271
259
|
: createStealthClient(stealthBaseUrl, stealthClientOptions)
|
|
272
260
|
: createStealthStub(),
|
|
273
261
|
browser:
|
|
@@ -380,11 +368,7 @@ function createAuthFlowContext(
|
|
|
380
368
|
http: createHttpClient(baseUrl, proxyClientOptions),
|
|
381
369
|
stealth: stealthBaseUrl
|
|
382
370
|
? stealthProfile
|
|
383
|
-
? createStealthClient(
|
|
384
|
-
stealthBaseUrl,
|
|
385
|
-
stealthProfile.name,
|
|
386
|
-
stealthClientOptions,
|
|
387
|
-
)
|
|
371
|
+
? createStealthClient(stealthBaseUrl, stealthProfile.name, stealthClientOptions)
|
|
388
372
|
: createStealthClient(stealthBaseUrl, stealthClientOptions)
|
|
389
373
|
: createStealthStub(),
|
|
390
374
|
env: createEnvContext(provider.secrets?.map((secret) => secret.name)),
|
|
@@ -429,6 +413,12 @@ export type ProviderServerLogEvent =
|
|
|
429
413
|
retryable?: boolean;
|
|
430
414
|
issues?: Array<{ path: string; code: string; message: string }>;
|
|
431
415
|
})
|
|
416
|
+
| {
|
|
417
|
+
level: "warn";
|
|
418
|
+
event: "provider_secrets_missing";
|
|
419
|
+
providerId: string;
|
|
420
|
+
missingSecrets: string[];
|
|
421
|
+
}
|
|
432
422
|
| {
|
|
433
423
|
level: "warn";
|
|
434
424
|
event: "provider_cleanup_failed";
|
|
@@ -497,11 +487,8 @@ function zodDetails(error: z.ZodError): Array<{
|
|
|
497
487
|
}));
|
|
498
488
|
}
|
|
499
489
|
|
|
500
|
-
function toErrorResponse(
|
|
501
|
-
error
|
|
502
|
-
requestId?: string,
|
|
503
|
-
): OperationErrorResponse {
|
|
504
|
-
if (error instanceof ProviderError) {
|
|
490
|
+
function toErrorResponse(error: unknown, requestId?: string): OperationErrorResponse {
|
|
491
|
+
if (isProviderError(error)) {
|
|
505
492
|
const details = publicProviderErrorDetails(error);
|
|
506
493
|
return {
|
|
507
494
|
error: {
|
|
@@ -525,11 +512,22 @@ function toErrorResponse(
|
|
|
525
512
|
};
|
|
526
513
|
}
|
|
527
514
|
|
|
515
|
+
// A masked internal error MUST NOT be advertised as retryable: without an
|
|
516
|
+
// explicit retryable:false the hub (bori provider-backed engine) defaults 5xx
|
|
517
|
+
// to retryable:true, which turns a deterministic pre-upstream crash into an
|
|
518
|
+
// infinite START->CONTINUE->restart loop (2026-07-22 catchtable reserve RCA).
|
|
519
|
+
// We still refuse to leak message/stack — only the error class name (or the
|
|
520
|
+
// primitive type for non-Error throwables) is surfaced for ops triage.
|
|
528
521
|
return {
|
|
529
522
|
error: {
|
|
530
523
|
code: "internal_error",
|
|
531
524
|
message: "Internal error",
|
|
532
525
|
...(requestId ? { requestId } : {}),
|
|
526
|
+
details: {
|
|
527
|
+
retryable: false,
|
|
528
|
+
category: "internal_error",
|
|
529
|
+
errorClass: error instanceof Error ? error.name : typeof error,
|
|
530
|
+
},
|
|
533
531
|
},
|
|
534
532
|
};
|
|
535
533
|
}
|
|
@@ -557,7 +555,12 @@ function isPlainRecord(value: unknown): value is Record<string, unknown> {
|
|
|
557
555
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
558
556
|
}
|
|
559
557
|
|
|
560
|
-
|
|
558
|
+
// Accepts `unknown` so the branded guards narrow cleanly from the top: the
|
|
559
|
+
// subtype error classes are structurally compatible with ProviderError, so
|
|
560
|
+
// narrowing from a ProviderError-typed value would collapse the negative branch
|
|
561
|
+
// to `never`. Narrowing from unknown avoids that while still recognizing errors
|
|
562
|
+
// from a duplicate SDK module instance.
|
|
563
|
+
function providerObservabilityDetails(error: unknown):
|
|
561
564
|
| {
|
|
562
565
|
category: ProviderErrorCategory;
|
|
563
566
|
taxonomyVersion: string;
|
|
@@ -570,14 +573,26 @@ function providerObservabilityDetails(error: ProviderError):
|
|
|
570
573
|
// operation (see design.md §4.3 D3). Without this branch the auth error would
|
|
571
574
|
// serialize as a bare 401 with no retryable/category, losing the refresh
|
|
572
575
|
// signal for exactly the retryOnAuthRefresh operations it is meant to enable.
|
|
573
|
-
if (error
|
|
576
|
+
if (isSessionExpiredError(error)) {
|
|
574
577
|
return {
|
|
575
578
|
category: error.options?.category ?? "credential_expired",
|
|
576
579
|
taxonomyVersion: PROVIDER_OBSERVABILITY_TAXONOMY_VERSION,
|
|
577
580
|
retryable: error.options?.retryable ?? false,
|
|
578
581
|
};
|
|
579
582
|
}
|
|
580
|
-
|
|
583
|
+
// Missing-secret errors carry the canonical credential_unavailable category
|
|
584
|
+
// so Gateway/observability can attribute the failure to provisioning, not
|
|
585
|
+
// the upstream. Matched by code (not constructor) so both the SDK-owned
|
|
586
|
+
// runtime gate and any not-yet-migrated provider-thrown MISSING_SECRET
|
|
587
|
+
// serialize identically, including across duplicate SDK module instances.
|
|
588
|
+
if (isProviderError(error) && error.code === MISSING_SECRET_CODE) {
|
|
589
|
+
return {
|
|
590
|
+
category: error.options?.category ?? "credential_unavailable",
|
|
591
|
+
taxonomyVersion: PROVIDER_OBSERVABILITY_TAXONOMY_VERSION,
|
|
592
|
+
retryable: error.options?.retryable ?? false,
|
|
593
|
+
};
|
|
594
|
+
}
|
|
595
|
+
if (!isTransportError(error)) {
|
|
581
596
|
return undefined;
|
|
582
597
|
}
|
|
583
598
|
const isProxyPoolCode =
|
|
@@ -610,7 +625,7 @@ function providerObservabilityDetails(error: ProviderError):
|
|
|
610
625
|
}
|
|
611
626
|
|
|
612
627
|
function publicProviderErrorMessage(error: ProviderError): string {
|
|
613
|
-
if (error
|
|
628
|
+
if (isTransportError(error)) {
|
|
614
629
|
if (error.code === PROXY_AUTH_IP_DENIED_CODE) {
|
|
615
630
|
return error.message;
|
|
616
631
|
}
|
|
@@ -633,22 +648,24 @@ function publicProviderErrorMessage(error: ProviderError): string {
|
|
|
633
648
|
return error.message;
|
|
634
649
|
}
|
|
635
650
|
|
|
636
|
-
function toStatusCode(
|
|
637
|
-
error: unknown,
|
|
638
|
-
): 400 | 401 | 404 | 429 | 500 | 502 | 503 | 504 {
|
|
651
|
+
function toStatusCode(error: unknown): 400 | 401 | 404 | 429 | 500 | 502 | 503 | 504 {
|
|
639
652
|
if (error instanceof z.ZodError) {
|
|
640
653
|
return 400;
|
|
641
654
|
}
|
|
642
655
|
|
|
643
|
-
if (error
|
|
656
|
+
if (isTransportError(error)) {
|
|
644
657
|
return error.code === "transport_timeout" ? 504 : 502;
|
|
645
658
|
}
|
|
646
659
|
|
|
647
|
-
if (error
|
|
660
|
+
if (isProviderError(error)) {
|
|
648
661
|
switch (error.code) {
|
|
649
662
|
case "AUTH_REQUIRED":
|
|
650
663
|
case "reauth_required":
|
|
651
664
|
return 401;
|
|
665
|
+
// Unprovisioned declared secret: a deployment/config defect, never an
|
|
666
|
+
// upstream failure — explicit 400 (was only reached via fallthrough).
|
|
667
|
+
case MISSING_SECRET_CODE:
|
|
668
|
+
return 400;
|
|
652
669
|
case "NOT_FOUND":
|
|
653
670
|
case "not_found":
|
|
654
671
|
case "NO_DATA":
|
|
@@ -690,20 +707,15 @@ function logProviderError(
|
|
|
690
707
|
status: number,
|
|
691
708
|
cost: ProviderRequestCost,
|
|
692
709
|
): void {
|
|
693
|
-
const code =
|
|
694
|
-
error
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
: "internal_error";
|
|
710
|
+
const code = isProviderError(error)
|
|
711
|
+
? (error.code ?? "provider_error")
|
|
712
|
+
: error instanceof z.ZodError
|
|
713
|
+
? "invalid_request"
|
|
714
|
+
: "internal_error";
|
|
699
715
|
const errorClass = error instanceof Error ? error.name : typeof error;
|
|
700
716
|
const message = error instanceof Error ? error.message : String(error);
|
|
701
|
-
const details =
|
|
702
|
-
|
|
703
|
-
? providerObservabilityDetails(error)
|
|
704
|
-
: undefined;
|
|
705
|
-
const emit =
|
|
706
|
-
typeof logger === "function" ? logger : defaultProviderServerLogger;
|
|
717
|
+
const details = isProviderError(error) ? providerObservabilityDetails(error) : undefined;
|
|
718
|
+
const emit = typeof logger === "function" ? logger : defaultProviderServerLogger;
|
|
707
719
|
emit({
|
|
708
720
|
level: status >= 500 ? "error" : "warn",
|
|
709
721
|
event: "provider_request_failed",
|
|
@@ -716,7 +728,7 @@ function logProviderError(
|
|
|
716
728
|
code,
|
|
717
729
|
errorClass,
|
|
718
730
|
message,
|
|
719
|
-
...(error
|
|
731
|
+
...(isTransportError(error) && error.upstreamStatus
|
|
720
732
|
? { upstreamStatus: error.upstreamStatus }
|
|
721
733
|
: {}),
|
|
722
734
|
...(details
|
|
@@ -738,8 +750,7 @@ function logProviderCleanupError(
|
|
|
738
750
|
resource: "browser" | "stealth",
|
|
739
751
|
error: unknown,
|
|
740
752
|
): void {
|
|
741
|
-
const emit =
|
|
742
|
-
typeof logger === "function" ? logger : defaultProviderServerLogger;
|
|
753
|
+
const emit = typeof logger === "function" ? logger : defaultProviderServerLogger;
|
|
743
754
|
const errorClass = error instanceof Error ? error.name : typeof error;
|
|
744
755
|
const message = error instanceof Error ? error.message : String(error);
|
|
745
756
|
emit({
|
|
@@ -764,8 +775,7 @@ function logProviderSuccess(
|
|
|
764
775
|
status: number,
|
|
765
776
|
cost: ProviderRequestCost,
|
|
766
777
|
): void {
|
|
767
|
-
const emit =
|
|
768
|
-
typeof logger === "function" ? logger : defaultProviderServerLogger;
|
|
778
|
+
const emit = typeof logger === "function" ? logger : defaultProviderServerLogger;
|
|
769
779
|
emit({
|
|
770
780
|
level: "info",
|
|
771
781
|
event: "provider_request_completed",
|
|
@@ -811,18 +821,13 @@ function toJsonSuccessResponse(
|
|
|
811
821
|
};
|
|
812
822
|
}
|
|
813
823
|
|
|
814
|
-
function isAsyncIterable<T = unknown>(
|
|
815
|
-
value: unknown,
|
|
816
|
-
): value is AsyncIterable<T> {
|
|
824
|
+
function isAsyncIterable<T = unknown>(value: unknown): value is AsyncIterable<T> {
|
|
817
825
|
if (!value || typeof value !== "object") return false;
|
|
818
826
|
const iterator = Reflect.get(value, Symbol.asyncIterator);
|
|
819
827
|
return typeof iterator === "function";
|
|
820
828
|
}
|
|
821
829
|
|
|
822
|
-
function responseWithCleanup(
|
|
823
|
-
response: Response,
|
|
824
|
-
cleanup: RequestCleanup,
|
|
825
|
-
): Response {
|
|
830
|
+
function responseWithCleanup(response: Response, cleanup: RequestCleanup): Response {
|
|
826
831
|
if (!response.body) {
|
|
827
832
|
void cleanup();
|
|
828
833
|
return response;
|
|
@@ -871,10 +876,7 @@ async function validateSseEvent(
|
|
|
871
876
|
const transport = getSseTransport(operation);
|
|
872
877
|
const schema = transport?.events?.[event.event];
|
|
873
878
|
if (!schema) {
|
|
874
|
-
if (
|
|
875
|
-
event.event === APIFUSE_STREAM_ERROR_EVENT ||
|
|
876
|
-
event.event === APIFUSE_STREAM_DONE_EVENT
|
|
877
|
-
) {
|
|
879
|
+
if (event.event === APIFUSE_STREAM_ERROR_EVENT || event.event === APIFUSE_STREAM_DONE_EVENT) {
|
|
878
880
|
return event;
|
|
879
881
|
}
|
|
880
882
|
throw new ProviderError(
|
|
@@ -887,11 +889,7 @@ async function validateSseEvent(
|
|
|
887
889
|
},
|
|
888
890
|
);
|
|
889
891
|
}
|
|
890
|
-
const data = await parseSchema(
|
|
891
|
-
schema,
|
|
892
|
-
event.data,
|
|
893
|
-
`transport.events.${event.event}`,
|
|
894
|
-
);
|
|
892
|
+
const data = await parseSchema(schema, event.data, `transport.events.${event.event}`);
|
|
895
893
|
return { ...event, data };
|
|
896
894
|
}
|
|
897
895
|
|
|
@@ -911,8 +909,7 @@ function assertStreamPayloadWithinLimit(
|
|
|
911
909
|
throw new ProviderError(
|
|
912
910
|
`Stream ${kind} exceeded declared byte limit (${actualBytes} > ${maxBytes}).`,
|
|
913
911
|
{
|
|
914
|
-
code:
|
|
915
|
-
kind === "event" ? "STREAM_EVENT_TOO_LARGE" : "STREAM_CHUNK_TOO_LARGE",
|
|
912
|
+
code: kind === "event" ? "STREAM_EVENT_TOO_LARGE" : "STREAM_CHUNK_TOO_LARGE",
|
|
916
913
|
retryable: false,
|
|
917
914
|
category: "input_validation",
|
|
918
915
|
fix:
|
|
@@ -957,15 +954,10 @@ function toSseResponse(
|
|
|
957
954
|
const validated = await validateSseEvent(operation, next.value);
|
|
958
955
|
const encodedEvent = encodeSseEvent(validated);
|
|
959
956
|
const encodedBytes = encoder.encode(encodedEvent);
|
|
960
|
-
assertStreamPayloadWithinLimit(
|
|
961
|
-
encodedBytes.byteLength,
|
|
962
|
-
transport?.maxEventBytes,
|
|
963
|
-
"event",
|
|
964
|
-
);
|
|
957
|
+
assertStreamPayloadWithinLimit(encodedBytes.byteLength, transport?.maxEventBytes, "event");
|
|
965
958
|
controller.enqueue(encodedBytes);
|
|
966
959
|
} catch (error) {
|
|
967
|
-
const message =
|
|
968
|
-
error instanceof Error ? error.message : "Stream failed";
|
|
960
|
+
const message = error instanceof Error ? error.message : "Stream failed";
|
|
969
961
|
controller.enqueue(
|
|
970
962
|
encoder.encode(
|
|
971
963
|
encodeSseEvent(
|
|
@@ -1012,11 +1004,7 @@ function enforceStreamChunkLimit(
|
|
|
1012
1004
|
return;
|
|
1013
1005
|
}
|
|
1014
1006
|
if (value) {
|
|
1015
|
-
assertStreamPayloadWithinLimit(
|
|
1016
|
-
byteLength(value),
|
|
1017
|
-
maxChunkBytes,
|
|
1018
|
-
"chunk",
|
|
1019
|
-
);
|
|
1007
|
+
assertStreamPayloadWithinLimit(byteLength(value), maxChunkBytes, "chunk");
|
|
1020
1008
|
controller.enqueue(value);
|
|
1021
1009
|
}
|
|
1022
1010
|
} catch (error) {
|
|
@@ -1036,10 +1024,7 @@ function toStreamingResponse(
|
|
|
1036
1024
|
requestId?: string,
|
|
1037
1025
|
): Response {
|
|
1038
1026
|
const transport = operation.transport?.kind ?? "json";
|
|
1039
|
-
if (
|
|
1040
|
-
transport === "sse" &&
|
|
1041
|
-
(result instanceof Response || result instanceof ReadableStream)
|
|
1042
|
-
) {
|
|
1027
|
+
if (transport === "sse" && (result instanceof Response || result instanceof ReadableStream)) {
|
|
1043
1028
|
void cleanup();
|
|
1044
1029
|
throw new ProviderError(
|
|
1045
1030
|
"SSE operations must return an AsyncIterable of typed stream.event(...) values.",
|
|
@@ -1053,20 +1038,13 @@ function toStreamingResponse(
|
|
|
1053
1038
|
}
|
|
1054
1039
|
if (result instanceof Response) {
|
|
1055
1040
|
const httpTransport = getHttpStreamTransport(operation);
|
|
1056
|
-
if (
|
|
1057
|
-
httpTransport &&
|
|
1058
|
-
result.body &&
|
|
1059
|
-
httpTransport?.maxChunkBytes !== undefined
|
|
1060
|
-
) {
|
|
1041
|
+
if (httpTransport && result.body && httpTransport?.maxChunkBytes !== undefined) {
|
|
1061
1042
|
return responseWithCleanup(
|
|
1062
|
-
new Response(
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
statusText: result.statusText,
|
|
1068
|
-
},
|
|
1069
|
-
),
|
|
1043
|
+
new Response(enforceStreamChunkLimit(result.body, httpTransport.maxChunkBytes), {
|
|
1044
|
+
headers: result.headers,
|
|
1045
|
+
status: result.status,
|
|
1046
|
+
statusText: result.statusText,
|
|
1047
|
+
}),
|
|
1070
1048
|
cleanup,
|
|
1071
1049
|
);
|
|
1072
1050
|
}
|
|
@@ -1086,8 +1064,7 @@ function toStreamingResponse(
|
|
|
1086
1064
|
: {
|
|
1087
1065
|
"Content-Type":
|
|
1088
1066
|
operation.transport?.kind === "http-stream"
|
|
1089
|
-
? (operation.transport.contentType ??
|
|
1090
|
-
"application/octet-stream")
|
|
1067
|
+
? (operation.transport.contentType ?? "application/octet-stream")
|
|
1091
1068
|
: "application/octet-stream",
|
|
1092
1069
|
},
|
|
1093
1070
|
}),
|
|
@@ -1107,18 +1084,14 @@ function toStreamingResponse(
|
|
|
1107
1084
|
);
|
|
1108
1085
|
}
|
|
1109
1086
|
|
|
1110
|
-
function getSseTransport(
|
|
1111
|
-
operation: OperationDefinition,
|
|
1112
|
-
): OperationSseTransport | undefined {
|
|
1087
|
+
function getSseTransport(operation: OperationDefinition): OperationSseTransport | undefined {
|
|
1113
1088
|
return operation.transport?.kind === "sse" ? operation.transport : undefined;
|
|
1114
1089
|
}
|
|
1115
1090
|
|
|
1116
1091
|
function getHttpStreamTransport(
|
|
1117
1092
|
operation: OperationDefinition,
|
|
1118
1093
|
): OperationHttpStreamTransport | undefined {
|
|
1119
|
-
return operation.transport?.kind === "http-stream"
|
|
1120
|
-
? operation.transport
|
|
1121
|
-
: undefined;
|
|
1094
|
+
return operation.transport?.kind === "http-stream" ? operation.transport : undefined;
|
|
1122
1095
|
}
|
|
1123
1096
|
|
|
1124
1097
|
function toAuthFlowResponse(
|
|
@@ -1139,9 +1112,7 @@ function toAuthFlowResponse(
|
|
|
1139
1112
|
};
|
|
1140
1113
|
}
|
|
1141
1114
|
|
|
1142
|
-
function authFlowLocaleFromHeaders(
|
|
1143
|
-
headers?: Record<string, string>,
|
|
1144
|
-
): ProviderLocale {
|
|
1115
|
+
function authFlowLocaleFromHeaders(headers?: Record<string, string>): ProviderLocale {
|
|
1145
1116
|
const header = Object.entries(headers ?? {}).find(
|
|
1146
1117
|
([key]) => key.toLowerCase() === "accept-language",
|
|
1147
1118
|
)?.[1];
|
|
@@ -1159,9 +1130,7 @@ function isAuthFlowLocale(value: string | undefined): value is ProviderLocale {
|
|
|
1159
1130
|
}
|
|
1160
1131
|
|
|
1161
1132
|
function isAuthTurn(value: unknown): value is AuthTurn {
|
|
1162
|
-
return
|
|
1163
|
-
!!value && typeof value === "object" && "kind" in value && "turnId" in value
|
|
1164
|
-
);
|
|
1133
|
+
return !!value && typeof value === "object" && "kind" in value && "turnId" in value;
|
|
1165
1134
|
}
|
|
1166
1135
|
|
|
1167
1136
|
function loadAuthFlowLocaleCatalogs(
|
|
@@ -1198,10 +1167,7 @@ function materializeAuthFlowTurn(
|
|
|
1198
1167
|
});
|
|
1199
1168
|
}
|
|
1200
1169
|
|
|
1201
|
-
function withAuthRequestHeaders(
|
|
1202
|
-
request: AuthFlowRequest,
|
|
1203
|
-
headers: Headers,
|
|
1204
|
-
): AuthFlowRequest {
|
|
1170
|
+
function withAuthRequestHeaders(request: AuthFlowRequest, headers: Headers): AuthFlowRequest {
|
|
1205
1171
|
return {
|
|
1206
1172
|
...request,
|
|
1207
1173
|
headers: {
|
|
@@ -1219,17 +1185,9 @@ async function handleOperation(
|
|
|
1219
1185
|
state: ProviderRuntimeState = createUnsupportedProviderRuntimeState(),
|
|
1220
1186
|
proxyTelemetry?: ProxyTelemetryCollector,
|
|
1221
1187
|
): Promise<Response | OperationResponse> {
|
|
1222
|
-
const ctx = createProviderContext(
|
|
1223
|
-
provider,
|
|
1224
|
-
request,
|
|
1225
|
-
operationId,
|
|
1226
|
-
options,
|
|
1227
|
-
state,
|
|
1228
|
-
proxyTelemetry,
|
|
1229
|
-
);
|
|
1188
|
+
const ctx = createProviderContext(provider, request, operationId, options, state, proxyTelemetry);
|
|
1230
1189
|
const operation = provider.operations[operationId];
|
|
1231
|
-
const streaming =
|
|
1232
|
-
operation?.transport?.kind && operation.transport.kind !== "json";
|
|
1190
|
+
const streaming = operation?.transport?.kind && operation.transport.kind !== "json";
|
|
1233
1191
|
let cleanupCalled = false;
|
|
1234
1192
|
const cleanup = async () => {
|
|
1235
1193
|
if (cleanupCalled) return;
|
|
@@ -1260,12 +1218,7 @@ async function handleOperation(
|
|
|
1260
1218
|
}
|
|
1261
1219
|
};
|
|
1262
1220
|
try {
|
|
1263
|
-
const result = await executeOperation(
|
|
1264
|
-
provider,
|
|
1265
|
-
operationId,
|
|
1266
|
-
ctx,
|
|
1267
|
-
request.input,
|
|
1268
|
-
);
|
|
1221
|
+
const result = await executeOperation(provider, operationId, ctx, request.input);
|
|
1269
1222
|
if (streaming && operation) {
|
|
1270
1223
|
return toStreamingResponse(operation, result, cleanup, request.requestId);
|
|
1271
1224
|
}
|
|
@@ -1309,13 +1262,16 @@ async function handleAuthFlow(
|
|
|
1309
1262
|
});
|
|
1310
1263
|
}
|
|
1311
1264
|
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
);
|
|
1265
|
+
// Same SDK-owned gate as executeOperation: OAuth/credentials ceremonies
|
|
1266
|
+
// depend on declared secrets (client ids/secrets), so fail structured before
|
|
1267
|
+
// any flow code runs instead of at whatever point the ceremony first reads
|
|
1268
|
+
// the env. `abort` stays exempt: a user must always be able to cancel a
|
|
1269
|
+
// stranded flow even when provisioning is broken.
|
|
1270
|
+
const { context, getPatch } = createAuthFlowContext(provider, request, options, signal);
|
|
1318
1271
|
try {
|
|
1272
|
+
if (route !== "abort") {
|
|
1273
|
+
assertRequiredSecretsPresent(provider, context.env);
|
|
1274
|
+
}
|
|
1319
1275
|
const result =
|
|
1320
1276
|
route === "start"
|
|
1321
1277
|
? await flow.start(context)
|
|
@@ -1370,6 +1326,24 @@ export function createServerApp(
|
|
|
1370
1326
|
allowMemoryFallback: options.allowMemoryStateFallback === true,
|
|
1371
1327
|
});
|
|
1372
1328
|
|
|
1329
|
+
// Boot-time visibility for unprovisioned declared secrets: emit a structured
|
|
1330
|
+
// warn so deploy tooling/alerting sees the gap the moment the pod boots,
|
|
1331
|
+
// instead of discovering it request-by-request. Deliberately log-only — a
|
|
1332
|
+
// boot crash would trade a structured MISSING_SECRET signal for
|
|
1333
|
+
// CrashLoopBackOff. Requests still fail closed via the executeOperation gate.
|
|
1334
|
+
const missingSecretsAtBoot = listMissingRequiredSecrets(
|
|
1335
|
+
provider,
|
|
1336
|
+
createEnvContext(provider.secrets?.map((secret) => secret.name)),
|
|
1337
|
+
);
|
|
1338
|
+
if (missingSecretsAtBoot.length > 0) {
|
|
1339
|
+
logger({
|
|
1340
|
+
level: "warn",
|
|
1341
|
+
event: "provider_secrets_missing",
|
|
1342
|
+
providerId: provider.id,
|
|
1343
|
+
missingSecrets: missingSecretsAtBoot,
|
|
1344
|
+
});
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1373
1347
|
app.notFound((c) =>
|
|
1374
1348
|
c.json(
|
|
1375
1349
|
{
|
|
@@ -1462,17 +1436,8 @@ export function createServerApp(
|
|
|
1462
1436
|
.clone()
|
|
1463
1437
|
.json()
|
|
1464
1438
|
.catch(() => undefined);
|
|
1465
|
-
const body = withAuthRequestHeaders(
|
|
1466
|
-
|
|
1467
|
-
c.req.raw.headers,
|
|
1468
|
-
);
|
|
1469
|
-
const response = await handleAuthFlow(
|
|
1470
|
-
provider,
|
|
1471
|
-
body,
|
|
1472
|
-
"start",
|
|
1473
|
-
options,
|
|
1474
|
-
c.req.raw.signal,
|
|
1475
|
-
);
|
|
1439
|
+
const body = withAuthRequestHeaders(AuthFlowRequestSchema.parse(rawBody), c.req.raw.headers);
|
|
1440
|
+
const response = await handleAuthFlow(provider, body, "start", options, c.req.raw.signal);
|
|
1476
1441
|
logProviderSuccess(
|
|
1477
1442
|
logger,
|
|
1478
1443
|
provider,
|
|
@@ -1508,17 +1473,8 @@ export function createServerApp(
|
|
|
1508
1473
|
.clone()
|
|
1509
1474
|
.json()
|
|
1510
1475
|
.catch(() => undefined);
|
|
1511
|
-
const body = withAuthRequestHeaders(
|
|
1512
|
-
|
|
1513
|
-
c.req.raw.headers,
|
|
1514
|
-
);
|
|
1515
|
-
const response = await handleAuthFlow(
|
|
1516
|
-
provider,
|
|
1517
|
-
body,
|
|
1518
|
-
"continue",
|
|
1519
|
-
options,
|
|
1520
|
-
c.req.raw.signal,
|
|
1521
|
-
);
|
|
1476
|
+
const body = withAuthRequestHeaders(AuthFlowRequestSchema.parse(rawBody), c.req.raw.headers);
|
|
1477
|
+
const response = await handleAuthFlow(provider, body, "continue", options, c.req.raw.signal);
|
|
1522
1478
|
logProviderSuccess(
|
|
1523
1479
|
logger,
|
|
1524
1480
|
provider,
|
|
@@ -1554,17 +1510,8 @@ export function createServerApp(
|
|
|
1554
1510
|
.clone()
|
|
1555
1511
|
.json()
|
|
1556
1512
|
.catch(() => undefined);
|
|
1557
|
-
const body = withAuthRequestHeaders(
|
|
1558
|
-
|
|
1559
|
-
c.req.raw.headers,
|
|
1560
|
-
);
|
|
1561
|
-
const response = await handleAuthFlow(
|
|
1562
|
-
provider,
|
|
1563
|
-
body,
|
|
1564
|
-
"poll",
|
|
1565
|
-
options,
|
|
1566
|
-
c.req.raw.signal,
|
|
1567
|
-
);
|
|
1513
|
+
const body = withAuthRequestHeaders(AuthFlowRequestSchema.parse(rawBody), c.req.raw.headers);
|
|
1514
|
+
const response = await handleAuthFlow(provider, body, "poll", options, c.req.raw.signal);
|
|
1568
1515
|
logProviderSuccess(
|
|
1569
1516
|
logger,
|
|
1570
1517
|
provider,
|
|
@@ -1600,17 +1547,8 @@ export function createServerApp(
|
|
|
1600
1547
|
.clone()
|
|
1601
1548
|
.json()
|
|
1602
1549
|
.catch(() => undefined);
|
|
1603
|
-
const body = withAuthRequestHeaders(
|
|
1604
|
-
|
|
1605
|
-
c.req.raw.headers,
|
|
1606
|
-
);
|
|
1607
|
-
const response = await handleAuthFlow(
|
|
1608
|
-
provider,
|
|
1609
|
-
body,
|
|
1610
|
-
"refresh",
|
|
1611
|
-
options,
|
|
1612
|
-
c.req.raw.signal,
|
|
1613
|
-
);
|
|
1550
|
+
const body = withAuthRequestHeaders(AuthFlowRequestSchema.parse(rawBody), c.req.raw.headers);
|
|
1551
|
+
const response = await handleAuthFlow(provider, body, "refresh", options, c.req.raw.signal);
|
|
1614
1552
|
logProviderSuccess(
|
|
1615
1553
|
logger,
|
|
1616
1554
|
provider,
|
|
@@ -1646,17 +1584,8 @@ export function createServerApp(
|
|
|
1646
1584
|
.clone()
|
|
1647
1585
|
.json()
|
|
1648
1586
|
.catch(() => undefined);
|
|
1649
|
-
const body = withAuthRequestHeaders(
|
|
1650
|
-
|
|
1651
|
-
c.req.raw.headers,
|
|
1652
|
-
);
|
|
1653
|
-
const response = await handleAuthFlow(
|
|
1654
|
-
provider,
|
|
1655
|
-
body,
|
|
1656
|
-
"abort",
|
|
1657
|
-
options,
|
|
1658
|
-
c.req.raw.signal,
|
|
1659
|
-
);
|
|
1587
|
+
const body = withAuthRequestHeaders(AuthFlowRequestSchema.parse(rawBody), c.req.raw.headers);
|
|
1588
|
+
const response = await handleAuthFlow(provider, body, "abort", options, c.req.raw.signal);
|
|
1660
1589
|
logProviderSuccess(
|
|
1661
1590
|
logger,
|
|
1662
1591
|
provider,
|
|
@@ -1731,12 +1660,9 @@ export async function serve(
|
|
|
1731
1660
|
const bunRuntime = getBunServeRuntime();
|
|
1732
1661
|
|
|
1733
1662
|
if (bunRuntime === undefined) {
|
|
1734
|
-
throw new ProviderError(
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
code: "RUNTIME_UNSUPPORTED",
|
|
1738
|
-
},
|
|
1739
|
-
);
|
|
1663
|
+
throw new ProviderError("Bun runtime is required to start the provider server", {
|
|
1664
|
+
code: "RUNTIME_UNSUPPORTED",
|
|
1665
|
+
});
|
|
1740
1666
|
}
|
|
1741
1667
|
|
|
1742
1668
|
const app = createServerApp(provider, {
|
|
@@ -1758,6 +1684,7 @@ export async function serve(
|
|
|
1758
1684
|
const selfTestApp = createSelfTestApp(provider, {
|
|
1759
1685
|
secrets: selfTestSecrets,
|
|
1760
1686
|
invoke: createSelfTestInvoke(app),
|
|
1687
|
+
authFlow: createSelfTestAuthFlowInvoke(app),
|
|
1761
1688
|
});
|
|
1762
1689
|
bunRuntime.serve({
|
|
1763
1690
|
port: options.selfTestPort ?? resolveSelfTestPort(),
|