capskip 1.2.0 → 1.4.0

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.
@@ -0,0 +1,101 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Solve a Capy Puzzle captcha.
5
+ *
6
+ * Capy is a slide puzzle: a piece has to be dragged into the hole cut out of a
7
+ * photograph. CapSkip locates the hole and produces the drag path, which takes
8
+ * one HTTP fetch and about a tenth of a second of pixel math -- no browser.
9
+ *
10
+ * You need two values, both read off the target page:
11
+ *
12
+ * * captchakey - the site's public Capy key, prefixed `PUZZLE_`. Look for
13
+ * `capy_captchakey` in the page source, or read it out of the widget script
14
+ * URL: <script src="https://jp.api.capy.me/puzzle/get_js/?k=PUZZLE_XXXX">
15
+ * * pageurl - the full URL of the page the captcha is on
16
+ *
17
+ * Optionally `api_server`, the root of the Capy API the key lives behind, taken
18
+ * from that same script URL. CapSkip defaults to https://jp.api.capy.me.
19
+ * Note that api.capy.me no longer resolves, although several solver services
20
+ * still document it -- if a site's widget points elsewhere, pass that.
21
+ *
22
+ * Unlike reCAPTCHA or Turnstile, the answer is NOT a single token. It is three
23
+ * values that together go into the target form, and all three must be submitted.
24
+ */
25
+
26
+ const { CapSkip } = require('capskip');
27
+
28
+ const solver = new CapSkip({
29
+ apiKey: process.env.CAPSKIP_API_KEY || 'capskip',
30
+ host: process.env.CAPSKIP_HOST || '127.0.0.1',
31
+ port: Number(process.env.CAPSKIP_PORT || 8080),
32
+ });
33
+
34
+ const CAPTCHA_KEY = 'PUZZLE_Abc1dEFghIJKLM2no34P56q7rStu8v';
35
+ const PAGE_URL = 'https://example.com/login';
36
+
37
+ async function main() {
38
+ const result = await solver.capy(CAPTCHA_KEY, PAGE_URL);
39
+
40
+ console.log('Captcha ID: ', result.captchaId);
41
+ console.log('Captcha key: ', result.captchakey);
42
+ console.log('Challenge key:', result.challengekey);
43
+ console.log('Answer: ', `${result.answer.slice(0, 48)}...`);
44
+
45
+ // --- With a non-default API server ---------------------------------------
46
+ //
47
+ // await solver.capy(CAPTCHA_KEY, PAGE_URL, {
48
+ // api_server: 'https://jp.api.capy.me/',
49
+ // });
50
+ //
51
+ // --- With a per-request proxy --------------------------------------------
52
+ // The proxy is used for the puzzle-image fetch, which is the only request a
53
+ // Capy solve makes. Worth configuring once you solve at any volume: a steady
54
+ // stream of puzzle draws from one address is the pattern rate limiting exists
55
+ // to catch.
56
+ //
57
+ // await solver.capy(CAPTCHA_KEY, PAGE_URL, {
58
+ // proxy: { type: 'HTTP', uri: 'login:password@1.2.3.4:8080' },
59
+ // });
60
+
61
+ // Post all three values back in the fields the Capy widget would have filled
62
+ // in. Submit them verbatim -- `answer` is the drag path the widget would have
63
+ // recorded, and the site's backend verifies it against the challenge it
64
+ // issued, so trimming or re-encoding it invalidates the solve.
65
+ //
66
+ // await fetch(LOGIN_URL, {
67
+ // method: 'POST',
68
+ // body: new URLSearchParams({
69
+ // email: 'someone@example.com',
70
+ // capy_captchakey: result.captchakey,
71
+ // capy_challengekey: result.challengekey,
72
+ // capy_answer: result.answer,
73
+ // }),
74
+ // });
75
+ console.log('Form fields:', {
76
+ capy_captchakey: result.captchakey,
77
+ capy_challengekey: result.challengekey,
78
+ capy_answer: `${result.answer.slice(0, 32)}...`,
79
+ });
80
+
81
+ // The challenge key is single-use and short-lived: CapSkip generates it at
82
+ // solve time and the puzzle is bound to it. Submit promptly rather than
83
+ // caching these values or reusing a challengekey for a second submission.
84
+ //
85
+ // One more thing worth knowing if you ever build against Capy yourself: it
86
+ // grades the wall-clock gap between issuing the puzzle and verifying the
87
+ // answer, and refuses anything superhuman with "CAPTCHA verification failed"
88
+ // -- the same message a wrong answer gets. CapSkip therefore holds every
89
+ // result until two seconds have elapsed, so a solve takes about that long
90
+ // rather than a fifth of a second. Nothing to configure; just do not be
91
+ // surprised by the latency.
92
+
93
+ // `respKey` comes back empty for a puzzle solve. It exists for shape
94
+ // compatibility with 2Captcha's documented response and carries nothing.
95
+ console.log('respKey is empty:', result.respKey === '');
96
+ }
97
+
98
+ main().catch((error) => {
99
+ console.error(`${error.name}: ${error.message}`);
100
+ process.exitCode = 1;
101
+ });
@@ -0,0 +1,115 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Solve a Friendly Captcha proof-of-work challenge.
5
+ *
6
+ * Friendly Captcha asks the visitor's browser to do a piece of arithmetic
7
+ * instead of asking the visitor to do anything: there is no image, no slider and
8
+ * no audio fallback. CapSkip does that work and returns the token the widget
9
+ * would have written into your form.
10
+ *
11
+ * You need two values, and a third is worth sending whenever you can get it:
12
+ *
13
+ * * sitekey - the `data-sitekey` attribute of the widget element, which
14
+ * is the one carrying class="frc-captcha"
15
+ * * pageurl - the full URL of the page the widget appears on
16
+ * * moduleScript - the `src` of the widget script tag carrying type="module".
17
+ * Not required, but it is what tells CapSkip which protocol
18
+ * version the site uses.
19
+ *
20
+ * READ THE VERSION SECTION BELOW. It is the part that will cost you an afternoon
21
+ * if you skip it.
22
+ */
23
+
24
+ const { CapSkip } = require('capskip');
25
+
26
+ const solver = new CapSkip({
27
+ apiKey: process.env.CAPSKIP_API_KEY || 'capskip',
28
+ host: process.env.CAPSKIP_HOST || '127.0.0.1',
29
+ port: Number(process.env.CAPSKIP_PORT || 8080),
30
+ });
31
+
32
+ const SITEKEY = 'FCMGEMUD2M567T8G';
33
+ const PAGE_URL = 'https://example.com/signup';
34
+
35
+ async function main() {
36
+ // --- Version 1 and version 2 ---------------------------------------------
37
+ // Two entirely different protocols ship under this one name, and a sitekey
38
+ // does not tell you which one a site uses -- they share a brand and a sitekey
39
+ // namespace and nothing else. Both are live and both are in use. Solve the
40
+ // wrong one and you get a well-formed token that the target site rejects, with
41
+ // no indication anywhere that the version was the problem.
42
+ //
43
+ // v1 friendly-challenge widget.module.min.js / widget.min.js (default)
44
+ // v2 @friendlycaptcha/sdk site.min.js
45
+ //
46
+ // CapSkip decides in this order: an explicit `version`, then the script URL,
47
+ // then v1. So either say which version, or send the script URL and let it read
48
+ // the version off the build the site actually loads.
49
+
50
+ // Option A: say which version.
51
+ const result = await solver.friendlyCaptcha(SITEKEY, PAGE_URL, { version: 'v2' });
52
+
53
+ // Option B: let the script URL decide -- the most reliable signal there is.
54
+ //
55
+ // await solver.friendlyCaptcha(SITEKEY, PAGE_URL, {
56
+ // moduleScript: 'https://cdn.example.com/@friendlycaptcha/sdk@0.1.6/site.min.js',
57
+ // nomoduleScript: 'https://cdn.example.com/@friendlycaptcha/sdk@0.1.6/site.compat.js',
58
+ // });
59
+
60
+ console.log('Captcha ID:', result.captchaId);
61
+ console.log('Token: ', `${result.token.slice(0, 60)}...`);
62
+ console.log('Length: ', result.token.length);
63
+
64
+ // --- Data residency -------------------------------------------------------
65
+ // Pass api_server for a sitekey on the EU tenant. Both tenants mint a token
66
+ // for the same sitekey, so the wrong one is only caught by the site's own
67
+ // verification -- which is the worst kind of silent.
68
+ //
69
+ // await solver.friendlyCaptcha(SITEKEY, PAGE_URL, { version: 'v2', api_server: 'eu' });
70
+ //
71
+ // --- With a per-request proxy --------------------------------------------
72
+ // You will want one sooner here than almost anywhere else. The service decides
73
+ // how much work each request is worth and raises that figure for addresses it
74
+ // has already seen a lot of -- the published range between a fresh address and
75
+ // a heavily used one is close to thirty times the work for the same token.
76
+ //
77
+ // await solver.friendlyCaptcha(SITEKEY, PAGE_URL, {
78
+ // version: 'v2',
79
+ // proxy: { type: 'HTTP', uri: 'login:password@1.2.3.4:8080' },
80
+ // });
81
+
82
+ // Post the token back in the hidden field the widget would have filled in.
83
+ // THE TWO VERSIONS DO NOT USE THE SAME FIELD NAME, which is what catches
84
+ // people who move a working v1 integration onto a v2 site:
85
+ //
86
+ // v1 -> frc-captcha-solution
87
+ // v2 -> frc-captcha-response
88
+ //
89
+ // await fetch(SIGNUP_URL, {
90
+ // method: 'POST',
91
+ // body: new URLSearchParams({
92
+ // email: 'someone@example.com',
93
+ // 'frc-captcha-response': result.token, // v2
94
+ // }),
95
+ // });
96
+ console.log('Form field:', { 'frc-captcha-response': `${result.token.slice(0, 40)}...` });
97
+
98
+ // Submit the token verbatim. Do not trim it or re-encode it.
99
+ //
100
+ // The two versions produce tokens of very different shapes and sizes: a v1
101
+ // token is four dot-separated parts and runs to a few hundred characters,
102
+ // while a v2 token is a single opaque string beginning `AQQA.` and is roughly
103
+ // six kilobytes long. Make sure whatever carries it -- a hidden field, a
104
+ // database column, a proxied request -- is sized for that.
105
+
106
+ // Solve time is not a constant for this method: the service decides how much
107
+ // work a request is worth at the moment it is made, so the same sitekey can
108
+ // cost noticeably more at one time than another. This is why the method uses
109
+ // recaptchaTimeout rather than the shorter default.
110
+ }
111
+
112
+ main().catch((error) => {
113
+ console.error(`${error.name}: ${error.message}`);
114
+ process.exitCode = 1;
115
+ });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "capskip",
3
- "version": "1.2.0",
4
- "description": "Captcha solver for Node.js: solve reCAPTCHA v2/v3, Cloudflare Turnstile, GeeTest and image captchas with CapSkip, a local unlimited captcha solver with no per-solve fees.",
3
+ "version": "1.4.0",
4
+ "description": "Captcha solver for Node.js: solve reCAPTCHA v2/v3, Cloudflare Turnstile, GeeTest, AWS WAF and image captchas with CapSkip, a local unlimited captcha solver with no per-solve fees.",
5
5
  "main": "src/index.js",
