@apifuse/provider-sdk 2.2.0-beta.2 → 2.2.0-beta.4

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.
Files changed (166) hide show
  1. package/AUTHORING.md +14 -0
  2. package/CHANGELOG.md +8 -0
  3. package/bin/apifuse-check.ts +18 -58
  4. package/bin/apifuse-create.ts +1 -1
  5. package/bin/apifuse-dev.ts +27 -52
  6. package/bin/apifuse-pack-smoke.ts +22 -81
  7. package/bin/apifuse-pack-types.ts +266 -0
  8. package/bin/apifuse-perf.ts +45 -127
  9. package/bin/apifuse-record.ts +23 -61
  10. package/bin/apifuse-submit-check.ts +73 -30
  11. package/bin/apifuse.ts +1 -1
  12. package/bin/submit-check-xml-semantics.ts +204 -0
  13. package/bin/submit-check-xml.ts +134 -0
  14. package/dist/auth-turn/index.d.ts +1 -1
  15. package/dist/auth.d.ts +2 -2
  16. package/dist/auth.js +9 -18
  17. package/dist/ceremonies/index.d.ts +1 -1
  18. package/dist/ceremonies/index.js +8 -18
  19. package/dist/config/loader.d.ts +2 -2
  20. package/dist/config/loader.js +19 -42
  21. package/dist/contract-serialization.d.ts +2 -2
  22. package/dist/contract-serialization.js +3 -6
  23. package/dist/contract-types.d.ts +2 -2
  24. package/dist/contract.d.ts +3 -3
  25. package/dist/contract.js +4 -6
  26. package/dist/define.d.ts +8 -1
  27. package/dist/define.js +57 -115
  28. package/dist/dev.d.ts +1 -1
  29. package/dist/dev.js +1 -1
  30. package/dist/errors.d.ts +4 -1
  31. package/dist/errors.js +48 -0
  32. package/dist/i18n/catalog.d.ts +2 -2
  33. package/dist/i18n/catalog.js +4 -10
  34. package/dist/i18n/index.d.ts +2 -2
  35. package/dist/i18n/index.js +2 -2
  36. package/dist/i18n/keys.d.ts +2 -2
  37. package/dist/index.d.ts +43 -42
  38. package/dist/index.js +38 -37
  39. package/dist/lint.js +8 -15
  40. package/dist/provider.d.ts +11 -11
  41. package/dist/provider.js +9 -9
  42. package/dist/public-schema-field-lint.d.ts +1 -1
  43. package/dist/recipes/gov-api.js +1 -1
  44. package/dist/runtime/auth-flow.d.ts +1 -1
  45. package/dist/runtime/auth-flow.js +3 -3
  46. package/dist/runtime/browser.d.ts +1 -1
  47. package/dist/runtime/browser.js +15 -29
  48. package/dist/runtime/cache.d.ts +1 -1
  49. package/dist/runtime/cache.js +4 -8
  50. package/dist/runtime/choice.d.ts +1 -1
  51. package/dist/runtime/choice.js +12 -34
  52. package/dist/runtime/credential.d.ts +1 -1
  53. package/dist/runtime/credential.js +1 -1
  54. package/dist/runtime/env.d.ts +1 -1
  55. package/dist/runtime/executor.d.ts +1 -1
  56. package/dist/runtime/executor.js +8 -3
  57. package/dist/runtime/http.d.ts +2 -2
  58. package/dist/runtime/http.js +5 -5
  59. package/dist/runtime/insights.d.ts +1 -1
  60. package/dist/runtime/insights.js +6 -13
  61. package/dist/runtime/instrumentation.d.ts +2 -2
  62. package/dist/runtime/instrumentation.js +5 -15
  63. package/dist/runtime/keyring.js +1 -1
  64. package/dist/runtime/namespace.js +1 -1
  65. package/dist/runtime/otlp.d.ts +1 -1
  66. package/dist/runtime/perf.d.ts +1 -1
  67. package/dist/runtime/provider.d.ts +1 -1
  68. package/dist/runtime/provider.js +1 -2
  69. package/dist/runtime/proxy-errors.d.ts +1 -1
  70. package/dist/runtime/proxy-errors.js +3 -5
  71. package/dist/runtime/proxy-retry-policy.d.ts +2 -2
  72. package/dist/runtime/proxy-retry-policy.js +2 -2
  73. package/dist/runtime/proxy-telemetry.d.ts +1 -1
  74. package/dist/runtime/proxy-telemetry.js +16 -48
  75. package/dist/runtime/redis.d.ts +1 -1
  76. package/dist/runtime/redis.js +2 -4
  77. package/dist/runtime/request-options.d.ts +1 -1
  78. package/dist/runtime/state.d.ts +2 -2
  79. package/dist/runtime/state.js +3 -3
  80. package/dist/runtime/stealth.d.ts +2 -2
  81. package/dist/runtime/stealth.js +6 -6
  82. package/dist/runtime/stt.d.ts +1 -1
  83. package/dist/runtime/stt.js +11 -15
  84. package/dist/runtime/trace.d.ts +2 -2
  85. package/dist/runtime/trace.js +2 -4
  86. package/dist/runtime/waterfall.d.ts +1 -1
  87. package/dist/schema.d.ts +1 -1
  88. package/dist/schema.js +7 -15
  89. package/dist/serve.d.ts +1 -1
  90. package/dist/serve.js +1 -1
  91. package/dist/server/index.d.ts +7 -7
  92. package/dist/server/index.js +6 -6
  93. package/dist/server/self-test-redaction.d.ts +1 -1
  94. package/dist/server/self-test-redaction.js +1 -1
  95. package/dist/server/self-test.d.ts +3 -3
  96. package/dist/server/self-test.js +3 -3
  97. package/dist/server/serve.d.ts +3 -3
  98. package/dist/server/serve.js +49 -57
  99. package/dist/server/types.js +2 -7
  100. package/dist/stealth/profiles.d.ts +1 -1
  101. package/dist/stealth/profiles.js +5 -14
  102. package/dist/stream.d.ts +1 -1
  103. package/dist/testing/index.d.ts +2 -2
  104. package/dist/testing/index.js +2 -2
  105. package/dist/testing/run.d.ts +1 -1
  106. package/dist/testing/run.js +7 -14
  107. package/dist/types.d.ts +57 -0
  108. package/dist/user-input.d.ts +30 -0
  109. package/dist/user-input.js +66 -0
  110. package/package.json +7 -4
  111. package/src/auth-turn/index.ts +1 -1
  112. package/src/auth.ts +28 -86
  113. package/src/ceremonies/index.ts +24 -78
  114. package/src/config/loader.ts +58 -176
  115. package/src/contract-serialization.ts +5 -11
  116. package/src/contract-types.ts +2 -2
  117. package/src/contract.ts +12 -28
  118. package/src/define.ts +174 -495
  119. package/src/dev.ts +4 -9
  120. package/src/errors.ts +58 -5
  121. package/src/i18n/catalog.ts +10 -32
  122. package/src/i18n/index.ts +2 -2
  123. package/src/i18n/keys.ts +5 -11
  124. package/src/index.ts +44 -42
  125. package/src/lint.ts +89 -159
  126. package/src/provider.ts +15 -11
  127. package/src/public-schema-field-lint.ts +7 -33
  128. package/src/recipes/gov-api.ts +2 -5
  129. package/src/runtime/auth-flow.ts +5 -7
  130. package/src/runtime/browser.ts +64 -187
  131. package/src/runtime/cache.ts +20 -67
  132. package/src/runtime/choice.ts +55 -129
  133. package/src/runtime/credential.ts +2 -2
  134. package/src/runtime/env.ts +1 -1
  135. package/src/runtime/executor.ts +15 -20
  136. package/src/runtime/http.ts +7 -7
  137. package/src/runtime/insights.ts +15 -53
  138. package/src/runtime/instrumentation.ts +19 -58
  139. package/src/runtime/keyring.ts +7 -19
  140. package/src/runtime/namespace.ts +2 -7
  141. package/src/runtime/otlp.ts +12 -23
  142. package/src/runtime/perf.ts +1 -1
  143. package/src/runtime/provider.ts +4 -9
  144. package/src/runtime/proxy-errors.ts +17 -38
  145. package/src/runtime/proxy-retry-policy.ts +3 -3
  146. package/src/runtime/proxy-telemetry.ts +23 -72
  147. package/src/runtime/redis.ts +4 -12
  148. package/src/runtime/request-options.ts +4 -13
  149. package/src/runtime/state.ts +30 -109
  150. package/src/runtime/stealth.ts +8 -8
  151. package/src/runtime/stt.ts +38 -94
  152. package/src/runtime/trace.ts +14 -44
  153. package/src/runtime/waterfall.ts +5 -18
  154. package/src/schema.ts +23 -84
  155. package/src/serve.ts +1 -1
  156. package/src/server/index.ts +7 -7
  157. package/src/server/self-test-redaction.ts +2 -2
  158. package/src/server/self-test.ts +5 -5
  159. package/src/server/serve.ts +103 -252
  160. package/src/server/types.ts +6 -19
  161. package/src/stealth/profiles.ts +10 -26
  162. package/src/stream.ts +8 -19
  163. package/src/testing/index.ts +2 -2
  164. package/src/testing/run.ts +19 -67
  165. package/src/types.ts +58 -0
  166. package/src/user-input.ts +118 -0
