@apifuse/provider-sdk 2.2.0-beta.60 → 2.2.0-beta.61
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 +17 -16
- package/CHANGELOG.md +4 -0
- package/bin/apifuse-dev.ts +5 -7
- package/bin/apifuse-pack-types.ts +22 -0
- package/bin/apifuse-perf.ts +4 -6
- package/bin/apifuse-record.ts +4 -7
- package/bin/apifuse-submit-check.ts +2 -2
- package/dist/define.d.ts +3 -2
- package/dist/define.js +1 -1
- package/dist/dev.js +3 -1
- package/dist/engine.d.ts +7 -6
- package/dist/engine.js +3 -4
- package/dist/error-resolution.js +21 -4
- package/dist/errors.d.ts +3 -0
- package/dist/errors.js +9 -0
- package/dist/handle-meta.d.ts +39 -0
- package/dist/handle-meta.js +29 -0
- package/dist/handle.d.ts +187 -0
- package/dist/handle.js +254 -0
- package/dist/i18n/catalog.js +21 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.js +3 -2
- package/dist/lint.d.ts +3 -0
- package/dist/lint.js +352 -47
- package/dist/provider.d.ts +3 -3
- package/dist/provider.js +2 -2
- package/dist/runtime/handle-wordlist.d.ts +43 -0
- package/dist/runtime/{choice-wordlist.js → handle-wordlist.js} +127 -9
- package/dist/runtime/handle.d.ts +71 -0
- package/dist/runtime/handle.js +895 -0
- package/dist/runtime/native-network.d.ts +10 -1
- package/dist/runtime/native-network.js +230 -29
- package/dist/runtime/native-telemetry.d.ts +142 -0
- package/dist/runtime/native-telemetry.js +231 -0
- package/dist/runtime/state.d.ts +4 -1
- package/dist/runtime/state.js +32 -17
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.js +1 -0
- package/dist/server/sensitive-values.d.ts +1 -1
- package/dist/server/sensitive-values.js +0 -3
- package/dist/server/serve-implementation.d.ts +4 -11
- package/dist/server/serve-implementation.js +86 -18
- package/dist/testing/run.js +10 -7
- package/dist/types.d.ts +4 -107
- package/package.json +1 -1
- package/src/define.ts +4 -3
- package/src/dev.ts +3 -1
- package/src/engine.ts +5 -6
- package/src/error-resolution.ts +21 -4
- package/src/errors.ts +13 -0
- package/src/handle-meta.ts +58 -0
- package/src/handle.ts +529 -0
- package/src/i18n/catalog.ts +23 -1
- package/src/index.ts +26 -13
- package/src/lint.ts +414 -55
- package/src/provider.ts +9 -20
- package/src/runtime/{choice-wordlist.ts → handle-wordlist.ts} +130 -9
- package/src/runtime/handle.ts +1162 -0
- package/src/runtime/native-network.ts +281 -33
- package/src/runtime/native-telemetry.ts +366 -0
- package/src/runtime/state.ts +44 -16
- package/src/server/index.ts +17 -0
- package/src/server/sensitive-values.ts +0 -3
- package/src/server/serve-implementation.ts +87 -32
- package/src/testing/run.ts +11 -7
- package/src/types.ts +4 -137
- package/dist/choice-token.d.ts +0 -24
- package/dist/choice-token.js +0 -74
- package/dist/runtime/choice-wordlist.d.ts +0 -9
- package/dist/runtime/choice.d.ts +0 -27
- package/dist/runtime/choice.js +0 -801
- package/src/choice-token.ts +0 -165
- package/src/runtime/choice.ts +0 -1197
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { NativeTelemetryCollector } from "../runtime/native-telemetry.js";
|
|
1
2
|
import { STATUS_CODES } from "node:http";
|
|
2
3
|
import { readDiagnosticEnv, withDiagnosticEnv } from "../runtime/diagnostic-env.js";
|
|
3
4
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
@@ -53,10 +54,8 @@ import {
|
|
|
53
54
|
import { createScratchpad } from "../runtime/auth-flow.js";
|
|
54
55
|
import type * as BrowserRuntimeModule from "../runtime/browser.js";
|
|
55
56
|
import { createProviderCache } from "../runtime/cache.js";
|
|
56
|
-
import {
|
|
57
|
-
|
|
58
|
-
PROVIDER_RUNTIME_CHOICE_TOKEN_MASTER_SECRET_ENV,
|
|
59
|
-
} from "../runtime/choice.js";
|
|
57
|
+
import type { HandleTelemetryEvent } from "../handle.js";
|
|
58
|
+
import { createHandleContext } from "../runtime/handle.js";
|
|
60
59
|
import { createCredentialContext } from "../runtime/credential.js";
|
|
61
60
|
import {
|
|
62
61
|
bindDiagnosticSensitiveRegistry,
|
|
@@ -845,6 +844,7 @@ type RequestScopeContext = {
|
|
|
845
844
|
trace: RuntimeTraceContext;
|
|
846
845
|
telemetry: RequestTelemetry;
|
|
847
846
|
resolverTelemetry: ResolverTelemetryCollector;
|
|
847
|
+
nativeTelemetry: NativeTelemetryCollector;
|
|
848
848
|
};
|
|
849
849
|
|
|
850
850
|
function createProviderContext(
|
|
@@ -921,10 +921,7 @@ function createProviderContext(
|
|
|
921
921
|
scope.redact,
|
|
922
922
|
);
|
|
923
923
|
|
|
924
|
-
const env = createEnvContext([
|
|
925
|
-
...providerSecretNames(provider),
|
|
926
|
-
PROVIDER_RUNTIME_CHOICE_TOKEN_MASTER_SECRET_ENV,
|
|
927
|
-
]);
|
|
924
|
+
const env = createEnvContext([...providerSecretNames(provider)]);
|
|
928
925
|
const credential = createCredentialContext({
|
|
929
926
|
allowedKeys: provider.credential?.keys,
|
|
930
927
|
mode: request.connection?.mode,
|
|
@@ -971,6 +968,8 @@ function createProviderContext(
|
|
|
971
968
|
? {
|
|
972
969
|
native: {
|
|
973
970
|
network: capabilityModules.nativeNetwork!.createNativeNetworkClient({
|
|
971
|
+
telemetry: scope.telemetry.proxy,
|
|
972
|
+
nativeTelemetry: scope.nativeTelemetry,
|
|
974
973
|
egress: provider.native.network,
|
|
975
974
|
proxyPolicy: resolveNativeProxyPolicy(provider),
|
|
976
975
|
affinityKey: proxyClientOptions.affinityKey,
|
|
@@ -1004,16 +1003,14 @@ function createProviderContext(
|
|
|
1004
1003
|
createUnsupportedResolverClient("Provider does not declare resolver capability"),
|
|
1005
1004
|
signal,
|
|
1006
1005
|
),
|
|
1007
|
-
|
|
1006
|
+
handle: createHandleContext({
|
|
1008
1007
|
providerId: provider.id,
|
|
1009
|
-
env,
|
|
1010
1008
|
request: requestContext,
|
|
1011
|
-
credential,
|
|
1012
1009
|
state: requestState,
|
|
1013
1010
|
onTelemetry: (event) =>
|
|
1014
1011
|
(options.logger ?? defaultProviderServerLogger)({
|
|
1015
1012
|
level: "info",
|
|
1016
|
-
event: "
|
|
1013
|
+
event: "provider_handle",
|
|
1017
1014
|
...event,
|
|
1018
1015
|
}),
|
|
1019
1016
|
}),
|
|
@@ -1213,6 +1210,8 @@ function createAuthFlowContext(
|
|
|
1213
1210
|
? {
|
|
1214
1211
|
native: {
|
|
1215
1212
|
network: capabilityModules.nativeNetwork!.createNativeNetworkClient({
|
|
1213
|
+
telemetry: scope.telemetry.proxy,
|
|
1214
|
+
nativeTelemetry: scope.nativeTelemetry,
|
|
1216
1215
|
egress: provider.native.network,
|
|
1217
1216
|
proxyPolicy: resolveNativeProxyPolicy(provider),
|
|
1218
1217
|
affinityKey: proxyClientOptions.affinityKey,
|
|
@@ -1318,18 +1317,10 @@ export type ProviderServerLogEvent =
|
|
|
1318
1317
|
providerId: string;
|
|
1319
1318
|
missingSecrets: string[];
|
|
1320
1319
|
}
|
|
1321
|
-
| {
|
|
1320
|
+
| ({
|
|
1322
1321
|
level: "info";
|
|
1323
|
-
event: "
|
|
1324
|
-
|
|
1325
|
-
purpose: string;
|
|
1326
|
-
operation: "parse" | "consume";
|
|
1327
|
-
format: "word" | "legacy";
|
|
1328
|
-
outcome: "success" | "not-found" | "invalid" | "unsupported" | "error";
|
|
1329
|
-
consumeMode: "never" | "on-parse" | "explicit";
|
|
1330
|
-
consumed: boolean;
|
|
1331
|
-
replay: boolean;
|
|
1332
|
-
}
|
|
1322
|
+
event: "provider_handle";
|
|
1323
|
+
} & HandleTelemetryEvent)
|
|
1333
1324
|
| {
|
|
1334
1325
|
level: "warn";
|
|
1335
1326
|
event: "provider_cleanup_failed";
|
|
@@ -1549,6 +1540,48 @@ function toErrorResponse(
|
|
|
1549
1540
|
// narrowing from a ProviderError-typed value would collapse the negative branch
|
|
1550
1541
|
// to `never`. Narrowing from unknown avoids that while still recognizing errors
|
|
1551
1542
|
// from a duplicate SDK module instance.
|
|
1543
|
+
function ownErrorCode(error: unknown): string | undefined {
|
|
1544
|
+
if (error === null || (typeof error !== "object" && typeof error !== "function")) {
|
|
1545
|
+
return undefined;
|
|
1546
|
+
}
|
|
1547
|
+
try {
|
|
1548
|
+
const descriptor = Object.getOwnPropertyDescriptor(error, "code");
|
|
1549
|
+
return descriptor && Object.hasOwn(descriptor, "value") && typeof descriptor.value === "string"
|
|
1550
|
+
? descriptor.value
|
|
1551
|
+
: undefined;
|
|
1552
|
+
} catch {
|
|
1553
|
+
return undefined;
|
|
1554
|
+
}
|
|
1555
|
+
}
|
|
1556
|
+
|
|
1557
|
+
function errorChainHasCode(error: unknown, expected: string): boolean {
|
|
1558
|
+
const seen = new Set<object>();
|
|
1559
|
+
let current = error;
|
|
1560
|
+
for (let depth = 0; depth < 5; depth++) {
|
|
1561
|
+
if (current === null || (typeof current !== "object" && typeof current !== "function")) {
|
|
1562
|
+
return false;
|
|
1563
|
+
}
|
|
1564
|
+
if (seen.has(current)) return false;
|
|
1565
|
+
seen.add(current);
|
|
1566
|
+
let code: string | undefined;
|
|
1567
|
+
try {
|
|
1568
|
+
code = providerErrorCode(current) ?? ownErrorCode(current);
|
|
1569
|
+
} catch {
|
|
1570
|
+
code = ownErrorCode(current);
|
|
1571
|
+
}
|
|
1572
|
+
if (code === expected) return true;
|
|
1573
|
+
let cause: PropertyDescriptor | undefined;
|
|
1574
|
+
try {
|
|
1575
|
+
cause = Object.getOwnPropertyDescriptor(current, "cause");
|
|
1576
|
+
} catch {
|
|
1577
|
+
return false;
|
|
1578
|
+
}
|
|
1579
|
+
if (!cause || !Object.hasOwn(cause, "value")) return false;
|
|
1580
|
+
current = cause.value;
|
|
1581
|
+
}
|
|
1582
|
+
return false;
|
|
1583
|
+
}
|
|
1584
|
+
|
|
1552
1585
|
function providerObservabilityDetails(
|
|
1553
1586
|
error: unknown,
|
|
1554
1587
|
declaredErrorCode?: OperationErrorCode,
|
|
@@ -1577,26 +1610,45 @@ function providerObservabilityDetails(
|
|
|
1577
1610
|
retryable: providerErrorOption(error, "retryable") ?? declaredRetryable ?? false,
|
|
1578
1611
|
};
|
|
1579
1612
|
}
|
|
1613
|
+
// Native proxy resolution throws ProxyResolutionError directly, while HTTP
|
|
1614
|
+
// normalizes it beneath a transport_network_error cause and stealth retains
|
|
1615
|
+
// the original code. Preserve proxy attribution across all three forms.
|
|
1616
|
+
if (errorChainHasCode(error, "PROXY_REQUIRED")) {
|
|
1617
|
+
return {
|
|
1618
|
+
category: "proxy_pool",
|
|
1619
|
+
taxonomyVersion: PROVIDER_OBSERVABILITY_TAXONOMY_VERSION,
|
|
1620
|
+
retryable: true,
|
|
1621
|
+
};
|
|
1622
|
+
}
|
|
1580
1623
|
if (!isTransportError(error)) {
|
|
1581
1624
|
return undefined;
|
|
1582
1625
|
}
|
|
1626
|
+
const errorCode = providerErrorCode(error);
|
|
1583
1627
|
const isProxyPoolCode =
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1628
|
+
errorCode === PROXY_POOL_EXHAUSTED_CODE ||
|
|
1629
|
+
errorCode === PROXY_EDGE_AUTH_REJECTED_CODE ||
|
|
1630
|
+
errorCode === "PROXY_ALLOCATION_FAILED" ||
|
|
1631
|
+
errorCode === "native_proxy_expired" ||
|
|
1632
|
+
errorCode === "native_proxy_invalid";
|
|
1587
1633
|
const category =
|
|
1588
1634
|
providerErrorOption(error, "category") ??
|
|
1589
1635
|
(isProxyPoolCode
|
|
1590
1636
|
? "proxy_pool"
|
|
1591
|
-
:
|
|
1637
|
+
: errorCode === PROXY_AUTH_IP_DENIED_CODE
|
|
1592
1638
|
? "anti_bot_blocked"
|
|
1593
|
-
:
|
|
1639
|
+
: errorCode === "transport_timeout" ||
|
|
1640
|
+
errorCode === "native_connection_timeout" ||
|
|
1641
|
+
errorCode === "native_connection_idle_timeout"
|
|
1594
1642
|
? "timeout"
|
|
1595
|
-
:
|
|
1643
|
+
: errorCode === "transport_network_error" ||
|
|
1644
|
+
errorCode === "native_connection_failed" ||
|
|
1645
|
+
errorCode === "native_connection_closed"
|
|
1596
1646
|
? "network"
|
|
1597
|
-
:
|
|
1598
|
-
?
|
|
1599
|
-
:
|
|
1647
|
+
: errorCode === "native_connection_aborted"
|
|
1648
|
+
? "client_cancelled"
|
|
1649
|
+
: error.upstreamStatus
|
|
1650
|
+
? categoryForStatus(error.upstreamStatus)
|
|
1651
|
+
: "upstream_http");
|
|
1600
1652
|
return {
|
|
1601
1653
|
category,
|
|
1602
1654
|
taxonomyVersion: PROVIDER_OBSERVABILITY_TAXONOMY_VERSION,
|
|
@@ -2223,9 +2275,11 @@ function createRequestScope(input: {
|
|
|
2223
2275
|
bindDiagnosticSensitiveRegistry(trace, sensitiveRegistry);
|
|
2224
2276
|
const proxyCollector = new ProxyTelemetryCollector();
|
|
2225
2277
|
const resolverCollector = new ResolverTelemetryCollector({ redact });
|
|
2278
|
+
const nativeCollector = new NativeTelemetryCollector({ redact });
|
|
2226
2279
|
const telemetry = new RequestTelemetry(trace);
|
|
2227
2280
|
telemetry.register(proxyCollector);
|
|
2228
2281
|
telemetry.register(resolverCollector);
|
|
2282
|
+
telemetry.register(nativeCollector);
|
|
2229
2283
|
let rootRunner: <T>(fn: () => Promise<T>) => Promise<T> = (fn) => fn();
|
|
2230
2284
|
let resolveRoot!: (outcome: RequestTerminalOutcome) => void;
|
|
2231
2285
|
const rootTerminal = new Promise<RequestTerminalOutcome>((resolve) => {
|
|
@@ -2307,6 +2361,7 @@ function createRequestScope(input: {
|
|
|
2307
2361
|
trace,
|
|
2308
2362
|
telemetry,
|
|
2309
2363
|
resolverTelemetry: resolverCollector,
|
|
2364
|
+
nativeTelemetry: nativeCollector,
|
|
2310
2365
|
seedCredentials(rawBody, kind): void {
|
|
2311
2366
|
const harvested = rawRequestCredentials(rawBody, kind);
|
|
2312
2367
|
sensitiveRegistry.add(harvested.values);
|
package/src/testing/run.ts
CHANGED
|
@@ -2,7 +2,13 @@ import { describe, expect, it } from "bun:test";
|
|
|
2
2
|
|
|
3
3
|
import { ProviderError } from "../errors.js";
|
|
4
4
|
import { createProviderCache } from "../runtime/cache.js";
|
|
5
|
-
import {
|
|
5
|
+
import { createTestHandleContext } from "../runtime/handle.js";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Stable connection scope for the local test harness so bound handles (drafts,
|
|
9
|
+
* default cursors) can be exercised exactly as in a connected production call.
|
|
10
|
+
*/
|
|
11
|
+
const STANDARD_TEST_CONNECTION_ID = "standard-test-connection";
|
|
6
12
|
import { createMemoryProviderRuntimeState } from "../runtime/state.js";
|
|
7
13
|
import { createUnsupportedOcrClient } from "../runtime/ocr.js";
|
|
8
14
|
import { createUnsupportedSttClient } from "../runtime/stt.js";
|
|
@@ -319,7 +325,7 @@ function createUpstreamContext(
|
|
|
319
325
|
getAccessToken: () => undefined,
|
|
320
326
|
getScopes: () => [],
|
|
321
327
|
};
|
|
322
|
-
const request = { headers: {} };
|
|
328
|
+
const request = { headers: {}, connectionId: STANDARD_TEST_CONNECTION_ID };
|
|
323
329
|
const state = createMemoryProviderRuntimeState();
|
|
324
330
|
const nativeEgress = provider.native
|
|
325
331
|
? createNativeEgressAuthorization({ egress: provider.native.network })
|
|
@@ -578,10 +584,9 @@ function createUpstreamContext(
|
|
|
578
584
|
resolver: {
|
|
579
585
|
solve: async () => unsupported("ctx.resolver.solve"),
|
|
580
586
|
},
|
|
581
|
-
|
|
587
|
+
handle: createTestHandleContext({
|
|
582
588
|
providerId: `standard-test-${operationName}`,
|
|
583
589
|
request,
|
|
584
|
-
credential,
|
|
585
590
|
state,
|
|
586
591
|
}),
|
|
587
592
|
} as ProviderContext;
|
|
@@ -622,7 +627,7 @@ export function createSnapshotContext(rawFixture: unknown): ProviderContext {
|
|
|
622
627
|
getAccessToken: () => undefined,
|
|
623
628
|
getScopes: () => [],
|
|
624
629
|
};
|
|
625
|
-
const request = { headers: {} };
|
|
630
|
+
const request = { headers: {}, connectionId: STANDARD_TEST_CONNECTION_ID };
|
|
626
631
|
const state = createMemoryProviderRuntimeState();
|
|
627
632
|
const streamCaptureGroup = findStreamCaptureGroup(rawFixture);
|
|
628
633
|
let nextCaptureItem = 0;
|
|
@@ -709,10 +714,9 @@ export function createSnapshotContext(rawFixture: unknown): ProviderContext {
|
|
|
709
714
|
resolver: {
|
|
710
715
|
solve: async () => unsupported("ctx.resolver.solve"),
|
|
711
716
|
},
|
|
712
|
-
|
|
717
|
+
handle: createTestHandleContext({
|
|
713
718
|
providerId: "standard-test",
|
|
714
719
|
request,
|
|
715
|
-
credential,
|
|
716
720
|
state,
|
|
717
721
|
}),
|
|
718
722
|
} satisfies Omit<ProviderContext, "native"> as unknown as ProviderContext;
|
package/src/types.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type ms from "ms";
|
|
2
|
+
import type { HandleContext, HandleKind } from "./handle.js";
|
|
2
3
|
import type { HealthScenario } from "./health-scenario.js";
|
|
3
4
|
import type { SerializedCookieJar } from "tough-cookie";
|
|
4
5
|
|
|
@@ -1997,140 +1998,6 @@ export interface ProviderRequestContext {
|
|
|
1997
1998
|
headers: Record<string, string>;
|
|
1998
1999
|
}
|
|
1999
2000
|
|
|
2000
|
-
export interface ProviderChoiceBindingOptions {
|
|
2001
|
-
connection?: boolean;
|
|
2002
|
-
credentialKeys?: readonly string[];
|
|
2003
|
-
}
|
|
2004
|
-
|
|
2005
|
-
export type ProviderChoiceConsumeMode = "never" | "on-parse" | "explicit";
|
|
2006
|
-
|
|
2007
|
-
export type ProviderChoiceConsumeResult =
|
|
2008
|
-
| { readonly status: "consumed" }
|
|
2009
|
-
| { readonly status: "already-consumed" }
|
|
2010
|
-
| { readonly status: "unsupported" };
|
|
2011
|
-
|
|
2012
|
-
export type ProviderChoiceExplicitParseResult =
|
|
2013
|
-
| {
|
|
2014
|
-
readonly status: "active";
|
|
2015
|
-
readonly payload: Record<string, unknown>;
|
|
2016
|
-
/** Stable, opaque key for provider-owned idempotency records. */
|
|
2017
|
-
readonly replayKey: string;
|
|
2018
|
-
/** Atomically claims a word token. Inline tokens report unsupported. */
|
|
2019
|
-
consume(): Promise<ProviderChoiceConsumeResult>;
|
|
2020
|
-
}
|
|
2021
|
-
| {
|
|
2022
|
-
readonly status: "consumed";
|
|
2023
|
-
/** Use this key to read the provider-owned result before returning an error. */
|
|
2024
|
-
readonly replayKey: string;
|
|
2025
|
-
};
|
|
2026
|
-
|
|
2027
|
-
export type ProviderChoiceStorageOptions =
|
|
2028
|
-
| {
|
|
2029
|
-
readonly mode: "inline";
|
|
2030
|
-
}
|
|
2031
|
-
| {
|
|
2032
|
-
readonly mode: "server";
|
|
2033
|
-
readonly namespace: string;
|
|
2034
|
-
readonly state?: ProviderRuntimeState;
|
|
2035
|
-
readonly ttl?: ProviderStateDurationString;
|
|
2036
|
-
readonly maxEntries: number;
|
|
2037
|
-
readonly maxValueBytes: number;
|
|
2038
|
-
readonly unavailable?: "reject";
|
|
2039
|
-
}
|
|
2040
|
-
| {
|
|
2041
|
-
readonly mode: "auto";
|
|
2042
|
-
readonly namespace: string;
|
|
2043
|
-
readonly state?: ProviderRuntimeState;
|
|
2044
|
-
readonly ttl?: ProviderStateDurationString;
|
|
2045
|
-
readonly maxInlineBytes: number;
|
|
2046
|
-
readonly maxEntries: number;
|
|
2047
|
-
readonly maxValueBytes: number;
|
|
2048
|
-
readonly unavailable?: "reject";
|
|
2049
|
-
};
|
|
2050
|
-
|
|
2051
|
-
export interface ProviderChoiceIssueOptions<
|
|
2052
|
-
TPayload extends Record<string, unknown>,
|
|
2053
|
-
> {
|
|
2054
|
-
prefix: string;
|
|
2055
|
-
purpose: string;
|
|
2056
|
-
payload: TPayload;
|
|
2057
|
-
ttlMs: number;
|
|
2058
|
-
nowMs?: number;
|
|
2059
|
-
bind?: ProviderChoiceBindingOptions;
|
|
2060
|
-
/** Server storage only: standard emits four words; high emits five. */
|
|
2061
|
-
strength?: "standard" | "high";
|
|
2062
|
-
storage?: ProviderChoiceStorageOptions;
|
|
2063
|
-
}
|
|
2064
|
-
|
|
2065
|
-
export interface ProviderChoiceParseOptions {
|
|
2066
|
-
token: string;
|
|
2067
|
-
prefix: string;
|
|
2068
|
-
purpose: string;
|
|
2069
|
-
ttlMs?: number;
|
|
2070
|
-
nowMs?: number;
|
|
2071
|
-
futureToleranceMs?: number;
|
|
2072
|
-
bind?: ProviderChoiceBindingOptions;
|
|
2073
|
-
storage?: ProviderChoiceStorageOptions;
|
|
2074
|
-
/** Defaults to never, preserving reusable choice-token parse semantics. */
|
|
2075
|
-
consume?: ProviderChoiceConsumeMode;
|
|
2076
|
-
}
|
|
2077
|
-
|
|
2078
|
-
export interface ProviderChoiceContext {
|
|
2079
|
-
issue<TPayload extends Record<string, unknown>>(
|
|
2080
|
-
options: ProviderChoiceIssueOptions<TPayload> & {
|
|
2081
|
-
readonly storage?: { readonly mode: "inline" };
|
|
2082
|
-
},
|
|
2083
|
-
): string;
|
|
2084
|
-
issue<TPayload extends Record<string, unknown>>(
|
|
2085
|
-
options: ProviderChoiceIssueOptions<TPayload> & {
|
|
2086
|
-
readonly storage: Extract<
|
|
2087
|
-
ProviderChoiceStorageOptions,
|
|
2088
|
-
{ readonly mode: "server" }
|
|
2089
|
-
>;
|
|
2090
|
-
},
|
|
2091
|
-
): Promise<string>;
|
|
2092
|
-
issue<TPayload extends Record<string, unknown>>(
|
|
2093
|
-
options: ProviderChoiceIssueOptions<TPayload> & {
|
|
2094
|
-
readonly storage: Extract<
|
|
2095
|
-
ProviderChoiceStorageOptions,
|
|
2096
|
-
{ readonly mode: "auto" }
|
|
2097
|
-
>;
|
|
2098
|
-
},
|
|
2099
|
-
): string | Promise<string>;
|
|
2100
|
-
issue<TPayload extends Record<string, unknown>>(
|
|
2101
|
-
options: ProviderChoiceIssueOptions<TPayload>,
|
|
2102
|
-
): string | Promise<string>;
|
|
2103
|
-
parse(
|
|
2104
|
-
options: ProviderChoiceParseOptions & { readonly consume: "explicit" },
|
|
2105
|
-
): Promise<ProviderChoiceExplicitParseResult>;
|
|
2106
|
-
parse(
|
|
2107
|
-
options: ProviderChoiceParseOptions & {
|
|
2108
|
-
readonly storage?: { readonly mode: "inline" };
|
|
2109
|
-
},
|
|
2110
|
-
): Record<string, unknown>;
|
|
2111
|
-
parse(
|
|
2112
|
-
options: ProviderChoiceParseOptions & {
|
|
2113
|
-
readonly storage: Extract<
|
|
2114
|
-
ProviderChoiceStorageOptions,
|
|
2115
|
-
{ readonly mode: "server" }
|
|
2116
|
-
>;
|
|
2117
|
-
},
|
|
2118
|
-
): Promise<Record<string, unknown>>;
|
|
2119
|
-
parse(
|
|
2120
|
-
options: ProviderChoiceParseOptions & {
|
|
2121
|
-
readonly storage: Extract<
|
|
2122
|
-
ProviderChoiceStorageOptions,
|
|
2123
|
-
{ readonly mode: "auto" }
|
|
2124
|
-
>;
|
|
2125
|
-
},
|
|
2126
|
-
): Record<string, unknown> | Promise<Record<string, unknown>>;
|
|
2127
|
-
parse(
|
|
2128
|
-
options: ProviderChoiceParseOptions,
|
|
2129
|
-
):
|
|
2130
|
-
| Record<string, unknown>
|
|
2131
|
-
| Promise<Record<string, unknown> | ProviderChoiceExplicitParseResult>;
|
|
2132
|
-
}
|
|
2133
|
-
|
|
2134
2001
|
export interface ContextScratchpad {
|
|
2135
2002
|
get(key: string): unknown;
|
|
2136
2003
|
set(key: string, value: unknown): void;
|
|
@@ -2402,8 +2269,8 @@ export type ProviderContext<TConfig = Record<string, unknown>> = {
|
|
|
2402
2269
|
& ("ocr" extends keyof TConfig ? { ocr: OcrContext } : Record<never, never>)
|
|
2403
2270
|
& ("stt" extends keyof TConfig ? { stt: SttContext } : Record<never, never>)
|
|
2404
2271
|
& ("resolver" extends keyof TConfig ? { resolver: ResolverContext } : Record<never, never>)
|
|
2405
|
-
& ("
|
|
2406
|
-
? {
|
|
2272
|
+
& ("handle" extends keyof TConfig
|
|
2273
|
+
? { handle: HandleContext }
|
|
2407
2274
|
: Record<never, never>);
|
|
2408
2275
|
|
|
2409
2276
|
/** Backwards-compatible name for a declaration-derived provider context. */
|
|
@@ -2588,7 +2455,7 @@ export interface ProviderDefinition<TContext = ProviderContext> {
|
|
|
2588
2455
|
engine: BrowserEngine;
|
|
2589
2456
|
};
|
|
2590
2457
|
auth?: AuthConfig;
|
|
2591
|
-
|
|
2458
|
+
handle?: readonly HandleKind[];
|
|
2592
2459
|
reviewed?: ProviderReviewed;
|
|
2593
2460
|
access?: ProviderAccessConfig;
|
|
2594
2461
|
secrets?: ProviderSecretDeclaration[];
|
package/dist/choice-token.d.ts
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
export type ProviderChoiceTokenPayload = Record<string, unknown>;
|
|
2
|
-
export type ProviderChoiceTokenErrorReason = "invalid_shape" | "invalid_signature" | "invalid_payload" | "invalid_binding" | "stale";
|
|
3
|
-
export declare class ProviderChoiceTokenError extends Error {
|
|
4
|
-
readonly reason: ProviderChoiceTokenErrorReason;
|
|
5
|
-
constructor(reason: ProviderChoiceTokenErrorReason, message: string);
|
|
6
|
-
}
|
|
7
|
-
export interface CreateProviderChoiceTokenOptions<TPayload extends ProviderChoiceTokenPayload> {
|
|
8
|
-
prefix: string;
|
|
9
|
-
payload: TPayload;
|
|
10
|
-
secret: string;
|
|
11
|
-
}
|
|
12
|
-
export interface ParseProviderChoiceTokenOptions {
|
|
13
|
-
token: string;
|
|
14
|
-
prefix: string;
|
|
15
|
-
secret: string;
|
|
16
|
-
}
|
|
17
|
-
export interface FreshProviderChoiceIssuedAtOptions {
|
|
18
|
-
ttlMs: number;
|
|
19
|
-
nowMs?: number;
|
|
20
|
-
futureToleranceMs?: number;
|
|
21
|
-
}
|
|
22
|
-
export declare function createProviderChoiceToken<TPayload extends ProviderChoiceTokenPayload>(options: CreateProviderChoiceTokenOptions<TPayload>): string;
|
|
23
|
-
export declare function parseProviderChoiceToken(options: ParseProviderChoiceTokenOptions): ProviderChoiceTokenPayload;
|
|
24
|
-
export declare function assertFreshProviderChoiceIssuedAt(issuedAtMs: unknown, options: FreshProviderChoiceIssuedAtOptions): number;
|
package/dist/choice-token.js
DELETED
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
import { createCipheriv, createDecipheriv, createHash, createHmac, randomBytes, timingSafeEqual, } from "node:crypto";
|
|
2
|
-
export class ProviderChoiceTokenError extends Error {
|
|
3
|
-
reason;
|
|
4
|
-
constructor(reason, message) {
|
|
5
|
-
super(message);
|
|
6
|
-
this.name = "ProviderChoiceTokenError";
|
|
7
|
-
this.reason = reason;
|
|
8
|
-
}
|
|
9
|
-
}
|
|
10
|
-
export function createProviderChoiceToken(options) {
|
|
11
|
-
const iv = randomBytes(12);
|
|
12
|
-
const cipher = createCipheriv("aes-256-gcm", choiceEncryptionKey(options.secret), iv);
|
|
13
|
-
const encryptedPayload = Buffer.concat([
|
|
14
|
-
cipher.update(JSON.stringify(options.payload), "utf8"),
|
|
15
|
-
cipher.final(),
|
|
16
|
-
]).toString("base64url");
|
|
17
|
-
const authTag = cipher.getAuthTag().toString("base64url");
|
|
18
|
-
const encodedIv = iv.toString("base64url");
|
|
19
|
-
const signature = signProviderChoiceTokenBody(`${options.prefix}.${encodedIv}.${encryptedPayload}.${authTag}`, options.secret);
|
|
20
|
-
return `${options.prefix}.${encodedIv}.${encryptedPayload}.${authTag}.${signature}`;
|
|
21
|
-
}
|
|
22
|
-
export function parseProviderChoiceToken(options) {
|
|
23
|
-
const [actualPrefix, encodedIv, encryptedPayload, authTag, signature, ...extra] = options.token.split(".");
|
|
24
|
-
if (actualPrefix !== options.prefix ||
|
|
25
|
-
!encodedIv ||
|
|
26
|
-
!encryptedPayload ||
|
|
27
|
-
!authTag ||
|
|
28
|
-
!signature ||
|
|
29
|
-
extra.length > 0) {
|
|
30
|
-
throw new ProviderChoiceTokenError("invalid_shape", "Provider choice token shape is invalid.");
|
|
31
|
-
}
|
|
32
|
-
const signedBody = `${options.prefix}.${encodedIv}.${encryptedPayload}.${authTag}`;
|
|
33
|
-
const expectedSignature = signProviderChoiceTokenBody(signedBody, options.secret);
|
|
34
|
-
const actualBuffer = Buffer.from(signature);
|
|
35
|
-
const expectedBuffer = Buffer.from(expectedSignature);
|
|
36
|
-
if (actualBuffer.length !== expectedBuffer.length ||
|
|
37
|
-
!timingSafeEqual(actualBuffer, expectedBuffer)) {
|
|
38
|
-
throw new ProviderChoiceTokenError("invalid_signature", "Provider choice token signature is invalid.");
|
|
39
|
-
}
|
|
40
|
-
try {
|
|
41
|
-
const decipher = createDecipheriv("aes-256-gcm", choiceEncryptionKey(options.secret), Buffer.from(encodedIv, "base64url"));
|
|
42
|
-
decipher.setAuthTag(Buffer.from(authTag, "base64url"));
|
|
43
|
-
const decrypted = Buffer.concat([
|
|
44
|
-
decipher.update(Buffer.from(encryptedPayload, "base64url")),
|
|
45
|
-
decipher.final(),
|
|
46
|
-
]).toString("utf8");
|
|
47
|
-
const parsed = JSON.parse(decrypted);
|
|
48
|
-
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
49
|
-
throw new Error("payload is not an object");
|
|
50
|
-
}
|
|
51
|
-
return Object.fromEntries(Object.entries(parsed));
|
|
52
|
-
}
|
|
53
|
-
catch {
|
|
54
|
-
throw new ProviderChoiceTokenError("invalid_payload", "Provider choice token payload is invalid.");
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
export function assertFreshProviderChoiceIssuedAt(issuedAtMs, options) {
|
|
58
|
-
const parsed = typeof issuedAtMs === "number" ? issuedAtMs : Number(issuedAtMs);
|
|
59
|
-
const nowMs = options.nowMs ?? Date.now();
|
|
60
|
-
const futureToleranceMs = options.futureToleranceMs ?? 30_000;
|
|
61
|
-
if (!Number.isFinite(parsed) ||
|
|
62
|
-
parsed <= 0 ||
|
|
63
|
-
nowMs - parsed > options.ttlMs ||
|
|
64
|
-
parsed - nowMs > futureToleranceMs) {
|
|
65
|
-
throw new ProviderChoiceTokenError("stale", "Provider choice token is stale.");
|
|
66
|
-
}
|
|
67
|
-
return parsed;
|
|
68
|
-
}
|
|
69
|
-
function choiceEncryptionKey(secret) {
|
|
70
|
-
return createHash("sha256").update(secret).digest();
|
|
71
|
-
}
|
|
72
|
-
function signProviderChoiceTokenBody(body, secret) {
|
|
73
|
-
return createHmac("sha256", secret).update(body).digest("base64url");
|
|
74
|
-
}
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
export declare const CHOICE_WORDLIST_SIZE = 1296;
|
|
2
|
-
export declare const CHOICE_WORD_ENTROPY_BITS: number;
|
|
3
|
-
export declare const STANDARD_CHOICE_WORD_COUNT = 4;
|
|
4
|
-
export declare const HIGH_CHOICE_WORD_COUNT = 5;
|
|
5
|
-
export declare const STANDARD_CHOICE_ENTROPY_BITS: number;
|
|
6
|
-
export declare const HIGH_CHOICE_ENTROPY_BITS: number;
|
|
7
|
-
export declare const EFF_SHORT_WORDLIST_2: readonly string[];
|
|
8
|
-
export declare function choiceWordAt(index: number): string;
|
|
9
|
-
export declare function isChoiceWord(value: string): boolean;
|
package/dist/runtime/choice.d.ts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import type { CredentialContext, EnvContext, ProviderChoiceConsumeMode, ProviderChoiceContext, ProviderRequestContext, ProviderRuntimeState } from "../types.js";
|
|
2
|
-
export declare const PROVIDER_RUNTIME_CHOICE_TOKEN_MASTER_SECRET_ENV = "APIFUSE__PROVIDER_RUNTIME__CHOICE_TOKEN_MASTER_SECRET";
|
|
3
|
-
export type ProviderChoiceTelemetryEvent = {
|
|
4
|
-
readonly providerId: string;
|
|
5
|
-
readonly purpose: string;
|
|
6
|
-
readonly operation: "parse" | "consume";
|
|
7
|
-
readonly format: "word" | "legacy";
|
|
8
|
-
readonly outcome: "success" | "not-found" | "invalid" | "unsupported" | "error";
|
|
9
|
-
readonly consumeMode: ProviderChoiceConsumeMode;
|
|
10
|
-
readonly consumed: boolean;
|
|
11
|
-
readonly replay: boolean;
|
|
12
|
-
};
|
|
13
|
-
export type CreateProviderChoiceContextOptions = {
|
|
14
|
-
readonly providerId: string;
|
|
15
|
-
readonly env?: EnvContext;
|
|
16
|
-
readonly request?: ProviderRequestContext;
|
|
17
|
-
readonly credential?: CredentialContext;
|
|
18
|
-
readonly state?: ProviderRuntimeState;
|
|
19
|
-
readonly masterSecret?: string;
|
|
20
|
-
readonly kid?: string;
|
|
21
|
-
/** Receives allowlisted metadata only; token and payload values are never included. */
|
|
22
|
-
readonly onTelemetry?: (event: ProviderChoiceTelemetryEvent) => void;
|
|
23
|
-
};
|
|
24
|
-
export declare function createProviderChoiceContext(options: CreateProviderChoiceContextOptions): ProviderChoiceContext;
|
|
25
|
-
export declare function createTestProviderChoiceContext(options: Omit<CreateProviderChoiceContextOptions, "masterSecret"> & {
|
|
26
|
-
readonly masterSecret?: string;
|
|
27
|
-
}): ProviderChoiceContext;
|