@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,1952 +0,0 @@
|
|
|
1
|
-
# Pesquisa: Content Extraction Avançada (2026)
|
|
2
|
-
|
|
3
|
-
## Table of Contents
|
|
4
|
-
|
|
5
|
-
- [Visão Geral](#visão-geral)
|
|
6
|
-
- [JSON-LD Extraction](#json-ld-extraction)
|
|
7
|
-
- [Microdata Extraction](#microdata-extraction)
|
|
8
|
-
- [OpenGraph / Twitter Card](#opengraph--twitter-card)
|
|
9
|
-
- [RSS/Atom Feeds](#rssatom-feeds)
|
|
10
|
-
- [Breadcrumb Extraction](#breadcrumb-extraction)
|
|
11
|
-
- [Readable Content Extraction](#readable-content-extraction)
|
|
12
|
-
- [HTML-to-Markdown](#html-to-markdown)
|
|
13
|
-
- [Content Detection Heuristics](#content-detection-heuristics)
|
|
14
|
-
- [API Interception Pattern](#api-interception-pattern)
|
|
15
|
-
- [Structured Data First Approach](#structured-data-first-approach)
|
|
16
|
-
- [Código Completo](#código-completo)
|
|
17
|
-
|
|
18
|
-
---
|
|
19
|
-
|
|
20
|
-
## Visão Geral
|
|
21
|
-
|
|
22
|
-
Content extraction é o processo de extrair dados estruturados e legíveis de HTML cru. Em 2026, a abordagem recomendada é **Structured Data First**: priorizar dados estruturados (JSON-LD, Microdata, OpenGraph) sobre extração de HTML bruto.
|
|
23
|
-
|
|
24
|
-
### Prioridade de Extração
|
|
25
|
-
|
|
26
|
-
```
|
|
27
|
-
1. JSON-LD → Dados estruturados mais completos e padronizados
|
|
28
|
-
2. Microdata → Schema.org inline (mais verboso que JSON-LD)
|
|
29
|
-
3. OpenGraph/Twitter → Metadados de social sharing
|
|
30
|
-
4. RSS/Atom → Feeds de conteúdo
|
|
31
|
-
5. HTML Readable → Conteúdo principal legível (Readability)
|
|
32
|
-
6. HTML Raw → Extração via seletores CSS
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
### Bibliotecas Principais (2026)
|
|
36
|
-
|
|
37
|
-
| Biblioteca | Versão | Uso |
|
|
38
|
-
|---|---|---|
|
|
39
|
-
| `cheerio` | 1.2.0 | Parsing HTML (jQuery-like) |
|
|
40
|
-
| `@mozilla/readability` | 0.6.0 | Extração de conteúdo legível |
|
|
41
|
-
| `turndown` | 7.2.4 | HTML → Markdown |
|
|
42
|
-
| `node-html-markdown` | 2.0.0 | HTML → Markdown (mais rápido) |
|
|
43
|
-
| `mdream` | 0.x | HTML → Markdown (Rust, 33x mais rápido) |
|
|
44
|
-
| `open-graph-scraper` | 6.11.0 | Extração OpenGraph |
|
|
45
|
-
| `feedsmith` | 2.x | Parsing RSS/Atom |
|
|
46
|
-
| `rss-parser` | 3.13.0 | Parsing RSS/Atom |
|
|
47
|
-
| `rss-finder` | 2.1.5 | Descoberta de feeds |
|
|
48
|
-
|
|
49
|
-
---
|
|
50
|
-
|
|
51
|
-
## JSON-LD Extraction
|
|
52
|
-
|
|
53
|
-
### Conceito
|
|
54
|
-
|
|
55
|
-
JSON-LD (JSON for Linked Data) é o formato preferido para dados estruturados. Google, Bing e outros motores de busca priorizam JSON-LD sobre Microdata.
|
|
56
|
-
|
|
57
|
-
### Estrutura JSON-LD
|
|
58
|
-
|
|
59
|
-
```json
|
|
60
|
-
{
|
|
61
|
-
"@context": "https://schema.org",
|
|
62
|
-
"@type": "Article",
|
|
63
|
-
"headline": "Como Criar um Web Scraper",
|
|
64
|
-
"author": {
|
|
65
|
-
"@type": "Person",
|
|
66
|
-
"name": "João Silva"
|
|
67
|
-
},
|
|
68
|
-
"datePublished": "2026-01-15",
|
|
69
|
-
"image": "https://example.com/image.jpg",
|
|
70
|
-
"publisher": {
|
|
71
|
-
"@type": "Organization",
|
|
72
|
-
"name": "DevThink",
|
|
73
|
-
"logo": {
|
|
74
|
-
"@type": "ImageObject",
|
|
75
|
-
"url": "https://example.com/logo.png"
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
### Extração com Cheerio
|
|
82
|
-
|
|
83
|
-
```ts
|
|
84
|
-
import * as cheerio from 'cheerio';
|
|
85
|
-
|
|
86
|
-
interface JsonLdData {
|
|
87
|
-
type: string;
|
|
88
|
-
data: Record<string, unknown>;
|
|
89
|
-
raw: string;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
function extractJsonLd(html: string): JsonLdData[] {
|
|
93
|
-
const $ = cheerio.load(html);
|
|
94
|
-
const results: JsonLdData[] = [];
|
|
95
|
-
|
|
96
|
-
$('script[type="application/ld+json"]').each((_, element) => {
|
|
97
|
-
const raw = $(element).html();
|
|
98
|
-
if (!raw) return;
|
|
99
|
-
|
|
100
|
-
try {
|
|
101
|
-
const data = JSON.parse(raw);
|
|
102
|
-
|
|
103
|
-
// JSON-LD pode ser um objeto único ou array
|
|
104
|
-
if (Array.isArray(data)) {
|
|
105
|
-
for (const item of data) {
|
|
106
|
-
results.push({
|
|
107
|
-
type: item['@type'] ?? 'Unknown',
|
|
108
|
-
data: item,
|
|
109
|
-
raw: JSON.stringify(item),
|
|
110
|
-
});
|
|
111
|
-
}
|
|
112
|
-
} else {
|
|
113
|
-
results.push({
|
|
114
|
-
type: data['@type'] ?? 'Unknown',
|
|
115
|
-
data,
|
|
116
|
-
raw: JSON.stringify(data),
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
} catch (error) {
|
|
120
|
-
console.error('Erro ao parsear JSON-LD:', error);
|
|
121
|
-
}
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
return results;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// Usar
|
|
128
|
-
const jsonLdItems = extractJsonLd(html);
|
|
129
|
-
for (const item of jsonLdItems) {
|
|
130
|
-
console.log(`Type: ${item.type}`);
|
|
131
|
-
|
|
132
|
-
if (item.type === 'Article') {
|
|
133
|
-
console.log(`Title: ${item.data.headline}`);
|
|
134
|
-
console.log(`Author: ${(item.data.author as any)?.name}`);
|
|
135
|
-
console.log(`Date: ${item.data.datePublished}`);
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
if (item.type === 'Product') {
|
|
139
|
-
console.log(`Name: ${item.data.name}`);
|
|
140
|
-
console.log(`Price: ${(item.data.offers as any)?.price}`);
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
if (item.type === 'Organization') {
|
|
144
|
-
console.log(`Name: ${item.data.name}`);
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
```
|
|
148
|
-
|
|
149
|
-
### Extração de Tipos Comuns
|
|
150
|
-
|
|
151
|
-
```ts
|
|
152
|
-
// Tipos JSON-LD comuns
|
|
153
|
-
type JsonLdType =
|
|
154
|
-
| 'Article'
|
|
155
|
-
| 'NewsArticle'
|
|
156
|
-
| 'BlogPosting'
|
|
157
|
-
| 'Product'
|
|
158
|
-
| 'Organization'
|
|
159
|
-
| 'Person'
|
|
160
|
-
| 'BreadcrumbList'
|
|
161
|
-
| 'FAQPage'
|
|
162
|
-
| 'HowTo'
|
|
163
|
-
| 'Event'
|
|
164
|
-
| 'LocalBusiness'
|
|
165
|
-
| 'WebSite'
|
|
166
|
-
| 'WebPage';
|
|
167
|
-
|
|
168
|
-
function extractJsonLdByType<T = Record<string, unknown>>(
|
|
169
|
-
html: string,
|
|
170
|
-
type: JsonLdType,
|
|
171
|
-
): T[] {
|
|
172
|
-
const items = extractJsonLd(html);
|
|
173
|
-
return items
|
|
174
|
-
.filter(item => item.type === type)
|
|
175
|
-
.map(item => item.data as T);
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
// Extrair artigo
|
|
179
|
-
interface ArticleData {
|
|
180
|
-
headline: string;
|
|
181
|
-
author: { name: string; url?: string };
|
|
182
|
-
datePublished: string;
|
|
183
|
-
dateModified?: string;
|
|
184
|
-
description?: string;
|
|
185
|
-
image?: string | string[];
|
|
186
|
-
publisher?: { name: string };
|
|
187
|
-
mainEntityOfPage?: string;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
const articles = extractJsonLdByType<ArticleData>(html, 'Article');
|
|
191
|
-
const article = articles[0];
|
|
192
|
-
if (article) {
|
|
193
|
-
console.log(`Title: ${article.headline}`);
|
|
194
|
-
console.log(`Author: ${article.author.name}`);
|
|
195
|
-
console.log(`Published: ${article.datePublished}`);
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
// Extrair produto
|
|
199
|
-
interface ProductData {
|
|
200
|
-
name: string;
|
|
201
|
-
description?: string;
|
|
202
|
-
image?: string | string[];
|
|
203
|
-
offers?: {
|
|
204
|
-
price: string;
|
|
205
|
-
priceCurrency: string;
|
|
206
|
-
availability: string;
|
|
207
|
-
};
|
|
208
|
-
brand?: { name: string };
|
|
209
|
-
sku?: string;
|
|
210
|
-
aggregateRating?: {
|
|
211
|
-
ratingValue: string;
|
|
212
|
-
reviewCount: string;
|
|
213
|
-
};
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
const products = extractJsonLdByType<ProductData>(html, 'Product');
|
|
217
|
-
```
|
|
218
|
-
|
|
219
|
-
### JSON-LD Composto (Graph)
|
|
220
|
-
|
|
221
|
-
```ts
|
|
222
|
-
// JSON-LD pode ter @graph com múltiplos tipos
|
|
223
|
-
const graphJsonLd = {
|
|
224
|
-
"@context": "https://schema.org",
|
|
225
|
-
"@graph": [
|
|
226
|
-
{
|
|
227
|
-
"@type": "Organization",
|
|
228
|
-
"name": "DevThink",
|
|
229
|
-
"url": "https://example.com"
|
|
230
|
-
},
|
|
231
|
-
{
|
|
232
|
-
"@type": "WebSite",
|
|
233
|
-
"name": "DevThink Blog",
|
|
234
|
-
"url": "https://example.com",
|
|
235
|
-
"potentialAction": {
|
|
236
|
-
"@type": "SearchAction",
|
|
237
|
-
"target": "https://example.com/search?q={search_term_string}"
|
|
238
|
-
}
|
|
239
|
-
},
|
|
240
|
-
{
|
|
241
|
-
"@type": "Article",
|
|
242
|
-
"headline": "Meu Artigo"
|
|
243
|
-
}
|
|
244
|
-
]
|
|
245
|
-
};
|
|
246
|
-
|
|
247
|
-
function extractFromGraph(html: string): Record<string, unknown>[] {
|
|
248
|
-
const $ = cheerio.load(html);
|
|
249
|
-
const results: Record<string, unknown>[] = [];
|
|
250
|
-
|
|
251
|
-
$('script[type="application/ld+json"]').each((_, element) => {
|
|
252
|
-
const raw = $(element).html();
|
|
253
|
-
if (!raw) return;
|
|
254
|
-
|
|
255
|
-
try {
|
|
256
|
-
const data = JSON.parse(raw);
|
|
257
|
-
|
|
258
|
-
if (data['@graph']) {
|
|
259
|
-
results.push(...data['@graph']);
|
|
260
|
-
} else if (data['@type']) {
|
|
261
|
-
results.push(data);
|
|
262
|
-
}
|
|
263
|
-
} catch {}
|
|
264
|
-
});
|
|
265
|
-
|
|
266
|
-
return results;
|
|
267
|
-
}
|
|
268
|
-
```
|
|
269
|
-
|
|
270
|
-
---
|
|
271
|
-
|
|
272
|
-
## Microdata Extraction
|
|
273
|
-
|
|
274
|
-
### Conceito
|
|
275
|
-
|
|
276
|
-
Microdata usa atributos HTML (`itemscope`, `itemtype`, `itemprop`) para marcar dados estruturados inline.
|
|
277
|
-
|
|
278
|
-
### Estrutura Microdata
|
|
279
|
-
|
|
280
|
-
```html
|
|
281
|
-
<div itemscope itemtype="https://schema.org/Article">
|
|
282
|
-
<h1 itemprop="headline">Como Criar um Web Scraper</h1>
|
|
283
|
-
<div itemprop="author" itemscope itemtype="https://schema.org/Person">
|
|
284
|
-
<span itemprop="name">João Silva</span>
|
|
285
|
-
</div>
|
|
286
|
-
<time itemprop="datePublished" datetime="2026-01-15">15 de Janeiro, 2026</time>
|
|
287
|
-
<img itemprop="image" src="https://example.com/image.jpg" alt="">
|
|
288
|
-
<div itemprop="articleBody">
|
|
289
|
-
<p>Conteúdo do artigo...</p>
|
|
290
|
-
</div>
|
|
291
|
-
</div>
|
|
292
|
-
```
|
|
293
|
-
|
|
294
|
-
### Extração com Cheerio
|
|
295
|
-
|
|
296
|
-
```ts
|
|
297
|
-
import * as cheerio from 'cheerio';
|
|
298
|
-
|
|
299
|
-
interface MicrodataItem {
|
|
300
|
-
type: string;
|
|
301
|
-
id?: string;
|
|
302
|
-
properties: Record<string, string | MicrodataItem[]>;
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
function extractMicrodata(html: string): MicrodataItem[] {
|
|
306
|
-
const $ = cheerio.load(html);
|
|
307
|
-
const results: MicrodataItem[] = [];
|
|
308
|
-
|
|
309
|
-
$('[itemscope]').each((_, element) => {
|
|
310
|
-
const $el = $(element);
|
|
311
|
-
const type = $el.attr('itemtype');
|
|
312
|
-
const id = $el.attr('itemid');
|
|
313
|
-
|
|
314
|
-
if (!type) return;
|
|
315
|
-
|
|
316
|
-
const properties: Record<string, string | MicrodataItem[]> = {};
|
|
317
|
-
|
|
318
|
-
// Extrair propriedades simples
|
|
319
|
-
$el.find('[itemprop]').each((_, prop) => {
|
|
320
|
-
const $prop = $(prop);
|
|
321
|
-
const name = $prop.attr('itemprop');
|
|
322
|
-
if (!name) return;
|
|
323
|
-
|
|
324
|
-
// Verificar se é um sub-item
|
|
325
|
-
if ($prop.attr('itemscope')) {
|
|
326
|
-
const subItems = extractMicrodata($.html($prop));
|
|
327
|
-
properties[name] = subItems;
|
|
328
|
-
} else {
|
|
329
|
-
// Valor pode estar em href, content, src, ou texto
|
|
330
|
-
const value = $prop.attr('content')
|
|
331
|
-
?? $prop.attr('href')
|
|
332
|
-
?? $prop.attr('src')
|
|
333
|
-
?? $prop.text().trim();
|
|
334
|
-
|
|
335
|
-
properties[name] = value;
|
|
336
|
-
}
|
|
337
|
-
});
|
|
338
|
-
|
|
339
|
-
results.push({ type, id, properties });
|
|
340
|
-
});
|
|
341
|
-
|
|
342
|
-
return results;
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
// Usar
|
|
346
|
-
const items = extractMicrodata(html);
|
|
347
|
-
for (const item of items) {
|
|
348
|
-
console.log(`Type: ${item.type}`);
|
|
349
|
-
console.log(`Properties:`, item.properties);
|
|
350
|
-
}
|
|
351
|
-
```
|
|
352
|
-
|
|
353
|
-
### microdata-minimal (Alternativa)
|
|
354
|
-
|
|
355
|
-
```ts
|
|
356
|
-
// Package: microdata-minimal
|
|
357
|
-
import { microdata } from 'microdata-minimal';
|
|
358
|
-
|
|
359
|
-
const items = microdata(html);
|
|
360
|
-
console.log(items);
|
|
361
|
-
// → [{ type: 'https://schema.org/Article', properties: { headline: '...' } }]
|
|
362
|
-
```
|
|
363
|
-
|
|
364
|
-
### Microdata vs JSON-LD
|
|
365
|
-
|
|
366
|
-
| Característica | Microdata | JSON-LD |
|
|
367
|
-
|---|---|---|
|
|
368
|
-
| Localização | Inline no HTML | Bloco `<script>` separado |
|
|
369
|
-
| Verbosidade | Alta (atributos em cada elemento) | Baixa (objeto JSON) |
|
|
370
|
-
| Legibilidade | Baixa | Alta |
|
|
371
|
-
| Google Priority | 2º | 1º |
|
|
372
|
-
| Mantimento | Difícil (acoplado ao HTML) | Fácil (bloco separado) |
|
|
373
|
-
| Suporte a @graph | ❌ | ✅ |
|
|
374
|
-
|
|
375
|
-
---
|
|
376
|
-
|
|
377
|
-
## OpenGraph / Twitter Card
|
|
378
|
-
|
|
379
|
-
### Conceito
|
|
380
|
-
|
|
381
|
-
OpenGraph (OG) e Twitter Card são metadados usados para social sharing. Menos completos que JSON-LD, mas universalmente suportados.
|
|
382
|
-
|
|
383
|
-
### Tags OpenGraph Comuns
|
|
384
|
-
|
|
385
|
-
```html
|
|
386
|
-
<!-- OpenGraph -->
|
|
387
|
-
<meta property="og:title" content="Título da Página">
|
|
388
|
-
<meta property="og:description" content="Descrição da Página">
|
|
389
|
-
<meta property="og:image" content="https://example.com/image.jpg">
|
|
390
|
-
<meta property="og:url" content="https://example.com/page">
|
|
391
|
-
<meta property="og:type" content="article">
|
|
392
|
-
<meta property="og:site_name" content="DevThink">
|
|
393
|
-
<meta property="og:locale" content="pt_BR">
|
|
394
|
-
<meta property="og:image:width" content="1200">
|
|
395
|
-
<meta property="og:image:height" content="630">
|
|
396
|
-
|
|
397
|
-
<!-- Twitter Card -->
|
|
398
|
-
<meta name="twitter:card" content="summary_large_image">
|
|
399
|
-
<meta name="twitter:site" content="@wenathlan">
|
|
400
|
-
<meta name="twitter:creator" content="@nasblor">
|
|
401
|
-
<meta name="twitter:title" content="Título da Página">
|
|
402
|
-
<meta name="twitter:description" content="Descrição da Página">
|
|
403
|
-
<meta name="twitter:image" content="https://example.com/image.jpg">
|
|
404
|
-
```
|
|
405
|
-
|
|
406
|
-
### Extração com Cheerio
|
|
407
|
-
|
|
408
|
-
```ts
|
|
409
|
-
import * as cheerio from 'cheerio';
|
|
410
|
-
|
|
411
|
-
interface OpenGraphData {
|
|
412
|
-
title?: string;
|
|
413
|
-
description?: string;
|
|
414
|
-
image?: string;
|
|
415
|
-
url?: string;
|
|
416
|
-
type?: string;
|
|
417
|
-
siteName?: string;
|
|
418
|
-
locale?: string;
|
|
419
|
-
imageWidth?: string;
|
|
420
|
-
imageHeight?: string;
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
interface TwitterCardData {
|
|
424
|
-
card?: string;
|
|
425
|
-
site?: string;
|
|
426
|
-
creator?: string;
|
|
427
|
-
title?: string;
|
|
428
|
-
description?: string;
|
|
429
|
-
image?: string;
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
interface SocialMeta {
|
|
433
|
-
openGraph: OpenGraphData;
|
|
434
|
-
twitter: TwitterCardData;
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
function extractSocialMeta(html: string): SocialMeta {
|
|
438
|
-
const $ = cheerio.load(html);
|
|
439
|
-
|
|
440
|
-
const openGraph: OpenGraphData = {};
|
|
441
|
-
const twitter: TwitterCardData = {};
|
|
442
|
-
|
|
443
|
-
// OpenGraph
|
|
444
|
-
$('meta[property^="og:"]').each((_, element) => {
|
|
445
|
-
const property = $(element).attr('property')?.replace('og:', '');
|
|
446
|
-
const content = $(element).attr('content');
|
|
447
|
-
if (property && content) {
|
|
448
|
-
(openGraph as any)[property] = content;
|
|
449
|
-
}
|
|
450
|
-
});
|
|
451
|
-
|
|
452
|
-
// Twitter Card
|
|
453
|
-
$('meta[name^="twitter:"]').each((_, element) => {
|
|
454
|
-
const name = $(element).attr('name')?.replace('twitter:', '');
|
|
455
|
-
const content = $(element).attr('content');
|
|
456
|
-
if (name && content) {
|
|
457
|
-
(twitter as any)[name] = content;
|
|
458
|
-
}
|
|
459
|
-
});
|
|
460
|
-
|
|
461
|
-
return { openGraph, twitter };
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
// Usar
|
|
465
|
-
const social = extractSocialMeta(html);
|
|
466
|
-
console.log(social.openGraph.title);
|
|
467
|
-
console.log(social.twitter.card);
|
|
468
|
-
```
|
|
469
|
-
|
|
470
|
-
### open-graph-scraper 6.11.0
|
|
471
|
-
|
|
472
|
-
```ts
|
|
473
|
-
import og from 'open-graph-scraper';
|
|
474
|
-
|
|
475
|
-
const options = {
|
|
476
|
-
url: 'https://example.com',
|
|
477
|
-
};
|
|
478
|
-
|
|
479
|
-
const result = await og(options);
|
|
480
|
-
if (result.success) {
|
|
481
|
-
const { ogTitle, ogDescription, ogImage, ogUrl, ogType } = result.result;
|
|
482
|
-
console.log(`Title: ${ogTitle}`);
|
|
483
|
-
console.log(`Description: ${ogDescription}`);
|
|
484
|
-
console.log(`Image: ${ogImage}`);
|
|
485
|
-
}
|
|
486
|
-
```
|
|
487
|
-
|
|
488
|
-
### ogie (Mais Moderno)
|
|
489
|
-
|
|
490
|
-
```ts
|
|
491
|
-
import ogie from 'ogie';
|
|
492
|
-
|
|
493
|
-
const result = await ogie('https://example.com');
|
|
494
|
-
console.log(result);
|
|
495
|
-
// → { title: '...', description: '...', image: '...', ... }
|
|
496
|
-
```
|
|
497
|
-
|
|
498
|
-
---
|
|
499
|
-
|
|
500
|
-
## RSS/Atom Feeds
|
|
501
|
-
|
|
502
|
-
### feedsmith 2.x (Mais Rápido)
|
|
503
|
-
|
|
504
|
-
`feedsmith` é o parser de feeds mais rápido em 2026. Suporta RSS 1.0, RSS 2.0, Atom, e JSON Feed.
|
|
505
|
-
|
|
506
|
-
```ts
|
|
507
|
-
import { parseFeed } from 'feedsmith';
|
|
508
|
-
|
|
509
|
-
const rssXml = `
|
|
510
|
-
<rss version="2.0">
|
|
511
|
-
<channel>
|
|
512
|
-
<title>Meu Blog</title>
|
|
513
|
-
<link>https://example.com</link>
|
|
514
|
-
<description>Descrição do blog</description>
|
|
515
|
-
<item>
|
|
516
|
-
<title>Artigo 1</title>
|
|
517
|
-
<link>https://example.com/artigo-1</link>
|
|
518
|
-
<description>Descrição do artigo 1</description>
|
|
519
|
-
<pubDate>Mon, 15 Jan 2026 10:00:00 GMT</pubDate>
|
|
520
|
-
<guid>https://example.com/artigo-1</guid>
|
|
521
|
-
</item>
|
|
522
|
-
<item>
|
|
523
|
-
<title>Artigo 2</title>
|
|
524
|
-
<link>https://example.com/artigo-2</link>
|
|
525
|
-
<description>Descrição do artigo 2</description>
|
|
526
|
-
<pubDate>Tue, 16 Jan 2026 10:00:00 GMT</pubDate>
|
|
527
|
-
</item>
|
|
528
|
-
</channel>
|
|
529
|
-
</rss>
|
|
530
|
-
`;
|
|
531
|
-
|
|
532
|
-
const feed = parseFeed(rssXml);
|
|
533
|
-
console.log(`Feed: ${feed.title}`);
|
|
534
|
-
console.log(`Link: ${feed.link}`);
|
|
535
|
-
console.log(`Items: ${feed.items.length}`);
|
|
536
|
-
|
|
537
|
-
for (const item of feed.items) {
|
|
538
|
-
console.log(` - ${item.title}`);
|
|
539
|
-
console.log(` Link: ${item.link}`);
|
|
540
|
-
console.log(` Date: ${item.pubDate}`);
|
|
541
|
-
}
|
|
542
|
-
```
|
|
543
|
-
|
|
544
|
-
### rss-parser 3.13.0
|
|
545
|
-
|
|
546
|
-
```ts
|
|
547
|
-
import RSSParser from 'rss-parser';
|
|
548
|
-
|
|
549
|
-
const parser = new RSSParser({
|
|
550
|
-
customFields: {
|
|
551
|
-
item: ['media:content', 'media:thumbnail'],
|
|
552
|
-
},
|
|
553
|
-
});
|
|
554
|
-
|
|
555
|
-
const feed = await parser.parseURL('https://example.com/feed.xml');
|
|
556
|
-
console.log(`Title: ${feed.title}`);
|
|
557
|
-
console.log(`Items: ${feed.items.length}`);
|
|
558
|
-
|
|
559
|
-
for (const item of feed.items) {
|
|
560
|
-
console.log(` - ${item.title}`);
|
|
561
|
-
console.log(` Link: ${item.link}`);
|
|
562
|
-
console.log(` Date: ${item.pubDate}`);
|
|
563
|
-
console.log(` Creator: ${item.creator}`);
|
|
564
|
-
}
|
|
565
|
-
```
|
|
566
|
-
|
|
567
|
-
### rss-finder 2.1.5 (Descoberta de Feeds)
|
|
568
|
-
|
|
569
|
-
`rss-finder` descobre automaticamente feeds RSS/Atom em uma página HTML.
|
|
570
|
-
|
|
571
|
-
```ts
|
|
572
|
-
import { findRssFeeds } from 'rss-finder';
|
|
573
|
-
|
|
574
|
-
const feeds = await findRssFeeds('https://example.com');
|
|
575
|
-
console.log(feeds);
|
|
576
|
-
// → [
|
|
577
|
-
// { title: 'Meu Blog RSS', url: 'https://example.com/feed.xml', type: 'application/rss+xml' },
|
|
578
|
-
// { title: 'Meu Blog Atom', url: 'https://example.com/atom.xml', type: 'application/atom+xml' },
|
|
579
|
-
// ]
|
|
580
|
-
|
|
581
|
-
// Usar com rss-parser
|
|
582
|
-
const parser = new RSSParser();
|
|
583
|
-
const feed = await parser.parseURL(feeds[0].url);
|
|
584
|
-
```
|
|
585
|
-
|
|
586
|
-
### Descoberta + Parse Combinados
|
|
587
|
-
|
|
588
|
-
```ts
|
|
589
|
-
import { findRssFeeds } from 'rss-finder';
|
|
590
|
-
import { parseFeed } from 'feedsmith';
|
|
591
|
-
import { gotScraping } from 'got-scraping';
|
|
592
|
-
|
|
593
|
-
async function discoverAndParseFeed(
|
|
594
|
-
url: string,
|
|
595
|
-
): Promise<{ feed: any; items: any[] } | null> {
|
|
596
|
-
// 1. Descobrir feeds
|
|
597
|
-
const feeds = await findRssFeeds(url);
|
|
598
|
-
if (feeds.length === 0) {
|
|
599
|
-
console.log(`Nenhum feed encontrado em ${url}`);
|
|
600
|
-
return null;
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
console.log(`Encontrados ${feeds.length} feeds`);
|
|
604
|
-
|
|
605
|
-
// 2. Parsear o primeiro feed
|
|
606
|
-
const feedUrl = feeds[0].url;
|
|
607
|
-
const response = await gotScraping(feedUrl);
|
|
608
|
-
const feed = parseFeed(response.body);
|
|
609
|
-
|
|
610
|
-
return {
|
|
611
|
-
feed: {
|
|
612
|
-
title: feed.title,
|
|
613
|
-
link: feed.link,
|
|
614
|
-
description: feed.description,
|
|
615
|
-
},
|
|
616
|
-
items: feed.items.map(item => ({
|
|
617
|
-
title: item.title,
|
|
618
|
-
link: item.link,
|
|
619
|
-
description: item.description,
|
|
620
|
-
pubDate: item.pubDate,
|
|
621
|
-
author: item.author,
|
|
622
|
-
})),
|
|
623
|
-
};
|
|
624
|
-
}
|
|
625
|
-
|
|
626
|
-
const result = await discoverAndParseFeed('https://example.com');
|
|
627
|
-
if (result) {
|
|
628
|
-
console.log(`Feed: ${result.feed.title}`);
|
|
629
|
-
for (const item of result.items) {
|
|
630
|
-
console.log(` - ${item.title}`);
|
|
631
|
-
}
|
|
632
|
-
}
|
|
633
|
-
```
|
|
634
|
-
|
|
635
|
-
---
|
|
636
|
-
|
|
637
|
-
## Breadcrumb Extraction
|
|
638
|
-
|
|
639
|
-
### JSON-LD BreadcrumbList
|
|
640
|
-
|
|
641
|
-
```json
|
|
642
|
-
{
|
|
643
|
-
"@context": "https://schema.org",
|
|
644
|
-
"@type": "BreadcrumbList",
|
|
645
|
-
"itemListElement": [
|
|
646
|
-
{
|
|
647
|
-
"@type": "ListItem",
|
|
648
|
-
"position": 1,
|
|
649
|
-
"name": "Home",
|
|
650
|
-
"item": "https://example.com"
|
|
651
|
-
},
|
|
652
|
-
{
|
|
653
|
-
"@type": "ListItem",
|
|
654
|
-
"position": 2,
|
|
655
|
-
"name": "Produtos",
|
|
656
|
-
"item": "https://example.com/products"
|
|
657
|
-
},
|
|
658
|
-
{
|
|
659
|
-
"@type": "ListItem",
|
|
660
|
-
"position": 3,
|
|
661
|
-
"name": "Produto X",
|
|
662
|
-
"item": "https://example.com/products/x"
|
|
663
|
-
}
|
|
664
|
-
]
|
|
665
|
-
}
|
|
666
|
-
```
|
|
667
|
-
|
|
668
|
-
```ts
|
|
669
|
-
import * as cheerio from 'cheerio';
|
|
670
|
-
|
|
671
|
-
interface BreadcrumbItem {
|
|
672
|
-
name: string;
|
|
673
|
-
url: string;
|
|
674
|
-
position: number;
|
|
675
|
-
}
|
|
676
|
-
|
|
677
|
-
function extractBreadcrumbs(html: string): BreadcrumbItem[] {
|
|
678
|
-
const $ = cheerio.load(html);
|
|
679
|
-
|
|
680
|
-
// 1. Tentar JSON-LD primeiro
|
|
681
|
-
const jsonLdBreadcrumbs = extractJsonLdBreadcrumbs(html);
|
|
682
|
-
if (jsonLdBreadcrumbs.length > 0) return jsonLdBreadcrumbs;
|
|
683
|
-
|
|
684
|
-
// 2. Tentar Microdata
|
|
685
|
-
const microdataBreadcrumbs = extractMicrodataBreadcrumbs(html);
|
|
686
|
-
if (microdataBreadcrumbs.length > 0) return microdataBreadcrumbs;
|
|
687
|
-
|
|
688
|
-
// 3. Fallback: HTML nav
|
|
689
|
-
return extractHtmlBreadcrumbs($);
|
|
690
|
-
}
|
|
691
|
-
|
|
692
|
-
function extractJsonLdBreadcrumbs(html: string): BreadcrumbItem[] {
|
|
693
|
-
const $ = cheerio.load(html);
|
|
694
|
-
const breadcrumbs: BreadcrumbItem[] = [];
|
|
695
|
-
|
|
696
|
-
$('script[type="application/ld+json"]').each((_, element) => {
|
|
697
|
-
const raw = $(element).html();
|
|
698
|
-
if (!raw) return;
|
|
699
|
-
|
|
700
|
-
try {
|
|
701
|
-
const data = JSON.parse(raw);
|
|
702
|
-
if (data['@type'] === 'BreadcrumbList' && data.itemListElement) {
|
|
703
|
-
for (const item of data.itemListElement) {
|
|
704
|
-
breadcrumbs.push({
|
|
705
|
-
name: item.name,
|
|
706
|
-
url: item.item,
|
|
707
|
-
position: item.position,
|
|
708
|
-
});
|
|
709
|
-
}
|
|
710
|
-
}
|
|
711
|
-
} catch {}
|
|
712
|
-
});
|
|
713
|
-
|
|
714
|
-
return breadcrumbs.sort((a, b) => a.position - b.position);
|
|
715
|
-
}
|
|
716
|
-
|
|
717
|
-
function extractMicrodataBreadcrumbs(html: string): BreadcrumbItem[] {
|
|
718
|
-
const $ = cheerio.load(html);
|
|
719
|
-
const breadcrumbs: BreadcrumbItem[] = [];
|
|
720
|
-
|
|
721
|
-
$('[itemtype="https://schema.org/BreadcrumbList"] [itemtype="https://schema.org/ListItem"]').each((_, element) => {
|
|
722
|
-
const $item = $(element);
|
|
723
|
-
const name = $item.find('[itemprop="name"]').text().trim();
|
|
724
|
-
const url = $item.find('[itemprop="item"]').attr('href') ?? '';
|
|
725
|
-
const position = Number.parseInt($item.find('[itemprop="position"]').text() ?? '0', 10);
|
|
726
|
-
|
|
727
|
-
if (name) {
|
|
728
|
-
breadcrumbs.push({ name, url, position });
|
|
729
|
-
}
|
|
730
|
-
});
|
|
731
|
-
|
|
732
|
-
return breadcrumbs.sort((a, b) => a.position - b.position);
|
|
733
|
-
}
|
|
734
|
-
|
|
735
|
-
function extractHtmlBreadcrumbs($: cheerio.CheerioAPI): BreadcrumbItem[] {
|
|
736
|
-
const breadcrumbs: BreadcrumbItem[] = [];
|
|
737
|
-
|
|
738
|
-
// Seletores comuns para breadcrumbs
|
|
739
|
-
const selectors = [
|
|
740
|
-
'nav[aria-label="breadcrumb"] a',
|
|
741
|
-
'ol.breadcrumb a',
|
|
742
|
-
'.breadcrumb a',
|
|
743
|
-
'[itemtype="https://schema.org/BreadcrumbList"] a',
|
|
744
|
-
];
|
|
745
|
-
|
|
746
|
-
for (const selector of selectors) {
|
|
747
|
-
$(selector).each((index, element) => {
|
|
748
|
-
const name = $(element).text().trim();
|
|
749
|
-
const url = $(element).attr('href') ?? '';
|
|
750
|
-
|
|
751
|
-
if (name) {
|
|
752
|
-
breadcrumbs.push({
|
|
753
|
-
name,
|
|
754
|
-
url,
|
|
755
|
-
position: index + 1,
|
|
756
|
-
});
|
|
757
|
-
}
|
|
758
|
-
});
|
|
759
|
-
|
|
760
|
-
if (breadcrumbs.length > 0) break;
|
|
761
|
-
}
|
|
762
|
-
|
|
763
|
-
return breadcrumbs;
|
|
764
|
-
}
|
|
765
|
-
```
|
|
766
|
-
|
|
767
|
-
### Exemplo de Uso
|
|
768
|
-
|
|
769
|
-
```ts
|
|
770
|
-
const breadcrumbs = extractBreadcrumbs(html);
|
|
771
|
-
if (breadcrumbs.length > 0) {
|
|
772
|
-
const path = breadcrumbs.map(b => b.name).join(' > ');
|
|
773
|
-
console.log(`Breadcrumb: ${path}`);
|
|
774
|
-
// → "Breadcrumb: Home > Produtos > Produto X"
|
|
775
|
-
}
|
|
776
|
-
```
|
|
777
|
-
|
|
778
|
-
---
|
|
779
|
-
|
|
780
|
-
## Readable Content Extraction
|
|
781
|
-
|
|
782
|
-
### @mozilla/readability 0.6.0
|
|
783
|
-
|
|
784
|
-
`@mozilla/readability` é a mesma biblioteca usada pelo Firefox Reader View. Extrai o conteúdo principal de uma página, removendo navigation, sidebars, ads, etc.
|
|
785
|
-
|
|
786
|
-
```ts
|
|
787
|
-
import { JSDOM } from 'jsdom';
|
|
788
|
-
import { Readability } from '@mozilla/readability';
|
|
789
|
-
|
|
790
|
-
function extractReadableContent(html: string, url = ''): {
|
|
791
|
-
title: string;
|
|
792
|
-
content: string;
|
|
793
|
-
textContent: string;
|
|
794
|
-
length: number;
|
|
795
|
-
excerpt: string;
|
|
796
|
-
byline: string;
|
|
797
|
-
dir: string;
|
|
798
|
-
siteName: string;
|
|
799
|
-
lang: string;
|
|
800
|
-
} | null {
|
|
801
|
-
const dom = new JSDOM(html, { url });
|
|
802
|
-
const document = dom.window.document;
|
|
803
|
-
|
|
804
|
-
const reader = new Readability(document, {
|
|
805
|
-
charThreshold: 100, // Mínimo de caracteres
|
|
806
|
-
keepClasses: false, // Remover classes CSS
|
|
807
|
-
nbTopCandidates: 5, // Top 5 candidates
|
|
808
|
-
});
|
|
809
|
-
|
|
810
|
-
const article = reader.parse();
|
|
811
|
-
if (!article) return null;
|
|
812
|
-
|
|
813
|
-
return {
|
|
814
|
-
title: article.title,
|
|
815
|
-
content: article.content, // HTML limpo
|
|
816
|
-
textContent: article.textContent, // Texto puro
|
|
817
|
-
length: article.length,
|
|
818
|
-
excerpt: article.excerpt,
|
|
819
|
-
byline: article.byline,
|
|
820
|
-
dir: article.dir,
|
|
821
|
-
siteName: article.siteName,
|
|
822
|
-
lang: article.lang,
|
|
823
|
-
};
|
|
824
|
-
}
|
|
825
|
-
|
|
826
|
-
// Usar
|
|
827
|
-
const readable = extractReadableContent(html, 'https://example.com/article');
|
|
828
|
-
if (readable) {
|
|
829
|
-
console.log(`Title: ${readable.title}`);
|
|
830
|
-
console.log(`Author: ${readable.byline}`);
|
|
831
|
-
console.log(`Length: ${readable.length} chars`);
|
|
832
|
-
console.log(`Excerpt: ${readable.excerpt}`);
|
|
833
|
-
console.log(`Content: ${readable.textContent.substring(0, 200)}...`);
|
|
834
|
-
}
|
|
835
|
-
```
|
|
836
|
-
|
|
837
|
-
### defuddle (Alternativa)
|
|
838
|
-
|
|
839
|
-
`defuddle` é uma alternativa ao Readability com foco em extração de conteúdo para e-readers.
|
|
840
|
-
|
|
841
|
-
```ts
|
|
842
|
-
import { defuddle } from 'defuddle';
|
|
843
|
-
|
|
844
|
-
const result = await defuddle(html, {
|
|
845
|
-
url: 'https://example.com',
|
|
846
|
-
});
|
|
847
|
-
|
|
848
|
-
console.log(result.title);
|
|
849
|
-
console.log(result.content); // Markdown formatado
|
|
850
|
-
console.log(result.excerpt);
|
|
851
|
-
console.log(result.siteName);
|
|
852
|
-
```
|
|
853
|
-
|
|
854
|
-
### Comparação Readability vs Defuddle
|
|
855
|
-
|
|
856
|
-
| Feature | @mozilla/readability | defuddle |
|
|
857
|
-
|---|---|---|
|
|
858
|
-
| Output | HTML | Markdown |
|
|
859
|
-
| Velocidade | ⭐⭐⭐⭐ | ⭐⭐⭐ |
|
|
860
|
-
| Qualidade | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
|
|
861
|
-
| Metadata | Básico | Completo |
|
|
862
|
-
| Manutenção | Mozilla (ativo) | Comunidade |
|
|
863
|
-
| Node.js | Via jsdom | Nativo |
|
|
864
|
-
|
|
865
|
-
---
|
|
866
|
-
|
|
867
|
-
## HTML-to-Markdown
|
|
868
|
-
|
|
869
|
-
### turndown 7.2.4 (Padrão)
|
|
870
|
-
|
|
871
|
-
`turndown` é o conversor HTML→Markdown mais popular. Usado pelo `@wenathlan/webscrape`.
|
|
872
|
-
|
|
873
|
-
```ts
|
|
874
|
-
import TurndownService from 'turndown';
|
|
875
|
-
|
|
876
|
-
const turndown = new TurndownService({
|
|
877
|
-
headingStyle: 'atx', # Heading
|
|
878
|
-
hr: '---', # Horizontal rule
|
|
879
|
-
bulletListMarker: '-', # Lista
|
|
880
|
-
codeBlockStyle: 'fenced', # Code block
|
|
881
|
-
fence: '```', # Fence
|
|
882
|
-
emDelimiter: '*', # Itálico
|
|
883
|
-
strongDelimiter: '**', # Bold
|
|
884
|
-
linkStyle: 'inlined', # Links
|
|
885
|
-
linkReferenceStyle: 'full', # Link references
|
|
886
|
-
});
|
|
887
|
-
|
|
888
|
-
// Customizar regras
|
|
889
|
-
turndown.addRule('customImages', {
|
|
890
|
-
filter: 'img',
|
|
891
|
-
replacement(content, node) {
|
|
892
|
-
const alt = node.getAttribute('alt') ?? '';
|
|
893
|
-
const src = node.getAttribute('src') ?? '';
|
|
894
|
-
return ``;
|
|
895
|
-
},
|
|
896
|
-
});
|
|
897
|
-
|
|
898
|
-
// Adicionar keep para preservar elementos
|
|
899
|
-
turndown.keep(['iframe', 'video']);
|
|
900
|
-
|
|
901
|
-
// Remover elementos
|
|
902
|
-
turndown.remove(['script', 'style', 'nav', 'footer', 'aside']);
|
|
903
|
-
|
|
904
|
-
const markdown = turndown.turndown(html);
|
|
905
|
-
console.log(markdown);
|
|
906
|
-
```
|
|
907
|
-
|
|
908
|
-
### node-html-markdown 2.0.0 (1.57x Mais Rápido)
|
|
909
|
-
|
|
910
|
-
```ts
|
|
911
|
-
import { NodeHtmlMarkdown } from 'node-html-markdown';
|
|
912
|
-
|
|
913
|
-
const nhm = new NodeHtmlMarkdown({
|
|
914
|
-
bulletMarker: '-',
|
|
915
|
-
codeBlockStyle: 'fenced',
|
|
916
|
-
useLinkReferenceDefinitions: false,
|
|
917
|
-
});
|
|
918
|
-
|
|
919
|
-
const markdown = nhm.translate(html);
|
|
920
|
-
console.log(markdown);
|
|
921
|
-
```
|
|
922
|
-
|
|
923
|
-
### mdream (Rust, 33x Mais Rápido)
|
|
924
|
-
|
|
925
|
-
```ts
|
|
926
|
-
// mdream é um conversor Rust compilado para WASM
|
|
927
|
-
// Performance: 33x mais rápido que turndown
|
|
928
|
-
import { convert } from 'mdream';
|
|
929
|
-
|
|
930
|
-
const markdown = convert(html, {
|
|
931
|
-
// Opções de conversão
|
|
932
|
-
});
|
|
933
|
-
```
|
|
934
|
-
|
|
935
|
-
### Comparação de Performance
|
|
936
|
-
|
|
937
|
-
| Biblioteca | Velocidade (1MB HTML) | Tamanho | linguagem |
|
|
938
|
-
|---|---|---|---|
|
|
939
|
-
| turndown | ~500ms | ~200KB | JavaScript |
|
|
940
|
-
| node-html-markdown | ~320ms | ~150KB | JavaScript |
|
|
941
|
-
| mdream | ~15ms | ~500KB | Rust/WASM |
|
|
942
|
-
|
|
943
|
-
### Conversão com Preservação de Estrutura
|
|
944
|
-
|
|
945
|
-
```ts
|
|
946
|
-
import TurndownService from 'turndown';
|
|
947
|
-
|
|
948
|
-
function htmlToMarkdownWithStructure(html: string): string {
|
|
949
|
-
const turndown = new TurndownService({
|
|
950
|
-
headingStyle: 'atx',
|
|
951
|
-
codeBlockStyle: 'fenced',
|
|
952
|
-
bulletListMarker: '-',
|
|
953
|
-
});
|
|
954
|
-
|
|
955
|
-
// Preservar tabelas
|
|
956
|
-
turndown.addRule('tables', {
|
|
957
|
-
filter: 'table',
|
|
958
|
-
replacement(content, node) {
|
|
959
|
-
// Implementação de tabela Markdown
|
|
960
|
-
const rows: string[] = [];
|
|
961
|
-
const headers: string[] = [];
|
|
962
|
-
|
|
963
|
-
node.querySelectorAll('thead th, th').forEach(th => {
|
|
964
|
-
headers.push(th.textContent?.trim() ?? '');
|
|
965
|
-
});
|
|
966
|
-
|
|
967
|
-
node.querySelectorAll('tbody tr, tr').forEach(tr => {
|
|
968
|
-
const cells: string[] = [];
|
|
969
|
-
tr.querySelectorAll('td').forEach(td => {
|
|
970
|
-
cells.push(td.textContent?.trim() ?? '');
|
|
971
|
-
});
|
|
972
|
-
if (cells.length > 0) rows.push(cells.join(' | '));
|
|
973
|
-
});
|
|
974
|
-
|
|
975
|
-
if (headers.length === 0) return content;
|
|
976
|
-
|
|
977
|
-
const headerRow = headers.join(' | ');
|
|
978
|
-
const separator = headers.map(() => '---').join(' | ');
|
|
979
|
-
const bodyRows = rows.join('\n');
|
|
980
|
-
|
|
981
|
-
return `\n\n${headerRow}\n${separator}\n${bodyRows}\n\n`;
|
|
982
|
-
},
|
|
983
|
-
});
|
|
984
|
-
|
|
985
|
-
// Preservar code blocks
|
|
986
|
-
turndown.addRule('codeBlocks', {
|
|
987
|
-
filter(node) {
|
|
988
|
-
return (
|
|
989
|
-
node.nodeName === 'PRE' &&
|
|
990
|
-
node.firstChild &&
|
|
991
|
-
node.firstChild.nodeName === 'CODE'
|
|
992
|
-
);
|
|
993
|
-
},
|
|
994
|
-
replacement(content, node) {
|
|
995
|
-
const code = node.querySelector('code');
|
|
996
|
-
const language = code?.className?.match(/language-(\w+)/)?.[1] ?? '';
|
|
997
|
-
const text = code?.textContent ?? node.textContent ?? '';
|
|
998
|
-
return `\n\n\`\`\`${language}\n${text.trim()}\n\`\`\`\n\n`;
|
|
999
|
-
},
|
|
1000
|
-
});
|
|
1001
|
-
|
|
1002
|
-
// Preservar imagens com caption
|
|
1003
|
-
turndown.addRule('figures', {
|
|
1004
|
-
filter: 'figure',
|
|
1005
|
-
replacement(content, node) {
|
|
1006
|
-
const img = node.querySelector('img');
|
|
1007
|
-
const caption = node.querySelector('figcaption')?.textContent?.trim();
|
|
1008
|
-
|
|
1009
|
-
if (!img) return content;
|
|
1010
|
-
|
|
1011
|
-
const src = img.getAttribute('src') ?? '';
|
|
1012
|
-
const alt = img.getAttribute('alt') ?? caption ?? '';
|
|
1013
|
-
|
|
1014
|
-
return `\n\n${caption ? `\n*${caption}*` : ''}\n\n`;
|
|
1015
|
-
},
|
|
1016
|
-
});
|
|
1017
|
-
|
|
1018
|
-
return turndown.turndown(html);
|
|
1019
|
-
}
|
|
1020
|
-
```
|
|
1021
|
-
|
|
1022
|
-
---
|
|
1023
|
-
|
|
1024
|
-
## Content Detection Heuristics
|
|
1025
|
-
|
|
1026
|
-
### Static vs SPA vs Hydrated
|
|
1027
|
-
|
|
1028
|
-
```ts
|
|
1029
|
-
interface DetectionResult {
|
|
1030
|
-
type: 'static' | 'spa' | 'hydrated' | 'api-driven';
|
|
1031
|
-
confidence: number;
|
|
1032
|
-
indicators: string[];
|
|
1033
|
-
}
|
|
1034
|
-
|
|
1035
|
-
function detectContentType(html: string, scripts: string[]): DetectionResult {
|
|
1036
|
-
const indicators: string[] = [];
|
|
1037
|
-
let spaScore = 0;
|
|
1038
|
-
let staticScore = 0;
|
|
1039
|
-
let hydratedScore = 0;
|
|
1040
|
-
|
|
1041
|
-
// SPA indicators
|
|
1042
|
-
if (html.includes('__NEXT_DATA__') || html.includes('_next/data')) {
|
|
1043
|
-
spaScore += 3;
|
|
1044
|
-
indicators.push('Next.js data');
|
|
1045
|
-
}
|
|
1046
|
-
if (html.includes('__NUXT__') || html.includes('_nuxt/')) {
|
|
1047
|
-
spaScore += 3;
|
|
1048
|
-
indicators.push('Nuxt.js data');
|
|
1049
|
-
}
|
|
1050
|
-
if (html.includes('window.__INITIAL_STATE__')) {
|
|
1051
|
-
spaScore += 2;
|
|
1052
|
-
indicators.push('Initial state');
|
|
1053
|
-
}
|
|
1054
|
-
if (html.includes('data-reactroot') || html.includes('data-reactroot')) {
|
|
1055
|
-
spaScore += 2;
|
|
1056
|
-
indicators.push('React root');
|
|
1057
|
-
}
|
|
1058
|
-
if (html.includes('ng-app') || html.includes('ng-controller')) {
|
|
1059
|
-
spaScore += 3;
|
|
1060
|
-
indicators.push('Angular');
|
|
1061
|
-
}
|
|
1062
|
-
if (html.includes('id="app"') && scripts.some(s => s.includes('vue'))) {
|
|
1063
|
-
spaScore += 3;
|
|
1064
|
-
indicators.push('Vue.js');
|
|
1065
|
-
}
|
|
1066
|
-
|
|
1067
|
-
// Static indicators
|
|
1068
|
-
if (!scripts.some(s => s.includes('react') || s.includes('vue') || s.includes('angular'))) {
|
|
1069
|
-
staticScore += 2;
|
|
1070
|
-
indicators.push('No SPA framework detected');
|
|
1071
|
-
}
|
|
1072
|
-
if (html.match(/<article[\s>]/i)) {
|
|
1073
|
-
staticScore += 1;
|
|
1074
|
-
indicators.push('Article tag present');
|
|
1075
|
-
}
|
|
1076
|
-
|
|
1077
|
-
// Hydrated indicators (SPA with server-rendered content)
|
|
1078
|
-
if (spaScore > 0 && html.match(/<article[\s>]/i)) {
|
|
1079
|
-
hydratedScore += 2;
|
|
1080
|
-
indicators.push('SSR with article content');
|
|
1081
|
-
}
|
|
1082
|
-
if (spaScore > 0 && html.includes('__NEXT_DATA__')) {
|
|
1083
|
-
hydratedScore += 2;
|
|
1084
|
-
indicators.push('Next.js SSR');
|
|
1085
|
-
}
|
|
1086
|
-
|
|
1087
|
-
// Determine type
|
|
1088
|
-
if (hydratedScore > spaScore && hydratedScore > staticScore) {
|
|
1089
|
-
return { type: 'hydrated', confidence: 0.8, indicators };
|
|
1090
|
-
}
|
|
1091
|
-
if (spaScore > staticScore) {
|
|
1092
|
-
return { type: 'spa', confidence: 0.7, indicators };
|
|
1093
|
-
}
|
|
1094
|
-
return { type: 'static', confidence: 0.9, indicators };
|
|
1095
|
-
}
|
|
1096
|
-
|
|
1097
|
-
// Usar
|
|
1098
|
-
const detection = detectContentType(html, scriptUrls);
|
|
1099
|
-
console.log(`Type: ${detection.type} (confidence: ${detection.confidence})`);
|
|
1100
|
-
console.log(`Indicators: ${detection.indicators.join(', ')}`);
|
|
1101
|
-
```
|
|
1102
|
-
|
|
1103
|
-
### Detecção de Framework
|
|
1104
|
-
|
|
1105
|
-
```ts
|
|
1106
|
-
interface FrameworkDetection {
|
|
1107
|
-
framework: string | null;
|
|
1108
|
-
version?: string;
|
|
1109
|
-
renderer: 'ssr' | 'csr' | 'ssg';
|
|
1110
|
-
}
|
|
1111
|
-
|
|
1112
|
-
function detectFramework(html: string): FrameworkDetection {
|
|
1113
|
-
// Next.js
|
|
1114
|
-
if (html.includes('__NEXT_DATA__')) {
|
|
1115
|
-
const versionMatch = html.match(/"buildId":"([^"]+)"/);
|
|
1116
|
-
return {
|
|
1117
|
-
framework: 'next.js',
|
|
1118
|
-
version: versionMatch?.[1],
|
|
1119
|
-
renderer: 'ssr',
|
|
1120
|
-
};
|
|
1121
|
-
}
|
|
1122
|
-
|
|
1123
|
-
// Nuxt.js
|
|
1124
|
-
if (html.includes('__NUXT__')) {
|
|
1125
|
-
return { framework: 'nuxt.js', renderer: 'ssr' };
|
|
1126
|
-
}
|
|
1127
|
-
|
|
1128
|
-
// Gatsby
|
|
1129
|
-
if (html.includes('___gatsby')) {
|
|
1130
|
-
return { framework: 'gatsby', renderer: 'ssg' };
|
|
1131
|
-
}
|
|
1132
|
-
|
|
1133
|
-
// Angular Universal
|
|
1134
|
-
if (html.includes('ng-server-app') || html.includes('TransferState')) {
|
|
1135
|
-
return { framework: 'angular', renderer: 'ssr' };
|
|
1136
|
-
}
|
|
1137
|
-
|
|
1138
|
-
// Remix
|
|
1139
|
-
if (html.includes('__remixContext')) {
|
|
1140
|
-
return { framework: 'remix', renderer: 'ssr' };
|
|
1141
|
-
}
|
|
1142
|
-
|
|
1143
|
-
// Astro
|
|
1144
|
-
if (html.includes('astro-island')) {
|
|
1145
|
-
return { framework: 'astro', renderer: 'ssr' };
|
|
1146
|
-
}
|
|
1147
|
-
|
|
1148
|
-
return { framework: null, renderer: 'csr' };
|
|
1149
|
-
}
|
|
1150
|
-
```
|
|
1151
|
-
|
|
1152
|
-
---
|
|
1153
|
-
|
|
1154
|
-
## API Interception Pattern
|
|
1155
|
-
|
|
1156
|
-
### Pular o Browser Totalmente
|
|
1157
|
-
|
|
1158
|
-
Para sites que usam APIs internas, interceptar as chamadas de rede é mais rápido e eficiente que renderizar o browser.
|
|
1159
|
-
|
|
1160
|
-
```ts
|
|
1161
|
-
import { chromium } from 'playwright';
|
|
1162
|
-
|
|
1163
|
-
interface InterceptedAPI {
|
|
1164
|
-
url: string;
|
|
1165
|
-
method: string;
|
|
1166
|
-
status: number;
|
|
1167
|
-
body: unknown;
|
|
1168
|
-
timestamp: number;
|
|
1169
|
-
}
|
|
1170
|
-
|
|
1171
|
-
async function scrapeWithAPIInterception(
|
|
1172
|
-
targetUrl: string,
|
|
1173
|
-
apiPattern: RegExp,
|
|
1174
|
-
): Promise<{ page: any; apiData: InterceptedAPI[] }> {
|
|
1175
|
-
const browser = await chromium.launch();
|
|
1176
|
-
const context = await browser.newContext();
|
|
1177
|
-
const page = await context.newPage();
|
|
1178
|
-
|
|
1179
|
-
const intercepted: InterceptedAPI[] = [];
|
|
1180
|
-
|
|
1181
|
-
// Interceptar requests de API
|
|
1182
|
-
page.on('response', async (response) => {
|
|
1183
|
-
const url = response.url();
|
|
1184
|
-
|
|
1185
|
-
if (apiPattern.test(url)) {
|
|
1186
|
-
try {
|
|
1187
|
-
const body = await response.json();
|
|
1188
|
-
intercepted.push({
|
|
1189
|
-
url,
|
|
1190
|
-
method: response.request().method(),
|
|
1191
|
-
status: response.status(),
|
|
1192
|
-
body,
|
|
1193
|
-
timestamp: Date.now(),
|
|
1194
|
-
});
|
|
1195
|
-
console.log(`API intercepted: ${url}`);
|
|
1196
|
-
} catch {
|
|
1197
|
-
// Response não é JSON
|
|
1198
|
-
}
|
|
1199
|
-
}
|
|
1200
|
-
});
|
|
1201
|
-
|
|
1202
|
-
await page.goto(targetUrl, { waitUntil: 'networkidle' });
|
|
1203
|
-
|
|
1204
|
-
return { page, apiData: intercepted };
|
|
1205
|
-
}
|
|
1206
|
-
|
|
1207
|
-
// Usar
|
|
1208
|
-
const { page, apiData } = await scrapeWithAPIInterception(
|
|
1209
|
-
'https://example.com/products',
|
|
1210
|
-
/\/api\/products/,
|
|
1211
|
-
);
|
|
1212
|
-
|
|
1213
|
-
console.log(`Interceptadas ${apiData.length} chamadas de API`);
|
|
1214
|
-
for (const api of apiData) {
|
|
1215
|
-
console.log(` ${api.method} ${api.url} → ${api.status}`);
|
|
1216
|
-
console.log(` Data:`, api.body);
|
|
1217
|
-
}
|
|
1218
|
-
```
|
|
1219
|
-
|
|
1220
|
-
### API Interception com Filtros
|
|
1221
|
-
|
|
1222
|
-
```ts
|
|
1223
|
-
interface APIFilter {
|
|
1224
|
-
method?: string[];
|
|
1225
|
-
status?: number[];
|
|
1226
|
-
contentType?: string[];
|
|
1227
|
-
pattern: RegExp;
|
|
1228
|
-
}
|
|
1229
|
-
|
|
1230
|
-
async function scrapeWithFilteredAPI(
|
|
1231
|
-
targetUrl: string,
|
|
1232
|
-
filters: APIFilter[],
|
|
1233
|
-
): Promise<Map<string, unknown[]>> {
|
|
1234
|
-
const browser = await chromium.launch();
|
|
1235
|
-
const context = await browser.newContext();
|
|
1236
|
-
const page = await context.newPage();
|
|
1237
|
-
|
|
1238
|
-
const results = new Map<string, unknown[]>();
|
|
1239
|
-
|
|
1240
|
-
page.on('response', async (response) => {
|
|
1241
|
-
const url = response.url();
|
|
1242
|
-
const method = response.request().method();
|
|
1243
|
-
const status = response.status();
|
|
1244
|
-
const contentType = response.headers()['content-type'] ?? '';
|
|
1245
|
-
|
|
1246
|
-
for (const filter of filters) {
|
|
1247
|
-
const matches =
|
|
1248
|
-
filter.pattern.test(url) &&
|
|
1249
|
-
(!filter.method || filter.method.includes(method)) &&
|
|
1250
|
-
(!filter.status || filter.status.includes(status)) &&
|
|
1251
|
-
(!filter.contentType || filter.contentType.some(ct => contentType.includes(ct)));
|
|
1252
|
-
|
|
1253
|
-
if (matches) {
|
|
1254
|
-
try {
|
|
1255
|
-
const body = await response.json();
|
|
1256
|
-
const key = filter.pattern.source;
|
|
1257
|
-
if (!results.has(key)) results.set(key, []);
|
|
1258
|
-
results.get(key)!.push(body);
|
|
1259
|
-
} catch {}
|
|
1260
|
-
}
|
|
1261
|
-
}
|
|
1262
|
-
});
|
|
1263
|
-
|
|
1264
|
-
await page.goto(targetUrl, { waitUntil: 'networkidle' });
|
|
1265
|
-
await browser.close();
|
|
1266
|
-
|
|
1267
|
-
return results;
|
|
1268
|
-
}
|
|
1269
|
-
```
|
|
1270
|
-
|
|
1271
|
-
### Extraindo Dados de APIs Interceptadas
|
|
1272
|
-
|
|
1273
|
-
```ts
|
|
1274
|
-
interface Product {
|
|
1275
|
-
id: string;
|
|
1276
|
-
name: string;
|
|
1277
|
-
price: number;
|
|
1278
|
-
image: string;
|
|
1279
|
-
}
|
|
1280
|
-
|
|
1281
|
-
async function extractProductsFromAPI(
|
|
1282
|
-
categoryUrl: string,
|
|
1283
|
-
): Promise<Product[]> {
|
|
1284
|
-
const browser = await chromium.launch();
|
|
1285
|
-
const context = await browser.newContext();
|
|
1286
|
-
const page = await context.newPage();
|
|
1287
|
-
|
|
1288
|
-
const products: Product[] = [];
|
|
1289
|
-
|
|
1290
|
-
page.on('response', async (response) => {
|
|
1291
|
-
if (response.url().includes('/api/products')) {
|
|
1292
|
-
try {
|
|
1293
|
-
const data = await response.json();
|
|
1294
|
-
if (Array.isArray(data.products)) {
|
|
1295
|
-
products.push(...data.products);
|
|
1296
|
-
}
|
|
1297
|
-
} catch {}
|
|
1298
|
-
}
|
|
1299
|
-
});
|
|
1300
|
-
|
|
1301
|
-
await page.goto(categoryUrl, { waitUntil: 'networkidle' });
|
|
1302
|
-
|
|
1303
|
-
// Scroll para carregar mais produtos (lazy loading)
|
|
1304
|
-
await page.evaluate(async () => {
|
|
1305
|
-
await new Promise((resolve) => {
|
|
1306
|
-
let totalHeight = 0;
|
|
1307
|
-
const distance = 500;
|
|
1308
|
-
const timer = setInterval(() => {
|
|
1309
|
-
window.scrollBy(0, distance);
|
|
1310
|
-
totalHeight += distance;
|
|
1311
|
-
if (totalHeight >= document.body.scrollHeight) {
|
|
1312
|
-
clearInterval(timer);
|
|
1313
|
-
resolve(undefined);
|
|
1314
|
-
}
|
|
1315
|
-
}, 200);
|
|
1316
|
-
});
|
|
1317
|
-
});
|
|
1318
|
-
|
|
1319
|
-
// Aguardar requests de API
|
|
1320
|
-
await page.waitForTimeout(2000);
|
|
1321
|
-
|
|
1322
|
-
await browser.close();
|
|
1323
|
-
|
|
1324
|
-
// Deduplicar por ID
|
|
1325
|
-
const seen = new Set<string>();
|
|
1326
|
-
return products.filter(p => {
|
|
1327
|
-
if (seen.has(p.id)) return false;
|
|
1328
|
-
seen.add(p.id);
|
|
1329
|
-
return true;
|
|
1330
|
-
});
|
|
1331
|
-
}
|
|
1332
|
-
```
|
|
1333
|
-
|
|
1334
|
-
---
|
|
1335
|
-
|
|
1336
|
-
## Structured Data First Approach
|
|
1337
|
-
|
|
1338
|
-
### Estratégia de Extração em Cascata
|
|
1339
|
-
|
|
1340
|
-
```ts
|
|
1341
|
-
import * as cheerio from 'cheerio';
|
|
1342
|
-
import { Readability } from '@mozilla/readability';
|
|
1343
|
-
import { JSDOM } from 'jsdom';
|
|
1344
|
-
|
|
1345
|
-
interface ExtractedData {
|
|
1346
|
-
source: 'json-ld' | 'microdata' | 'og' | 'readability' | 'html';
|
|
1347
|
-
type: string;
|
|
1348
|
-
title: string;
|
|
1349
|
-
description?: string;
|
|
1350
|
-
author?: string;
|
|
1351
|
-
date?: string;
|
|
1352
|
-
image?: string;
|
|
1353
|
-
content: string;
|
|
1354
|
-
url: string;
|
|
1355
|
-
metadata: Record<string, unknown>;
|
|
1356
|
-
}
|
|
1357
|
-
|
|
1358
|
-
function extractWithPriority(html: string, url: string): ExtractedData | null {
|
|
1359
|
-
// 1. Tentar JSON-LD
|
|
1360
|
-
const jsonLdResult = extractFromJsonLd(html, url);
|
|
1361
|
-
if (jsonLdResult) return jsonLdResult;
|
|
1362
|
-
|
|
1363
|
-
// 2. Tentar Microdata
|
|
1364
|
-
const microdataResult = extractFromMicrodata(html, url);
|
|
1365
|
-
if (microdataResult) return microdataResult;
|
|
1366
|
-
|
|
1367
|
-
// 3. Tentar OpenGraph
|
|
1368
|
-
const ogResult = extractFromOpenGraph(html, url);
|
|
1369
|
-
if (ogResult) return ogResult;
|
|
1370
|
-
|
|
1371
|
-
// 4. Fallback: Readability
|
|
1372
|
-
const readableResult = extractFromReadability(html, url);
|
|
1373
|
-
if (readableResult) return readableResult;
|
|
1374
|
-
|
|
1375
|
-
// 5. Último recurso: HTML cru
|
|
1376
|
-
return extractFromHtml(html, url);
|
|
1377
|
-
}
|
|
1378
|
-
|
|
1379
|
-
function extractFromJsonLd(html: string, url: string): ExtractedData | null {
|
|
1380
|
-
const $ = cheerio.load(html);
|
|
1381
|
-
|
|
1382
|
-
const jsonLdScripts = $('script[type="application/ld+json"]');
|
|
1383
|
-
if (jsonLdScripts.length === 0) return null;
|
|
1384
|
-
|
|
1385
|
-
for (const script of jsonLdScripts.toArray()) {
|
|
1386
|
-
const raw = $(script).html();
|
|
1387
|
-
if (!raw) continue;
|
|
1388
|
-
|
|
1389
|
-
try {
|
|
1390
|
-
const data = JSON.parse(raw);
|
|
1391
|
-
|
|
1392
|
-
// Suporte a @graph
|
|
1393
|
-
const items = data['@graph'] ?? [data];
|
|
1394
|
-
|
|
1395
|
-
for (const item of items) {
|
|
1396
|
-
const type = item['@type'];
|
|
1397
|
-
if (!type) continue;
|
|
1398
|
-
|
|
1399
|
-
// Extrair baseado no tipo
|
|
1400
|
-
const result = mapJsonLdToExtracted(item, type, url);
|
|
1401
|
-
if (result) return result;
|
|
1402
|
-
}
|
|
1403
|
-
} catch {}
|
|
1404
|
-
}
|
|
1405
|
-
|
|
1406
|
-
return null;
|
|
1407
|
-
}
|
|
1408
|
-
|
|
1409
|
-
function mapJsonLdToExtracted(
|
|
1410
|
-
data: Record<string, unknown>,
|
|
1411
|
-
type: string,
|
|
1412
|
-
url: string,
|
|
1413
|
-
): ExtractedData | null {
|
|
1414
|
-
const base = {
|
|
1415
|
-
source: 'json-ld' as const,
|
|
1416
|
-
type,
|
|
1417
|
-
url,
|
|
1418
|
-
metadata: data,
|
|
1419
|
-
};
|
|
1420
|
-
|
|
1421
|
-
if (['Article', 'NewsArticle', 'BlogPosting'].includes(type)) {
|
|
1422
|
-
return {
|
|
1423
|
-
...base,
|
|
1424
|
-
title: (data.headline as string) ?? '',
|
|
1425
|
-
description: (data.description as string) ?? '',
|
|
1426
|
-
author: (data.author as any)?.name ?? '',
|
|
1427
|
-
date: (data.datePublished as string) ?? '',
|
|
1428
|
-
image: Array.isArray(data.image) ? data.image[0] as string : (data.image as string) ?? '',
|
|
1429
|
-
content: (data.articleBody as string) ?? '',
|
|
1430
|
-
};
|
|
1431
|
-
}
|
|
1432
|
-
|
|
1433
|
-
if (type === 'Product') {
|
|
1434
|
-
return {
|
|
1435
|
-
...base,
|
|
1436
|
-
title: (data.name as string) ?? '',
|
|
1437
|
-
description: (data.description as string) ?? '',
|
|
1438
|
-
image: Array.isArray(data.image) ? data.image[0] as string : (data.image as string) ?? '',
|
|
1439
|
-
content: JSON.stringify(data.offers),
|
|
1440
|
-
};
|
|
1441
|
-
}
|
|
1442
|
-
|
|
1443
|
-
if (type === 'WebPage') {
|
|
1444
|
-
return {
|
|
1445
|
-
...base,
|
|
1446
|
-
title: (data.name as string) ?? '',
|
|
1447
|
-
description: (data.description as string) ?? '',
|
|
1448
|
-
content: '',
|
|
1449
|
-
};
|
|
1450
|
-
}
|
|
1451
|
-
|
|
1452
|
-
return null;
|
|
1453
|
-
}
|
|
1454
|
-
|
|
1455
|
-
function extractFromOpenGraph(html: string, url: string): ExtractedData | null {
|
|
1456
|
-
const $ = cheerio.load(html);
|
|
1457
|
-
|
|
1458
|
-
const title = $('meta[property="og:title"]').attr('content');
|
|
1459
|
-
if (!title) return null;
|
|
1460
|
-
|
|
1461
|
-
return {
|
|
1462
|
-
source: 'og',
|
|
1463
|
-
type: 'og:article',
|
|
1464
|
-
title,
|
|
1465
|
-
description: $('meta[property="og:description"]').attr('content'),
|
|
1466
|
-
image: $('meta[property="og:image"]').attr('content'),
|
|
1467
|
-
url: $('meta[property="og:url"]').attr('content') ?? url,
|
|
1468
|
-
metadata: {
|
|
1469
|
-
siteName: $('meta[property="og:site_name"]').attr('content'),
|
|
1470
|
-
type: $('meta[property="og:type"]').attr('content'),
|
|
1471
|
-
},
|
|
1472
|
-
content: '',
|
|
1473
|
-
};
|
|
1474
|
-
}
|
|
1475
|
-
|
|
1476
|
-
function extractFromReadability(html: string, url: string): ExtractedData | null {
|
|
1477
|
-
const dom = new JSDOM(html, { url });
|
|
1478
|
-
const reader = new Readability(dom.window.document);
|
|
1479
|
-
const article = reader.parse();
|
|
1480
|
-
|
|
1481
|
-
if (!article) return null;
|
|
1482
|
-
|
|
1483
|
-
return {
|
|
1484
|
-
source: 'readability',
|
|
1485
|
-
type: 'article',
|
|
1486
|
-
title: article.title,
|
|
1487
|
-
author: article.byline,
|
|
1488
|
-
content: article.textContent,
|
|
1489
|
-
url,
|
|
1490
|
-
metadata: {
|
|
1491
|
-
excerpt: article.excerpt,
|
|
1492
|
-
siteName: article.siteName,
|
|
1493
|
-
},
|
|
1494
|
-
};
|
|
1495
|
-
}
|
|
1496
|
-
|
|
1497
|
-
function extractFromHtml(html: string, url: string): ExtractedData {
|
|
1498
|
-
const $ = cheerio.load(html);
|
|
1499
|
-
|
|
1500
|
-
return {
|
|
1501
|
-
source: 'html',
|
|
1502
|
-
type: 'page',
|
|
1503
|
-
title: $('title').text().trim() || $('h1').first().text().trim(),
|
|
1504
|
-
description: $('meta[name="description"]').attr('content'),
|
|
1505
|
-
author: $('meta[name="author"]').attr('content'),
|
|
1506
|
-
url,
|
|
1507
|
-
metadata: {},
|
|
1508
|
-
content: $('body').text().replace(/\s+/g, ' ').trim(),
|
|
1509
|
-
};
|
|
1510
|
-
}
|
|
1511
|
-
|
|
1512
|
-
// Usar
|
|
1513
|
-
const data = extractWithPriority(html, 'https://example.com/article');
|
|
1514
|
-
if (data) {
|
|
1515
|
-
console.log(`Source: ${data.source}`);
|
|
1516
|
-
console.log(`Type: ${data.type}`);
|
|
1517
|
-
console.log(`Title: ${data.title}`);
|
|
1518
|
-
}
|
|
1519
|
-
```
|
|
1520
|
-
|
|
1521
|
-
---
|
|
1522
|
-
|
|
1523
|
-
## Código Completo
|
|
1524
|
-
|
|
1525
|
-
### Exemplo 1: Content Extractor Completo
|
|
1526
|
-
|
|
1527
|
-
```ts
|
|
1528
|
-
import * as cheerio from 'cheerio';
|
|
1529
|
-
import { Readability } from '@mozilla/readability';
|
|
1530
|
-
import { JSDOM } from 'jsdom';
|
|
1531
|
-
import TurndownService from 'turndown';
|
|
1532
|
-
|
|
1533
|
-
interface ContentExtractorOptions {
|
|
1534
|
-
url: string;
|
|
1535
|
-
extractJsonLd?: boolean;
|
|
1536
|
-
extractMicrodata?: boolean;
|
|
1537
|
-
extractOpenGraph?: boolean;
|
|
1538
|
-
extractBreadcrumbs?: boolean;
|
|
1539
|
-
extractFeeds?: boolean;
|
|
1540
|
-
extractReadable?: boolean;
|
|
1541
|
-
htmlToMarkdown?: boolean;
|
|
1542
|
-
}
|
|
1543
|
-
|
|
1544
|
-
interface ContentExtractorResult {
|
|
1545
|
-
url: string;
|
|
1546
|
-
title: string;
|
|
1547
|
-
author?: string;
|
|
1548
|
-
date?: string;
|
|
1549
|
-
description?: string;
|
|
1550
|
-
image?: string;
|
|
1551
|
-
content: string;
|
|
1552
|
-
contentMarkdown?: string;
|
|
1553
|
-
breadcrumbs: Array<{ name: string; url: string }>;
|
|
1554
|
-
feeds: Array<{ title: string; url: string; type: string }>;
|
|
1555
|
-
openGraph: Record<string, string>;
|
|
1556
|
-
jsonLd: Record<string, unknown>[];
|
|
1557
|
-
metadata: Record<string, unknown>;
|
|
1558
|
-
}
|
|
1559
|
-
|
|
1560
|
-
class ContentExtractor {
|
|
1561
|
-
private options: ContentExtractorOptions;
|
|
1562
|
-
|
|
1563
|
-
constructor(options: ContentExtractorOptions) {
|
|
1564
|
-
this.options = options;
|
|
1565
|
-
}
|
|
1566
|
-
|
|
1567
|
-
async extract(html: string): Promise<ContentExtractorResult> {
|
|
1568
|
-
const $ = cheerio.load(html);
|
|
1569
|
-
const url = this.options.url;
|
|
1570
|
-
|
|
1571
|
-
const result: ContentExtractorResult = {
|
|
1572
|
-
url,
|
|
1573
|
-
title: $('title').text().trim(),
|
|
1574
|
-
content: '',
|
|
1575
|
-
breadcrumbs: [],
|
|
1576
|
-
feeds: [],
|
|
1577
|
-
openGraph: {},
|
|
1578
|
-
jsonLd: [],
|
|
1579
|
-
metadata: {},
|
|
1580
|
-
};
|
|
1581
|
-
|
|
1582
|
-
// 1. JSON-LD
|
|
1583
|
-
if (this.options.extractJsonLd) {
|
|
1584
|
-
result.jsonLd = this.extractJsonLd($);
|
|
1585
|
-
const article = this.findArticleInJsonLd(result.jsonLd);
|
|
1586
|
-
if (article) {
|
|
1587
|
-
result.title = article.headline ?? result.title;
|
|
1588
|
-
result.author = article.author;
|
|
1589
|
-
result.date = article.datePublished;
|
|
1590
|
-
result.description = article.description;
|
|
1591
|
-
result.image = article.image;
|
|
1592
|
-
}
|
|
1593
|
-
}
|
|
1594
|
-
|
|
1595
|
-
// 2. OpenGraph
|
|
1596
|
-
if (this.options.extractOpenGraph) {
|
|
1597
|
-
result.openGraph = this.extractOpenGraph($);
|
|
1598
|
-
if (!result.description) {
|
|
1599
|
-
result.description = result.openGraph['og:description'];
|
|
1600
|
-
}
|
|
1601
|
-
if (!result.image) {
|
|
1602
|
-
result.image = result.openGraph['og:image'];
|
|
1603
|
-
}
|
|
1604
|
-
}
|
|
1605
|
-
|
|
1606
|
-
// 3. Breadcrumbs
|
|
1607
|
-
if (this.options.extractBreadcrumbs) {
|
|
1608
|
-
result.breadcrumbs = this.extractBreadcrumbs($);
|
|
1609
|
-
}
|
|
1610
|
-
|
|
1611
|
-
// 4. Feeds
|
|
1612
|
-
if (this.options.extractFeeds) {
|
|
1613
|
-
result.feeds = this.extractFeeds($);
|
|
1614
|
-
}
|
|
1615
|
-
|
|
1616
|
-
// 5. Readable content
|
|
1617
|
-
if (this.options.extractReadable) {
|
|
1618
|
-
const readable = this.extractReadable(html);
|
|
1619
|
-
if (readable) {
|
|
1620
|
-
result.content = readable.textContent;
|
|
1621
|
-
if (!result.title || result.title === 'Untitled') {
|
|
1622
|
-
result.title = readable.title;
|
|
1623
|
-
}
|
|
1624
|
-
if (!result.author) {
|
|
1625
|
-
result.author = readable.byline;
|
|
1626
|
-
}
|
|
1627
|
-
}
|
|
1628
|
-
}
|
|
1629
|
-
|
|
1630
|
-
// 6. HTML to Markdown
|
|
1631
|
-
if (this.options.htmlToMarkdown && result.content) {
|
|
1632
|
-
const turndown = new TurndownService({
|
|
1633
|
-
headingStyle: 'atx',
|
|
1634
|
-
codeBlockStyle: 'fenced',
|
|
1635
|
-
});
|
|
1636
|
-
result.contentMarkdown = turndown.turndown(result.content);
|
|
1637
|
-
}
|
|
1638
|
-
|
|
1639
|
-
// 7. Fallback: raw content
|
|
1640
|
-
if (!result.content) {
|
|
1641
|
-
result.content = $('body').text().replace(/\s+/g, ' ').trim();
|
|
1642
|
-
}
|
|
1643
|
-
|
|
1644
|
-
return result;
|
|
1645
|
-
}
|
|
1646
|
-
|
|
1647
|
-
private extractJsonLd($: cheerio.CheerioAPI): Record<string, unknown>[] {
|
|
1648
|
-
const results: Record<string, unknown>[] = [];
|
|
1649
|
-
|
|
1650
|
-
$('script[type="application/ld+json"]').each((_, element) => {
|
|
1651
|
-
const raw = $(element).html();
|
|
1652
|
-
if (!raw) return;
|
|
1653
|
-
|
|
1654
|
-
try {
|
|
1655
|
-
const data = JSON.parse(raw);
|
|
1656
|
-
if (data['@graph']) {
|
|
1657
|
-
results.push(...data['@graph']);
|
|
1658
|
-
} else {
|
|
1659
|
-
results.push(data);
|
|
1660
|
-
}
|
|
1661
|
-
} catch {}
|
|
1662
|
-
});
|
|
1663
|
-
|
|
1664
|
-
return results;
|
|
1665
|
-
}
|
|
1666
|
-
|
|
1667
|
-
private findArticleInJsonLd(jsonLd: Record<string, unknown>[]): any {
|
|
1668
|
-
return jsonLd.find(item =>
|
|
1669
|
-
['Article', 'NewsArticle', 'BlogPosting'].includes(item['@type'] as string)
|
|
1670
|
-
);
|
|
1671
|
-
}
|
|
1672
|
-
|
|
1673
|
-
private extractOpenGraph($: cheerio.CheerioAPI): Record<string, string> {
|
|
1674
|
-
const og: Record<string, string> = {};
|
|
1675
|
-
|
|
1676
|
-
$('meta[property^="og:"]').each((_, element) => {
|
|
1677
|
-
const property = $(element).attr('property')?.replace('og:', '');
|
|
1678
|
-
const content = $(element).attr('content');
|
|
1679
|
-
if (property && content) {
|
|
1680
|
-
og[property] = content;
|
|
1681
|
-
}
|
|
1682
|
-
});
|
|
1683
|
-
|
|
1684
|
-
return og;
|
|
1685
|
-
}
|
|
1686
|
-
|
|
1687
|
-
private extractBreadcrumbs($: cheerio.CheerioAPI): Array<{ name: string; url: string }> {
|
|
1688
|
-
const breadcrumbs: Array<{ name: string; url: string }> = [];
|
|
1689
|
-
|
|
1690
|
-
$('nav[aria-label="breadcrumb"] a, .breadcrumb a').each((_, element) => {
|
|
1691
|
-
const name = $(element).text().trim();
|
|
1692
|
-
const url = $(element).attr('href') ?? '';
|
|
1693
|
-
if (name) {
|
|
1694
|
-
breadcrumbs.push({ name, url });
|
|
1695
|
-
}
|
|
1696
|
-
});
|
|
1697
|
-
|
|
1698
|
-
return breadcrumbs;
|
|
1699
|
-
}
|
|
1700
|
-
|
|
1701
|
-
private extractFeeds($: cheerio.CheerioAPI): Array<{ title: string; url: string; type: string }> {
|
|
1702
|
-
const feeds: Array<{ title: string; url: string; type: string }> = [];
|
|
1703
|
-
|
|
1704
|
-
$('link[type="application/rss+xml"], link[type="application/atom+xml"]').each((_, element) => {
|
|
1705
|
-
const title = $(element).attr('title') ?? '';
|
|
1706
|
-
const url = $(element).attr('href') ?? '';
|
|
1707
|
-
const type = $(element).attr('type') ?? '';
|
|
1708
|
-
|
|
1709
|
-
if (url) {
|
|
1710
|
-
feeds.push({ title, url, type });
|
|
1711
|
-
}
|
|
1712
|
-
});
|
|
1713
|
-
|
|
1714
|
-
return feeds;
|
|
1715
|
-
}
|
|
1716
|
-
|
|
1717
|
-
private extractReadable(html: string): {
|
|
1718
|
-
title: string;
|
|
1719
|
-
content: string;
|
|
1720
|
-
textContent: string;
|
|
1721
|
-
byline: string;
|
|
1722
|
-
} | null {
|
|
1723
|
-
const dom = new JSDOM(html, { url: this.options.url });
|
|
1724
|
-
const reader = new Readability(dom.window.document);
|
|
1725
|
-
const article = reader.parse();
|
|
1726
|
-
|
|
1727
|
-
if (!article) return null;
|
|
1728
|
-
|
|
1729
|
-
return {
|
|
1730
|
-
title: article.title,
|
|
1731
|
-
content: article.content,
|
|
1732
|
-
textContent: article.textContent,
|
|
1733
|
-
byline: article.byline,
|
|
1734
|
-
};
|
|
1735
|
-
}
|
|
1736
|
-
}
|
|
1737
|
-
|
|
1738
|
-
// Usar
|
|
1739
|
-
const extractor = new ContentExtractor({
|
|
1740
|
-
url: 'https://example.com/article',
|
|
1741
|
-
extractJsonLd: true,
|
|
1742
|
-
extractMicrodata: true,
|
|
1743
|
-
extractOpenGraph: true,
|
|
1744
|
-
extractBreadcrumbs: true,
|
|
1745
|
-
extractFeeds: true,
|
|
1746
|
-
extractReadable: true,
|
|
1747
|
-
htmlToMarkdown: true,
|
|
1748
|
-
});
|
|
1749
|
-
|
|
1750
|
-
const result = await extractor.extract(html);
|
|
1751
|
-
console.log(`Title: ${result.title}`);
|
|
1752
|
-
console.log(`Author: ${result.author}`);
|
|
1753
|
-
console.log(`Breadcrumbs: ${result.breadcrumbs.map(b => b.name).join(' > ')}`);
|
|
1754
|
-
console.log(`Feeds: ${result.feeds.length}`);
|
|
1755
|
-
console.log(`JSON-LD items: ${result.jsonLd.length}`);
|
|
1756
|
-
```
|
|
1757
|
-
|
|
1758
|
-
### Exemplo 2: Scraper com API Interception + Content Extraction
|
|
1759
|
-
|
|
1760
|
-
```ts
|
|
1761
|
-
import { chromium } from 'playwright';
|
|
1762
|
-
import * as cheerio from 'cheerio';
|
|
1763
|
-
|
|
1764
|
-
interface SmartScraperConfig {
|
|
1765
|
-
url: string;
|
|
1766
|
-
apiPatterns?: RegExp[];
|
|
1767
|
-
waitFor?: number;
|
|
1768
|
-
scroll?: boolean;
|
|
1769
|
-
}
|
|
1770
|
-
|
|
1771
|
-
class SmartScraper {
|
|
1772
|
-
private config: SmartScraperConfig;
|
|
1773
|
-
|
|
1774
|
-
constructor(config: SmartScraperConfig) {
|
|
1775
|
-
this.config = config;
|
|
1776
|
-
}
|
|
1777
|
-
|
|
1778
|
-
async scrape(): Promise<{
|
|
1779
|
-
html: string;
|
|
1780
|
-
apiData: unknown[];
|
|
1781
|
-
extracted: ContentExtractorResult;
|
|
1782
|
-
}> {
|
|
1783
|
-
const browser = await chromium.launch();
|
|
1784
|
-
const context = await browser.newContext();
|
|
1785
|
-
const page = await context.newPage();
|
|
1786
|
-
|
|
1787
|
-
const apiData: unknown[] = [];
|
|
1788
|
-
|
|
1789
|
-
// Interceptar APIs se configurado
|
|
1790
|
-
if (this.config.apiPatterns) {
|
|
1791
|
-
page.on('response', async (response) => {
|
|
1792
|
-
const url = response.url();
|
|
1793
|
-
const matches = this.config.apiPatterns!.some(p => p.test(url));
|
|
1794
|
-
|
|
1795
|
-
if (matches) {
|
|
1796
|
-
try {
|
|
1797
|
-
const body = await response.json();
|
|
1798
|
-
apiData.push(body);
|
|
1799
|
-
} catch {}
|
|
1800
|
-
}
|
|
1801
|
-
});
|
|
1802
|
-
}
|
|
1803
|
-
|
|
1804
|
-
// Navegar
|
|
1805
|
-
await page.goto(this.config.url, { waitUntil: 'networkidle' });
|
|
1806
|
-
|
|
1807
|
-
// Scroll se configurado
|
|
1808
|
-
if (this.config.scroll) {
|
|
1809
|
-
await page.evaluate(async () => {
|
|
1810
|
-
await new Promise(resolve => {
|
|
1811
|
-
let totalHeight = 0;
|
|
1812
|
-
const distance = 500;
|
|
1813
|
-
const timer = setInterval(() => {
|
|
1814
|
-
window.scrollBy(0, distance);
|
|
1815
|
-
totalHeight += distance;
|
|
1816
|
-
if (totalHeight >= document.body.scrollHeight) {
|
|
1817
|
-
clearInterval(timer);
|
|
1818
|
-
resolve(undefined);
|
|
1819
|
-
}
|
|
1820
|
-
}, 200);
|
|
1821
|
-
});
|
|
1822
|
-
});
|
|
1823
|
-
}
|
|
1824
|
-
|
|
1825
|
-
// Aguardar se configurado
|
|
1826
|
-
if (this.config.waitFor) {
|
|
1827
|
-
await page.waitForTimeout(this.config.waitFor);
|
|
1828
|
-
}
|
|
1829
|
-
|
|
1830
|
-
// Extrair HTML
|
|
1831
|
-
const html = await page.content();
|
|
1832
|
-
await browser.close();
|
|
1833
|
-
|
|
1834
|
-
// Extrair conteúdo
|
|
1835
|
-
const extractor = new ContentExtractor({
|
|
1836
|
-
url: this.config.url,
|
|
1837
|
-
extractJsonLd: true,
|
|
1838
|
-
extractOpenGraph: true,
|
|
1839
|
-
extractBreadcrumbs: true,
|
|
1840
|
-
extractReadable: true,
|
|
1841
|
-
htmlToMarkdown: true,
|
|
1842
|
-
});
|
|
1843
|
-
|
|
1844
|
-
const extracted = await extractor.extract(html);
|
|
1845
|
-
|
|
1846
|
-
return { html, apiData, extracted };
|
|
1847
|
-
}
|
|
1848
|
-
}
|
|
1849
|
-
|
|
1850
|
-
// Usar
|
|
1851
|
-
const scraper = new SmartScraper({
|
|
1852
|
-
url: 'https://example.com/products',
|
|
1853
|
-
apiPatterns: [/\/api\/products/],
|
|
1854
|
-
scroll: true,
|
|
1855
|
-
waitFor: 2000,
|
|
1856
|
-
});
|
|
1857
|
-
|
|
1858
|
-
const result = await scraper.scrape();
|
|
1859
|
-
console.log(`HTML length: ${result.html.length}`);
|
|
1860
|
-
console.log(`API calls intercepted: ${result.apiData.length}`);
|
|
1861
|
-
console.log(`Title: ${result.extracted.title}`);
|
|
1862
|
-
console.log(`Content length: ${result.extracted.content.length}`);
|
|
1863
|
-
```
|
|
1864
|
-
|
|
1865
|
-
### Exemplo 3: Batch Extraction com Cache
|
|
1866
|
-
|
|
1867
|
-
```ts
|
|
1868
|
-
import { LRUCache } from 'lru-cache';
|
|
1869
|
-
import { ContentExtractor, ContentExtractorResult } from './content-extractor';
|
|
1870
|
-
|
|
1871
|
-
class BatchExtractor {
|
|
1872
|
-
private cache: LRUCache<string, ContentExtractorResult>;
|
|
1873
|
-
private extractor: ContentExtractor;
|
|
1874
|
-
|
|
1875
|
-
constructor(options: { cacheSize?: number; cacheTtl?: number } = {}) {
|
|
1876
|
-
this.cache = new LRUCache({
|
|
1877
|
-
max: options.cacheSize ?? 1000,
|
|
1878
|
-
ttl: options.cacheTtl ?? 1000 * 60 * 30, // 30 min
|
|
1879
|
-
});
|
|
1880
|
-
|
|
1881
|
-
this.extractor = new ContentExtractor({
|
|
1882
|
-
url: '',
|
|
1883
|
-
extractJsonLd: true,
|
|
1884
|
-
extractOpenGraph: true,
|
|
1885
|
-
extractReadable: true,
|
|
1886
|
-
htmlToMarkdown: true,
|
|
1887
|
-
});
|
|
1888
|
-
}
|
|
1889
|
-
|
|
1890
|
-
async extract(url: string, html: string): Promise<ContentExtractorResult> {
|
|
1891
|
-
const cached = this.cache.get(url);
|
|
1892
|
-
if (cached) {
|
|
1893
|
-
console.log(`Cache HIT: ${url}`);
|
|
1894
|
-
return cached;
|
|
1895
|
-
}
|
|
1896
|
-
|
|
1897
|
-
console.log(`Cache MISS: ${url}`);
|
|
1898
|
-
this.extractor = new ContentExtractor({ url, extractJsonLd: true, extractOpenGraph: true, extractReadable: true, htmlToMarkdown: true });
|
|
1899
|
-
const result = await this.extractor.extract(html);
|
|
1900
|
-
|
|
1901
|
-
this.cache.set(url, result);
|
|
1902
|
-
return result;
|
|
1903
|
-
}
|
|
1904
|
-
|
|
1905
|
-
async extractBatch(
|
|
1906
|
-
items: Array<{ url: string; html: string }>,
|
|
1907
|
-
): Promise<ContentExtractorResult[]> {
|
|
1908
|
-
const results: ContentExtractorResult[] = [];
|
|
1909
|
-
|
|
1910
|
-
for (const item of items) {
|
|
1911
|
-
const result = await this.extract(item.url, item.html);
|
|
1912
|
-
results.push(result);
|
|
1913
|
-
}
|
|
1914
|
-
|
|
1915
|
-
return results;
|
|
1916
|
-
}
|
|
1917
|
-
|
|
1918
|
-
stats(): { size: number } {
|
|
1919
|
-
return { size: this.cache.size };
|
|
1920
|
-
}
|
|
1921
|
-
}
|
|
1922
|
-
|
|
1923
|
-
// Usar
|
|
1924
|
-
const batchExtractor = new BatchExtractor({ cacheSize: 5000 });
|
|
1925
|
-
|
|
1926
|
-
const results = await batchExtractor.extractBatch([
|
|
1927
|
-
{ url: 'https://example.com/page1', html: html1 },
|
|
1928
|
-
{ url: 'https://example.com/page2', html: html2 },
|
|
1929
|
-
{ url: 'https://example.com/page1', html: html1 }, // Cache HIT
|
|
1930
|
-
]);
|
|
1931
|
-
|
|
1932
|
-
console.log(`Cache stats:`, batchExtractor.stats());
|
|
1933
|
-
```
|
|
1934
|
-
|
|
1935
|
-
---
|
|
1936
|
-
|
|
1937
|
-
## Referências
|
|
1938
|
-
|
|
1939
|
-
- [Schema.org](https://schema.org)
|
|
1940
|
-
- [JSON-LD Specification](https://www.w3.org/TR/json-ld/)
|
|
1941
|
-
- [OpenGraph Protocol](https://ogp.me/)
|
|
1942
|
-
- [Twitter Card Documentation](https://developer.twitter.com/en/docs/twitter-for-websites/cards/overview/abouts-cards)
|
|
1943
|
-
- [@mozilla/readability npm](https://www.npmjs.com/package/@mozilla/readability)
|
|
1944
|
-
- [turndown npm](https://www.npmjs.com/package/turndown)
|
|
1945
|
-
- [node-html-markdown npm](https://www.npmjs.com/package/node-html-markdown)
|
|
1946
|
-
- [mdream npm](https://www.npmjs.com/package/mdream)
|
|
1947
|
-
- [open-graph-scraper npm](https://www.npmjs.com/package/open-graph-scraper)
|
|
1948
|
-
- [feedsmith npm](https://www.npmjs.com/package/feedsmith)
|
|
1949
|
-
- [rss-parser npm](https://www.npmjs.com/package/rss-parser)
|
|
1950
|
-
- [rss-finder npm](https://www.npmjs.com/package/rss-finder)
|
|
1951
|
-
- [Cheerio Documentation](https://cheerio.js.org/)
|
|
1952
|
-
- [Playwright Network Interception](https://playwright.dev/docs/network)
|