@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,1417 +0,0 @@
|
|
|
1
|
-
# Pesquisa: Web Crawling Patterns (2026)
|
|
2
|
-
|
|
3
|
-
## Table of Contents
|
|
4
|
-
|
|
5
|
-
- [Visão Geral](#visão-geral)
|
|
6
|
-
- [BFS vs DFS Crawling](#bfs-vs-dfs-crawling)
|
|
7
|
-
- [robots.txt Parsing](#robotstxt-parsing)
|
|
8
|
-
- [Sitemap Parsing](#sitemap-parsing)
|
|
9
|
-
- [URL Frontier com Prioridade](#url-frontier-com-prioridade)
|
|
10
|
-
- [URL Normalization](#url-normalization)
|
|
11
|
-
- [Depth-Limited Crawling](#depth-limited-crawling)
|
|
12
|
-
- [Domain-Specific Crawl Rules](#domain-specific-crawl-rules)
|
|
13
|
-
- [Canonical URLs e Redirect Handling](#canonical-urls-e-redirect-handling)
|
|
14
|
-
- [Código Completo](#código-completo)
|
|
15
|
-
|
|
16
|
-
---
|
|
17
|
-
|
|
18
|
-
## Visão Geral
|
|
19
|
-
|
|
20
|
-
Web crawling patterns define como um crawler descobre, prioriza e acessa URLs em um site. A escolha entre BFS (Breadth-First Search) e DFS (Depth-First Search), combined com normalização de URLs, respeito a robots.txt, e limitação de profundidade, determina a eficiência e robustez do crawler.
|
|
21
|
-
|
|
22
|
-
### Bibliotecas Principais (2026)
|
|
23
|
-
|
|
24
|
-
| Biblioteca | Versão | Uso |
|
|
25
|
-
|---|---|---|
|
|
26
|
-
| `crawlee` | 3.x | Framework completo de crawling |
|
|
27
|
-
| `robots-parser` | 3.0.1 | Parsing de robots.txt (RFC 9309) |
|
|
28
|
-
| `sitemapper` | 4.1.6 | Parsing de sitemaps XML |
|
|
29
|
-
| `p-queue` | 9.3.0 | Fila com prioridade |
|
|
30
|
-
| `normalize-url` | 9.0.0 | Normalização de URLs |
|
|
31
|
-
| `ufo` | latest | Normalização leve de URLs |
|
|
32
|
-
|
|
33
|
-
---
|
|
34
|
-
|
|
35
|
-
## BFS vs DFS Crawling
|
|
36
|
-
|
|
37
|
-
### Breadth-First Search (BFS)
|
|
38
|
-
|
|
39
|
-
BFS visita todas as URLs de um nível antes de descer para o próximo nível. É o padrão em Crawlee porque garante cobertura uniforme e evita ficar preso em sub-árvores profundas.
|
|
40
|
-
|
|
41
|
-
```
|
|
42
|
-
Level 0: [https://example.com]
|
|
43
|
-
│
|
|
44
|
-
Level 1: [A] [B] [C]
|
|
45
|
-
│ │
|
|
46
|
-
Level 2: [D] [E] [F] [G]
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
**Por que BFS é o padrão:**
|
|
50
|
-
- Cobertura uniforme de todas as seções do site
|
|
51
|
-
- Menor risco de ficar preso em páginas infinitas
|
|
52
|
-
- Distribuição mais equilibrada de carga
|
|
53
|
-
- Mais previsível para controle de taxa
|
|
54
|
-
|
|
55
|
-
### Depth-First Search (DFS)
|
|
56
|
-
|
|
57
|
-
DFS explora um caminho completo antes de voltar. Útil para crawl de árvores de navegação, categorias, ou quando se quer explorar um sub-site completamente.
|
|
58
|
-
|
|
59
|
-
```
|
|
60
|
-
Start → [A] → [D] → [H] → backtrack → [E] → backtrack → [B] → [F] → ...
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
**Quando usar DFS:**
|
|
64
|
-
- Crawl de documentação hierárquica
|
|
65
|
-
- Exploração de categorias/subcategorias
|
|
66
|
-
- Quando a prioridade é profundidade sobre amplitude
|
|
67
|
-
|
|
68
|
-
### Crawlee: RequestQueue
|
|
69
|
-
|
|
70
|
-
O `RequestQueue` do Crawlee é a fila persistente que gerencia URLs. Por padrão, opera em BFS. A ordem de inserção determina a ordem de processamento.
|
|
71
|
-
|
|
72
|
-
```ts
|
|
73
|
-
import { RequestQueue, PlaywrightCrawler } from 'crawlee';
|
|
74
|
-
|
|
75
|
-
const requestQueue = await RequestQueue.open('my-queue');
|
|
76
|
-
|
|
77
|
-
// Inserir URLs — ordem de inserção = ordem de processamento (BFS)
|
|
78
|
-
await requestQueue.addRequest({ url: 'https://example.com' }); // Nível 0
|
|
79
|
-
await requestQueue.addRequest({ url: 'https://example.com/a' }); // Nível 1
|
|
80
|
-
await requestQueue.addRequest({ url: 'https://example.com/b' }); // Nível 1
|
|
81
|
-
await requestQueue.addRequest({ url: 'https://example.com/a/d' }); // Nível 2
|
|
82
|
-
await requestQueue.addRequest({ url: 'https://example.com/a/e' }); // Nível 2
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
### Crawlee: enqueueLinks
|
|
86
|
-
|
|
87
|
-
`enqueueLinks()` extrai e enfileira links da página atual. Por padrão usa BFS (insere no final da fila).
|
|
88
|
-
|
|
89
|
-
```ts
|
|
90
|
-
import { PlaywrightCrawler } from 'crawlee';
|
|
91
|
-
|
|
92
|
-
const crawler = new PlaywrightCrawler({
|
|
93
|
-
requestQueue,
|
|
94
|
-
async requestHandler({ page, enqueueLinks }) {
|
|
95
|
-
// BFS padrão — links vão para o FINAL da fila
|
|
96
|
-
await enqueueLinks();
|
|
97
|
-
},
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
await crawler.run(['https://example.com']);
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
### DFS com forefront: true
|
|
104
|
-
|
|
105
|
-
Para DFS, use `forefront: true` na `enqueueLinks()`. Isso insere os novos links no INÍCIO da fila, fazendo o crawler explorar profundidade antes de largura.
|
|
106
|
-
|
|
107
|
-
```ts
|
|
108
|
-
const crawler = new PlaywrightCrawler({
|
|
109
|
-
requestQueue,
|
|
110
|
-
async requestHandler({ page, enqueueLinks }) {
|
|
111
|
-
// DFS — links vão para o INÍCIO da fila
|
|
112
|
-
await enqueueLinks({ forefront: true });
|
|
113
|
-
},
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
await crawler.run(['https://example.com']);
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
### Comparação BFS vs DFS
|
|
120
|
-
|
|
121
|
-
| Característica | BFS | DFS |
|
|
122
|
-
|---|---|---|
|
|
123
|
-
| Cobertura | Uniforme, amplo | Profundo, focado |
|
|
124
|
-
| Risco de loop | Baixo | Alto |
|
|
125
|
-
| Uso de memória | Médio (muitas URLs pendentes) | Baixo (poucas URLs pendentes) |
|
|
126
|
-
| Velocidade de descoberta | Lenta (explora tudo) | Rápida (explora fundo) |
|
|
127
|
-
| Uso ideal | Sites grandes, e-commerce | Documentação, árvores |
|
|
128
|
-
| Crawlee padrão | `enqueueLinks()` | `enqueueLinks({ forefront: true })` |
|
|
129
|
-
|
|
130
|
-
---
|
|
131
|
-
|
|
132
|
-
## robots.txt Parsing
|
|
133
|
-
|
|
134
|
-
### robots-parser 3.0.1
|
|
135
|
-
|
|
136
|
-
`robots-parser` é a biblioteca mais completa para parsing de robots.txt. Versão 3.0.1 é compatível com RFC 9309 (a versão atual do padrão).
|
|
137
|
-
|
|
138
|
-
**Características RFC 9309:**
|
|
139
|
-
- Suporte a `User-agent`, `Allow`, `Disallow`, `Crawl-delay`, `Sitemap`
|
|
140
|
-
- Suporte a `*` como curinga no User-agent
|
|
141
|
-
- Suporte a `$` como curinga no final de Allow/Disallow
|
|
142
|
-
- Suporte a linhas de comment (`#`)
|
|
143
|
-
|
|
144
|
-
```ts
|
|
145
|
-
import robotsParser from 'robots-parser';
|
|
146
|
-
|
|
147
|
-
const robotsTxt = `
|
|
148
|
-
User-agent: *
|
|
149
|
-
Disallow: /admin/
|
|
150
|
-
Disallow: /private/
|
|
151
|
-
Allow: /admin/dashboard
|
|
152
|
-
Crawl-delay: 2
|
|
153
|
-
Sitemap: https://example.com/sitemap.xml
|
|
154
|
-
|
|
155
|
-
User-agent: Googlebot
|
|
156
|
-
Allow: /
|
|
157
|
-
`;
|
|
158
|
-
|
|
159
|
-
const robots = robotsParser('https://example.com/robots.txt', robotsTxt);
|
|
160
|
-
|
|
161
|
-
// Verificar se um URL é permitido
|
|
162
|
-
const isAllowed = robots.isAllowed('https://example.com/page');
|
|
163
|
-
// → true
|
|
164
|
-
|
|
165
|
-
const isDisallowed = robots.isAllowed('https://example.com/admin/settings');
|
|
166
|
-
// → false
|
|
167
|
-
|
|
168
|
-
// Obter crawl-delay
|
|
169
|
-
const delay = robots.getCrawlDelay('Googlebot');
|
|
170
|
-
// → undefined (não definido para Googlebot)
|
|
171
|
-
|
|
172
|
-
// Obter Sitemap
|
|
173
|
-
const sitemaps = robots.getSitemaps();
|
|
174
|
-
// → ['https://example.com/sitemap.xml']
|
|
175
|
-
|
|
176
|
-
// Obter Allowed/Disallow para um user-agent específico
|
|
177
|
-
const rules = robots.getDisallowedPaths('*');
|
|
178
|
-
// → ['/admin/', '/private/']
|
|
179
|
-
```
|
|
180
|
-
|
|
181
|
-
### Crawlee: RobotsTxtFile
|
|
182
|
-
|
|
183
|
-
Crawlee tem suporte built-in a robots.txt via `RobotsTxtFile`. Ele é integrado automaticamente ao crawler, respeitando regras.
|
|
184
|
-
|
|
185
|
-
```ts
|
|
186
|
-
import { RobotsTxtFile } from 'crawlee';
|
|
187
|
-
|
|
188
|
-
// Crawlee respeita robots.txt automaticamente
|
|
189
|
-
const crawler = new PlaywrightCrawler({
|
|
190
|
-
// robots.txt é checado antes de cada request
|
|
191
|
-
// URLs bloqueadas são ignoradas silenciosamente
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
// Forçar desabilitação (não recomendado)
|
|
195
|
-
const crawler = new PlaywrightCrawler({
|
|
196
|
-
// NOTA: Não há opção direta para desabilitar robots.txt check
|
|
197
|
-
// Robots.txt é sempre respeitado pelo Crawlee
|
|
198
|
-
});
|
|
199
|
-
```
|
|
200
|
-
|
|
201
|
-
### Parsing Manual com robots-parser
|
|
202
|
-
|
|
203
|
-
Para controle total, use `robots-parser` diretamente:
|
|
204
|
-
|
|
205
|
-
```ts
|
|
206
|
-
import robotsParser from 'robots-parser';
|
|
207
|
-
import { gotScraping } from 'got-scraping';
|
|
208
|
-
|
|
209
|
-
async function checkRobotsTxt(url: string, userAgent = '*'): Promise<{
|
|
210
|
-
allowed: boolean;
|
|
211
|
-
crawlDelay: number | null;
|
|
212
|
-
sitemaps: string[];
|
|
213
|
-
}> {
|
|
214
|
-
const parsedUrl = new URL(url);
|
|
215
|
-
const robotsUrl = `${parsedUrl.origin}/robots.txt`;
|
|
216
|
-
|
|
217
|
-
const response = await gotScraping(robotsUrl);
|
|
218
|
-
const robots = robotsParser(robotsUrl, response.body);
|
|
219
|
-
|
|
220
|
-
return {
|
|
221
|
-
allowed: robots.isAllowed(url, userAgent) ?? true,
|
|
222
|
-
crawlDelay: robots.getCrawlDelay(userAgent) ?? null,
|
|
223
|
-
sitemaps: robots.getSitemaps() ?? [],
|
|
224
|
-
};
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
// Usar
|
|
228
|
-
const result = await checkRobotsTxt('https://example.com/page');
|
|
229
|
-
if (!result.allowed) {
|
|
230
|
-
console.log('URL bloqueada por robots.txt');
|
|
231
|
-
if (result.crawlDelay) {
|
|
232
|
-
console.log(`Aguardar ${result.crawlDelay} segundos`);
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
```
|
|
236
|
-
|
|
237
|
-
### Rate Limiting Baseado em Crawl-Delay
|
|
238
|
-
|
|
239
|
-
```ts
|
|
240
|
-
function getCrawlDelayMs(robots: ReturnType<typeof robotsParser>, userAgent: string): number {
|
|
241
|
-
const delay = robots.getCrawlDelay(userAgent);
|
|
242
|
-
if (delay === null || delay === undefined) return 1000; // Default 1s
|
|
243
|
-
return delay * 1000;
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
// Usar no crawler
|
|
247
|
-
const crawler = new PlaywrightCrawler({
|
|
248
|
-
async requestHandler({ page, enqueueLinks }) {
|
|
249
|
-
const robots = robotsParser('https://example.com/robots.txt', robotsContent);
|
|
250
|
-
const delayMs = getCrawlDelayMs(robots, '*');
|
|
251
|
-
|
|
252
|
-
// Aplicar delay entre requests
|
|
253
|
-
await new Promise(resolve => setTimeout(resolve, delayMs));
|
|
254
|
-
|
|
255
|
-
// ... processar página
|
|
256
|
-
},
|
|
257
|
-
});
|
|
258
|
-
```
|
|
259
|
-
|
|
260
|
-
---
|
|
261
|
-
|
|
262
|
-
## Sitemap Parsing
|
|
263
|
-
|
|
264
|
-
### sitemapper 4.1.6
|
|
265
|
-
|
|
266
|
-
`sitemapper` é a biblioteca mais rápida para parsing de sitemaps XML. Suporta sitemaps com milhões de URLs.
|
|
267
|
-
|
|
268
|
-
**Características:**
|
|
269
|
-
- Parsing streaming (não carrega tudo na memória)
|
|
270
|
-
- Suporte a sitemap index (sitemaps que referenciam outros sitemaps)
|
|
271
|
-
- Tratamento de erros robusto
|
|
272
|
-
|
|
273
|
-
```ts
|
|
274
|
-
import { Sitemapper } from 'sitemapper';
|
|
275
|
-
|
|
276
|
-
const sitemapper = new Sitemapper({
|
|
277
|
-
timeout: 15000,
|
|
278
|
-
url: 'https://example.com/sitemap.xml',
|
|
279
|
-
});
|
|
280
|
-
|
|
281
|
-
const { sites } = await sitemapper.fetch();
|
|
282
|
-
console.log(sites);
|
|
283
|
-
// → ['https://example.com/', 'https://example.com/page1', ...]
|
|
284
|
-
```
|
|
285
|
-
|
|
286
|
-
### Crawlee RobotsTxtFile.parseUrlsFromSitemaps()
|
|
287
|
-
|
|
288
|
-
Crawlee pode extrair URLs de sitemaps referenciados no robots.txt:
|
|
289
|
-
|
|
290
|
-
```ts
|
|
291
|
-
import { RobotsTxtFile } from 'crawlee';
|
|
292
|
-
|
|
293
|
-
const robotsContent = `
|
|
294
|
-
User-agent: *
|
|
295
|
-
Disallow: /admin/
|
|
296
|
-
Sitemap: https://example.com/sitemap.xml
|
|
297
|
-
Sitemap: https://example.com/sitemap-products.xml
|
|
298
|
-
`;
|
|
299
|
-
|
|
300
|
-
const robots = RobotsTxtFile.parse(robotsContent);
|
|
301
|
-
const sitemapUrls = robots.getSitemaps();
|
|
302
|
-
// → ['https://example.com/sitemap.xml', 'https://example.com/sitemap-products.xml']
|
|
303
|
-
|
|
304
|
-
// Para cada sitemap, usar sitemapper para extrair URLs
|
|
305
|
-
import { Sitemapper } from 'sitemapper';
|
|
306
|
-
|
|
307
|
-
const allUrls: string[] = [];
|
|
308
|
-
for (const sitemapUrl of sitemapUrls) {
|
|
309
|
-
const sitemapper = new Sitemapper({ url: sitemapUrl });
|
|
310
|
-
const { sites } = await sitemapper.fetch();
|
|
311
|
-
allUrls.push(...sites);
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
console.log(`Encontradas ${allUrls.length} URLs em sitemaps`);
|
|
315
|
-
```
|
|
316
|
-
|
|
317
|
-
### Parsing Combinado: robots.txt + sitemaps
|
|
318
|
-
|
|
319
|
-
```ts
|
|
320
|
-
import robotsParser from 'robots-parser';
|
|
321
|
-
import { Sitemapper } from 'sitemapper';
|
|
322
|
-
import { gotScraping } from 'got-scraping';
|
|
323
|
-
|
|
324
|
-
async function discoverUrlsFromSitemaps(
|
|
325
|
-
baseUrl: string,
|
|
326
|
-
): Promise<{ urls: string[]; robots: ReturnType<typeof robotsParser> }> {
|
|
327
|
-
const parsedUrl = new URL(baseUrl);
|
|
328
|
-
const robotsUrl = `${parsedUrl.origin}/robots.txt`;
|
|
329
|
-
|
|
330
|
-
// 1. Buscar e parsear robots.txt
|
|
331
|
-
const robotsResponse = await gotScraping(robotsUrl);
|
|
332
|
-
const robots = robotsParser(robotsUrl, robotsResponse.body);
|
|
333
|
-
|
|
334
|
-
// 2. Extrair URLs de sitemaps
|
|
335
|
-
const sitemapUrls = robots.getSitemaps() ?? [];
|
|
336
|
-
const discoveredUrls: string[] = [];
|
|
337
|
-
|
|
338
|
-
for (const sitemapUrl of sitemapUrls) {
|
|
339
|
-
try {
|
|
340
|
-
const sitemapper = new Sitemapper({
|
|
341
|
-
url: sitemapUrl,
|
|
342
|
-
timeout: 30000,
|
|
343
|
-
});
|
|
344
|
-
const { sites } = await sitemapper.fetch();
|
|
345
|
-
discoveredUrls.push(...sites);
|
|
346
|
-
} catch (error) {
|
|
347
|
-
console.error(`Erro ao parse sitemap ${sitemapUrl}:`, error);
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
// 3. Filtrar por robots.txt
|
|
352
|
-
const allowedUrls = discoveredUrls.filter(url => robots.isAllowed(url) ?? true);
|
|
353
|
-
|
|
354
|
-
return { urls: allowedUrls, robots };
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
const { urls } = await discoverUrlsFromSitemaps('https://example.com');
|
|
358
|
-
console.log(`${urls.length} URLs permitidas por robots.txt`);
|
|
359
|
-
```
|
|
360
|
-
|
|
361
|
-
### Sitemap Index com Muitas URLs
|
|
362
|
-
|
|
363
|
-
```ts
|
|
364
|
-
// Para sitemaps muito grandes, usar streaming
|
|
365
|
-
import { Sitemapper } from 'sitemapper';
|
|
366
|
-
|
|
367
|
-
const sitemapper = new Sitemapper({
|
|
368
|
-
url: 'https://example.com/sitemap.xml',
|
|
369
|
-
timeout: 60000, // 60s para sitemaps grandes
|
|
370
|
-
});
|
|
371
|
-
|
|
372
|
-
// Processar em lotes para memória
|
|
373
|
-
const { sites } = await sitemapper.fetch();
|
|
374
|
-
const batchSize = 1000;
|
|
375
|
-
|
|
376
|
-
for (let i = 0; i < sites.length; i += batchSize) {
|
|
377
|
-
const batch = sites.slice(i, i + batchSize);
|
|
378
|
-
// Processar batch
|
|
379
|
-
console.log(`Processando batch ${Math.floor(i / batchSize) + 1}: ${batch.length} URLs`);
|
|
380
|
-
}
|
|
381
|
-
```
|
|
382
|
-
|
|
383
|
-
---
|
|
384
|
-
|
|
385
|
-
## URL Frontier com Prioridade
|
|
386
|
-
|
|
387
|
-
### p-queue 9.3.0
|
|
388
|
-
|
|
389
|
-
`p-queue` é a fila com prioridade mais popular no Node.js. Permite controle fino de concorrência e prioridade.
|
|
390
|
-
|
|
391
|
-
**Características:**
|
|
392
|
-
- Prioridade 1-10 (1 = mais alta, 10 = mais baixa)
|
|
393
|
-
- Controle de concorrência (limite de workers)
|
|
394
|
-
- Intervalos entre requests (rate limiting)
|
|
395
|
-
- Timeout por item
|
|
396
|
-
- Persistência (pode ser combinada com storage)
|
|
397
|
-
|
|
398
|
-
```ts
|
|
399
|
-
import PQueue from 'p-queue';
|
|
400
|
-
|
|
401
|
-
const queue = new PQueue({
|
|
402
|
-
concurrency: 5, // 5 requests simultâneos
|
|
403
|
-
intervalCap: 10, // Máximo 10 requests por intervalo
|
|
404
|
-
interval: 1000, // 1 segundo entre intervalos
|
|
405
|
-
timeout: 30000, // Timeout de 30s por request
|
|
406
|
-
});
|
|
407
|
-
|
|
408
|
-
// Prioridade: 1 = mais alta, 10 = mais baixa
|
|
409
|
-
queue.add(() => fetch('https://example.com'), { priority: 1 }); // Alta
|
|
410
|
-
queue.add(() => fetch('https://example.com/a'), { priority: 5 }); // Média
|
|
411
|
-
queue.add(() => fetch('https://example.com/b'), { priority: 10 }); // Baixa
|
|
412
|
-
```
|
|
413
|
-
|
|
414
|
-
### microfrontier
|
|
415
|
-
|
|
416
|
-
`microfrontier` é uma alternativa leve para frontiers de URL. Oferece persistência em disco e controle de prioridade.
|
|
417
|
-
|
|
418
|
-
```ts
|
|
419
|
-
import { Microfrontier } from 'microfrontier';
|
|
420
|
-
|
|
421
|
-
const frontier = new Microfrontier({
|
|
422
|
-
storage: './frontier-storage',
|
|
423
|
-
maxConcurrent: 5,
|
|
424
|
-
});
|
|
425
|
-
|
|
426
|
-
// Adicionar URLs com prioridade
|
|
427
|
-
frontier.push({ url: 'https://example.com', priority: 1 });
|
|
428
|
-
frontier.push({ url: 'https://example.com/a', priority: 5 });
|
|
429
|
-
|
|
430
|
-
// Pegar próxima URL
|
|
431
|
-
const next = frontier.shift();
|
|
432
|
-
```
|
|
433
|
-
|
|
434
|
-
### Frontier com p-queue para Crawlee
|
|
435
|
-
|
|
436
|
-
```ts
|
|
437
|
-
import PQueue from 'p-queue';
|
|
438
|
-
import { PlaywrightCrawler } from 'crawlee';
|
|
439
|
-
|
|
440
|
-
const queue = new PQueue({ concurrency: 3, intervalCap: 5, interval: 2000 });
|
|
441
|
-
|
|
442
|
-
const crawler = new PlaywrightCrawler({
|
|
443
|
-
maxConcurrency: 3,
|
|
444
|
-
requestHandlerTimeoutSecs: 30,
|
|
445
|
-
async requestHandler({ page, enqueueLinks }) {
|
|
446
|
-
// Extrair e classificar links
|
|
447
|
-
const links = await page.$$eval('a[href]', anchors =>
|
|
448
|
-
anchors.map(a => ({
|
|
449
|
-
url: a.href,
|
|
450
|
-
priority: a.closest('nav') ? 1 : // Navegação = alta
|
|
451
|
-
a.closest('article') ? 3 : // Artigo = média
|
|
452
|
-
a.closest('footer') ? 8 : // Footer = baixa
|
|
453
|
-
5 // Default
|
|
454
|
-
}))
|
|
455
|
-
);
|
|
456
|
-
|
|
457
|
-
// Adicionar com prioridade
|
|
458
|
-
await queue.addAll(
|
|
459
|
-
links.map(link => async () => {
|
|
460
|
-
await page.goto(link.url);
|
|
461
|
-
// Processar...
|
|
462
|
-
}),
|
|
463
|
-
{ priority: link.priority }
|
|
464
|
-
);
|
|
465
|
-
},
|
|
466
|
-
});
|
|
467
|
-
```
|
|
468
|
-
|
|
469
|
-
### Rate Limiting por Domínio
|
|
470
|
-
|
|
471
|
-
```ts
|
|
472
|
-
import PQueue from 'p-queue';
|
|
473
|
-
|
|
474
|
-
// Filas separadas por domínio
|
|
475
|
-
const domainQueues = new Map<string, PQueue>();
|
|
476
|
-
|
|
477
|
-
function getQueueForDomain(domain: string): PQueue {
|
|
478
|
-
if (!domainQueues.has(domain)) {
|
|
479
|
-
domainQueues.set(domain, new PQueue({
|
|
480
|
-
concurrency: 2,
|
|
481
|
-
intervalCap: 3,
|
|
482
|
-
interval: 5000, // 3 requests a cada 5 segundos por domínio
|
|
483
|
-
}));
|
|
484
|
-
}
|
|
485
|
-
return domainQueues.get(domain)!;
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
// Usar
|
|
489
|
-
const domain = new URL(url).hostname;
|
|
490
|
-
const queue = getQueueForDomain(domain);
|
|
491
|
-
await queue.add(() => fetchAndProcess(url));
|
|
492
|
-
```
|
|
493
|
-
|
|
494
|
-
---
|
|
495
|
-
|
|
496
|
-
## URL Normalization
|
|
497
|
-
|
|
498
|
-
### normalize-url 9.0.0
|
|
499
|
-
|
|
500
|
-
`normalize-url` é a biblioteca mais completa para normalização de URLs. Remove tracking params, sorting query strings, e muito mais.
|
|
501
|
-
|
|
502
|
-
**Características:**
|
|
503
|
-
- Remove `utm_*`, `fbclid`, `gclid` e outros tracking params
|
|
504
|
-
- Sorting de query string
|
|
505
|
-
- Conversão de protocolo
|
|
506
|
-
- Remoção de trailing slashes
|
|
507
|
-
- Normalização de host (lowercase)
|
|
508
|
-
|
|
509
|
-
```ts
|
|
510
|
-
import normalizeUrl from 'normalize-url';
|
|
511
|
-
|
|
512
|
-
// Remover tracking params
|
|
513
|
-
normalizeUrl('https://example.com/page?utm_source=google&utm_medium=cpc&id=123');
|
|
514
|
-
// → 'https://example.com/page?id=123'
|
|
515
|
-
|
|
516
|
-
// Sorting de query
|
|
517
|
-
normalizeUrl('https://example.com/page?z=1&a=2&m=3');
|
|
518
|
-
// → 'https://example.com/page?a=2&m=3&z=1'
|
|
519
|
-
|
|
520
|
-
// Normalização completa
|
|
521
|
-
normalizeUrl('https://Example.COM/Page/?id=123#section', {
|
|
522
|
-
sortQueryParams: true,
|
|
523
|
-
removeTrailingSlash: true,
|
|
524
|
-
removeExplicitPort: true,
|
|
525
|
-
stripWWW: true,
|
|
526
|
-
stripHash: true,
|
|
527
|
-
removeDuplicateSlash: true,
|
|
528
|
-
});
|
|
529
|
-
// → 'https://example.com/Page?id=123'
|
|
530
|
-
|
|
531
|
-
// Configuração customizada
|
|
532
|
-
normalizeUrl('https://example.com/page', {
|
|
533
|
-
protocols: ['https'],
|
|
534
|
-
forceHttp: false,
|
|
535
|
-
forceHttps: true,
|
|
536
|
-
stripAuthentication: true,
|
|
537
|
-
stripWWW: true,
|
|
538
|
-
removeTrailingSlash: true,
|
|
539
|
-
removeSingleDotSlash: true,
|
|
540
|
-
sortQueryParams: true,
|
|
541
|
-
removeDirectoryIndex: true,
|
|
542
|
-
});
|
|
543
|
-
```
|
|
544
|
-
|
|
545
|
-
### Tracking Parameters Comuns para Remoção
|
|
546
|
-
|
|
547
|
-
```ts
|
|
548
|
-
const TRACKING_PARAMS = new Set([
|
|
549
|
-
// Google Analytics
|
|
550
|
-
'utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',
|
|
551
|
-
'utm_id', 'utm_cid', 'utm_reader', 'utm_name', 'utm_social',
|
|
552
|
-
'utm_social-type', 'utm_internal',
|
|
553
|
-
// Google Ads
|
|
554
|
-
'gclid', 'gclsrc', 'dclid', 'gbraid', 'wbraid',
|
|
555
|
-
// Facebook
|
|
556
|
-
'fbclid', 'fb_action_ids', 'fb_action_types', 'fb_ref', 'fb_source',
|
|
557
|
-
// Microsoft
|
|
558
|
-
'msclkid', 'mc_cid', 'mc_eid',
|
|
559
|
-
// Twitter
|
|
560
|
-
'twclid',
|
|
561
|
-
// HubSpot
|
|
562
|
-
'hsa_cam', 'hsa_grp', 'hsa_mt', 'hsa_src', 'hsa_ad', 'hsa_acc',
|
|
563
|
-
'hsa_net', 'hsa_ver', 'hsa_la', 'hsa_ol', 'hsa_kw', 'hsa_target',
|
|
564
|
-
'hsa addTarget', 'hsa_src_campaign', 'hsa_src_ad',
|
|
565
|
-
// Outros
|
|
566
|
-
'ref', 'source', 'medium', 'campaign', 'content', 'term',
|
|
567
|
-
'spm', 'from', 'share_id',
|
|
568
|
-
]);
|
|
569
|
-
|
|
570
|
-
function normalizeForDedup(url: string): string {
|
|
571
|
-
const parsed = new URL(url);
|
|
572
|
-
|
|
573
|
-
// Remove tracking params
|
|
574
|
-
const params = [...parsed.searchParams.entries()]
|
|
575
|
-
.filter(([key]) => !TRACKING_PARAMS.has(key.toLowerCase()));
|
|
576
|
-
|
|
577
|
-
// Sort params
|
|
578
|
-
params.sort((a, b) => a[0].localeCompare(b[0]));
|
|
579
|
-
|
|
580
|
-
// Reconstruir URL
|
|
581
|
-
parsed.search = params.length > 0
|
|
582
|
-
? new URLSearchParams(params).toString()
|
|
583
|
-
: '';
|
|
584
|
-
|
|
585
|
-
// Normalizar
|
|
586
|
-
return normalizeUrl(parsed.toString(), {
|
|
587
|
-
sortQueryParams: true,
|
|
588
|
-
removeTrailingSlash: true,
|
|
589
|
-
stripWWW: true,
|
|
590
|
-
stripHash: true,
|
|
591
|
-
});
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
// Resultados
|
|
595
|
-
normalizeForDedup('https://example.com/page?utm_source=google&id=123');
|
|
596
|
-
// → 'https://example.com/page?id=123'
|
|
597
|
-
|
|
598
|
-
normalizeForDedup('https://www.example.com/page/?utm_campaign=test#section');
|
|
599
|
-
// → 'https://example.com/page'
|
|
600
|
-
```
|
|
601
|
-
|
|
602
|
-
### url-normalize (Alternativa)
|
|
603
|
-
|
|
604
|
-
```ts
|
|
605
|
-
import { normalize } from 'url-normalize';
|
|
606
|
-
|
|
607
|
-
const normalized = normalize('https://example.com/./page/../page?id=123');
|
|
608
|
-
// → 'https://example.com/page?id=123'
|
|
609
|
-
```
|
|
610
|
-
|
|
611
|
-
### ufo (Alternativa Leve)
|
|
612
|
-
|
|
613
|
-
```ts
|
|
614
|
-
import { normalizeURL, withQuery } from 'ufo';
|
|
615
|
-
|
|
616
|
-
const normalized = normalizeURL('https://example.com/page/?id=123');
|
|
617
|
-
// → 'https://example.com/page?id=123'
|
|
618
|
-
```
|
|
619
|
-
|
|
620
|
-
### Deduplicação de URLs Normalizadas
|
|
621
|
-
|
|
622
|
-
```ts
|
|
623
|
-
import normalizeUrl from 'normalize-url';
|
|
624
|
-
|
|
625
|
-
class UrlDeduplicator {
|
|
626
|
-
private seen = new Set<string>();
|
|
627
|
-
|
|
628
|
-
isUnique(url: string): boolean {
|
|
629
|
-
const normalized = normalizeUrl(url, {
|
|
630
|
-
sortQueryParams: true,
|
|
631
|
-
removeTrailingSlash: true,
|
|
632
|
-
stripWWW: true,
|
|
633
|
-
stripHash: true,
|
|
634
|
-
});
|
|
635
|
-
|
|
636
|
-
if (this.seen.has(normalized)) {
|
|
637
|
-
return false;
|
|
638
|
-
}
|
|
639
|
-
|
|
640
|
-
this.seen.add(normalized);
|
|
641
|
-
return true;
|
|
642
|
-
}
|
|
643
|
-
|
|
644
|
-
get size(): number {
|
|
645
|
-
return this.seen.size;
|
|
646
|
-
}
|
|
647
|
-
|
|
648
|
-
clear(): void {
|
|
649
|
-
this.seen.clear();
|
|
650
|
-
}
|
|
651
|
-
}
|
|
652
|
-
|
|
653
|
-
// Usar no crawler
|
|
654
|
-
const dedup = new UrlDeduplicator();
|
|
655
|
-
const urls = [
|
|
656
|
-
'https://example.com/page?utm_source=google',
|
|
657
|
-
'https://www.example.com/page/',
|
|
658
|
-
'https://example.com/page#section',
|
|
659
|
-
'https://example.com/page?id=123',
|
|
660
|
-
];
|
|
661
|
-
|
|
662
|
-
const unique = urls.filter(url => dedup.isUnique(url));
|
|
663
|
-
// → ['https://example.com/page?utm_source=google'] (primeira única)
|
|
664
|
-
```
|
|
665
|
-
|
|
666
|
-
---
|
|
667
|
-
|
|
668
|
-
## Depth-Limited Crawling
|
|
669
|
-
|
|
670
|
-
### Crawlee maxCrawlDepth
|
|
671
|
-
|
|
672
|
-
`maxCrawlDepth` controla a profundidade máxima do crawl. A página inicial tem profundidade 0, links dela têm profundidade 1, e assim por diante.
|
|
673
|
-
|
|
674
|
-
```ts
|
|
675
|
-
import { PlaywrightCrawler } from 'crawlee';
|
|
676
|
-
|
|
677
|
-
const crawler = new PlaywrightCrawler({
|
|
678
|
-
maxCrawlDepth: 3, // Máximo 3 níveis de profundidade
|
|
679
|
-
requestHandlerTimeoutSecs: 30,
|
|
680
|
-
async requestHandler({ page, enqueueLinks, request }) {
|
|
681
|
-
const depth = request.userData?.depth ?? 0;
|
|
682
|
-
console.log(`Processando: ${request.url} (profundidade: ${depth})`);
|
|
683
|
-
|
|
684
|
-
// Só enfileirar se não atingiu o limite
|
|
685
|
-
if (depth < 3) {
|
|
686
|
-
await enqueueLinks({
|
|
687
|
-
userData: { depth: depth + 1 },
|
|
688
|
-
});
|
|
689
|
-
}
|
|
690
|
-
},
|
|
691
|
-
});
|
|
692
|
-
|
|
693
|
-
await crawler.run(['https://example.com']);
|
|
694
|
-
```
|
|
695
|
-
|
|
696
|
-
### Controle de Profundidade Manual
|
|
697
|
-
|
|
698
|
-
```ts
|
|
699
|
-
import { PlaywrightCrawler } from 'crawlee';
|
|
700
|
-
|
|
701
|
-
interface CrawlState {
|
|
702
|
-
url: string;
|
|
703
|
-
depth: number;
|
|
704
|
-
parent: string | null;
|
|
705
|
-
}
|
|
706
|
-
|
|
707
|
-
const MAX_DEPTH = 3;
|
|
708
|
-
|
|
709
|
-
const crawler = new PlaywrightCrawler({
|
|
710
|
-
requestHandlerTimeoutSecs: 30,
|
|
711
|
-
async requestHandler({ page, enqueueLinks, request }) {
|
|
712
|
-
const state: CrawlState = request.userData ?? { url: request.url, depth: 0, parent: null };
|
|
713
|
-
|
|
714
|
-
console.log(`${' '.repeat(state.depth)}[${state.depth}] ${request.url}`);
|
|
715
|
-
|
|
716
|
-
// Processar página...
|
|
717
|
-
|
|
718
|
-
// Enfileirar com controle de profundidade
|
|
719
|
-
if (state.depth < MAX_DEPTH) {
|
|
720
|
-
const links = await page.$$eval('a[href]', anchors =>
|
|
721
|
-
anchors.map(a => a.href)
|
|
722
|
-
);
|
|
723
|
-
|
|
724
|
-
// Filtrar links internos
|
|
725
|
-
const internalLinks = links.filter(link => {
|
|
726
|
-
try {
|
|
727
|
-
return new URL(link).hostname === new URL(request.url).hostname;
|
|
728
|
-
} catch {
|
|
729
|
-
return false;
|
|
730
|
-
}
|
|
731
|
-
});
|
|
732
|
-
|
|
733
|
-
// Enfileirar com profundidade incrementada
|
|
734
|
-
await enqueueLinks({
|
|
735
|
-
userData: internalLinks.map(link => ({
|
|
736
|
-
url: link,
|
|
737
|
-
depth: state.depth + 1,
|
|
738
|
-
parent: request.url,
|
|
739
|
-
})),
|
|
740
|
-
});
|
|
741
|
-
}
|
|
742
|
-
},
|
|
743
|
-
});
|
|
744
|
-
|
|
745
|
-
await crawler.run(['https://example.com']);
|
|
746
|
-
```
|
|
747
|
-
|
|
748
|
-
### Profundidade por Seção
|
|
749
|
-
|
|
750
|
-
```ts
|
|
751
|
-
// Profundidade diferente por seção do site
|
|
752
|
-
function getMaxDepthForPath(pathname: string): number {
|
|
753
|
-
if (pathname.startsWith('/docs')) return 5; // Docs: mais profundo
|
|
754
|
-
if (pathname.startsWith('/blog')) return 3; // Blog: médio
|
|
755
|
-
if (pathname.startsWith('/products')) return 2; // Produtos: raso
|
|
756
|
-
return 4; // Default
|
|
757
|
-
}
|
|
758
|
-
|
|
759
|
-
const crawler = new PlaywrightCrawler({
|
|
760
|
-
async requestHandler({ page, enqueueLinks, request }) {
|
|
761
|
-
const url = new URL(request.url);
|
|
762
|
-
const maxDepth = getMaxDepthForPath(url.pathname);
|
|
763
|
-
const currentDepth = request.userData?.depth ?? 0;
|
|
764
|
-
|
|
765
|
-
if (currentDepth < maxDepth) {
|
|
766
|
-
await enqueueLinks({
|
|
767
|
-
userData: { depth: currentDepth + 1 },
|
|
768
|
-
});
|
|
769
|
-
}
|
|
770
|
-
},
|
|
771
|
-
});
|
|
772
|
-
```
|
|
773
|
-
|
|
774
|
-
---
|
|
775
|
-
|
|
776
|
-
## Domain-Specific Crawl Rules
|
|
777
|
-
|
|
778
|
-
### Crawlee EnqueueStrategy
|
|
779
|
-
|
|
780
|
-
Crawlee oferece estratégias de enfileiramento por domínio/origem:
|
|
781
|
-
|
|
782
|
-
```ts
|
|
783
|
-
import { PlaywrightCrawler, EnqueueStrategy } from 'crawlee';
|
|
784
|
-
|
|
785
|
-
const crawler = new PlaywrightCrawler({
|
|
786
|
-
async requestHandler({ page, enqueueLinks }) {
|
|
787
|
-
// SameHostname: mesmo host (subdomínios diferentes NÃO são incluídos)
|
|
788
|
-
await enqueueLinks({ strategy: EnqueueStrategy.SameHostname });
|
|
789
|
-
// Exemplo: example.com → example.com OK, sub.example.com NÃO
|
|
790
|
-
|
|
791
|
-
// SameDomain: mesmo domínio (subdomínios SÃO incluídos)
|
|
792
|
-
await enqueueLinks({ strategy: EnqueueStrategy.SameDomain });
|
|
793
|
-
// Exemplo: example.com → sub.example.com OK, other.com NÃO
|
|
794
|
-
|
|
795
|
-
// SameOrigin: mesma origem (host + porta)
|
|
796
|
-
await enqueueLinks({ strategy: EnqueueStrategy.SameOrigin });
|
|
797
|
-
// Exemplo: https://example.com:443 → https://example.com:443 OK
|
|
798
|
-
|
|
799
|
-
// All: todos os links (incluindo externos)
|
|
800
|
-
await enqueueLinks({ strategy: EnqueueStrategy.All });
|
|
801
|
-
// Exemplo: example.com → qualquer site
|
|
802
|
-
},
|
|
803
|
-
});
|
|
804
|
-
```
|
|
805
|
-
|
|
806
|
-
### Globs e Regexps
|
|
807
|
-
|
|
808
|
-
```ts
|
|
809
|
-
const crawler = new PlaywrightCrawler({
|
|
810
|
-
async requestHandler({ page, enqueueLinks }) {
|
|
811
|
-
// Glob patterns — incluir apenas URLs que correspondem
|
|
812
|
-
await enqueueLinks({
|
|
813
|
-
globs: [
|
|
814
|
-
'https://example.com/blog/**', // Blog e subpáginas
|
|
815
|
-
'https://example.com/docs/**/api/**', // Documentação API
|
|
816
|
-
],
|
|
817
|
-
});
|
|
818
|
-
|
|
819
|
-
// Regex patterns — incluir apenas URLs que correspondem
|
|
820
|
-
await enqueueLinks({
|
|
821
|
-
regexps: [
|
|
822
|
-
/https:\/\/example\.com\/products\/\d+/, // Páginas de produto
|
|
823
|
-
/https:\/\/example\.com\/categories\/\w+/, // Categorias
|
|
824
|
-
],
|
|
825
|
-
});
|
|
826
|
-
|
|
827
|
-
// Excluir URLs com patterns
|
|
828
|
-
await enqueueLinks({
|
|
829
|
-
exclude: [
|
|
830
|
-
'**/login', // Páginas de login
|
|
831
|
-
'**/signup', // Cadastro
|
|
832
|
-
'**/*.pdf', // PDFs
|
|
833
|
-
'**/admin/**', // Admin
|
|
834
|
-
'**/*?sort=*', // Ordenação
|
|
835
|
-
'**/*?page=*', // Paginação
|
|
836
|
-
],
|
|
837
|
-
});
|
|
838
|
-
},
|
|
839
|
-
});
|
|
840
|
-
```
|
|
841
|
-
|
|
842
|
-
### Regras Combinadas
|
|
843
|
-
|
|
844
|
-
```ts
|
|
845
|
-
const crawler = new PlaywrightCrawler({
|
|
846
|
-
async requestHandler({ page, enqueueLinks }) {
|
|
847
|
-
await enqueueLinks({
|
|
848
|
-
strategy: EnqueueStrategy.SameDomain,
|
|
849
|
-
globs: [
|
|
850
|
-
'https://example.com/**',
|
|
851
|
-
],
|
|
852
|
-
exclude: [
|
|
853
|
-
'**/login',
|
|
854
|
-
'**/signup',
|
|
855
|
-
'**/admin/**',
|
|
856
|
-
'**/*.pdf',
|
|
857
|
-
'**/*.zip',
|
|
858
|
-
],
|
|
859
|
-
regexps: [],
|
|
860
|
-
userData: { crawledBy: 'main-crawler' },
|
|
861
|
-
});
|
|
862
|
-
},
|
|
863
|
-
});
|
|
864
|
-
```
|
|
865
|
-
|
|
866
|
-
### Filtragem por Tipo de Link
|
|
867
|
-
|
|
868
|
-
```ts
|
|
869
|
-
function classifyLink(url: string, text: string): 'high' | 'medium' | 'low' | 'skip' {
|
|
870
|
-
const parsed = new URL(url);
|
|
871
|
-
|
|
872
|
-
// Pular links indesejados
|
|
873
|
-
if (parsed.pathname.match(/\.(pdf|zip|jpg|png|gif|mp4)$/i)) return 'skip';
|
|
874
|
-
if (parsed.pathname.includes('/login') || parsed.pathname.includes('/signup')) return 'skip';
|
|
875
|
-
if (parsed.search.includes('sort=') || parsed.search.includes('page=')) return 'skip';
|
|
876
|
-
|
|
877
|
-
// Links de alta prioridade
|
|
878
|
-
if (parsed.pathname.startsWith('/docs')) return 'high';
|
|
879
|
-
if (parsed.pathname.startsWith('/api')) return 'high';
|
|
880
|
-
if (parsed.pathname.startsWith('/blog')) return 'medium';
|
|
881
|
-
|
|
882
|
-
// Links de baixa prioridade
|
|
883
|
-
if (parsed.pathname.startsWith('/terms')) return 'low';
|
|
884
|
-
if (parsed.pathname.startsWith('/privacy')) return 'low';
|
|
885
|
-
|
|
886
|
-
return 'medium';
|
|
887
|
-
}
|
|
888
|
-
|
|
889
|
-
const crawler = new PlaywrightCrawler({
|
|
890
|
-
async requestHandler({ page, enqueueLinks }) {
|
|
891
|
-
const links = await page.$$eval('a[href]', anchors =>
|
|
892
|
-
anchors.map(a => ({ url: a.href, text: a.textContent?.trim() ?? '' }))
|
|
893
|
-
);
|
|
894
|
-
|
|
895
|
-
const priorityMap = { high: 1, medium: 5, low: 8, skip: 10 };
|
|
896
|
-
|
|
897
|
-
// Filtrar e classificar
|
|
898
|
-
const filtered = links
|
|
899
|
-
.map(link => ({
|
|
900
|
-
...link,
|
|
901
|
-
priority: classifyLink(link.url, link.text),
|
|
902
|
-
}))
|
|
903
|
-
.filter(link => link.priority !== 'skip');
|
|
904
|
-
|
|
905
|
-
// Enfileirar com prioridade
|
|
906
|
-
for (const link of filtered) {
|
|
907
|
-
await enqueueLinks({
|
|
908
|
-
userData: { priority: priorityMap[link.priority] },
|
|
909
|
-
});
|
|
910
|
-
}
|
|
911
|
-
},
|
|
912
|
-
});
|
|
913
|
-
```
|
|
914
|
-
|
|
915
|
-
---
|
|
916
|
-
|
|
917
|
-
## Canonical URLs e Redirect Handling
|
|
918
|
-
|
|
919
|
-
### Canonical URLs
|
|
920
|
-
|
|
921
|
-
Canonical URLs indicam a URL preferida para uma página. Crawlers devem usar a canonical em vez da URL original para evitar conteúdo duplicado.
|
|
922
|
-
|
|
923
|
-
```ts
|
|
924
|
-
import { PlaywrightCrawler } from 'crawlee';
|
|
925
|
-
|
|
926
|
-
const crawler = new PlaywrightCrawler({
|
|
927
|
-
async requestHandler({ page, enqueueLinks, request }) {
|
|
928
|
-
// Extrair canonical URL
|
|
929
|
-
const canonical = await page.$eval(
|
|
930
|
-
'link[rel="canonical"]',
|
|
931
|
-
el => el.getAttribute('href'),
|
|
932
|
-
).catch(() => null);
|
|
933
|
-
|
|
934
|
-
// Se canonical existe e é diferente, usar canonical
|
|
935
|
-
if (canonical && canonical !== request.url) {
|
|
936
|
-
const canonicalUrl = new URL(canonical, request.url).toString();
|
|
937
|
-
console.log(`Canonical: ${request.url} → ${canonicalUrl}`);
|
|
938
|
-
|
|
939
|
-
// Enfileirar canonical se ainda não foi visitado
|
|
940
|
-
await enqueueLinks({
|
|
941
|
-
globs: [canonicalUrl],
|
|
942
|
-
});
|
|
943
|
-
}
|
|
944
|
-
|
|
945
|
-
// Processar normalmente...
|
|
946
|
-
},
|
|
947
|
-
});
|
|
948
|
-
```
|
|
949
|
-
|
|
950
|
-
### Redirect Handling
|
|
951
|
-
|
|
952
|
-
```ts
|
|
953
|
-
import { PlaywrightCrawler } from 'crawlee';
|
|
954
|
-
|
|
955
|
-
const crawler = new PlaywrightCrawler({
|
|
956
|
-
async requestHandler({ page, enqueueLinks, request }) {
|
|
957
|
-
// Capturar redirects durante a navegação
|
|
958
|
-
const response = await page.goto(request.url);
|
|
959
|
-
|
|
960
|
-
if (response) {
|
|
961
|
-
const finalUrl = page.url();
|
|
962
|
-
const status = response.status();
|
|
963
|
-
|
|
964
|
-
// 301/302 redirects
|
|
965
|
-
if ([301, 302, 307, 308].includes(status)) {
|
|
966
|
-
console.log(`Redirect ${status}: ${request.url} → ${finalUrl}`);
|
|
967
|
-
}
|
|
968
|
-
|
|
969
|
-
// URL final pode ter mudado
|
|
970
|
-
if (finalUrl !== request.url) {
|
|
971
|
-
console.log(`Final URL: ${finalUrl}`);
|
|
972
|
-
}
|
|
973
|
-
}
|
|
974
|
-
|
|
975
|
-
// Extrair canonical
|
|
976
|
-
const canonical = await page.$eval(
|
|
977
|
-
'link[rel="canonical"]',
|
|
978
|
-
el => el.getAttribute('href'),
|
|
979
|
-
).catch(() => null);
|
|
980
|
-
|
|
981
|
-
// Usar canonical se disponível
|
|
982
|
-
const effectiveUrl = canonical
|
|
983
|
-
? new URL(canonical, page.url()).toString()
|
|
984
|
-
: page.url();
|
|
985
|
-
|
|
986
|
-
console.log(`Effective URL: ${effectiveUrl}`);
|
|
987
|
-
},
|
|
988
|
-
});
|
|
989
|
-
```
|
|
990
|
-
|
|
991
|
-
### Redirect Chain Tracking
|
|
992
|
-
|
|
993
|
-
```ts
|
|
994
|
-
import { PlaywrightCrawler } from 'crawlee';
|
|
995
|
-
|
|
996
|
-
interface RedirectChain {
|
|
997
|
-
original: string;
|
|
998
|
-
redirects: Array<{ url: string; status: number; timestamp: number }>;
|
|
999
|
-
final: string;
|
|
1000
|
-
}
|
|
1001
|
-
|
|
1002
|
-
const crawler = new PlaywrightCrawler({
|
|
1003
|
-
async requestHandler({ page, request }) {
|
|
1004
|
-
const chain: RedirectChain = {
|
|
1005
|
-
original: request.url,
|
|
1006
|
-
redirects: [],
|
|
1007
|
-
final: '',
|
|
1008
|
-
};
|
|
1009
|
-
|
|
1010
|
-
// Interceptar redirects
|
|
1011
|
-
page.on('response', response => {
|
|
1012
|
-
const status = response.status();
|
|
1013
|
-
if ([301, 302, 307, 308].includes(status)) {
|
|
1014
|
-
chain.redirects.push({
|
|
1015
|
-
url: response.url(),
|
|
1016
|
-
status,
|
|
1017
|
-
timestamp: Date.now(),
|
|
1018
|
-
});
|
|
1019
|
-
}
|
|
1020
|
-
});
|
|
1021
|
-
|
|
1022
|
-
await page.goto(request.url, { waitUntil: 'networkidle' });
|
|
1023
|
-
chain.final = page.url();
|
|
1024
|
-
|
|
1025
|
-
if (chain.redirects.length > 0) {
|
|
1026
|
-
console.log(`Redirect chain: ${chain.original}`);
|
|
1027
|
-
for (const redirect of chain.redirects) {
|
|
1028
|
-
console.log(` ${redirect.status} → ${redirect.url}`);
|
|
1029
|
-
}
|
|
1030
|
-
console.log(` Final: ${chain.final}`);
|
|
1031
|
-
}
|
|
1032
|
-
},
|
|
1033
|
-
});
|
|
1034
|
-
```
|
|
1035
|
-
|
|
1036
|
-
### Canonical + Redirect Combinado
|
|
1037
|
-
|
|
1038
|
-
```ts
|
|
1039
|
-
function resolveEffectiveUrl(pageUrl: string, canonicalHref: string | null): string {
|
|
1040
|
-
// 1. Se canonical existe, é a fonte de verdade
|
|
1041
|
-
if (canonicalHref) {
|
|
1042
|
-
try {
|
|
1043
|
-
return new URL(canonicalHref, pageUrl).toString();
|
|
1044
|
-
} catch {
|
|
1045
|
-
// Canonical inválido, usar pageUrl
|
|
1046
|
-
}
|
|
1047
|
-
}
|
|
1048
|
-
|
|
1049
|
-
// 2. Caso contrário, usar a URL da página
|
|
1050
|
-
return pageUrl;
|
|
1051
|
-
}
|
|
1052
|
-
|
|
1053
|
-
const crawler = new PlaywrightCrawler({
|
|
1054
|
-
async requestHandler({ page, request }) {
|
|
1055
|
-
const canonical = await page.$eval(
|
|
1056
|
-
'link[rel="canonical"]',
|
|
1057
|
-
el => el.getAttribute('href'),
|
|
1058
|
-
).catch(() => null);
|
|
1059
|
-
|
|
1060
|
-
const effectiveUrl = resolveEffectiveUrl(request.url, canonical);
|
|
1061
|
-
|
|
1062
|
-
// Verificar se já processamos esta canonical
|
|
1063
|
-
if (effectiveUrl !== request.url) {
|
|
1064
|
-
console.log(`Canonical mismatch: ${request.url} → ${effectiveUrl}`);
|
|
1065
|
-
}
|
|
1066
|
-
|
|
1067
|
-
// Processar com a URL efetiva
|
|
1068
|
-
},
|
|
1069
|
-
});
|
|
1070
|
-
```
|
|
1071
|
-
|
|
1072
|
-
---
|
|
1073
|
-
|
|
1074
|
-
## Código Completo
|
|
1075
|
-
|
|
1076
|
-
### Exemplo 1: Crawler BFS com robots.txt, Sitemap, e Profundidade
|
|
1077
|
-
|
|
1078
|
-
```ts
|
|
1079
|
-
import { PlaywrightCrawler, RobotsTxtFile } from 'crawlee';
|
|
1080
|
-
import robotsParser from 'robots-parser';
|
|
1081
|
-
import { Sitemapper } from 'sitemapper';
|
|
1082
|
-
import { gotScraping } from 'got-scraping';
|
|
1083
|
-
import normalizeUrl from 'normalize-url';
|
|
1084
|
-
|
|
1085
|
-
interface CrawlConfig {
|
|
1086
|
-
startUrls: string[];
|
|
1087
|
-
maxDepth: number;
|
|
1088
|
-
maxConcurrency: number;
|
|
1089
|
-
crawlDelay: number;
|
|
1090
|
-
userAgent: string;
|
|
1091
|
-
}
|
|
1092
|
-
|
|
1093
|
-
async function buildCrawler(config: CrawlConfig) {
|
|
1094
|
-
// 1. Descobrir URLs via sitemap
|
|
1095
|
-
const sitemapUrls: string[] = [];
|
|
1096
|
-
for (const startUrl of config.startUrls) {
|
|
1097
|
-
const parsedUrl = new URL(startUrl);
|
|
1098
|
-
|
|
1099
|
-
try {
|
|
1100
|
-
const robotsResponse = await gotScraping(`${parsedUrl.origin}/robots.txt`);
|
|
1101
|
-
const robots = robotsParser(`${parsedUrl.origin}/robots.txt`, robotsResponse.body);
|
|
1102
|
-
|
|
1103
|
-
const sitemapEndpoints = robots.getSitemaps() ?? [];
|
|
1104
|
-
for (const sitemapUrl of sitemapEndpoints) {
|
|
1105
|
-
const sitemapper = new Sitemapper({ url: sitemapUrl, timeout: 30000 });
|
|
1106
|
-
const { sites } = await sitemapper.fetch();
|
|
1107
|
-
sitemapUrls.push(...sites);
|
|
1108
|
-
}
|
|
1109
|
-
} catch (error) {
|
|
1110
|
-
console.error(`Erro ao descobrir sitemaps para ${parsedUrl.origin}:`, error);
|
|
1111
|
-
}
|
|
1112
|
-
}
|
|
1113
|
-
|
|
1114
|
-
console.log(`Descobertas ${sitemapUrls.length} URLs via sitemaps`);
|
|
1115
|
-
|
|
1116
|
-
// 2. Deduplicar URLs normalizadas
|
|
1117
|
-
const seen = new Set<string>();
|
|
1118
|
-
const uniqueUrls = sitemapUrls.filter(url => {
|
|
1119
|
-
const normalized = normalizeUrl(url, {
|
|
1120
|
-
sortQueryParams: true,
|
|
1121
|
-
stripWWW: true,
|
|
1122
|
-
stripHash: true,
|
|
1123
|
-
removeTrailingSlash: true,
|
|
1124
|
-
});
|
|
1125
|
-
if (seen.has(normalized)) return false;
|
|
1126
|
-
seen.add(normalized);
|
|
1127
|
-
return true;
|
|
1128
|
-
});
|
|
1129
|
-
|
|
1130
|
-
console.log(`${uniqueUrls.length} URLs únicas após normalização`);
|
|
1131
|
-
|
|
1132
|
-
// 3. Criar crawler BFS com controle de profundidade
|
|
1133
|
-
const crawler = new PlaywrightCrawler({
|
|
1134
|
-
maxConcurrency: config.maxConcurrency,
|
|
1135
|
-
requestHandlerTimeoutSecs: 30,
|
|
1136
|
-
async requestHandler({ page, enqueueLinks, request }) {
|
|
1137
|
-
const depth = request.userData?.depth ?? 0;
|
|
1138
|
-
const indent = ' '.repeat(depth);
|
|
1139
|
-
|
|
1140
|
-
console.log(`${indent}[Depth ${depth}] ${request.url}`);
|
|
1141
|
-
|
|
1142
|
-
// Extrair dados da página
|
|
1143
|
-
const title = await page.title();
|
|
1144
|
-
const content = await page.content();
|
|
1145
|
-
|
|
1146
|
-
// Processar dados...
|
|
1147
|
-
|
|
1148
|
-
// Enfileirar links se não atingiu limite de profundidade
|
|
1149
|
-
if (depth < config.maxDepth) {
|
|
1150
|
-
await enqueueLinks({
|
|
1151
|
-
strategy: 'same-domain',
|
|
1152
|
-
userData: { depth: depth + 1 },
|
|
1153
|
-
});
|
|
1154
|
-
}
|
|
1155
|
-
},
|
|
1156
|
-
});
|
|
1157
|
-
|
|
1158
|
-
// 4. Executar
|
|
1159
|
-
const seedUrls = uniqueUrls.slice(0, 100); // Limitar URLs iniciais
|
|
1160
|
-
await crawler.run(seedUrls);
|
|
1161
|
-
|
|
1162
|
-
return {
|
|
1163
|
-
totalProcessed: crawler.requestQueue?.handledCount() ?? 0,
|
|
1164
|
-
totalEnqueued: crawler.requestQueue?.pendingCount() ?? 0,
|
|
1165
|
-
};
|
|
1166
|
-
}
|
|
1167
|
-
|
|
1168
|
-
// Executar
|
|
1169
|
-
const result = await buildCrawler({
|
|
1170
|
-
startUrls: ['https://example.com'],
|
|
1171
|
-
maxDepth: 3,
|
|
1172
|
-
maxConcurrency: 3,
|
|
1173
|
-
crawlDelay: 2,
|
|
1174
|
-
userAgent: 'MyBot/1.0',
|
|
1175
|
-
});
|
|
1176
|
-
|
|
1177
|
-
console.log(`Crawl completo: ${result.totalProcessed} processadas, ${result.totalEnqueued} enfileiradas`);
|
|
1178
|
-
```
|
|
1179
|
-
|
|
1180
|
-
### Exemplo 2: Crawler DFS com Prioridade
|
|
1181
|
-
|
|
1182
|
-
```ts
|
|
1183
|
-
import { PlaywrightCrawler } from 'crawlee';
|
|
1184
|
-
import PQueue from 'p-queue';
|
|
1185
|
-
|
|
1186
|
-
const queue = new PQueue({ concurrency: 5, intervalCap: 10, interval: 1000 });
|
|
1187
|
-
|
|
1188
|
-
function classifyPriority(url: string): number {
|
|
1189
|
-
const parsed = new URL(url);
|
|
1190
|
-
if (parsed.pathname.startsWith('/docs')) return 1; // Alta
|
|
1191
|
-
if (parsed.pathname.startsWith('/api')) return 2; // Alta
|
|
1192
|
-
if (parsed.pathname.startsWith('/blog')) return 4; // Média
|
|
1193
|
-
if (parsed.pathname.startsWith('/products')) return 6; // Média-baixa
|
|
1194
|
-
return 8; // Baixa
|
|
1195
|
-
}
|
|
1196
|
-
|
|
1197
|
-
const crawler = new PlaywrightCrawler({
|
|
1198
|
-
requestQueue: await (await import('crawlee')).RequestQueue.open('dfs-queue'),
|
|
1199
|
-
async requestHandler({ page, enqueueLinks, request }) {
|
|
1200
|
-
const depth = request.userData?.depth ?? 0;
|
|
1201
|
-
|
|
1202
|
-
// DFS: forefront = true para ir para o início da fila
|
|
1203
|
-
await enqueueLinks({
|
|
1204
|
-
forefront: true, // DFS!
|
|
1205
|
-
userData: { depth: depth + 1 },
|
|
1206
|
-
});
|
|
1207
|
-
},
|
|
1208
|
-
});
|
|
1209
|
-
|
|
1210
|
-
// Iniciar com URLs seed
|
|
1211
|
-
await crawler.run([
|
|
1212
|
-
'https://example.com/docs',
|
|
1213
|
-
'https://example.com/api',
|
|
1214
|
-
]);
|
|
1215
|
-
```
|
|
1216
|
-
|
|
1217
|
-
### Exemplo 3: Rate Limiting por Domínio com p-queue
|
|
1218
|
-
|
|
1219
|
-
```ts
|
|
1220
|
-
import PQueue from 'p-queue';
|
|
1221
|
-
|
|
1222
|
-
const domainQueues = new Map<string, PQueue>();
|
|
1223
|
-
|
|
1224
|
-
function getDomainQueue(domain: string): PQueue {
|
|
1225
|
-
if (!domainQueues.has(domain)) {
|
|
1226
|
-
domainQueues.set(domain, new PQueue({
|
|
1227
|
-
concurrency: 2,
|
|
1228
|
-
intervalCap: 3,
|
|
1229
|
-
interval: 5000, // 3 requests a cada 5s por domínio
|
|
1230
|
-
}));
|
|
1231
|
-
}
|
|
1232
|
-
return domainQueues.get(domain)!;
|
|
1233
|
-
}
|
|
1234
|
-
|
|
1235
|
-
async function crawlWithRateLimit(urls: string[]): Promise<void> {
|
|
1236
|
-
const promises = urls.map(async (url) => {
|
|
1237
|
-
const domain = new URL(url).hostname;
|
|
1238
|
-
const queue = getDomainQueue(domain);
|
|
1239
|
-
|
|
1240
|
-
await queue.add(async () => {
|
|
1241
|
-
console.log(`Crawling: ${url}`);
|
|
1242
|
-
// Simular crawl
|
|
1243
|
-
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
1244
|
-
console.log(`Concluído: ${url}`);
|
|
1245
|
-
});
|
|
1246
|
-
});
|
|
1247
|
-
|
|
1248
|
-
await Promise.all(promises);
|
|
1249
|
-
}
|
|
1250
|
-
|
|
1251
|
-
await crawlWithRateLimit([
|
|
1252
|
-
'https://example.com/page1',
|
|
1253
|
-
'https://example.com/page2',
|
|
1254
|
-
'https://other.com/page1',
|
|
1255
|
-
'https://other.com/page2',
|
|
1256
|
-
]);
|
|
1257
|
-
```
|
|
1258
|
-
|
|
1259
|
-
### Exemplo 4: Crawler Completo com Todas as Funcionalidades
|
|
1260
|
-
|
|
1261
|
-
```ts
|
|
1262
|
-
import { PlaywrightCrawler, RequestQueue } from 'crawlee';
|
|
1263
|
-
import robotsParser from 'robots-parser';
|
|
1264
|
-
import { Sitemapper } from 'sitemapper';
|
|
1265
|
-
import { gotScraping } from 'got-scraping';
|
|
1266
|
-
import normalizeUrl from 'normalize-url';
|
|
1267
|
-
|
|
1268
|
-
interface FullCrawlConfig {
|
|
1269
|
-
startUrls: string[];
|
|
1270
|
-
maxDepth: number;
|
|
1271
|
-
maxConcurrency: number;
|
|
1272
|
-
crawlDelay: number;
|
|
1273
|
-
respectRobotsTxt: boolean;
|
|
1274
|
-
useSitemaps: boolean;
|
|
1275
|
-
}
|
|
1276
|
-
|
|
1277
|
-
class WebCrawler {
|
|
1278
|
-
private queue: RequestQueue;
|
|
1279
|
-
private robots: Map<string, ReturnType<typeof robotsParser>> = new Map();
|
|
1280
|
-
private seen: Set<string> = new Set();
|
|
1281
|
-
private config: FullCrawlConfig;
|
|
1282
|
-
|
|
1283
|
-
constructor(config: FullCrawlConfig) {
|
|
1284
|
-
this.config = config;
|
|
1285
|
-
}
|
|
1286
|
-
|
|
1287
|
-
async initialize(): Promise<void> {
|
|
1288
|
-
this.queue = await RequestQueue.open('full-crawl');
|
|
1289
|
-
|
|
1290
|
-
// Descobrir sitemaps se habilitado
|
|
1291
|
-
if (this.config.useSitemaps) {
|
|
1292
|
-
await this.discoverFromSitemaps();
|
|
1293
|
-
}
|
|
1294
|
-
}
|
|
1295
|
-
|
|
1296
|
-
private async discoverFromSitemaps(): Promise<void> {
|
|
1297
|
-
for (const url of this.config.startUrls) {
|
|
1298
|
-
const origin = new URL(url).origin;
|
|
1299
|
-
|
|
1300
|
-
try {
|
|
1301
|
-
// Buscar robots.txt
|
|
1302
|
-
const robotsResponse = await gotScraping(`${origin}/robots.txt`);
|
|
1303
|
-
const robots = robotsParser(`${origin}/robots.txt`, robotsResponse.body);
|
|
1304
|
-
this.robots.set(origin, robots);
|
|
1305
|
-
|
|
1306
|
-
// Extrair sitemaps
|
|
1307
|
-
const sitemapEndpoints = robots.getSitemaps() ?? [];
|
|
1308
|
-
for (const sitemapUrl of sitemapEndpoints) {
|
|
1309
|
-
const sitemapper = new Sitemapper({ url: sitemapUrl, timeout: 30000 });
|
|
1310
|
-
const { sites } = await sitemapper.fetch();
|
|
1311
|
-
|
|
1312
|
-
for (const siteUrl of sites) {
|
|
1313
|
-
const normalized = this.normalizeUrl(siteUrl);
|
|
1314
|
-
if (!this.seen.has(normalized) && this.isAllowed(siteUrl)) {
|
|
1315
|
-
this.seen.add(normalized);
|
|
1316
|
-
await this.queue.addRequest({
|
|
1317
|
-
url: siteUrl,
|
|
1318
|
-
userData: { depth: 0 },
|
|
1319
|
-
});
|
|
1320
|
-
}
|
|
1321
|
-
}
|
|
1322
|
-
}
|
|
1323
|
-
} catch (error) {
|
|
1324
|
-
console.error(`Erro ao processar ${origin}:`, error);
|
|
1325
|
-
}
|
|
1326
|
-
}
|
|
1327
|
-
}
|
|
1328
|
-
|
|
1329
|
-
private normalizeUrl(url: string): string {
|
|
1330
|
-
return normalizeUrl(url, {
|
|
1331
|
-
sortQueryParams: true,
|
|
1332
|
-
stripWWW: true,
|
|
1333
|
-
stripHash: true,
|
|
1334
|
-
removeTrailingSlash: true,
|
|
1335
|
-
});
|
|
1336
|
-
}
|
|
1337
|
-
|
|
1338
|
-
private isAllowed(url: string): boolean {
|
|
1339
|
-
if (!this.config.respectRobotsTxt) return true;
|
|
1340
|
-
|
|
1341
|
-
const origin = new URL(url).origin;
|
|
1342
|
-
const robots = this.robots.get(origin);
|
|
1343
|
-
if (!robots) return true;
|
|
1344
|
-
|
|
1345
|
-
return robots.isAllowed(url) ?? true;
|
|
1346
|
-
}
|
|
1347
|
-
|
|
1348
|
-
async crawl(): Promise<{ processed: number; enqueued: number }> {
|
|
1349
|
-
const crawler = new PlaywrightCrawler({
|
|
1350
|
-
maxConcurrency: this.config.maxConcurrency,
|
|
1351
|
-
requestHandlerTimeoutSecs: 30,
|
|
1352
|
-
requestQueue: this.queue,
|
|
1353
|
-
async requestHandler({ page, enqueueLinks, request }) {
|
|
1354
|
-
const depth = request.userData?.depth ?? 0;
|
|
1355
|
-
const indent = ' '.repeat(depth);
|
|
1356
|
-
|
|
1357
|
-
console.log(`${indent}[${depth}] ${request.url}`);
|
|
1358
|
-
|
|
1359
|
-
// Extrair dados...
|
|
1360
|
-
const title = await page.title();
|
|
1361
|
-
console.log(`${indent} Title: ${title}`);
|
|
1362
|
-
|
|
1363
|
-
// Enfileirar links
|
|
1364
|
-
if (depth < this.config.maxDepth) {
|
|
1365
|
-
const links = await page.$$eval('a[href]', anchors =>
|
|
1366
|
-
anchors.map(a => a.href)
|
|
1367
|
-
);
|
|
1368
|
-
|
|
1369
|
-
for (const link of links) {
|
|
1370
|
-
const normalized = this.normalizeUrl(link);
|
|
1371
|
-
if (!this.seen.has(normalized) && this.isAllowed(link)) {
|
|
1372
|
-
this.seen.add(normalized);
|
|
1373
|
-
await this.queue.addRequest({
|
|
1374
|
-
url: link,
|
|
1375
|
-
userData: { depth: depth + 1 },
|
|
1376
|
-
});
|
|
1377
|
-
}
|
|
1378
|
-
}
|
|
1379
|
-
}
|
|
1380
|
-
},
|
|
1381
|
-
});
|
|
1382
|
-
|
|
1383
|
-
await crawler.run(this.config.startUrls);
|
|
1384
|
-
|
|
1385
|
-
return {
|
|
1386
|
-
processed: this.queue.handledCount(),
|
|
1387
|
-
enqueued: this.queue.pendingCount(),
|
|
1388
|
-
};
|
|
1389
|
-
}
|
|
1390
|
-
}
|
|
1391
|
-
|
|
1392
|
-
// Executar crawler completo
|
|
1393
|
-
const crawler = new WebCrawler({
|
|
1394
|
-
startUrls: ['https://example.com'],
|
|
1395
|
-
maxDepth: 3,
|
|
1396
|
-
maxConcurrency: 3,
|
|
1397
|
-
crawlDelay: 2,
|
|
1398
|
-
respectRobotsTxt: true,
|
|
1399
|
-
useSitemaps: true,
|
|
1400
|
-
});
|
|
1401
|
-
|
|
1402
|
-
await crawler.initialize();
|
|
1403
|
-
const result = await crawler.crawl();
|
|
1404
|
-
console.log(`Crawl completo: ${result.processed} processadas`);
|
|
1405
|
-
```
|
|
1406
|
-
|
|
1407
|
-
---
|
|
1408
|
-
|
|
1409
|
-
## Referências
|
|
1410
|
-
|
|
1411
|
-
- [Crawlee Documentation](https://crawlee.dev)
|
|
1412
|
-
- [robots-parser npm](https://www.npmjs.com/package/robots-parser)
|
|
1413
|
-
- [RFC 9309 - Robots Exclusion Protocol](https://www.rfc-editor.org/rfc/rfc9309)
|
|
1414
|
-
- [sitemapper npm](https://www.npmjs.com/package/sitemapper)
|
|
1415
|
-
- [p-queue npm](https://www.npmjs.com/package/p-queue)
|
|
1416
|
-
- [normalize-url npm](https://www.npmjs.com/package/normalize-url)
|
|
1417
|
-
- [ufo npm](https://www.npmjs.com/package/ufo)
|