@apifuse/provider-sdk 2.2.0-beta.24 → 2.2.0-beta.26
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 +7 -6
- package/CHANGELOG.md +9 -1
- package/README.md +3 -3
- package/bin/apifuse-check.ts +62 -3
- package/bin/apifuse-pack-check.ts +8 -2
- package/bin/apifuse-pack-smoke.ts +43 -2
- package/bin/apifuse-pack-types.ts +58 -0
- package/bin/apifuse-submit-check.ts +15 -2
- package/dist/auth.js +29 -0
- package/dist/cli/templates/provider/README.md.tpl +4 -4
- package/dist/contract-serialization.d.ts +20 -1
- package/dist/contract-serialization.js +583 -8
- package/dist/contract.d.ts +2 -0
- package/dist/contract.js +9 -5
- package/dist/declaration-validation.d.ts +23 -0
- package/dist/declaration-validation.js +159 -0
- package/dist/define.d.ts +1 -1
- package/dist/define.js +13 -2
- package/dist/index.d.ts +4 -3
- package/dist/index.js +3 -3
- package/dist/lint.js +85 -3
- package/dist/provider.d.ts +1 -1
- package/dist/provider.js +1 -1
- package/dist/runtime/cache.d.ts +1 -0
- package/dist/runtime/cache.js +169 -15
- package/dist/runtime/resolver-vendors/bindings.d.ts +42 -2
- package/dist/runtime/resolver-vendors/bindings.js +31 -6
- package/dist/runtime/resolver-vendors/browser.d.ts +3 -7
- package/dist/runtime/resolver-vendors/browser.js +7 -22
- package/dist/runtime/resolver-vendors/hosts.d.ts +2 -0
- package/dist/runtime/resolver-vendors/hosts.js +33 -0
- package/dist/runtime/resolver-vendors/twocaptcha.d.ts +23 -0
- package/dist/runtime/resolver-vendors/twocaptcha.js +264 -0
- package/dist/runtime/resolver-vendors/types.d.ts +44 -3
- package/dist/runtime/resolver-vendors/types.js +10 -0
- package/dist/runtime/resolver.d.ts +17 -2
- package/dist/runtime/resolver.js +237 -15
- package/dist/runtime/stealth.d.ts +26 -4
- package/dist/runtime/stealth.js +224 -114
- package/dist/schema.d.ts +63 -0
- package/dist/schema.js +808 -8
- package/dist/server/serve.js +8 -0
- package/dist/stealth/profiles.js +16 -7
- package/dist/types.d.ts +37 -4
- package/package.json +2 -2
- package/src/auth.ts +40 -0
- package/src/cli/templates/provider/README.md.tpl +4 -4
- package/src/contract-serialization.ts +857 -8
- package/src/contract.ts +16 -5
- package/src/declaration-validation.ts +202 -0
- package/src/define.ts +23 -2
- package/src/index.ts +13 -0
- package/src/lint.ts +98 -3
- package/src/provider.ts +10 -0
- package/src/runtime/cache.ts +189 -14
- package/src/runtime/resolver-vendors/bindings.ts +40 -15
- package/src/runtime/resolver-vendors/browser.ts +9 -31
- package/src/runtime/resolver-vendors/hosts.ts +38 -0
- package/src/runtime/resolver-vendors/twocaptcha.ts +366 -0
- package/src/runtime/resolver-vendors/types.ts +54 -0
- package/src/runtime/resolver.ts +304 -24
- package/src/runtime/stealth.ts +317 -136
- package/src/schema.ts +1060 -9
- package/src/server/serve.ts +8 -0
- package/src/stealth/profiles.ts +17 -7
- package/src/types.ts +39 -6
package/dist/server/serve.js
CHANGED
|
@@ -3,6 +3,7 @@ import { join } from "node:path";
|
|
|
3
3
|
import { Hono } from "hono";
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
import { AuthAbortError, createAuthFlowHelpers } from "../auth.js";
|
|
6
|
+
import { validateFailClosedDeclaration } from "../declaration-validation.js";
|
|
6
7
|
import { SDK_OWNED_PROVIDER_ERROR_CODES, SDK_RUNTIME_OWNED_ERROR_CODES, SDK_STATUS_MAPPED_PROVIDER_ERROR_CODES, } from "../error-resolution.js";
|
|
7
8
|
import { AuthError, isProviderError, isSessionExpiredError, isTransportError, isValidationError, ProviderError, } from "../errors.js";
|
|
8
9
|
import { loadProviderLocaleCatalogs, localizeAuthTurn, } from "../i18n/catalog.js";
|
|
@@ -1298,6 +1299,11 @@ function parseStatefulForwardingEnvelope(rawBody) {
|
|
|
1298
1299
|
});
|
|
1299
1300
|
}
|
|
1300
1301
|
export function createServerApp(provider, options = {}) {
|
|
1302
|
+
// Fail-closed validation runs here rather than only in serve(): createServerApp
|
|
1303
|
+
// is a public export, so a cast-bypassed declaration would otherwise reach the
|
|
1304
|
+
// request path unvalidated. serve() calls into this function, so validating
|
|
1305
|
+
// here covers both entry points exactly once per app construction.
|
|
1306
|
+
validateFailClosedDeclaration(provider);
|
|
1301
1307
|
validateStatefulServerConfig(options);
|
|
1302
1308
|
const app = new Hono();
|
|
1303
1309
|
const logger = options.logger ?? defaultProviderServerLogger;
|
|
@@ -1626,6 +1632,8 @@ const DEFAULT_SHUTDOWN_TIMEOUT_MS = 30_000;
|
|
|
1626
1632
|
const DEFAULT_SHUTDOWN_SIGNALS = ["SIGTERM", "SIGINT"];
|
|
1627
1633
|
const processSignalCoordinators = new Map();
|
|
1628
1634
|
export async function serve(provider, options = {}) {
|
|
1635
|
+
// Declaration validation happens inside createServerApp (the shared app
|
|
1636
|
+
// construction path), so serve() does not duplicate the call here.
|
|
1629
1637
|
const bunRuntime = getBunServeRuntime();
|
|
1630
1638
|
if (bunRuntime === undefined) {
|
|
1631
1639
|
throw new ProviderError("Bun runtime is required to start the provider server", {
|
package/dist/stealth/profiles.js
CHANGED
|
@@ -57,7 +57,7 @@ const FIREFOX_H2_SETTINGS = {
|
|
|
57
57
|
MAX_HEADER_LIST_SIZE: 65536,
|
|
58
58
|
};
|
|
59
59
|
const SAFARI_H2_SETTINGS = {
|
|
60
|
-
|
|
60
|
+
// Safari 17 also sends a connection-level WINDOW_UPDATE increment of 10485760.
|
|
61
61
|
ENABLE_PUSH: 0,
|
|
62
62
|
INITIAL_WINDOW_SIZE: 4194304,
|
|
63
63
|
MAX_CONCURRENT_STREAMS: 100,
|
|
@@ -162,8 +162,8 @@ const STEALTH_PROFILES = {
|
|
|
162
162
|
}),
|
|
163
163
|
"firefox-132": createProfile("firefox-132", {
|
|
164
164
|
platform: "macos",
|
|
165
|
-
version: "
|
|
166
|
-
userAgent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:
|
|
165
|
+
version: "133.0",
|
|
166
|
+
userAgent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:133.0) Gecko/20100101 Firefox/133.0",
|
|
167
167
|
tlsClientIdentifier: "firefox_132",
|
|
168
168
|
ja3: FIREFOX_JA3,
|
|
169
169
|
h2Settings: FIREFOX_H2_SETTINGS,
|
|
@@ -178,6 +178,15 @@ const STEALTH_PROFILES = {
|
|
|
178
178
|
h2Settings: SAFARI_H2_SETTINGS,
|
|
179
179
|
headerOrder: SAFARI_HEADER_ORDER,
|
|
180
180
|
}),
|
|
181
|
+
"safari-17": createProfile("safari-17", {
|
|
182
|
+
platform: "macos",
|
|
183
|
+
version: "17.0",
|
|
184
|
+
userAgent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Safari/605.1.15",
|
|
185
|
+
tlsClientIdentifier: "safari_17_0",
|
|
186
|
+
ja3: SAFARI_JA3,
|
|
187
|
+
h2Settings: SAFARI_H2_SETTINGS,
|
|
188
|
+
headerOrder: SAFARI_HEADER_ORDER,
|
|
189
|
+
}),
|
|
181
190
|
"safari-15": createProfile("safari-15", {
|
|
182
191
|
platform: "macos",
|
|
183
192
|
version: "15.6.1",
|
|
@@ -198,8 +207,8 @@ const STEALTH_PROFILES = {
|
|
|
198
207
|
}),
|
|
199
208
|
"ios-safari-18": createProfile("ios-safari-18", {
|
|
200
209
|
platform: "ios",
|
|
201
|
-
version: "18.
|
|
202
|
-
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS
|
|
210
|
+
version: "18.1.1",
|
|
211
|
+
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 18_1_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.1.1 Mobile/15E148 Safari/604.1",
|
|
203
212
|
tlsClientIdentifier: "safari_ios_18_0",
|
|
204
213
|
ja3: SAFARI_JA3,
|
|
205
214
|
h2Settings: SAFARI_H2_SETTINGS,
|
|
@@ -207,8 +216,8 @@ const STEALTH_PROFILES = {
|
|
|
207
216
|
}),
|
|
208
217
|
"ios-safari-17": createProfile("ios-safari-17", {
|
|
209
218
|
platform: "ios",
|
|
210
|
-
version: "17.
|
|
211
|
-
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS
|
|
219
|
+
version: "17.2",
|
|
220
|
+
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 17_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.2 Mobile/15E148 Safari/604.1",
|
|
212
221
|
tlsClientIdentifier: "safari_ios_17_0",
|
|
213
222
|
ja3: SAFARI_JA3,
|
|
214
223
|
h2Settings: SAFARI_H2_SETTINGS,
|
package/dist/types.d.ts
CHANGED
|
@@ -298,9 +298,27 @@ export type ProviderChallenge = {
|
|
|
298
298
|
} | {
|
|
299
299
|
readonly kind: "aws_waf";
|
|
300
300
|
readonly pageUrl: string;
|
|
301
|
+
/** `window.gokuProps.key`; solver vendors require it, while `"browser"` does not. */
|
|
302
|
+
readonly siteKey?: string;
|
|
301
303
|
readonly captchaScript?: string;
|
|
302
304
|
readonly context?: string;
|
|
303
305
|
readonly iv?: string;
|
|
306
|
+
} | {
|
|
307
|
+
readonly kind: "akamai_sec_cpt";
|
|
308
|
+
readonly pageUrl: string;
|
|
309
|
+
/** The admitted challenge document, needed for tile/context extraction. */
|
|
310
|
+
readonly challengeHtml?: string;
|
|
311
|
+
} | {
|
|
312
|
+
readonly kind: "akamai_sensor";
|
|
313
|
+
readonly pageUrl: string;
|
|
314
|
+
/** Upstream sensor script URL the payload must be POSTed to. */
|
|
315
|
+
readonly scriptUrl: string;
|
|
316
|
+
/** Current `_abck` cookie value, rotates each round. */
|
|
317
|
+
readonly abck?: string;
|
|
318
|
+
/** Current `bm_sz` / `ak_bmsc` value when the upstream set one. */
|
|
319
|
+
readonly bmsz?: string;
|
|
320
|
+
/** Bot Manager major version when known ("3" measured on zozo.jp). */
|
|
321
|
+
readonly version?: string;
|
|
304
322
|
};
|
|
305
323
|
export type ProviderChallengeKind = ProviderChallenge["kind"];
|
|
306
324
|
/**
|
|
@@ -317,12 +335,21 @@ export type ChallengeSolution = {
|
|
|
317
335
|
readonly form: "cookies";
|
|
318
336
|
readonly cookies: Readonly<Record<string, string>>;
|
|
319
337
|
readonly userAgent: string;
|
|
338
|
+
/** Epoch seconds copied from the upstream cookie's own expiry attribute; never a constant. */
|
|
339
|
+
readonly expires?: number;
|
|
320
340
|
};
|
|
321
341
|
export interface ProviderResolverConfig {
|
|
322
342
|
/** Ordered vendor fallback chain, tried first to last. */
|
|
323
343
|
readonly vendors: readonly ProviderResolverVendor[];
|
|
324
344
|
/** Challenge kinds this provider is permitted to request. */
|
|
325
345
|
readonly kinds: readonly ProviderChallengeKind[];
|
|
346
|
+
/**
|
|
347
|
+
* Client fingerprint profile the SDK must use when reaching this upstream.
|
|
348
|
+
* Measured on zozo.jp: Chrome/Firefox profiles are refused 403 before any
|
|
349
|
+
* challenge is served, while a Safari profile is admitted. Provider-declared
|
|
350
|
+
* because only the provider knows its upstream's admission rule.
|
|
351
|
+
*/
|
|
352
|
+
readonly clientProfile?: string;
|
|
326
353
|
}
|
|
327
354
|
export type SttAudioInput = {
|
|
328
355
|
kind: "base64";
|
|
@@ -534,7 +561,13 @@ export interface HealthJourneyDefinition {
|
|
|
534
561
|
requiredSecrets?: readonly string[];
|
|
535
562
|
manualTrigger?: HealthJourneyManualTriggerPolicy;
|
|
536
563
|
steps: readonly [HealthJourneyStep, ...HealthJourneyStep[]];
|
|
537
|
-
|
|
564
|
+
/**
|
|
565
|
+
* Required: a journey always declares `coversOperations`, and the health
|
|
566
|
+
* monitor reports a run-less journey as `journey_run_missing`. Declaration
|
|
567
|
+
* validation rejects a missing `run` (`health-journey-executable`), so this
|
|
568
|
+
* is typed required to fail at compile time rather than at boot.
|
|
569
|
+
*/
|
|
570
|
+
run: (ctx: HealthJourneyRunContext) => Promise<HealthJourneyRunResult | undefined>;
|
|
538
571
|
}
|
|
539
572
|
/**
|
|
540
573
|
* Health-check authoring surface owned by `@apifuse/provider-sdk`.
|
|
@@ -1332,9 +1365,9 @@ export interface NativeProviderConfig {
|
|
|
1332
1365
|
}
|
|
1333
1366
|
export interface ProviderCacheKeyOptions {
|
|
1334
1367
|
/**
|
|
1335
|
-
* Additional field names
|
|
1336
|
-
*
|
|
1337
|
-
* cookie, token, password, and secret.
|
|
1368
|
+
* Additional field names whose values are hashed in stable key material. The
|
|
1369
|
+
* SDK always hashes values under known secret-bearing names such as serviceKey,
|
|
1370
|
+
* authorization, cookie, token, password, and secret.
|
|
1338
1371
|
*/
|
|
1339
1372
|
redactFields?: string[];
|
|
1340
1373
|
}
|
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "2.2.0-beta.
|
|
2
|
+
"version": "2.2.0-beta.26",
|
|
3
3
|
"name": "@apifuse/provider-sdk",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
@@ -106,7 +106,6 @@
|
|
|
106
106
|
"acorn": "^8.17.0",
|
|
107
107
|
"ajv": "^8.17",
|
|
108
108
|
"hono": "^4.12.25",
|
|
109
|
-
"impit": "0.14.1",
|
|
110
109
|
"ioredis": "^5.11.1",
|
|
111
110
|
"ms": "^2.1.3",
|
|
112
111
|
"playwright": "^1.55.1",
|
|
@@ -116,6 +115,7 @@
|
|
|
116
115
|
"safe-regex": "^2.1",
|
|
117
116
|
"socks": "^2.8.9",
|
|
118
117
|
"tough-cookie": "^6.0.2",
|
|
118
|
+
"wreq-js": "3.0.0",
|
|
119
119
|
"zod": "^4.4.3"
|
|
120
120
|
},
|
|
121
121
|
"repository": {
|
package/src/auth.ts
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
import { AuthError, ProviderError } from "./errors.js";
|
|
2
|
+
import {
|
|
3
|
+
declarationInvalidError,
|
|
4
|
+
DECLARATION_RULE_IDS,
|
|
5
|
+
type DeclarationViolation,
|
|
6
|
+
} from "./declaration-validation.js";
|
|
2
7
|
import type {
|
|
3
8
|
AuthAbortData,
|
|
4
9
|
AuthConfig,
|
|
@@ -656,6 +661,7 @@ export function defineCredentialsAuth<
|
|
|
656
661
|
string,
|
|
657
662
|
CredentialsAuthChallengeDefinition<CredentialsAuthFields, TCredentialKeys, string>
|
|
658
663
|
>;
|
|
664
|
+
validateCredentialsAuthChallenges(challenges);
|
|
659
665
|
|
|
660
666
|
return {
|
|
661
667
|
auth: {
|
|
@@ -726,3 +732,37 @@ export function defineCredentialsAuth<
|
|
|
726
732
|
},
|
|
727
733
|
};
|
|
728
734
|
}
|
|
735
|
+
|
|
736
|
+
function validateCredentialsAuthChallenges(
|
|
737
|
+
challenges: Record<
|
|
738
|
+
string,
|
|
739
|
+
CredentialsAuthChallengeDefinition<CredentialsAuthFields, readonly string[], string>
|
|
740
|
+
>,
|
|
741
|
+
): void {
|
|
742
|
+
const violations: DeclarationViolation[] = [];
|
|
743
|
+
for (const [challengeId, challenge] of Object.entries(challenges)) {
|
|
744
|
+
const fieldCount =
|
|
745
|
+
challenge.fields && typeof challenge.fields === "object"
|
|
746
|
+
? Object.keys(challenge.fields).length
|
|
747
|
+
: 0;
|
|
748
|
+
const fieldsDeclared =
|
|
749
|
+
challenge.fields !== undefined && challenge.fields !== null;
|
|
750
|
+
const hasFields = fieldCount > 0;
|
|
751
|
+
const hasVerify = typeof challenge.verify === "function";
|
|
752
|
+
const hasPoll = typeof challenge.poll === "function";
|
|
753
|
+
const isInteractive = hasFields && hasVerify && !hasPoll;
|
|
754
|
+
const isPolling = !fieldsDeclared && !hasVerify && hasPoll;
|
|
755
|
+
const isHybrid = hasFields && hasVerify && hasPoll;
|
|
756
|
+
const emptyFieldsDeclared = fieldsDeclared && !hasFields;
|
|
757
|
+
if (!emptyFieldsDeclared && (isInteractive || isPolling || isHybrid)) continue;
|
|
758
|
+
|
|
759
|
+
const path = `challenges.${challengeId}`;
|
|
760
|
+
violations.push({
|
|
761
|
+
ruleId: DECLARATION_RULE_IDS.challengeShape,
|
|
762
|
+
path,
|
|
763
|
+
message: "challenge must be interactive, polling, or an explicit hybrid.",
|
|
764
|
+
fix: `Give ${path} non-empty fields plus verify, poll alone, or all three for a hybrid.`,
|
|
765
|
+
});
|
|
766
|
+
}
|
|
767
|
+
if (violations.length > 0) throw declarationInvalidError(violations);
|
|
768
|
+
}
|
|
@@ -114,10 +114,10 @@ Structured errors return an `error` object with `code`, `message`,
|
|
|
114
114
|
- Auth flow: call `/auth/start`, then `/auth/continue` with the same `flowId`;
|
|
115
115
|
carry returned `contextPatch` values into the next request's `context`.
|
|
116
116
|
- Stealth/browser runtime: keep access-sensitive operations on `ctx.stealth.fetch()` with an
|
|
117
|
-
SDK stealth `profile`; the TypeScript stealth runtime uses `
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
(`nodriver` is Python-runtime only)
|
|
117
|
+
SDK stealth `profile`; the TypeScript stealth runtime uses `wreq-js` internally
|
|
118
|
+
and supports Chrome, Firefox, and Safari profiles. Use `ctx.browser` only when
|
|
119
|
+
the provider needs browser execution; TypeScript browser Providers use
|
|
120
|
+
`browser.engine: "playwright-stealth"` (`nodriver` is Python-runtime only). Install local Chromium with
|
|
121
121
|
`bunx playwright install chromium` or set `APIFUSE__CDP_POOL__URL`.
|
|
122
122
|
|
|
123
123
|
## Next steps
|