@@ -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
 
@@ -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 { ProviderError } from "../errors.js";
15
15
  import type {
16
16
  CredentialContext,
17
17
  EnvContext,
@@ -23,7 +23,7 @@ import type {
23
23
  ProviderRequestContext,
24
24
  ProviderRuntimeState,
25
25
  ProviderStateDurationString,
26
- } from "../types";
26
+ } from "../types.js";
27
27
 
28
28
  export const PROVIDER_RUNTIME_CHOICE_TOKEN_MASTER_SECRET_ENV =
29
29
  "APIFUSE__PROVIDER_RUNTIME__CHOICE_TOKEN_MASTER_SECRET";
@@ -74,18 +74,12 @@ export function createProviderChoiceContext(
74
74
  ): string;
75
75
  function issue<TPayload extends ProviderChoiceTokenPayload>(
76
76
  issueOptions: ProviderChoiceIssueOptions<TPayload> & {
77
- readonly storage: Extract<
78
- ProviderChoiceStorageOptions,
79
- { readonly mode: "server" }
80
- >;
77
+ readonly storage: Extract<ProviderChoiceStorageOptions, { readonly mode: "server" }>;
81
78
  },
82
79
  ): Promise<string>;
83
80
  function issue<TPayload extends ProviderChoiceTokenPayload>(
84
81
  issueOptions: ProviderChoiceIssueOptions<TPayload> & {
85
- readonly storage: Extract<
86
- ProviderChoiceStorageOptions,
87
- { readonly mode: "auto" }
88
- >;
82
+ readonly storage: Extract<ProviderChoiceStorageOptions, { readonly mode: "auto" }>;
89
83
  },
90
84
  ): string | Promise<string>;
