@isdk/web-fetcher 0.2.12 → 0.3.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 (72) hide show
  1. package/README.action.cn.md +312 -11
  2. package/README.action.md +324 -11
  3. package/README.cn.md +13 -11
  4. package/README.engine.cn.md +96 -13
  5. package/README.engine.md +93 -13
  6. package/README.md +11 -9
  7. package/dist/index.d.mts +521 -50
  8. package/dist/index.d.ts +521 -50
  9. package/dist/index.js +1 -1
  10. package/dist/index.mjs +1 -1
  11. package/docs/README.md +11 -9
  12. package/docs/_media/README.action.md +324 -11
  13. package/docs/_media/README.cn.md +13 -11
  14. package/docs/_media/README.engine.md +93 -13
  15. package/docs/classes/CheerioFetchEngine.md +647 -125
  16. package/docs/classes/ClickAction.md +33 -33
  17. package/docs/classes/EvaluateAction.md +559 -0
  18. package/docs/classes/ExtractAction.md +33 -33
  19. package/docs/classes/FetchAction.md +35 -33
  20. package/docs/classes/FetchEngine.md +528 -122
  21. package/docs/classes/FetchSession.md +38 -16
  22. package/docs/classes/FillAction.md +33 -33
  23. package/docs/classes/GetContentAction.md +33 -33
  24. package/docs/classes/GotoAction.md +33 -33
  25. package/docs/classes/PauseAction.md +33 -33
  26. package/docs/classes/PlaywrightFetchEngine.md +570 -122
  27. package/docs/classes/SubmitAction.md +33 -33
  28. package/docs/classes/TrimAction.md +533 -0
  29. package/docs/classes/WaitForAction.md +33 -33
  30. package/docs/classes/WebFetcher.md +9 -9
  31. package/docs/enumerations/FetchActionResultStatus.md +4 -4
  32. package/docs/functions/fetchWeb.md +6 -6
  33. package/docs/globals.md +6 -0
  34. package/docs/interfaces/BaseFetchActionProperties.md +12 -12
  35. package/docs/interfaces/BaseFetchCollectorActionProperties.md +16 -16
  36. package/docs/interfaces/BaseFetcherProperties.md +28 -28
  37. package/docs/interfaces/DispatchedEngineAction.md +4 -4
  38. package/docs/interfaces/EvaluateActionOptions.md +81 -0
  39. package/docs/interfaces/ExtractActionProperties.md +12 -12
  40. package/docs/interfaces/FetchActionInContext.md +15 -15
  41. package/docs/interfaces/FetchActionProperties.md +13 -13
  42. package/docs/interfaces/FetchActionResult.md +6 -6
  43. package/docs/interfaces/FetchContext.md +38 -38
  44. package/docs/interfaces/FetchEngineContext.md +33 -33
  45. package/docs/interfaces/FetchMetadata.md +5 -5
  46. package/docs/interfaces/FetchResponse.md +14 -14
  47. package/docs/interfaces/FetchReturnTypeRegistry.md +8 -8
  48. package/docs/interfaces/FetchSite.md +31 -31
  49. package/docs/interfaces/FetcherOptions.md +30 -30
  50. package/docs/interfaces/GotoActionOptions.md +6 -6
  51. package/docs/interfaces/PendingEngineRequest.md +3 -3
  52. package/docs/interfaces/StorageOptions.md +5 -5
  53. package/docs/interfaces/SubmitActionOptions.md +2 -2
  54. package/docs/interfaces/TrimActionOptions.md +27 -0
  55. package/docs/interfaces/WaitForActionOptions.md +5 -5
  56. package/docs/type-aliases/BaseFetchActionOptions.md +1 -1
  57. package/docs/type-aliases/BaseFetchCollectorOptions.md +1 -1
  58. package/docs/type-aliases/BrowserEngine.md +1 -1
  59. package/docs/type-aliases/FetchActionCapabilities.md +1 -1
  60. package/docs/type-aliases/FetchActionCapabilityMode.md +1 -1
  61. package/docs/type-aliases/FetchActionOptions.md +1 -1
  62. package/docs/type-aliases/FetchEngineAction.md +2 -2
  63. package/docs/type-aliases/FetchEngineType.md +1 -1
  64. package/docs/type-aliases/FetchReturnType.md +1 -1
  65. package/docs/type-aliases/FetchReturnTypeFor.md +1 -1
  66. package/docs/type-aliases/OnFetchPauseCallback.md +1 -1
  67. package/docs/type-aliases/ResourceType.md +1 -1
  68. package/docs/type-aliases/TrimPreset.md +13 -0
  69. package/docs/variables/DefaultFetcherProperties.md +1 -1
  70. package/docs/variables/FetcherOptionKeys.md +1 -1
  71. package/docs/variables/TRIM_PRESETS.md +11 -0
  72. package/package.json +4 -4
