@thetypefounders/continue-with-google 1.4.3 → 1.5.2

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/.env-example ADDED
@@ -0,0 +1,3 @@
1
+ GOOGLE_USER_EMAIL=test@thetypefounders.com
2
+ GOOGLE_USER_PASSWORD=
3
+ GOOGLE_USER_SECRET=
@@ -32,4 +32,6 @@ jobs:
32
32
  with:
33
33
  node-version: 20.x
34
34
  - run: npm install
35
- - run: npm run test
35
+ - run: |
36
+ echo "${{ secrets.ENVIRONMENT }}" | base64 --decode > .env
37
+ npm run test
package/dist/index.d.ts CHANGED
@@ -1,7 +1,8 @@
1
1
  import { type ElementHandle, Page, WaitForSelectorOptions } from 'puppeteer';
2
2
  export interface Logger {
3
- info(message: string): void;
4
- warn(message: string): void;
3
+ info(value: any): void;
4
+ warn(value: any): void;
5
+ error(value: any): void;
5
6
  }
6
7
  export type Options = {
7
8
  challengeCount?: number;
@@ -11,4 +12,6 @@ export type Options = {
11
12
  screenshot?: string;
12
13
  waitForSelector?: WaitForSelectorOptions;
13
14
  };
15
+ export declare class CaptchaError extends Error {
16
+ }
14
17
  export declare function authenticate(page: Page, email: string, password: string, secret: string, selector: string, options?: Options, logger?: Logger): Promise<ElementHandle | null>;
package/dist/index.js CHANGED
@@ -7,6 +7,8 @@ const DEFAULTS = {
7
7
  trialCount: 10,
8
8
  trialTimeoutSeconds: 2,
9
9
  };
10
+ export class CaptchaError extends Error {
11
+ }
10
12
  export async function authenticate(page, email, password, secret, selector, options = DEFAULTS, logger = console) {
11
13
  const mergedOptions = { ...DEFAULTS, ...options };
12
14
  logger.info('Waiting to enter the email...');
@@ -18,7 +20,17 @@ export async function authenticate(page, email, password, secret, selector, opti
18
20
  await page.keyboard.press('Enter');
19
21
  logger.info('Waiting to enter the password...');
20
22
  await showScreenshot(page, mergedOptions.screenshot, logger);
21
- await page.waitForSelector('input[type=password]', { visible: true });
23
+ const captcha = await Promise.any([
24
+ page
25
+ .waitForSelector('input[type=password]', { visible: true })
26
+ .then(() => false),
27
+ page
28
+ .waitForSelector('input[type=text]', { visible: true })
29
+ .then(() => true),
30
+ ]);
31
+ if (captcha) {
32
+ throw new CaptchaError('failed to proceed due to CAPTCHA');
33
+ }
22
34
  logger.info('Entering the password...');
23
35
  await showScreenshot(page, mergedOptions.screenshot, logger);
24
36
  await page.type('input[type=password]', password);
@@ -72,8 +84,8 @@ async function takeContent(page, logger) {
72
84
  try {
73
85
  return await page.evaluate(() => document.body.innerText);
74
86
  }
75
- catch (error) {
76
- logger.warn(`Failed to take the content (${error}).`);
87
+ catch (cause) {
88
+ logger.error(new Error(`failed to take the content`, { cause }));
77
89
  return undefined;
78
90
  }
79
91
  }
@@ -83,8 +95,8 @@ async function takeImage(page, logger) {
83
95
  await page.addStyleTag({ content });
84
96
  return await page.screenshot({ encoding: 'base64' });
85
97
  }
86
- catch (error) {
87
- logger.warn(`Failed to take a screenshot (${error}).`);
98
+ catch (cause) {
99
+ logger.error(new Error(`failed to take a screenshot`, { cause }));
88
100
  return undefined;
89
101
  }
90
102
  }
package/package.json CHANGED
@@ -1,31 +1,35 @@
1
1
  {
2
2
  "name": "@thetypefounders/continue-with-google",
3
- "version": "1.4.3",
3
+ "version": "1.5.2",
4
4
  "license": "Apache-2.0",
5
5
  "author": "Ivan Ukhov <ivan.ukhov@gmail.com>",
6
6
  "description": "Two-factor authentication with Google via Puppeteer",
7
7
  "repository": {
8
8
  "type": "git",
9
- "url": "https://github.com/the-type-founders/continue-with-google.git"
9
+ "url": "git+https://github.com/the-type-founders/continue-with-google.git"
10
10
  },
11
11
  "type": "module",
12
12
  "main": "dist/index.js",
13
13
  "types": "dist/index.d.ts",
14
14
  "dependencies": {
15
15
  "authenticator": "^1.1.5",
16
- "puppeteer": "^24.9.0"
16
+ "puppeteer": "^24.34.0"
17
17
  },
18
18
  "devDependencies": {
19
- "@trivago/prettier-plugin-sort-imports": "^5.2.2",
19
+ "@trivago/prettier-plugin-sort-imports": "^6.0.0",
20
20
  "@types/authenticator": "^1.1.4",
21
- "prettier": "^3.5.3",
22
- "typescript": "^5.8.3"
21
+ "dotenv": "^17.2.3",
22
+ "prettier": "^3.7.4",
23
+ "puppeteer-extra": "^3.3.6",
24
+ "puppeteer-extra-plugin-stealth": "^2.11.2",
25
+ "typescript": "^5.9.3",
26
+ "vitest": "^4.0.16"
23
27
  },
24
28
  "scripts": {
25
29
  "build": "tsc",
26
30
  "check": "prettier . --check",
27
31
  "format": "prettier . --check --write",
28
32
  "prepublish": "tsc",
29
- "test": "echo"
33
+ "test": "vitest --bail=1"
30
34
  }
31
35
  }
package/src/index.ts CHANGED
@@ -4,8 +4,9 @@ import { setTimeout } from 'node:timers/promises';
4
4
  import { type ElementHandle, Page, WaitForSelectorOptions } from 'puppeteer';
5
5
 
6
6
  export interface Logger {
7
- info(message: string): void;
8
- warn(message: string): void;
7
+ info(value: any): void;
8
+ warn(value: any): void;
9
+ error(value: any): void;
9
10
  }
10
11
 
11
12
  export type Options = {
@@ -24,6 +25,8 @@ const DEFAULTS: Options = {
24
25
  trialTimeoutSeconds: 2,
25
26
  };
26
27
 
28
+ export class CaptchaError extends Error {}
29
+
27
30
  export async function authenticate(
28
31
  page: Page,
29
32
  email: string,
@@ -46,7 +49,17 @@ export async function authenticate(
46
49
 
47
50
  logger.info('Waiting to enter the password...');
48
51
  await showScreenshot(page, mergedOptions.screenshot, logger);
49
- await page.waitForSelector('input[type=password]', { visible: true });
52
+ const captcha = await Promise.any([
53
+ page
54
+ .waitForSelector('input[type=password]', { visible: true })
55
+ .then(() => false),
56
+ page
57
+ .waitForSelector('input[type=text]', { visible: true })
58
+ .then(() => true),
59
+ ]);
60
+ if (captcha) {
61
+ throw new CaptchaError('failed to proceed due to CAPTCHA');
62
+ }
50
63
 
51
64
  logger.info('Entering the password...');
52
65
  await showScreenshot(page, mergedOptions.screenshot, logger);
@@ -119,8 +132,8 @@ async function takeContent(
119
132
  ): Promise<string | undefined> {
120
133
  try {
121
134
  return await page.evaluate(() => document.body.innerText);
122
- } catch (error) {
123
- logger.warn(`Failed to take the content (${error}).`);
135
+ } catch (cause) {
136
+ logger.error(new Error(`failed to take the content`, { cause }));
124
137
  return undefined;
125
138
  }
126
139
  }
@@ -133,8 +146,8 @@ async function takeImage(
133
146
  const content = '* { caret-color: transparent !important; }';
134
147
  await page.addStyleTag({ content });
135
148
  return await page.screenshot({ encoding: 'base64' });
136
- } catch (error) {
137
- logger.warn(`Failed to take a screenshot (${error}).`);
149
+ } catch (cause) {
150
+ logger.error(new Error(`failed to take a screenshot`, { cause }));
138
151
  return undefined;
139
152
  }
140
153
  }
@@ -0,0 +1,31 @@
1
+ import 'dotenv/config';
2
+ import assert from 'node:assert';
3
+ import PuppeteerExtra from 'puppeteer-extra';
4
+ import StealthPlugin from 'puppeteer-extra-plugin-stealth';
5
+ import { test } from 'vitest';
6
+
7
+ import { authenticate } from '../src/index.js';
8
+
9
+ test('authenticate', { timeout: 5 * 60 * 1000 }, async () => {
10
+ PuppeteerExtra.use(StealthPlugin());
11
+ const browser = await PuppeteerExtra.launch({
12
+ args: ['--no-sandbox'],
13
+ slowMo: parseInt(process.env.PUPPETEER_SLOW_MOTION || '100'),
14
+ });
15
+ try {
16
+ const page = await browser.newPage();
17
+ await page.setViewport({ width: 1280, height: 800 });
18
+ await page.goto('https://mail.google.com');
19
+ await assert.doesNotReject(
20
+ authenticate(
21
+ page,
22
+ process.env.GOOGLE_USER_EMAIL!,
23
+ process.env.GOOGLE_USER_PASSWORD!,
24
+ process.env.GOOGLE_USER_SECRET!,
25
+ '[aria-label="Search mail"]'
26
+ )
27
+ );
28
+ } finally {
29
+ await browser.close();
30
+ }
31
+ });