@apifuse/provider-sdk 2.2.0-beta.35 → 2.2.0-beta.37

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 (44) hide show
  1. package/AUTHORING.md +22 -8
  2. package/CHANGELOG.md +8 -0
  3. package/README.md +20 -8
  4. package/bin/apifuse-dev.ts +1 -1
  5. package/bin/apifuse-pack-types.ts +6 -5
  6. package/bin/apifuse-record.ts +1 -1
  7. package/bin/apifuse-submit-check.ts +23 -10
  8. package/dist/ceremonies/index.d.ts +8 -0
  9. package/dist/ceremonies/index.js +32 -25
  10. package/dist/cli/templates/provider/Dockerfile.tpl +1 -1
  11. package/dist/cli/templates/provider/index.ts.tpl +6 -3
  12. package/dist/cli/templates/provider/operations/ping.ts.tpl +2 -1
  13. package/dist/define.d.ts +46 -25
  14. package/dist/define.js +381 -9
  15. package/dist/index.d.ts +2 -2
  16. package/dist/provider.d.ts +2 -1
  17. package/dist/runtime/browser.js +19 -11
  18. package/dist/runtime/choice.js +24 -7
  19. package/dist/runtime/resolver-public.d.ts +1 -1
  20. package/dist/runtime/resolver-public.js +1 -1
  21. package/dist/runtime/resolver-vendors/browser.js +57 -14
  22. package/dist/runtime/resolver-vendors/types.d.ts +9 -1
  23. package/dist/runtime/resolver-vendors/types.js +15 -0
  24. package/dist/runtime/resolver.d.ts +1 -0
  25. package/dist/runtime/resolver.js +13 -7
  26. package/dist/server/serve-implementation.js +25 -0
  27. package/dist/types.d.ts +25 -17
  28. package/package.json +6 -1
  29. package/src/ceremonies/index.ts +45 -31
  30. package/src/cli/templates/provider/Dockerfile.tpl +1 -1
  31. package/src/cli/templates/provider/index.ts.tpl +6 -3
  32. package/src/cli/templates/provider/operations/ping.ts.tpl +2 -1
  33. package/src/define.ts +462 -59
  34. package/src/index.ts +5 -2
  35. package/src/provider.ts +6 -1
  36. package/src/runtime/browser.ts +34 -11
  37. package/src/runtime/choice.ts +25 -8
  38. package/src/runtime/resolver-public.ts +2 -0
  39. package/src/runtime/resolver-vendors/browser.ts +69 -11
  40. package/src/runtime/resolver-vendors/types.ts +21 -0
  41. package/src/runtime/resolver.ts +17 -5
  42. package/src/server/serve-implementation.ts +39 -1
  43. package/src/testing/run.ts +3 -3
  44. package/src/types.ts +41 -17
package/dist/define.js CHANGED
@@ -109,13 +109,357 @@ function parsePositiveMsDuration(value) {
109
109
  return undefined;
110
110
  return parsed;
111
111
  }