91
85
  function issue<TPayload extends ProviderChoiceTokenPayload>(
@@ -112,10 +106,7 @@ export function createProviderChoiceContext(
112
106
  required: true,
113
107
  }),
114
108
  };
115
- const resolvedStorage = resolveIssueStorage(
116
- issueOptions.storage,
117
- issueOptions.payload,
118
- );
109
+ const resolvedStorage = resolveIssueStorage(issueOptions.storage, issueOptions.payload);
119
110
  if (resolvedStorage.mode === "server") {
120
111
  return issueServerStoredChoice({
121
112
  baseEnvelope,
@@ -146,31 +137,19 @@ export function createProviderChoiceContext(
146
137
  ): ProviderChoiceTokenPayload;
147
138
  function parse(
148
139
  parseOptions: ProviderChoiceParseOptions & {
149
- readonly storage: Extract<
150
- ProviderChoiceStorageOptions,
151
- { readonly mode: "server" }
152
- >;
140
+ readonly storage: Extract<ProviderChoiceStorageOptions, { readonly mode: "server" }>;
153
141
  },
154
142
  ): Promise<ProviderChoiceTokenPayload>;
155
143
  function parse(
156
144
  parseOptions: ProviderChoiceParseOptions & {
157
- readonly storage: Extract<
158
- ProviderChoiceStorageOptions,
159
- { readonly mode: "auto" }
160
- >;
145
+ readonly storage: Extract<ProviderChoiceStorageOptions, { readonly mode: "auto" }>;
161
146
  },
162
147
  ): ProviderChoiceTokenPayload | Promise<ProviderChoiceTokenPayload>;
163
148
  function parse(
164
149
  parseOptions: ProviderChoiceParseOptions,
165
150
  ): ProviderChoiceTokenPayload | Promise<ProviderChoiceTokenPayload> {
166
- const [
167
- actualPrefix,
168
- tokenKid,
169
- encodedIv,
170
- encryptedPayload,
171
- authTag,
172
- signature,
173
- ] = parseManagedChoiceTokenParts(parseOptions.token);
151
+ const [actualPrefix, tokenKid, encodedIv, encryptedPayload, authTag, signature] =
152
+ parseManagedChoiceTokenParts(parseOptions.token);
174
153
  if (
175
154
  actualPrefix !== parseOptions.prefix ||
176
155
  tokenKid !== kid ||
@@ -191,13 +170,9 @@ export function createProviderChoiceContext(
191
170
  purpose: parseOptions.purpose,
192
171
  kid: tokenKid,
193
172
  });
194
- const signedBody = [
195
- parseOptions.prefix,
196
- tokenKid,
197
- encodedIv,
198
- encryptedPayload,
199
- authTag,
200
- ].join(".");
173
+ const signedBody = [parseOptions.prefix, tokenKid, encodedIv, encryptedPayload, authTag].join(
174
+ ".",
175
+ );
201
176
  assertManagedChoiceSignature({
202
177
  signedBody,
203
178
  signature,
@@ -247,30 +222,23 @@ export function createTestProviderChoiceContext(
247
222
  return createProviderChoiceContext({
248
223
  ...options,
249
224
  masterSecret:
250
- options.masterSecret ??
251
- "apifuse-test-provider-runtime-choice-token-master-secret",
225
+ options.masterSecret ?? "apifuse-test-provider-runtime-choice-token-master-secret",
252
226
  });
253
227
  }
254
228
 
255
- function resolveChoiceMasterSecret(
256
- options: CreateProviderChoiceContextOptions,
257
- ): string {
229
+ function resolveChoiceMasterSecret(options: CreateProviderChoiceContextOptions): string {
258
230
  const configured =
259
- options.masterSecret ??
260
- options.env?.get(PROVIDER_RUNTIME_CHOICE_TOKEN_MASTER_SECRET_ENV);
231
+ options.masterSecret ?? options.env?.get(PROVIDER_RUNTIME_CHOICE_TOKEN_MASTER_SECRET_ENV);
261
232
  const trimmed = configured?.trim();
262
233
  if (trimmed) return trimmed;
263
- throw new ProviderError(
264
- "Provider runtime choice-token master secret is not configured.",
265
- {
266
- code: "CHOICE_TOKEN_MASTER_SECRET_NOT_CONFIGURED",
267
- category: "internal_error",
268
- retryable: false,
269
- details: {
270
- secret: PROVIDER_RUNTIME_CHOICE_TOKEN_MASTER_SECRET_ENV,
271
- },
234
+ throw new ProviderError("Provider runtime choice-token master secret is not configured.", {
235
+ code: "CHOICE_TOKEN_MASTER_SECRET_NOT_CONFIGURED",
236
+ category: "internal_error",
237
+ retryable: false,
238
+ details: {
239
+ secret: PROVIDER_RUNTIME_CHOICE_TOKEN_MASTER_SECRET_ENV,
272
240
  },
273
- );
241
+ });
274
242
  }
275
243
 
276
244
  type ManagedChoiceKeyInput = {
@@ -286,9 +254,7 @@ type ManagedChoiceKeys = {
286
254
  readonly binding: Buffer;
287
255
  };
288
256
 
289
- function deriveManagedChoiceKeys(
290
- input: ManagedChoiceKeyInput,
291
- ): ManagedChoiceKeys {
257
+ function deriveManagedChoiceKeys(input: ManagedChoiceKeyInput): ManagedChoiceKeys {
292
258
  return {
293
259
  encryption: deriveManagedChoiceKey(input, "encryption"),
294
260
  signing: deriveManagedChoiceKey(input, "signing"),
@@ -327,22 +293,14 @@ function encryptManagedChoiceToken(options: {
327
293
  ]).toString("base64url");
328
294
  const authTag = cipher.getAuthTag().toString("base64url");
329
295
  const encodedIv = iv.toString("base64url");
330
- const signedBody = [
331
- options.prefix,
332
- options.kid,
333
- encodedIv,
334
- encryptedPayload,
335
- authTag,
336
- ].join(".");
296
+ const signedBody = [options.prefix, options.kid, encodedIv, encryptedPayload, authTag].join(".");
337
297
  const signature = createHmac("sha256", options.keys.signing)
338
298
  .update(signedBody)
339
299
  .digest("base64url");
340
300
  return `${signedBody}.${signature}`;
341
301
  }
342
302
 
343
- async function issueServerStoredChoice<
344
- TPayload extends ProviderChoiceTokenPayload,
345
- >(options: {
303
+ async function issueServerStoredChoice<TPayload extends ProviderChoiceTokenPayload>(options: {
346
304
  readonly baseEnvelope: Omit<ManagedChoiceEnvelope, "payload">;
347
305
  readonly issueOptions: ProviderChoiceIssueOptions<TPayload>;
348
306
  readonly storage: ServerProviderChoiceStorageOptions;
@@ -351,23 +309,18 @@ async function issueServerStoredChoice<
351
309
  readonly keys: ManagedChoiceKeys;
352
310
  readonly issuedAtMs: number;
353
311
  }): Promise<string> {
354
- const serializedPayload = serializeChoicePayload(
355
- options.issueOptions.payload,
356
- );
312
+ const serializedPayload = serializeChoicePayload(options.issueOptions.payload);
357
313
  const payloadBytes = Buffer.byteLength(serializedPayload, "utf8");
358
314
  if (payloadBytes > options.storage.maxValueBytes) {
359
- throw new ProviderError(
360
- "Provider choice payload exceeds state storage policy.",
361
- {
362
- code: "CHOICE_STATE_PAYLOAD_TOO_LARGE",
363
- category: "input_validation",
364
- retryable: false,
365
- details: {
366
- maxValueBytes: options.storage.maxValueBytes,
367
- payloadBytes,
368
- },
315
+ throw new ProviderError("Provider choice payload exceeds state storage policy.", {
316
+ code: "CHOICE_STATE_PAYLOAD_TOO_LARGE",
317
+ category: "input_validation",
318
+ retryable: false,
319
+ details: {
320
+ maxValueBytes: options.storage.maxValueBytes,
321
+ payloadBytes,
369
322
  },
370
- );
323
+ });
371
324
  }
372
325
  const stateId = `choice_${randomBytes(16).toString("base64url")}`;
373
326
  const digest = digestChoicePayload(serializedPayload);
@@ -439,10 +392,7 @@ function resolveIssueStorage<TPayload extends ProviderChoiceTokenPayload>(
439
392
  } {
440
393
  if (!storage || storage.mode === "inline") return { mode: "inline" };
441
394
  if (storage.mode === "server") return { mode: "server", storage };
442
- const payloadBytes = Buffer.byteLength(
443
- serializeChoicePayload(payload),
444
- "utf8",
445
- );
395
+ const payloadBytes = Buffer.byteLength(serializeChoicePayload(payload), "utf8");
446
396
  if (payloadBytes <= storage.maxInlineBytes) return { mode: "inline" };
447
397
  return { mode: "server", storage };
448
398
  }
@@ -536,10 +486,7 @@ function parseManagedChoiceTokenParts(
536
486
  ] {
537
487
  const parts = token.split(".");
538
488
  if (parts.length !== 6) {
539
- throw new ProviderChoiceTokenError(
540
- "invalid_shape",
541
- "Provider choice token shape is invalid.",
542
- );
489
+ throw new ProviderChoiceTokenError("invalid_shape", "Provider choice token shape is invalid.");
543
490
  }
544
491
  return [parts[0], parts[1], parts[2], parts[3], parts[4], parts[5]];
545
492
  }
@@ -601,9 +548,7 @@ function decryptManagedChoiceToken(options: {
601
548
  }
602
549
  }
603
550
 
604
- function isManagedChoiceEnvelope(
605
- value: unknown,
606
- ): value is ManagedChoiceEnvelope {
551
+ function isManagedChoiceEnvelope(value: unknown): value is ManagedChoiceEnvelope {
607
552
  if (!value || typeof value !== "object" || Array.isArray(value)) return false;
608
553
  if (!("payload" in value) || !isChoicePayload(value.payload)) return false;
609
554
  return (
@@ -617,9 +562,7 @@ function isManagedChoiceEnvelope(
617
562
  typeof value.issued_at_ms === "number" &&
618
563
  "ttl_ms" in value &&
619
564
  typeof value.ttl_ms === "number" &&
620
- (!("binding" in value) ||
621
- value.binding === undefined ||
622
- isChoiceBinding(value.binding))
565
+ (!("binding" in value) || value.binding === undefined || isChoiceBinding(value.binding))
623
566
  );
624
567
  }
625
568
 
@@ -627,13 +570,10 @@ function isChoicePayload(value: unknown): value is ProviderChoiceTokenPayload {
627
570
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
628
571
  }
629
572
 
630
- function isChoiceBinding(
631
- value: unknown,
632
- ): value is ManagedChoiceEnvelope["binding"] {
573
+ function isChoiceBinding(value: unknown): value is ManagedChoiceEnvelope["binding"] {
633
574
  if (!value || typeof value !== "object" || Array.isArray(value)) return false;
634
575
  return (
635
- (!("connection_hash" in value) ||
636
- typeof value.connection_hash === "string") &&
576
+ (!("connection_hash" in value) || typeof value.connection_hash === "string") &&
637
577
  (!("credential_hash" in value) || typeof value.credential_hash === "string")
638
578
  );
639
579
  }
@@ -648,10 +588,7 @@ function assertManagedChoiceEnvelope(
648
588
  readonly futureToleranceMs?: number;
649
589
  },
650
590
  ): void {
651
- if (
652
- envelope.provider_id !== options.providerId ||
653
- envelope.purpose !== options.purpose
654
- ) {
591
+ if (envelope.provider_id !== options.providerId || envelope.purpose !== options.purpose) {
655
592
  throw new ProviderChoiceTokenError(
656
593
  "invalid_payload",
657
594
  "Provider choice token payload is invalid.",
@@ -660,10 +597,7 @@ function assertManagedChoiceEnvelope(
660
597
  assertFreshProviderChoiceIssuedAt(envelope.issued_at_ms, {
661
598
  // Clamp to the issuer's embedded TTL so a caller-supplied value cannot
662
599
  // 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,
600
+ ttlMs: options.ttlMs != null ? Math.min(options.ttlMs, envelope.ttl_ms) : envelope.ttl_ms,
667
601
  nowMs: options.nowMs,
668
602
  futureToleranceMs: options.futureToleranceMs,
669
603
  });
@@ -676,9 +610,7 @@ function createChoiceBinding(options: {
676
610
  readonly credential?: CredentialContext;
677
611
  readonly required: boolean;
678
612
  }): ManagedChoiceEnvelope["binding"] {
679
- const connectionHash = options.options?.connection
680
- ? hashRequiredConnection(options)
681
- : undefined;
613
+ const connectionHash = options.options?.connection ? hashRequiredConnection(options) : undefined;
682
614
  const credentialHash = options.options?.credentialKeys?.length
683
615
  ? hashCredentialKeys(options)
684
616
  : undefined;
@@ -697,14 +629,11 @@ function hashRequiredConnection(options: {
697
629
  const connectionId = options.request?.connectionId;
698
630
  if (!connectionId) {
699
631
  if (!options.required) return undefined;
700
- throw new ProviderError(
701
- "Provider choice tokens require connection context.",
702
- {
703
- code: "CHOICE_CONTEXT_REQUIRED",
704
- category: "input_validation",
705
- retryable: false,
706
- },
707
- );
632
+ throw new ProviderError("Provider choice tokens require connection context.", {
633
+ code: "CHOICE_CONTEXT_REQUIRED",
634
+ category: "input_validation",
635
+ retryable: false,
636
+ });
708
637
  }
709
638
  return createHmac("sha256", options.keys.binding)
710
639
  .update("connection")
@@ -722,15 +651,12 @@ function hashCredentialKeys(options: {
722
651
  const material = credentialKeys.map((key) => {
723
652
  const value = options.credential?.get(key);
724
653
  if (typeof value !== "string" || value.length === 0) {
725
- throw new ProviderError(
726
- "Provider choice tokens require configured credential binding.",
727
- {
728
- code: "CHOICE_CONTEXT_REQUIRED",
729
- category: "input_validation",
730
- retryable: false,
731
- details: { credentialKey: key },
732
- },
733
- );
654
+ throw new ProviderError("Provider choice tokens require configured credential binding.", {
655
+ code: "CHOICE_CONTEXT_REQUIRED",
656
+ category: "input_validation",
657
+ retryable: false,
658
+ details: { credentialKey: key },
659
+ });
734
660
  }
735
661
  return [key, value];
736
662
  });
@@ -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[];
@@ -1,4 +1,4 @@
1
- import type { EnvContext } from "../types";
1
+ import type { EnvContext } from "../types.js";
2
2
 
3
3
  export function createEnvContext(allowedKeys?: string[]): EnvContext {
4
4
  return {
@@ -1,11 +1,8 @@
1
- import { ProviderError, SessionExpiredError } from "../errors";
2
- import { parseSchema } from "../schema";
3
- import type { ProviderContext, ProviderDefinition } from "../types";
1
+ import { isSessionExpiredError, ProviderError, SessionExpiredError } from "../errors.js";
2
+ import { parseSchema } from "../schema.js";
3
+ import type { ProviderContext, ProviderDefinition } from "../types.js";
4
4
 
5
- export function isStreamingOperation(
6
- provider: ProviderDefinition,
7
- operationId: string,
8
- ): boolean {
5
+ export function isStreamingOperation(provider: ProviderDefinition, operationId: string): boolean {
9
6
  const kind = provider.operations[operationId]?.transport?.kind ?? "json";
10
7
  return kind !== "json";
11
8
  }
@@ -31,13 +28,10 @@ export async function executeOperation(
31
28
  const operation = provider.operations[operationId];
32
29
 
33
30
  if (!operation) {
34
- throw new ProviderError(
35
- `Unknown operation: ${provider.id}/${operationId}`,
36
- {
37
- code: "NOT_FOUND",
38
- fix: `Valid operations: ${Object.keys(provider.operations).join(", ")}`,
39
- },
40
- );
31
+ throw new ProviderError(`Unknown operation: ${provider.id}/${operationId}`, {
32
+ code: "NOT_FOUND",
33
+ fix: `Valid operations: ${Object.keys(provider.operations).join(", ")}`,
34
+ });
41
35
  }
42
36
 
43
37
  const validatedInput = await parseSchema(
@@ -64,7 +58,12 @@ export async function executeOperation(
64
58
  // operation is safe to re-drive after refresh, which we signal by marking
65
59
  // the surfaced error retryable; non-idempotent operations (the default)
66
60
  // stay non-retryable so they are not auto-re-driven. See design.md §4.3 D3.
67
- if (error instanceof SessionExpiredError && operation.retryOnAuthRefresh) {
61
+ // Use the branded guard, not `instanceof`: a handler loaded through a
62
+ // duplicate/published SDK module can throw a correctly branded
63
+ // SessionExpiredError whose constructor identity differs from this
64
+ // executor's, which `instanceof` would miss — dropping the retryable
65
+ // upgrade and stranding an operation that opted into auth refresh.
66
+ if (isSessionExpiredError(error) && operation.retryOnAuthRefresh) {
68
67
  throw new SessionExpiredError(error.message, { retryable: true });
69
68
  }
70
69
  throw error;
@@ -74,9 +73,5 @@ export async function executeOperation(
74
73
  return result;
75
74
  }
76
75
 
77
- return parseSchema(
78
- operation.output,
79
- result,
80
- `operations.${operationId}.output`,
81
- );
76
+ return parseSchema(operation.output, result, `operations.${operationId}.output`);
82
77
  }
@@ -1,7 +1,7 @@
1
- import type { ProxyResolutionOptions } from "../config/loader";
2
- import { resolveProxyConfigAsync } from "../config/loader";
3
- import { ProviderError, TransportError } from "../errors";
4
- import { parseSseStream, readableBytes, readableLines, readableTextChunks } from "../stream";
1
+ import type { ProxyResolutionOptions } from "../config/loader.js";
2
+ import { resolveProxyConfigAsync } from "../config/loader.js";
3
+ import { ProviderError, TransportError } from "../errors.js";
4
+ import { parseSseStream, readableBytes, readableLines, readableTextChunks } from "../stream.js";
5
5
  import type {
6
6
  HttpClient,
7
7
  HttpMethod,
@@ -11,7 +11,7 @@ import type {
11
11
  RequestOptions,
12
12
  RequestWithMethodOptions,
13
13
  SseMessage,
14
- } from "../types";
14
+ } from "../types.js";
15
15
  import {
16
16
  computeProxyAttemptIndex,
17
17
  computeProxyTransportRetryDelayMs,
@@ -22,8 +22,8 @@ import {
22
22
  proxyTransportRetryErrorStatus,
23
23
  shouldRetryProxyTransportAttempt,
24
24
  validateUnsafeProxyTransportRetryMethods,
25
- } from "./proxy-retry-policy";
26
- import { appendQueryParams, normalizeHttpRequestBody } from "./request-options";
25
+ } from "./proxy-retry-policy.js";
26
+ import { appendQueryParams, normalizeHttpRequestBody } from "./request-options.js";
27
27
 
28
28
  const DEFAULT_HTTP_BASE_URL = "http://localhost";
29
29