@ultimat3/scraping 19.1.2 → 19.2.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.
- package/package.json +5 -5
- package/src/error-throws.ts +57 -6
- package/src/errors.ts +14 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ultimat3/scraping",
|
|
3
|
-
"version": "19.
|
|
3
|
+
"version": "19.2.0",
|
|
4
4
|
"description": "Browser automation as a job: scrape() returns a JobHandle",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -30,9 +30,9 @@
|
|
|
30
30
|
"test": "bun test"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@ultimat3/core": "19.
|
|
34
|
-
"@ultimat3/jobs": "19.
|
|
35
|
-
"@ultimat3/schema": "19.
|
|
36
|
-
"@ultimat3/storage": "19.
|
|
33
|
+
"@ultimat3/core": "19.2.0",
|
|
34
|
+
"@ultimat3/jobs": "19.2.0",
|
|
35
|
+
"@ultimat3/schema": "19.2.0",
|
|
36
|
+
"@ultimat3/storage": "19.2.0"
|
|
37
37
|
}
|
|
38
38
|
}
|
package/src/error-throws.ts
CHANGED
|
@@ -36,13 +36,64 @@ export const cdpAttachFailed = (cdpUrl: string, thrown: unknown): ScrapeError =>
|
|
|
36
36
|
meta: { cdpUrl },
|
|
37
37
|
});
|
|
38
38
|
|
|
39
|
+
/**
|
|
40
|
+
* What separates "there is no browser" from "the browser stopped answering". Both arrive at the
|
|
41
|
+
* same `catch` around `launch()` (`driver-cdp.ts`), and only one of them is worth a second attempt.
|
|
42
|
+
*
|
|
43
|
+
* Matched on the LAUNCHER'S OWN WORDS rather than on a flag this function could be passed, because
|
|
44
|
+
* the launch site hands it one `unknown` and nothing else. Every entry below is a message a browser
|
|
45
|
+
* library or the kernel emits for a browser that is not there, and none of them is producible by a
|
|
46
|
+
* page that merely went away mid-run — which is what keeps the split honest at `cdp-target.ts`'s
|
|
47
|
+
* call site, where the browser demonstrably existed.
|
|
48
|
+
*
|
|
49
|
+
* Lowercase, and compared against a lowercased render, so a library re-capitalising its own message
|
|
50
|
+
* does not silently turn a permanent misconfiguration back into five retries.
|
|
51
|
+
*/
|
|
52
|
+
const BROWSER_ABSENT_SIGNS: readonly string[] = [
|
|
53
|
+
// puppeteer-core ships no browser and has no default: this is its whole message when nothing was
|
|
54
|
+
// configured — ``An `executablePath` or `channel` must be specified for `puppeteer-core```.
|
|
55
|
+
'must be specified',
|
|
56
|
+
// `spawn /usr/bin/google-chrome ENOENT` — an executablePath naming a file that is not there,
|
|
57
|
+
// which is what a CHROME_PATH left over from another machine is.
|
|
58
|
+
'enoent',
|
|
59
|
+
// The binary is there and this uid may not execute it. Attempt 2 runs as the same user.
|
|
60
|
+
'eacces',
|
|
61
|
+
// The two spellings puppeteer has shipped for "I looked for a browser and found none".
|
|
62
|
+
'could not find chrome',
|
|
63
|
+
'could not find browser',
|
|
64
|
+
'was not found at the configured executablepath',
|
|
65
|
+
];
|
|
66
|
+
|
|
67
|
+
const browserIsAbsent = (thrown: unknown): boolean => {
|
|
68
|
+
const text = renderThrowable(thrown).toLowerCase();
|
|
69
|
+
return BROWSER_ABSENT_SIGNS.some((sign) => text.includes(sign));
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* A launch or a page call that failed. TWO codes, because the two failures have opposite answers.
|
|
74
|
+
*
|
|
75
|
+
* `X_SCRAPE_BROWSER_UNREACHABLE` is transport and stays retryable: a browser that was there is
|
|
76
|
+
* gone, and attempt 2 may find it back.
|
|
77
|
+
*
|
|
78
|
+
* `X_SCRAPE_BROWSER_MISSING` is a misconfiguration and is terminal. Its fix used to read "raise
|
|
79
|
+
* watchdog: { idleMs } on the scrape() definition … otherwise re-run once the browser host is
|
|
80
|
+
* back", which on the `x shot` path names a definition that does not exist and an act that never
|
|
81
|
+
* helps — while the retry table spent five browser launches proving it.
|
|
82
|
+
*/
|
|
39
83
|
export const browserUnreachable = (driver: string, thrown: unknown): ScrapeError =>
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
84
|
+
browserIsAbsent(thrown)
|
|
85
|
+
? new ScrapeError({
|
|
86
|
+
code: 'X_SCRAPE_BROWSER_MISSING',
|
|
87
|
+
cause: `the ${driver} driver has no browser to launch: ${renderThrowable(thrown)}`,
|
|
88
|
+
fix: 'install a Chrome or Chromium and name it — localBrowser({ launcher, executablePath: env.CHROME_PATH }) — or attach to one somebody else is running with remoteBrowser({ cdpUrl: env.SCRAPE_CDP_URL })',
|
|
89
|
+
meta: { driver },
|
|
90
|
+
})
|
|
91
|
+
: new ScrapeError({
|
|
92
|
+
code: 'X_SCRAPE_BROWSER_UNREACHABLE',
|
|
93
|
+
cause: `the ${driver} browser stopped answering: ${renderThrowable(thrown)}`,
|
|
94
|
+
fix: 'raise watchdog: { idleMs } on the scrape() definition if the site is genuinely slow, otherwise re-run once the browser host is back',
|
|
95
|
+
meta: { driver },
|
|
96
|
+
});
|
|
46
97
|
|
|
47
98
|
export const profileLocked = (profileDir: string): ScrapeError =>
|
|
48
99
|
new ScrapeError({
|
package/src/errors.ts
CHANGED
|
@@ -11,6 +11,7 @@ export const SCRAPE_OWNED_ERROR_CODES = [
|
|
|
11
11
|
'X_SCRAPE_DRIVER_UNKNOWN',
|
|
12
12
|
'X_SCRAPE_CDP_ATTACH_FAILED',
|
|
13
13
|
'X_SCRAPE_BROWSER_UNREACHABLE',
|
|
14
|
+
'X_SCRAPE_BROWSER_MISSING',
|
|
14
15
|
'X_SCRAPE_PROFILE_LOCKED',
|
|
15
16
|
'X_SCRAPE_HOST_BLOCKED',
|
|
16
17
|
'X_SCRAPE_SELECTOR_MISSING',
|
|
@@ -59,6 +60,7 @@ export const SCRAPE_ERROR_TITLES: Readonly<Record<ScrapeOwnedErrorCode, string>>
|
|
|
59
60
|
X_SCRAPE_DRIVER_UNKNOWN: 'no browser driver is installed for this run',
|
|
60
61
|
X_SCRAPE_CDP_ATTACH_FAILED: 'the CDP endpoint refused the attach',
|
|
61
62
|
X_SCRAPE_BROWSER_UNREACHABLE: 'the browser went away mid-run',
|
|
63
|
+
X_SCRAPE_BROWSER_MISSING: 'there is no browser on this machine for the driver to launch',
|
|
62
64
|
X_SCRAPE_PROFILE_LOCKED: 'another process holds this browser profile',
|
|
63
65
|
X_SCRAPE_HOST_BLOCKED: 'the page asked for a host allowHosts does not list',
|
|
64
66
|
X_SCRAPE_SELECTOR_MISSING: 'the selector never appeared inside its window',
|
|
@@ -106,6 +108,10 @@ registerErrorCodes(
|
|
|
106
108
|
*/
|
|
107
109
|
export const SCRAPE_ERROR_RETRY = {
|
|
108
110
|
X_SCRAPE_CDP_ATTACH_FAILED: 'retryable',
|
|
111
|
+
// Retryable, and it now means ONLY what its title says: a browser that was there and stopped
|
|
112
|
+
// answering. It used to be raised for a launch that failed because there was no browser to
|
|
113
|
+
// launch at all, which the same run repeated five times — `X_SCRAPE_BROWSER_MISSING` below is
|
|
114
|
+
// that half, split out and terminal.
|
|
109
115
|
X_SCRAPE_BROWSER_UNREACHABLE: 'retryable',
|
|
110
116
|
X_SCRAPE_TIMEOUT: 'retryable',
|
|
111
117
|
X_SCRAPE_WEDGED: 'retryable',
|
|
@@ -124,6 +130,14 @@ export const SCRAPE_ERROR_RETRY = {
|
|
|
124
130
|
// Everything below is terminal, and each one is listed rather than left to the default so that
|
|
125
131
|
// deleting a line is a visible decision.
|
|
126
132
|
X_SCRAPE_DRIVER_UNKNOWN: 'terminal',
|
|
133
|
+
// TERMINAL where its sibling `X_SCRAPE_BROWSER_UNREACHABLE` is retryable, and the difference IS
|
|
134
|
+
// the reason the two codes are separate. "The browser host went away" is transport — attempt 2
|
|
135
|
+
// may find it back. "There is no browser binary on this box, or none was configured" is a
|
|
136
|
+
// MISCONFIGURATION: attempt 2 spawns the same absent path, five times, on a five-attempt policy,
|
|
137
|
+
// and on an authenticated target that is five arrivals at a login for no chance of a different
|
|
138
|
+
// answer. Measured on `x shot`, where there is no `scrape()` definition to raise a watchdog on
|
|
139
|
+
// and re-running never helps. The repair is an install or an `executablePath`, so a human decides.
|
|
140
|
+
X_SCRAPE_BROWSER_MISSING: 'terminal',
|
|
127
141
|
X_SCRAPE_PROFILE_LOCKED: 'terminal',
|
|
128
142
|
X_SCRAPE_HOST_BLOCKED: 'terminal',
|
|
129
143
|
X_SCRAPE_SELECTOR_MISSING: 'terminal',
|