112
- /** Define one provider operation with schema-driven handler inference. */
113
- export function defineOperation(operation) {
114
- return operation;
112
+ function splitAuthStartParameters(parameters) {
113
+ const parts = [];
114
+ let start = 0;
115
+ let round = 0;
116
+ let square = 0;
117
+ let curly = 0;
118
+ let quote;
119
+ let escaped = false;
120
+ let lineComment = false;
121
+ let blockComment = false;
122
+ const templateDepths = [0];
123
+ templateDepths.length = 0;
124
+ for (let index = 0; index < parameters.length; index++) {
125
+ const character = parameters[index];
126
+ const nextCharacter = parameters[index + 1];
127
+ if (lineComment) {
128
+ if (character === "\n" || character === "\r")
129
+ lineComment = false;
130
+ else
131
+ continue;
132
+ }
133
+ if (blockComment) {
134
+ if (character === "*" && nextCharacter === "/") {
135
+ blockComment = false;
136
+ index++;
137
+ }
138
+ continue;
139
+ }
140
+ if (quote) {
141
+ if (escaped) {
142
+ escaped = false;
143
+ }
144
+ else if (character === "\\") {
145
+ escaped = true;
146
+ }
147
+ else if (quote === "`" && character === "$" && nextCharacter === "{") {
148
+ curly++;
149
+ templateDepths.push(curly);
150
+ quote = undefined;
151
+ index++;
152
+ }
153
+ else if (character === quote) {
154
+ quote = undefined;
155
+ }
156
+ continue;
157
+ }
158
+ if (character === "'" || character === '"' || character === "`") {
159
+ quote = character;
160
+ continue;
161
+ }
162
+ if (character === "/" && nextCharacter === "/") {
163
+ lineComment = true;
164
+ index++;
165
+ continue;
166
+ }
167
+ if (character === "/" && nextCharacter === "*") {
168
+ blockComment = true;
169
+ index++;
170
+ continue;
171
+ }
172
+ if (character === "/")
173
+ return undefined;
174
+ // Annex B HTML-like comments are not lexed here; give up rather than
175
+ // risk misreading the parameter list.
176
+ if (character === "<" && parameters.startsWith("!--", index + 1))
177
+ return undefined;
178
+ if (character === "-" && parameters.startsWith("->", index + 1))
179
+ return undefined;
180
+ if (character === "(")
181
+ round++;
182
+ else if (character === ")")
183
+ round--;
184
+ else if (character === "[")
185
+ square++;
186
+ else if (character === "]")
187
+ square--;
188
+ else if (character === "{")
189
+ curly++;
190
+ else if (character === "}") {
191
+ if (templateDepths.at(-1) === curly) {
192
+ templateDepths.pop();
193
+ curly--;
194
+ quote = "`";
195
+ }
196
+ else
197
+ curly--;
198
+ }
199
+ else if (character === "," && round === 0 && square === 0 && curly === 0) {
200
+ parts.push(parameters.slice(start, index));
201
+ start = index + 1;
202
+ }
203
+ if (round < 0 || square < 0 || curly < 0)
204
+ return undefined;
205
+ }
206
+ if (quote ||
207
+ blockComment ||
208
+ templateDepths.length > 0 ||
209
+ round !== 0 ||
210
+ square !== 0 ||
211
+ curly !== 0)
212
+ return undefined;
213
+ parts.push(parameters.slice(start));
214
+ return parts;
215
+ }
216
+ function authStartParameterList(source) {
217
+ let index = 0;
218
+ while (index < source.length && /\s/.test(source[index] ?? ""))
219
+ index++;
220
+ if (index >= source.length)
221
+ return undefined;
222
+ let openIndex = -1;
223
+ let parenthesizedArrow = false;
224
+ let asyncMethodOrArrow = false;
225
+ const skipTrivia = () => {
226
+ while (index < source.length) {
227
+ if (/\s/.test(source[index] ?? "")) {
228
+ index++;
229
+ continue;
230
+ }
231
+ if (source[index] === "/" && source[index + 1] === "/") {
232
+ index += 2;
233
+ while (index < source.length && source[index] !== "\n" && source[index] !== "\r")
234
+ index++;
235
+ continue;
236
+ }
237
+ if (source[index] === "/" && source[index + 1] === "*") {
238
+ const end = source.indexOf("*/", index + 2);
239
+ if (end < 0) {
240
+ index = source.length;
241
+ return;
242
+ }
243
+ index = end + 2;
244
+ continue;
245
+ }
246
+ return;
247
+ }
248
+ };
249
+ const initial = source.slice(index);
250
+ const isAsync = initial.startsWith("async") && !/[\w$]/.test(initial[5] ?? "");
251
+ if (isAsync) {
252
+ index += 5;
253
+ skipTrivia();
254
+ }
255
+ const afterAsync = source.slice(index);
256
+ const isFunction = afterAsync.startsWith("function") && !/[\w$]/.test(afterAsync[8] ?? "");
257
+ if (isFunction) {
258
+ index += 8;
259
+ skipTrivia();
260
+ if (source[index] === "*") {
261
+ index++;
262
+ skipTrivia();
263
+ }
264
+ }
265
+ else if (source[index] === "*") {
266
+ index++;
267
+ skipTrivia();
268
+ }
269
+ if (source[index] === "(") {
270
+ openIndex = index;
271
+ parenthesizedArrow = !isFunction;
272
+ asyncMethodOrArrow = isAsync && !isFunction;
273
+ }
274
+ else if (isFunction) {
275
+ if (!/[A-Za-z_$]/.test(source[index] ?? ""))
276
+ return undefined;
277
+ index++;
278
+ while (index < source.length && /[A-Za-z0-9_$]/.test(source[index] ?? ""))
279
+ index++;
280
+ skipTrivia();
281
+ if (source[index] !== "(")
282
+ return undefined;
283
+ openIndex = index;
284
+ }
285
+ else {
286
+ const identifierStart = index;
287
+ if (!/[A-Za-z_$]/.test(source[index] ?? ""))
288
+ return undefined;
289
+ index++;
290
+ while (index < source.length && /[A-Za-z0-9_$]/.test(source[index] ?? ""))
291
+ index++;
292
+ const firstIdentifier = source.slice(identifierStart, index);
293
+ skipTrivia();
294
+ if (source[index] === "=" && source[index + 1] === ">")
295
+ return undefined;
296
+ if (source[index] !== "(") {
297
+ if (firstIdentifier !== "get" && firstIdentifier !== "set")
298
+ return undefined;
299
+ if (!/[A-Za-z_$]/.test(source[index] ?? ""))
300
+ return undefined;
301
+ index++;
302
+ while (index < source.length && /[A-Za-z0-9_$]/.test(source[index] ?? ""))
303
+ index++;
304
+ skipTrivia();
305
+ }
306
+ if (source[index] !== "(")
307
+ return undefined;
308
+ openIndex = index;
309
+ }
310
+ if (openIndex < 0)
311
+ return undefined;
312
+ let depth = 1;
313
+ let square = 0;
314
+ let curly = 0;
315
+ let quote;
316
+ let escaped = false;
317
+ let lineComment = false;
318
+ let blockComment = false;
319
+ const templateDepths = [0];
320
+ templateDepths.length = 0;
321
+ for (index = openIndex + 1; index < source.length; index++) {
322
+ const character = source[index];
323
+ const nextCharacter = source[index + 1];
324
+ if (lineComment) {
325
+ if (character === "\n" || character === "\r")
326
+ lineComment = false;
327
+ else
328
+ continue;
329
+ }
330
+ if (blockComment) {
331
+ if (character === "*" && nextCharacter === "/") {
332
+ blockComment = false;
333
+ index++;
334
+ }
335
+ continue;
336
+ }
337
+ if (quote) {
338
+ if (escaped)
339
+ escaped = false;
340
+ else if (character === "\\")
341
+ escaped = true;
342
+ else if (quote === "`" && character === "$" && nextCharacter === "{") {
343
+ curly++;
344
+ templateDepths.push(curly);
345
+ quote = undefined;
346
+ index++;
347
+ }
348
+ else if (character === quote)
349
+ quote = undefined;
350
+ continue;
351
+ }
352
+ if (character === "'" || character === '"' || character === "`") {
353
+ quote = character;
354
+ continue;
355
+ }
356
+ if (character === "/" && nextCharacter === "/") {
357
+ lineComment = true;
358
+ index++;
359
+ continue;
360
+ }
361
+ if (character === "/" && nextCharacter === "*") {
362
+ blockComment = true;
363
+ index++;
364
+ continue;
365
+ }
366
+ if (character === "/")
367
+ return undefined;
368
+ // Annex B HTML-like comments are not lexed here; give up rather than
369
+ // risk misreading the parameter list.
370
+ if (character === "<" && source.startsWith("!--", index + 1))
371
+ return undefined;
372
+ if (character === "-" && source.startsWith("->", index + 1))
373
+ return undefined;
374
+ if (character === "(")
375
+ depth++;
376
+ else if (character === ")") {
377
+ depth--;
378
+ if (depth === 0 && square === 0 && curly === 0) {
379
+ const closeIndex = index;
380
+ if (parenthesizedArrow) {
381
+ index++;
382
+ skipTrivia();
383
+ const hasArrow = source[index] === "=" && source[index + 1] === ">";
384
+ if (!hasArrow && (!asyncMethodOrArrow || source[index] !== "{"))
385
+ return undefined;
386
+ }
387
+ return source.slice(openIndex + 1, closeIndex);
388
+ }
389
+ if (depth < 0)
390
+ return undefined;
391
+ }
392
+ else if (character === "[")
393
+ square++;
394
+ else if (character === "]") {
395
+ square--;
396
+ if (square < 0)
397
+ return undefined;
398
+ }
399
+ else if (character === "{")
400
+ curly++;
401
+ else if (character === "}") {
402
+ if (templateDepths.at(-1) === curly) {
403
+ templateDepths.pop();
404
+ curly--;
405
+ quote = "`";
406
+ }
407
+ else
408
+ curly--;
409
+ if (curly < 0)
410
+ return undefined;
411
+ }
412
+ }
413
+ return undefined;
115
414
  }
116
- /** Define a non-JSON provider operation with explicit transport metadata. */
117
- export function defineStreamOperation(operation) {
118
- return operation;
415
+ /**
416
+ * Conservative defense in depth for defaulted second parameters, which
417
+ * JavaScript intentionally omits from Function.length. Ambiguous source is
418
+ * ignored so this check can never reject a valid provider on weak evidence.
419
+ */
420
+ function authStartHasHiddenInput(start) {
421
+ let source;
422
+ try {
423
+ source = Function.prototype.toString.call(start);
424
+ }
425
+ catch {
426
+ return false;
427
+ }
428
+ if (!source ||
429
+ source.includes("[native code]") ||
430
+ /^\s*(?:async\s+)?function\s+bound\b/.test(source))
431
+ return false;
432
+ const parameters = authStartParameterList(source);
433
+ if (!parameters)
434
+ return false;
435
+ const parts = splitAuthStartParameters(parameters);
436
+ if (!parts || parts.length < 2)
437
+ return false;
438
+ // Require ordinary, readable source formatting. This intentionally fails
439
+ // open for minified output and for transpilers that rewrite defaults.
440
+ const commaIndex = parameters.indexOf(",");
441
+ if (commaIndex < 0 || !/\s/.test(parameters[commaIndex + 1] ?? ""))
442
+ return false;
443
+ const first = parts[0].trim();
444
+ const second = parts[1].trim();
445
+ const identifier = /^[_$A-Za-z][_$A-Za-z0-9]*/;
446
+ const firstName = first.match(identifier)?.[0];
447
+ const secondName = second.match(identifier)?.[0];
448
+ if (!firstName || !secondName || firstName.length < 3 || secondName.length < 3)
449
+ return false;
450
+ return /\s=\s/.test(second);
451
+ }
452
+ /** Define one factored provider operation with schema-driven handler inference. */
453
+ export function defineOperation() {
454
+ return function operation(config) {
455
+ return config;
456
+ };
457
+ }
458
+ /** Define a factored non-JSON operation with explicit transport metadata. */
459
+ export function defineStreamOperation() {
460
+ return function streamOperation(config) {
461
+ return config;
462
+ };
119
463
  }
120
464
  function assertObjectConfig(value) {
121
465
  if (!value || typeof value !== "object") {
@@ -218,6 +562,11 @@ function validateProviderShape(config) {
218
562
  assertRequiredField(config, "operations", String(config.id));
219
563
  if (typeof config.runtime === "string")
220
564
  assertLiteralField(config.runtime, "runtime", VALID_RUNTIMES, String(config.id));
565
+ if (config.native !== undefined && config.runtime === "browser") {
566
+ throw new ValidationError(`Provider "${String(config.id)}" cannot declare capability "native" with runtime "browser"`, {
567
+ fix: 'Use runtime: "standard" or runtime: "shared", or remove the native declaration.',
568
+ });
569
+ }
221
570
  const auth = config.auth;
222
571
  if (auth && typeof auth === "object" && "mode" in auth && typeof auth.mode === "string")
223
572
  assertLiteralField(auth.mode, "auth.mode", VALID_AUTH_MODES, String(config.id));
@@ -241,6 +590,19 @@ function validateProviderShape(config) {
241
590
  fix: "Return a form turn from start(ctx), then receive user input in continue(ctx, input).",
242
591
  });
243
592
  }
593
+ if (auth &&
594
+ typeof auth === "object" &&
595
+ "flow" in auth &&
596
+ auth.flow &&
597
+ typeof auth.flow === "object" &&
598
+ "start" in auth.flow &&
599
+ typeof auth.flow.start === "function" &&
600
+ auth.flow.start.length <= 1 &&
601
+ authStartHasHiddenInput(auth.flow.start)) {
602
+ throw new ProviderError(`Provider "${String(config.id)}" auth.flow.start must not declare an input parameter`, {
603
+ fix: "Return a form turn from start(ctx), then receive user input in continue(ctx, input).",
604
+ });
605
+ }
244
606
  const access = config.access;
245
607
  if (access !== undefined) {
246
608
  if (!access || typeof access !== "object" || Array.isArray(access)) {
@@ -427,7 +789,9 @@ function validateProviderResolver(config) {
427
789
  });
428
790
  }
429
791
  rejectUnknownFields(resolver, new Set(["vendors", "kinds", "clientProfile"]), "resolver", config.id);
430
- validateResolverLiteralArray(resolver.vendors, "resolver.vendors", VALID_PROVIDER_RESOLVER_VENDORS, config.id);
792
+ if (resolver.vendors !== undefined) {
793
+ validateResolverLiteralArray(resolver.vendors, "resolver.vendors", VALID_PROVIDER_RESOLVER_VENDORS, config.id);
794
+ }
431
795
  validateResolverLiteralArray(resolver.kinds, "resolver.kinds", VALID_PROVIDER_CHALLENGE_KINDS, config.id);
432
796
  if (resolver.clientProfile !== undefined &&
433
797
  (typeof resolver.clientProfile !== "string" || !resolver.clientProfile.trim())) {
@@ -1580,7 +1944,15 @@ function validateProviderDeployment(providerId, deployment) {
1580
1944
  fix: 'Pass deployment: { runtime: "shared" | "dedicated" | "browser", ... } or remove the field',
1581
1945
  });
1582
1946
  }
1583
- export function defineProvider(config) {
1947
+ /** Establish a provider declaration before its operations are contextually typed. */
1948
+ export function defineProvider(declaration) {
1949
+ const buildProvider = (implementation) => finalizeProvider({
1950
+ ...declaration,
1951
+ ...implementation,
1952
+ });
1953
+ return buildProvider;
1954
+ }
1955
+ function finalizeProvider(config) {
1584
1956
  validateProviderShape(config);
1585
1957
  const operations = resolveOperationFixtureRequests(config.operations);
1586
1958
  if (!CONNECTOR_ID_REGEX.test(config.id))
@@ -1647,7 +2019,7 @@ export function defineProvider(config) {
1647
2019
  credential: config.credential,
1648
2020
  context: config.context,
1649
2021
  meta: config.meta,
1650
- operations,
2022
+ operations: operations,
1651
2023
  // Transitional healthMonitor → healthProbe alias: mirror whichever field
1652
2024
  // was declared onto both so old and new consumers keep working.
1653
2025
  healthMonitor: config.healthMonitor ?? config.healthProbe,
package/dist/index.d.ts CHANGED
@@ -4,7 +4,7 @@ export * from "./choice-token.js";
4
4
  export type { ApiFuseConfig, BrowserConfig, ProxyProtocol, ProxyResolutionOptions, ProxyResolutionSource, ProxyVendorName, ResolvedProxyConfig, SessionConfig, } from "./config/loader.js";
5
5
  export { defineConfig, loadApiFuseConfig, resolveProxy } from "./config/loader.js";
6
6
  export { canonicalJson, digestProviderContract, extractProviderContract, type JsonPrimitive, type JsonValue, PROVIDER_CONTRACT_SCHEMA_VERSION, type ProviderContractOperation, type ProviderContractSnapshot, } from "./contract.js";
7
- export { centered, delayed, defineHealthJourney, defineOperation, defineProvider, defineSmsOtpMatcher, defineStreamOperation, every, type ProviderConfig, } from "./define.js";
7
+ export { centered, delayed, defineHealthJourney, defineOperation, defineProvider, defineSmsOtpMatcher, defineStreamOperation, every, type AuthStartNoInputGuard, type ProviderBuilder, type ProviderContextOf, type ProviderDeclaration, } from "./define.js";
8
8
  export type { DevServerOptions } from "./dev.js";
9
9
  export { createDevServer, startDevServer } from "./dev.js";
10
10
  export * from "./errors.js";
@@ -42,7 +42,7 @@ export { APIFUSE_DESCRIPTION_KEY_META_KEY, APIFUSE_REDACTION_MARKER, APIFUSE_SEN
42
42
  export { createServerApp, createServerAppAsync, ERROR_OBSERVABILITY_HEADER, type ServeOptions, serve, } from "./server/serve.js";
43
43
  export { getStealthProfile, listStealthProfiles } from "./stealth/profiles.js";
44
44
  export * from "./stream.js";
45
- export type { ApiFuseResponse, AuthConfig, AuthContext, AuthFlowDefinition, AuthFlowInputHandler, AuthFlowStartHandler, AuthMode, AuthTurn, Bcp47Locale, BrowserCookie, BrowserEngine, BrowserOptions, BrowserResourceBody, BrowserResourceDecision, BrowserResourceMethod, BrowserResourcePolicy, BrowserResourceRequest, BrowserResourceRoute, ChallengeSolution, ConnectionMode, ContextDeclaration, CookieJar, ContextScratchpad, CredentialContext, CredentialDeclaration, E164PhoneNumber, EnvContext, FlowContext, FlowContextStore, HealthCheckAssertionContext, HealthCheckCase, HealthCheckCaseResult, HealthCheckSuite, HealthCheckUnsupported, HealthJourneyDefinition, HealthJourneyEventContext, HealthJourneyGatewayContext, HealthJourneyJournalContext, HealthJourneyManualTriggerPolicy, HealthJourneyRunContext, HealthJourneyRunResult, HealthJourneySchedule, HealthScheduleRandomization, HealthJourneySmsContext, HealthJourneyStep, HttpClient, HttpMethod, HttpRedirectFailureReason, HttpRedirectPolicy, HttpRedirectPolicyMode, HttpResponse, HttpRetryOptions, HttpRetrySummary, HttpStreamResponse, IanaTimeZone, InferSchemaOutput, Iso3166Alpha2CountryCode, Iso4217CurrencyCode, Iso8601Duration, NativeContext, NativeNetworkClient, NativeNetworkCloseReason, NativeNetworkConnection, NativeNetworkConnectInput, NativeNetworkConnectOptions, NativeNetworkDynamicGrantOptions, NativeNetworkEgressGrant, NativeProviderConfig, NativeProviderContext, NativeProxyDrainHandler, NativeProxyEgressInfo, NativeProxyExpiringEvent, NativeProxyExpiringReason, NativeTcpDynamicEgressRule, NativeTcpEgressGrant, NativeTcpEgressRule, NativeTcpPortRange, NativeTcpTlsMode, NativeTlsConnectOptions, OcrCaptchaCandidate, OcrCaptchaOptions, OcrCaptchaResult, OcrContext, OcrImageInput, OcrRecognizeRequest, OcrResult, OcrWarning, OperationAnnotations, OperationApprovalPolicy, OperationContractMetadata, OperationDefinition, OperationDeprecationMetadata, OperationDocMeta, OperationErrorCode, ProviderErrorStatus, OperationHandlerResult, OperationInputExample, OperationLifecycle, OperationObservabilityConfig, OperationObservabilitySensitiveConfig, OperationRelationships, OperationRiskClass, OperationSensitivePath, OperationToolRouterMetadata, OperationTransport, OperationTransportKind, ProbeInterval, ProviderAccessConfig, ProviderAccessVisibility, ProviderCache, ProviderCacheGetOrSetOptions, ProviderCacheKeyOptions, ProviderCacheLookupMeta, ProviderCacheResponseMeta, ProviderCacheResult, ProviderChoiceBindingOptions, ProviderChoiceConsumeMode, ProviderChoiceConsumeResult, ProviderChoiceContext, ProviderChoiceExplicitParseResult, ProviderChoiceIssueOptions, ProviderChoiceParseOptions, ProviderChallenge, ProviderChallengeKind, ProviderContext, ProviderDefinition, ProviderDeploymentOverrides, ProviderFileRef, ProviderFilesContext, ProviderHealthMonitorConfig, ProviderHealthProbeConfig, ProviderLocale, ProviderLocaleKey, ProviderLocaleKeyInput, ProviderLogoProfile, ProviderLogoSource, ProviderMeta, ProviderOcrConfig, ProviderProxyConfig, ProviderProxyMode, ProviderProxyPolicy, ProxiedOAuthConfig, ProviderProxyProvider, ProviderProxySessionAffinity, ProviderPublicConnectionMode, ProviderPublicProfile, ProviderReviewed, ProviderResolvedFile, ProviderResolverConfig, ProviderResolverVendor, ProviderRuntimeState, ProviderSecretDeclaration, ProviderStateDurationString, ProviderStateNamespace, ProviderStreamEvent, ProviderSttConfig, ProviderSttMode, ProviderSupportLevel, RequestOptions, RedirectRunReason, ResolverContext, Rfc3339Instant, SchemaLike, SmsOrigin, SmsOtpExtractionPattern, SmsOtpMatcherDefinition, SseMessage, StandardSchemaV1, StateCasResult, StateNamespaceOptions, StateNamespaceScope, StateValue, StateWriteOptions, StealthClient, StealthCookieStore, StealthCookieStoreV1, StealthFetchOptions, StealthPlatform, StealthProfile, StealthRedirectHop, StealthRedirectRunOptions, StealthRedirectRunResult, StealthResponse, StealthSession, StealthSessionCookies, SttAudioInput, SttContext, SttPromptPolicy, SttSegment, SttTranscribeMode, SttTranscribeRequest, SttTranscript, SttUnsupportedOptionPolicy, SttUsage, SttVerificationCodeOptions, SttWarning, TraceConfig, TraceSpan, VerificationCodeCandidate, VerificationCodeCandidateSource, VerificationCodeExtractionResult, } from "./types.js";
45
+ export type { ApiFuseResponse, AuthConfig, AuthContext, AuthFlowDefinition, AuthFlowInputHandler, AuthFlowStartHandler, AuthMode, AuthTurn, Bcp47Locale, BrowserCookie, BrowserEngine, BrowserOptions, BrowserResourceBody, BrowserResourceDecision, BrowserResourceMethod, BrowserResourcePolicy, BrowserResourceRequest, BrowserResourceRoute, ChallengeSolution, ConnectionMode, ContextDeclaration, CookieJar, ContextScratchpad, CredentialContext, CredentialDeclaration, E164PhoneNumber, EnvContext, FlowContext, FlowContextStore, HealthCheckAssertionContext, HealthCheckCase, HealthCheckCaseResult, HealthCheckSuite, HealthCheckUnsupported, HealthJourneyDefinition, HealthJourneyEventContext, HealthJourneyGatewayContext, HealthJourneyJournalContext, HealthJourneyManualTriggerPolicy, HealthJourneyRunContext, HealthJourneyRunResult, HealthJourneySchedule, HealthScheduleRandomization, HealthJourneySmsContext, HealthJourneyStep, HttpClient, HttpMethod, HttpRedirectFailureReason, HttpRedirectPolicy, HttpRedirectPolicyMode, HttpResponse, HttpRetryOptions, HttpRetrySummary, HttpStreamResponse, IanaTimeZone, InferSchemaOutput, Iso3166Alpha2CountryCode, Iso4217CurrencyCode, Iso8601Duration, NativeContext, NativeNetworkClient, NativeNetworkCloseReason, NativeNetworkConnection, NativeNetworkConnectInput, NativeNetworkConnectOptions, NativeNetworkDynamicGrantOptions, NativeNetworkEgressGrant, NativeProviderConfig, NativeProxyDrainHandler, NativeProxyEgressInfo, NativeProxyExpiringEvent, NativeProxyExpiringReason, NativeTcpDynamicEgressRule, NativeTcpEgressGrant, NativeTcpEgressRule, NativeTcpPortRange, NativeTcpTlsMode, NativeTlsConnectOptions, OcrCaptchaCandidate, OcrCaptchaOptions, OcrCaptchaResult, OcrContext, OcrImageInput, OcrRecognizeRequest, OcrResult, OcrWarning, OperationAnnotations, OperationApprovalPolicy, OperationContractMetadata, OperationDefinition, OperationDeprecationMetadata, OperationDocMeta, OperationErrorCode, ProviderErrorStatus, OperationHandlerResult, OperationInputExample, OperationLifecycle, OperationObservabilityConfig, OperationObservabilitySensitiveConfig, OperationRelationships, OperationRiskClass, OperationSensitivePath, OperationToolRouterMetadata, OperationTransport, OperationTransportKind, ProbeInterval, ProviderAccessConfig, ProviderAccessVisibility, ProviderCache, ProviderCacheGetOrSetOptions, ProviderCacheKeyOptions, ProviderCacheLookupMeta, ProviderCacheResponseMeta, ProviderCacheResult, ProviderChoiceBindingOptions, ProviderChoiceConsumeMode, ProviderChoiceConsumeResult, ProviderChoiceContext, ProviderChoiceExplicitParseResult, ProviderChoiceIssueOptions, ProviderChoiceParseOptions, ProviderChallenge, ProviderChallengeKind, ProviderContext, ProviderContextFor, ProviderDefinition, ProviderDeploymentOverrides, ProviderFileRef, ProviderFilesContext, ProviderHealthMonitorConfig, ProviderHealthProbeConfig, ProviderLocale, ProviderLocaleKey, ProviderLocaleKeyInput, ProviderLogoProfile, ProviderLogoSource, ProviderMeta, ProviderOcrConfig, ProviderProxyConfig, ProviderProxyMode, ProviderProxyPolicy, ProxiedOAuthConfig, ProviderProxyProvider, ProviderProxySessionAffinity, ProviderPublicConnectionMode, ProviderPublicProfile, ProviderReviewed, ProviderResolvedFile, ProviderResolverConfig, ProviderResolverVendor, ProviderRuntimeState, ProviderSecretDeclaration, ProviderStateDurationString, ProviderStateNamespace, ProviderStreamEvent, ProviderSttConfig, ProviderSttMode, ProviderSupportLevel, RequestOptions, RedirectRunReason, ResolverContext, Rfc3339Instant, SchemaLike, SmsOrigin, SmsOtpExtractionPattern, SmsOtpMatcherDefinition, SseMessage, StandardSchemaV1, StateCasResult, StateNamespaceOptions, StateNamespaceScope, StateValue, StateWriteOptions, StealthClient, StealthCookieStore, StealthCookieStoreV1, StealthFetchOptions, StealthPlatform, StealthProfile, StealthRedirectHop, StealthRedirectRunOptions, StealthRedirectRunResult, StealthResponse, StealthSession, StealthSessionCookies, SttAudioInput, SttContext, SttPromptPolicy, SttSegment, SttTranscribeMode, SttTranscribeRequest, SttTranscript, SttUnsupportedOptionPolicy, SttUsage, SttVerificationCodeOptions, SttWarning, TraceConfig, TraceSpan, VerificationCodeCandidate, VerificationCodeCandidateSource, VerificationCodeExtractionResult, } from "./types.js";
46
46
  export { DEFAULT_OPERATION_TRANSPORT, HttpRetryAfterPolicy, HttpRetryDelayStrategy, HttpRetryJitter, HttpRetryPreset, HttpRetryUnsafeMethodPolicy, PROBE_INTERVALS, STREAM_CHUNK_BYTES_MAX, STREAM_CHUNK_BYTES_MIN, STREAM_HEARTBEAT_MS_MAX, STREAM_HEARTBEAT_MS_MIN, STREAM_IDLE_TIMEOUT_MS_MAX, STREAM_IDLE_TIMEOUT_MS_MIN, STREAM_MAX_DURATION_MS_MAX, STREAM_MAX_DURATION_MS_MIN, } from "./types.js";
47
47
  export * from "./utils/date.js";
48
48
  export * from "./utils/parse.js";
@@ -3,11 +3,12 @@ export type { CredentialsAuthChallengeDefinition, CredentialsAuthChallengeReques
3
3
  export { createFormCeremony } from "./ceremonies/index.js";
4
4
  export { assertFreshProviderChoiceIssuedAt, createProviderChoiceToken, ProviderChoiceTokenError, type ProviderChoiceTokenErrorReason, type ProviderChoiceTokenPayload, parseProviderChoiceToken, } from "./choice-token.js";
5
5
  export { centered, delayed, defineHealthJourney, defineOperation, defineProvider, defineSmsOtpMatcher, every, } from "./define.js";
6
+ export type { ProviderBuilder, ProviderContextOf, ProviderDeclaration, } from "./define.js";
6
7
  export { AuthError, HttpRedirectError, isProviderError, isSessionExpiredError, isTransportError, ProviderError, SessionExpiredError, TransportError, ValidationError, } from "./errors.js";
7
8
  export { getProviderLocalePath, providerLocaleKey, qualifyProviderLocaleKey, } from "./i18n/index.js";
8
9
  export { type CreateProviderChoiceContextOptions, createProviderChoiceContext, createTestProviderChoiceContext, PROVIDER_RUNTIME_CHOICE_TOKEN_MASTER_SECRET_ENV, } from "./runtime/choice.js";
9
10
  export { APIFUSE_DESCRIPTION_KEY_META_KEY, APIFUSE_REDACTION_MARKER, APIFUSE_SENSITIVE_KIND_META_KEY, APIFUSE_SENSITIVE_META_KEY, collectSensitivePaths, describeKey, field, fields, isSensitiveSchema, redactPayload, type SensitiveFieldKind, type SensitiveFieldOptions, type SensitivePath, sensitive, z, } from "./schema.js";
10
- export type { AuthAbortData, AuthAbortRetry, AuthFlowTerminalContext, AuthMode, AuthSafeData, AuthSafeJson, FlowContext, HealthCheckAssertionContext, HealthCheckCase, HealthCheckSuite, HealthCheckUnsupported, HealthJourneyDefinition, HealthJourneyEventContext, HealthJourneyManualTriggerPolicy, HealthJourneyRunContext, HealthJourneyRunResult, HealthScheduleRandomization, HttpRedirectFailureReason, HttpRedirectPolicy, HttpRedirectPolicyMode, HttpRetryOptions, HttpRetrySummary, InferSchemaOutput, NativeContext, NativeNetworkClient, NativeNetworkCloseReason, NativeNetworkConnection, NativeNetworkConnectInput, NativeNetworkConnectOptions, NativeNetworkDynamicGrantOptions, NativeNetworkEgressGrant, NativeProviderConfig, NativeProviderContext, NativeProxyDrainHandler, NativeProxyEgressInfo, NativeProxyExpiringEvent, NativeProxyExpiringReason, NativeTcpDynamicEgressRule, NativeTcpEgressGrant, NativeTcpEgressRule, NativeTcpPortRange, NativeTcpTlsMode, NativeTlsConnectOptions, OperationApprovalPolicy, OperationContractMetadata, OperationDefinition, OperationDocMeta, OperationErrorCode, ProviderErrorStatus, OperationInputExample, OperationLifecycle, OperationObservabilityConfig, OperationObservabilitySensitiveConfig, OperationRelationships, OperationRiskClass, OperationSensitivePath, OperationToolRouterMetadata, OperationTransport, ProviderAccessVisibility, ProviderChoiceBindingOptions, ProviderChoiceConsumeMode, ProviderChoiceConsumeResult, ProviderChoiceContext, ProviderChoiceExplicitParseResult, ProviderChoiceIssueOptions, ProviderChoiceParseOptions, ProviderContext, ProviderDefinition, ProviderDeploymentOverrides, ProviderFileRef, ProviderFilesContext, ProviderLocale, ProviderLocaleKey, ProviderLocaleKeyInput, ProviderLogoProfile, ProviderProxyPolicy, ProxiedOAuthConfig, ProviderPublicConnectionMode, ProviderPublicProfile, ProviderResolvedFile, ProviderRuntimeState, ProviderStateDurationString, ProviderStateNamespace, ProviderSupportLevel, RedirectRunReason, SchemaLike, SmsOtpMatcherDefinition, StandardSchemaV1, StateCasResult, StateNamespaceOptions, StateNamespaceScope, StateValue, StateWriteOptions, } from "./types.js";
11
+ export type { AuthAbortData, AuthAbortRetry, AuthFlowTerminalContext, AuthMode, AuthSafeData, AuthSafeJson, FlowContext, HealthCheckAssertionContext, HealthCheckCase, HealthCheckSuite, HealthCheckUnsupported, HealthJourneyDefinition, HealthJourneyEventContext, HealthJourneyManualTriggerPolicy, HealthJourneyRunContext, HealthJourneyRunResult, HealthScheduleRandomization, HttpRedirectFailureReason, HttpRedirectPolicy, HttpRedirectPolicyMode, HttpRetryOptions, HttpRetrySummary, InferSchemaOutput, NativeContext, NativeNetworkClient, NativeNetworkCloseReason, NativeNetworkConnection, NativeNetworkConnectInput, NativeNetworkConnectOptions, NativeNetworkDynamicGrantOptions, NativeNetworkEgressGrant, NativeProviderConfig, NativeProxyDrainHandler, NativeProxyEgressInfo, NativeProxyExpiringEvent, NativeProxyExpiringReason, NativeTcpDynamicEgressRule, NativeTcpEgressGrant, NativeTcpEgressRule, NativeTcpPortRange, NativeTcpTlsMode, NativeTlsConnectOptions, OperationApprovalPolicy, OperationContractMetadata, OperationDefinition, OperationDocMeta, OperationErrorCode, ProviderErrorStatus, OperationInputExample, OperationLifecycle, OperationObservabilityConfig, OperationObservabilitySensitiveConfig, OperationRelationships, OperationRiskClass, OperationSensitivePath, OperationToolRouterMetadata, OperationTransport, ProviderAccessVisibility, ProviderChoiceBindingOptions, ProviderChoiceConsumeMode, ProviderChoiceConsumeResult, ProviderChoiceContext, ProviderChoiceExplicitParseResult, ProviderChoiceIssueOptions, ProviderChoiceParseOptions, ProviderContext, ProviderContextFor, ProviderDefinition, ProviderDeploymentOverrides, ProviderFileRef, ProviderFilesContext, ProviderLocale, ProviderLocaleKey, ProviderLocaleKeyInput, ProviderLogoProfile, ProviderProxyPolicy, ProxiedOAuthConfig, ProviderPublicConnectionMode, ProviderPublicProfile, ProviderResolvedFile, ProviderRuntimeState, ProviderStateDurationString, ProviderStateNamespace, ProviderSupportLevel, RedirectRunReason, SchemaLike, SmsOtpMatcherDefinition, StandardSchemaV1, StateCasResult, StateNamespaceOptions, StateNamespaceScope, StateValue, StateWriteOptions, } from "./types.js";
11
12
  export { NativeEgressGrantExpiredError, NativeEgressNotDeclaredError, NativeIdleTimeoutError, NativeNetworkError, NativeProxyExpiredError, } from "./runtime/native-network-errors.js";
12
13
  export type { NativeGatewayProxy, NativeGatewayProxyResolutionInput, NativeGatewayProxySkipReason, NativeGatewayProxySynthesizer, NativeGatewayProxySynthesisResult, NativeGatewayProxySynthesisInput, NativeNetworkClientOptions, NativeNetworkErrorCode, VendorCredentialLookup, VendorCredentialResolver, } from "./runtime/native-network.js";
13
14
  export { HttpRetryAfterPolicy, HttpRetryDelayStrategy, HttpRetryJitter, HttpRetryPreset, HttpRetryUnsafeMethodPolicy, } from "./types.js";
@@ -548,8 +548,8 @@ class PlaywrightBrowserPage {
548
548
  this.page = page;
549
549
  this.proxy = proxy;
550
550
  }
551
- async goto(url) {
552
- await this.page.goto(url);
551
+ async goto(url, options) {
552
+ await this.page.goto(url, options);
553
553
  }
554
554
  async evaluate(fn) {
555
555
  if (typeof fn === "string") {
@@ -1101,16 +1101,22 @@ class CdpPoolBrowserPage {
1101
1101
  get id() {
1102
1102
  return this.pageId;
1103
1103
  }
1104
- async goto(url) {
1104
+ async goto(url, options) {
1105
1105
  await this.initialize();
1106
1106
  const startedAt = Date.now();
1107
- let loadEventSeen = false;
1108
- const unsubscribe = this.pageClient.on("Page.loadEventFired", () => {
1109
- loadEventSeen = true;
1107
+ const timeout = options?.timeout ?? DEFAULT_WAIT_TIMEOUT_MS;
1108
+ const waitUntil = options?.waitUntil ?? "load";
1109
+ let expectedEventSeen = false;
1110
+ const eventName = waitUntil === "domcontentloaded" ? "Page.domContentEventFired" : "Page.loadEventFired";
1111
+ const unsubscribe = this.pageClient.on(eventName, () => {
1112
+ expectedEventSeen = true;
1110
1113
  });
1111
1114
  try {
1112
- await this.pageClient.send("Page.navigate", { url });
1113
- await this.waitForDocumentReady(startedAt + DEFAULT_WAIT_TIMEOUT_MS, () => loadEventSeen);
1115
+ const navigation = (await this.pageClient.send("Page.navigate", { url }));
1116
+ if (typeof navigation.errorText === "string" && navigation.errorText.length > 0) {
1117
+ throw new Error(`Page.navigate failed: ${navigation.errorText} at ${url}`);
1118
+ }
1119
+ await this.waitForDocumentReady(startedAt + timeout, () => expectedEventSeen, waitUntil);
1114
1120
  }
1115
1121
  finally {
1116
1122
  unsubscribe();
@@ -1397,11 +1403,13 @@ class CdpPoolBrowserPage {
1397
1403
  this.frameExecutionContexts.set(frameId, contextId);
1398
1404
  return contextId;
1399
1405
  }
1400
- async waitForDocumentReady(deadline, isLoadEventSeen) {
1406
+ async waitForDocumentReady(deadline, isExpectedEventSeen, waitUntil) {
1401
1407
  while (Date.now() < deadline) {
1402
1408
  const readyState = await this.evaluate("document.readyState");
1403
- if (readyState === "complete" || readyState === "interactive") {
1404
- if (isLoadEventSeen() || readyState === "complete") {
1409
+ const documentReady = readyState === "complete" ||
1410
+ (waitUntil === "domcontentloaded" && readyState === "interactive");
1411
+ if (documentReady) {
1412
+ if (isExpectedEventSeen() || readyState === "complete") {
1405
1413
  return;
1406
1414
  }
1407
1415
  }
@@ -407,13 +407,6 @@ async function parseWordServerStoredChoice(options) {
407
407
  record.prefix !== options.parseOptions.prefix) {
408
408
  throw wordChoiceNotFoundError();
409
409
  }
410
- assertFreshProviderChoiceIssuedAt(record.issued_at_ms, {
411
- ttlMs: options.parseOptions.ttlMs != null
412
- ? Math.min(options.parseOptions.ttlMs, record.ttl_ms)
413
- : record.ttl_ms,
414
- nowMs: options.parseOptions.nowMs,
415
- futureToleranceMs: options.parseOptions.futureToleranceMs,
416
- });
417
410
  assertPayloadDigestMatches({
418
411
  actual: digestChoicePayload(serializeChoicePayload(record.payload)),
419
412
  expected: record.payload_digest,
@@ -434,6 +427,30 @@ async function parseWordServerStoredChoice(options) {
434
427
  }
435
428
  throw error;
436
429
  }
430
+ // Freshness is classified last, reachable only after every identity,
431
+ // integrity, and binding check above has passed (ADR 0006, amended
432
+ // 2026-08-20): a caller that proved the record's binding may observe the
433
+ // canonical stale error, while an unbound record keeps the collapsed
434
+ // not-found error so expiry never becomes an existence signal for
435
+ // guessable tokens.
436
+ try {
437
+ assertFreshProviderChoiceIssuedAt(record.issued_at_ms, {
438
+ ttlMs: options.parseOptions.ttlMs != null
439
+ ? Math.min(options.parseOptions.ttlMs, record.ttl_ms)
440
+ : record.ttl_ms,
441
+ nowMs: options.parseOptions.nowMs,
442
+ futureToleranceMs: options.parseOptions.futureToleranceMs,
443
+ });
444
+ }
445
+ catch (error) {
446
+ const recordIsBound = Boolean(record.binding?.connection_hash || record.binding?.credential_hash);
447
+ if (recordIsBound && error instanceof ProviderChoiceTokenError && error.reason === "stale") {
448
+ throw error;
449
+ }
450
+ if (error instanceof ProviderChoiceTokenError)
451
+ throw wordChoiceNotFoundError();
452
+ throw error;
453
+ }
437
454
  const consumeMode = options.parseOptions.consume ?? "never";
438
455
  if (record.status === "consumed") {
439
456
  if (consumeMode === "explicit") {
@@ -1 +1 @@
1
- export { APIFUSE__CDP_POOL__URL, APIFUSE__RESOLVER__2CAPTCHA__API_KEY, APIFUSE__RESOLVER__CAPMONSTER__API_KEY, APIFUSE__RESOLVER__CAPSOLVER__API_KEY, APIFUSE__RESOLVER__TIMEOUT_MS, bindResolverSignal, createResolverClient, createResolverClientFromEnv, createUnsupportedResolverClient, DEFAULT_RESOLVER_TIMEOUT_MS, invalidateResolverSolution, RESOLVER_ADAPTER_REGISTRY, RESOLVER_INSTRUMENTATION_METADATA, type ResolverAdapterFactory, type ResolverInstrumentationMetadata, type ResolverRuntimeOptions, } from "./resolver.js";
1
+ export { APIFUSE__CDP_POOL__URL, APIFUSE__RESOLVER__2CAPTCHA__API_KEY, APIFUSE__RESOLVER__CAPMONSTER__API_KEY, APIFUSE__RESOLVER__CAPSOLVER__API_KEY, APIFUSE__RESOLVER__TIMEOUT_MS, bindResolverSignal, createResolverClient, createResolverClientFromEnv, createUnsupportedResolverClient, DEFAULT_RESOLVER_VENDOR_PREFERENCE, DEFAULT_RESOLVER_TIMEOUT_MS, invalidateResolverSolution, RESOLVER_ADAPTER_REGISTRY, RESOLVER_INSTRUMENTATION_METADATA, resolveProviderResolverVendors, type ResolverAdapterFactory, type ResolverInstrumentationMetadata, type ResolverRuntimeOptions, } from "./resolver.js";
@@ -1 +1 @@
1
- export { APIFUSE__CDP_POOL__URL, APIFUSE__RESOLVER__2CAPTCHA__API_KEY, APIFUSE__RESOLVER__CAPMONSTER__API_KEY, APIFUSE__RESOLVER__CAPSOLVER__API_KEY, APIFUSE__RESOLVER__TIMEOUT_MS, bindResolverSignal, createResolverClient, createResolverClientFromEnv, createUnsupportedResolverClient, DEFAULT_RESOLVER_TIMEOUT_MS, invalidateResolverSolution, RESOLVER_ADAPTER_REGISTRY, RESOLVER_INSTRUMENTATION_METADATA, } from "./resolver.js";
1
+ export { APIFUSE__CDP_POOL__URL, APIFUSE__RESOLVER__2CAPTCHA__API_KEY, APIFUSE__RESOLVER__CAPMONSTER__API_KEY, APIFUSE__RESOLVER__CAPSOLVER__API_KEY, APIFUSE__RESOLVER__TIMEOUT_MS, bindResolverSignal, createResolverClient, createResolverClientFromEnv, createUnsupportedResolverClient, DEFAULT_RESOLVER_VENDOR_PREFERENCE, DEFAULT_RESOLVER_TIMEOUT_MS, invalidateResolverSolution, RESOLVER_ADAPTER_REGISTRY, RESOLVER_INSTRUMENTATION_METADATA, resolveProviderResolverVendors, } from "./resolver.js";