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.
Files changed (181) hide show
  1. package/README.md +47 -5
  2. package/dist/bin.js +3109 -490
  3. package/dist/cjs/detect-client-shell.js +78 -0
  4. package/dist/cjs/extract-mdast.js +58 -0
  5. package/dist/cjs/extract-meta-refresh.js +71 -0
  6. package/dist/cjs/extractors/index.js +5 -0
  7. package/dist/cjs/extractors/lib/block-features.js +465 -0
  8. package/dist/cjs/extractors/lib/block-model.generated.js +45 -0
  9. package/dist/cjs/extractors/lib/block-model.js +72 -0
  10. package/dist/cjs/extractors/lib/blocks.js +212 -0
  11. package/dist/cjs/extractors/lib/code-tabs.js +191 -0
  12. package/dist/cjs/extractors/lib/constants.js +1 -1
  13. package/dist/cjs/extractors/lib/layout-tables.js +123 -0
  14. package/dist/cjs/extractors/lib/sanitize.js +146 -26
  15. package/dist/cjs/extractors/lib/takumi-signal.js +76 -0
  16. package/dist/cjs/extractors/lib/ui-chrome.js +7 -0
  17. package/dist/cjs/extractors/presets/auto.js +3 -3
  18. package/dist/cjs/extractors/presets/kiwame.js +231 -0
  19. package/dist/cjs/extractors/presets/takumi.js +34 -3
  20. package/dist/cjs/html-to-markdown.js +58 -4
  21. package/dist/cjs/html-to-mdast.js +4 -3
  22. package/dist/cjs/index.js +16 -0
  23. package/dist/cjs/link-replacer.js +19 -13
  24. package/dist/cjs/loaders/cf-puppeteer.js +14 -7
  25. package/dist/cjs/loaders/fetch.js +14 -2
  26. package/dist/cjs/loaders/playwright.js +32 -30
  27. package/dist/cjs/loaders/puppeteer.js +14 -7
  28. package/dist/cjs/md-splitter.js +24 -11
  29. package/dist/cjs/mdast-handlers/custom-code-handler.js +32 -9
  30. package/dist/cjs/mdast-handlers/custom-div-handler.js +14 -35
  31. package/dist/cjs/mdast-handlers/custom-img-handler.js +10 -1
  32. package/dist/cjs/mdast-handlers/custom-table-handler.js +18 -1
  33. package/dist/cjs/mdast-handlers/definition-list-handler.js +17 -6
  34. package/dist/cjs/mdast-handlers/table-cell-flatten.js +195 -0
  35. package/dist/cjs/mdast-handlers/twoslash.js +71 -0
  36. package/dist/cjs/mdast-to-markdown.js +2 -5
  37. package/dist/cjs/metadata/index.js +4 -3
  38. package/dist/cjs/normalize/index.js +11 -2
  39. package/dist/cjs/platform/client.js +73 -16
  40. package/dist/cjs/platform/index.js +4 -0
  41. package/dist/cjs/platform/types.js +7 -1
  42. package/dist/cjs/utils/hast-fast.js +1 -1
  43. package/dist/cjs/utils/parse-html.js +113 -0
  44. package/dist/cjs/utils/pre-text.js +104 -0
  45. package/dist/detect-client-shell.js +53 -0
  46. package/dist/extract-mdast.js +58 -0
  47. package/dist/extract-meta-refresh.js +47 -0
  48. package/dist/extractors/index.js +3 -0
  49. package/dist/extractors/lib/block-features.js +437 -0
  50. package/dist/extractors/lib/block-model.generated.js +21 -0
  51. package/dist/extractors/lib/block-model.js +48 -0
  52. package/dist/extractors/lib/blocks.js +185 -0
  53. package/dist/extractors/lib/code-tabs.js +168 -0
  54. package/dist/extractors/lib/constants.js +1 -1
  55. package/dist/extractors/lib/layout-tables.js +99 -0
  56. package/dist/extractors/lib/sanitize.js +144 -26
  57. package/dist/extractors/lib/takumi-signal.js +53 -0
  58. package/dist/extractors/lib/ui-chrome.js +5 -0
  59. package/dist/extractors/presets/auto.js +3 -3
  60. package/dist/extractors/presets/kiwame.js +206 -0
  61. package/dist/extractors/presets/takumi.js +34 -3
  62. package/dist/html-to-markdown.js +57 -4
  63. package/dist/html-to-mdast.js +4 -3
  64. package/dist/index.js +18 -0
  65. package/dist/link-replacer.js +19 -13
  66. package/dist/loaders/cf-puppeteer.js +15 -8
  67. package/dist/loaders/fetch.js +14 -2
  68. package/dist/loaders/playwright.js +32 -30
  69. package/dist/loaders/puppeteer.js +15 -8
  70. package/dist/md-splitter.js +24 -11
  71. package/dist/mdast-handlers/custom-code-handler.js +30 -9
  72. package/dist/mdast-handlers/custom-div-handler.js +14 -35
  73. package/dist/mdast-handlers/custom-img-handler.js +10 -1
  74. package/dist/mdast-handlers/custom-table-handler.js +19 -2
  75. package/dist/mdast-handlers/definition-list-handler.js +17 -6
  76. package/dist/mdast-handlers/table-cell-flatten.js +170 -0
  77. package/dist/mdast-handlers/twoslash.js +48 -0
  78. package/dist/mdast-to-markdown.js +2 -5
  79. package/dist/metadata/index.js +4 -3
  80. package/dist/normalize/index.js +12 -2
  81. package/dist/platform/client.js +73 -16
  82. package/dist/platform/index.js +4 -0
  83. package/dist/platform/types.js +5 -1
  84. package/dist/types/cli/bin.js.map +1 -1
  85. package/dist/types/cli/commands/jobs/files.d.ts +28 -0
  86. package/dist/types/cli/commands/jobs/files.js.map +1 -0
  87. package/dist/types/cli/commands/jobs/llms-txt.d.ts +33 -0
  88. package/dist/types/cli/commands/jobs/llms-txt.js.map +1 -0
  89. package/dist/types/cli/commands/jobs/options.d.ts +58 -0
  90. package/dist/types/cli/commands/jobs/options.js.map +1 -0
  91. package/dist/types/cli/commands/jobs/run.d.ts +44 -0
  92. package/dist/types/cli/commands/jobs/run.js.map +1 -0
  93. package/dist/types/cli/commands/skill/content.js.map +1 -1
  94. package/dist/types/cli/commands/webforai/interactive.d.ts +1 -1
  95. package/dist/types/cli/commands/webforai/interactive.js.map +1 -1
  96. package/dist/types/cli/commands/webforai/options.d.ts +9 -2
  97. package/dist/types/cli/commands/webforai/options.js.map +1 -1
  98. package/dist/types/cli/commands/webforai/run.js.map +1 -1
  99. package/dist/types/cli/constants.d.ts +3 -1
  100. package/dist/types/cli/constants.js.map +1 -1
  101. package/dist/types/cli/utils.d.ts +8 -1
  102. package/dist/types/cli/utils.js.map +1 -1
  103. package/dist/types/detect-client-shell.d.ts +24 -0
  104. package/dist/types/detect-client-shell.js.map +1 -0
  105. package/dist/types/extract-mdast.js.map +1 -1
  106. package/dist/types/extract-meta-refresh.d.ts +29 -0
  107. package/dist/types/extract-meta-refresh.js.map +1 -0
  108. package/dist/types/extractors/index.d.ts +1 -0
  109. package/dist/types/extractors/index.js.map +1 -1
  110. package/dist/types/extractors/lib/block-features.d.ts +43 -0
  111. package/dist/types/extractors/lib/block-features.js.map +1 -0
  112. package/dist/types/extractors/lib/block-model.d.ts +28 -0
  113. package/dist/types/extractors/lib/block-model.generated.d.ts +5 -0
  114. package/dist/types/extractors/lib/block-model.generated.js.map +1 -0
  115. package/dist/types/extractors/lib/block-model.js.map +1 -0
  116. package/dist/types/extractors/lib/blocks.d.ts +53 -0
  117. package/dist/types/extractors/lib/blocks.js.map +1 -0
  118. package/dist/types/extractors/lib/code-tabs.d.ts +30 -0
  119. package/dist/types/extractors/lib/code-tabs.js.map +1 -0
  120. package/dist/types/extractors/lib/constants.js.map +1 -1
  121. package/dist/types/extractors/lib/layout-tables.d.ts +17 -0
  122. package/dist/types/extractors/lib/layout-tables.js.map +1 -0
  123. package/dist/types/extractors/lib/sanitize.d.ts +33 -3
  124. package/dist/types/extractors/lib/sanitize.js.map +1 -1
  125. package/dist/types/extractors/lib/takumi-signal.d.ts +12 -0
  126. package/dist/types/extractors/lib/takumi-signal.js.map +1 -0
  127. package/dist/types/extractors/lib/ui-chrome.d.ts +8 -0
  128. package/dist/types/extractors/lib/ui-chrome.js.map +1 -1
  129. package/dist/types/extractors/presets/auto.d.ts +6 -1
  130. package/dist/types/extractors/presets/auto.js.map +1 -1
  131. package/dist/types/extractors/presets/kiwame.d.ts +31 -0
  132. package/dist/types/extractors/presets/kiwame.js.map +1 -0
  133. package/dist/types/extractors/presets/takumi.js.map +1 -1
  134. package/dist/types/html-to-markdown.d.ts +9 -0
  135. package/dist/types/html-to-markdown.js.map +1 -1
  136. package/dist/types/html-to-mdast.js.map +1 -1
  137. package/dist/types/index.d.ts +3 -1
  138. package/dist/types/index.js.map +1 -1
  139. package/dist/types/link-replacer.d.ts +3 -1
  140. package/dist/types/link-replacer.js.map +1 -1
  141. package/dist/types/loaders/cf-puppeteer.js.map +1 -1
  142. package/dist/types/loaders/fetch.d.ts +4 -0
  143. package/dist/types/loaders/fetch.js.map +1 -1
  144. package/dist/types/loaders/playwright.js.map +1 -1
  145. package/dist/types/loaders/puppeteer.js.map +1 -1
  146. package/dist/types/md-splitter.d.ts +1 -1
  147. package/dist/types/md-splitter.js.map +1 -1
  148. package/dist/types/mdast-handlers/custom-code-handler.d.ts +9 -0
  149. package/dist/types/mdast-handlers/custom-code-handler.js.map +1 -1
  150. package/dist/types/mdast-handlers/custom-div-handler.js.map +1 -1
  151. package/dist/types/mdast-handlers/custom-img-handler.js.map +1 -1
  152. package/dist/types/mdast-handlers/custom-table-handler.js.map +1 -1
  153. package/dist/types/mdast-handlers/definition-list-handler.d.ts +0 -11
  154. package/dist/types/mdast-handlers/definition-list-handler.js.map +1 -1
  155. package/dist/types/mdast-handlers/table-cell-flatten.d.ts +18 -0
  156. package/dist/types/mdast-handlers/table-cell-flatten.js.map +1 -0
  157. package/dist/types/mdast-handlers/twoslash.d.ts +14 -0
  158. package/dist/types/mdast-handlers/twoslash.js.map +1 -0
  159. package/dist/types/mdast-to-markdown.js.map +1 -1
  160. package/dist/types/metadata/index.js.map +1 -1
  161. package/dist/types/normalize/index.js.map +1 -1
  162. package/dist/types/platform/client.d.ts +2 -1
  163. package/dist/types/platform/client.js.map +1 -1
  164. package/dist/types/platform/error.d.ts +5 -3
  165. package/dist/types/platform/error.js.map +1 -1
  166. package/dist/types/platform/fetch.d.ts +2 -0
  167. package/dist/types/platform/fetch.js.map +1 -1
  168. package/dist/types/platform/index.d.ts +1 -1
  169. package/dist/types/platform/index.js.map +1 -1
  170. package/dist/types/platform/types.d.ts +35 -6
  171. package/dist/types/platform/types.js.map +1 -1
  172. package/dist/types/utils/hast-fast.d.ts +1 -1
  173. package/dist/types/utils/hast-fast.js.map +1 -1
  174. package/dist/types/utils/parse-html.d.ts +33 -0
  175. package/dist/types/utils/parse-html.js.map +1 -0
  176. package/dist/types/utils/pre-text.d.ts +12 -0
  177. package/dist/types/utils/pre-text.js.map +1 -0
  178. package/dist/utils/hast-fast.js +1 -1
  179. package/dist/utils/parse-html.js +87 -0
  180. package/dist/utils/pre-text.js +81 -0
  181. 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
