@wenathlan/saddle 1.8.17 → 2.0.2
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/Dockerfile +1339 -0
- package/README.md +68 -22
- package/alternatives.ts +1218 -0
- package/biome.json +153 -0
- package/boards.json +1517 -0
- package/compute.ts +3144 -0
- package/cores.json +1801 -0
- package/dist/acquisition.d.ts +359 -0
- package/dist/acquisition.d.ts.map +1 -0
- package/dist/acquisition.js +629 -0
- package/dist/acquisition.js.map +1 -0
- package/dist/alternatives.d.ts +509 -0
- package/dist/alternatives.d.ts.map +1 -0
- package/dist/alternatives.js +951 -0
- package/dist/alternatives.js.map +1 -0
- package/dist/automation.d.ts +426 -0
- package/dist/automation.d.ts.map +1 -0
- package/dist/automation.js +561 -0
- package/dist/automation.js.map +1 -0
- package/dist/browser.d.ts +562 -0
- package/dist/browser.d.ts.map +1 -0
- package/dist/browser.js +1057 -0
- package/dist/browser.js.map +1 -0
- package/dist/capacitor.config.d.ts +13 -0
- package/dist/capacitor.config.d.ts.map +1 -0
- package/dist/capacitor.config.js +26 -0
- package/dist/capacitor.config.js.map +1 -0
- package/dist/cli.d.ts +17 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +111 -0
- package/dist/cli.js.map +1 -0
- package/dist/communication.d.ts +192 -0
- package/dist/communication.d.ts.map +1 -0
- package/dist/communication.js +418 -0
- package/dist/communication.js.map +1 -0
- package/dist/compute.d.ts +1161 -0
- package/dist/compute.d.ts.map +1 -0
- package/dist/compute.js +2273 -0
- package/dist/compute.js.map +1 -0
- package/dist/distribution.d.ts +529 -0
- package/dist/distribution.d.ts.map +1 -0
- package/dist/distribution.js +811 -0
- package/dist/distribution.js.map +1 -0
- package/dist/execution.d.ts +530 -0
- package/dist/execution.d.ts.map +1 -0
- package/dist/execution.js +761 -0
- package/dist/execution.js.map +1 -0
- package/dist/format.d.ts +19 -0
- package/dist/format.d.ts.map +1 -0
- package/dist/format.js +60 -0
- package/dist/format.js.map +1 -0
- package/dist/foundation.d.ts +176 -0
- package/dist/foundation.d.ts.map +1 -0
- package/dist/foundation.js +309 -0
- package/dist/foundation.js.map +1 -0
- package/dist/index.d.ts +733 -122
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1018 -122
- package/dist/index.js.map +1 -1
- package/dist/integration.d.ts +236 -0
- package/dist/integration.d.ts.map +1 -0
- package/dist/integration.js +236 -0
- package/dist/integration.js.map +1 -0
- package/dist/intelligence.d.ts +94 -0
- package/dist/intelligence.d.ts.map +1 -0
- package/dist/intelligence.js +145 -0
- package/dist/intelligence.js.map +1 -0
- package/dist/isolation.d.ts +129 -0
- package/dist/isolation.d.ts.map +1 -0
- package/dist/isolation.js +96 -0
- package/dist/isolation.js.map +1 -0
- package/dist/media.d.ts +344 -0
- package/dist/media.d.ts.map +1 -0
- package/dist/media.js +986 -0
- package/dist/media.js.map +1 -0
- package/dist/modes.d.ts +359 -0
- package/dist/modes.d.ts.map +1 -0
- package/dist/modes.js +134 -0
- package/dist/modes.js.map +1 -0
- package/dist/{library/public.d.ts → operations.d.ts} +15 -1
- package/dist/operations.d.ts.map +1 -0
- package/dist/{library/public.js → operations.js} +18 -7
- package/dist/operations.js.map +1 -0
- package/dist/orchestrator.d.ts +1339 -0
- package/dist/orchestrator.d.ts.map +1 -0
- package/dist/orchestrator.js +4308 -0
- package/dist/orchestrator.js.map +1 -0
- package/dist/performance.d.ts +416 -0
- package/dist/performance.d.ts.map +1 -0
- package/dist/performance.js +827 -0
- package/dist/performance.js.map +1 -0
- package/dist/quantum.d.ts +810 -0
- package/dist/quantum.d.ts.map +1 -0
- package/dist/quantum.js +2073 -0
- package/dist/quantum.js.map +1 -0
- package/dist/render.d.ts +381 -0
- package/dist/render.d.ts.map +1 -0
- package/dist/render.js +1073 -0
- package/dist/render.js.map +1 -0
- package/dist/scheduler.d.ts +596 -0
- package/dist/scheduler.d.ts.map +1 -0
- package/dist/scheduler.js +1259 -0
- package/dist/scheduler.js.map +1 -0
- package/dist/security.d.ts +501 -0
- package/dist/security.d.ts.map +1 -0
- package/dist/security.js +1170 -0
- package/dist/security.js.map +1 -0
- package/dist/server.d.ts +21 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/{server/node.js → server.js} +16 -2
- package/dist/server.js.map +1 -0
- package/dist/tiers.d.ts +1002 -0
- package/dist/tiers.d.ts.map +1 -0
- package/dist/tiers.js +2165 -0
- package/dist/tiers.js.map +1 -0
- package/dist/virtual.d.ts +641 -0
- package/dist/virtual.d.ts.map +1 -0
- package/dist/virtual.js +1285 -0
- package/dist/virtual.js.map +1 -0
- package/dist/virtualcpu.d.ts +316 -0
- package/dist/virtualcpu.d.ts.map +1 -0
- package/dist/virtualcpu.js +1180 -0
- package/dist/virtualcpu.js.map +1 -0
- package/dist/virtualgpu.d.ts +345 -0
- package/dist/virtualgpu.d.ts.map +1 -0
- package/dist/virtualgpu.js +1000 -0
- package/dist/virtualgpu.js.map +1 -0
- package/dist/virtualization.d.ts +607 -0
- package/dist/virtualization.d.ts.map +1 -0
- package/dist/virtualization.js +1187 -0
- package/dist/virtualization.js.map +1 -0
- package/dist/virtualmemory.d.ts +818 -0
- package/dist/virtualmemory.d.ts.map +1 -0
- package/dist/virtualmemory.js +1796 -0
- package/dist/virtualmemory.js.map +1 -0
- package/dist/webscrape.d.ts +720 -0
- package/dist/webscrape.d.ts.map +1 -0
- package/dist/webscrape.js +2697 -0
- package/dist/webscrape.js.map +1 -0
- package/docker.config +769 -0
- package/docs/CONVERSA.txt +1523 -0
- package/docs/alternatives.md +499 -0
- package/docs/architecture-1.8.19.md +76 -0
- package/docs/architecture-1.8.19.mmd +33 -0
- package/docs/architecture-1.8.19.png +0 -0
- package/docs/architecture.md +355 -0
- package/docs/artifactavailability.md +6 -0
- package/docs/brancharchive-2026-08-18.md +18 -0
- package/docs/browser.md +7 -0
- package/docs/consolidation.md +70 -0
- package/docs/e2ugh-engine.md +528 -0
- package/{dist/examples/localjob.js → docs/example-localjob.ts} +3 -3
- package/{dist/examples/publicapi.js → docs/example-publicapi.ts} +2 -2
- package/docs/hardware.md +342 -0
- package/docs/optimization.md +126 -0
- package/docs/performance.md +499 -0
- package/docs/planning.1.8.18.md +94 -0
- package/docs/planning.1.8.19.md +49 -0
- package/docs/releasenotes-1.8.17.md +6 -0
- package/docs/releasenotes-1.8.18.md +32 -0
- package/docs/releasenotes-1.8.19.md +35 -0
- package/docs/research-1.8.18-isolation.md +43 -0
- package/docs/research-1.8.19-virtual-browser.md +27 -0
- package/docs/saddle.archive.1.8.17.tar.gz.gpg +0 -0
- package/docs/security.md +454 -0
- package/docs/todo-1.8.16.md +56 -1
- package/docs/todo-1.8.18.md +164 -0
- package/docs/todo-1.8.19.md +241 -0
- package/docs/viability.md +777 -0
- package/docs/virtualization.md +335 -0
- package/docs/web-duplicatecleanup.md +9 -0
- package/docs/web-ideas.md +106 -0
- package/docs/workflowimprovements-2026-08-19.md +70 -0
- package/gpumonitor.cpp +1543 -0
- package/gpus.json +2408 -0
- package/index.ts +1478 -0
- package/media.ts +1337 -0
- package/mttg.config +1549 -0
- package/orchestrator.ts +5462 -0
- package/package.json +304 -55
- package/passage.config +1331 -0
- package/performance.ts +1049 -0
- package/processors.json +2217 -0
- package/qemu.config +1062 -0
- package/qemubridge.py +1340 -0
- package/quantum.ts +2393 -0
- package/render.ts +1325 -0
- package/scheduler.ts +1600 -0
- package/security.ts +1449 -0
- package/tiers.ts +2871 -0
- package/tsconfig.json +29 -0
- package/virtualcpu.ts +1273 -0
- package/virtualgpu.ts +1181 -0
- package/virtualhardware.c +1011 -0
- package/virtualhardware.json +739 -0
- package/virtualization.ts +1628 -0
- package/virtualizationcore.cpp +4635 -0
- package/virtualmemory.ts +2303 -0
- package/vm.config.json +1910 -0
- package/{extension → web/extension}/manifest.json +1 -1
- package/web/readme.md +382 -0
- package/web/tsconfig.json +25 -0
- package/dist/adapters/forge.d.ts +0 -17
- package/dist/adapters/forge.d.ts.map +0 -1
- package/dist/adapters/forge.js +0 -19
- package/dist/adapters/forge.js.map +0 -1
- package/dist/adapters/forgejo.d.ts +0 -49
- package/dist/adapters/forgejo.d.ts.map +0 -1
- package/dist/adapters/forgejo.js +0 -8
- package/dist/adapters/forgejo.js.map +0 -1
- package/dist/adapters/github.d.ts +0 -12
- package/dist/adapters/github.d.ts.map +0 -1
- package/dist/adapters/github.js +0 -20
- package/dist/adapters/github.js.map +0 -1
- package/dist/adapters/gitlab.d.ts +0 -17
- package/dist/adapters/gitlab.d.ts.map +0 -1
- package/dist/adapters/gitlab.js +0 -10
- package/dist/adapters/gitlab.js.map +0 -1
- package/dist/adapters/huggingface.d.ts +0 -17
- package/dist/adapters/huggingface.d.ts.map +0 -1
- package/dist/adapters/huggingface.js +0 -6
- package/dist/adapters/huggingface.js.map +0 -1
- package/dist/adapters/socket.d.ts +0 -11
- package/dist/adapters/socket.d.ts.map +0 -1
- package/dist/adapters/socket.js +0 -17
- package/dist/adapters/socket.js.map +0 -1
- package/dist/adapters/transport.d.ts +0 -7
- package/dist/adapters/transport.d.ts.map +0 -1
- package/dist/adapters/transport.js +0 -36
- package/dist/adapters/transport.js.map +0 -1
- package/dist/ai/chunk.d.ts +0 -2
- package/dist/ai/chunk.d.ts.map +0 -1
- package/dist/ai/chunk.js +0 -36
- package/dist/ai/chunk.js.map +0 -1
- package/dist/ai/llmstxt.d.ts +0 -6
- package/dist/ai/llmstxt.d.ts.map +0 -1
- package/dist/ai/llmstxt.js +0 -12
- package/dist/ai/llmstxt.js.map +0 -1
- package/dist/ai/provenance.d.ts +0 -22
- package/dist/ai/provenance.d.ts.map +0 -1
- package/dist/ai/provenance.js +0 -25
- package/dist/ai/provenance.js.map +0 -1
- package/dist/ai/rag.d.ts +0 -26
- package/dist/ai/rag.d.ts.map +0 -1
- package/dist/ai/rag.js +0 -22
- package/dist/ai/rag.js.map +0 -1
- package/dist/ai/tokens.d.ts +0 -15
- package/dist/ai/tokens.d.ts.map +0 -1
- package/dist/ai/tokens.js +0 -9
- package/dist/ai/tokens.js.map +0 -1
- package/dist/api/auth.d.ts +0 -16
- package/dist/api/auth.d.ts.map +0 -1
- package/dist/api/auth.js +0 -19
- package/dist/api/auth.js.map +0 -1
- package/dist/api/contracts.d.ts +0 -28
- package/dist/api/contracts.d.ts.map +0 -1
- package/dist/api/contracts.js +0 -14
- package/dist/api/contracts.js.map +0 -1
- package/dist/api/control.d.ts +0 -10
- package/dist/api/control.d.ts.map +0 -1
- package/dist/api/control.js +0 -35
- package/dist/api/control.js.map +0 -1
- package/dist/api/http.d.ts +0 -7
- package/dist/api/http.d.ts.map +0 -1
- package/dist/api/http.js +0 -12
- package/dist/api/http.js.map +0 -1
- package/dist/api/rate.d.ts +0 -28
- package/dist/api/rate.d.ts.map +0 -1
- package/dist/api/rate.js +0 -36
- package/dist/api/rate.js.map +0 -1
- package/dist/api/security.d.ts +0 -12
- package/dist/api/security.d.ts.map +0 -1
- package/dist/api/security.js +0 -56
- package/dist/api/security.js.map +0 -1
- package/dist/api/service.d.ts +0 -5
- package/dist/api/service.d.ts.map +0 -1
- package/dist/api/service.js +0 -70
- package/dist/api/service.js.map +0 -1
- package/dist/apps/registry.d.ts +0 -33
- package/dist/apps/registry.d.ts.map +0 -1
- package/dist/apps/registry.js +0 -24
- package/dist/apps/registry.js.map +0 -1
- package/dist/binary/archive.d.ts +0 -27
- package/dist/binary/archive.d.ts.map +0 -1
- package/dist/binary/archive.js +0 -46
- package/dist/binary/archive.js.map +0 -1
- package/dist/binary/build.d.ts +0 -29
- package/dist/binary/build.d.ts.map +0 -1
- package/dist/binary/build.js +0 -17
- package/dist/binary/build.js.map +0 -1
- package/dist/binary/transform.d.ts +0 -70
- package/dist/binary/transform.d.ts.map +0 -1
- package/dist/binary/transform.js +0 -105
- package/dist/binary/transform.js.map +0 -1
- package/dist/bot/adapter.d.ts +0 -5
- package/dist/bot/adapter.d.ts.map +0 -1
- package/dist/bot/adapter.js +0 -11
- package/dist/bot/adapter.js.map +0 -1
- package/dist/bot/bot.d.ts +0 -28
- package/dist/bot/bot.d.ts.map +0 -1
- package/dist/bot/bot.js +0 -52
- package/dist/bot/bot.js.map +0 -1
- package/dist/bot/commands.d.ts +0 -8
- package/dist/bot/commands.d.ts.map +0 -1
- package/dist/bot/commands.js +0 -19
- package/dist/bot/commands.js.map +0 -1
- package/dist/bot/permissions.d.ts +0 -13
- package/dist/bot/permissions.d.ts.map +0 -1
- package/dist/bot/permissions.js +0 -16
- package/dist/bot/permissions.js.map +0 -1
- package/dist/browser/actions.d.ts +0 -33
- package/dist/browser/actions.d.ts.map +0 -1
- package/dist/browser/actions.js +0 -44
- package/dist/browser/actions.js.map +0 -1
- package/dist/browser/agent.d.ts +0 -21
- package/dist/browser/agent.d.ts.map +0 -1
- package/dist/browser/agent.js +0 -13
- package/dist/browser/agent.js.map +0 -1
- package/dist/browser/context.d.ts +0 -43
- package/dist/browser/context.d.ts.map +0 -1
- package/dist/browser/context.js +0 -54
- package/dist/browser/context.js.map +0 -1
- package/dist/browser/fingerprint.d.ts +0 -4
- package/dist/browser/fingerprint.d.ts.map +0 -1
- package/dist/browser/fingerprint.js +0 -12
- package/dist/browser/fingerprint.js.map +0 -1
- package/dist/browser/index.d.ts +0 -11
- package/dist/browser/index.d.ts.map +0 -1
- package/dist/browser/index.js +0 -11
- package/dist/browser/index.js.map +0 -1
- package/dist/browser/playwright.d.ts +0 -11
- package/dist/browser/playwright.d.ts.map +0 -1
- package/dist/browser/playwright.js +0 -24
- package/dist/browser/playwright.js.map +0 -1
- package/dist/browser/recorder.d.ts +0 -21
- package/dist/browser/recorder.d.ts.map +0 -1
- package/dist/browser/recorder.js +0 -58
- package/dist/browser/recorder.js.map +0 -1
- package/dist/browser/session.d.ts +0 -14
- package/dist/browser/session.d.ts.map +0 -1
- package/dist/browser/session.js +0 -21
- package/dist/browser/session.js.map +0 -1
- package/dist/browser/snapshot.d.ts +0 -69
- package/dist/browser/snapshot.d.ts.map +0 -1
- package/dist/browser/snapshot.js +0 -133
- package/dist/browser/snapshot.js.map +0 -1
- package/dist/captcha/contract.d.ts +0 -35
- package/dist/captcha/contract.d.ts.map +0 -1
- package/dist/captcha/contract.js +0 -17
- package/dist/captcha/contract.js.map +0 -1
- package/dist/captcha/evidence.d.ts +0 -10
- package/dist/captcha/evidence.d.ts.map +0 -1
- package/dist/captcha/evidence.js +0 -9
- package/dist/captcha/evidence.js.map +0 -1
- package/dist/captcha/guard.d.ts +0 -23
- package/dist/captcha/guard.d.ts.map +0 -1
- package/dist/captcha/guard.js +0 -12
- package/dist/captcha/guard.js.map +0 -1
- package/dist/cli/main.d.ts +0 -4
- package/dist/cli/main.d.ts.map +0 -1
- package/dist/cli/main.js +0 -46
- package/dist/cli/main.js.map +0 -1
- package/dist/core/errors.d.ts +0 -84
- package/dist/core/errors.d.ts.map +0 -1
- package/dist/core/errors.js +0 -54
- package/dist/core/errors.js.map +0 -1
- package/dist/core/events.d.ts +0 -9
- package/dist/core/events.d.ts.map +0 -1
- package/dist/core/events.js +0 -21
- package/dist/core/events.js.map +0 -1
- package/dist/core/hash.d.ts +0 -10
- package/dist/core/hash.d.ts.map +0 -1
- package/dist/core/hash.js +0 -82
- package/dist/core/hash.js.map +0 -1
- package/dist/core/ids.d.ts +0 -10
- package/dist/core/ids.d.ts.map +0 -1
- package/dist/core/ids.js +0 -15
- package/dist/core/ids.js.map +0 -1
- package/dist/delivery/manifest.d.ts +0 -35
- package/dist/delivery/manifest.d.ts.map +0 -1
- package/dist/delivery/manifest.js +0 -68
- package/dist/delivery/manifest.js.map +0 -1
- package/dist/deploy/index.d.ts +0 -6
- package/dist/deploy/index.d.ts.map +0 -1
- package/dist/deploy/index.js +0 -6
- package/dist/deploy/index.js.map +0 -1
- package/dist/dispatch/resumable.d.ts +0 -102
- package/dist/dispatch/resumable.d.ts.map +0 -1
- package/dist/dispatch/resumable.js +0 -64
- package/dist/dispatch/resumable.js.map +0 -1
- package/dist/dispatch/workflow.d.ts +0 -6
- package/dist/dispatch/workflow.d.ts.map +0 -1
- package/dist/dispatch/workflow.js +0 -37
- package/dist/dispatch/workflow.js.map +0 -1
- package/dist/domain/artifacts.d.ts +0 -12
- package/dist/domain/artifacts.d.ts.map +0 -1
- package/dist/domain/artifacts.js +0 -14
- package/dist/domain/artifacts.js.map +0 -1
- package/dist/domain/jobs.d.ts +0 -12
- package/dist/domain/jobs.d.ts.map +0 -1
- package/dist/domain/jobs.js +0 -20
- package/dist/domain/jobs.js.map +0 -1
- package/dist/domain/providers.d.ts +0 -10
- package/dist/domain/providers.d.ts.map +0 -1
- package/dist/domain/providers.js +0 -8
- package/dist/domain/providers.js.map +0 -1
- package/dist/domain/runtime.d.ts +0 -14
- package/dist/domain/runtime.d.ts.map +0 -1
- package/dist/domain/runtime.js +0 -10
- package/dist/domain/runtime.js.map +0 -1
- package/dist/domain/sessions.d.ts +0 -12
- package/dist/domain/sessions.d.ts.map +0 -1
- package/dist/domain/sessions.js +0 -57
- package/dist/domain/sessions.js.map +0 -1
- package/dist/examples/localjob.d.ts +0 -2
- package/dist/examples/localjob.d.ts.map +0 -1
- package/dist/examples/localjob.js.map +0 -1
- package/dist/examples/publicapi.d.ts +0 -2
- package/dist/examples/publicapi.d.ts.map +0 -1
- package/dist/examples/publicapi.js.map +0 -1
- package/dist/extension/build.d.ts +0 -9
- package/dist/extension/build.d.ts.map +0 -1
- package/dist/extension/build.js +0 -85
- package/dist/extension/build.js.map +0 -1
- package/dist/extension/content.d.ts +0 -6
- package/dist/extension/content.d.ts.map +0 -1
- package/dist/extension/content.js +0 -135
- package/dist/extension/content.js.map +0 -1
- package/dist/extension/index.d.ts +0 -7
- package/dist/extension/index.d.ts.map +0 -1
- package/dist/extension/index.js +0 -7
- package/dist/extension/index.js.map +0 -1
- package/dist/extension/pagebridge.d.ts +0 -6
- package/dist/extension/pagebridge.d.ts.map +0 -1
- package/dist/extension/pagebridge.js +0 -42
- package/dist/extension/pagebridge.js.map +0 -1
- package/dist/extension/permissions.d.ts +0 -26
- package/dist/extension/permissions.d.ts.map +0 -1
- package/dist/extension/permissions.js +0 -28
- package/dist/extension/permissions.js.map +0 -1
- package/dist/extension/popup.d.ts +0 -5
- package/dist/extension/popup.d.ts.map +0 -1
- package/dist/extension/popup.js +0 -26
- package/dist/extension/popup.js.map +0 -1
- package/dist/extension/protocol.d.ts +0 -58
- package/dist/extension/protocol.d.ts.map +0 -1
- package/dist/extension/protocol.js +0 -101
- package/dist/extension/protocol.js.map +0 -1
- package/dist/extension/serviceworker.d.ts +0 -25
- package/dist/extension/serviceworker.d.ts.map +0 -1
- package/dist/extension/serviceworker.js +0 -102
- package/dist/extension/serviceworker.js.map +0 -1
- package/dist/extension/worker.d.ts +0 -28
- package/dist/extension/worker.d.ts.map +0 -1
- package/dist/extension/worker.js +0 -22
- package/dist/extension/worker.js.map +0 -1
- package/dist/format/check.d.ts +0 -8
- package/dist/format/check.d.ts.map +0 -1
- package/dist/format/check.js +0 -38
- package/dist/format/check.js.map +0 -1
- package/dist/library/public.d.ts.map +0 -1
- package/dist/library/public.js.map +0 -1
- package/dist/mcp/browser.d.ts +0 -9
- package/dist/mcp/browser.d.ts.map +0 -1
- package/dist/mcp/browser.js +0 -13
- package/dist/mcp/browser.js.map +0 -1
- package/dist/mcp/server.d.ts +0 -35
- package/dist/mcp/server.d.ts.map +0 -1
- package/dist/mcp/server.js +0 -40
- package/dist/mcp/server.js.map +0 -1
- package/dist/mcp/transport.d.ts +0 -7
- package/dist/mcp/transport.d.ts.map +0 -1
- package/dist/mcp/transport.js +0 -17
- package/dist/mcp/transport.js.map +0 -1
- package/dist/memory/bridge.d.ts +0 -14
- package/dist/memory/bridge.d.ts.map +0 -1
- package/dist/memory/bridge.js +0 -16
- package/dist/memory/bridge.js.map +0 -1
- package/dist/memory/engine.d.ts +0 -38
- package/dist/memory/engine.d.ts.map +0 -1
- package/dist/memory/engine.js +0 -109
- package/dist/memory/engine.js.map +0 -1
- package/dist/memory/modes.d.ts +0 -43
- package/dist/memory/modes.d.ts.map +0 -1
- package/dist/memory/modes.js +0 -66
- package/dist/memory/modes.js.map +0 -1
- package/dist/memory/objects.d.ts +0 -21
- package/dist/memory/objects.d.ts.map +0 -1
- package/dist/memory/objects.js +0 -20
- package/dist/memory/objects.js.map +0 -1
- package/dist/memory/planner.d.ts +0 -66
- package/dist/memory/planner.d.ts.map +0 -1
- package/dist/memory/planner.js +0 -108
- package/dist/memory/planner.js.map +0 -1
- package/dist/memory/targets.d.ts +0 -78
- package/dist/memory/targets.d.ts.map +0 -1
- package/dist/memory/targets.js +0 -29
- package/dist/memory/targets.js.map +0 -1
- package/dist/memory/transforms.d.ts +0 -19
- package/dist/memory/transforms.d.ts.map +0 -1
- package/dist/memory/transforms.js +0 -14
- package/dist/memory/transforms.js.map +0 -1
- package/dist/modes/matrix.d.ts +0 -23
- package/dist/modes/matrix.d.ts.map +0 -1
- package/dist/modes/matrix.js +0 -18
- package/dist/modes/matrix.js.map +0 -1
- package/dist/modes/modes.d.ts +0 -33
- package/dist/modes/modes.d.ts.map +0 -1
- package/dist/modes/modes.js +0 -15
- package/dist/modes/modes.js.map +0 -1
- package/dist/modes/resolve.d.ts +0 -96
- package/dist/modes/resolve.d.ts.map +0 -1
- package/dist/modes/resolve.js +0 -48
- package/dist/modes/resolve.js.map +0 -1
- package/dist/observability/metrics.d.ts +0 -14
- package/dist/observability/metrics.d.ts.map +0 -1
- package/dist/observability/metrics.js +0 -18
- package/dist/observability/metrics.js.map +0 -1
- package/dist/packager/manifest.d.ts +0 -66
- package/dist/packager/manifest.d.ts.map +0 -1
- package/dist/packager/manifest.js +0 -94
- package/dist/packager/manifest.js.map +0 -1
- package/dist/packager/publish.d.ts +0 -51
- package/dist/packager/publish.d.ts.map +0 -1
- package/dist/packager/publish.js +0 -18
- package/dist/packager/publish.js.map +0 -1
- package/dist/packager/targetcli.d.ts +0 -7
- package/dist/packager/targetcli.d.ts.map +0 -1
- package/dist/packager/targetcli.js +0 -22
- package/dist/packager/targetcli.js.map +0 -1
- package/dist/persistence/adapter.d.ts +0 -5
- package/dist/persistence/adapter.d.ts.map +0 -1
- package/dist/persistence/adapter.js +0 -11
- package/dist/persistence/adapter.js.map +0 -1
- package/dist/persistence/drizzle.d.ts +0 -2
- package/dist/persistence/drizzle.d.ts.map +0 -1
- package/dist/persistence/drizzle.js +0 -12
- package/dist/persistence/drizzle.js.map +0 -1
- package/dist/persistence/memory.d.ts +0 -2
- package/dist/persistence/memory.d.ts.map +0 -1
- package/dist/persistence/memory.js +0 -27
- package/dist/persistence/memory.js.map +0 -1
- package/dist/persistence/migrations.d.ts +0 -19
- package/dist/persistence/migrations.d.ts.map +0 -1
- package/dist/persistence/migrations.js +0 -13
- package/dist/persistence/migrations.js.map +0 -1
- package/dist/persistence/prisma.d.ts +0 -2
- package/dist/persistence/prisma.d.ts.map +0 -1
- package/dist/persistence/prisma.js +0 -25
- package/dist/persistence/prisma.js.map +0 -1
- package/dist/persistence/schema.d.ts +0 -63
- package/dist/persistence/schema.d.ts.map +0 -1
- package/dist/persistence/schema.js +0 -28
- package/dist/persistence/schema.js.map +0 -1
- package/dist/persistence/sql.d.ts +0 -3
- package/dist/persistence/sql.d.ts.map +0 -1
- package/dist/persistence/sql.js +0 -37
- package/dist/persistence/sql.js.map +0 -1
- package/dist/protocol/blocks.d.ts +0 -10
- package/dist/protocol/blocks.d.ts.map +0 -1
- package/dist/protocol/blocks.js +0 -26
- package/dist/protocol/blocks.js.map +0 -1
- package/dist/protocol/json.d.ts +0 -6
- package/dist/protocol/json.d.ts.map +0 -1
- package/dist/protocol/json.js +0 -6
- package/dist/protocol/json.js.map +0 -1
- package/dist/protocol/ndjson.d.ts +0 -3
- package/dist/protocol/ndjson.d.ts.map +0 -1
- package/dist/protocol/ndjson.js +0 -20
- package/dist/protocol/ndjson.js.map +0 -1
- package/dist/protocol/sse.d.ts +0 -7
- package/dist/protocol/sse.d.ts.map +0 -1
- package/dist/protocol/sse.js +0 -25
- package/dist/protocol/sse.js.map +0 -1
- package/dist/proxy/pool.d.ts +0 -9
- package/dist/proxy/pool.d.ts.map +0 -1
- package/dist/proxy/pool.js +0 -27
- package/dist/proxy/pool.js.map +0 -1
- package/dist/queue/idempotency.d.ts +0 -10
- package/dist/queue/idempotency.d.ts.map +0 -1
- package/dist/queue/idempotency.js +0 -13
- package/dist/queue/idempotency.js.map +0 -1
- package/dist/queue/persistent.d.ts +0 -14
- package/dist/queue/persistent.d.ts.map +0 -1
- package/dist/queue/persistent.js +0 -66
- package/dist/queue/persistent.js.map +0 -1
- package/dist/queue/queue.d.ts +0 -7
- package/dist/queue/queue.d.ts.map +0 -1
- package/dist/queue/queue.js +0 -55
- package/dist/queue/queue.js.map +0 -1
- package/dist/queue/saga.d.ts +0 -5
- package/dist/queue/saga.d.ts.map +0 -1
- package/dist/queue/saga.js +0 -20
- package/dist/queue/saga.js.map +0 -1
- package/dist/release/assets.d.ts +0 -144
- package/dist/release/assets.d.ts.map +0 -1
- package/dist/release/assets.js +0 -157
- package/dist/release/assets.js.map +0 -1
- package/dist/release/evidence.d.ts +0 -92
- package/dist/release/evidence.d.ts.map +0 -1
- package/dist/release/evidence.js +0 -148
- package/dist/release/evidence.js.map +0 -1
- package/dist/release/verify.d.ts +0 -12
- package/dist/release/verify.d.ts.map +0 -1
- package/dist/release/verify.js +0 -112
- package/dist/release/verify.js.map +0 -1
- package/dist/runners/chain.d.ts +0 -125
- package/dist/runners/chain.d.ts.map +0 -1
- package/dist/runners/chain.js +0 -95
- package/dist/runners/chain.js.map +0 -1
- package/dist/runners/health.d.ts +0 -36
- package/dist/runners/health.d.ts.map +0 -1
- package/dist/runners/health.js +0 -26
- package/dist/runners/health.js.map +0 -1
- package/dist/runners/heartbeat.d.ts +0 -23
- package/dist/runners/heartbeat.d.ts.map +0 -1
- package/dist/runners/heartbeat.js +0 -29
- package/dist/runners/heartbeat.js.map +0 -1
- package/dist/runners/inprocess.d.ts +0 -17
- package/dist/runners/inprocess.d.ts.map +0 -1
- package/dist/runners/inprocess.js +0 -20
- package/dist/runners/inprocess.js.map +0 -1
- package/dist/runners/scheduler.d.ts +0 -5
- package/dist/runners/scheduler.d.ts.map +0 -1
- package/dist/runners/scheduler.js +0 -19
- package/dist/runners/scheduler.js.map +0 -1
- package/dist/runtime/abort.d.ts +0 -8
- package/dist/runtime/abort.d.ts.map +0 -1
- package/dist/runtime/abort.js +0 -13
- package/dist/runtime/abort.js.map +0 -1
- package/dist/runtime/compatibility.d.ts +0 -37
- package/dist/runtime/compatibility.d.ts.map +0 -1
- package/dist/runtime/compatibility.js +0 -10
- package/dist/runtime/compatibility.js.map +0 -1
- package/dist/runtime/detect.d.ts +0 -13
- package/dist/runtime/detect.d.ts.map +0 -1
- package/dist/runtime/detect.js +0 -18
- package/dist/runtime/detect.js.map +0 -1
- package/dist/runtime/engine.d.ts +0 -20
- package/dist/runtime/engine.d.ts.map +0 -1
- package/dist/runtime/engine.js +0 -61
- package/dist/runtime/engine.js.map +0 -1
- package/dist/runtime/retry.d.ts +0 -19
- package/dist/runtime/retry.d.ts.map +0 -1
- package/dist/runtime/retry.js +0 -54
- package/dist/runtime/retry.js.map +0 -1
- package/dist/runtime/worker.d.ts +0 -10
- package/dist/runtime/worker.d.ts.map +0 -1
- package/dist/runtime/worker.js +0 -25
- package/dist/runtime/worker.js.map +0 -1
- package/dist/scrape/cache.d.ts +0 -45
- package/dist/scrape/cache.d.ts.map +0 -1
- package/dist/scrape/cache.js +0 -45
- package/dist/scrape/cache.js.map +0 -1
- package/dist/scrape/crawl.d.ts +0 -47
- package/dist/scrape/crawl.d.ts.map +0 -1
- package/dist/scrape/crawl.js +0 -114
- package/dist/scrape/crawl.js.map +0 -1
- package/dist/scrape/extract.d.ts +0 -63
- package/dist/scrape/extract.d.ts.map +0 -1
- package/dist/scrape/extract.js +0 -47
- package/dist/scrape/extract.js.map +0 -1
- package/dist/scrape/normalize.d.ts +0 -52
- package/dist/scrape/normalize.d.ts.map +0 -1
- package/dist/scrape/normalize.js +0 -102
- package/dist/scrape/normalize.js.map +0 -1
- package/dist/scrape/robots.d.ts +0 -37
- package/dist/scrape/robots.d.ts.map +0 -1
- package/dist/scrape/robots.js +0 -71
- package/dist/scrape/robots.js.map +0 -1
- package/dist/scrape/schema.d.ts +0 -23
- package/dist/scrape/schema.d.ts.map +0 -1
- package/dist/scrape/schema.js +0 -92
- package/dist/scrape/schema.js.map +0 -1
- package/dist/scrape/scraper.d.ts +0 -6
- package/dist/scrape/scraper.d.ts.map +0 -1
- package/dist/scrape/scraper.js +0 -46
- package/dist/scrape/scraper.js.map +0 -1
- package/dist/scrape/semantic.d.ts +0 -25
- package/dist/scrape/semantic.d.ts.map +0 -1
- package/dist/scrape/semantic.js +0 -27
- package/dist/scrape/semantic.js.map +0 -1
- package/dist/server/node.d.ts +0 -7
- package/dist/server/node.d.ts.map +0 -1
- package/dist/server/node.js.map +0 -1
- package/dist/sessions/file.d.ts +0 -25
- package/dist/sessions/file.d.ts.map +0 -1
- package/dist/sessions/file.js +0 -13
- package/dist/sessions/file.js.map +0 -1
- package/dist/sessions/replay.d.ts +0 -9
- package/dist/sessions/replay.d.ts.map +0 -1
- package/dist/sessions/replay.js +0 -70
- package/dist/sessions/replay.js.map +0 -1
- package/dist/sessions/store.d.ts +0 -25
- package/dist/sessions/store.d.ts.map +0 -1
- package/dist/sessions/store.js +0 -13
- package/dist/sessions/store.js.map +0 -1
- package/dist/storage/adapter.d.ts +0 -5
- package/dist/storage/adapter.d.ts.map +0 -1
- package/dist/storage/adapter.js +0 -11
- package/dist/storage/adapter.js.map +0 -1
- package/dist/storage/cache.d.ts +0 -23
- package/dist/storage/cache.d.ts.map +0 -1
- package/dist/storage/cache.js +0 -79
- package/dist/storage/cache.js.map +0 -1
- package/dist/storage/checksum.d.ts +0 -3
- package/dist/storage/checksum.d.ts.map +0 -1
- package/dist/storage/checksum.js +0 -19
- package/dist/storage/checksum.js.map +0 -1
- package/dist/storage/chunked.d.ts +0 -21
- package/dist/storage/chunked.d.ts.map +0 -1
- package/dist/storage/chunked.js +0 -67
- package/dist/storage/chunked.js.map +0 -1
- package/dist/storage/content.d.ts +0 -24
- package/dist/storage/content.d.ts.map +0 -1
- package/dist/storage/content.js +0 -44
- package/dist/storage/content.js.map +0 -1
- package/dist/storage/filehosting.d.ts +0 -2
- package/dist/storage/filehosting.d.ts.map +0 -1
- package/dist/storage/filehosting.js +0 -24
- package/dist/storage/filehosting.js.map +0 -1
- package/dist/storage/githubcontents.d.ts +0 -2
- package/dist/storage/githubcontents.d.ts.map +0 -1
- package/dist/storage/githubcontents.js +0 -29
- package/dist/storage/githubcontents.js.map +0 -1
- package/dist/storage/index.d.ts +0 -12
- package/dist/storage/index.d.ts.map +0 -1
- package/dist/storage/index.js +0 -12
- package/dist/storage/index.js.map +0 -1
- package/dist/storage/local.d.ts +0 -2
- package/dist/storage/local.d.ts.map +0 -1
- package/dist/storage/local.js +0 -72
- package/dist/storage/local.js.map +0 -1
- package/dist/storage/memory.d.ts +0 -6
- package/dist/storage/memory.d.ts.map +0 -1
- package/dist/storage/memory.js +0 -36
- package/dist/storage/memory.js.map +0 -1
- package/dist/storage/pool.d.ts +0 -95
- package/dist/storage/pool.d.ts.map +0 -1
- package/dist/storage/pool.js +0 -202
- package/dist/storage/pool.js.map +0 -1
- package/dist/storage/s3compatible.d.ts +0 -3
- package/dist/storage/s3compatible.d.ts.map +0 -1
- package/dist/storage/s3compatible.js +0 -70
- package/dist/storage/s3compatible.js.map +0 -1
- package/dist/storage/sync.d.ts +0 -47
- package/dist/storage/sync.d.ts.map +0 -1
- package/dist/storage/sync.js +0 -75
- package/dist/storage/sync.js.map +0 -1
- package/dist/surfaces/adapters.d.ts +0 -97
- package/dist/surfaces/adapters.d.ts.map +0 -1
- package/dist/surfaces/adapters.js +0 -50
- package/dist/surfaces/adapters.js.map +0 -1
- package/dist/surfaces/controls.d.ts +0 -18
- package/dist/surfaces/controls.d.ts.map +0 -1
- package/dist/surfaces/controls.js +0 -45
- package/dist/surfaces/controls.js.map +0 -1
- package/dist/surfaces/manifest.d.ts +0 -68
- package/dist/surfaces/manifest.d.ts.map +0 -1
- package/dist/surfaces/manifest.js +0 -42
- package/dist/surfaces/manifest.js.map +0 -1
- package/dist/surfaces/n8n.d.ts +0 -25
- package/dist/surfaces/n8n.d.ts.map +0 -1
- package/dist/surfaces/n8n.js +0 -31
- package/dist/surfaces/n8n.js.map +0 -1
- package/dist/surfaces/operations.d.ts +0 -32
- package/dist/surfaces/operations.d.ts.map +0 -1
- package/dist/surfaces/operations.js +0 -53
- package/dist/surfaces/operations.js.map +0 -1
- package/dist/surfaces/requirements.d.ts +0 -29
- package/dist/surfaces/requirements.d.ts.map +0 -1
- package/dist/surfaces/requirements.js +0 -50
- package/dist/surfaces/requirements.js.map +0 -1
- package/dist/surfaces/targets.d.ts +0 -196
- package/dist/surfaces/targets.d.ts.map +0 -1
- package/dist/surfaces/targets.js +0 -27
- package/dist/surfaces/targets.js.map +0 -1
- package/dist/webhook/delivery.d.ts +0 -18
- package/dist/webhook/delivery.d.ts.map +0 -1
- package/dist/webhook/delivery.js +0 -36
- package/dist/webhook/delivery.js.map +0 -1
- package/dist/webhook/receiver.d.ts +0 -26
- package/dist/webhook/receiver.d.ts.map +0 -1
- package/dist/webhook/receiver.js +0 -24
- package/dist/webhook/receiver.js.map +0 -1
- package/dist/webhook/signature.d.ts +0 -3
- package/dist/webhook/signature.d.ts.map +0 -1
- package/dist/webhook/signature.js +0 -7
- package/dist/webhook/signature.js.map +0 -1
- package/dist/workflow/manifest.d.ts +0 -22
- package/dist/workflow/manifest.d.ts.map +0 -1
- package/dist/workflow/manifest.js +0 -21
- package/dist/workflow/manifest.js.map +0 -1
- package/dist/workflow/registry.d.ts +0 -7
- package/dist/workflow/registry.d.ts.map +0 -1
- package/dist/workflow/registry.js +0 -18
- package/dist/workflow/registry.js.map +0 -1
- package/dist/workflow/templates.d.ts +0 -6
- package/dist/workflow/templates.d.ts.map +0 -1
- package/dist/workflow/templates.js +0 -18
- package/dist/workflow/templates.js.map +0 -1
- package/dist/workflow/triggers.d.ts +0 -64
- package/dist/workflow/triggers.d.ts.map +0 -1
- package/dist/workflow/triggers.js +0 -99
- package/dist/workflow/triggers.js.map +0 -1
- package/docs/logs/.gitkeep +0 -0
- package/docs/plans/00.index.md +0 -50
- package/docs/plans/01.architecture.md +0 -86
- package/docs/plans/02.research.computer.use.md +0 -58
- package/docs/plans/03.research.captcha.bypass.md +0 -68
- package/docs/plans/04.research.sandbox.ai.md +0 -52
- package/docs/plans/05.capture.platform.md +0 -57
- package/docs/plans/06.dependencies.md +0 -97
- package/docs/plans/07.captcha.test.page.md +0 -41
- package/docs/plans/08.production.infra.md +0 -70
- package/docs/plans/09.database.schema.md +0 -121
- package/docs/plans/10.cloudinary.storage.md +0 -57
- package/docs/plans/11.movement.logs.json.md +0 -72
- package/docs/plans/12.research.atlas.agent.browser.md +0 -79
- package/docs/plans/13.research.anti.detection.md +0 -898
- package/docs/plans/14.research.proxy.md +0 -1495
- package/docs/plans/15.research.retry.rate.limit.md +0 -1958
- package/docs/plans/16.research.crawling.md +0 -1417
- package/docs/plans/17.research.caching.md +0 -1610
- package/docs/plans/18.research.content.extraction.md +0 -1952
- package/docs/plans/19.research.errors.events.md +0 -1523
- package/docs/plans/20.research.zod.validation.md +0 -1350
- package/docs/plans/21.research.batch.concurrency.md +0 -1888
- package/docs/plans/22.research.universal.runtime.md +0 -944
- package/docs/plans/23.research.ai.integration.md +0 -1465
- package/docs/plans/24.research.memory.persistence.md +0 -1979
- package/docs/plans/25.research.server.api.md +0 -342
- package/docs/plans/26.research.compilation.md +0 -249
- package/docs/plans/27.research.html.parsing.md +0 -251
- package/docs/plans/28.action.plan.md +0 -50
- package/docs/plans/29.api.reference.md +0 -174
- package/docs/plans/30.architecture.plan.md +0 -94
- package/docs/plans/31.auditoria.dados.md +0 -163
- package/docs/plans/32.bots.automacao.computacional.md +0 -214
- package/docs/plans/33.bots.codigo.revisao.md +0 -220
- package/docs/plans/34.bots.seguranca.cicd.md +0 -366
- package/docs/plans/35.comparativo.concorrencia.md +0 -464
- package/docs/plans/36.computational.memory.md +0 -340
- package/docs/plans/37.deploystrategy.md +0 -394
- package/docs/plans/38.flow.md +0 -155
- package/docs/plans/39.multi.platform.bot.md +0 -252
- package/docs/plans/40.npm.publish.md +0 -250
- package/docs/plans/41.o.que.falta.md +0 -407
- package/docs/plans/42.pesquisa.concorrencia.md +0 -721
- package/docs/plans/43.plan.universal.architecture.md +0 -496
- package/docs/plans/44.reference.md +0 -100
- package/docs/plans/45.robotarchitecture.md +0 -237
- package/docs/plans/46.scdnintegration.md +0 -284
- package/docs/plans/47.multiforge.readme.md +0 -129
- package/docs/plans/48.theory.v4.repo.os.md +0 -152
- package/docs/plans/49.third.party.infra.md +0 -12
- package/docs/plans/50.file.as.compute.md +0 -39
- package/docs/plans/51.architecture.virtual.processor.md +0 -80
- package/docs/plans/52.manifesto.v8.md +0 -11
- package/docs/plans/58.cdn.list.md +0 -23
- package/docs/plans/59.sql.frameworks.md +0 -33
- package/docs/plans/60.sql.thirdparty.md +0 -26
- package/docs/plans/61.objective.multiforge.md +0 -63
- package/docs/plans/62.huggingface.upload.md +0 -26
- package/docs/plans/63.kaggle.upload.md +0 -24
- package/docs/plans/64.npm.storage.md +0 -30
- package/docs/plans/65.rclone.terabox.md +0 -32
- package/docs/plans/66.buckets.and.models.todo.md +0 -14
- package/docs/plans/67.database.todo.md +0 -13
- package/docs/plans/68.deploy.packages.todo.md +0 -12
- package/docs/plans/69.report.human.operator.md +0 -133
- package/docs/plans/70.report.brain2qwerty.ems.md +0 -135
- package/docs/plans/71.report.hd.infinito.vram.md +0 -155
- package/docs/plans/72.plan.hd.infinito.node.md +0 -146
- package/docs/plans/73.plan.scifi.repos.md +0 -125
- package/docs/plans/74.000.manifesto.v8.flat.2..md +0 -11
- package/docs/plans/README.md +0 -489
- package/docs/plans/aggregate_platforms.mjs +0 -146
- package/docs/plans/examplesession.json +0 -36
- package/docs/plans/missing-facts.md +0 -192
- package/docs/plans/models.md +0 -64
- package/docs/plans/organize.cjs +0 -270
- package/docs/plans/platforms.md +0 -2887
- package/docs/plans/sites.md +0 -31322
- package/docs/sources/farm.py +0 -117
- package/docs/sources/html/saddle1.html +0 -132
- package/docs/sources/html/saddle2.html +0 -157
- package/docs/sources/html/saddle3.html +0 -119
- package/docs/sources/html/saddle4.html +0 -144
- package/docs/sources/html/saddle5.html +0 -72
- package/docs/sources/html/saddle6.html +0 -171
- package/docs/sources/html/saddle7.html +0 -236
- package/docs/sources/saddle.ts +0 -74
- package/docs/sources/schema.prisma +0 -88
- package/docs/sources/script.sh +0 -64
- package/docs/sources/workflows.yml +0 -458
- package/docs/talks1/_body.txt +0 -14
- package/docs/talks1/_index.md +0 -15
- package/docs/talks1/_screenshot.png +0 -0
- package/docs/talks1/assistant-01.md +0 -5
- package/docs/talks1/assistant-02.md +0 -5
- package/docs/talks1/assistant-03.md +0 -531
- package/docs/talks1/assistant-04.md +0 -26
- package/docs/talks1/assistant-05.md +0 -774
- package/docs/talks1/assistant-06.md +0 -1718
- package/docs/talks1/scrape-share.cjs +0 -185
- package/docs/talks1/scrape-share.ts +0 -183
- package/docs/talks1/user-01.md +0 -3
- package/docs/talks1/user-02.md +0 -3
- package/docs/talks1/user-03.md +0 -88
- package/docs/talks1/user-04.md +0 -3
- package/docs/talks1/user-05.md +0 -3
- package/docs/talks1/user-06.md +0 -88
- package/docs/talks1/user-07.md +0 -88
- package/docs/talks2/_body.txt +0 -14
- package/docs/talks2/_index.md +0 -16
- package/docs/talks2/_screenshot.png +0 -0
- package/docs/talks2/assistant-01.md +0 -5
- package/docs/talks2/assistant-02.md +0 -5
- package/docs/talks2/assistant-03.md +0 -424
- package/docs/talks2/assistant-04.md +0 -598
- package/docs/talks2/assistant-05.md +0 -1280
- package/docs/talks2/assistant-06.md +0 -1227
- package/docs/talks2/assistant-07.md +0 -1252
- package/docs/talks2/user-01.md +0 -3
- package/docs/talks2/user-02.md +0 -3
- package/docs/talks2/user-03.md +0 -88
- package/docs/talks2/user-04.md +0 -88
- package/docs/talks2/user-05.md +0 -88
- package/docs/talks2/user-06.md +0 -88
- package/docs/talks2/user-07.md +0 -3
- package/docs/talks3/_body.txt +0 -467
- package/docs/talks3/_index.md +0 -10
- package/docs/talks3/_screenshot.png +0 -0
- package/docs/talks3/assistant-01.md +0 -417
- package/docs/talks3/assistant-02.md +0 -417
- package/docs/talks3/assistant-03.md +0 -29
- package/docs/talks3/assistant-04.md +0 -727
- package/docs/talks3/user-01.md +0 -88
- package/docs/talks3/user-02.md +0 -88
- package/docs/talks3/user-03.md +0 -3
- package/docs/talks3/user-04.md +0 -3
- package/docs/talks4/_body.txt +0 -14
- package/docs/talks4/_index.md +0 -12
- package/docs/talks4/_screenshot.png +0 -0
- package/docs/talks4/assistant-01.md +0 -5
- package/docs/talks4/assistant-02.md +0 -5
- package/docs/talks4/assistant-03.md +0 -35
- package/docs/talks4/assistant-04.md +0 -512
- package/docs/talks4/assistant-05.md +0 -599
- package/docs/talks4/user-01.md +0 -3
- package/docs/talks4/user-02.md +0 -3
- package/docs/talks4/user-03.md +0 -88
- package/docs/talks4/user-04.md +0 -88
- package/docs/talks4/user-05.md +0 -7
- package/docs/talks5/_body.txt +0 -14
- package/docs/talks5/_index.md +0 -13
- package/docs/talks5/_screenshot.png +0 -0
- package/docs/talks5/assistant-01.md +0 -5
- package/docs/talks5/assistant-02.md +0 -5
- package/docs/talks5/assistant-03.md +0 -690
- package/docs/talks5/assistant-04.md +0 -758
- package/docs/talks5/assistant-05.md +0 -974
- package/docs/talks5/user-01.md +0 -3
- package/docs/talks5/user-02.md +0 -3
- package/docs/talks5/user-03.md +0 -105
- package/docs/talks5/user-04.md +0 -105
- package/docs/talks5/user-05.md +0 -63
- package/docs/talks5/user-06.md +0 -105
- package/docs/talks6/_body.txt +0 -14
- package/docs/talks6/_index.md +0 -9
- package/docs/talks6/_screenshot.png +0 -0
- package/docs/talks6/assistant-01.md +0 -5
- package/docs/talks6/assistant-02.md +0 -5
- package/docs/talks6/assistant-03.md +0 -1499
- package/docs/talks6/user-01.md +0 -3
- package/docs/talks6/user-02.md +0 -3
- package/docs/talks6/user-03.md +0 -88
- package/docs/talks6/user-04.md +0 -88
- package/docs/talks7/_body.txt +0 -14
- package/docs/talks7/_index.md +0 -10
- package/docs/talks7/_screenshot.png +0 -0
- package/docs/talks7/assistant-01.md +0 -5
- package/docs/talks7/assistant-02.md +0 -5
- package/docs/talks7/assistant-03.md +0 -523
- package/docs/talks7/assistant-04.md +0 -617
- package/docs/talks7/user-01.md +0 -3
- package/docs/talks7/user-02.md +0 -3
- package/docs/talks7/user-03.md +0 -105
- package/docs/talks7/user-04.md +0 -67
- package/docs/talks8/conversa1.txt +0 -1322
- package/docs/talks8/conversa2.txt +0 -237
- package/docs/talks9/Beyond the Obvious_ 50 Plataformas Auto-Hospedadas de Forja de C/303/263digo para Al/303/251m de Gitea e GitLab.md" +0 -174
- package/docs/talks9/De NPM a Multi-Linguagem_ Uma Arquitetura T/303/251cnica para a Execu/303/247/303/243o Integrada de C/303/263digo no Ecossistema Node.js.md" +0 -59
- package/docs/talks9/De NPM a VMs Virtuais_ Uma An/303/241lise Arquitet/303/264nica para a Realiza/303/247/303/243o do Ciclo de Vida do Projeto SADDLE.md" +0 -91
- package/docs/talks9/Mapeamento da Engrenagem Computacional_ Uma Arquitetura para Execu/303/247/303/243o Isolada e Persist/303/252ncia em Ambientes Distribu/303/255dos.md" +0 -116
- package/docs/talks9/O Cen/303/241rio Pr/303/241tico do SADDLE_ Uma An/303/241lise de Viabilidade e Modelo de Ciclo de Vida Integrado.md" +0 -128
- package/docs/talks9/README (2).md +0 -489
- package/docs/talks9/README.md +0 -198
- package/docs/talks9/Viabilidade do Saddle_ Uma An/303/241lise T/303/251cnica da Transforma/303/247/303/243o de Armazenamento Remoto em Mem/303/263ria Computacional.md" +0 -80
- package/docs/talks9/conversa.txt +0 -544
- package/docs/talks9/other (2).md +0 -39
- package/docs/talks9/other.md +0 -57
- package/docs/talks9/outro.txt +0 -24
- /package/{extension/README.md → docs/extension.md} +0 -0
|
@@ -1,1523 +0,0 @@
|
|
|
1
|
-
# Pesquisa: Error Handling e Event Emitter Patterns (2026)
|
|
2
|
-
|
|
3
|
-
## Sumário
|
|
4
|
-
|
|
5
|
-
1. [Custom Error Classes](#1-custom-error-classes)
|
|
6
|
-
2. [Error Hierarchies with Error Codes](#2-error-hierarchies-with-error-codes)
|
|
7
|
-
3. [Crawlee Error Pattern](#3-crawlee-error-pattern)
|
|
8
|
-
4. [Error Classification](#4-error-classification)
|
|
9
|
-
5. [Recovery Suggestions in Error Objects](#5-recovery-suggestions-in-error-objects)
|
|
10
|
-
6. [Typed EventEmitter in TypeScript](#6-typed-eventemitter-in-typescript)
|
|
11
|
-
7. [Best Packages](#7-best-packages)
|
|
12
|
-
8. [Lifecycle Hooks](#8-lifecycle-hooks)
|
|
13
|
-
9. [Middleware Pipelines](#9-middleware-pipelines)
|
|
14
|
-
10. [Plugin/Hook Systems](#10-pluginhook-systems)
|
|
15
|
-
11. [Tree-shaking](#11-tree-shaking)
|
|
16
|
-
|
|
17
|
-
---
|
|
18
|
-
|
|
19
|
-
## 1. Custom Error Classes
|
|
20
|
-
|
|
21
|
-
### The Problem with TypeScript Errors
|
|
22
|
-
|
|
23
|
-
In TypeScript, when you extend `Error`, the prototype chain breaks. `instanceof` checks fail because TypeScript's transpilation of `extends Error` doesn't correctly set up the prototype. The fix is `Object.setPrototypeOf(this, ClassName.prototype)` in the constructor.
|
|
24
|
-
|
|
25
|
-
### Basic Pattern
|
|
26
|
-
|
|
27
|
-
```typescript
|
|
28
|
-
class ScraperError extends Error {
|
|
29
|
-
public readonly code: string;
|
|
30
|
-
public readonly timestamp: Date;
|
|
31
|
-
|
|
32
|
-
constructor(message: string, code: string = 'SCRAPER_ERROR') {
|
|
33
|
-
super(message);
|
|
34
|
-
this.name = this.constructor.name;
|
|
35
|
-
this.code = code;
|
|
36
|
-
this.timestamp = new Date();
|
|
37
|
-
|
|
38
|
-
// Fix prototype chain for instanceof checks
|
|
39
|
-
Object.setPrototypeOf(this, new.target.prototype);
|
|
40
|
-
|
|
41
|
-
// Capture stack trace (V8 only, not available in all environments)
|
|
42
|
-
if (Error.captureStackTrace) {
|
|
43
|
-
Error.captureStackTrace(this, this.constructor);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// Now instanceof works correctly
|
|
49
|
-
const err = new ScraperError('Connection failed');
|
|
50
|
-
console.log(err instanceof ScraperError); // true
|
|
51
|
-
console.log(err instanceof Error); // true
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
### With `new.target` vs Explicit Class
|
|
55
|
-
|
|
56
|
-
`new.target` is dynamic and works with subclasses. If you need broader compatibility:
|
|
57
|
-
|
|
58
|
-
```typescript
|
|
59
|
-
class ScraperError extends Error {
|
|
60
|
-
constructor(message: string) {
|
|
61
|
-
super(message);
|
|
62
|
-
this.name = this.constructor.name;
|
|
63
|
-
// Explicit class version (safer for older transpilers)
|
|
64
|
-
Object.setPrototypeOf(this, ScraperError.prototype);
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
class NetworkError extends ScraperError {
|
|
69
|
-
constructor(message: string, public readonly url: string) {
|
|
70
|
-
super(message);
|
|
71
|
-
// Must re-set for each subclass in the chain
|
|
72
|
-
Object.setPrototypeOf(this, NetworkError.prototype);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
### Error Options Pattern
|
|
78
|
-
|
|
79
|
-
```typescript
|
|
80
|
-
interface ScraperErrorOptions {
|
|
81
|
-
code: string;
|
|
82
|
-
cause?: unknown;
|
|
83
|
-
retryable?: boolean;
|
|
84
|
-
metadata?: Record<string, unknown>;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
class ScraperError extends Error {
|
|
88
|
-
public readonly code: string;
|
|
89
|
-
public readonly retryable: boolean;
|
|
90
|
-
public readonly metadata: Record<string, unknown>;
|
|
91
|
-
|
|
92
|
-
constructor(
|
|
93
|
-
message: string,
|
|
94
|
-
options: ScraperErrorOptions = { code: 'SCRAPER_ERROR' }
|
|
95
|
-
) {
|
|
96
|
-
super(message, { cause: options.cause });
|
|
97
|
-
this.name = this.constructor.name;
|
|
98
|
-
this.code = options.code;
|
|
99
|
-
this.retryable = options.retryable ?? false;
|
|
100
|
-
this.metadata = options.metadata ?? {};
|
|
101
|
-
Object.setPrototypeOf(this, new.target.prototype);
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
### 2026 Best Practice: Error Cause Chain
|
|
107
|
-
|
|
108
|
-
Use the `cause` property (native since ES2022) to chain errors without losing context:
|
|
109
|
-
|
|
110
|
-
```typescript
|
|
111
|
-
async function fetchPage(url: string): Promise<string> {
|
|
112
|
-
try {
|
|
113
|
-
const res = await fetch(url);
|
|
114
|
-
return await res.text();
|
|
115
|
-
} catch (error) {
|
|
116
|
-
throw new ScraperError(`Failed to fetch ${url}`, {
|
|
117
|
-
code: 'FETCH_FAILED',
|
|
118
|
-
retryable: true,
|
|
119
|
-
cause: error,
|
|
120
|
-
metadata: { url, timestamp: Date.now() }
|
|
121
|
-
});
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
---
|
|
127
|
-
|
|
128
|
-
## 2. Error Hierarchies with Error Codes
|
|
129
|
-
|
|
130
|
-
### Enum-Based Error Codes
|
|
131
|
-
|
|
132
|
-
```typescript
|
|
133
|
-
// Error codes as const enum for tree-shaking
|
|
134
|
-
const ErrorCodes = {
|
|
135
|
-
// Network errors (1xxx)
|
|
136
|
-
NETWORK_TIMEOUT: 'E1001',
|
|
137
|
-
NETWORK_CONNECTION_REFUSED: 'E1002',
|
|
138
|
-
NETWORK_DNS_FAILURE: 'E1003',
|
|
139
|
-
NETWORK_TLS_ERROR: 'E1004',
|
|
140
|
-
|
|
141
|
-
// HTTP errors (2xxx)
|
|
142
|
-
HTTP_RATE_LIMITED: 'E2001',
|
|
143
|
-
HTTP_403: 'E2002',
|
|
144
|
-
HTTP_404: 'E2003',
|
|
145
|
-
HTTP_500: 'E2004',
|
|
146
|
-
HTTP_503: 'E2005',
|
|
147
|
-
|
|
148
|
-
// Browser errors (3xxx)
|
|
149
|
-
BROWSER_LAUNCH_FAILED: 'E3001',
|
|
150
|
-
BROWSER_CRASHED: 'E3002',
|
|
151
|
-
BROWSER_TIMEOUT: 'E3003',
|
|
152
|
-
BROWSER_NAVIGATION: 'E3004',
|
|
153
|
-
|
|
154
|
-
// Scraping errors (4xxx)
|
|
155
|
-
SELECTOR_NOT_FOUND: 'E4001',
|
|
156
|
-
PARSING_FAILED: 'E4002',
|
|
157
|
-
CAPTCHA_DETECTED: 'E4003',
|
|
158
|
-
BOT_DETECTED: 'E4004',
|
|
159
|
-
|
|
160
|
-
// Session errors (5xxx)
|
|
161
|
-
SESSION_EXPIRED: 'E5001',
|
|
162
|
-
SESSION_BLOCKED: 'E5002',
|
|
163
|
-
COOKIES_INVALID: 'E5003',
|
|
164
|
-
|
|
165
|
-
// Configuration errors (6xxx)
|
|
166
|
-
CONFIG_INVALID: 'E6001',
|
|
167
|
-
CONFIG_MISSING: 'E6002',
|
|
168
|
-
} as const;
|
|
169
|
-
|
|
170
|
-
type ErrorCode = typeof ErrorCodes[keyof typeof ErrorCodes];
|
|
171
|
-
|
|
172
|
-
// Metadata about each error code
|
|
173
|
-
const ErrorMetadata: Record<ErrorCode, {
|
|
174
|
-
description: string;
|
|
175
|
-
retryable: boolean;
|
|
176
|
-
severity: 'low' | 'medium' | 'high' | 'critical';
|
|
177
|
-
defaultRetryDelay?: number;
|
|
178
|
-
}> = {
|
|
179
|
-
[ErrorCodes.NETWORK_TIMEOUT]: {
|
|
180
|
-
description: 'Request timed out',
|
|
181
|
-
retryable: true,
|
|
182
|
-
severity: 'medium',
|
|
183
|
-
defaultRetryDelay: 1000,
|
|
184
|
-
},
|
|
185
|
-
[ErrorCodes.HTTP_RATE_LIMITED]: {
|
|
186
|
-
description: 'Server returned 429 Too Many Requests',
|
|
187
|
-
retryable: true,
|
|
188
|
-
severity: 'medium',
|
|
189
|
-
defaultRetryDelay: 5000,
|
|
190
|
-
},
|
|
191
|
-
[ErrorCodes.BOT_DETECTED]: {
|
|
192
|
-
description: 'Anti-bot protection triggered',
|
|
193
|
-
retryable: false,
|
|
194
|
-
severity: 'high',
|
|
195
|
-
},
|
|
196
|
-
[ErrorCodes.SELECTOR_NOT_FOUND]: {
|
|
197
|
-
description: 'CSS selector matched no elements',
|
|
198
|
-
retryable: false,
|
|
199
|
-
severity: 'low',
|
|
200
|
-
},
|
|
201
|
-
} as const;
|
|
202
|
-
```
|
|
203
|
-
|
|
204
|
-
### Full Hierarchy Implementation
|
|
205
|
-
|
|
206
|
-
```typescript
|
|
207
|
-
// Base error
|
|
208
|
-
class ScraperError extends Error {
|
|
209
|
-
public readonly code: ErrorCode;
|
|
210
|
-
public readonly retryable: boolean;
|
|
211
|
-
public readonly severity: 'low' | 'medium' | 'high' | 'critical';
|
|
212
|
-
public readonly timestamp: number;
|
|
213
|
-
public readonly metadata: Record<string, unknown>;
|
|
214
|
-
|
|
215
|
-
constructor(
|
|
216
|
-
message: string,
|
|
217
|
-
options: {
|
|
218
|
-
code: ErrorCode;
|
|
219
|
-
cause?: unknown;
|
|
220
|
-
metadata?: Record<string, unknown>;
|
|
221
|
-
}
|
|
222
|
-
) {
|
|
223
|
-
super(message, { cause: options.cause });
|
|
224
|
-
this.name = this.constructor.name;
|
|
225
|
-
this.code = options.code;
|
|
226
|
-
this.timestamp = Date.now();
|
|
227
|
-
this.metadata = options.metadata ?? {};
|
|
228
|
-
|
|
229
|
-
const info = ErrorMetadata[options.code];
|
|
230
|
-
this.retryable = info?.retryable ?? false;
|
|
231
|
-
this.severity = info?.severity ?? 'medium';
|
|
232
|
-
|
|
233
|
-
Object.setPrototypeOf(this, new.target.prototype);
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
toJSON() {
|
|
237
|
-
return {
|
|
238
|
-
name: this.name,
|
|
239
|
-
message: this.message,
|
|
240
|
-
code: this.code,
|
|
241
|
-
retryable: this.retryable,
|
|
242
|
-
severity: this.severity,
|
|
243
|
-
timestamp: this.timestamp,
|
|
244
|
-
metadata: this.metadata,
|
|
245
|
-
stack: this.stack,
|
|
246
|
-
};
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
// Network errors
|
|
251
|
-
class NetworkError extends ScraperError {
|
|
252
|
-
public readonly url: string;
|
|
253
|
-
|
|
254
|
-
constructor(message: string, url: string, options: { code: ErrorCode; cause?: unknown }) {
|
|
255
|
-
super(message, { ...options, metadata: { ...options.metadata, url } });
|
|
256
|
-
this.name = 'NetworkError';
|
|
257
|
-
this.url = url;
|
|
258
|
-
Object.setPrototypeOf(this, NetworkError.prototype);
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
// HTTP errors
|
|
263
|
-
class HttpError extends ScraperError {
|
|
264
|
-
public readonly statusCode: number;
|
|
265
|
-
public readonly headers: Record<string, string>;
|
|
266
|
-
|
|
267
|
-
constructor(
|
|
268
|
-
message: string,
|
|
269
|
-
statusCode: number,
|
|
270
|
-
headers: Record<string, string> = {},
|
|
271
|
-
cause?: unknown
|
|
272
|
-
) {
|
|
273
|
-
const code = mapHttpStatus(statusCode);
|
|
274
|
-
super(message, { code, cause, metadata: { statusCode, headers } });
|
|
275
|
-
this.name = 'HttpError';
|
|
276
|
-
this.statusCode = statusCode;
|
|
277
|
-
this.headers = headers;
|
|
278
|
-
Object.setPrototypeOf(this, HttpError.prototype);
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
function mapHttpStatus(status: number): ErrorCode {
|
|
283
|
-
if (status === 429) return ErrorCodes.HTTP_RATE_LIMITED;
|
|
284
|
-
if (status === 403) return ErrorCodes.HTTP_403;
|
|
285
|
-
if (status === 404) return ErrorCodes.HTTP_404;
|
|
286
|
-
if (status === 500) return ErrorCodes.HTTP_500;
|
|
287
|
-
if (status === 503) return ErrorCodes.HTTP_503;
|
|
288
|
-
return ErrorCodes.HTTP_500;
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
// Browser errors
|
|
292
|
-
class BrowserError extends ScraperError {
|
|
293
|
-
constructor(message: string, code: ErrorCode, cause?: unknown) {
|
|
294
|
-
super(message, { code, cause });
|
|
295
|
-
this.name = 'BrowserError';
|
|
296
|
-
Object.setPrototypeOf(this, BrowserError.prototype);
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
// Scraping errors
|
|
301
|
-
class ScrapingError extends ScraperError {
|
|
302
|
-
public readonly selector?: string;
|
|
303
|
-
public readonly url: string;
|
|
304
|
-
|
|
305
|
-
constructor(
|
|
306
|
-
message: string,
|
|
307
|
-
url: string,
|
|
308
|
-
options: { code: ErrorCode; selector?: string; cause?: unknown }
|
|
309
|
-
) {
|
|
310
|
-
super(message, {
|
|
311
|
-
...options,
|
|
312
|
-
metadata: { ...options.metadata, url, selector: options.selector },
|
|
313
|
-
});
|
|
314
|
-
this.name = 'ScrapingError';
|
|
315
|
-
this.url = url;
|
|
316
|
-
this.selector = options.selector;
|
|
317
|
-
Object.setPrototypeOf(this, ScrapingError.prototype);
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
```
|
|
321
|
-
|
|
322
|
-
---
|
|
323
|
-
|
|
324
|
-
## 3. Crawlee Error Pattern
|
|
325
|
-
|
|
326
|
-
Crawlee (by Apify) defines a set of specialized error classes that communicate error intent to the framework. This pattern is highly reusable.
|
|
327
|
-
|
|
328
|
-
### Core Crawlee Errors
|
|
329
|
-
|
|
330
|
-
```typescript
|
|
331
|
-
// From Crawlee's design philosophy:
|
|
332
|
-
|
|
333
|
-
/**
|
|
334
|
-
* NonRetryableError - Signals the crawler to stop immediately.
|
|
335
|
-
* Use for: fatal configuration errors, invalid state, permanent failures.
|
|
336
|
-
*/
|
|
337
|
-
class NonRetryableError extends Error {
|
|
338
|
-
constructor(message: string, options?: { cause?: unknown }) {
|
|
339
|
-
super(message, options);
|
|
340
|
-
this.name = 'NonRetryableError';
|
|
341
|
-
Object.setPrototypeOf(this, NonRetryableError.prototype);
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
/**
|
|
346
|
-
* CriticalError - Crash-level error. The crawler cannot recover.
|
|
347
|
-
* Use for: browser crashed, out of memory, system-level failures.
|
|
348
|
-
*/
|
|
349
|
-
class CriticalError extends Error {
|
|
350
|
-
constructor(message: string, options?: { cause?: unknown }) {
|
|
351
|
-
super(message, options);
|
|
352
|
-
this.name = 'CriticalError';
|
|
353
|
-
Object.setPrototypeOf(this, CriticalError.prototype);
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
/**
|
|
358
|
-
* RetryRequestError - Explicitly signals that the request should be retried.
|
|
359
|
-
* Use for: transient failures you want to handle manually.
|
|
360
|
-
*/
|
|
361
|
-
class RetryRequestError extends Error {
|
|
362
|
-
public readonly retryDelay?: number;
|
|
363
|
-
public readonly errorCode?: string;
|
|
364
|
-
|
|
365
|
-
constructor(
|
|
366
|
-
message: string,
|
|
367
|
-
options?: {
|
|
368
|
-
retryDelay?: number;
|
|
369
|
-
errorCode?: string;
|
|
370
|
-
cause?: unknown;
|
|
371
|
-
}
|
|
372
|
-
) {
|
|
373
|
-
super(message, options);
|
|
374
|
-
this.name = 'RetryRequestError';
|
|
375
|
-
this.retryDelay = options?.retryDelay;
|
|
376
|
-
this.errorCode = options?.errorCode;
|
|
377
|
-
Object.setPrototypeOf(this, RetryRequestError.prototype);
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
/**
|
|
382
|
-
* SessionError - Session-specific failure (blocked, expired).
|
|
383
|
-
* Use for: cookie invalidation, IP bans, session rotation needed.
|
|
384
|
-
*/
|
|
385
|
-
class SessionError extends Error {
|
|
386
|
-
public readonly shouldRotateSession: boolean;
|
|
387
|
-
|
|
388
|
-
constructor(
|
|
389
|
-
message: string,
|
|
390
|
-
options?: { shouldRotateSession?: boolean; cause?: unknown }
|
|
391
|
-
) {
|
|
392
|
-
super(message, options);
|
|
393
|
-
this.name = 'SessionError';
|
|
394
|
-
this.shouldRotateSession = options?.shouldRotateSession ?? true;
|
|
395
|
-
Object.setPrototypeOf(this, SessionError.prototype);
|
|
396
|
-
}
|
|
397
|
-
}
|
|
398
|
-
```
|
|
399
|
-
|
|
400
|
-
### Using Crawlee Errors in Practice
|
|
401
|
-
|
|
402
|
-
```typescript
|
|
403
|
-
import { PlaywrightCrawler } from 'crawlee';
|
|
404
|
-
|
|
405
|
-
const crawler = new PlaywrightCrawler({
|
|
406
|
-
async requestHandler({ request, page, session }) {
|
|
407
|
-
// Check for CAPTCHA → SessionError triggers rotation
|
|
408
|
-
if (await page.$('#captcha')) {
|
|
409
|
-
throw new SessionError('CAPTCHA detected', {
|
|
410
|
-
shouldRotateSession: true,
|
|
411
|
-
});
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
// Check for bot detection → NonRetryable stops the crawler
|
|
415
|
-
if (await page.$('.access-denied')) {
|
|
416
|
-
throw new NonRetryableError('Bot detected, IP is banned');
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
// Rate limiting → RetryRequestError with custom delay
|
|
420
|
-
if (await page.$('.rate-limit')) {
|
|
421
|
-
throw new RetryRequestError('Rate limited', {
|
|
422
|
-
retryDelay: 30_000,
|
|
423
|
-
errorCode: 'RATE_LIMITED',
|
|
424
|
-
});
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
// Browser crash → CriticalError stops everything
|
|
428
|
-
if (page.isClosed()) {
|
|
429
|
-
throw new CriticalError('Page was closed unexpectedly');
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
// Normal scraping...
|
|
433
|
-
const data = await page.evaluate(() => {
|
|
434
|
-
return { title: document.title };
|
|
435
|
-
});
|
|
436
|
-
|
|
437
|
-
awaitcrawler.pushData(data);
|
|
438
|
-
},
|
|
439
|
-
|
|
440
|
-
async failedRequestHandler({ request, error }) {
|
|
441
|
-
if (error instanceof NonRetryableError) {
|
|
442
|
-
console.error(`FATAL: ${error.message} — skipping ${request.url}`);
|
|
443
|
-
} else if (error instanceof SessionError) {
|
|
444
|
-
console.warn(`Session issue on ${request.url}, rotating...`);
|
|
445
|
-
} else {
|
|
446
|
-
console.error(`Request failed: ${request.url}`, error);
|
|
447
|
-
}
|
|
448
|
-
},
|
|
449
|
-
});
|
|
450
|
-
```
|
|
451
|
-
|
|
452
|
-
---
|
|
453
|
-
|
|
454
|
-
## 4. Error Classification
|
|
455
|
-
|
|
456
|
-
### Strategy Pattern for Retry Decisions
|
|
457
|
-
|
|
458
|
-
```typescript
|
|
459
|
-
interface RetryPolicy {
|
|
460
|
-
maxRetries: number;
|
|
461
|
-
baseDelay: number;
|
|
462
|
-
maxDelay: number;
|
|
463
|
-
backoffMultiplier: number;
|
|
464
|
-
retryableErrors: Set<ErrorCode>;
|
|
465
|
-
nonRetryableErrors: Set<ErrorCode>;
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
const DEFAULT_RETRY_POLICY: RetryPolicy = {
|
|
469
|
-
maxRetries: 3,
|
|
470
|
-
baseDelay: 1000,
|
|
471
|
-
maxDelay: 30_000,
|
|
472
|
-
backoffMultiplier: 2,
|
|
473
|
-
retryableErrors: new Set([
|
|
474
|
-
ErrorCodes.NETWORK_TIMEOUT,
|
|
475
|
-
ErrorCodes.NETWORK_CONNECTION_REFUSED,
|
|
476
|
-
ErrorCodes.HTTP_RATE_LIMITED,
|
|
477
|
-
ErrorCodes.HTTP_500,
|
|
478
|
-
ErrorCodes.HTTP_503,
|
|
479
|
-
ErrorCodes.BROWSER_CRASHED,
|
|
480
|
-
]),
|
|
481
|
-
nonRetryableErrors: new Set([
|
|
482
|
-
ErrorCodes.HTTP_404,
|
|
483
|
-
ErrorCodes.BOT_DETECTED,
|
|
484
|
-
ErrorCodes.CAPTCHA_DETECTED,
|
|
485
|
-
ErrorCodes.CONFIG_INVALID,
|
|
486
|
-
]),
|
|
487
|
-
};
|
|
488
|
-
|
|
489
|
-
class ErrorClassifier {
|
|
490
|
-
private policy: RetryPolicy;
|
|
491
|
-
|
|
492
|
-
constructor(policy: RetryPolicy = DEFAULT_RETRY_POLICY) {
|
|
493
|
-
this.policy = policy;
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
shouldRetry(error: ScraperError, attempt: number): boolean {
|
|
497
|
-
if (attempt >= this.policy.maxRetries) return false;
|
|
498
|
-
if (this.policy.nonRetryableErrors.has(error.code)) return false;
|
|
499
|
-
if (this.policy.retryableErrors.has(error.code)) return true;
|
|
500
|
-
return error.retryable;
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
getRetryDelay(error: ScraperError, attempt: number): number {
|
|
504
|
-
const delay = this.policy.baseDelay * Math.pow(this.policy.backoffMultiplier, attempt);
|
|
505
|
-
return Math.min(delay, this.policy.maxDelay);
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
classify(error: unknown): {
|
|
509
|
-
retryable: boolean;
|
|
510
|
-
severity: 'low' | 'medium' | 'high' | 'critical';
|
|
511
|
-
suggestedAction: string;
|
|
512
|
-
} {
|
|
513
|
-
if (error instanceof ScraperError) {
|
|
514
|
-
const retryable = this.shouldRetry(error, 0);
|
|
515
|
-
return {
|
|
516
|
-
retryable,
|
|
517
|
-
severity: error.severity,
|
|
518
|
-
suggestedAction: this.getSuggestedAction(error),
|
|
519
|
-
};
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
// Unknown errors are generally retryable once
|
|
523
|
-
return {
|
|
524
|
-
retryable: true,
|
|
525
|
-
severity: 'medium',
|
|
526
|
-
suggestedAction: 'Unknown error, retry cautiously',
|
|
527
|
-
};
|
|
528
|
-
}
|
|
529
|
-
|
|
530
|
-
private getSuggestedAction(error: ScraperError): string {
|
|
531
|
-
const actions: Partial<Record<ErrorCode, string>> = {
|
|
532
|
-
[ErrorCodes.NETWORK_TIMEOUT]: 'Increase timeout or reduce request rate',
|
|
533
|
-
[ErrorCodes.HTTP_RATE_LIMITED]: 'Wait and retry with exponential backoff',
|
|
534
|
-
[ErrorCodes.BOT_DETECTED]: 'Rotate proxy/user-agent, review request patterns',
|
|
535
|
-
[ErrorCodes.CAPTCHA_DETECTED]: 'Use CAPTCHA solving service or skip URL',
|
|
536
|
-
[ErrorCodes.SELECTOR_NOT_FOUND]: 'Verify selector, page structure may have changed',
|
|
537
|
-
[ErrorCodes.BROWSER_CRASHED]: 'Restart browser pool, check memory limits',
|
|
538
|
-
[ErrorCodes.SESSION_EXPIRED]: 'Refresh session cookies',
|
|
539
|
-
};
|
|
540
|
-
return actions[error.code] ?? 'Review error details';
|
|
541
|
-
}
|
|
542
|
-
}
|
|
543
|
-
```
|
|
544
|
-
|
|
545
|
-
---
|
|
546
|
-
|
|
547
|
-
## 5. Recovery Suggestions in Error Objects
|
|
548
|
-
|
|
549
|
-
### Structured Recovery Metadata
|
|
550
|
-
|
|
551
|
-
```typescript
|
|
552
|
-
interface RecoverySuggestion {
|
|
553
|
-
action: string;
|
|
554
|
-
description: string;
|
|
555
|
-
automated: boolean; // Can the framework do this automatically?
|
|
556
|
-
fallback?: () => unknown;
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
class ScraperError extends Error {
|
|
560
|
-
// ... (previous properties)
|
|
561
|
-
public readonly recovery: RecoverySuggestion[];
|
|
562
|
-
|
|
563
|
-
constructor(
|
|
564
|
-
message: string,
|
|
565
|
-
options: {
|
|
566
|
-
code: ErrorCode;
|
|
567
|
-
cause?: unknown;
|
|
568
|
-
recovery?: RecoverySuggestion[];
|
|
569
|
-
metadata?: Record<string, unknown>;
|
|
570
|
-
}
|
|
571
|
-
) {
|
|
572
|
-
super(message, { cause: options.cause });
|
|
573
|
-
this.name = this.constructor.name;
|
|
574
|
-
this.code = options.code;
|
|
575
|
-
this.recovery = options.recovery ?? [];
|
|
576
|
-
this.timestamp = Date.now();
|
|
577
|
-
this.metadata = options.metadata ?? {};
|
|
578
|
-
Object.setPrototypeOf(this, new.target.prototype);
|
|
579
|
-
}
|
|
580
|
-
|
|
581
|
-
/**
|
|
582
|
-
* Get the first automated recovery action, if any.
|
|
583
|
-
*/
|
|
584
|
-
getAutoRecovery(): RecoverySuggestion | undefined {
|
|
585
|
-
return this.recovery.find(r => r.automated);
|
|
586
|
-
}
|
|
587
|
-
|
|
588
|
-
/**
|
|
589
|
-
* Get all human-readable recovery instructions.
|
|
590
|
-
*/
|
|
591
|
-
getRecoveryInstructions(): string[] {
|
|
592
|
-
return this.recovery.map(r => `[${r.action}] ${r.description}`);
|
|
593
|
-
}
|
|
594
|
-
}
|
|
595
|
-
|
|
596
|
-
// Factory function for common errors with recovery suggestions
|
|
597
|
-
function createRateLimitError(
|
|
598
|
-
url: string,
|
|
599
|
-
retryAfter?: number
|
|
600
|
-
): ScraperError {
|
|
601
|
-
const delay = retryAfter ?? 5000;
|
|
602
|
-
return new ScraperError(`Rate limited by ${new URL(url).hostname}`, {
|
|
603
|
-
code: ErrorCodes.HTTP_RATE_LIMITED,
|
|
604
|
-
metadata: { url, retryAfter: delay },
|
|
605
|
-
recovery: [
|
|
606
|
-
{
|
|
607
|
-
action: 'WAIT_AND_RETRY',
|
|
608
|
-
description: `Wait ${delay}ms then retry the request`,
|
|
609
|
-
automated: true,
|
|
610
|
-
},
|
|
611
|
-
{
|
|
612
|
-
action: 'REDUCE_CONCURRENCY',
|
|
613
|
-
description: 'Lower concurrent request count for this domain',
|
|
614
|
-
automated: true,
|
|
615
|
-
},
|
|
616
|
-
{
|
|
617
|
-
action: 'ROTATE_PROXY',
|
|
618
|
-
description: 'Switch to a different proxy IP address',
|
|
619
|
-
automated: true,
|
|
620
|
-
fallback: () => ({ proxyChanged: true }),
|
|
621
|
-
},
|
|
622
|
-
{
|
|
623
|
-
action: 'REVIEW_ROBOTS_TXT',
|
|
624
|
-
description: 'Check if the site\'s robots.txt allows your crawl pattern',
|
|
625
|
-
automated: false,
|
|
626
|
-
},
|
|
627
|
-
],
|
|
628
|
-
});
|
|
629
|
-
}
|
|
630
|
-
|
|
631
|
-
function createBotDetectedError(url: string): ScraperError {
|
|
632
|
-
return new ScraperError(`Bot detection triggered on ${new URL(url).hostname}`, {
|
|
633
|
-
code: ErrorCodes.BOT_DETECTED,
|
|
634
|
-
metadata: { url },
|
|
635
|
-
recovery: [
|
|
636
|
-
{
|
|
637
|
-
action: 'ROTATE_USER_AGENT',
|
|
638
|
-
description: 'Switch to a different browser fingerprint',
|
|
639
|
-
automated: true,
|
|
640
|
-
},
|
|
641
|
-
{
|
|
642
|
-
action: 'ADD_HUMAN_DELAY',
|
|
643
|
-
description: 'Insert random delays between actions',
|
|
644
|
-
automated: true,
|
|
645
|
-
},
|
|
646
|
-
{
|
|
647
|
-
action: 'STOP_CRAWLING',
|
|
648
|
-
description: 'This site has strong bot protection, consider stopping',
|
|
649
|
-
automated: false,
|
|
650
|
-
},
|
|
651
|
-
],
|
|
652
|
-
});
|
|
653
|
-
}
|
|
654
|
-
```
|
|
655
|
-
|
|
656
|
-
### Error Handler Using Recovery
|
|
657
|
-
|
|
658
|
-
```typescript
|
|
659
|
-
async function handleError(error: unknown): Promise<void> {
|
|
660
|
-
if (error instanceof ScraperError) {
|
|
661
|
-
console.error(`[${error.code}] ${error.message}`);
|
|
662
|
-
|
|
663
|
-
// Log recovery suggestions
|
|
664
|
-
const instructions = error.getRecoveryInstructions();
|
|
665
|
-
if (instructions.length > 0) {
|
|
666
|
-
console.log('Recovery suggestions:');
|
|
667
|
-
instructions.forEach(i => console.log(` - ${i}`));
|
|
668
|
-
}
|
|
669
|
-
|
|
670
|
-
// Try automated recovery
|
|
671
|
-
const autoRecovery = error.getAutoRecovery();
|
|
672
|
-
if (autoRecovery?.fallback) {
|
|
673
|
-
const result = autoRecovery.fallback();
|
|
674
|
-
console.log(`Auto-recovery executed: ${autoRecovery.action}`, result);
|
|
675
|
-
}
|
|
676
|
-
} else {
|
|
677
|
-
console.error('Unexpected error:', error);
|
|
678
|
-
}
|
|
679
|
-
}
|
|
680
|
-
```
|
|
681
|
-
|
|
682
|
-
---
|
|
683
|
-
|
|
684
|
-
## 6. Typed EventEmitter in TypeScript
|
|
685
|
-
|
|
686
|
-
### The Event Map Pattern
|
|
687
|
-
|
|
688
|
-
The core idea: define an interface mapping event names to their payload types. This gives you full type safety — TypeScript knows what `on`, `emit`, and `once` accept and return.
|
|
689
|
-
|
|
690
|
-
```typescript
|
|
691
|
-
// Define your event map
|
|
692
|
-
interface ScraperEvents {
|
|
693
|
-
// Event name → payload type
|
|
694
|
-
'request:start': { url: string; timestamp: number };
|
|
695
|
-
'request:success': { url: string; data: unknown; duration: number };
|
|
696
|
-
'request:error': { url: string; error: ScraperError };
|
|
697
|
-
'request:retry': { url: string; attempt: number; delay: number };
|
|
698
|
-
'batch:complete': { total: number; succeeded: number; failed: number };
|
|
699
|
-
'browser:launch': { browser: string; pid: number };
|
|
700
|
-
'browser:crash': { browser: string; error: string };
|
|
701
|
-
'session:rotate': { oldSession: string; newSession: string };
|
|
702
|
-
}
|
|
703
|
-
```
|
|
704
|
-
|
|
705
|
-
### Hand-Rolled Typed Emitter
|
|
706
|
-
|
|
707
|
-
```typescript
|
|
708
|
-
type EventMap = Record<string, unknown>;
|
|
709
|
-
|
|
710
|
-
type EventKey<T extends EventMap> = string & keyof T;
|
|
711
|
-
type EventCallback<T extends EventMap, K extends EventKey<T>> =
|
|
712
|
-
(payload: T[K]) => void | Promise<void>;
|
|
713
|
-
|
|
714
|
-
class TypedEmitter<T extends EventMap> {
|
|
715
|
-
private listeners = new Map<string, Set<Function>>();
|
|
716
|
-
|
|
717
|
-
on<K extends EventKey<T>>(
|
|
718
|
-
event: K,
|
|
719
|
-
callback: EventCallback<T, K>
|
|
720
|
-
): () => void {
|
|
721
|
-
if (!this.listeners.has(event)) {
|
|
722
|
-
this.listeners.set(event, new Set());
|
|
723
|
-
}
|
|
724
|
-
this.listeners.get(event)!.add(callback);
|
|
725
|
-
|
|
726
|
-
// Return unsubscribe function
|
|
727
|
-
return () => {
|
|
728
|
-
this.listeners.get(event)?.delete(callback);
|
|
729
|
-
};
|
|
730
|
-
}
|
|
731
|
-
|
|
732
|
-
once<K extends EventKey<T>>(event: K, callback: EventCallback<T, K>): () => void {
|
|
733
|
-
const wrapped = ((payload: T[K]) => {
|
|
734
|
-
this.off(event, wrapped as EventCallback<T, K>);
|
|
735
|
-
return callback(payload);
|
|
736
|
-
}) as EventCallback<T, K>;
|
|
737
|
-
return this.on(event, wrapped);
|
|
738
|
-
}
|
|
739
|
-
|
|
740
|
-
off<K extends EventKey<T>>(event: K, callback: EventCallback<T, K>): void {
|
|
741
|
-
this.listeners.get(event)?.delete(callback);
|
|
742
|
-
}
|
|
743
|
-
|
|
744
|
-
async emit<K extends EventKey<T>>(event: K, payload: T[K]): Promise<void> {
|
|
745
|
-
const callbacks = this.listeners.get(event);
|
|
746
|
-
if (!callbacks) return;
|
|
747
|
-
|
|
748
|
-
for (const callback of callbacks) {
|
|
749
|
-
try {
|
|
750
|
-
await (callback as EventCallback<T, K>)(payload);
|
|
751
|
-
} catch (error) {
|
|
752
|
-
console.error(`Error in listener for "${event}":`, error);
|
|
753
|
-
}
|
|
754
|
-
}
|
|
755
|
-
}
|
|
756
|
-
|
|
757
|
-
removeAllListeners(event?: EventKey<T>): void {
|
|
758
|
-
if (event) {
|
|
759
|
-
this.listeners.delete(event);
|
|
760
|
-
} else {
|
|
761
|
-
this.listeners.clear();
|
|
762
|
-
}
|
|
763
|
-
}
|
|
764
|
-
|
|
765
|
-
listenerCount(event: EventKey<T>): number {
|
|
766
|
-
return this.listeners.get(event)?.size ?? 0;
|
|
767
|
-
}
|
|
768
|
-
}
|
|
769
|
-
|
|
770
|
-
// Usage
|
|
771
|
-
const emitter = new TypedEmitter<ScraperEvents>();
|
|
772
|
-
|
|
773
|
-
// ✅ TypeScript knows payload is { url: string; timestamp: number }
|
|
774
|
-
emitter.on('request:start', (payload) => {
|
|
775
|
-
console.log(`Starting ${payload.url} at ${payload.timestamp}`);
|
|
776
|
-
});
|
|
777
|
-
|
|
778
|
-
// ✅ TypeScript error if you try wrong event name or wrong payload
|
|
779
|
-
// emitter.on('request:start', (payload) => {
|
|
780
|
-
// console.log(payload.data); // ❌ Error: 'data' does not exist
|
|
781
|
-
// });
|
|
782
|
-
```
|
|
783
|
-
|
|
784
|
-
---
|
|
785
|
-
|
|
786
|
-
## 7. Best Packages
|
|
787
|
-
|
|
788
|
-
### emittery 2.0.0 (Recommended for Modern Projects)
|
|
789
|
-
|
|
790
|
-
- **Async-first**: All handlers are awaited
|
|
791
|
-
- **AbortSignal support**: Native integration with `AbortController`
|
|
792
|
-
- **Symbol.dispose**: Supports the new `using` keyword (TC39 Explicit Resource Management)
|
|
793
|
-
- **Type-safe**: Excellent TypeScript support
|
|
794
|
-
|
|
795
|
-
```typescript
|
|
796
|
-
import Emittery from 'emittery';
|
|
797
|
-
|
|
798
|
-
interface Events {
|
|
799
|
-
data: { chunk: Buffer; source: string };
|
|
800
|
-
error: { error: Error; recoverable: boolean };
|
|
801
|
-
done: undefined;
|
|
802
|
-
}
|
|
803
|
-
|
|
804
|
-
const emitter = new Emittery<Events>();
|
|
805
|
-
|
|
806
|
-
// Async iteration
|
|
807
|
-
async function processEvents() {
|
|
808
|
-
for await (const data of emitter.events('data')) {
|
|
809
|
-
console.log(`Received ${data.chunk.length} bytes from ${data.source}`);
|
|
810
|
-
}
|
|
811
|
-
}
|
|
812
|
-
|
|
813
|
-
// AbortSignal support
|
|
814
|
-
const controller = new AbortController();
|
|
815
|
-
emitter.on('data', async (data) => {
|
|
816
|
-
await processChunk(data);
|
|
817
|
-
}, { signal: controller.signal });
|
|
818
|
-
|
|
819
|
-
// Symbol.dispose for `using` keyword
|
|
820
|
-
{
|
|
821
|
-
using _sub = emitter.on('error', (err) => console.error(err));
|
|
822
|
-
// Automatically unsubscribed when scope exits
|
|
823
|
-
}
|
|
824
|
-
```
|
|
825
|
-
|
|
826
|
-
### eventemitter3 5.0.4 (Fastest)
|
|
827
|
-
|
|
828
|
-
- **Fastest**: Benchmarks 2-5x faster than Node's EventEmitter
|
|
829
|
-
- **Tiny**: ~1KB minified
|
|
830
|
-
- **No extra features**: Just fast events
|
|
831
|
-
|
|
832
|
-
```typescript
|
|
833
|
-
import EventEmitter from 'eventemitter3';
|
|
834
|
-
|
|
835
|
-
interface Events {
|
|
836
|
-
'data:chunk': Buffer;
|
|
837
|
-
'scrape:complete': { url: string; items: number };
|
|
838
|
-
}
|
|
839
|
-
|
|
840
|
-
const ee = new EventEmitter<Events>();
|
|
841
|
-
|
|
842
|
-
// Same API as Node EventEmitter, but faster
|
|
843
|
-
ee.on('data:chunk', (chunk) => {
|
|
844
|
-
console.log(chunk.length);
|
|
845
|
-
});
|
|
846
|
-
|
|
847
|
-
ee.emit('scrape:complete', { url: 'https://example.com', items: 42 });
|
|
848
|
-
```
|
|
849
|
-
|
|
850
|
-
### mitt 3.0.1 (Smallest)
|
|
851
|
-
|
|
852
|
-
- **200 bytes** gzipped
|
|
853
|
-
- **No dependencies**
|
|
854
|
-
- **Works everywhere**: Browser, Node, Deno, Bun
|
|
855
|
-
|
|
856
|
-
```typescript
|
|
857
|
-
import mitt from 'mitt';
|
|
858
|
-
|
|
859
|
-
interface Events {
|
|
860
|
-
'scrape:start': string;
|
|
861
|
-
'scrape:end': { url: string; duration: number };
|
|
862
|
-
}
|
|
863
|
-
|
|
864
|
-
const emitter = mitt<Events>();
|
|
865
|
-
|
|
866
|
-
emitter.on('scrape:start', (url) => console.log(`Starting ${url}`));
|
|
867
|
-
emitter.on('scrape:end', ({ url, duration }) => console.log(`${url}: ${duration}ms`));
|
|
868
|
-
|
|
869
|
-
emitter.emit('scrape:start', 'https://example.com');
|
|
870
|
-
```
|
|
871
|
-
|
|
872
|
-
### Comparison Table
|
|
873
|
-
|
|
874
|
-
| Feature | emittery 2.0 | eventemitter3 5.0 | mitt 3.0 |
|
|
875
|
-
|---------|-------------|-------------------|----------|
|
|
876
|
-
| Size (gzip) | ~3KB | ~1KB | ~200B |
|
|
877
|
-
| Async handlers | ✅ Native | ❌ Manual | ❌ Manual |
|
|
878
|
-
| AbortSignal | ✅ Built-in | ❌ No | ❌ No |
|
|
879
|
-
| Symbol.dispose | ✅ Yes | ❌ No | ❌ No |
|
|
880
|
-
| Type safety | ✅ Excellent | ✅ Good | ✅ Good |
|
|
881
|
-
| Performance | Good | Best | Good |
|
|
882
|
-
| Node.js | ✅ | ✅ | ✅ |
|
|
883
|
-
| Browser | ✅ | ✅ | ✅ |
|
|
884
|
-
|
|
885
|
-
**Recommendation**: Use `emittery` for new projects (best DX), `eventemitter3` for performance-critical paths, `mitt` for minimal bundles.
|
|
886
|
-
|
|
887
|
-
---
|
|
888
|
-
|
|
889
|
-
## 8. Lifecycle Hooks
|
|
890
|
-
|
|
891
|
-
### The Pattern
|
|
892
|
-
|
|
893
|
-
Lifecycle hooks are named extension points where plugins or middleware can inject behavior. This pattern is used by `ky`, `ofetch`, and various scraping frameworks.
|
|
894
|
-
|
|
895
|
-
```typescript
|
|
896
|
-
interface RequestLifecycle {
|
|
897
|
-
beforeRequest: ((request: Request) => Request | Promise<Request>)[];
|
|
898
|
-
afterResponse: ((response: Response, request: Request) => Response | Promise<Response>)[];
|
|
899
|
-
beforeRetry: ((error: ScraperError, attempt: number) => void | Promise<void>)[];
|
|
900
|
-
beforeError: ((error: ScraperError) => ScraperError | Promise<ScraperError>)[];
|
|
901
|
-
}
|
|
902
|
-
|
|
903
|
-
class RequestPipeline {
|
|
904
|
-
private hooks: RequestLifecycle = {
|
|
905
|
-
beforeRequest: [],
|
|
906
|
-
afterResponse: [],
|
|
907
|
-
beforeRetry: [],
|
|
908
|
-
beforeError: [],
|
|
909
|
-
};
|
|
910
|
-
|
|
911
|
-
on<K extends keyof RequestLifecycle>(
|
|
912
|
-
hook: K,
|
|
913
|
-
callback: RequestLifecycle[K][number]
|
|
914
|
-
): () => void {
|
|
915
|
-
this.hooks[hook].push(callback as never);
|
|
916
|
-
return () => {
|
|
917
|
-
const idx = this.hooks[hook].indexOf(callback as never);
|
|
918
|
-
if (idx !== -1) this.hooks[hook].splice(idx, 1);
|
|
919
|
-
};
|
|
920
|
-
}
|
|
921
|
-
|
|
922
|
-
async executeRequest(
|
|
923
|
-
url: string,
|
|
924
|
-
options: RequestInit = {}
|
|
925
|
-
): Promise<Response> {
|
|
926
|
-
let request = new Request(url, options);
|
|
927
|
-
let attempt = 0;
|
|
928
|
-
const maxAttempts = 3;
|
|
929
|
-
|
|
930
|
-
while (attempt < maxAttempts) {
|
|
931
|
-
try {
|
|
932
|
-
// beforeRequest hooks
|
|
933
|
-
for (const hook of this.hooks.beforeRequest) {
|
|
934
|
-
request = await hook(request);
|
|
935
|
-
}
|
|
936
|
-
|
|
937
|
-
const response = await fetch(request);
|
|
938
|
-
|
|
939
|
-
// afterResponse hooks
|
|
940
|
-
let processedResponse = response;
|
|
941
|
-
for (const hook of this.hooks.afterResponse) {
|
|
942
|
-
processedResponse = await hook(processedResponse, request);
|
|
943
|
-
}
|
|
944
|
-
|
|
945
|
-
if (!processedResponse.ok) {
|
|
946
|
-
throw new HttpError(
|
|
947
|
-
`HTTP ${processedResponse.status}`,
|
|
948
|
-
processedResponse.status
|
|
949
|
-
);
|
|
950
|
-
}
|
|
951
|
-
|
|
952
|
-
return processedResponse;
|
|
953
|
-
} catch (error) {
|
|
954
|
-
const scraperError = error instanceof ScraperError
|
|
955
|
-
? error
|
|
956
|
-
: new ScraperError(String(error), {
|
|
957
|
-
code: ErrorCodes.NETWORK_TIMEOUT,
|
|
958
|
-
cause: error,
|
|
959
|
-
});
|
|
960
|
-
|
|
961
|
-
// beforeError hooks
|
|
962
|
-
let finalError = scraperError;
|
|
963
|
-
for (const hook of this.hooks.beforeError) {
|
|
964
|
-
finalError = await hook(finalError);
|
|
965
|
-
}
|
|
966
|
-
|
|
967
|
-
if (finalError.retryable && attempt < maxAttempts - 1) {
|
|
968
|
-
// beforeRetry hooks
|
|
969
|
-
for (const hook of this.hooks.beforeRetry) {
|
|
970
|
-
await hook(finalError, attempt);
|
|
971
|
-
}
|
|
972
|
-
attempt++;
|
|
973
|
-
await new Promise(r => setTimeout(r, 1000 * attempt));
|
|
974
|
-
continue;
|
|
975
|
-
}
|
|
976
|
-
|
|
977
|
-
throw finalError;
|
|
978
|
-
}
|
|
979
|
-
}
|
|
980
|
-
|
|
981
|
-
throw new ScraperError('Max retries exceeded', {
|
|
982
|
-
code: ErrorCodes.NETWORK_TIMEOUT,
|
|
983
|
-
});
|
|
984
|
-
}
|
|
985
|
-
}
|
|
986
|
-
```
|
|
987
|
-
|
|
988
|
-
### ky-Style Plugin API
|
|
989
|
-
|
|
990
|
-
```typescript
|
|
991
|
-
// ky-style hooks (simplified)
|
|
992
|
-
interface KyOptions {
|
|
993
|
-
hooks?: {
|
|
994
|
-
beforeRequest?: ((request: Request) => Request | Promise<Request>)[];
|
|
995
|
-
afterResponse?: ((response: Response) => Response | Promise<Response>)[];
|
|
996
|
-
beforeRetry?: ((error: Error, retryCount: number) => void | Promise<void>)[];
|
|
997
|
-
beforeError?: ((error: HTTPError) => HTTPError | Promise<HTTPError>)[];
|
|
998
|
-
};
|
|
999
|
-
}
|
|
1000
|
-
|
|
1001
|
-
// Usage with ky
|
|
1002
|
-
import ky from 'ky';
|
|
1003
|
-
|
|
1004
|
-
const api = ky.create({
|
|
1005
|
-
hooks: {
|
|
1006
|
-
beforeRequest: [
|
|
1007
|
-
(request) => {
|
|
1008
|
-
request.headers.set('Authorization', `Bearer ${getToken()}`);
|
|
1009
|
-
return request;
|
|
1010
|
-
},
|
|
1011
|
-
],
|
|
1012
|
-
afterResponse: [
|
|
1013
|
-
(response) => {
|
|
1014
|
-
if (response.status === 401) {
|
|
1015
|
-
refreshToken();
|
|
1016
|
-
}
|
|
1017
|
-
return response;
|
|
1018
|
-
},
|
|
1019
|
-
],
|
|
1020
|
-
beforeRetry: [
|
|
1021
|
-
(error, retryCount) => {
|
|
1022
|
-
console.log(`Retry #${retryCount} after ${error.message}`);
|
|
1023
|
-
},
|
|
1024
|
-
],
|
|
1025
|
-
},
|
|
1026
|
-
});
|
|
1027
|
-
```
|
|
1028
|
-
|
|
1029
|
-
### ofetch Pattern
|
|
1030
|
-
|
|
1031
|
-
```typescript
|
|
1032
|
-
import { ofetch } from 'ofetch';
|
|
1033
|
-
|
|
1034
|
-
const fetcher = ofetch.create({
|
|
1035
|
-
baseURL: 'https://api.example.com',
|
|
1036
|
-
headers: { 'User-Agent': 'MyScraper/1.0' },
|
|
1037
|
-
|
|
1038
|
-
// Retry configuration
|
|
1039
|
-
retry: 3,
|
|
1040
|
-
retryDelay: 1000,
|
|
1041
|
-
backoff: (attempt) => Math.pow(2, attempt) * 1000,
|
|
1042
|
-
|
|
1043
|
-
// Hook
|
|
1044
|
-
onResponse({ response }) {
|
|
1045
|
-
console.log(`Response: ${response.status}`);
|
|
1046
|
-
},
|
|
1047
|
-
onRequestError({ error }) {
|
|
1048
|
-
console.error(`Request failed: ${error.message}`);
|
|
1049
|
-
},
|
|
1050
|
-
});
|
|
1051
|
-
```
|
|
1052
|
-
|
|
1053
|
-
---
|
|
1054
|
-
|
|
1055
|
-
## 9. Middleware Pipelines
|
|
1056
|
-
|
|
1057
|
-
### Koa-Style Onion Model
|
|
1058
|
-
|
|
1059
|
-
The onion model wraps the core handler in layers. Each middleware can run code before and after the core, forming a concentric structure like onion layers.
|
|
1060
|
-
|
|
1061
|
-
```typescript
|
|
1062
|
-
type Middleware<TContext> = (
|
|
1063
|
-
context: TContext,
|
|
1064
|
-
next: () => Promise<void>
|
|
1065
|
-
) => Promise<void>;
|
|
1066
|
-
|
|
1067
|
-
class MiddlewarePipeline<TContext> {
|
|
1068
|
-
private middlewares: Middleware<TContext>[] = [];
|
|
1069
|
-
|
|
1070
|
-
use(middleware: Middleware<TContext>): this {
|
|
1071
|
-
this.middlewares.push(middleware);
|
|
1072
|
-
return this;
|
|
1073
|
-
}
|
|
1074
|
-
|
|
1075
|
-
async execute(context: TContext): Promise<TContext> {
|
|
1076
|
-
// Build the onion: each middleware wraps the next
|
|
1077
|
-
let index = this.middlewares.length;
|
|
1078
|
-
|
|
1079
|
-
const dispatch = async (i: number): Promise<void> => {
|
|
1080
|
-
if (i < this.middlewares.length) {
|
|
1081
|
-
const middleware = this.middlewares[i];
|
|
1082
|
-
await middleware(context, () => dispatch(i + 1));
|
|
1083
|
-
}
|
|
1084
|
-
};
|
|
1085
|
-
|
|
1086
|
-
await dispatch(0);
|
|
1087
|
-
return context;
|
|
1088
|
-
}
|
|
1089
|
-
}
|
|
1090
|
-
|
|
1091
|
-
// Usage
|
|
1092
|
-
interface ScrapeContext {
|
|
1093
|
-
url: string;
|
|
1094
|
-
html?: string;
|
|
1095
|
-
data?: unknown;
|
|
1096
|
-
error?: ScraperError;
|
|
1097
|
-
metadata: Record<string, unknown>;
|
|
1098
|
-
}
|
|
1099
|
-
|
|
1100
|
-
const pipeline = new MiddlewarePipeline<ScrapeContext>();
|
|
1101
|
-
|
|
1102
|
-
// Logging middleware
|
|
1103
|
-
pipeline.use(async (ctx, next) => {
|
|
1104
|
-
const start = Date.now();
|
|
1105
|
-
console.log(`→ ${ctx.url}`);
|
|
1106
|
-
await next();
|
|
1107
|
-
const duration = Date.now() - start;
|
|
1108
|
-
console.log(`← ${ctx.url} (${duration}ms)`);
|
|
1109
|
-
});
|
|
1110
|
-
|
|
1111
|
-
// User-Agent middleware
|
|
1112
|
-
pipeline.use(async (ctx, next) => {
|
|
1113
|
-
// Modify request before
|
|
1114
|
-
ctx.metadata.userAgent = 'Mozilla/5.0 ...';
|
|
1115
|
-
await next();
|
|
1116
|
-
// Process response after
|
|
1117
|
-
if (ctx.html) {
|
|
1118
|
-
console.log(`Fetched ${ctx.html.length} bytes`);
|
|
1119
|
-
}
|
|
1120
|
-
});
|
|
1121
|
-
|
|
1122
|
-
// Error handling middleware
|
|
1123
|
-
pipeline.use(async (ctx, next) => {
|
|
1124
|
-
try {
|
|
1125
|
-
await next();
|
|
1126
|
-
} catch (error) {
|
|
1127
|
-
ctx.error = error instanceof ScraperError
|
|
1128
|
-
? error
|
|
1129
|
-
: new ScraperError(String(error), { code: ErrorCodes.NETWORK_TIMEOUT });
|
|
1130
|
-
console.error(`Error on ${ctx.url}: ${ctx.error.message}`);
|
|
1131
|
-
}
|
|
1132
|
-
});
|
|
1133
|
-
|
|
1134
|
-
// Core handler (the innermost layer)
|
|
1135
|
-
pipeline.use(async (ctx, next) => {
|
|
1136
|
-
const res = await fetch(ctx.url, {
|
|
1137
|
-
headers: { 'User-Agent': ctx.metadata.userAgent as string },
|
|
1138
|
-
});
|
|
1139
|
-
ctx.html = await res.text();
|
|
1140
|
-
await next();
|
|
1141
|
-
});
|
|
1142
|
-
|
|
1143
|
-
// Execute
|
|
1144
|
-
const context: ScrapeContext = {
|
|
1145
|
-
url: 'https://example.com',
|
|
1146
|
-
metadata: {},
|
|
1147
|
-
};
|
|
1148
|
-
await pipeline.execute(context);
|
|
1149
|
-
```
|
|
1150
|
-
|
|
1151
|
-
### Express-Style Flat Middleware
|
|
1152
|
-
|
|
1153
|
-
```typescript
|
|
1154
|
-
type FlatMiddleware<T> = (item: T, next: () => Promise<void>) => Promise<void>;
|
|
1155
|
-
|
|
1156
|
-
class FlatPipeline<T> {
|
|
1157
|
-
private middlewares: FlatMiddleware<T>[] = [];
|
|
1158
|
-
|
|
1159
|
-
use(middleware: FlatMiddleware<T>): this {
|
|
1160
|
-
this.middlewares.push(middleware);
|
|
1161
|
-
return this;
|
|
1162
|
-
}
|
|
1163
|
-
|
|
1164
|
-
async execute(item: T): Promise<T> {
|
|
1165
|
-
let index = 0;
|
|
1166
|
-
|
|
1167
|
-
const next = async (): Promise<void> => {
|
|
1168
|
-
if (index < this.middlewares.length) {
|
|
1169
|
-
const middleware = this.middlewares[index++];
|
|
1170
|
-
await middleware(item, next);
|
|
1171
|
-
}
|
|
1172
|
-
};
|
|
1173
|
-
|
|
1174
|
-
await next();
|
|
1175
|
-
return item;
|
|
1176
|
-
}
|
|
1177
|
-
}
|
|
1178
|
-
```
|
|
1179
|
-
|
|
1180
|
-
### Interceptor Pattern (Axios-Style)
|
|
1181
|
-
|
|
1182
|
-
```typescript
|
|
1183
|
-
interface Interceptor<T> {
|
|
1184
|
-
onFulfilled: (value: T) => T | Promise<T>;
|
|
1185
|
-
onRejected?: (error: unknown) => T | Promise<T>;
|
|
1186
|
-
}
|
|
1187
|
-
|
|
1188
|
-
class InterceptorManager<T> {
|
|
1189
|
-
private interceptors: Interceptor<T>[] = [];
|
|
1190
|
-
|
|
1191
|
-
use(
|
|
1192
|
-
onFulfilled: (value: T) => T | Promise<T>,
|
|
1193
|
-
onRejected?: (error: unknown) => T | Promise<T>
|
|
1194
|
-
): number {
|
|
1195
|
-
this.interceptors.push({ onFulfilled, onRejected });
|
|
1196
|
-
return this.interceptors.length - 1;
|
|
1197
|
-
}
|
|
1198
|
-
|
|
1199
|
-
eject(id: number): void {
|
|
1200
|
-
this.interceptors.splice(id, 1);
|
|
1201
|
-
}
|
|
1202
|
-
|
|
1203
|
-
async run(value: T): Promise<T> {
|
|
1204
|
-
let result = value;
|
|
1205
|
-
for (const interceptor of this.interceptors) {
|
|
1206
|
-
try {
|
|
1207
|
-
result = await interceptor.onFulfilled(result);
|
|
1208
|
-
} catch (error) {
|
|
1209
|
-
if (interceptor.onRejected) {
|
|
1210
|
-
result = await interceptor.onRejected(error);
|
|
1211
|
-
} else {
|
|
1212
|
-
throw error;
|
|
1213
|
-
}
|
|
1214
|
-
}
|
|
1215
|
-
}
|
|
1216
|
-
return result;
|
|
1217
|
-
}
|
|
1218
|
-
}
|
|
1219
|
-
|
|
1220
|
-
// Usage
|
|
1221
|
-
const requestInterceptors = new InterceptorManager<Request>();
|
|
1222
|
-
const responseInterceptors = new InterceptorManager<Response>();
|
|
1223
|
-
|
|
1224
|
-
requestInterceptors.use(
|
|
1225
|
-
(request) => {
|
|
1226
|
-
request.headers.set('X-Request-ID', crypto.randomUUID());
|
|
1227
|
-
return request;
|
|
1228
|
-
}
|
|
1229
|
-
);
|
|
1230
|
-
|
|
1231
|
-
responseInterceptors.use(
|
|
1232
|
-
(response) => {
|
|
1233
|
-
console.log(`Status: ${response.status}`);
|
|
1234
|
-
return response;
|
|
1235
|
-
},
|
|
1236
|
-
(error) => {
|
|
1237
|
-
console.error('Response error:', error);
|
|
1238
|
-
throw error;
|
|
1239
|
-
}
|
|
1240
|
-
);
|
|
1241
|
-
```
|
|
1242
|
-
|
|
1243
|
-
---
|
|
1244
|
-
|
|
1245
|
-
## 10. Plugin/Hook Systems
|
|
1246
|
-
|
|
1247
|
-
### HookRegistry with Typed Events
|
|
1248
|
-
|
|
1249
|
-
```typescript
|
|
1250
|
-
type HookHandler<T = void> = (context: T) => T | Promise<T>;
|
|
1251
|
-
|
|
1252
|
-
class HookRegistry<TEventMap extends Record<string, unknown>> {
|
|
1253
|
-
private hooks = new Map<string, Set<HookHandler<any>>>();
|
|
1254
|
-
private priorities = new Map<string, Map<HookHandler<any>, number>>();
|
|
1255
|
-
|
|
1256
|
-
/**
|
|
1257
|
-
* Register a hook handler with optional priority (lower = runs first).
|
|
1258
|
-
*/
|
|
1259
|
-
on<K extends keyof TEventMap & string>(
|
|
1260
|
-
event: K,
|
|
1261
|
-
handler: HookHandler<TEventMap[K]>,
|
|
1262
|
-
priority: number = 10
|
|
1263
|
-
): () => void {
|
|
1264
|
-
if (!this.hooks.has(event)) {
|
|
1265
|
-
this.hooks.set(event, new Set());
|
|
1266
|
-
this.priorities.set(event, new Map());
|
|
1267
|
-
}
|
|
1268
|
-
|
|
1269
|
-
this.hooks.get(event)!.add(handler);
|
|
1270
|
-
this.priorities.get(event)!.set(handler, priority);
|
|
1271
|
-
|
|
1272
|
-
return () => {
|
|
1273
|
-
this.hooks.get(event)?.delete(handler);
|
|
1274
|
-
this.priorities.get(event)?.delete(handler);
|
|
1275
|
-
};
|
|
1276
|
-
}
|
|
1277
|
-
|
|
1278
|
-
/**
|
|
1279
|
-
* Execute all handlers for an event, in priority order.
|
|
1280
|
-
* Each handler receives the result of the previous one.
|
|
1281
|
-
*/
|
|
1282
|
-
async trigger<K extends keyof TEventMap & string>(
|
|
1283
|
-
event: K,
|
|
1284
|
-
initial: TEventMap[K]
|
|
1285
|
-
): Promise<TEventMap[K]> {
|
|
1286
|
-
const handlers = this.hooks.get(event);
|
|
1287
|
-
if (!handlers || handlers.size === 0) return initial;
|
|
1288
|
-
|
|
1289
|
-
// Sort by priority
|
|
1290
|
-
const sorted = [...handlers].sort((a, b) => {
|
|
1291
|
-
const pa = this.priorities.get(event)!.get(a) ?? 10;
|
|
1292
|
-
const pb = this.priorities.get(event)!.get(b) ?? 10;
|
|
1293
|
-
return pa - pb;
|
|
1294
|
-
});
|
|
1295
|
-
|
|
1296
|
-
let result = initial;
|
|
1297
|
-
for (const handler of sorted) {
|
|
1298
|
-
try {
|
|
1299
|
-
result = await handler(result);
|
|
1300
|
-
} catch (error) {
|
|
1301
|
-
console.error(`Hook "${event}" handler failed:`, error);
|
|
1302
|
-
}
|
|
1303
|
-
}
|
|
1304
|
-
|
|
1305
|
-
return result;
|
|
1306
|
-
}
|
|
1307
|
-
|
|
1308
|
-
/**
|
|
1309
|
-
* Execute all handlers for an event (fire-and-forget style).
|
|
1310
|
-
*/
|
|
1311
|
-
async notify<K extends keyof TEventMap & string>(
|
|
1312
|
-
event: K,
|
|
1313
|
-
payload: TEventMap[K]
|
|
1314
|
-
): Promise<void> {
|
|
1315
|
-
const handlers = this.hooks.get(event);
|
|
1316
|
-
if (!handlers) return;
|
|
1317
|
-
|
|
1318
|
-
for (const handler of handlers) {
|
|
1319
|
-
try {
|
|
1320
|
-
await handler(payload);
|
|
1321
|
-
} catch (error) {
|
|
1322
|
-
console.error(`Notification handler for "${event}" failed:`, error);
|
|
1323
|
-
}
|
|
1324
|
-
}
|
|
1325
|
-
}
|
|
1326
|
-
|
|
1327
|
-
/**
|
|
1328
|
-
* Remove all hooks, optionally for a specific event.
|
|
1329
|
-
*/
|
|
1330
|
-
clear(event?: keyof TEventMap & string): void {
|
|
1331
|
-
if (event) {
|
|
1332
|
-
this.hooks.delete(event);
|
|
1333
|
-
this.priorities.delete(event);
|
|
1334
|
-
} else {
|
|
1335
|
-
this.hooks.clear();
|
|
1336
|
-
this.priorities.clear();
|
|
1337
|
-
}
|
|
1338
|
-
}
|
|
1339
|
-
}
|
|
1340
|
-
|
|
1341
|
-
// Define event types
|
|
1342
|
-
interface CrawlerHooks {
|
|
1343
|
-
'request:before': { url: string; options: RequestInit };
|
|
1344
|
-
'request:after': { url: string; response: Response };
|
|
1345
|
-
'parse:before': { url: string; html: string };
|
|
1346
|
-
'parse:after': { url: string; data: unknown };
|
|
1347
|
-
'error': { url: string; error: ScraperError };
|
|
1348
|
-
}
|
|
1349
|
-
|
|
1350
|
-
// Usage
|
|
1351
|
-
const hooks = new HookRegistry<CrawlerHooks>();
|
|
1352
|
-
|
|
1353
|
-
// Register hooks with priorities
|
|
1354
|
-
hooks.on('request:before', async (ctx) => {
|
|
1355
|
-
ctx.options.headers = { ...ctx.options.headers, 'User-Agent': 'Bot/1.0' };
|
|
1356
|
-
return ctx;
|
|
1357
|
-
}, 1); // Priority 1 (runs first)
|
|
1358
|
-
|
|
1359
|
-
hooks.on('request:before', async (ctx) => {
|
|
1360
|
-
console.log(`Fetching ${ctx.url}`);
|
|
1361
|
-
return ctx;
|
|
1362
|
-
}, 10); // Priority 10 (runs second)
|
|
1363
|
-
|
|
1364
|
-
// Plugin system using HookRegistry
|
|
1365
|
-
class RateLimitPlugin {
|
|
1366
|
-
private requestCounts = new Map<string, number>();
|
|
1367
|
-
|
|
1368
|
-
register(hooks: HookRegistry<CrawlerHooks>): void {
|
|
1369
|
-
hooks.on('request:before', async (ctx) => {
|
|
1370
|
-
const domain = new URL(ctx.url).hostname;
|
|
1371
|
-
const count = this.requestCounts.get(domain) ?? 0;
|
|
1372
|
-
|
|
1373
|
-
if (count >= 10) {
|
|
1374
|
-
await new Promise(r => setTimeout(r, 1000));
|
|
1375
|
-
this.requestCounts.set(domain, 0);
|
|
1376
|
-
}
|
|
1377
|
-
|
|
1378
|
-
this.requestCounts.set(domain, count + 1);
|
|
1379
|
-
return ctx;
|
|
1380
|
-
}, 5);
|
|
1381
|
-
}
|
|
1382
|
-
}
|
|
1383
|
-
|
|
1384
|
-
// Plugin usage
|
|
1385
|
-
const rateLimitPlugin = new RateLimitPlugin();
|
|
1386
|
-
rateLimitPlugin.register(hooks);
|
|
1387
|
-
```
|
|
1388
|
-
|
|
1389
|
-
---
|
|
1390
|
-
|
|
1391
|
-
## 11. Tree-shaking
|
|
1392
|
-
|
|
1393
|
-
### Principles
|
|
1394
|
-
|
|
1395
|
-
Tree-shaking removes unused code at build time. For your error classes and utilities to be tree-shakeable:
|
|
1396
|
-
|
|
1397
|
-
1. **ESM-only**: Use `export`/`import`, never `module.exports`
|
|
1398
|
-
2. **Named exports**: Avoid default exports (bundlers can't rename them)
|
|
1399
|
-
3. **No side effects**: Mark package as side-effect-free
|
|
1400
|
-
4. **Avoid namespace re-exports**: Don't do `export * as utils from './utils'`
|
|
1401
|
-
|
|
1402
|
-
### package.json Configuration
|
|
1403
|
-
|
|
1404
|
-
```json
|
|
1405
|
-
{
|
|
1406
|
-
"type": "module",
|
|
1407
|
-
"sideEffects": false,
|
|
1408
|
-
"exports": {
|
|
1409
|
-
".": {
|
|
1410
|
-
"import": "./dist/index.mjs",
|
|
1411
|
-
"types": "./dist/index.d.ts"
|
|
1412
|
-
},
|
|
1413
|
-
"./errors": {
|
|
1414
|
-
"import": "./dist/errors.mjs",
|
|
1415
|
-
"types": "./dist/errors.d.ts"
|
|
1416
|
-
},
|
|
1417
|
-
"./events": {
|
|
1418
|
-
"import": "./dist/events.mjs",
|
|
1419
|
-
"types": "./dist/events.d.ts"
|
|
1420
|
-
}
|
|
1421
|
-
}
|
|
1422
|
-
}
|
|
1423
|
-
```
|
|
1424
|
-
|
|
1425
|
-
### ESM-Only Module Structure
|
|
1426
|
-
|
|
1427
|
-
```typescript
|
|
1428
|
-
// src/errors/index.ts — Named exports only!
|
|
1429
|
-
export { ScraperError } from './scraper-error.js';
|
|
1430
|
-
export { NetworkError } from './network-error.js';
|
|
1431
|
-
export { HttpError } from './http-error.js';
|
|
1432
|
-
export { BrowserError } from './browser-error.js';
|
|
1433
|
-
export { ScrapingError } from './scraping-error.js';
|
|
1434
|
-
export { ErrorCodes } from './error-codes.js';
|
|
1435
|
-
export { ErrorClassifier } from './error-classifier.js';
|
|
1436
|
-
|
|
1437
|
-
// ❌ DON'T: Default export
|
|
1438
|
-
// export default { ScraperError, NetworkError };
|
|
1439
|
-
|
|
1440
|
-
// ❌ DON'T: Namespace re-export
|
|
1441
|
-
// import * as errors from './errors';
|
|
1442
|
-
// export { errors };
|
|
1443
|
-
|
|
1444
|
-
// ✅ DO: Named exports (tree-shakeable)
|
|
1445
|
-
// import { ScraperError, NetworkError } from '@myscrape/errors';
|
|
1446
|
-
```
|
|
1447
|
-
|
|
1448
|
-
### Avoiding Side Effects
|
|
1449
|
-
|
|
1450
|
-
```typescript
|
|
1451
|
-
// ❌ BAD: Side effects in module scope
|
|
1452
|
-
export class ScraperError extends Error { /* ... */ }
|
|
1453
|
-
const globalRegistry = new ErrorRegistry(); // Side effect!
|
|
1454
|
-
globalRegistry.register(ScraperError);
|
|
1455
|
-
|
|
1456
|
-
// ✅ GOOD: Pure exports, side effects only on explicit call
|
|
1457
|
-
export class ScraperError extends Error { /* ... */ }
|
|
1458
|
-
export function createRegistry(): ErrorRegistry {
|
|
1459
|
-
return new ErrorRegistry();
|
|
1460
|
-
}
|
|
1461
|
-
|
|
1462
|
-
// ✅ GOOD: Lazy initialization
|
|
1463
|
-
let _registry: ErrorRegistry | null = null;
|
|
1464
|
-
export function getRegistry(): ErrorRegistry {
|
|
1465
|
-
if (!_registry) {
|
|
1466
|
-
_registry = new ErrorRegistry();
|
|
1467
|
-
}
|
|
1468
|
-
return _registry;
|
|
1469
|
-
}
|
|
1470
|
-
```
|
|
1471
|
-
|
|
1472
|
-
### tsconfig.json for ESM
|
|
1473
|
-
|
|
1474
|
-
```json
|
|
1475
|
-
{
|
|
1476
|
-
"compilerOptions": {
|
|
1477
|
-
"target": "ES2022",
|
|
1478
|
-
"module": "NodeNext",
|
|
1479
|
-
"moduleResolution": "NodeNext",
|
|
1480
|
-
"declaration": true,
|
|
1481
|
-
"declarationMap": true,
|
|
1482
|
-
"sourceMap": true,
|
|
1483
|
-
"outDir": "./dist",
|
|
1484
|
-
"rootDir": "./src",
|
|
1485
|
-
"strict": true,
|
|
1486
|
-
"esModuleInterop": true,
|
|
1487
|
-
"skipLibCheck": true,
|
|
1488
|
-
"forceConsistentCasingInFileNames": true
|
|
1489
|
-
},
|
|
1490
|
-
"include": ["src/**/*"],
|
|
1491
|
-
"exclude": ["node_modules", "dist", "**/*.test.ts"]
|
|
1492
|
-
}
|
|
1493
|
-
```
|
|
1494
|
-
|
|
1495
|
-
### Bundle Analysis
|
|
1496
|
-
|
|
1497
|
-
```bash
|
|
1498
|
-
# Analyze bundle size
|
|
1499
|
-
npx @next/bundle-analyzer
|
|
1500
|
-
|
|
1501
|
-
# Or use source-map-explorer
|
|
1502
|
-
npx source-map-explorer dist/index.mjs
|
|
1503
|
-
|
|
1504
|
-
# Check what's included
|
|
1505
|
-
npx esbuild src/index.ts --bundle --analyze
|
|
1506
|
-
```
|
|
1507
|
-
|
|
1508
|
-
---
|
|
1509
|
-
|
|
1510
|
-
## Summary: Quick Reference
|
|
1511
|
-
|
|
1512
|
-
| Pattern | When to Use | Key Package |
|
|
1513
|
-
|---------|-------------|-------------|
|
|
1514
|
-
| Custom errors with `Object.setPrototypeOf` | Any TypeScript project | Built-in |
|
|
1515
|
-
| Error codes (enum) | Large projects needing error taxonomy | Built-in |
|
|
1516
|
-
| Crawlee error classes | Crawling frameworks | crawlee |
|
|
1517
|
-
| Error classifier | Retry logic | Custom |
|
|
1518
|
-
| Recovery suggestions | Self-healing systems | Custom |
|
|
1519
|
-
| TypedEmitter | Event-driven scrapers | emittery / eventemitter3 |
|
|
1520
|
-
| Lifecycle hooks | HTTP clients, scrapers | Custom / ky / ofetch |
|
|
1521
|
-
| Middleware pipelines | Request processing chains | Custom |
|
|
1522
|
-
| HookRegistry | Plugin systems | Custom |
|
|
1523
|
-
| Tree-shaking | Production bundles | tsconfig + package.json |
|