@@ -90,6 +90,8 @@ export class FillAction extends FetchAction {
90
90
  * ...其他导航选项,如 **`waitUntil`**, **`timeout`**,这些选项会传递给引擎。
91
91
  * **`returns`**: `response`
92
92
 
93
+ > **注意**:此 Action 可以在单个会话脚本中多次调用。引擎确保每次导航都已完成且其对应的 Action 循环已稳定,然后再处理序列中的下一个 Action。
94
+
93
95
  #### `click`
94
96
 
95
97
  点击由 **`selector`** (CSS 选择器) 指定的元素。
@@ -123,6 +125,41 @@ export class FillAction extends FetchAction {
123
125
  * **`selector`** (string, optional): 表单元素的选择器。
124
126
  * **`returns`**: `none`
125
127
 
128
+ #### `trim`
129
+
130
+ 从 DOM 中移除特定元素以在提取前清理页面。这会对当前会话的页面状态进行持久修改。
131
+
132
+ * **`id`**: `trim`
133
+ * **`params`**:
134
+ * **`selectors`** (string | string[], optional): 一个或多个要移除元素的 CSS 选择器。
135
+ * **`presets`** (string | string[], optional): 预定义的移除元素组。支持的预设:
136
+ * `scripts`: 移除所有 `<script>` 标签。
137
+ * `styles`: 移除所有 `<style>` 和 `<link rel="stylesheet">` 标签。
138
+ * `svgs`: 移除所有 `<svg>` 元素。
139
+ * `images`: 移除 `<img>`, `<picture>` 和 `<canvas>` 元素。
140
+ * `comments`: 移除 HTML 注释。
141
+ * `hidden`: 移除带有 `hidden` 属性或内联 `display:none` 的元素。在 **browser** 模式下,它还会检测并移除通过外部 CSS(如样式表中的 `display: none` 或 `visibility: hidden`)隐藏的元素。
142
+ * `all`: 包含上述所有预设。
143
+ * **`returns`**: `none`
144
+
145
+ **示例:在提取前清理页面**
146
+
147
+ ```json
148
+ {
149
+ "actions": [
150
+ { "action": "goto", "params": { "url": "https://example.com" } },
151
+ {
152
+ "action": "trim",
153
+ "params": {
154
+ "selectors": ["#ad-banner", ".popup"],
155
+ "presets": ["scripts", "styles", "comments"]
156
+ }
157
+ },
158
+ { "action": "extract", "params": { "schema": { "content": "#main-content" } } }
159
+ ]
160
+ }
161
+ ```
162
+
126
163
  #### `waitFor`
127
164
 
128
165
  暂停执行,以等待一个或多个条件的满足。
@@ -208,6 +245,51 @@ await fetchWeb({
208
245
  * **`params`**: (无)
209
246
  * **`returns`**: `response`
210
247
 
248
+ #### `evaluate`
249
+
250
+ 在页面上下文中执行 JavaScript 函数或表达式。这是一个强大的 Action,用于执行内置 Action 未涵盖的自定义逻辑。
251
+
252
+ * **`id`**: `evaluate`
253
+ * **`params`**:
254
+ * **`fn`** (string | function): 要执行的函数或表达式。
255
+ * **`args`** (any, 可选): 传递给函数的单个参数。如需传递多个参数,请使用数组或对象。
256
+ * **`returns`**: `any` (执行结果)
257
+
258
+ > **💡 跨引擎兼容性**:
259
+ >
260
+ > * **`browser`**: 直接在浏览器中运行。
261
+ > * **`http`**: 在 Node.js 中运行,并提供模拟环境(提供 `window`, `document` 和 `$`)。
262
+ * **`args`**: `any` - 传递给函数的单个参数。如需传递多个值,请使用数组或对象。
263
+
264
+ **核心特性:**
265
+
266
+ - **自动导航检测**:如果代码修改了 `window.location.href` 或调用了 `assign()`/`replace()`,引擎会自动触发并等待导航完成。
267
+ - **增强型 Mock DOM (HTTP 模式)**:支持常用的 DOM 方法,如 `querySelector`, `querySelectorAll`, `getElementById`, `getElementsByClassName`,以及 `document.body` 和 `document.title` 等属性。
268
+ - **沙箱安全**:在 HTTP 模式下使用 `util-ex` 的 `newFunction`,防止全局状态污染。
269
+
270
+ **示例 (数组参数):**
271
+
272
+ ```json
273
+ {
274
+ "action": "evaluate",
275
+ "params": {
276
+ "fn": "([a, b]) => a + b",
277
+ "args": [1, 2]
278
+ }
279
+ }
280
+ ```
281
+
282
+ **示例 (导航):**
283
+
284
+ ```json
285
+ {
286
+ "action": "evaluate",
287
+ "params": {
288
+ "fn": "() => { window.location.href = '/new-page'; }"
289
+ }
290
+ }
291
+ ```
292
+
211
293
  #### `extract`
212
294
 
213
295
  使用一个强大且声明式的 **`ExtractSchema`** 从当前页面中提取结构化数据。这是进行数据采集的核心 Action。
@@ -224,6 +306,8 @@ await fetchWeb({
224
306
 
225
307
  最基础的提取,可以指定 **`selector`** (CSS 选择器), **`attribute`** (要提取的属性名), **`type`** (string, number, boolean, html), 以及 **`mode`** (text, innerText)。
226
308
 
309
+ * **`depth`** (number, 可选): 在通过 `selector` 匹配到元素后, 向上回溯指定的 DOM 层级。最终回溯到的祖先元素将作为实际的值提取目标 (例如, 用于从父级包装容器中提取属性)。
310
+
227
311
  ```json
228
312
  {
229
313
  "id": "extract",
@@ -261,6 +345,20 @@ await fetchWeb({
261
345
  }
262
346
  ```
263
347
 
348
+ * **`depth`** (number, 可选): 启用“按需回溯 (Try-And-Bubble)”策略。如果在当前匹配的元素中缺少 `required` (必填) 字段,引擎会尝试向上回溯 DOM 树(最多 `depth` 层),寻找包含该必填字段的祖先元素。当选择器匹配的是后代元素(如内部 `span`),但数据位于父级容器时,此功能非常有用。
349
+
350
+ **Object 进阶功能:**
351
+
352
+ * **锚点跳转 (`anchor`)**: 指定该字段提取的起始参考点。
353
+ * **字段引用**: 引用同一对象中先前已提取的字段(使用其 DOM 元素)。
354
+ * **CSS 选择器**: 在当前对象作用域内即时查找锚点。
355
+ * **`depth`** (number, 可选): 使用锚点时,定义向上回溯多少层父节点来收集后续兄弟节点。
356
+ * **注意**: 如果省略,引擎默认会回溯到最大深度(直至对象根节点)以保持向后兼容。若要严格限制在锚点自身的兄弟节点内搜索,请设置 `depth: 0`。
357
+ * **效果**: 锚点生效后,该字段的搜索范围将变为锚点**之后**的兄弟节点(及其祖先节点,取决于 `depth`)。这允许在平铺结构中实现非线性的“跳转”提取。
358
+ * **顺序消费 (`relativeTo: "previous"`)**:
359
+ * 配合 `order` 属性,使每个字段的搜索范围都从前一个字段的匹配位置之后开始。
360
+ * 这对于提取由多个相同标签组成的列表(如多个连续的 `<p>` 标签分别代表不同含义)至关重要。
361
+
264
362
  ###### 3. 提取数组 (便捷用法)
265
363
 
266
364
  通过 **`type: 'array'`** 来提取一个列表。为了让最常见的操作更简单,我们提供了一些便捷用法。
@@ -303,7 +401,9 @@ await fetchWeb({
303
401
 
304
402
  ###### 4. 列对齐模式 (Columnar Mode,原 Zip 策略)
305
403
 
306
- 当 `selector` 指向一个**容器**(如结果列表)且项目数据以平行的独立列散落在其中时使用。
404
+ 当 `selector` 指向一个**容器**(如结果列表)且项目数据以平行的独立列散落在其中时使用。此模式针对性能进行了深度优化,特别是在浏览器模式下,通过最小化 DOM 查询和 RPC 调用来提升速度。
405
+
406
+ > **💡 广播与性能 (Broadcasting & Performance)**:如果某个属性匹配容器元素本身(例如省略 selector 或匹配容器自身的属性),其值将被**广播**到每一行。这不仅是一个功能,更是一项重大性能优化:该值仅被提取**一次**并在所有行中复用,避免了成千上万次冗余的引擎调用。
307
407
 
308
408
  ```json
309
409
  {
@@ -322,14 +422,44 @@ await fetchWeb({
322
422
 
323
423
  > **启发式自动检测:** 如果省略了 `mode`,且 `selector` 恰好只匹配到一个元素,同时 `items` 中包含选择器,引擎会自动使用 **columnar** 模式。
324
424
 
425
+ **示例:列模式的广播行为 (Broadcasting)**
426
+
427
+ 当列表中的某些数据(如分类)位于容器上,而具体项目在容器内部时。
428
+
429
+ ```json
430
+ {
431
+ "id": "extract",
432
+ "params": {
433
+ "type": "array",
434
+ "selector": "#book-category",
435
+ "mode": "columnar",
436
+ "items": {
437
+ "category": { "attribute": "data-category" },
438
+ "title": { "selector": ".book-title" }
439
+ }
440
+ }
441
+ }
442
+ ```
443
+
444
+ > 如果 `#book-category` 拥有 `data-category="Sci-Fi"` 属性并包含 3 本书,结果将返回 3 个项目,且每个项目都带有 `"category": "Sci-Fi"`。
445
+
325
446
  **Columnar 配置参数:**
326
447
 
327
448
  * **`strict`** (boolean, 默认: `true`): 如果为 `true`,当不同字段匹配到的数量不一致时将抛出错误。
328
- * **`inference`** (boolean, 默认: `false`): 如果为 `true`,当字段数量不匹配时,尝试通过 DOM 树自动寻找“包裹元素”来修复错位的列表。
449
+ * **`inference`** (boolean, 默认: `false`): 如果为 `true`,当字段数量不匹配时,尝试通过 DOM 树自动寻找“包裹元素”来修复错位的列表。它使用经过优化的**祖先搜索算法**,在浏览器模式下比手动遍历快得多。
450
+ * **性能备注**: 引擎会自动检测共享结构并预计算对齐方式,确保即使在复杂的 DOM 树中也能保持 O(N) 的性能。在浏览器模式下,通过预计算“广播”标志来最小化 IPC 往返开销。
329
451
 
330
452
  ###### 5. 分段扫描模式 (Segmented Mode)
331
453
 
332
- 适用于完全“平铺”且没有包裹元素的结构。它使用第一个字段(或指定的 `anchor`)作为锚点来对容器内容进行分段。
454
+ 适用于完全“平铺”且没有包裹元素的结构。它使用指定的 `anchor` 锚点来对容器内容进行分段。
455
+
456
+ **核心特性:自动容器检测 (Bubble Up)**
457
+
458
+ 为了处理“看似平铺实则有细微容器”的结构,引擎引入了冒泡定位策略:
459
+
460
+ - **智能冒泡**:当锚点深埋于嵌套结构中(如 `div.card > h3.title`),引擎会自动向上回溯,寻找在不包含相邻锚点的前提下最大的“安全容器”(如 `div.card`)。
461
+ - **逻辑隔离**:如果找到安全容器,该容器将成为分段的作用域。这使您能够通过简单的相对选择器提取容器内的任何内容,即使它位于锚点的“上方”或深层。
462
+ - **平铺回退**:如果锚点之间完全没有容器隔离,引擎将自动回退到传统的兄弟节点扫描模式。
333
463
 
334
464
  ```json
335
465
  {
@@ -337,7 +467,7 @@ await fetchWeb({
337
467
  "params": {
338
468
  "type": "array",
339
469
  "selector": "#flat-container",
340
- "mode": { "type": "segmented", "anchor": "title" },
470
+ "mode": { "type": "segmented", "anchor": "h3.item-title" },
341
471
  "items": {
342
472
  "title": { "selector": "h3" },
343
473
  "desc": { "selector": "p" }
@@ -346,26 +476,103 @@ await fetchWeb({
346
476
  }
347
477
  ```
348
478
 
349
- > 在此模式下,每当引擎发现一个 `h3`,就会开启一个新项目。随后找到的 `p` 标签(直到下一个 `h3` 出现前)都归属于该项目。
479
+ **Segmented 配置参数:**
480
+
481
+ * **`anchor`** (string):
482
+ * 可以是 `items` 中定义的**字段名**(如 `"title"`)。
483
+ * 也可以是直接的 **CSS 选择器**(如 `"h3.item-title"`)。
484
+ * 默认使用 `items` 中第一个字段的选择器。
485
+ * **`depth`** (number, 可选): 从锚点向上寻找分段容器时的最大回溯层级。如果省略,它将尽可能向上冒泡,只要不与相邻分段冲突。
486
+ * **`strict`** (boolean, 默认: `false`): 如果为 `true`,若未找到任何锚点元素,或任何项目违反了自身的 `required` 约束,将抛出错误。
350
487
 
351
- ###### 6. 隐式对象提取 (最简语法)
488
+ ###### 5.1 进阶:处理重复标签 (relativeTo)
352
489
 
353
- 为了让对象提取更简单,你可以省略 `type: 'object'` `properties`。如果 schema 对象包含非保留关键字(如 `selector`, `attribute`, `type` 等)的键,它将被视为对象 schema,其中的键作为属性名。
490
+ 当一个分段中包含多个完全相同的标签(例如连续多个 `<p>` 标签)分别代表不同字段时,可以使用 `relativeTo: "previous"` 按顺序“消耗”它们。
491
+
492
+ ```json
493
+ {
494
+ "id": "extract",
495
+ "params": {
496
+ "type": "array",
497
+ "selector": "#container",
498
+ "mode": {
499
+ "type": "segmented",
500
+ "anchor": ".item-start",
501
+ "relativeTo": "previous"
502
+ },
503
+ "items": {
504
+ "type": "object",
505
+ "order": ["id", "desc", "extra"],
506
+ "properties": {
507
+ "id": "h1",
508
+ "desc": "p",
509
+ "extra": "p"
510
+ }
511
+ }
512
+ }
513
+ }
514
+ ```
515
+
516
+ * **`relativeTo: "previous"`**: 找到 `id` (h1) 后,查找 `desc` 会从该 h1 之后开始。找到 `desc` (第一个 p) 后,查找 `extra` 会从该 p 之后开始,从而成功匹配到第二个 `<p>`。
517
+ * **`order`**: 定义了消耗 DOM 的确切顺序。在使用 `relativeTo: "previous"` 时强烈建议显式指定。
518
+
519
+ ###### 6. 数据质量控制: `required` 和 `strict`
520
+
521
+ - **`required`**: 将字段标记为必填。
522
+ - **Object 中**: 如果任何必填字段为 `null`,整个对象返回 `null`。
523
+ - **Array 中**: 缺失必填字段的项目会被自动跳过。
524
+ - **空值传播**: 对于没有 `selector` 的隐式对象,如果其所有子字段均为 `null`,则该对象本身被视为 `null`,从而触发父级的必填或跳过逻辑。
525
+ - **`strict`**:
526
+ - `false` (默认): 静默跳过不完整数据。
527
+ - `true`: 任何必填项缺失或列模式对齐失败都将抛出错误。
528
+ - **继承性**: 在数组模式中设置 `strict` 会自动向下传递给所有嵌套子项。
529
+
530
+ **示例:忽略缺少关键信息的项目**
531
+
532
+ ```json
533
+ {
534
+ "id": "extract",
535
+ "params": {
536
+ "type": "array",
537
+ "selector": ".product-list",
538
+ "mode": "columnar",
539
+ "items": {
540
+ "name": { "selector": ".title", "required": true },
541
+ "price": { "selector": ".price", "required": true },
542
+ "discount": ".promo"
543
+ }
544
+ }
545
+ }
546
+ ```
547
+
548
+ > 在此示例中,如果某个产品缺少 `name` 或 `price`,它将从结果数组中完全剔除。可选的 `discount` 字段缺失不会影响该项的保留。
549
+
550
+ ###### 7. 隐式对象提取 (最简语法)
551
+
552
+ 为了让对象提取更简单,你可以省略 `type: 'object'` 和 `properties`。如果 schema 对象包含非上下文定义关键字(如 `selector`, `has`, `exclude`, `required`, `strict`, `depth`)的键,它将被视为对象 schema,其中的键作为属性名。
553
+
554
+ > **关键字冲突处理:** 您可以安全地抓取名为 `type` 的数据字段,只要它的值不是保留的 Schema 类型(如 `"string"`, `"object"`, `"array"` 等)。
354
555
 
355
556
  ```json
356
557
  {
357
558
  "id": "extract",
358
559
  "params": {
359
560
  "selector": ".author-bio",
360
- "name": { "selector": ".author-name" },
361
- "email": { "selector": "a.email", "attribute": "href" }
561
+ "name": ".author-name",
562
+ "type": ".author-rank",
563
+ "items": { "type": "array", "selector": "li" }
362
564
  }
363
565
  }
364
566
  ```
365
567
 
366
- > 这等同于示例 2,但更简洁。
568
+ > **隐式对象的核心特性:**
569
+ >
570
+ > 1. **关键字处理**:常用的配置关键字如 `items`、`attribute` 或 `mode` **可以作为属性名**在隐式对象中使用。只有当显式存在 `type`(如 `array`)时,它们才会被视为配置项。同时,`required`、`strict` 和 `depth` 也会被当作上下文定义关键字处理。
571
+ > 2. **字符串简写**:你可以直接使用字符串作为属性值(例如 `"email": "a.email"`),它会自动扩展为 `{ "selector": "a.email" }`。
572
+ > 3. **上下文分离**:只有 `selector`、`has`、`exclude`、`required`、`strict` 和 `depth` 用于定义隐式对象的上下文及校验逻辑;所有其他键都被视为要提取的数据。
573
+ > 4. **空值传递 (Null Propagation)**: 如果一个隐式对象没有 `selector`,且其所有的子属性提取结果均为 `null`,则该对象本身返回 `null`。这对于父对象的 `required` 校验或数组中的跳过逻辑至关重要。
367
574
 
368
- ###### 6. 精确筛选: `has` 和 `exclude`
575
+ ###### 8. 精确筛选: `has` 和 `exclude`
369
576
 
370
577
  您可以在任何包含 **`selector`** 的 Schema 中使用 **`has`** 和 **`exclude`** 字段来精确控制元素的选择。
371
578
 
@@ -545,3 +752,97 @@ await fetchWeb({
545
752
  * `protected onAfterExec?()`: 在 `onExecute` 执行后调用。
546
753
 
547
754
  对于需要管理复杂状态或资源的 Action,可以实现这些钩子。通常,对于组合式 Action,直接在 `onExecute` 中编写逻辑已经足够。
755
+
756
+ ---
757
+
758
+ ## 💎 7. Action 返回类型与状态管理 (进阶)
759
+
760
+ 在 `@isdk/web-fetcher` 中,Action 的 `static returnType` 不仅仅是一个类型提示。它定义了框架如何管理 **Session 状态** 以及如何在执行后自动同步数据。
761
+
762
+ ### 7.1 返回类型详解
763
+
764
+ #### 🟢 `response` (页面响应)
765
+
766
+ * **定义**: 包含 HTTP 状态码、响应头、正文和 Cookie 的 `FetchResponse` 对象。
767
+ * **用途**: 将最新的页面内容和状态同步到会话中。
768
+ * **用法**: 适用于执行导航、刷新或获取当前页面快照的动作。
769
+ * **系统行为**: 框架在 `afterExec` 阶段会自动将此结果更新到 `context.lastResponse`。后续动作可以通过上下文访问它。
770
+ * **典型 Action**: `goto`, `getContent`, `fill`(在某些引擎中)。
771
+ * **示例**:
772
+
773
+ ```typescript
774
+ export class MyNavigateAction extends FetchAction {
775
+ static override id = 'myGoto';
776
+ static override returnType = 'response' as const;
777
+
778
+ async onExecute(context, options) {
779
+ // 返回 FetchResponse 的逻辑
780
+ return await this.delegateToEngine(context, 'goto', options.params.url);
781
+ }
782
+ }
783
+ ```
784
+
785
+ #### 🟡 `any` (通用数据 - 默认)
786
+
787
+ * **定义**: 任何可序列化的数据结构(对象、数组、字符串等)。
788
+ * **用途**: 业务数据提取的主要机制。
789
+ * **用法**: 当你的动作产生处理后的业务数据(而不是代表整个页面或系统状态)时使用。
790
+ * **系统行为**: 如果 Action 配置包含 `storeAs: "key"`,框架会自动将 `result` 保存到 `context.outputs["key"]` 中。
791
+ * **典型 Action**: `extract`。
792
+ * **示例**:
793
+
794
+ ```typescript
795
+ static override returnType = 'any' as const;
796
+ async onExecute(context, options) {
797
+ return { title: 'Hello', price: 99 }; // 如果设置了 storeAs,则保存到 outputs
798
+ }
799
+ ```
800
+
801
+ #### ⚪ `none` (无返回)
802
+
803
+ * **定义**: `void`。
804
+ * **用途**: 纯交互或副作用,不输出数据。
805
+ * **用法**: 执行 UI 交互或时间控制的动作。
806
+ * **典型 Action**: `click`, `submit`, `pause`, `trim`, `waitFor`。
807
+ * **示例**:
808
+
809
+ ```typescript
810
+ static override returnType = 'none' as const;
811
+ async onExecute(context, options) {
812
+ await this.delegateToEngine(context, 'click', options.params.selector);
813
+ // 不需要返回值
814
+ }
815
+ ```
816
+
817
+ #### 🔵 `outputs` (累积结果)
818
+
819
+ * **定义**: 整个 `context.outputs` 记录 (`Record<string, any>`)。
820
+ * **用途**: 获取当前会话期间提取并存储的所有数据。
821
+ * **用法**: 通常用作链条末尾的“总结”动作或用于调试。
822
+ * **典型 Action**: 自定义数据总结动作。
823
+
824
+ #### 🟣 `context` (会话快照)
825
+
826
+ * **定义**: 完整的 `FetchContext` 对象。
827
+ * **用途**: 元编程和深度调试。
828
+ * **用法**: 允许调用者检查当前的会话配置(超时、代理、请求头)和内部引擎元数据。
829
+
830
+ ---
831
+
832
+ ### 7.2 结果包装机制 (`FetchActionResult`)
833
+
834
+ `onExecute` 返回的每个值都会被 `FetchAction.execute` 方法自动包装成 `FetchActionResult` 对象。这确保了所有动作之间一致的错误处理和元数据追踪。
835
+
836
+ **`FetchActionResult` 的结构**:
837
+
838
+ * `status`: `Success` (成功), `Failed` (失败), 或 `Skipped` (跳过)。
839
+ * `returnType`: 匹配 Action 的 `static returnType`。
840
+ * `result`: `onExecute` 返回的原始数据。
841
+ * `error`: 如果动作失败,捕获到的错误对象。
842
+ * `meta`: 诊断信息,包括执行时间、引擎类型和重试次数。
843
+
844
+ ### 7.3 开发者最佳实践
845
+
846
+ 1. **为导航选择 `response`**: 对于跳转到新 URL 的动作,务必使用 `response`,以确保会话的“当前页面”保持同步。
847
+ 2. **利用 `any` + `storeAs`**: 对于数据提取,将数据作为 `any` 返回,并让用户通过 JSON 脚本中的 `storeAs` 决定存储键。
848
+ 3. **明确使用 `none`**: 使用 `none` 可以清晰地表明该动作用于其副作用(如点击或等待),使工作流更易于理解。