- let markdown = (0, import_mdast_util_to_markdown.toMarkdown)((0, import_mdast_utils.warpRoot)(mdast), toMarkdownOptions).replace(/\*\*\*\*/g, "");
37
- if (baseUrl) {
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 asTitle = (value) => {
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 <= MAX_TITLE_LENGTH ? cleaned : void 0;
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((child) => (0, import_hast_fast.isElement)(child) && (0, import_hast_fast.classList)(child).includes("katex-mathml"));
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 toApiError = (response, body) => {
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
- const message = typeof envelope?.error?.message === "string" ? envelope.error.message : `unexpected response (HTTP ${response.status})`;
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
- "This endpoint needs an API key: pass `apiKey` to createPlatformClient (create one on the platform dashboard).",
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
- const startedAt = Date.now();
118
- for (; ; ) {
119
- const status = await getJob(jobId);
120
- opts.onStatus?.(status);
121
- if (TERMINAL_STATES.has(status.status)) {
122
- return status;
123
- }
124
- if (Date.now() - startedAt + interval > timeout) {
125
- throw new import_error.PlatformApiError("poll_timeout", `job ${jobId} still ${status.status} after ${timeout}ms`, 0);
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
- await sleep(interval, opts.signal);
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 REGIONS = ["auto", "us", "eu", "uk", "jp", "asia"];
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
+ "&nbsp;": " ",
14
+ "&amp;": "&",
15
+ "&lt;": "<",
16
+ "&gt;": ">",
17
+ "&quot;": '"',
18
+ "&#39;": "'"
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
+ };