@apifuse/provider-sdk 2.2.0-beta.2 → 2.2.0-beta.21
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 +487 -0
- package/CHANGELOG.md +90 -0
- package/README.md +49 -3
- 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 +36 -81
- package/bin/apifuse-pack-types.ts +305 -0
- package/bin/apifuse-perf.ts +45 -127
- package/bin/apifuse-record.ts +659 -111
- package/bin/apifuse-submit-check.ts +546 -48
- 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 +9 -1
- package/dist/ceremonies/index.js +65 -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 +176 -8
- package/dist/config/loader.js +424 -95
- 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 +9 -1
- package/dist/define.js +304 -119
- package/dist/dev.d.ts +1 -1
- package/dist/dev.js +1 -1
- package/dist/error-resolution.d.ts +2 -0
- package/dist/error-resolution.js +90 -0
- package/dist/errors.d.ts +22 -1
- package/dist/errors.js +88 -0
- package/dist/fixture-sanitization.d.ts +26 -0
- package/dist/fixture-sanitization.js +216 -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 +46 -42
- package/dist/index.js +41 -37
- package/dist/lint.d.ts +1 -1
- package/dist/lint.js +8 -15
- package/dist/native-address.d.ts +43 -0
- package/dist/native-address.js +281 -0
- package/dist/native-egress-policy.d.ts +31 -0
- package/dist/native-egress-policy.js +288 -0
- package/dist/observability.d.ts +5 -2
- package/dist/observability.js +48 -1
- package/dist/provider.d.ts +12 -11
- package/dist/provider.js +10 -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 +2 -1
- package/dist/runtime/auth-flow.js +4 -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 +31 -4
- package/dist/runtime/http.d.ts +2 -2
- package/dist/runtime/http.js +387 -47
- 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 +345 -22
- package/dist/runtime/keyring.js +1 -1
- package/dist/runtime/namespace.js +1 -1
- package/dist/runtime/native-network.d.ts +127 -0
- package/dist/runtime/native-network.js +1298 -0
- 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 +56 -0
- package/dist/runtime/proxy-nodemaven.js +146 -0
- package/dist/runtime/proxy-retry-policy.d.ts +2 -2
- package/dist/runtime/proxy-retry-policy.js +2 -2
- package/dist/runtime/proxy-telemetry.d.ts +2 -1
- package/dist/runtime/proxy-telemetry.js +55 -52
- package/dist/runtime/redirects.d.ts +29 -0
- package/dist/runtime/redirects.js +36 -0
- package/dist/runtime/redis.d.ts +1 -1
- package/dist/runtime/redis.js +2 -4
- package/dist/runtime/request-options.d.ts +68 -1
- package/dist/runtime/request-options.js +548 -0
- 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 +238 -26
- package/dist/runtime/stealth.d.ts +5 -3
- package/dist/runtime/stealth.js +423 -143
- 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-input-tokens.d.ts +2 -1
- package/dist/server/self-test-input-tokens.js +18 -14
- 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 +116 -4
- package/dist/server/serve.js +799 -128
- package/dist/server/types.d.ts +34 -9
- package/dist/server/types.js +8 -1
- package/dist/stateful/errors.d.ts +14 -0
- package/dist/stateful/errors.js +14 -0
- package/dist/stateful/http-provider-event-emitter.d.ts +40 -0
- package/dist/stateful/http-provider-event-emitter.js +237 -0
- package/dist/stateful/http-session-owner-registry.d.ts +44 -0
- package/dist/stateful/http-session-owner-registry.js +210 -0
- package/dist/stateful/index.d.ts +18 -0
- package/dist/stateful/index.js +18 -0
- package/dist/stateful/provider-event-delivery-failures.d.ts +32 -0
- package/dist/stateful/provider-event-delivery-failures.js +43 -0
- package/dist/stateful/provider-event-pipeline-metrics.d.ts +46 -0
- package/dist/stateful/provider-event-pipeline-metrics.js +48 -0
- package/dist/stateful/provider-event-pipeline.d.ts +50 -0
- package/dist/stateful/provider-event-pipeline.js +1 -0
- package/dist/stateful/provider-events.d.ts +101 -0
- package/dist/stateful/provider-events.js +289 -0
- package/dist/stateful/session-key.d.ts +15 -0
- package/dist/stateful/session-key.js +86 -0
- package/dist/stateful/stateful-provider-adapter-context.d.ts +5 -0
- package/dist/stateful/stateful-provider-adapter-context.js +42 -0
- package/dist/stateful/stateful-provider-adapter-metrics.d.ts +15 -0
- package/dist/stateful/stateful-provider-adapter-metrics.js +21 -0
- package/dist/stateful/stateful-provider-adapter.d.ts +98 -0
- package/dist/stateful/stateful-provider-adapter.js +287 -0
- package/dist/stateful/stateful-provider-observability.d.ts +62 -0
- package/dist/stateful/stateful-provider-observability.js +161 -0
- package/dist/stateful/stateful-provider-owner-forwarder.d.ts +41 -0
- package/dist/stateful/stateful-provider-owner-forwarder.js +215 -0
- package/dist/stateful/stateful-provider-runtime-context.d.ts +32 -0
- package/dist/stateful/stateful-provider-runtime-context.js +60 -0
- package/dist/stateful/stateful-provider-runtime-executor.d.ts +34 -0
- package/dist/stateful/stateful-provider-runtime-executor.js +52 -0
- package/dist/stateful/stateful-provider-session-routing.d.ts +71 -0
- package/dist/stateful/stateful-provider-session-routing.js +353 -0
- package/dist/stateful/stateful-provider-session-runtime.d.ts +98 -0
- package/dist/stateful/stateful-provider-session-runtime.js +245 -0
- package/dist/stateful-signing.d.ts +18 -0
- package/dist/stateful-signing.js +27 -0
- package/dist/stealth/profiles.d.ts +1 -1
- package/dist/stealth/profiles.js +5 -14
- package/dist/stream-evidence.d.ts +74 -0
- package/dist/stream-evidence.js +785 -0
- 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 +32 -2
- package/dist/testing/run.js +478 -28
- package/dist/types.d.ts +342 -14
- package/dist/types.js +1 -0
- package/dist/user-input.d.ts +30 -0
- package/dist/user-input.js +66 -0
- package/package.json +16 -5
- package/src/auth-turn/index.ts +1 -1
- package/src/auth.ts +28 -86
- package/src/ceremonies/index.ts +103 -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 +652 -204
- package/src/contract-serialization.ts +5 -11
- package/src/contract-types.ts +2 -2
- package/src/contract.ts +12 -28
- package/src/define.ts +499 -498
- package/src/dev.ts +4 -9
- package/src/error-resolution.ts +91 -0
- package/src/errors.ts +110 -5
- package/src/fixture-sanitization.ts +247 -0
- 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 +112 -42
- package/src/lint.ts +88 -152
- package/src/native-address.ts +340 -0
- package/src/native-egress-policy.ts +358 -0
- package/src/observability.ts +51 -1
- package/src/provider.ts +66 -11
- package/src/public-schema-field-lint.ts +7 -33
- package/src/recipes/gov-api.ts +2 -5
- package/src/runtime/auth-flow.ts +7 -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 +43 -20
- package/src/runtime/http.ts +494 -57
- package/src/runtime/insights.ts +15 -53
- package/src/runtime/instrumentation.ts +495 -66
- package/src/runtime/keyring.ts +7 -19
- package/src/runtime/namespace.ts +2 -7
- package/src/runtime/native-network.ts +1686 -0
- 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 +221 -0
- package/src/runtime/proxy-retry-policy.ts +3 -3
- package/src/runtime/proxy-telemetry.ts +79 -77
- package/src/runtime/redirects.ts +66 -0
- package/src/runtime/redis.ts +4 -12
- package/src/runtime/request-options.ts +679 -9
- package/src/runtime/secrets.ts +64 -0
- package/src/runtime/state.ts +353 -133
- package/src/runtime/stealth.ts +505 -154
- 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 +29 -7
- package/src/server/self-test-input-tokens.ts +29 -14
- package/src/server/self-test-redaction.ts +2 -2
- package/src/server/self-test.ts +857 -132
- package/src/server/serve.ts +1151 -328
- package/src/server/types.ts +12 -13
- package/src/stateful/README.md +146 -0
- package/src/stateful/errors.ts +23 -0
- package/src/stateful/http-provider-event-emitter.ts +314 -0
- package/src/stateful/http-session-owner-registry.ts +306 -0
- package/src/stateful/index.ts +18 -0
- package/src/stateful/provider-event-delivery-failures.ts +80 -0
- package/src/stateful/provider-event-pipeline-metrics.ts +95 -0
- package/src/stateful/provider-event-pipeline.ts +61 -0
- package/src/stateful/provider-events.ts +462 -0
- package/src/stateful/session-key.ts +111 -0
- package/src/stateful/stateful-provider-adapter-context.ts +59 -0
- package/src/stateful/stateful-provider-adapter-metrics.ts +48 -0
- package/src/stateful/stateful-provider-adapter.ts +562 -0
- package/src/stateful/stateful-provider-observability.ts +261 -0
- package/src/stateful/stateful-provider-owner-forwarder.ts +287 -0
- package/src/stateful/stateful-provider-runtime-context.ts +92 -0
- package/src/stateful/stateful-provider-runtime-executor.ts +96 -0
- package/src/stateful/stateful-provider-session-routing.ts +555 -0
- package/src/stateful/stateful-provider-session-runtime.ts +403 -0
- package/src/stateful-signing.ts +46 -0
- package/src/stealth/profiles.ts +10 -26
- package/src/stream-evidence.ts +988 -0
- package/src/stream.ts +8 -19
- package/src/testing/index.ts +10 -2
- package/src/testing/run.ts +653 -74
- package/src/types.ts +408 -28
- 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/runtime/cache.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
|
|
3
|
-
import { providerCacheRedisUrlFromEnv } from "../config/loader";
|
|
3
|
+
import { providerCacheRedisUrlFromEnv } from "../config/loader.js";
|
|
4
4
|
import type {
|
|
5
5
|
ProviderCache,
|
|
6
6
|
ProviderCacheGetOrSetOptions,
|
|
@@ -8,13 +8,13 @@ import type {
|
|
|
8
8
|
ProviderCacheLookupMeta,
|
|
9
9
|
ProviderCacheResponseMeta,
|
|
10
10
|
ProviderCacheResult,
|
|
11
|
-
} from "../types";
|
|
11
|
+
} from "../types.js";
|
|
12
12
|
import {
|
|
13
13
|
createProviderRedisClient,
|
|
14
14
|
ensureRedisReady,
|
|
15
15
|
type ProviderRedisClient,
|
|
16
16
|
withRedisTimeout,
|
|
17
|
-
} from "./redis";
|
|
17
|
+
} from "./redis.js";
|
|
18
18
|
|
|
19
19
|
type CacheSource = ProviderCacheLookupMeta["source"];
|
|
20
20
|
|
|
@@ -123,10 +123,7 @@ function normalizeKeyPart(value: unknown, extra: Set<string>): unknown {
|
|
|
123
123
|
}
|
|
124
124
|
|
|
125
125
|
function stableHash(value: unknown): string {
|
|
126
|
-
return createHash("sha256")
|
|
127
|
-
.update(JSON.stringify(value))
|
|
128
|
-
.digest("hex")
|
|
129
|
-
.slice(0, 32);
|
|
126
|
+
return createHash("sha256").update(JSON.stringify(value)).digest("hex").slice(0, 32);
|
|
130
127
|
}
|
|
131
128
|
|
|
132
129
|
function jitteredTtlMs(ttlMs: number, jitterPct: number | undefined): number {
|
|
@@ -161,10 +158,7 @@ function safeParseEnvelope(raw: string | null): CacheEnvelope | null {
|
|
|
161
158
|
}
|
|
162
159
|
}
|
|
163
160
|
|
|
164
|
-
function resultWithValue<T>(
|
|
165
|
-
value: unknown,
|
|
166
|
-
meta: ProviderCacheLookupMeta,
|
|
167
|
-
): ProviderCacheResult<T> {
|
|
161
|
+
function resultWithValue<T>(value: unknown, meta: ProviderCacheLookupMeta): ProviderCacheResult<T> {
|
|
168
162
|
return {
|
|
169
163
|
value: <T>value,
|
|
170
164
|
meta,
|
|
@@ -187,18 +181,14 @@ function resultFromEnvelope<T>(
|
|
|
187
181
|
});
|
|
188
182
|
}
|
|
189
183
|
|
|
190
|
-
function sourceSummary(
|
|
191
|
-
events: ProviderCacheLookupMeta[],
|
|
192
|
-
): ProviderCacheResponseMeta["source"] {
|
|
184
|
+
function sourceSummary(events: ProviderCacheLookupMeta[]): ProviderCacheResponseMeta["source"] {
|
|
193
185
|
const sources = new Set(events.map((event) => event.source));
|
|
194
186
|
if (sources.size === 0) return undefined;
|
|
195
187
|
if (sources.size === 1) return events[0]?.source;
|
|
196
188
|
return "mixed";
|
|
197
189
|
}
|
|
198
190
|
|
|
199
|
-
async function withRedisFallback<T>(
|
|
200
|
-
operation: () => Promise<T>,
|
|
201
|
-
): Promise<T | undefined> {
|
|
191
|
+
async function withRedisFallback<T>(operation: () => Promise<T>): Promise<T | undefined> {
|
|
202
192
|
return await withRedisTimeout(operation, {
|
|
203
193
|
timeoutMs: DEFAULT_REDIS_TIMEOUT_MS,
|
|
204
194
|
onTimeout: () => undefined,
|
|
@@ -206,15 +196,10 @@ async function withRedisFallback<T>(
|
|
|
206
196
|
});
|
|
207
197
|
}
|
|
208
198
|
|
|
209
|
-
export function createProviderCache(
|
|
210
|
-
options: ProviderCacheOptions,
|
|
211
|
-
): ProviderCache {
|
|
199
|
+
export function createProviderCache(options: ProviderCacheOptions): ProviderCache {
|
|
212
200
|
const redisUrl = options.redisUrl ?? providerCacheRedisUrlFromEnv();
|
|
213
201
|
const backend = getSharedBackend(redisUrl);
|
|
214
|
-
const memoryMaxEntries = Math.max(
|
|
215
|
-
1,
|
|
216
|
-
options.memoryMaxEntries ?? DEFAULT_MEMORY_MAX_ENTRIES,
|
|
217
|
-
);
|
|
202
|
+
const memoryMaxEntries = Math.max(1, options.memoryMaxEntries ?? DEFAULT_MEMORY_MAX_ENTRIES);
|
|
218
203
|
const now = options.now ?? Date.now;
|
|
219
204
|
const events: ProviderCacheLookupMeta[] = [];
|
|
220
205
|
|
|
@@ -238,11 +223,7 @@ export function createProviderCache(
|
|
|
238
223
|
}
|
|
239
224
|
}
|
|
240
225
|
|
|
241
|
-
function rememberEnvelope(
|
|
242
|
-
key: string,
|
|
243
|
-
envelope: CacheEnvelope,
|
|
244
|
-
currentTime: number,
|
|
245
|
-
): void {
|
|
226
|
+
function rememberEnvelope(key: string, envelope: CacheEnvelope, currentTime: number): void {
|
|
246
227
|
sweepMemory(currentTime);
|
|
247
228
|
backend.memory.delete(key);
|
|
248
229
|
backend.memory.set(key, {
|
|
@@ -253,11 +234,7 @@ export function createProviderCache(
|
|
|
253
234
|
enforceMemoryLimit();
|
|
254
235
|
}
|
|
255
236
|
|
|
256
|
-
function touchMemory(
|
|
257
|
-
key: string,
|
|
258
|
-
entry: MemoryEntry,
|
|
259
|
-
currentTime: number,
|
|
260
|
-
): void {
|
|
237
|
+
function touchMemory(key: string, entry: MemoryEntry, currentTime: number): void {
|
|
261
238
|
backend.memory.delete(key);
|
|
262
239
|
backend.memory.set(key, { ...entry, lastAccessedAt: currentTime });
|
|
263
240
|
}
|
|
@@ -298,12 +275,7 @@ export function createProviderCache(
|
|
|
298
275
|
if (memoryEntry.expiresAt <= currentTime) {
|
|
299
276
|
backend.memory.delete(key);
|
|
300
277
|
} else {
|
|
301
|
-
const memoryResult = resultFromEnvelope<T>(
|
|
302
|
-
key,
|
|
303
|
-
memoryEntry,
|
|
304
|
-
currentTime,
|
|
305
|
-
"memory",
|
|
306
|
-
);
|
|
278
|
+
const memoryResult = resultFromEnvelope<T>(key, memoryEntry, currentTime, "memory");
|
|
307
279
|
if (memoryResult && !memoryResult.meta.stale) {
|
|
308
280
|
touchMemory(key, memoryEntry, currentTime);
|
|
309
281
|
return memoryResult;
|
|
@@ -334,10 +306,7 @@ export function createProviderCache(
|
|
|
334
306
|
cacheOptions: ProviderCacheGetOrSetOptions,
|
|
335
307
|
): Promise<void> {
|
|
336
308
|
const currentTime = now();
|
|
337
|
-
const freshTtlMs = jitteredTtlMs(
|
|
338
|
-
cacheOptions.ttlMs,
|
|
339
|
-
cacheOptions.jitterPct,
|
|
340
|
-
);
|
|
309
|
+
const freshTtlMs = jitteredTtlMs(cacheOptions.ttlMs, cacheOptions.jitterPct);
|
|
341
310
|
const staleIfErrorMs = cacheOptions.staleIfErrorMs ?? 0;
|
|
342
311
|
const staleTtlMs = freshTtlMs + staleIfErrorMs;
|
|
343
312
|
const envelope: CacheEnvelope = {
|
|
@@ -352,9 +321,7 @@ export function createProviderCache(
|
|
|
352
321
|
if (!redis || !(await ensureRedisReady(redis, DEFAULT_REDIS_TIMEOUT_MS))) {
|
|
353
322
|
return;
|
|
354
323
|
}
|
|
355
|
-
await withRedisFallback(() =>
|
|
356
|
-
redis.set(key, JSON.stringify(envelope), "PX", staleTtlMs),
|
|
357
|
-
);
|
|
324
|
+
await withRedisFallback(() => redis.set(key, JSON.stringify(envelope), "PX", staleTtlMs));
|
|
358
325
|
}
|
|
359
326
|
|
|
360
327
|
async function loadAndStore<T>(
|
|
@@ -385,16 +352,12 @@ export function createProviderCache(
|
|
|
385
352
|
|
|
386
353
|
return {
|
|
387
354
|
key(namespace, parts, keyOptions?: ProviderCacheKeyOptions) {
|
|
388
|
-
const extra = new Set(
|
|
389
|
-
(keyOptions?.redactFields ?? []).map((field) => field.toLowerCase()),
|
|
390
|
-
);
|
|
355
|
+
const extra = new Set((keyOptions?.redactFields ?? []).map((field) => field.toLowerCase()));
|
|
391
356
|
const normalized = normalizeKeyPart(parts, extra);
|
|
392
357
|
return `${DEFAULT_PREFIX}:${options.providerId}:${namespace}:${stableHash(normalized)}`;
|
|
393
358
|
},
|
|
394
359
|
|
|
395
|
-
async get<T = unknown>(
|
|
396
|
-
key: string,
|
|
397
|
-
): Promise<ProviderCacheResult<T> | null> {
|
|
360
|
+
async get<T = unknown>(key: string): Promise<ProviderCacheResult<T> | null> {
|
|
398
361
|
const result = await read<T>(key);
|
|
399
362
|
if (result) record(result.meta);
|
|
400
363
|
return result;
|
|
@@ -405,10 +368,7 @@ export function createProviderCache(
|
|
|
405
368
|
async delete(key: string): Promise<void> {
|
|
406
369
|
backend.memory.delete(key);
|
|
407
370
|
const redis = backend.redis;
|
|
408
|
-
if (
|
|
409
|
-
!redis ||
|
|
410
|
-
!(await ensureRedisReady(redis, DEFAULT_REDIS_TIMEOUT_MS))
|
|
411
|
-
) {
|
|
371
|
+
if (!redis || !(await ensureRedisReady(redis, DEFAULT_REDIS_TIMEOUT_MS))) {
|
|
412
372
|
return;
|
|
413
373
|
}
|
|
414
374
|
await withRedisFallback(() => redis.del(key));
|
|
@@ -428,10 +388,7 @@ export function createProviderCache(
|
|
|
428
388
|
const existingInflight = backend.inflight.get(key);
|
|
429
389
|
if (existingInflight) {
|
|
430
390
|
const inflightResult = await existingInflight;
|
|
431
|
-
const result = resultWithValue<T>(
|
|
432
|
-
inflightResult.value,
|
|
433
|
-
inflightResult.meta,
|
|
434
|
-
);
|
|
391
|
+
const result = resultWithValue<T>(inflightResult.value, inflightResult.meta);
|
|
435
392
|
record(result.meta);
|
|
436
393
|
return result;
|
|
437
394
|
}
|
|
@@ -470,16 +427,12 @@ export function createBypassProviderCache(
|
|
|
470
427
|
|
|
471
428
|
return {
|
|
472
429
|
key(namespace, parts, keyOptions?: ProviderCacheKeyOptions) {
|
|
473
|
-
const extra = new Set(
|
|
474
|
-
(keyOptions?.redactFields ?? []).map((field) => field.toLowerCase()),
|
|
475
|
-
);
|
|
430
|
+
const extra = new Set((keyOptions?.redactFields ?? []).map((field) => field.toLowerCase()));
|
|
476
431
|
const normalized = normalizeKeyPart(parts, extra);
|
|
477
432
|
return `${DEFAULT_PREFIX}:${options.providerId}:${namespace}:${stableHash(normalized)}`;
|
|
478
433
|
},
|
|
479
434
|
|
|
480
|
-
async get<T = unknown>(
|
|
481
|
-
_key: string,
|
|
482
|
-
): Promise<ProviderCacheResult<T> | null> {
|
|
435
|
+
async get<T = unknown>(_key: string): Promise<ProviderCacheResult<T> | null> {
|
|
483
436
|
return null;
|
|
484
437
|
},
|
|
485
438
|
|
package/src/runtime/choice.ts
CHANGED
|
@@ -10,8 +10,8 @@ import {
|
|
|
10
10
|
assertFreshProviderChoiceIssuedAt,
|
|
11
11
|
ProviderChoiceTokenError,
|
|
12
12
|
type ProviderChoiceTokenPayload,
|
|
13
|
-
} from "../choice-token";
|
|
14
|
-
import { ProviderError } from "../errors";
|
|
13
|
+
} from "../choice-token.js";
|
|
14
|
+
import { isProviderError, ProviderError } from "../errors.js";
|
|
15
15
|
import type {
|
|
16
16
|
CredentialContext,
|
|
17
17
|
EnvContext,
|
|
@@ -23,7 +23,8 @@ import type {
|
|
|
23
23
|
ProviderRequestContext,
|
|
24
24
|
ProviderRuntimeState,
|
|
25
25
|
ProviderStateDurationString,
|
|
26
|
-
|
|
26
|
+
StateValue,
|
|
27
|
+
} from "../types.js";
|
|
27
28
|
|
|
28
29
|
export const PROVIDER_RUNTIME_CHOICE_TOKEN_MASTER_SECRET_ENV =
|
|
29
30
|
"APIFUSE__PROVIDER_RUNTIME__CHOICE_TOKEN_MASTER_SECRET";
|
|
@@ -74,18 +75,12 @@ export function createProviderChoiceContext(
|
|
|
74
75
|
): string;
|
|
75
76
|
function issue<TPayload extends ProviderChoiceTokenPayload>(
|
|
76
77
|
issueOptions: ProviderChoiceIssueOptions<TPayload> & {
|
|
77
|
-
readonly storage: Extract<
|
|
78
|
-
ProviderChoiceStorageOptions,
|
|
79
|
-
{ readonly mode: "server" }
|
|
80
|
-
>;
|
|
78
|
+
readonly storage: Extract<ProviderChoiceStorageOptions, { readonly mode: "server" }>;
|
|
81
79
|
},
|
|
82
80
|
): Promise<string>;
|
|
83
81
|
function issue<TPayload extends ProviderChoiceTokenPayload>(
|
|
84
82
|
issueOptions: ProviderChoiceIssueOptions<TPayload> & {
|
|
85
|
-
readonly storage: Extract<
|
|
86
|
-
ProviderChoiceStorageOptions,
|
|
87
|
-
{ readonly mode: "auto" }
|
|
88
|
-
>;
|
|
83
|
+
readonly storage: Extract<ProviderChoiceStorageOptions, { readonly mode: "auto" }>;
|
|
89
84
|
},
|
|
90
85
|
): string | Promise<string>;
|
|
91
86
|
function issue<TPayload extends ProviderChoiceTokenPayload>(
|
|
@@ -112,10 +107,7 @@ export function createProviderChoiceContext(
|
|
|
112
107
|
required: true,
|
|
113
108
|
}),
|
|
114
109
|
};
|
|
115
|
-
const resolvedStorage = resolveIssueStorage(
|
|
116
|
-
issueOptions.storage,
|
|
117
|
-
issueOptions.payload,
|
|
118
|
-
);
|
|
110
|
+
const resolvedStorage = resolveIssueStorage(issueOptions.storage, issueOptions.payload);
|
|
119
111
|
if (resolvedStorage.mode === "server") {
|
|
120
112
|
return issueServerStoredChoice({
|
|
121
113
|
baseEnvelope,
|
|
@@ -146,31 +138,19 @@ export function createProviderChoiceContext(
|
|
|
146
138
|
): ProviderChoiceTokenPayload;
|
|
147
139
|
function parse(
|
|
148
140
|
parseOptions: ProviderChoiceParseOptions & {
|
|
149
|
-
readonly storage: Extract<
|
|
150
|
-
ProviderChoiceStorageOptions,
|
|
151
|
-
{ readonly mode: "server" }
|
|
152
|
-
>;
|
|
141
|
+
readonly storage: Extract<ProviderChoiceStorageOptions, { readonly mode: "server" }>;
|
|
153
142
|
},
|
|
154
143
|
): Promise<ProviderChoiceTokenPayload>;
|
|
155
144
|
function parse(
|
|
156
145
|
parseOptions: ProviderChoiceParseOptions & {
|
|
157
|
-
readonly storage: Extract<
|
|
158
|
-
ProviderChoiceStorageOptions,
|
|
159
|
-
{ readonly mode: "auto" }
|
|
160
|
-
>;
|
|
146
|
+
readonly storage: Extract<ProviderChoiceStorageOptions, { readonly mode: "auto" }>;
|
|
161
147
|
},
|
|
162
148
|
): ProviderChoiceTokenPayload | Promise<ProviderChoiceTokenPayload>;
|
|
163
149
|
function parse(
|
|
164
150
|
parseOptions: ProviderChoiceParseOptions,
|
|
165
151
|
): ProviderChoiceTokenPayload | Promise<ProviderChoiceTokenPayload> {
|
|
166
|
-
const [
|
|
167
|
-
|
|
168
|
-
tokenKid,
|
|
169
|
-
encodedIv,
|
|
170
|
-
encryptedPayload,
|
|
171
|
-
authTag,
|
|
172
|
-
signature,
|
|
173
|
-
] = parseManagedChoiceTokenParts(parseOptions.token);
|
|
152
|
+
const [actualPrefix, tokenKid, encodedIv, encryptedPayload, authTag, signature] =
|
|
153
|
+
parseManagedChoiceTokenParts(parseOptions.token);
|
|
174
154
|
if (
|
|
175
155
|
actualPrefix !== parseOptions.prefix ||
|
|
176
156
|
tokenKid !== kid ||
|
|
@@ -191,13 +171,9 @@ export function createProviderChoiceContext(
|
|
|
191
171
|
purpose: parseOptions.purpose,
|
|
192
172
|
kid: tokenKid,
|
|
193
173
|
});
|
|
194
|
-
const signedBody = [
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
encodedIv,
|
|
198
|
-
encryptedPayload,
|
|
199
|
-
authTag,
|
|
200
|
-
].join(".");
|
|
174
|
+
const signedBody = [parseOptions.prefix, tokenKid, encodedIv, encryptedPayload, authTag].join(
|
|
175
|
+
".",
|
|
176
|
+
);
|
|
201
177
|
assertManagedChoiceSignature({
|
|
202
178
|
signedBody,
|
|
203
179
|
signature,
|
|
@@ -247,30 +223,23 @@ export function createTestProviderChoiceContext(
|
|
|
247
223
|
return createProviderChoiceContext({
|
|
248
224
|
...options,
|
|
249
225
|
masterSecret:
|
|
250
|
-
options.masterSecret ??
|
|
251
|
-
"apifuse-test-provider-runtime-choice-token-master-secret",
|
|
226
|
+
options.masterSecret ?? "apifuse-test-provider-runtime-choice-token-master-secret",
|
|
252
227
|
});
|
|
253
228
|
}
|
|
254
229
|
|
|
255
|
-
function resolveChoiceMasterSecret(
|
|
256
|
-
options: CreateProviderChoiceContextOptions,
|
|
257
|
-
): string {
|
|
230
|
+
function resolveChoiceMasterSecret(options: CreateProviderChoiceContextOptions): string {
|
|
258
231
|
const configured =
|
|
259
|
-
options.masterSecret ??
|
|
260
|
-
options.env?.get(PROVIDER_RUNTIME_CHOICE_TOKEN_MASTER_SECRET_ENV);
|
|
232
|
+
options.masterSecret ?? options.env?.get(PROVIDER_RUNTIME_CHOICE_TOKEN_MASTER_SECRET_ENV);
|
|
261
233
|
const trimmed = configured?.trim();
|
|
262
234
|
if (trimmed) return trimmed;
|
|
263
|
-
throw new ProviderError(
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
details: {
|
|
270
|
-
secret: PROVIDER_RUNTIME_CHOICE_TOKEN_MASTER_SECRET_ENV,
|
|
271
|
-
},
|
|
235
|
+
throw new ProviderError("Provider runtime choice-token master secret is not configured.", {
|
|
236
|
+
code: "CHOICE_TOKEN_MASTER_SECRET_NOT_CONFIGURED",
|
|
237
|
+
category: "internal_error",
|
|
238
|
+
retryable: false,
|
|
239
|
+
details: {
|
|
240
|
+
secret: PROVIDER_RUNTIME_CHOICE_TOKEN_MASTER_SECRET_ENV,
|
|
272
241
|
},
|
|
273
|
-
);
|
|
242
|
+
});
|
|
274
243
|
}
|
|
275
244
|
|
|
276
245
|
type ManagedChoiceKeyInput = {
|
|
@@ -286,9 +255,7 @@ type ManagedChoiceKeys = {
|
|
|
286
255
|
readonly binding: Buffer;
|
|
287
256
|
};
|
|
288
257
|
|
|
289
|
-
function deriveManagedChoiceKeys(
|
|
290
|
-
input: ManagedChoiceKeyInput,
|
|
291
|
-
): ManagedChoiceKeys {
|
|
258
|
+
function deriveManagedChoiceKeys(input: ManagedChoiceKeyInput): ManagedChoiceKeys {
|
|
292
259
|
return {
|
|
293
260
|
encryption: deriveManagedChoiceKey(input, "encryption"),
|
|
294
261
|
signing: deriveManagedChoiceKey(input, "signing"),
|
|
@@ -327,22 +294,14 @@ function encryptManagedChoiceToken(options: {
|
|
|
327
294
|
]).toString("base64url");
|
|
328
295
|
const authTag = cipher.getAuthTag().toString("base64url");
|
|
329
296
|
const encodedIv = iv.toString("base64url");
|
|
330
|
-
const signedBody = [
|
|
331
|
-
options.prefix,
|
|
332
|
-
options.kid,
|
|
333
|
-
encodedIv,
|
|
334
|
-
encryptedPayload,
|
|
335
|
-
authTag,
|
|
336
|
-
].join(".");
|
|
297
|
+
const signedBody = [options.prefix, options.kid, encodedIv, encryptedPayload, authTag].join(".");
|
|
337
298
|
const signature = createHmac("sha256", options.keys.signing)
|
|
338
299
|
.update(signedBody)
|
|
339
300
|
.digest("base64url");
|
|
340
301
|
return `${signedBody}.${signature}`;
|
|
341
302
|
}
|
|
342
303
|
|
|
343
|
-
async function issueServerStoredChoice<
|
|
344
|
-
TPayload extends ProviderChoiceTokenPayload,
|
|
345
|
-
>(options: {
|
|
304
|
+
async function issueServerStoredChoice<TPayload extends ProviderChoiceTokenPayload>(options: {
|
|
346
305
|
readonly baseEnvelope: Omit<ManagedChoiceEnvelope, "payload">;
|
|
347
306
|
readonly issueOptions: ProviderChoiceIssueOptions<TPayload>;
|
|
348
307
|
readonly storage: ServerProviderChoiceStorageOptions;
|
|
@@ -351,23 +310,18 @@ async function issueServerStoredChoice<
|
|
|
351
310
|
readonly keys: ManagedChoiceKeys;
|
|
352
311
|
readonly issuedAtMs: number;
|
|
353
312
|
}): Promise<string> {
|
|
354
|
-
const serializedPayload = serializeChoicePayload(
|
|
355
|
-
options.issueOptions.payload,
|
|
356
|
-
);
|
|
313
|
+
const serializedPayload = serializeChoicePayload(options.issueOptions.payload);
|
|
357
314
|
const payloadBytes = Buffer.byteLength(serializedPayload, "utf8");
|
|
358
315
|
if (payloadBytes > options.storage.maxValueBytes) {
|
|
359
|
-
throw new ProviderError(
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
maxValueBytes: options.storage.maxValueBytes,
|
|
367
|
-
payloadBytes,
|
|
368
|
-
},
|
|
316
|
+
throw new ProviderError("Provider choice payload exceeds state storage policy.", {
|
|
317
|
+
code: "CHOICE_STATE_PAYLOAD_TOO_LARGE",
|
|
318
|
+
category: "input_validation",
|
|
319
|
+
retryable: false,
|
|
320
|
+
details: {
|
|
321
|
+
maxValueBytes: options.storage.maxValueBytes,
|
|
322
|
+
payloadBytes,
|
|
369
323
|
},
|
|
370
|
-
);
|
|
324
|
+
});
|
|
371
325
|
}
|
|
372
326
|
const stateId = `choice_${randomBytes(16).toString("base64url")}`;
|
|
373
327
|
const digest = digestChoicePayload(serializedPayload);
|
|
@@ -406,9 +360,29 @@ async function parseServerStoredChoice(options: {
|
|
|
406
360
|
storage,
|
|
407
361
|
contextState: options.contextState,
|
|
408
362
|
});
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
)
|
|
363
|
+
// Reading a server-stored choice back deserializes a persisted value. A
|
|
364
|
+
// corrupt/undecodable value would otherwise surface as a raw JSON.parse
|
|
365
|
+
// SyntaxError (or another unexpected throwable) that escapes the choice error
|
|
366
|
+
// taxonomy, gets masked as internal_error 500, and is treated as retryable by
|
|
367
|
+
// the hub -> reservation restart loop (2026-07-22 catchtable RCA, candidate A).
|
|
368
|
+
// Convert any non-branded throwable into a branded invalid_payload so it maps
|
|
369
|
+
// to a clean, non-retryable 400. Branded ProviderChoiceTokenError and genuine
|
|
370
|
+
// ProviderError (e.g. Redis-unavailable / state-unavailable) pass through so
|
|
371
|
+
// their category/retryable semantics are preserved.
|
|
372
|
+
let record: StateValue<ProviderChoiceTokenPayload> | null;
|
|
373
|
+
try {
|
|
374
|
+
record = await namespace.get<ProviderChoiceTokenPayload>(
|
|
375
|
+
optionsStateKey(options.handle.state_id),
|
|
376
|
+
);
|
|
377
|
+
} catch (error) {
|
|
378
|
+
if (error instanceof ProviderChoiceTokenError || isProviderError(error)) {
|
|
379
|
+
throw error;
|
|
380
|
+
}
|
|
381
|
+
throw new ProviderChoiceTokenError(
|
|
382
|
+
"invalid_payload",
|
|
383
|
+
"Provider choice token state payload could not be decoded.",
|
|
384
|
+
);
|
|
385
|
+
}
|
|
412
386
|
if (!record) {
|
|
413
387
|
throw new ProviderChoiceTokenError(
|
|
414
388
|
"invalid_payload",
|
|
@@ -439,10 +413,7 @@ function resolveIssueStorage<TPayload extends ProviderChoiceTokenPayload>(
|
|
|
439
413
|
} {
|
|
440
414
|
if (!storage || storage.mode === "inline") return { mode: "inline" };
|
|
441
415
|
if (storage.mode === "server") return { mode: "server", storage };
|
|
442
|
-
const payloadBytes = Buffer.byteLength(
|
|
443
|
-
serializeChoicePayload(payload),
|
|
444
|
-
"utf8",
|
|
445
|
-
);
|
|
416
|
+
const payloadBytes = Buffer.byteLength(serializeChoicePayload(payload), "utf8");
|
|
446
417
|
if (payloadBytes <= storage.maxInlineBytes) return { mode: "inline" };
|
|
447
418
|
return { mode: "server", storage };
|
|
448
419
|
}
|
|
@@ -536,10 +507,7 @@ function parseManagedChoiceTokenParts(
|
|
|
536
507
|
] {
|
|
537
508
|
const parts = token.split(".");
|
|
538
509
|
if (parts.length !== 6) {
|
|
539
|
-
throw new ProviderChoiceTokenError(
|
|
540
|
-
"invalid_shape",
|
|
541
|
-
"Provider choice token shape is invalid.",
|
|
542
|
-
);
|
|
510
|
+
throw new ProviderChoiceTokenError("invalid_shape", "Provider choice token shape is invalid.");
|
|
543
511
|
}
|
|
544
512
|
return [parts[0], parts[1], parts[2], parts[3], parts[4], parts[5]];
|
|
545
513
|
}
|
|
@@ -601,9 +569,7 @@ function decryptManagedChoiceToken(options: {
|
|
|
601
569
|
}
|
|
602
570
|
}
|
|
603
571
|
|
|
604
|
-
function isManagedChoiceEnvelope(
|
|
605
|
-
value: unknown,
|
|
606
|
-
): value is ManagedChoiceEnvelope {
|
|
572
|
+
function isManagedChoiceEnvelope(value: unknown): value is ManagedChoiceEnvelope {
|
|
607
573
|
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
608
574
|
if (!("payload" in value) || !isChoicePayload(value.payload)) return false;
|
|
609
575
|
return (
|
|
@@ -617,9 +583,7 @@ function isManagedChoiceEnvelope(
|
|
|
617
583
|
typeof value.issued_at_ms === "number" &&
|
|
618
584
|
"ttl_ms" in value &&
|
|
619
585
|
typeof value.ttl_ms === "number" &&
|
|
620
|
-
(!("binding" in value) ||
|
|
621
|
-
value.binding === undefined ||
|
|
622
|
-
isChoiceBinding(value.binding))
|
|
586
|
+
(!("binding" in value) || value.binding === undefined || isChoiceBinding(value.binding))
|
|
623
587
|
);
|
|
624
588
|
}
|
|
625
589
|
|
|
@@ -627,13 +591,10 @@ function isChoicePayload(value: unknown): value is ProviderChoiceTokenPayload {
|
|
|
627
591
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
628
592
|
}
|
|
629
593
|
|
|
630
|
-
function isChoiceBinding(
|
|
631
|
-
value: unknown,
|
|
632
|
-
): value is ManagedChoiceEnvelope["binding"] {
|
|
594
|
+
function isChoiceBinding(value: unknown): value is ManagedChoiceEnvelope["binding"] {
|
|
633
595
|
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
634
596
|
return (
|
|
635
|
-
(!("connection_hash" in value) ||
|
|
636
|
-
typeof value.connection_hash === "string") &&
|
|
597
|
+
(!("connection_hash" in value) || typeof value.connection_hash === "string") &&
|
|
637
598
|
(!("credential_hash" in value) || typeof value.credential_hash === "string")
|
|
638
599
|
);
|
|
639
600
|
}
|
|
@@ -648,10 +609,7 @@ function assertManagedChoiceEnvelope(
|
|
|
648
609
|
readonly futureToleranceMs?: number;
|
|
649
610
|
},
|
|
650
611
|
): void {
|
|
651
|
-
if (
|
|
652
|
-
envelope.provider_id !== options.providerId ||
|
|
653
|
-
envelope.purpose !== options.purpose
|
|
654
|
-
) {
|
|
612
|
+
if (envelope.provider_id !== options.providerId || envelope.purpose !== options.purpose) {
|
|
655
613
|
throw new ProviderChoiceTokenError(
|
|
656
614
|
"invalid_payload",
|
|
657
615
|
"Provider choice token payload is invalid.",
|
|
@@ -660,10 +618,7 @@ function assertManagedChoiceEnvelope(
|
|
|
660
618
|
assertFreshProviderChoiceIssuedAt(envelope.issued_at_ms, {
|
|
661
619
|
// Clamp to the issuer's embedded TTL so a caller-supplied value cannot
|
|
662
620
|
// silently extend token validity past the deadline the issuer intended.
|
|
663
|
-
ttlMs:
|
|
664
|
-
options.ttlMs != null
|
|
665
|
-
? Math.min(options.ttlMs, envelope.ttl_ms)
|
|
666
|
-
: envelope.ttl_ms,
|
|
621
|
+
ttlMs: options.ttlMs != null ? Math.min(options.ttlMs, envelope.ttl_ms) : envelope.ttl_ms,
|
|
667
622
|
nowMs: options.nowMs,
|
|
668
623
|
futureToleranceMs: options.futureToleranceMs,
|
|
669
624
|
});
|
|
@@ -676,9 +631,7 @@ function createChoiceBinding(options: {
|
|
|
676
631
|
readonly credential?: CredentialContext;
|
|
677
632
|
readonly required: boolean;
|
|
678
633
|
}): ManagedChoiceEnvelope["binding"] {
|
|
679
|
-
const connectionHash = options.options?.connection
|
|
680
|
-
? hashRequiredConnection(options)
|
|
681
|
-
: undefined;
|
|
634
|
+
const connectionHash = options.options?.connection ? hashRequiredConnection(options) : undefined;
|
|
682
635
|
const credentialHash = options.options?.credentialKeys?.length
|
|
683
636
|
? hashCredentialKeys(options)
|
|
684
637
|
: undefined;
|
|
@@ -697,14 +650,11 @@ function hashRequiredConnection(options: {
|
|
|
697
650
|
const connectionId = options.request?.connectionId;
|
|
698
651
|
if (!connectionId) {
|
|
699
652
|
if (!options.required) return undefined;
|
|
700
|
-
throw new ProviderError(
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
retryable: false,
|
|
706
|
-
},
|
|
707
|
-
);
|
|
653
|
+
throw new ProviderError("Provider choice tokens require connection context.", {
|
|
654
|
+
code: "CHOICE_CONTEXT_REQUIRED",
|
|
655
|
+
category: "input_validation",
|
|
656
|
+
retryable: false,
|
|
657
|
+
});
|
|
708
658
|
}
|
|
709
659
|
return createHmac("sha256", options.keys.binding)
|
|
710
660
|
.update("connection")
|
|
@@ -722,15 +672,12 @@ function hashCredentialKeys(options: {
|
|
|
722
672
|
const material = credentialKeys.map((key) => {
|
|
723
673
|
const value = options.credential?.get(key);
|
|
724
674
|
if (typeof value !== "string" || value.length === 0) {
|
|
725
|
-
throw new ProviderError(
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
details: { credentialKey: key },
|
|
732
|
-
},
|
|
733
|
-
);
|
|
675
|
+
throw new ProviderError("Provider choice tokens require configured credential binding.", {
|
|
676
|
+
code: "CHOICE_CONTEXT_REQUIRED",
|
|
677
|
+
category: "input_validation",
|
|
678
|
+
retryable: false,
|
|
679
|
+
details: { credentialKey: key },
|
|
680
|
+
});
|
|
734
681
|
}
|
|
735
682
|
return [key, value];
|
|
736
683
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { CredentialModeError } from "../errors";
|
|
2
|
-
import type { AuthMode, CredentialContext } from "../types";
|
|
1
|
+
import { CredentialModeError } from "../errors.js";
|
|
2
|
+
import type { AuthMode, CredentialContext } from "../types.js";
|
|
3
3
|
|
|
4
4
|
export interface CreateCredentialContextOptions {
|
|
5
5
|
allowedKeys?: string[];
|
package/src/runtime/env.ts
CHANGED