@apifuse/provider-sdk 2.2.0-beta.38 → 2.2.0-beta.39
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -0
- package/dist/declaration-validation.d.ts +7 -0
- package/dist/declaration-validation.js +22 -0
- package/dist/define.d.ts +2 -2
- package/dist/define.js +32 -22
- package/dist/index.d.ts +2 -1
- package/dist/runtime/executor.d.ts +2 -2
- package/dist/runtime/executor.js +3 -1
- package/dist/runtime/instrumentation.d.ts +2 -2
- package/dist/runtime/proxy-telemetry.d.ts +41 -1
- package/dist/runtime/proxy-telemetry.js +59 -56
- package/dist/server/index.d.ts +2 -0
- package/dist/server/serve-implementation.d.ts +2 -0
- package/dist/server/serve-implementation.js +81 -30
- package/package.json +1 -1
- package/src/declaration-validation.ts +41 -4
- package/src/define.ts +52 -38
- package/src/index.ts +4 -0
- package/src/runtime/executor.ts +11 -6
- package/src/runtime/instrumentation.ts +5 -2
- package/src/runtime/proxy-telemetry.ts +100 -99
- package/src/server/index.ts +8 -0
- package/src/server/serve-implementation.ts +90 -8
|
@@ -21,7 +21,7 @@ import { wrapWithInstrumentation } from "../runtime/instrumentation.js";
|
|
|
21
21
|
import { getProviderBaseUrl } from "../runtime/provider.js";
|
|
22
22
|
import { createOcrClientFromEnv } from "../runtime/ocr.js";
|
|
23
23
|
import { PROXY_AUTH_IP_DENIED_CODE, PROXY_EDGE_AUTH_REJECTED_CODE, PROXY_POOL_EXHAUSTED_CODE, } from "../runtime/proxy-errors.js";
|
|
24
|
-
import { PROVIDER_TELEMETRY_HEADER, ProxyTelemetryCollector } from "../runtime/proxy-telemetry.js";
|
|
24
|
+
import { PROVIDER_TELEMETRY_HEADER, ProxyTelemetryCollector, } from "../runtime/proxy-telemetry.js";
|
|
25
25
|
import { createUnsupportedResolverClient } from "../runtime/resolver-shared.js";
|
|
26
26
|
import { assertRequiredSecretsPresent, listMissingRequiredSecrets, MISSING_SECRET_CODE, } from "../runtime/secrets.js";
|
|
27
27
|
import { createProviderRuntimeStateFromEnv, createUnsupportedProviderRuntimeState, } from "../runtime/state.js";
|
|
@@ -348,7 +348,7 @@ function resolveOperationConnectionId(request) {
|
|
|
348
348
|
// absent so it can never override a valid id or key a real scope. Requests
|
|
349
349
|
// without any usable id fall back to the documented missing-connection
|
|
350
350
|
// sentinel scope instead of scoping context/affinity/state under "".
|
|
351
|
-
return normalizeConnectionId(request.connection?.id) ?? normalizeConnectionId(request.connectionId);
|
|
351
|
+
return (normalizeConnectionId(request.connection?.id) ?? normalizeConnectionId(request.connectionId));
|
|
352
352
|
}
|
|
353
353
|
function normalizeConnectionId(id) {
|
|
354
354
|
return id === "" ? undefined : id;
|
|
@@ -512,7 +512,7 @@ export function resolveAuthFlowProxyAffinityKey(provider, request) {
|
|
|
512
512
|
request.providerId ??
|
|
513
513
|
provider.id);
|
|
514
514
|
}
|
|
515
|
-
function createAuthFlowContext(provider, request, options, state, signal) {
|
|
515
|
+
function createAuthFlowContext(provider, request, options, state, proxyTelemetry, signal) {
|
|
516
516
|
const baseUrl = getProviderBaseUrl(provider);
|
|
517
517
|
const stealthBaseUrl = getProviderStealthBaseUrl(provider);
|
|
518
518
|
const stealthProfile = getProviderStealthProfile(provider);
|
|
@@ -522,11 +522,13 @@ function createAuthFlowContext(provider, request, options, state, signal) {
|
|
|
522
522
|
const proxyClientOptions = {
|
|
523
523
|
upstream: { proxy: provider.proxy },
|
|
524
524
|
affinityKey: resolveAuthFlowProxyAffinityKey(provider, request),
|
|
525
|
+
telemetry: proxyTelemetry,
|
|
525
526
|
};
|
|
526
527
|
const resolverIdentityScope = resolveProviderResolverIdentityScope(provider, proxyClientOptions.affinityKey, request.requestId);
|
|
527
528
|
const stealthClientOptions = {
|
|
528
529
|
upstream: proxyClientOptions.upstream,
|
|
529
530
|
affinityKey: proxyClientOptions.affinityKey,
|
|
531
|
+
telemetry: proxyTelemetry,
|
|
530
532
|
};
|
|
531
533
|
const { capabilityModules } = options;
|
|
532
534
|
const logStealthCleanupError = (error) => logProviderCleanupError(options.logger, provider, "auth", "flow", request.requestId, "stealth", error);
|
|
@@ -947,7 +949,7 @@ function providerErrorCauseChain(error) {
|
|
|
947
949
|
}
|
|
948
950
|
return frames.length > 0 ? frames : undefined;
|
|
949
951
|
}
|
|
950
|
-
function logProviderError(logger, provider, kind, route, requestId, error, status, cost, declaredErrorCode) {
|
|
952
|
+
function logProviderError(logger, provider, kind, route, requestId, error, status, cost, declaredErrorCode, proxyTelemetry) {
|
|
951
953
|
const code = isProviderError(error)
|
|
952
954
|
? (error.code ?? "provider_error")
|
|
953
955
|
: error instanceof z.ZodError
|
|
@@ -965,6 +967,7 @@ function logProviderError(logger, provider, kind, route, requestId, error, statu
|
|
|
965
967
|
typeof error.code === "string" &&
|
|
966
968
|
!SDK_OWNED_PROVIDER_ERROR_CODES.has(error.code) &&
|
|
967
969
|
declaredErrorCode === undefined;
|
|
970
|
+
const proxy = proxyTelemetry?.toLogPayload();
|
|
968
971
|
const emit = typeof logger === "function" ? logger : defaultProviderServerLogger;
|
|
969
972
|
emit({
|
|
970
973
|
level: status >= 500 ? "error" : "warn",
|
|
@@ -975,6 +978,7 @@ function logProviderError(logger, provider, kind, route, requestId, error, statu
|
|
|
975
978
|
...(requestId ? { requestId } : {}),
|
|
976
979
|
status,
|
|
977
980
|
...cost,
|
|
981
|
+
...(proxy ? { proxy } : {}),
|
|
978
982
|
code,
|
|
979
983
|
errorClass,
|
|
980
984
|
message,
|
|
@@ -1008,7 +1012,8 @@ function logProviderCleanupError(logger, provider, kind, operationId, requestId,
|
|
|
1008
1012
|
message,
|
|
1009
1013
|
});
|
|
1010
1014
|
}
|
|
1011
|
-
function logProviderSuccess(logger, provider, kind, route, requestId, status, cost) {
|
|
1015
|
+
function logProviderSuccess(logger, provider, kind, route, requestId, status, cost, proxyTelemetry) {
|
|
1016
|
+
const proxy = proxyTelemetry?.toLogPayload();
|
|
1012
1017
|
const emit = typeof logger === "function" ? logger : defaultProviderServerLogger;
|
|
1013
1018
|
emit({
|
|
1014
1019
|
level: "info",
|
|
@@ -1019,6 +1024,7 @@ function logProviderSuccess(logger, provider, kind, route, requestId, status, co
|
|
|
1019
1024
|
...(requestId ? { requestId } : {}),
|
|
1020
1025
|
status,
|
|
1021
1026
|
...cost,
|
|
1027
|
+
...(proxy ? { proxy } : {}),
|
|
1022
1028
|
});
|
|
1023
1029
|
}
|
|
1024
1030
|
function toJsonSuccessResponse(result, ctx) {
|
|
@@ -1395,7 +1401,7 @@ function responseWithProviderTelemetry(response, proxyTelemetry) {
|
|
|
1395
1401
|
statusText: response.statusText,
|
|
1396
1402
|
});
|
|
1397
1403
|
}
|
|
1398
|
-
async function handleAuthFlow(provider, request, route, options, state, signal) {
|
|
1404
|
+
async function handleAuthFlow(provider, request, route, options, state, proxyTelemetry, signal) {
|
|
1399
1405
|
const flow = provider.auth?.flow;
|
|
1400
1406
|
if (!flow) {
|
|
1401
1407
|
throw new ProviderError("Auth flow is not configured", {
|
|
@@ -1407,7 +1413,7 @@ async function handleAuthFlow(provider, request, route, options, state, signal)
|
|
|
1407
1413
|
// any flow code runs instead of at whatever point the ceremony first reads
|
|
1408
1414
|
// the env. `abort` stays exempt: a user must always be able to cancel a
|
|
1409
1415
|
// stranded flow even when provisioning is broken.
|
|
1410
|
-
const { context, getPatch } = createAuthFlowContext(provider, request, options, state, signal);
|
|
1416
|
+
const { context, getPatch } = createAuthFlowContext(provider, request, options, state, proxyTelemetry, signal);
|
|
1411
1417
|
try {
|
|
1412
1418
|
if (route !== "abort") {
|
|
1413
1419
|
assertRequiredSecretsPresent(provider, context.env);
|
|
@@ -1752,20 +1758,20 @@ function createServerAppWithCapabilityModules(provider, serverOptions, capabilit
|
|
|
1752
1758
|
body.headers = { ...requestHeaders, ...body.headers };
|
|
1753
1759
|
const response = await handleOperation(provider, body, operation, options, state, proxyTelemetry, c.req.raw.signal);
|
|
1754
1760
|
if (response instanceof Response) {
|
|
1755
|
-
logProviderSuccess(logger, provider, "operation", operation, body.requestId, response.status, finishRequestCost(requestCost));
|
|
1761
|
+
logProviderSuccess(logger, provider, "operation", operation, body.requestId, response.status, finishRequestCost(requestCost), proxyTelemetry);
|
|
1756
1762
|
return responseWithProviderTelemetry(response, proxyTelemetry);
|
|
1757
1763
|
}
|
|
1758
1764
|
const telemetryHeader = proxyTelemetry.toHeaderValue();
|
|
1759
1765
|
if (telemetryHeader)
|
|
1760
1766
|
c.header(PROVIDER_TELEMETRY_HEADER, telemetryHeader);
|
|
1761
|
-
logProviderSuccess(logger, provider, "operation", operation, body.requestId, 200, finishRequestCost(requestCost));
|
|
1767
|
+
logProviderSuccess(logger, provider, "operation", operation, body.requestId, 200, finishRequestCost(requestCost), proxyTelemetry);
|
|
1762
1768
|
return c.json(response);
|
|
1763
1769
|
}
|
|
1764
1770
|
catch (error) {
|
|
1765
1771
|
const declaredErrorCode = declaredErrorCodeFor(error, operation, operationErrorCodes);
|
|
1766
1772
|
const status = toStatusCode(error, declaredErrorCode);
|
|
1767
1773
|
const requestId = extractRequestId(rawBody);
|
|
1768
|
-
logProviderError(logger, provider, "operation", operation, requestId, error, status, finishRequestCost(requestCost), declaredErrorCode);
|
|
1774
|
+
logProviderError(logger, provider, "operation", operation, requestId, error, status, finishRequestCost(requestCost), declaredErrorCode, proxyTelemetry);
|
|
1769
1775
|
const telemetryHeader = proxyTelemetry.toHeaderValue();
|
|
1770
1776
|
if (telemetryHeader)
|
|
1771
1777
|
c.header(PROVIDER_TELEMETRY_HEADER, telemetryHeader);
|
|
@@ -1774,6 +1780,7 @@ function createServerAppWithCapabilityModules(provider, serverOptions, capabilit
|
|
|
1774
1780
|
});
|
|
1775
1781
|
app.post("/auth/start", async (c) => {
|
|
1776
1782
|
let rawBody;
|
|
1783
|
+
const proxyTelemetry = new ProxyTelemetryCollector();
|
|
1777
1784
|
const requestCost = startRequestCost();
|
|
1778
1785
|
try {
|
|
1779
1786
|
rawBody = await c.req.raw
|
|
@@ -1781,19 +1788,28 @@ function createServerAppWithCapabilityModules(provider, serverOptions, capabilit
|
|
|
1781
1788
|
.json()
|
|
1782
1789
|
.catch(() => undefined);
|
|
1783
1790
|
const body = withAuthRequestHeaders(AuthFlowRequestSchema.parse(rawBody), c.req.raw.headers);
|
|
1784
|
-
const response = await handleAuthFlow(provider, body, "start", options, state, c.req.raw.signal);
|
|
1785
|
-
logProviderSuccess(logger, provider, "auth", "start", body.requestId, response instanceof Response ? response.status : 200, finishRequestCost(requestCost));
|
|
1786
|
-
|
|
1791
|
+
const response = await handleAuthFlow(provider, body, "start", options, state, proxyTelemetry, c.req.raw.signal);
|
|
1792
|
+
logProviderSuccess(logger, provider, "auth", "start", body.requestId, response instanceof Response ? response.status : 200, finishRequestCost(requestCost), proxyTelemetry);
|
|
1793
|
+
if (response instanceof Response)
|
|
1794
|
+
return responseWithProviderTelemetry(response, proxyTelemetry);
|
|
1795
|
+
const telemetryHeader = proxyTelemetry.toHeaderValue();
|
|
1796
|
+
if (telemetryHeader)
|
|
1797
|
+
c.header(PROVIDER_TELEMETRY_HEADER, telemetryHeader);
|
|
1798
|
+
return c.json(response);
|
|
1787
1799
|
}
|
|
1788
1800
|
catch (error) {
|
|
1789
1801
|
const status = toStatusCode(error);
|
|
1790
1802
|
const requestId = extractRequestId(rawBody);
|
|
1791
|
-
logProviderError(logger, provider, "auth", "start", requestId, error, status, finishRequestCost(requestCost));
|
|
1803
|
+
logProviderError(logger, provider, "auth", "start", requestId, error, status, finishRequestCost(requestCost), undefined, proxyTelemetry);
|
|
1804
|
+
const telemetryHeader = proxyTelemetry.toHeaderValue();
|
|
1805
|
+
if (telemetryHeader)
|
|
1806
|
+
c.header(PROVIDER_TELEMETRY_HEADER, telemetryHeader);
|
|
1792
1807
|
return responseWithErrorObservability(c.json(toErrorResponse(error, requestId), status), error);
|
|
1793
1808
|
}
|
|
1794
1809
|
});
|
|
1795
1810
|
app.post("/auth/continue", async (c) => {
|
|
1796
1811
|
let rawBody;
|
|
1812
|
+
const proxyTelemetry = new ProxyTelemetryCollector();
|
|
1797
1813
|
const requestCost = startRequestCost();
|
|
1798
1814
|
try {
|
|
1799
1815
|
rawBody = await c.req.raw
|
|
@@ -1801,19 +1817,28 @@ function createServerAppWithCapabilityModules(provider, serverOptions, capabilit
|
|
|
1801
1817
|
.json()
|
|
1802
1818
|
.catch(() => undefined);
|
|
1803
1819
|
const body = withAuthRequestHeaders(AuthFlowRequestSchema.parse(rawBody), c.req.raw.headers);
|
|
1804
|
-
const response = await handleAuthFlow(provider, body, "continue", options, state, c.req.raw.signal);
|
|
1805
|
-
logProviderSuccess(logger, provider, "auth", "continue", body.requestId, response instanceof Response ? response.status : 200, finishRequestCost(requestCost));
|
|
1806
|
-
|
|
1820
|
+
const response = await handleAuthFlow(provider, body, "continue", options, state, proxyTelemetry, c.req.raw.signal);
|
|
1821
|
+
logProviderSuccess(logger, provider, "auth", "continue", body.requestId, response instanceof Response ? response.status : 200, finishRequestCost(requestCost), proxyTelemetry);
|
|
1822
|
+
if (response instanceof Response)
|
|
1823
|
+
return responseWithProviderTelemetry(response, proxyTelemetry);
|
|
1824
|
+
const telemetryHeader = proxyTelemetry.toHeaderValue();
|
|
1825
|
+
if (telemetryHeader)
|
|
1826
|
+
c.header(PROVIDER_TELEMETRY_HEADER, telemetryHeader);
|
|
1827
|
+
return c.json(response);
|
|
1807
1828
|
}
|
|
1808
1829
|
catch (error) {
|
|
1809
1830
|
const status = toStatusCode(error);
|
|
1810
1831
|
const requestId = extractRequestId(rawBody);
|
|
1811
|
-
logProviderError(logger, provider, "auth", "continue", requestId, error, status, finishRequestCost(requestCost));
|
|
1832
|
+
logProviderError(logger, provider, "auth", "continue", requestId, error, status, finishRequestCost(requestCost), undefined, proxyTelemetry);
|
|
1833
|
+
const telemetryHeader = proxyTelemetry.toHeaderValue();
|
|
1834
|
+
if (telemetryHeader)
|
|
1835
|
+
c.header(PROVIDER_TELEMETRY_HEADER, telemetryHeader);
|
|
1812
1836
|
return responseWithErrorObservability(c.json(toErrorResponse(error, requestId), status), error);
|
|
1813
1837
|
}
|
|
1814
1838
|
});
|
|
1815
1839
|
app.post("/auth/poll", async (c) => {
|
|
1816
1840
|
let rawBody;
|
|
1841
|
+
const proxyTelemetry = new ProxyTelemetryCollector();
|
|
1817
1842
|
const requestCost = startRequestCost();
|
|
1818
1843
|
try {
|
|
1819
1844
|
rawBody = await c.req.raw
|
|
@@ -1821,19 +1846,28 @@ function createServerAppWithCapabilityModules(provider, serverOptions, capabilit
|
|
|
1821
1846
|
.json()
|
|
1822
1847
|
.catch(() => undefined);
|
|
1823
1848
|
const body = withAuthRequestHeaders(AuthFlowRequestSchema.parse(rawBody), c.req.raw.headers);
|
|
1824
|
-
const response = await handleAuthFlow(provider, body, "poll", options, state, c.req.raw.signal);
|
|
1825
|
-
logProviderSuccess(logger, provider, "auth", "poll", body.requestId, response instanceof Response ? response.status : 200, finishRequestCost(requestCost));
|
|
1826
|
-
|
|
1849
|
+
const response = await handleAuthFlow(provider, body, "poll", options, state, proxyTelemetry, c.req.raw.signal);
|
|
1850
|
+
logProviderSuccess(logger, provider, "auth", "poll", body.requestId, response instanceof Response ? response.status : 200, finishRequestCost(requestCost), proxyTelemetry);
|
|
1851
|
+
if (response instanceof Response)
|
|
1852
|
+
return responseWithProviderTelemetry(response, proxyTelemetry);
|
|
1853
|
+
const telemetryHeader = proxyTelemetry.toHeaderValue();
|
|
1854
|
+
if (telemetryHeader)
|
|
1855
|
+
c.header(PROVIDER_TELEMETRY_HEADER, telemetryHeader);
|
|
1856
|
+
return c.json(response);
|
|
1827
1857
|
}
|
|
1828
1858
|
catch (error) {
|
|
1829
1859
|
const status = toStatusCode(error);
|
|
1830
1860
|
const requestId = extractRequestId(rawBody);
|
|
1831
|
-
logProviderError(logger, provider, "auth", "poll", requestId, error, status, finishRequestCost(requestCost));
|
|
1861
|
+
logProviderError(logger, provider, "auth", "poll", requestId, error, status, finishRequestCost(requestCost), undefined, proxyTelemetry);
|
|
1862
|
+
const telemetryHeader = proxyTelemetry.toHeaderValue();
|
|
1863
|
+
if (telemetryHeader)
|
|
1864
|
+
c.header(PROVIDER_TELEMETRY_HEADER, telemetryHeader);
|
|
1832
1865
|
return responseWithErrorObservability(c.json(toErrorResponse(error, requestId), status), error);
|
|
1833
1866
|
}
|
|
1834
1867
|
});
|
|
1835
1868
|
app.post("/auth/refresh", async (c) => {
|
|
1836
1869
|
let rawBody;
|
|
1870
|
+
const proxyTelemetry = new ProxyTelemetryCollector();
|
|
1837
1871
|
const requestCost = startRequestCost();
|
|
1838
1872
|
try {
|
|
1839
1873
|
rawBody = await c.req.raw
|
|
@@ -1841,19 +1875,28 @@ function createServerAppWithCapabilityModules(provider, serverOptions, capabilit
|
|
|
1841
1875
|
.json()
|
|
1842
1876
|
.catch(() => undefined);
|
|
1843
1877
|
const body = withAuthRequestHeaders(AuthFlowRequestSchema.parse(rawBody), c.req.raw.headers);
|
|
1844
|
-
const response = await handleAuthFlow(provider, body, "refresh", options, state, c.req.raw.signal);
|
|
1845
|
-
logProviderSuccess(logger, provider, "auth", "refresh", body.requestId, response instanceof Response ? response.status : 200, finishRequestCost(requestCost));
|
|
1846
|
-
|
|
1878
|
+
const response = await handleAuthFlow(provider, body, "refresh", options, state, proxyTelemetry, c.req.raw.signal);
|
|
1879
|
+
logProviderSuccess(logger, provider, "auth", "refresh", body.requestId, response instanceof Response ? response.status : 200, finishRequestCost(requestCost), proxyTelemetry);
|
|
1880
|
+
if (response instanceof Response)
|
|
1881
|
+
return responseWithProviderTelemetry(response, proxyTelemetry);
|
|
1882
|
+
const telemetryHeader = proxyTelemetry.toHeaderValue();
|
|
1883
|
+
if (telemetryHeader)
|
|
1884
|
+
c.header(PROVIDER_TELEMETRY_HEADER, telemetryHeader);
|
|
1885
|
+
return c.json(response);
|
|
1847
1886
|
}
|
|
1848
1887
|
catch (error) {
|
|
1849
1888
|
const status = toStatusCode(error);
|
|
1850
1889
|
const requestId = extractRequestId(rawBody);
|
|
1851
|
-
logProviderError(logger, provider, "auth", "refresh", requestId, error, status, finishRequestCost(requestCost));
|
|
1890
|
+
logProviderError(logger, provider, "auth", "refresh", requestId, error, status, finishRequestCost(requestCost), undefined, proxyTelemetry);
|
|
1891
|
+
const telemetryHeader = proxyTelemetry.toHeaderValue();
|
|
1892
|
+
if (telemetryHeader)
|
|
1893
|
+
c.header(PROVIDER_TELEMETRY_HEADER, telemetryHeader);
|
|
1852
1894
|
return responseWithErrorObservability(c.json(toErrorResponse(error, requestId), status), error);
|
|
1853
1895
|
}
|
|
1854
1896
|
});
|
|
1855
1897
|
app.post("/auth/disconnect", async (c) => {
|
|
1856
1898
|
let rawBody;
|
|
1899
|
+
const proxyTelemetry = new ProxyTelemetryCollector();
|
|
1857
1900
|
const requestCost = startRequestCost();
|
|
1858
1901
|
try {
|
|
1859
1902
|
rawBody = await c.req.raw
|
|
@@ -1861,14 +1904,22 @@ function createServerAppWithCapabilityModules(provider, serverOptions, capabilit
|
|
|
1861
1904
|
.json()
|
|
1862
1905
|
.catch(() => undefined);
|
|
1863
1906
|
const body = withAuthRequestHeaders(AuthFlowRequestSchema.parse(rawBody), c.req.raw.headers);
|
|
1864
|
-
const response = await handleAuthFlow(provider, body, "abort", options, state, c.req.raw.signal);
|
|
1865
|
-
logProviderSuccess(logger, provider, "auth", "disconnect", body.requestId, response instanceof Response ? response.status : 200, finishRequestCost(requestCost));
|
|
1866
|
-
|
|
1907
|
+
const response = await handleAuthFlow(provider, body, "abort", options, state, proxyTelemetry, c.req.raw.signal);
|
|
1908
|
+
logProviderSuccess(logger, provider, "auth", "disconnect", body.requestId, response instanceof Response ? response.status : 200, finishRequestCost(requestCost), proxyTelemetry);
|
|
1909
|
+
if (response instanceof Response)
|
|
1910
|
+
return responseWithProviderTelemetry(response, proxyTelemetry);
|
|
1911
|
+
const telemetryHeader = proxyTelemetry.toHeaderValue();
|
|
1912
|
+
if (telemetryHeader)
|
|
1913
|
+
c.header(PROVIDER_TELEMETRY_HEADER, telemetryHeader);
|
|
1914
|
+
return c.json(response);
|
|
1867
1915
|
}
|
|
1868
1916
|
catch (error) {
|
|
1869
1917
|
const status = toStatusCode(error);
|
|
1870
1918
|
const requestId = extractRequestId(rawBody);
|
|
1871
|
-
logProviderError(logger, provider, "auth", "disconnect", requestId, error, status, finishRequestCost(requestCost));
|
|
1919
|
+
logProviderError(logger, provider, "auth", "disconnect", requestId, error, status, finishRequestCost(requestCost), undefined, proxyTelemetry);
|
|
1920
|
+
const telemetryHeader = proxyTelemetry.toHeaderValue();
|
|
1921
|
+
if (telemetryHeader)
|
|
1922
|
+
c.header(PROVIDER_TELEMETRY_HEADER, telemetryHeader);
|
|
1872
1923
|
return responseWithErrorObservability(c.json(toErrorResponse(error, requestId), status), error);
|
|
1873
1924
|
}
|
|
1874
1925
|
});
|
package/package.json
CHANGED
|
@@ -59,8 +59,45 @@ export function validateFailClosedDeclaration(provider: ProviderDefinition): voi
|
|
|
59
59
|
if (violations.length > 0) throw declarationInvalidError(violations);
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
+
type ProviderDeclarationRulesInput = Pick<ProviderDefinition, "healthJourneys" | "proxy">;
|
|
63
|
+
type OperationDeclarationRulesInput = Pick<ProviderDefinition, "operations">;
|
|
64
|
+
|
|
65
|
+
/** Enforces fail-closed rules that only depend on the provider declaration. */
|
|
66
|
+
export function validateFailClosedProviderDeclaration(
|
|
67
|
+
provider: ProviderDeclarationRulesInput,
|
|
68
|
+
): void {
|
|
69
|
+
const violations: DeclarationViolation[] = [];
|
|
70
|
+
collectProviderDeclarationViolations(provider, violations);
|
|
71
|
+
if (violations.length > 0) throw declarationInvalidError(violations);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** Enforces fail-closed rules that depend on the operation implementation. */
|
|
75
|
+
export function validateFailClosedOperationDeclaration(
|
|
76
|
+
provider: OperationDeclarationRulesInput,
|
|
77
|
+
): void {
|
|
78
|
+
const violations: DeclarationViolation[] = [];
|
|
79
|
+
collectOperationDeclarationViolations(provider, violations);
|
|
80
|
+
if (violations.length > 0) throw declarationInvalidError(violations);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function collectProviderDeclarationViolations(
|
|
84
|
+
provider: ProviderDeclarationRulesInput,
|
|
85
|
+
violations: DeclarationViolation[],
|
|
86
|
+
): void {
|
|
87
|
+
validateHealthDeclaration(provider, violations);
|
|
88
|
+
validateProxyDeclaration(provider, violations);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function collectOperationDeclarationViolations(
|
|
92
|
+
provider: OperationDeclarationRulesInput,
|
|
93
|
+
violations: DeclarationViolation[],
|
|
94
|
+
): void {
|
|
95
|
+
validateSchemaDeclaration(provider, violations);
|
|
96
|
+
validateOperationDeclaration(provider, violations);
|
|
97
|
+
}
|
|
98
|
+
|
|
62
99
|
function validateHealthDeclaration(
|
|
63
|
-
provider:
|
|
100
|
+
provider: ProviderDeclarationRulesInput,
|
|
64
101
|
violations: DeclarationViolation[],
|
|
65
102
|
): void {
|
|
66
103
|
for (const [index, journey] of (provider.healthJourneys ?? []).entries()) {
|
|
@@ -113,7 +150,7 @@ function healthJourneyPath(journey: HealthJourneyDefinition, index: number): str
|
|
|
113
150
|
}
|
|
114
151
|
|
|
115
152
|
function validateSchemaDeclaration(
|
|
116
|
-
provider:
|
|
153
|
+
provider: OperationDeclarationRulesInput,
|
|
117
154
|
violations: DeclarationViolation[],
|
|
118
155
|
): void {
|
|
119
156
|
for (const [operationId, operation] of Object.entries(provider.operations ?? {})) {
|
|
@@ -153,7 +190,7 @@ const MANAGED_PROXY_VENDORS = new Set<ProviderProxyProvider>(["smartproxy", "nod
|
|
|
153
190
|
const STATIC_PROXY_VENDORS = new Set<ProviderProxyProvider>(["custom", "decodo"]);
|
|
154
191
|
|
|
155
192
|
function validateProxyDeclaration(
|
|
156
|
-
provider:
|
|
193
|
+
provider: ProviderDeclarationRulesInput,
|
|
157
194
|
violations: DeclarationViolation[],
|
|
158
195
|
): void {
|
|
159
196
|
if (provider.proxy === true) {
|
|
@@ -213,7 +250,7 @@ function declaredProxyVendors(policy: ProviderProxyPolicy): ProviderProxyProvide
|
|
|
213
250
|
}
|
|
214
251
|
|
|
215
252
|
function validateOperationDeclaration(
|
|
216
|
-
provider:
|
|
253
|
+
provider: OperationDeclarationRulesInput,
|
|
217
254
|
violations: DeclarationViolation[],
|
|
218
255
|
): void {
|
|
219
256
|
for (const [operationId, operation] of Object.entries(provider.operations ?? {})) {
|
package/src/define.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import ms from "ms";
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
validateFailClosedOperationDeclaration,
|
|
5
|
+
validateFailClosedProviderDeclaration,
|
|
6
|
+
} from "./declaration-validation.js";
|
|
4
7
|
import { SDK_RUNTIME_OWNED_ERROR_CODES } from "./error-resolution.js";
|
|
5
8
|
import { ProviderError, ValidationError } from "./errors.js";
|
|
6
9
|
import {
|
|
@@ -30,12 +33,12 @@ import type {
|
|
|
30
33
|
OperationWebSocketTransport,
|
|
31
34
|
ProviderAccessConfig,
|
|
32
35
|
ProviderChallengeKind,
|
|
33
|
-
ProviderDefinition,
|
|
34
36
|
ProviderContext,
|
|
35
37
|
ProviderContextFor,
|
|
36
|
-
|
|
38
|
+
ProviderDefinition,
|
|
37
39
|
ProviderDeploymentOverrides,
|
|
38
40
|
ProviderHealthMonitorConfig,
|
|
41
|
+
ProviderOcrConfig,
|
|
39
42
|
ProviderProxyConfig,
|
|
40
43
|
ProviderProxyProvider,
|
|
41
44
|
ProviderPublicProfile,
|
|
@@ -806,13 +809,12 @@ function validateProxiedOAuthAuth(auth: Record<string, unknown>, providerId: str
|
|
|
806
809
|
validateProxiedOAuthParams(config.tokenParams, "tokenParams", providerId);
|
|
807
810
|
}
|
|
808
811
|
|
|
809
|
-
function
|
|
812
|
+
function validateProviderDeclarationShape(config: unknown): void {
|
|
810
813
|
assertObjectConfig(config);
|
|
811
814
|
assertRequiredField(config, "id");
|
|
812
815
|
assertRequiredField(config, "version", String(config.id));
|
|
813
816
|
assertRequiredField(config, "runtime", String(config.id));
|
|
814
817
|
assertRequiredField(config, "meta", String(config.id));
|
|
815
|
-
assertRequiredField(config, "operations", String(config.id));
|
|
816
818
|
if (typeof config.runtime === "string")
|
|
817
819
|
assertLiteralField(config.runtime, "runtime", VALID_RUNTIMES, String(config.id));
|
|
818
820
|
if (config.native !== undefined && config.runtime === "browser") {
|
|
@@ -910,6 +912,11 @@ function validateProviderShape(config: unknown): void {
|
|
|
910
912
|
}
|
|
911
913
|
}
|
|
912
914
|
|
|
915
|
+
function validateProviderImplementationShape(config: { id: string }): void {
|
|
916
|
+
const configRecord = config as unknown as Record<string, unknown>;
|
|
917
|
+
assertRequiredField(configRecord, "operations", String(config.id));
|
|
918
|
+
}
|
|
919
|
+
|
|
913
920
|
function validateProviderProxy(config: {
|
|
914
921
|
id: string;
|
|
915
922
|
proxy?: ProviderProxyConfig;
|
|
@@ -2844,7 +2851,7 @@ export type ProviderBuilder<TDeclaration extends ProviderDeclaration> = <
|
|
|
2844
2851
|
implementation: {
|
|
2845
2852
|
operations: OperationMapConfig<TOperations, ProviderContextFor<TDeclaration>>;
|
|
2846
2853
|
},
|
|
2847
|
-
) => ProviderDefinition & {
|
|
2854
|
+
) => Omit<ProviderDefinition, "operations"> & {
|
|
2848
2855
|
operations: OperationMapConfig<TOperations, ProviderContextFor<TDeclaration>>;
|
|
2849
2856
|
};
|
|
2850
2857
|
|
|
@@ -2859,6 +2866,7 @@ export function defineProvider<const TDeclaration extends ProviderDeclaration>(
|
|
|
2859
2866
|
Record<Exclude<keyof TDeclaration, keyof ProviderDeclaration>, never> &
|
|
2860
2867
|
AuthStartNoInputGuard<TDeclaration>,
|
|
2861
2868
|
): ProviderBuilder<TDeclaration> {
|
|
2869
|
+
validateProviderDeclaration(declaration);
|
|
2862
2870
|
const buildProvider = <TOperations extends Record<string, ProviderOperation>>(
|
|
2863
2871
|
implementation: {
|
|
2864
2872
|
operations: OperationMapConfig<TOperations, ProviderContextFor<TDeclaration>>;
|
|
@@ -2871,37 +2879,12 @@ export function defineProvider<const TDeclaration extends ProviderDeclaration>(
|
|
|
2871
2879
|
return buildProvider as ProviderBuilder<TDeclaration>;
|
|
2872
2880
|
}
|
|
2873
2881
|
|
|
2874
|
-
function
|
|
2875
|
-
|
|
2876
|
-
TContext,
|
|
2877
|
-
>(
|
|
2878
|
-
config: ProviderConfig<TOperations, TContext>,
|
|
2879
|
-
): ProviderDefinition & {
|
|
2880
|
-
operations: OperationMapConfig<TOperations, TContext>;
|
|
2881
|
-
} {
|
|
2882
|
-
validateProviderShape(config);
|
|
2883
|
-
const operations = resolveOperationFixtureRequests(config.operations);
|
|
2882
|
+
function validateProviderDeclaration(config: ProviderDeclaration): void {
|
|
2883
|
+
validateProviderDeclarationShape(config);
|
|
2884
2884
|
if (!CONNECTOR_ID_REGEX.test(config.id))
|
|
2885
2885
|
throw new ProviderError(`Invalid provider id: "${config.id}"`, {
|
|
2886
2886
|
fix: 'Use lowercase alphanumeric with dashes, e.g., "korea-air-quality"',
|
|
2887
2887
|
});
|
|
2888
|
-
if (Object.keys(config.operations).length === 0)
|
|
2889
|
-
throw new ProviderError(`Provider "${config.id}" must define at least one operation`, {
|
|
2890
|
-
fix: "Add at least one operation to the operations object",
|
|
2891
|
-
});
|
|
2892
|
-
validateOperationIds(config.id, config.operations);
|
|
2893
|
-
validateOperationAnnotations(config.id, config.operations);
|
|
2894
|
-
validateOperationObservability(config.id, config.operations);
|
|
2895
|
-
validateOperationErrorCodes(config.id, config.operations);
|
|
2896
|
-
validateOperationTransports(config.id, config.operations);
|
|
2897
|
-
validateOperationContracts(config.id, config.operations);
|
|
2898
|
-
validateToolRouterMetadata(config.id, config.operations);
|
|
2899
|
-
const journeyCoveredOperations = validateHealthJourneys(
|
|
2900
|
-
config.id,
|
|
2901
|
-
config.operations,
|
|
2902
|
-
config.healthJourneys,
|
|
2903
|
-
);
|
|
2904
|
-
validateOperationHealthChecks(config.id, config.operations, journeyCoveredOperations);
|
|
2905
2888
|
if (config.healthMonitor !== undefined && config.healthProbe !== undefined)
|
|
2906
2889
|
throw new ValidationError(
|
|
2907
2890
|
`Provider "${config.id}" declares both healthMonitor and healthProbe. They are aliases; declare exactly one.`,
|
|
@@ -2914,7 +2897,6 @@ function finalizeProvider<
|
|
|
2914
2897
|
config.healthProbe ?? config.healthMonitor,
|
|
2915
2898
|
config.healthProbe !== undefined ? "healthProbe" : "healthMonitor",
|
|
2916
2899
|
);
|
|
2917
|
-
validateOperationFixtures(config.id, operations);
|
|
2918
2900
|
validateProviderDeployment(config.id, config.deployment);
|
|
2919
2901
|
try {
|
|
2920
2902
|
validateNativeProviderConfig(config.native);
|
|
@@ -2939,7 +2921,38 @@ function finalizeProvider<
|
|
|
2939
2921
|
`Provider "${config.id}" cannot define browser config unless runtime is "browser"`,
|
|
2940
2922
|
{ fix: 'Set runtime: "browser" or remove the browser config' },
|
|
2941
2923
|
);
|
|
2942
|
-
|
|
2924
|
+
validateFailClosedProviderDeclaration(config);
|
|
2925
|
+
}
|
|
2926
|
+
|
|
2927
|
+
function finalizeProvider<
|
|
2928
|
+
TOperations extends Record<string, ProviderOperation>,
|
|
2929
|
+
TContext,
|
|
2930
|
+
>(
|
|
2931
|
+
config: ProviderConfig<TOperations, TContext>,
|
|
2932
|
+
): Omit<ProviderDefinition, "operations"> & {
|
|
2933
|
+
operations: OperationMapConfig<TOperations, TContext>;
|
|
2934
|
+
} {
|
|
2935
|
+
validateProviderImplementationShape(config);
|
|
2936
|
+
const operations = resolveOperationFixtureRequests(config.operations);
|
|
2937
|
+
if (Object.keys(config.operations).length === 0)
|
|
2938
|
+
throw new ProviderError(`Provider "${config.id}" must define at least one operation`, {
|
|
2939
|
+
fix: "Add at least one operation to the operations object",
|
|
2940
|
+
});
|
|
2941
|
+
validateOperationIds(config.id, config.operations);
|
|
2942
|
+
validateOperationAnnotations(config.id, config.operations);
|
|
2943
|
+
validateOperationObservability(config.id, config.operations);
|
|
2944
|
+
validateOperationErrorCodes(config.id, config.operations);
|
|
2945
|
+
validateOperationTransports(config.id, config.operations);
|
|
2946
|
+
validateOperationContracts(config.id, config.operations);
|
|
2947
|
+
validateToolRouterMetadata(config.id, config.operations);
|
|
2948
|
+
const journeyCoveredOperations = validateHealthJourneys(
|
|
2949
|
+
config.id,
|
|
2950
|
+
config.operations,
|
|
2951
|
+
config.healthJourneys,
|
|
2952
|
+
);
|
|
2953
|
+
validateOperationHealthChecks(config.id, config.operations, journeyCoveredOperations);
|
|
2954
|
+
validateOperationFixtures(config.id, operations);
|
|
2955
|
+
const provider: Omit<ProviderDefinition, "operations"> & {
|
|
2943
2956
|
operations: OperationMapConfig<TOperations, TContext>;
|
|
2944
2957
|
} = {
|
|
2945
2958
|
id: config.id,
|
|
@@ -2963,14 +2976,15 @@ function finalizeProvider<
|
|
|
2963
2976
|
credential: config.credential,
|
|
2964
2977
|
context: config.context,
|
|
2965
2978
|
meta: config.meta,
|
|
2966
|
-
operations
|
|
2967
|
-
OperationMapConfig<TOperations, TContext>,
|
|
2979
|
+
operations,
|
|
2968
2980
|
// Transitional healthMonitor → healthProbe alias: mirror whichever field
|
|
2969
2981
|
// was declared onto both so old and new consumers keep working.
|
|
2970
2982
|
healthMonitor: config.healthMonitor ?? config.healthProbe,
|
|
2971
2983
|
healthProbe: config.healthProbe ?? config.healthMonitor,
|
|
2972
2984
|
healthJourneys: config.healthJourneys,
|
|
2973
2985
|
};
|
|
2974
|
-
|
|
2986
|
+
// Declaration validation never invokes handlers, so their declaration-bound
|
|
2987
|
+
// context parameter is irrelevant to the runtime ProviderDefinition shape.
|
|
2988
|
+
validateFailClosedOperationDeclaration(provider as unknown as ProviderDefinition);
|
|
2975
2989
|
return provider;
|
|
2976
2990
|
}
|
package/src/index.ts
CHANGED
|
@@ -6,12 +6,15 @@ export * from "./choice-token.js";
|
|
|
6
6
|
export type {
|
|
7
7
|
ApiFuseConfig,
|
|
8
8
|
BrowserConfig,
|
|
9
|
+
ProxyCacheStatus,
|
|
9
10
|
ProxyProtocol,
|
|
10
11
|
ProxyResolutionOptions,
|
|
11
12
|
ProxyResolutionSource,
|
|
13
|
+
ProxyUserAgentSource,
|
|
12
14
|
ProxyVendorName,
|
|
13
15
|
ResolvedProxyConfig,
|
|
14
16
|
SessionConfig,
|
|
17
|
+
SmartproxyAllocatorBodyClass,
|
|
15
18
|
} from "./config/loader.js";
|
|
16
19
|
export { defineConfig, loadApiFuseConfig, resolveProxy } from "./config/loader.js";
|
|
17
20
|
export {
|
|
@@ -174,6 +177,7 @@ export {
|
|
|
174
177
|
} from "./runtime/instrumentation.js";
|
|
175
178
|
export type { PrevalidateResult } from "./runtime/prevalidate.js";
|
|
176
179
|
export { getProviderBaseUrl } from "./runtime/provider.js";
|
|
180
|
+
export type { ProxyTelemetryLogPayload } from "./runtime/proxy-telemetry.js";
|
|
177
181
|
export {
|
|
178
182
|
APIFUSE__CDP_POOL__URL,
|
|
179
183
|
APIFUSE__RESOLVER__2CAPTCHA__API_KEY,
|
package/src/runtime/executor.ts
CHANGED
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
} from "../errors.js";
|
|
8
8
|
import { z } from "zod";
|
|
9
9
|
import { parseSchema } from "../schema.js";
|
|
10
|
-
import type {
|
|
10
|
+
import type { ProviderDefinition } from "../types.js";
|
|
11
11
|
import { assertRequiredSecretsPresent } from "./secrets.js";
|
|
12
12
|
|
|
13
13
|
export function isStreamingOperation(provider: ProviderDefinition, operationId: string): boolean {
|
|
@@ -26,10 +26,13 @@ export function isStreamingOperation(provider: ProviderDefinition, operationId:
|
|
|
26
26
|
*
|
|
27
27
|
* @see openspec/provider-sdk/03-sdk-core.md §3.6
|
|
28
28
|
*/
|
|
29
|
-
export async function executeOperation
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
29
|
+
export async function executeOperation<
|
|
30
|
+
const TProvider extends ProviderDefinition,
|
|
31
|
+
const TOperationId extends keyof TProvider["operations"] & string,
|
|
32
|
+
>(
|
|
33
|
+
provider: TProvider,
|
|
34
|
+
operationId: TOperationId,
|
|
35
|
+
ctx: NoInfer<Parameters<TProvider["operations"][TOperationId]["handler"]>[0]>,
|
|
33
36
|
input: unknown,
|
|
34
37
|
_options?: { skipAuth?: boolean },
|
|
35
38
|
): Promise<unknown> {
|
|
@@ -47,7 +50,9 @@ export async function executeOperation(
|
|
|
47
50
|
// handler, so every invocation path (serve /v1, self-test probes, perf,
|
|
48
51
|
// record) fails with the same structured MISSING_SECRET error instead of a
|
|
49
52
|
// handler-specific crash. Providers must not re-check presence locally.
|
|
50
|
-
|
|
53
|
+
if (provider.secrets?.some((secret) => secret.required === true)) {
|
|
54
|
+
assertRequiredSecretsPresent(provider, "env" in ctx ? ctx.env : { get: () => undefined });
|
|
55
|
+
}
|
|
51
56
|
|
|
52
57
|
const validatedInput = await parseSchema(
|
|
53
58
|
operation.input,
|
|
@@ -27,7 +27,10 @@ import { RESOLVER_INSTRUMENTATION_METADATA } from "./resolver-shared.js";
|
|
|
27
27
|
|
|
28
28
|
export interface InstrumentationOptions extends CreateTraceContextOptions {}
|
|
29
29
|
|
|
30
|
-
export type InstrumentedProviderContext<T extends ProviderContext
|
|
30
|
+
export type InstrumentedProviderContext<T extends Pick<ProviderContext, "trace">> = Omit<
|
|
31
|
+
T,
|
|
32
|
+
"trace"
|
|
33
|
+
> & {
|
|
31
34
|
trace: TraceContext;
|
|
32
35
|
};
|
|
33
36
|
|
|
@@ -850,7 +853,7 @@ function hasTraceOverrides(options: InstrumentationOptions): boolean {
|
|
|
850
853
|
return options.maxSpans !== undefined || options.onSpan !== undefined;
|
|
851
854
|
}
|
|
852
855
|
|
|
853
|
-
export function wrapWithInstrumentation<T extends ProviderContext
|
|
856
|
+
export function wrapWithInstrumentation<T extends Pick<ProviderContext, "trace">>(
|
|
854
857
|
ctx: T,
|
|
855
858
|
options: InstrumentationOptions = {},
|
|
856
859
|
): InstrumentedProviderContext<T> {
|