webforai 3.0.0 → 4.1.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/README.md +47 -5
- package/dist/bin.js +3109 -490
- package/dist/cjs/detect-client-shell.js +78 -0
- package/dist/cjs/extract-mdast.js +58 -0
- package/dist/cjs/extract-meta-refresh.js +71 -0
- package/dist/cjs/extractors/index.js +5 -0
- package/dist/cjs/extractors/lib/block-features.js +465 -0
- package/dist/cjs/extractors/lib/block-model.generated.js +45 -0
- package/dist/cjs/extractors/lib/block-model.js +72 -0
- package/dist/cjs/extractors/lib/blocks.js +212 -0
- package/dist/cjs/extractors/lib/code-tabs.js +191 -0
- package/dist/cjs/extractors/lib/constants.js +1 -1
- package/dist/cjs/extractors/lib/layout-tables.js +123 -0
- package/dist/cjs/extractors/lib/sanitize.js +146 -26
- package/dist/cjs/extractors/lib/takumi-signal.js +76 -0
- package/dist/cjs/extractors/lib/ui-chrome.js +7 -0
- package/dist/cjs/extractors/presets/auto.js +3 -3
- package/dist/cjs/extractors/presets/kiwame.js +231 -0
- package/dist/cjs/extractors/presets/takumi.js +34 -3
- package/dist/cjs/html-to-markdown.js +58 -4
- package/dist/cjs/html-to-mdast.js +4 -3
- package/dist/cjs/index.js +16 -0
- package/dist/cjs/link-replacer.js +19 -13
- package/dist/cjs/loaders/cf-puppeteer.js +14 -7
- package/dist/cjs/loaders/fetch.js +14 -2
- package/dist/cjs/loaders/playwright.js +32 -30
- package/dist/cjs/loaders/puppeteer.js +14 -7
- package/dist/cjs/md-splitter.js +24 -11
- package/dist/cjs/mdast-handlers/custom-code-handler.js +32 -9
- package/dist/cjs/mdast-handlers/custom-div-handler.js +14 -35
- package/dist/cjs/mdast-handlers/custom-img-handler.js +10 -1
- package/dist/cjs/mdast-handlers/custom-table-handler.js +18 -1
- package/dist/cjs/mdast-handlers/definition-list-handler.js +17 -6
- package/dist/cjs/mdast-handlers/table-cell-flatten.js +195 -0
- package/dist/cjs/mdast-handlers/twoslash.js +71 -0
- package/dist/cjs/mdast-to-markdown.js +2 -5
- package/dist/cjs/metadata/index.js +4 -3
- package/dist/cjs/normalize/index.js +11 -2
- package/dist/cjs/platform/client.js +73 -16
- package/dist/cjs/platform/index.js +4 -0
- package/dist/cjs/platform/types.js +7 -1
- package/dist/cjs/utils/hast-fast.js +1 -1
- package/dist/cjs/utils/parse-html.js +113 -0
- package/dist/cjs/utils/pre-text.js +104 -0
- package/dist/detect-client-shell.js +53 -0
- package/dist/extract-mdast.js +58 -0
- package/dist/extract-meta-refresh.js +47 -0
- package/dist/extractors/index.js +3 -0
- package/dist/extractors/lib/block-features.js +437 -0
- package/dist/extractors/lib/block-model.generated.js +21 -0
- package/dist/extractors/lib/block-model.js +48 -0
- package/dist/extractors/lib/blocks.js +185 -0
- package/dist/extractors/lib/code-tabs.js +168 -0
- package/dist/extractors/lib/constants.js +1 -1
- package/dist/extractors/lib/layout-tables.js +99 -0
- package/dist/extractors/lib/sanitize.js +144 -26
- package/dist/extractors/lib/takumi-signal.js +53 -0
- package/dist/extractors/lib/ui-chrome.js +5 -0
- package/dist/extractors/presets/auto.js +3 -3
- package/dist/extractors/presets/kiwame.js +206 -0
- package/dist/extractors/presets/takumi.js +34 -3
- package/dist/html-to-markdown.js +57 -4
- package/dist/html-to-mdast.js +4 -3
- package/dist/index.js +18 -0
- package/dist/link-replacer.js +19 -13
- package/dist/loaders/cf-puppeteer.js +15 -8
- package/dist/loaders/fetch.js +14 -2
- package/dist/loaders/playwright.js +32 -30
- package/dist/loaders/puppeteer.js +15 -8
- package/dist/md-splitter.js +24 -11
- package/dist/mdast-handlers/custom-code-handler.js +30 -9
- package/dist/mdast-handlers/custom-div-handler.js +14 -35
- package/dist/mdast-handlers/custom-img-handler.js +10 -1
- package/dist/mdast-handlers/custom-table-handler.js +19 -2
- package/dist/mdast-handlers/definition-list-handler.js +17 -6
- package/dist/mdast-handlers/table-cell-flatten.js +170 -0
- package/dist/mdast-handlers/twoslash.js +48 -0
- package/dist/mdast-to-markdown.js +2 -5
- package/dist/metadata/index.js +4 -3
- package/dist/normalize/index.js +12 -2
- package/dist/platform/client.js +73 -16
- package/dist/platform/index.js +4 -0
- package/dist/platform/types.js +5 -1
- package/dist/types/cli/bin.js.map +1 -1
- package/dist/types/cli/commands/jobs/files.d.ts +28 -0
- package/dist/types/cli/commands/jobs/files.js.map +1 -0
- package/dist/types/cli/commands/jobs/llms-txt.d.ts +33 -0
- package/dist/types/cli/commands/jobs/llms-txt.js.map +1 -0
- package/dist/types/cli/commands/jobs/options.d.ts +58 -0
- package/dist/types/cli/commands/jobs/options.js.map +1 -0
- package/dist/types/cli/commands/jobs/run.d.ts +44 -0
- package/dist/types/cli/commands/jobs/run.js.map +1 -0
- package/dist/types/cli/commands/skill/content.js.map +1 -1
- package/dist/types/cli/commands/webforai/interactive.d.ts +1 -1
- package/dist/types/cli/commands/webforai/interactive.js.map +1 -1
- package/dist/types/cli/commands/webforai/options.d.ts +9 -2
- package/dist/types/cli/commands/webforai/options.js.map +1 -1
- package/dist/types/cli/commands/webforai/run.js.map +1 -1
- package/dist/types/cli/constants.d.ts +3 -1
- package/dist/types/cli/constants.js.map +1 -1
- package/dist/types/cli/utils.d.ts +8 -1
- package/dist/types/cli/utils.js.map +1 -1
- package/dist/types/detect-client-shell.d.ts +24 -0
- package/dist/types/detect-client-shell.js.map +1 -0
- package/dist/types/extract-mdast.js.map +1 -1
- package/dist/types/extract-meta-refresh.d.ts +29 -0
- package/dist/types/extract-meta-refresh.js.map +1 -0
- package/dist/types/extractors/index.d.ts +1 -0
- package/dist/types/extractors/index.js.map +1 -1
- package/dist/types/extractors/lib/block-features.d.ts +43 -0
- package/dist/types/extractors/lib/block-features.js.map +1 -0
- package/dist/types/extractors/lib/block-model.d.ts +28 -0
- package/dist/types/extractors/lib/block-model.generated.d.ts +5 -0
- package/dist/types/extractors/lib/block-model.generated.js.map +1 -0
- package/dist/types/extractors/lib/block-model.js.map +1 -0
- package/dist/types/extractors/lib/blocks.d.ts +53 -0
- package/dist/types/extractors/lib/blocks.js.map +1 -0
- package/dist/types/extractors/lib/code-tabs.d.ts +30 -0
- package/dist/types/extractors/lib/code-tabs.js.map +1 -0
- package/dist/types/extractors/lib/constants.js.map +1 -1
- package/dist/types/extractors/lib/layout-tables.d.ts +17 -0
- package/dist/types/extractors/lib/layout-tables.js.map +1 -0
- package/dist/types/extractors/lib/sanitize.d.ts +33 -3
- package/dist/types/extractors/lib/sanitize.js.map +1 -1
- package/dist/types/extractors/lib/takumi-signal.d.ts +12 -0
- package/dist/types/extractors/lib/takumi-signal.js.map +1 -0
- package/dist/types/extractors/lib/ui-chrome.d.ts +8 -0
- package/dist/types/extractors/lib/ui-chrome.js.map +1 -1
- package/dist/types/extractors/presets/auto.d.ts +6 -1
- package/dist/types/extractors/presets/auto.js.map +1 -1
- package/dist/types/extractors/presets/kiwame.d.ts +31 -0
- package/dist/types/extractors/presets/kiwame.js.map +1 -0
- package/dist/types/extractors/presets/takumi.js.map +1 -1
- package/dist/types/html-to-markdown.d.ts +9 -0
- package/dist/types/html-to-markdown.js.map +1 -1
- package/dist/types/html-to-mdast.js.map +1 -1
- package/dist/types/index.d.ts +3 -1
- package/dist/types/index.js.map +1 -1
- package/dist/types/link-replacer.d.ts +3 -1
- package/dist/types/link-replacer.js.map +1 -1
- package/dist/types/loaders/cf-puppeteer.js.map +1 -1
- package/dist/types/loaders/fetch.d.ts +4 -0
- package/dist/types/loaders/fetch.js.map +1 -1
- package/dist/types/loaders/playwright.js.map +1 -1
- package/dist/types/loaders/puppeteer.js.map +1 -1
- package/dist/types/md-splitter.d.ts +1 -1
- package/dist/types/md-splitter.js.map +1 -1
- package/dist/types/mdast-handlers/custom-code-handler.d.ts +9 -0
- package/dist/types/mdast-handlers/custom-code-handler.js.map +1 -1
- package/dist/types/mdast-handlers/custom-div-handler.js.map +1 -1
- package/dist/types/mdast-handlers/custom-img-handler.js.map +1 -1
- package/dist/types/mdast-handlers/custom-table-handler.js.map +1 -1
- package/dist/types/mdast-handlers/definition-list-handler.d.ts +0 -11
- package/dist/types/mdast-handlers/definition-list-handler.js.map +1 -1
- package/dist/types/mdast-handlers/table-cell-flatten.d.ts +18 -0
- package/dist/types/mdast-handlers/table-cell-flatten.js.map +1 -0
- package/dist/types/mdast-handlers/twoslash.d.ts +14 -0
- package/dist/types/mdast-handlers/twoslash.js.map +1 -0
- package/dist/types/mdast-to-markdown.js.map +1 -1
- package/dist/types/metadata/index.js.map +1 -1
- package/dist/types/normalize/index.js.map +1 -1
- package/dist/types/platform/client.d.ts +2 -1
- package/dist/types/platform/client.js.map +1 -1
- package/dist/types/platform/error.d.ts +5 -3
- package/dist/types/platform/error.js.map +1 -1
- package/dist/types/platform/fetch.d.ts +2 -0
- package/dist/types/platform/fetch.js.map +1 -1
- package/dist/types/platform/index.d.ts +1 -1
- package/dist/types/platform/index.js.map +1 -1
- package/dist/types/platform/types.d.ts +35 -6
- package/dist/types/platform/types.js.map +1 -1
- package/dist/types/utils/hast-fast.d.ts +1 -1
- package/dist/types/utils/hast-fast.js.map +1 -1
- package/dist/types/utils/parse-html.d.ts +33 -0
- package/dist/types/utils/parse-html.js.map +1 -0
- package/dist/types/utils/pre-text.d.ts +12 -0
- package/dist/types/utils/pre-text.js.map +1 -0
- package/dist/utils/hast-fast.js +1 -1
- package/dist/utils/parse-html.js +87 -0
- package/dist/utils/pre-text.js +81 -0
- package/package.json +18 -5
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var twoslash_exports = {};
|
|
20
|
+
__export(twoslash_exports, {
|
|
21
|
+
cleanTwoslash: () => cleanTwoslash
|
|
22
|
+
});
|
|
23
|
+
module.exports = __toCommonJS(twoslash_exports);
|
|
24
|
+
var import_hast_fast = require("../utils/hast-fast");
|
|
25
|
+
var import_pre_text = require("../utils/pre-text");
|
|
26
|
+
const cleanTwoslash = (pre) => {
|
|
27
|
+
if (!(0, import_hast_fast.classList)(pre).includes("twoslash")) {
|
|
28
|
+
return void 0;
|
|
29
|
+
}
|
|
30
|
+
let language;
|
|
31
|
+
const clean = (element) => {
|
|
32
|
+
element.children = element.children.flatMap((child) => {
|
|
33
|
+
if (!(0, import_hast_fast.isElement)(child)) {
|
|
34
|
+
return [child];
|
|
35
|
+
}
|
|
36
|
+
const classes = (0, import_hast_fast.classList)(child);
|
|
37
|
+
if (classes.includes("language-id")) {
|
|
38
|
+
language ||= (0, import_pre_text.preText)(child).trim() || void 0;
|
|
39
|
+
return [];
|
|
40
|
+
}
|
|
41
|
+
if (classes.includes("playground-link") || classes.includes("error-behind")) {
|
|
42
|
+
return [];
|
|
43
|
+
}
|
|
44
|
+
if (classes.includes("error")) {
|
|
45
|
+
return [errorLine(child)];
|
|
46
|
+
}
|
|
47
|
+
clean(child);
|
|
48
|
+
return [child];
|
|
49
|
+
});
|
|
50
|
+
};
|
|
51
|
+
clean(pre);
|
|
52
|
+
return language && /^[\w+#.-]+$/.test(language) ? language : void 0;
|
|
53
|
+
};
|
|
54
|
+
const errorLine = (error) => {
|
|
55
|
+
let code = "";
|
|
56
|
+
let message = "";
|
|
57
|
+
for (const child of error.children) {
|
|
58
|
+
if ((0, import_hast_fast.isElement)(child) && (0, import_hast_fast.classList)(child).includes("code")) {
|
|
59
|
+
code = (0, import_pre_text.preText)(child).trim();
|
|
60
|
+
} else {
|
|
61
|
+
message += (0, import_hast_fast.isElement)(child) ? (0, import_pre_text.preText)(child) : child.type === "text" ? child.value : "";
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
const prefix = /^\d+$/.test(code) ? `error TS${code}: ` : "error: ";
|
|
65
|
+
const lines = `${prefix}${message.trim()}`.split("\n").map((line) => `// ${line}`);
|
|
66
|
+
return { type: "element", tagName: "div", properties: {}, children: [{ type: "text", value: lines.join("\n") }] };
|
|
67
|
+
};
|
|
68
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
69
|
+
0 && (module.exports = {
|
|
70
|
+
cleanTwoslash
|
|
71
|
+
});
|
|
@@ -33,11 +33,8 @@ const DEFAULT_MDAST_TO_MARKDOWN_OPTIONS = {
|
|
|
33
33
|
};
|
|
34
34
|
const mdastToMarkdown = (mdast, options) => {
|
|
35
35
|
const { baseUrl, ...toMarkdownOptions } = { ...DEFAULT_MDAST_TO_MARKDOWN_OPTIONS, ...options };
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
markdown = (0, import_link_replacer.linkReplacer)(markdown, baseUrl);
|
|
39
|
-
}
|
|
40
|
-
return markdown;
|
|
36
|
+
const tree = (0, import_mdast_utils.warpRoot)(mdast);
|
|
37
|
+
return (0, import_mdast_util_to_markdown.toMarkdown)(baseUrl ? (0, import_link_replacer.linkReplacer)(tree, baseUrl) : tree, toMarkdownOptions);
|
|
41
38
|
};
|
|
42
39
|
// Annotate the CommonJS export names for ESM import in node:
|
|
43
40
|
0 && (module.exports = {
|
|
@@ -116,9 +116,10 @@ const readName = (value) => {
|
|
|
116
116
|
};
|
|
117
117
|
const asString = (value) => typeof value === "string" && value.length > 0 ? value : void 0;
|
|
118
118
|
const MAX_TITLE_LENGTH = 160;
|
|
119
|
-
const
|
|
119
|
+
const MAX_DOCUMENT_TITLE_LENGTH = 300;
|
|
120
|
+
const asTitle = (value, maxLength = MAX_TITLE_LENGTH) => {
|
|
120
121
|
const cleaned = clean(value);
|
|
121
|
-
return cleaned && cleaned.length <=
|
|
122
|
+
return cleaned && cleaned.length <= maxLength ? cleaned : void 0;
|
|
122
123
|
};
|
|
123
124
|
const clean = (value) => {
|
|
124
125
|
if (value === void 0) {
|
|
@@ -184,7 +185,7 @@ const extractMetadata = (tree) => {
|
|
|
184
185
|
}
|
|
185
186
|
return void 0;
|
|
186
187
|
};
|
|
187
|
-
const title = asTitle(get("og:title", "twitter:title")) ?? asTitle(asString(article?.headline)) ?? asTitle(asString(article?.name)) ?? asTitle(documentTitle(tree)) ?? asTitle(firstHeading(tree));
|
|
188
|
+
const title = asTitle(get("og:title", "twitter:title")) ?? asTitle(asString(article?.headline)) ?? asTitle(asString(article?.name)) ?? asTitle(documentTitle(tree), MAX_DOCUMENT_TITLE_LENGTH) ?? asTitle(firstHeading(tree));
|
|
188
189
|
return {
|
|
189
190
|
title,
|
|
190
191
|
description: clean(get("og:description", "twitter:description", "description")) ?? clean(asString(article?.description)),
|
|
@@ -98,14 +98,16 @@ const katexDuplicates = (node) => {
|
|
|
98
98
|
if (!(classes.includes("katex") || classes.includes("katex-display"))) {
|
|
99
99
|
return [];
|
|
100
100
|
}
|
|
101
|
-
const hasMathml = node.children.some(
|
|
101
|
+
const hasMathml = node.children.some(
|
|
102
|
+
(child) => (0, import_hast_fast.isElement)(child) && (0, import_hast_fast.classList)(child).includes("katex-mathml") && hasMathSource(child)
|
|
103
|
+
);
|
|
102
104
|
if (!hasMathml) {
|
|
103
105
|
return [];
|
|
104
106
|
}
|
|
105
107
|
return node.children.filter((child) => (0, import_hast_fast.isElement)(child) && (0, import_hast_fast.classList)(child).includes("katex-html"));
|
|
106
108
|
};
|
|
107
109
|
const mediawikiDuplicates = (node) => {
|
|
108
|
-
if (!(0, import_hast_fast.classList)(node).includes("mwe-math-element")) {
|
|
110
|
+
if (!(0, import_hast_fast.classList)(node).includes("mwe-math-element") || !hasMathSource(node)) {
|
|
109
111
|
return [];
|
|
110
112
|
}
|
|
111
113
|
return node.children.filter(
|
|
@@ -116,8 +118,15 @@ const mathjaxDuplicates = (node) => {
|
|
|
116
118
|
if (node.tagName !== "mjx-container") {
|
|
117
119
|
return [];
|
|
118
120
|
}
|
|
121
|
+
const hasMathml = node.children.some(
|
|
122
|
+
(child) => (0, import_hast_fast.isElement)(child) && child.tagName === "mjx-assistive-mml" && hasMathSource(child)
|
|
123
|
+
);
|
|
124
|
+
if (!hasMathml) {
|
|
125
|
+
return [];
|
|
126
|
+
}
|
|
119
127
|
return node.children.filter((child) => (0, import_hast_fast.isElement)(child) && child.tagName !== "mjx-assistive-mml");
|
|
120
128
|
};
|
|
129
|
+
const hasMathSource = (node) => Boolean((0, import_hast_fast.findElement)(node, (element) => element.tagName === "math" && element.children.length > 0));
|
|
121
130
|
const dedupeRenderedMath = (tree) => {
|
|
122
131
|
const doomed = /* @__PURE__ */ new Set();
|
|
123
132
|
(0, import_hast_fast.walk)(tree, (node) => {
|
|
@@ -50,14 +50,28 @@ const parseRetryAfter = (body, response) => {
|
|
|
50
50
|
const header = Number(response.headers.get("retry-after"));
|
|
51
51
|
return Number.isFinite(header) && header > 0 ? header : void 0;
|
|
52
52
|
};
|
|
53
|
-
const
|
|
53
|
+
const dashboardHint = (status, dashboardUrl) => {
|
|
54
|
+
if (status === 401) {
|
|
55
|
+
return `create or check API keys at ${dashboardUrl}`;
|
|
56
|
+
}
|
|
57
|
+
if (status === 402) {
|
|
58
|
+
return `manage billing, credits and the spend cap at ${dashboardUrl}`;
|
|
59
|
+
}
|
|
60
|
+
return void 0;
|
|
61
|
+
};
|
|
62
|
+
const toApiError = (response, body, dashboardUrl) => {
|
|
54
63
|
const envelope = body;
|
|
55
64
|
const code = typeof envelope?.error?.code === "string" ? envelope.error.code : "invalid_response";
|
|
56
|
-
|
|
65
|
+
let message = typeof envelope?.error?.message === "string" ? envelope.error.message : `unexpected response (HTTP ${response.status})`;
|
|
66
|
+
const hint = code === "invalid_response" ? void 0 : dashboardHint(response.status, dashboardUrl);
|
|
67
|
+
if (hint && !message.includes("/dashboard")) {
|
|
68
|
+
message = `${message} (${hint})`;
|
|
69
|
+
}
|
|
57
70
|
return new import_error.PlatformApiError(code, message, response.status, parseRetryAfter(body, response));
|
|
58
71
|
};
|
|
59
72
|
const createPlatformClient = (options = {}) => {
|
|
60
73
|
const baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
74
|
+
const dashboardUrl = `${baseUrl}/dashboard`;
|
|
61
75
|
const fetchImpl = (0, import_fetch.resolveFetch)(options.fetch);
|
|
62
76
|
const request = async (path, init) => {
|
|
63
77
|
const headers = { accept: "application/json" };
|
|
@@ -68,7 +82,7 @@ const createPlatformClient = (options = {}) => {
|
|
|
68
82
|
if (!options.apiKey) {
|
|
69
83
|
throw new import_error.PlatformApiError(
|
|
70
84
|
"missing_api_key",
|
|
71
|
-
|
|
85
|
+
`This endpoint needs an API key: pass \`apiKey\` to createPlatformClient (create one at ${dashboardUrl}; 1,000 free credits/month).`,
|
|
72
86
|
0
|
|
73
87
|
);
|
|
74
88
|
}
|
|
@@ -77,18 +91,19 @@ const createPlatformClient = (options = {}) => {
|
|
|
77
91
|
const response = await fetchImpl(`${baseUrl}${path}`, {
|
|
78
92
|
method: init.method,
|
|
79
93
|
headers,
|
|
80
|
-
body: init.body === void 0 ? void 0 : JSON.stringify(init.body)
|
|
94
|
+
body: init.body === void 0 ? void 0 : JSON.stringify(init.body),
|
|
95
|
+
...init.signal ? { signal: init.signal } : {}
|
|
81
96
|
});
|
|
82
97
|
const body = await response.json().catch(() => void 0);
|
|
83
98
|
if (!response.ok) {
|
|
84
|
-
throw toApiError(response, body);
|
|
99
|
+
throw toApiError(response, body, dashboardUrl);
|
|
85
100
|
}
|
|
86
101
|
if (body === void 0) {
|
|
87
102
|
throw new import_error.PlatformApiError("invalid_response", "response body is not JSON", response.status);
|
|
88
103
|
}
|
|
89
104
|
return body;
|
|
90
105
|
};
|
|
91
|
-
const getJob = (jobId) => request(`/v1/jobs/${encodeURIComponent(jobId)}`, { method: "GET", auth: true });
|
|
106
|
+
const getJob = (jobId, signal) => request(`/v1/jobs/${encodeURIComponent(jobId)}`, { method: "GET", auth: true, signal });
|
|
92
107
|
const getJobResults = (jobId, opts) => {
|
|
93
108
|
const query = opts?.cursor ? `?cursor=${encodeURIComponent(opts.cursor)}` : "";
|
|
94
109
|
return request(`/v1/jobs/${encodeURIComponent(jobId)}/results${query}`, { method: "GET", auth: true });
|
|
@@ -104,6 +119,17 @@ const createPlatformClient = (options = {}) => {
|
|
|
104
119
|
}
|
|
105
120
|
return await response.json();
|
|
106
121
|
};
|
|
122
|
+
const getJobOrBackOff = async (jobId, signal, interval) => {
|
|
123
|
+
try {
|
|
124
|
+
return await getJob(jobId, signal);
|
|
125
|
+
} catch (error) {
|
|
126
|
+
if (!(error instanceof import_error.PlatformApiError && error.status === 429)) {
|
|
127
|
+
throw error;
|
|
128
|
+
}
|
|
129
|
+
await sleep(error.retryAfter !== void 0 ? error.retryAfter * 1e3 : interval, signal);
|
|
130
|
+
return void 0;
|
|
131
|
+
}
|
|
132
|
+
};
|
|
107
133
|
return {
|
|
108
134
|
scrape: (opts) => request("/v1/scrape", { method: "POST", body: opts, auth: true }),
|
|
109
135
|
scrapeAsync: (opts) => request("/v1/scrape", { method: "POST", body: { ...opts, async: true }, auth: true }),
|
|
@@ -114,17 +140,48 @@ const createPlatformClient = (options = {}) => {
|
|
|
114
140
|
waitForJob: async (jobId, opts = {}) => {
|
|
115
141
|
const interval = opts.pollIntervalMs ?? 2e3;
|
|
116
142
|
const timeout = opts.timeoutMs ?? 10 * 60 * 1e3;
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
143
|
+
if (!Number.isFinite(interval) || interval < 0 || interval > 2147483647) {
|
|
144
|
+
throw new RangeError("pollIntervalMs must be between 0 and 2147483647");
|
|
145
|
+
}
|
|
146
|
+
if (!Number.isFinite(timeout) || timeout <= 0 || timeout > 2147483647) {
|
|
147
|
+
throw new RangeError("timeoutMs must be greater than 0 and at most 2147483647");
|
|
148
|
+
}
|
|
149
|
+
opts.signal?.throwIfAborted();
|
|
150
|
+
const controller = new AbortController();
|
|
151
|
+
const { signal } = controller;
|
|
152
|
+
const onAbort = () => controller.abort(opts.signal?.reason);
|
|
153
|
+
opts.signal?.addEventListener("abort", onAbort, { once: true });
|
|
154
|
+
const timer = setTimeout(
|
|
155
|
+
() => controller.abort(new import_error.PlatformApiError("poll_timeout", `job ${jobId} did not finish within ${timeout}ms`, 0)),
|
|
156
|
+
timeout
|
|
157
|
+
);
|
|
158
|
+
let rejectOnAbort = () => {
|
|
159
|
+
};
|
|
160
|
+
const aborted = new Promise((_resolve, reject) => {
|
|
161
|
+
rejectOnAbort = () => reject(signal.reason);
|
|
162
|
+
signal.addEventListener("abort", rejectOnAbort, { once: true });
|
|
163
|
+
});
|
|
164
|
+
const poll = async () => {
|
|
165
|
+
for (; ; ) {
|
|
166
|
+
signal.throwIfAborted();
|
|
167
|
+
const status = await getJobOrBackOff(jobId, signal, interval);
|
|
168
|
+
signal.throwIfAborted();
|
|
169
|
+
if (status === void 0) {
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
opts.onStatus?.(status);
|
|
173
|
+
if (TERMINAL_STATES.has(status.status)) {
|
|
174
|
+
return status;
|
|
175
|
+
}
|
|
176
|
+
await sleep(interval, signal);
|
|
126
177
|
}
|
|
127
|
-
|
|
178
|
+
};
|
|
179
|
+
try {
|
|
180
|
+
return await Promise.race([poll(), aborted]);
|
|
181
|
+
} finally {
|
|
182
|
+
clearTimeout(timer);
|
|
183
|
+
opts.signal?.removeEventListener("abort", onAbort);
|
|
184
|
+
signal.removeEventListener("abort", rejectOnAbort);
|
|
128
185
|
}
|
|
129
186
|
},
|
|
130
187
|
jobResults: async function* (jobId) {
|
|
@@ -18,10 +18,12 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
18
18
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
19
|
var platform_exports = {};
|
|
20
20
|
__export(platform_exports, {
|
|
21
|
+
CRAWL_SITEMAP_MODES: () => import_types.CRAWL_SITEMAP_MODES,
|
|
21
22
|
DEFAULT_BASE_URL: () => import_client.DEFAULT_BASE_URL,
|
|
22
23
|
ENGINES: () => import_types.ENGINES,
|
|
23
24
|
PlatformApiError: () => import_error.PlatformApiError,
|
|
24
25
|
REGIONS: () => import_types.REGIONS,
|
|
26
|
+
REQUESTED_ENGINES: () => import_types.REQUESTED_ENGINES,
|
|
25
27
|
createPlatformClient: () => import_client.createPlatformClient,
|
|
26
28
|
isStoredPageStub: () => import_types.isStoredPageStub
|
|
27
29
|
});
|
|
@@ -31,10 +33,12 @@ var import_error = require("./error");
|
|
|
31
33
|
var import_types = require("./types");
|
|
32
34
|
// Annotate the CommonJS export names for ESM import in node:
|
|
33
35
|
0 && (module.exports = {
|
|
36
|
+
CRAWL_SITEMAP_MODES,
|
|
34
37
|
DEFAULT_BASE_URL,
|
|
35
38
|
ENGINES,
|
|
36
39
|
PlatformApiError,
|
|
37
40
|
REGIONS,
|
|
41
|
+
REQUESTED_ENGINES,
|
|
38
42
|
createPlatformClient,
|
|
39
43
|
isStoredPageStub
|
|
40
44
|
});
|
|
@@ -18,17 +18,23 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
18
18
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
19
|
var types_exports = {};
|
|
20
20
|
__export(types_exports, {
|
|
21
|
+
CRAWL_SITEMAP_MODES: () => CRAWL_SITEMAP_MODES,
|
|
21
22
|
ENGINES: () => ENGINES,
|
|
22
23
|
REGIONS: () => REGIONS,
|
|
24
|
+
REQUESTED_ENGINES: () => REQUESTED_ENGINES,
|
|
23
25
|
isStoredPageStub: () => isStoredPageStub
|
|
24
26
|
});
|
|
25
27
|
module.exports = __toCommonJS(types_exports);
|
|
26
28
|
const ENGINES = ["fetch", "browser", "proxy-fetch", "proxy-browser"];
|
|
27
|
-
const
|
|
29
|
+
const REQUESTED_ENGINES = [...ENGINES, "auto"];
|
|
30
|
+
const REGIONS = ["auto", "jp"];
|
|
31
|
+
const CRAWL_SITEMAP_MODES = ["skip", "include", "only"];
|
|
28
32
|
const isStoredPageStub = (item) => item.status === "ok" && "resultUrl" in item;
|
|
29
33
|
// Annotate the CommonJS export names for ESM import in node:
|
|
30
34
|
0 && (module.exports = {
|
|
35
|
+
CRAWL_SITEMAP_MODES,
|
|
31
36
|
ENGINES,
|
|
32
37
|
REGIONS,
|
|
38
|
+
REQUESTED_ENGINES,
|
|
33
39
|
isStoredPageStub
|
|
34
40
|
});
|
|
@@ -136,7 +136,7 @@ const pruneInPlace = (tree, keep) => {
|
|
|
136
136
|
let write = 0;
|
|
137
137
|
for (let read = 0; read < children.length; read++) {
|
|
138
138
|
const child = children[read];
|
|
139
|
-
if (!keep(child)) {
|
|
139
|
+
if (!keep(child, parent)) {
|
|
140
140
|
continue;
|
|
141
141
|
}
|
|
142
142
|
children[write++] = child;
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var parse_html_exports = {};
|
|
20
|
+
__export(parse_html_exports, {
|
|
21
|
+
LARGE_DOCUMENT_CHARS: () => LARGE_DOCUMENT_CHARS,
|
|
22
|
+
MAX_ELEMENT_DEPTH: () => MAX_ELEMENT_DEPTH,
|
|
23
|
+
parseHtml: () => parseHtml,
|
|
24
|
+
stripScriptBodies: () => stripScriptBodies
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(parse_html_exports);
|
|
27
|
+
var import_hast_util_from_parse5 = require("hast-util-from-parse5");
|
|
28
|
+
var import_parse5 = require("parse5");
|
|
29
|
+
const MAX_ELEMENT_DEPTH = 256;
|
|
30
|
+
const isParent = (node) => "childNodes" in node;
|
|
31
|
+
const LINE_ELEMENTS = /* @__PURE__ */ new Set([
|
|
32
|
+
"p",
|
|
33
|
+
"div",
|
|
34
|
+
"li",
|
|
35
|
+
"tr",
|
|
36
|
+
"td",
|
|
37
|
+
"th",
|
|
38
|
+
"h1",
|
|
39
|
+
"h2",
|
|
40
|
+
"h3",
|
|
41
|
+
"h4",
|
|
42
|
+
"h5",
|
|
43
|
+
"h6",
|
|
44
|
+
"br",
|
|
45
|
+
"section",
|
|
46
|
+
"article",
|
|
47
|
+
"pre",
|
|
48
|
+
"blockquote"
|
|
49
|
+
]);
|
|
50
|
+
const textNode = (value, parentNode) => ({ nodeName: "#text", value, parentNode });
|
|
51
|
+
const coalesceText = (parent) => {
|
|
52
|
+
const merged = [];
|
|
53
|
+
for (const child of parent.childNodes) {
|
|
54
|
+
const previous = merged.at(-1);
|
|
55
|
+
if (child.nodeName === "#text" && previous?.nodeName === "#text") {
|
|
56
|
+
previous.value += child.value;
|
|
57
|
+
} else {
|
|
58
|
+
merged.push(child);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
parent.childNodes = merged;
|
|
62
|
+
};
|
|
63
|
+
const flattenDeep = (root, maxDepth) => {
|
|
64
|
+
const stack = [[root, 0]];
|
|
65
|
+
while (stack.length > 0) {
|
|
66
|
+
const [parent, depth] = stack.pop();
|
|
67
|
+
const children = parent.childNodes;
|
|
68
|
+
let unwrapped = false;
|
|
69
|
+
for (let index = 0; index < children.length; index++) {
|
|
70
|
+
const child = children[index];
|
|
71
|
+
if (!isParent(child)) {
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
if (depth + 1 > maxDepth && "tagName" in child) {
|
|
75
|
+
const replacement = [...child.childNodes];
|
|
76
|
+
if (LINE_ELEMENTS.has(child.tagName)) {
|
|
77
|
+
replacement.unshift(textNode("\n", parent));
|
|
78
|
+
replacement.push(textNode("\n", parent));
|
|
79
|
+
}
|
|
80
|
+
for (const node of replacement) {
|
|
81
|
+
node.parentNode = parent;
|
|
82
|
+
}
|
|
83
|
+
children.splice(index, 1, ...replacement);
|
|
84
|
+
index -= 1;
|
|
85
|
+
unwrapped = true;
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
stack.push([child, depth + 1]);
|
|
89
|
+
}
|
|
90
|
+
if (unwrapped) {
|
|
91
|
+
coalesceText(parent);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
const SCRIPT_OR_STYLE = /<(script|style)\b([^>]*)>[\s\S]*?<\/\1\s*>/gi;
|
|
96
|
+
const LARGE_DOCUMENT_CHARS = 2e6;
|
|
97
|
+
const stripScriptBodies = (html) => html.replace(
|
|
98
|
+
SCRIPT_OR_STYLE,
|
|
99
|
+
(match, tag, attributes) => /application\/ld\+json/i.test(attributes) ? match : `<${tag}${attributes}></${tag}>`
|
|
100
|
+
);
|
|
101
|
+
const parseHtml = (html, options = {}) => {
|
|
102
|
+
const source = html.length > LARGE_DOCUMENT_CHARS ? stripScriptBodies(html) : html;
|
|
103
|
+
const document = options.fragment ? (0, import_parse5.parseFragment)(source, { scriptingEnabled: false }) : (0, import_parse5.parse)(source, { scriptingEnabled: false });
|
|
104
|
+
flattenDeep(document, MAX_ELEMENT_DEPTH);
|
|
105
|
+
return (0, import_hast_util_from_parse5.fromParse5)(document);
|
|
106
|
+
};
|
|
107
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
108
|
+
0 && (module.exports = {
|
|
109
|
+
LARGE_DOCUMENT_CHARS,
|
|
110
|
+
MAX_ELEMENT_DEPTH,
|
|
111
|
+
parseHtml,
|
|
112
|
+
stripScriptBodies
|
|
113
|
+
});
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var pre_text_exports = {};
|
|
20
|
+
__export(pre_text_exports, {
|
|
21
|
+
preText: () => preText
|
|
22
|
+
});
|
|
23
|
+
module.exports = __toCommonJS(pre_text_exports);
|
|
24
|
+
const LINE_BLOCK_TAGS = /* @__PURE__ */ new Set([
|
|
25
|
+
"address",
|
|
26
|
+
"article",
|
|
27
|
+
"aside",
|
|
28
|
+
"blockquote",
|
|
29
|
+
"dd",
|
|
30
|
+
"div",
|
|
31
|
+
"dl",
|
|
32
|
+
"dt",
|
|
33
|
+
"figcaption",
|
|
34
|
+
"figure",
|
|
35
|
+
"footer",
|
|
36
|
+
"form",
|
|
37
|
+
"h1",
|
|
38
|
+
"h2",
|
|
39
|
+
"h3",
|
|
40
|
+
"h4",
|
|
41
|
+
"h5",
|
|
42
|
+
"h6",
|
|
43
|
+
"header",
|
|
44
|
+
"hr",
|
|
45
|
+
"li",
|
|
46
|
+
"main",
|
|
47
|
+
"nav",
|
|
48
|
+
"ol",
|
|
49
|
+
"p",
|
|
50
|
+
"pre",
|
|
51
|
+
"section",
|
|
52
|
+
"table",
|
|
53
|
+
"tr",
|
|
54
|
+
"ul"
|
|
55
|
+
]);
|
|
56
|
+
const INLINE_CONTEXT_TAGS = /* @__PURE__ */ new Set(["span", "a", "b", "i", "em", "strong", "mark", "small", "u", "s", "label"]);
|
|
57
|
+
const preText = (node) => {
|
|
58
|
+
let out = "";
|
|
59
|
+
const ensureLineStart = () => {
|
|
60
|
+
if (out.length > 0 && out.charCodeAt(out.length - 1) !== 10) {
|
|
61
|
+
out += "\n";
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
const visitChildren = (parent, inline) => {
|
|
65
|
+
let cells = 0;
|
|
66
|
+
for (const child of parent.children) {
|
|
67
|
+
const isCell = child.type === "element" && (child.tagName === "td" || child.tagName === "th");
|
|
68
|
+
visit(child, isCell ? cells++ : 0, inline);
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
const visitElement = (element, cellIndex, inline) => {
|
|
72
|
+
const tag = element.tagName;
|
|
73
|
+
if (tag === "br") {
|
|
74
|
+
out += "\n";
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
if (cellIndex > 0) {
|
|
78
|
+
out += " ";
|
|
79
|
+
}
|
|
80
|
+
const block = !inline && LINE_BLOCK_TAGS.has(tag);
|
|
81
|
+
if (block) {
|
|
82
|
+
ensureLineStart();
|
|
83
|
+
}
|
|
84
|
+
visitChildren(element, inline || INLINE_CONTEXT_TAGS.has(tag));
|
|
85
|
+
if (block) {
|
|
86
|
+
ensureLineStart();
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
const visit = (current, cellIndex, inline) => {
|
|
90
|
+
if (current.type === "text") {
|
|
91
|
+
out += current.value;
|
|
92
|
+
} else if (current.type === "element") {
|
|
93
|
+
visitElement(current, cellIndex, inline);
|
|
94
|
+
} else if (current.type === "root") {
|
|
95
|
+
visitChildren(current, inline);
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
visit(node, 0, false);
|
|
99
|
+
return out;
|
|
100
|
+
};
|
|
101
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
102
|
+
0 && (module.exports = {
|
|
103
|
+
preText
|
|
104
|
+
});
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// src/detect-client-shell.ts
|
|
2
|
+
var SHELL_SUSPICION_THRESHOLD = 300;
|
|
3
|
+
var SHELL_EMPTY_THRESHOLD = 50;
|
|
4
|
+
var BODY_PATTERN = /<body[^>]*>([\s\S]*?)<\/body>/i;
|
|
5
|
+
var HEAD_PATTERN = /<head[^>]*>[\s\S]*?<\/head>/gi;
|
|
6
|
+
var NOSCRIPT_PATTERN = /<noscript[^>]*>([\s\S]*?)<\/noscript>/gi;
|
|
7
|
+
var INVISIBLE_ELEMENT_PATTERN = /<(script|style|template|svg|noscript)[^>]*>[\s\S]*?<\/\1>/gi;
|
|
8
|
+
var COMMENT_PATTERN = /<!--[\s\S]*?-->/g;
|
|
9
|
+
var TAG_PATTERN = /<[^>]+>/g;
|
|
10
|
+
var MOUNT_POINT_PATTERN = /<[a-z][^>]*\sid=["'](?:root|app|__next|___gatsby|__nuxt|q-app|svelte|react-root)["']|<app-root[\s>]|\sdata-reactroot[\s>=]/i;
|
|
11
|
+
var CHALLENGE_PATTERN = /cf-chl|challenge-platform|cf_chl_opt|just a moment|attention required|checking your browser|_incapsula_|px-captcha|datadome/i;
|
|
12
|
+
var ENTITIES = {
|
|
13
|
+
" ": " ",
|
|
14
|
+
"&": "&",
|
|
15
|
+
"<": "<",
|
|
16
|
+
">": ">",
|
|
17
|
+
""": '"',
|
|
18
|
+
"'": "'"
|
|
19
|
+
};
|
|
20
|
+
var visibleTextOf = (markup) => markup.replace(COMMENT_PATTERN, " ").replace(INVISIBLE_ELEMENT_PATTERN, " ").replace(TAG_PATTERN, " ").replace(/&(?:nbsp|amp|lt|gt|quot|#39);/g, (entity) => ENTITIES[entity] ?? " ").replace(/\s+/g, " ").trim();
|
|
21
|
+
var noscriptMentionsJavascript = (html) => {
|
|
22
|
+
for (const match of html.matchAll(NOSCRIPT_PATTERN)) {
|
|
23
|
+
if (/javascript/i.test(match[1] ?? "")) {
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return false;
|
|
28
|
+
};
|
|
29
|
+
var detectClientShell = (html) => {
|
|
30
|
+
const body = BODY_PATTERN.exec(html)?.[1] ?? html.replace(HEAD_PATTERN, " ");
|
|
31
|
+
const visibleTextLength = visibleTextOf(body).length;
|
|
32
|
+
if (visibleTextLength >= SHELL_SUSPICION_THRESHOLD) {
|
|
33
|
+
return { isShell: false, visibleTextLength };
|
|
34
|
+
}
|
|
35
|
+
if (CHALLENGE_PATTERN.test(html)) {
|
|
36
|
+
return { isShell: true, reason: "anti-bot-challenge", visibleTextLength };
|
|
37
|
+
}
|
|
38
|
+
if (MOUNT_POINT_PATTERN.test(body)) {
|
|
39
|
+
return { isShell: true, reason: "spa-shell", visibleTextLength };
|
|
40
|
+
}
|
|
41
|
+
if (noscriptMentionsJavascript(html)) {
|
|
42
|
+
return { isShell: true, reason: "noscript-only", visibleTextLength };
|
|
43
|
+
}
|
|
44
|
+
if (visibleTextLength < SHELL_EMPTY_THRESHOLD) {
|
|
45
|
+
return { isShell: true, reason: "empty-body", visibleTextLength };
|
|
46
|
+
}
|
|
47
|
+
return { isShell: false, visibleTextLength };
|
|
48
|
+
};
|
|
49
|
+
export {
|
|
50
|
+
SHELL_EMPTY_THRESHOLD,
|
|
51
|
+
SHELL_SUSPICION_THRESHOLD,
|
|
52
|
+
detectClientShell
|
|
53
|
+
};
|