6
6
  "types": "types/index.d.ts",
7
7
  "exports": {
@@ -39,6 +39,8 @@
39
39
  "cloudflare-turnstile",
40
40
  "geetest",
41
41
  "geetest-solver",
42
+ "aws-waf",
43
+ "aws-waf-captcha",
42
44
  "image-captcha",
43
45
  "ocr",
44
46
  "2captcha-alternative",
package/src/apiParams.js CHANGED
@@ -31,6 +31,43 @@ const ALTCHA_SUBMIT = new Set([
31
31
  'proxy', 'proxytype',
32
32
  ]);
33
33
 
34
+ const CAPY_SUBMIT = new Set([
35
+ 'method', 'captchakey', 'pageurl', 'api_server', 'version', 'useragent', 'json',
36
+ 'proxy', 'proxytype',
37
+ ]);
38
+
39
+ const CAPTCHAFOX_SUBMIT = new Set([
40
+ 'method', 'sitekey', 'pageurl', 'api_server', 'useragent', 'json',
41
+ 'proxy', 'proxytype',
42
+ ]);
43
+
44
+ const FRIENDLY_CAPTCHA_SUBMIT = new Set([
45
+ 'method', 'sitekey', 'pageurl', 'version', 'module_script', 'nomodule_script',
46
+ 'api_server', 'useragent', 'json', 'proxy', 'proxytype',
47
+ ]);
48
+
49
+ const AMAZON_WAF_SUBMIT = new Set([
50
+ 'method', 'sitekey', 'pageurl', 'iv', 'context', 'challenge_script',
51
+ 'captcha_script', 'jsapiScript', 'json', 'proxy', 'proxytype',
52
+ ]);
53
+
54
+ // The script parameters of an AWS WAF task. CapSkip POSTs the solution to the
55
+ // host these name, so it refuses any that is not a genuine AWS WAF one.
56
+ const AMAZON_WAF_SCRIPTS = ['challenge_script', 'captcha_script', 'jsapiScript'];
57
+
58
+ // The server's own test: one or more [A-Za-z0-9_-] path segments, then one of
59
+ // the three AWS WAF script files.
60
+ const AMAZON_WAF_SCRIPT_PATH = /^(\/[A-Za-z0-9_-]+)+\/(challenge|captcha|jsapi)\.js$/;
61
+
62
+ // CapSkip solves the puzzle family only. `avatar` is a different challenge
63
+ // behind a different endpoint; the server refuses it at submit time rather than
64
+ // answering it with a puzzle answer, which would bill for a solve the target
65
+ // site rejects.
66
+ const CAPY_VERSIONS = ['puzzle'];
67
+
68
+ // Both spellings the server accepts, and the bare digits it also takes.
69
+ const FRIENDLY_CAPTCHA_VERSIONS = ['v1', 'v2', '1', '2'];
70
+
34
71
  // The only values CapSkip maps to a proxy scheme; it answers
35
72
  // ERROR_BAD_PARAMETERS for anything else, SOCKS4 included. Matched
36
73
  // case-insensitively, as the server does.
@@ -48,6 +85,21 @@ const PARAM_ALIASES = {
48
85
  challengeURL: 'challenge_url',
49
86
  challengeJson: 'challenge_json',
50
87
  challengeJSON: 'challenge_json',
88
+ // The server reads userAgent and useragent interchangeably on every method
89
+ // that takes one, so the SDK settles on the lowercase spelling its parameter
90
+ // tables document and accepts the camelCase one callers arrive with.
91
+ userAgent: 'useragent',
92
+ user_agent: 'useragent',
93
+ captchaKey: 'captchakey',
94
+ moduleScript: 'module_script',
95
+ nomoduleScript: 'nomodule_script',
96
+ noModuleScript: 'nomodule_script',
97
+ // AWS WAF: the API documents challenge_script / captcha_script in snake case
98
+ // and jsapiScript in camel case, as 2Captcha does. Accept the other spelling
99
+ // of each, which is what createTask-style callers arrive with.
100
+ challengeScript: 'challenge_script',
101
+ captchaScript: 'captcha_script',
102
+ jsapi_script: 'jsapiScript',
51
103
  };
52
104
 
53
105
  function has(obj, key) {
@@ -233,6 +285,177 @@ function validateAltchaSubmit(params) {
233
285
  }
234
286
  }
235
287
 
288
+ /**
289
+ * Drop parameters left unset so an omitted optional is not sent as "undefined".
290
+ *
291
+ * The form body can only carry strings, so a default of undefined would
292
+ * otherwise reach the server stringified. Mirrors the server, which reads a
293
+ * JSON-body `null` as "not sent".
294
+ */
295
+ function dropUnset(params) {
296
+ const out = {};
297
+ for (const [key, value] of Object.entries(params)) {
298
+ if (value !== undefined && value !== null) {
299
+ out[key] = value;
300
+ }
301
+ }
302
+ return out;
303
+ }
304
+
305
+ function validateCapySubmit(params) {
306
+ // Both are documented as required; without captchakey the server answers
307
+ // ERROR_BAD_PARAMETERS and without pageurl ERROR_PAGEURL. Fail locally so a
308
+ // missing value does not cost a round-trip.
309
+ for (const key of ['captchakey', 'pageurl']) {
310
+ if (!params[key]) {
311
+ throw new ValidationException(`'${key}' is required for Capy.`);
312
+ }
313
+ }
314
+
315
+ const version = params.version;
316
+ if (version !== undefined && version !== null && version !== ''
317
+ && !CAPY_VERSIONS.includes(String(version).toLowerCase())) {
318
+ throw new ValidationException(
319
+ `Unsupported Capy version '${version}'. CapSkip solves the puzzle family `
320
+ + "only -- 'avatar' is a different challenge behind a different endpoint, "
321
+ + 'and the server refuses it rather than returning a puzzle answer the '
322
+ + 'target site would reject.',
323
+ );
324
+ }
325
+
326
+ const unknown = unknownKeys(params, CAPY_SUBMIT);
327
+ if (unknown.length > 0) {
328
+ throw new ValidationException(
329
+ `Unsupported parameters for Capy: ${reprList(unknown)}.`,
330
+ );
331
+ }
332
+ }
333
+
334
+ function validateCaptchaFoxSubmit(params) {
335
+ for (const key of ['sitekey', 'pageurl']) {
336
+ if (!params[key]) {
337
+ throw new ValidationException(`'${key}' is required for CaptchaFox.`);
338
+ }
339
+ }
340
+
341
+ const unknown = unknownKeys(params, CAPTCHAFOX_SUBMIT);
342
+ if (unknown.length > 0) {
343
+ throw new ValidationException(
344
+ `Unsupported parameters for CaptchaFox: ${reprList(unknown)}.`,
345
+ );
346
+ }
347
+ }
348
+
349
+ function validateFriendlyCaptchaSubmit(params) {
350
+ for (const key of ['sitekey', 'pageurl']) {
351
+ if (!params[key]) {
352
+ throw new ValidationException(`'${key}' is required for Friendly Captcha.`);
353
+ }
354
+ }
355
+
356
+ const version = params.version;
357
+ if (version !== undefined && version !== null && version !== ''
358
+ && !FRIENDLY_CAPTCHA_VERSIONS.includes(String(version).toLowerCase())) {
359
+ throw new ValidationException(
360
+ `Unsupported Friendly Captcha version '${version}'. Use 'v1' or 'v2' `
361
+ + '(a bare 1 or 2 is accepted too). The two are different protocols '
362
+ + 'sharing one sitekey namespace, so solving the wrong one returns a '
363
+ + 'well-formed token the target site rejects.',
364
+ );
365
+ }
366
+
367
+ const unknown = unknownKeys(params, FRIENDLY_CAPTCHA_SUBMIT);
368
+ if (unknown.length > 0) {
369
+ throw new ValidationException(
370
+ `Unsupported parameters for Friendly Captcha: ${reprList(unknown)}.`,
371
+ );
372
+ }
373
+ }
374
+
375
+ /**
376
+ * True for a script URL CapSkip will accept on an AWS WAF task.
377
+ *
378
+ * Mirrors the server: https, a host under .awswaf.com, no credentials or
379
+ * explicit port, and a path ending in challenge.js, captcha.js or jsapi.js.
380
+ * The server only applies the check once the task is queued, so failing here
381
+ * saves a submit that could never solve.
382
+ */
383
+ function isAmazonWafScriptUrl(url) {
384
+ let parsed;
385
+ try {
386
+ parsed = new URL(String(url).trim());
387
+ } catch (error) {
388
+ return false;
389
+ }
390
+ // WHATWG URL already lowercases the scheme and host, and drops a :443 on an
391
+ // https URL, so `port` is empty for both the default and an explicit 443.
392
+ return parsed.protocol === 'https:'
393
+ && parsed.hostname.toLowerCase().endsWith('.awswaf.com')
394
+ && parsed.username === ''
395
+ && parsed.password === ''
396
+ && (parsed.port === '' || parsed.port === '443')
397
+ && AMAZON_WAF_SCRIPT_PATH.test(parsed.pathname);
398
+ }
399
+
400
+ /**
401
+ * Drop unset optionals, and the empty ones too: an AWS WAF optional left as ''
402
+ * means "not sent" to the server, exactly like an absent one, so it is dropped
403
+ * rather than tripping the iv/context pairing.
404
+ */
405
+ function normalizeAmazonWafSubmit(params) {
406
+ const out = {};
407
+ for (const [key, value] of Object.entries(dropUnset(params))) {
408
+ if (value !== '') {
409
+ out[key] = value;
410
+ }
411
+ }
412
+ return out;
413
+ }
414
+
415
+ function validateAmazonWafSubmit(params) {
416
+ // Without sitekey the server answers ERROR_BAD_PARAMETERS and without
417
+ // pageurl ERROR_PAGEURL. Fail locally so a missing value costs no round-trip.
418
+ for (const key of ['sitekey', 'pageurl']) {
419
+ if (!params[key]) {
420
+ throw new ValidationException(`'${key}' is required for Amazon WAF.`);
421
+ }
422
+ }
423
+
424
+ const pageurl = String(params.pageurl).trim();
425
+ if (!/^https?:\/\//i.test(pageurl)) {
426
+ throw new ValidationException(
427
+ `'pageurl' must be a full http or https URL for Amazon WAF, got '${pageurl}'.`,
428
+ );
429
+ }
430
+
431
+ // iv and context come off the same challenge page and only work as a pair;
432
+ // the server refuses one without the other with ERROR_BAD_PARAMETERS.
433
+ if (Boolean(params.iv) !== Boolean(params.context)) {
434
+ throw new ValidationException(
435
+ "Amazon WAF 'iv' and 'context' go together: send both (a challenge "
436
+ + 'page) or neither (a widget page, or let CapSkip read them from pageurl).',
437
+ );
438
+ }
439
+
440
+ for (const key of AMAZON_WAF_SCRIPTS) {
441
+ const value = params[key];
442
+ if (value && !isAmazonWafScriptUrl(value)) {
443
+ throw new ValidationException(
444
+ `'${key}' must be the https URL of an AWS WAF script on an awswaf.com `
445
+ + `host (challenge.js, captcha.js or jsapi.js), got '${value}'. CapSkip `
446
+ + 'refuses any other script, because it is not an AWS WAF integration.',
447
+ );
448
+ }
449
+ }
450
+
451
+ const unknown = unknownKeys(params, AMAZON_WAF_SUBMIT);
452
+ if (unknown.length > 0) {
453
+ throw new ValidationException(
454
+ `Unsupported parameters for Amazon WAF: ${reprList(unknown)}.`,
455
+ );
456
+ }
457
+ }
458
+
236
459
  function validateProxyType(params) {
237
460
  const proxytype = params.proxytype;
238
461
  if (proxytype === undefined || proxytype === null || proxytype === '') {
@@ -261,6 +484,18 @@ function prepareSubmitParams(params, captchaType, version = 'v2') {
261
484
  } else if (captchaType === 'altcha') {
262
485
  prepared = normalizeAltchaSubmit(prepared);
263
486
  validateAltchaSubmit(prepared);
487
+ } else if (captchaType === 'capy') {
488
+ prepared = dropUnset(prepared);
489
+ validateCapySubmit(prepared);
490
+ } else if (captchaType === 'captchafox') {
491
+ prepared = dropUnset(prepared);
492
+ validateCaptchaFoxSubmit(prepared);
493
+ } else if (captchaType === 'friendly_captcha') {
494
+ prepared = dropUnset(prepared);
495
+ validateFriendlyCaptchaSubmit(prepared);
496
+ } else if (captchaType === 'amazon_waf') {
497
+ prepared = normalizeAmazonWafSubmit(prepared);
498
+ validateAmazonWafSubmit(prepared);
264
499
  }
265
500
 
266
501
  // Skipped for 'normal', which rejects proxy outright with a clearer message.
@@ -278,6 +513,13 @@ module.exports = {
278
513
  TURNSTILE_SUBMIT,
279
514
  GEETEST_SUBMIT,
280
515
  ALTCHA_SUBMIT,
516
+ CAPY_SUBMIT,
517
+ CAPTCHAFOX_SUBMIT,
518
+ FRIENDLY_CAPTCHA_SUBMIT,
519
+ AMAZON_WAF_SUBMIT,
520
+ AMAZON_WAF_SCRIPTS,
521
+ CAPY_VERSIONS,
522
+ FRIENDLY_CAPTCHA_VERSIONS,
281
523
  PROXY_TYPES,
282
524
  PARAM_ALIASES,
283
525
  applyParamAliases,
@@ -288,6 +530,13 @@ module.exports = {
288
530
  validateGeetestSubmit,
289
531
  normalizeAltchaSubmit,
290
532
  validateAltchaSubmit,
533
+ dropUnset,
534
+ validateCapySubmit,
535
+ validateCaptchaFoxSubmit,
536
+ validateFriendlyCaptchaSubmit,
537
+ isAmazonWafScriptUrl,
538
+ normalizeAmazonWafSubmit,
539
+ validateAmazonWafSubmit,
291
540
  validateProxyType,
292
541
  prepareSubmitParams,
293
542
  };
package/src/index.js CHANGED
@@ -27,5 +27,5 @@ module.exports = {
27
27
  NetworkException,
28
28
  ApiException,
29
29
  TimeoutException,
30
- version: '1.1.0',
30
+ version: '1.4.0',
31
31
  };