File size: 3,187 Bytes
dc89ab8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
import Puppeteer from "https://deno.land/x/puppeteer@14.1.1/mod.ts";
import EventEmitter from "https://deno.land/x/events@v1.0.0/mod.ts";
import type { Browser, Page } from "https://deno.land/x/puppeteer@14.1.1/mod.ts";

const BROWSER_SIGNALS = [
    'disconnected',
    'targetchanged',
    'targetcreated',
    'targetdestroyed'
];

const CHROME_ARGS = [
    '--no-sandbox',
    '--disable-setuid-sandbox'
];

async function resolve(a: string) {
    if (a.match(/(\d.?){4}/)) {
        return a;
    }
    return await Deno.resolveDns(a, "A");
}

export default class Runner extends EventEmitter {
    config: {
        BROWSERLESS_HOST: string;
        BROWSERLESS_PORT: string;
    };
    target: string;
    browser: Browser | undefined;
    connected: Promise<boolean> | undefined;

    constructor(config = {
        BROWSERLESS_HOST: Deno.env.get("BROWSERLESS_HOST") || "localhost",
        BROWSERLESS_PORT: Deno.env.get("BROWSERLESS_PORT") || "3000",
    }) {
        super();
        this.target = `ws://${config.BROWSERLESS_HOST}:${config.BROWSERLESS_PORT}`;
        this.config = config;
        this.connected
    }
    public async close() {
        try {
            if (this.browser) await this.browser.close();
        } catch (err) {
            console.error(`${err} on close`)
        }
    }
    async connect() {
        if (!this.connected)
            this.connected = this._connect()
        return this.connected
    }
    async _connect() {
        try {
            const host = await resolve(this.config.BROWSERLESS_HOST);
            const ver = await fetch(`http://${host}:${this.config.BROWSERLESS_PORT}/json/version`)
                .then(async res => await res.json())
            this.target = ver.webSocketDebuggerUrl;
            this.browser = this.browser || await Puppeteer.connect({
                browserWSEndpoint: this.target
            }).catch(() => {
                console.error(`
⚠ COULD NOT CONNECT TO BROWSERLESS
🦄 will try to spawn a chromedriver instance for you to debug`)
                return Puppeteer.launch({
                    args: CHROME_ARGS,
                    headless: false
                })
            });

            if (!this.browser) {
                console.error("couldn't init Browser");
                return false;
            }
            BROWSER_SIGNALS.map(e => this.browser?.on(e, d => this.emit(`browser:${e}`, d)))
            this.browser.on('error', e => console.error(`got browser error: ${e}`))

            const pages = await this.browser.pages();
            for (let p in pages) {
                await pages[p].close();
            }
            this.emit("ready")
        } catch (e) {
            console.error(e);
        }
        return true;
    }

    public async run(fn: (page: Page) => void) {
        await this.connect();

        if (!this.browser) {
            return;
        }
        try {
            const page = await this.browser.newPage()
            if (!page) {
                return;
            }
            const ret = await fn(page)
            await page.close()
            return ret
        } catch (e) {
            return
        }
    }
}