@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,1350 +0,0 @@
|
|
|
1
|
-
# Pesquisa: Zod Schema Validation para TypeScript (2026)
|
|
2
|
-
|
|
3
|
-
## Sumário
|
|
4
|
-
|
|
5
|
-
1. [Zod v4: O que mudou](#1-zod-v4-o-que-mudou)
|
|
6
|
-
2. [Runtime Validation of Scraping Options](#2-runtime-validation-of-scraping-options)
|
|
7
|
-
3. [Creating Zod Schemas for Core Types](#3-creating-zod-schemas-for-core-types)
|
|
8
|
-
4. [API Input Validation Middleware](#4-api-input-validation-middleware)
|
|
9
|
-
5. [CLI Validation with z.coerce](#5-cli-validation-with-zcoerce)
|
|
10
|
-
6. [Deriving TypeScript Types](#6-deriving-typescript-types)
|
|
11
|
-
7. [Best Practices](#7-best-practices)
|
|
12
|
-
8. [Response Validation Pattern](#8-response-validation-pattern)
|
|
13
|
-
9. [JSON Schema Generation](#9-json-schema-generation)
|
|
14
|
-
10. [Complete Code Examples](#10-complete-code-examples)
|
|
15
|
-
|
|
16
|
-
---
|
|
17
|
-
|
|
18
|
-
## 1. Zod v4: O que mudou
|
|
19
|
-
|
|
20
|
-
### Performance Improvements
|
|
21
|
-
|
|
22
|
-
Zod v4 (released 2025-2026) brings major improvements:
|
|
23
|
-
|
|
24
|
-
| Metric | Zod v3 | Zod v4 | Improvement |
|
|
25
|
-
|--------|--------|--------|-------------|
|
|
26
|
-
| Parse speed | ~1x | ~14x | 14x faster |
|
|
27
|
-
| Bundle size | ~60KB | ~26KB | 2.3x smaller |
|
|
28
|
-
| Type inference | Good | Excellent | Better DX |
|
|
29
|
-
| Error messages | Basic | Rich | More helpful |
|
|
30
|
-
|
|
31
|
-
### Key New Features
|
|
32
|
-
|
|
33
|
-
```typescript
|
|
34
|
-
import { z } from 'zod/v4';
|
|
35
|
-
|
|
36
|
-
// 1. z.strictObject — rejects unknown keys at runtime
|
|
37
|
-
const strictConfig = z.strictObject({
|
|
38
|
-
url: z.string().url(),
|
|
39
|
-
maxRetries: z.number().int().positive(),
|
|
40
|
-
// Unknown keys throw ZodError
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
// 2. .prefault() — default values computed lazily
|
|
44
|
-
const options = z.object({
|
|
45
|
-
concurrency: z.number().default(5),
|
|
46
|
-
timeout: z.number().default(30_000),
|
|
47
|
-
userAgent: z.string().prefault(() => `Bot/${Math.random().toString(36)}`),
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
// 3. z.interface() — proper interface-like semantics
|
|
51
|
-
const IScrapeResult = z.interface({
|
|
52
|
-
url: z.string(),
|
|
53
|
-
title: z.string(),
|
|
54
|
-
links: z.array(z.string()),
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
// 4. Improved error messages
|
|
58
|
-
const schema = z.string().email();
|
|
59
|
-
schema.parse('not-an-email');
|
|
60
|
-
// v3: "Invalid email"
|
|
61
|
-
// v4: "Invalid email: expected string matching email format, received 'not-an-email'"
|
|
62
|
-
|
|
63
|
-
// 5. Pattern matching
|
|
64
|
-
const result = z.union([z.string(), z.number()]).safeParse(true);
|
|
65
|
-
// v4 gives better error: "Invalid input: expected string | number, received boolean"
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
### Migration from v3 to v4
|
|
69
|
-
|
|
70
|
-
```typescript
|
|
71
|
-
// v3
|
|
72
|
-
import { z } from 'zod';
|
|
73
|
-
const schema = z.object({ name: z.string() });
|
|
74
|
-
|
|
75
|
-
// v4 (drop-in with import path change)
|
|
76
|
-
import { z } from 'zod/v4';
|
|
77
|
-
const schema = z.object({ name: z.string() });
|
|
78
|
-
|
|
79
|
-
// For backwards compatibility
|
|
80
|
-
import { z } from 'zod';
|
|
81
|
-
// Zod v4 is backwards compatible with v3 API
|
|
82
|
-
// But v4 has new features only available with 'zod/v4'
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
---
|
|
86
|
-
|
|
87
|
-
## 2. Runtime Validation of Scraping Options
|
|
88
|
-
|
|
89
|
-
### Why Runtime Validation?
|
|
90
|
-
|
|
91
|
-
TypeScript types are erased at compile time. If your scraper receives options from:
|
|
92
|
-
- CLI arguments (strings)
|
|
93
|
-
- API requests (JSON)
|
|
94
|
-
- Config files (YAML/JSON)
|
|
95
|
-
- Environment variables (strings)
|
|
96
|
-
|
|
97
|
-
...you need runtime validation to ensure they match your expected types.
|
|
98
|
-
|
|
99
|
-
### Core Scraping Options Schema
|
|
100
|
-
|
|
101
|
-
```typescript
|
|
102
|
-
import { z } from 'zod';
|
|
103
|
-
|
|
104
|
-
// Enum-like unions
|
|
105
|
-
const HttpMethod = z.enum(['GET', 'POST', 'PUT', 'PATCH', 'DELETE']);
|
|
106
|
-
const ProxyType = z.enum(['http', 'https', 'socks5']);
|
|
107
|
-
const BrowserType = z.enum(['chromium', 'firefox', 'webkit']);
|
|
108
|
-
|
|
109
|
-
// URL with custom validation
|
|
110
|
-
const SafeUrl = z.string()
|
|
111
|
-
.url()
|
|
112
|
-
.refine(
|
|
113
|
-
(url) => {
|
|
114
|
-
try {
|
|
115
|
-
const parsed = new URL(url);
|
|
116
|
-
return ['http:', 'https:'].includes(parsed.protocol);
|
|
117
|
-
} catch {
|
|
118
|
-
return false;
|
|
119
|
-
}
|
|
120
|
-
},
|
|
121
|
-
{ message: 'URL must use http or https protocol' }
|
|
122
|
-
);
|
|
123
|
-
|
|
124
|
-
// Proxy configuration
|
|
125
|
-
const ProxyConfig = z.object({
|
|
126
|
-
url: z.string().url(),
|
|
127
|
-
username: z.string().optional(),
|
|
128
|
-
password: z.string().optional(),
|
|
129
|
-
type: ProxyType.default('http'),
|
|
130
|
-
}).strict();
|
|
131
|
-
|
|
132
|
-
// Header map
|
|
133
|
-
const Headers = z.record(z.string(), z.string()).default({
|
|
134
|
-
'Accept': 'text/html,application/xhtml+xml',
|
|
135
|
-
'Accept-Language': 'en-US,en;q=0.9',
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
// Selector with timeout
|
|
139
|
-
const SelectorConfig = z.object({
|
|
140
|
-
selector: z.string().min(1),
|
|
141
|
-
timeout: z.number().int().positive().default(5000),
|
|
142
|
-
optional: z.boolean().default(false),
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
// Main scrape options — the most important schema
|
|
146
|
-
const ScrapeOptions = z.object({
|
|
147
|
-
// Target
|
|
148
|
-
url: SafeUrl,
|
|
149
|
-
method: HttpMethod.default('GET'),
|
|
150
|
-
headers: Headers,
|
|
151
|
-
|
|
152
|
-
// Browser settings
|
|
153
|
-
browser: BrowserType.default('chromium'),
|
|
154
|
-
headless: z.boolean().default(true),
|
|
155
|
-
viewport: z.object({
|
|
156
|
-
width: z.number().int().min(320).max(7680).default(1920),
|
|
157
|
-
height: z.number().int().min(240).max(4320).default(1080),
|
|
158
|
-
}).default({}),
|
|
159
|
-
|
|
160
|
-
// Timing
|
|
161
|
-
timeout: z.number().int().positive().default(30_000),
|
|
162
|
-
waitForSelector: SelectorConfig.optional(),
|
|
163
|
-
waitAfterLoad: z.number().int().min(0).default(0),
|
|
164
|
-
|
|
165
|
-
// Retry
|
|
166
|
-
maxRetries: z.number().int().min(0).max(10).default(3),
|
|
167
|
-
retryDelay: z.number().int().min(0).default(1000),
|
|
168
|
-
retryBackoff: z.enum(['linear', 'exponential']).default('exponential'),
|
|
169
|
-
|
|
170
|
-
// Proxy
|
|
171
|
-
proxy: ProxyConfig.optional(),
|
|
172
|
-
|
|
173
|
-
// Extraction
|
|
174
|
-
selectors: z.record(z.string(), SelectorConfig).optional(),
|
|
175
|
-
evaluatePage: z.string().optional(), // Serialized function
|
|
176
|
-
|
|
177
|
-
// Session
|
|
178
|
-
sessionId: z.string().uuid().optional(),
|
|
179
|
-
cookies: z.array(z.object({
|
|
180
|
-
name: z.string(),
|
|
181
|
-
value: z.string(),
|
|
182
|
-
domain: z.string().optional(),
|
|
183
|
-
path: z.string().default('/'),
|
|
184
|
-
})).optional(),
|
|
185
|
-
|
|
186
|
-
// Rate limiting
|
|
187
|
-
maxConcurrency: z.number().int().min(1).max(100).default(5),
|
|
188
|
-
requestsPerSecond: z.number().positive().optional(),
|
|
189
|
-
delayBetweenRequests: z.number().int().min(0).optional(),
|
|
190
|
-
|
|
191
|
-
// Output
|
|
192
|
-
format: z.enum(['html', 'text', 'json', 'screenshot']).default('html'),
|
|
193
|
-
screenshotFullPage: z.boolean().default(false),
|
|
194
|
-
}).strict();
|
|
195
|
-
|
|
196
|
-
// Cross-field refinements
|
|
197
|
-
const ScrapeOptionsWithRefinements = ScrapeOptions.refine(
|
|
198
|
-
(opts) => {
|
|
199
|
-
// If using screenshot, must be in headless mode or have specific format
|
|
200
|
-
if (opts.screenshotFullPage && opts.format !== 'screenshot') {
|
|
201
|
-
return false;
|
|
202
|
-
}
|
|
203
|
-
return true;
|
|
204
|
-
},
|
|
205
|
-
{
|
|
206
|
-
message: 'screenshotFullPage requires format="screenshot"',
|
|
207
|
-
path: ['screenshotFullPage'],
|
|
208
|
-
}
|
|
209
|
-
).refine(
|
|
210
|
-
(opts) => {
|
|
211
|
-
// requestsPerSecond and delayBetweenRequests are mutually exclusive
|
|
212
|
-
if (opts.requestsPerSecond && opts.delayBetweenRequests) {
|
|
213
|
-
return false;
|
|
214
|
-
}
|
|
215
|
-
return true;
|
|
216
|
-
},
|
|
217
|
-
{
|
|
218
|
-
message: 'Cannot set both requestsPerSecond and delayBetweenRequests',
|
|
219
|
-
path: ['requestsPerSecond'],
|
|
220
|
-
}
|
|
221
|
-
);
|
|
222
|
-
```
|
|
223
|
-
|
|
224
|
-
### Browser Agent Config Schema
|
|
225
|
-
|
|
226
|
-
```typescript
|
|
227
|
-
const BrowserAgentConfig = z.object({
|
|
228
|
-
// Browser management
|
|
229
|
-
browserPool: z.object({
|
|
230
|
-
minBrowsers: z.number().int().min(1).default(1),
|
|
231
|
-
maxBrowsers: z.number().int().min(1).max(20).default(5),
|
|
232
|
-
maxPagesPerBrowser: z.number().int().min(1).max(50).default(10),
|
|
233
|
-
idleTimeout: z.number().int().min(1000).default(60_000),
|
|
234
|
-
launchTimeout: z.number().int().min(1000).default(30_000),
|
|
235
|
-
}).default({}),
|
|
236
|
-
|
|
237
|
-
// Session management
|
|
238
|
-
sessions: z.object({
|
|
239
|
-
maxSessions: z.number().int().min(1).default(100),
|
|
240
|
-
sessionTimeout: z.number().int().min(60_000).default(3_600_000),
|
|
241
|
-
rotateOnBlock: z.boolean().default(true),
|
|
242
|
-
cookiePersistence: z.boolean().default(true),
|
|
243
|
-
}).default({}),
|
|
244
|
-
|
|
245
|
-
// Anti-detection
|
|
246
|
-
stealth: z.object({
|
|
247
|
-
enabled: z.boolean().default(true),
|
|
248
|
-
randomizeViewport: z.boolean().default(true),
|
|
249
|
-
randomizeTiming: z.boolean().default(true),
|
|
250
|
-
minDelay: z.number().int().min(0).default(500),
|
|
251
|
-
maxDelay: z.number().int().min(0).default(2000),
|
|
252
|
-
humanizeMouse: z.boolean().default(true),
|
|
253
|
-
}).default({}),
|
|
254
|
-
|
|
255
|
-
// Plugin system
|
|
256
|
-
plugins: z.array(z.object({
|
|
257
|
-
name: z.string(),
|
|
258
|
-
enabled: z.boolean().default(true),
|
|
259
|
-
config: z.record(z.unknown()).default({}),
|
|
260
|
-
})).default([]),
|
|
261
|
-
|
|
262
|
-
// Error handling
|
|
263
|
-
errorHandling: z.object({
|
|
264
|
-
maxConsecutiveErrors: z.number().int().min(1).default(5),
|
|
265
|
-
circuitBreakerThreshold: z.number().min(0).max(1).default(0.5),
|
|
266
|
-
circuitBreakerResetTimeout: z.number().int().min(1000).default(60_000),
|
|
267
|
-
onCriticalError: z.enum(['stop', 'skip', 'restart']).default('stop'),
|
|
268
|
-
}).default({}),
|
|
269
|
-
|
|
270
|
-
// Logging
|
|
271
|
-
logging: z.object({
|
|
272
|
-
level: z.enum(['debug', 'info', 'warn', 'error']).default('info'),
|
|
273
|
-
requestLogging: z.boolean().default(true),
|
|
274
|
-
errorLogging: z.boolean().default(true),
|
|
275
|
-
}).default({}),
|
|
276
|
-
}).strict();
|
|
277
|
-
```
|
|
278
|
-
|
|
279
|
-
---
|
|
280
|
-
|
|
281
|
-
## 3. Creating Zod Schemas for Core Types
|
|
282
|
-
|
|
283
|
-
### Data Extraction Schema
|
|
284
|
-
|
|
285
|
-
```typescript
|
|
286
|
-
// Schema for scraped data
|
|
287
|
-
const ScrapedItem = z.object({
|
|
288
|
-
url: z.string().url(),
|
|
289
|
-
title: z.string(),
|
|
290
|
-
description: z.string().optional(),
|
|
291
|
-
content: z.string().optional(),
|
|
292
|
-
links: z.array(z.string().url()).default([]),
|
|
293
|
-
images: z.array(z.object({
|
|
294
|
-
src: z.string().url(),
|
|
295
|
-
alt: z.string().default(''),
|
|
296
|
-
})).default([]),
|
|
297
|
-
metadata: z.record(z.string(), z.unknown()).default({}),
|
|
298
|
-
scrapedAt: z.string().datetime().default(() => new Date().toISOString()),
|
|
299
|
-
});
|
|
300
|
-
|
|
301
|
-
// Schema for structured data extraction
|
|
302
|
-
const ExtractionRule = z.object({
|
|
303
|
-
name: z.string().min(1),
|
|
304
|
-
selector: z.string().min(1),
|
|
305
|
-
type: z.enum(['text', 'html', 'attribute', 'list', 'count']),
|
|
306
|
-
attribute: z.string().optional(), // For type='attribute'
|
|
307
|
-
multiple: z.boolean().default(false),
|
|
308
|
-
required: z.boolean().default(true),
|
|
309
|
-
transform: z.enum(['string', 'number', 'boolean', 'date', 'json']).optional(),
|
|
310
|
-
default: z.unknown().optional(),
|
|
311
|
-
}).refine(
|
|
312
|
-
(rule) => rule.type === 'attribute' ? !!rule.attribute : true,
|
|
313
|
-
{ message: 'attribute name required when type="attribute"', path: ['attribute'] }
|
|
314
|
-
);
|
|
315
|
-
|
|
316
|
-
const ExtractionConfig = z.object({
|
|
317
|
-
items: z.string().min(1), // Container selector
|
|
318
|
-
rules: z.array(ExtractionRule).min(1),
|
|
319
|
-
pagination: z.object({
|
|
320
|
-
nextSelector: z.string().min(1),
|
|
321
|
-
maxPages: z.number().int().min(1).default(10),
|
|
322
|
-
waitForLoad: z.number().int().min(0).default(2000),
|
|
323
|
-
}).optional(),
|
|
324
|
-
}).strict();
|
|
325
|
-
```
|
|
326
|
-
|
|
327
|
-
### Queue and Job Schema
|
|
328
|
-
|
|
329
|
-
```typescript
|
|
330
|
-
const JobPriority = z.enum(['critical', 'high', 'normal', 'low']);
|
|
331
|
-
|
|
332
|
-
const ScrapeJob = z.object({
|
|
333
|
-
id: z.string().uuid().default(() => crypto.randomUUID()),
|
|
334
|
-
url: z.string().url(),
|
|
335
|
-
priority: JobPriority.default('normal'),
|
|
336
|
-
options: ScrapeOptions.partial().default({}),
|
|
337
|
-
metadata: z.record(z.unknown()).default({}),
|
|
338
|
-
createdAt: z.string().datetime().default(() => new Date().toISOString()),
|
|
339
|
-
attempts: z.number().int().min(0).default(0),
|
|
340
|
-
maxAttempts: z.number().int().min(1).default(3),
|
|
341
|
-
status: z.enum(['pending', 'running', 'completed', 'failed', 'cancelled']).default('pending'),
|
|
342
|
-
}).strict();
|
|
343
|
-
|
|
344
|
-
const BatchConfig = z.object({
|
|
345
|
-
jobs: z.array(ScrapeJob).min(1).max(10_000),
|
|
346
|
-
concurrency: z.number().int().min(1).max(100).default(10),
|
|
347
|
-
rateLimit: z.object({
|
|
348
|
-
requestsPerSecond: z.number().positive(),
|
|
349
|
-
burstSize: z.number().int().positive().default(5),
|
|
350
|
-
}).optional(),
|
|
351
|
-
deadline: z.string().datetime().optional(),
|
|
352
|
-
onProgress: z.function().optional(), // Can't validate functions with Zod
|
|
353
|
-
}).strict();
|
|
354
|
-
```
|
|
355
|
-
|
|
356
|
-
---
|
|
357
|
-
|
|
358
|
-
## 4. API Input Validation Middleware
|
|
359
|
-
|
|
360
|
-
### Express/Hono/Fastify Pattern
|
|
361
|
-
|
|
362
|
-
```typescript
|
|
363
|
-
import { z } from 'zod';
|
|
364
|
-
import type { ZodSchema, ZodError } from 'zod';
|
|
365
|
-
import type { Request, Response, NextFunction } from 'express';
|
|
366
|
-
|
|
367
|
-
// Generic validation middleware
|
|
368
|
-
function validate<T extends ZodSchema>(
|
|
369
|
-
schema: T,
|
|
370
|
-
source: 'body' | 'query' | 'params' = 'body'
|
|
371
|
-
) {
|
|
372
|
-
return (req: Request, res: Response, next: NextFunction) => {
|
|
373
|
-
const result = schema.safeParse(req[source]);
|
|
374
|
-
|
|
375
|
-
if (!result.success) {
|
|
376
|
-
const errors = formatZodError(result.error);
|
|
377
|
-
return res.status(400).json({
|
|
378
|
-
error: 'Validation failed',
|
|
379
|
-
details: errors,
|
|
380
|
-
});
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
// Replace with validated/transformed data
|
|
384
|
-
(req as any)[source] = result.data;
|
|
385
|
-
next();
|
|
386
|
-
};
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
// Format ZodError into a readable structure
|
|
390
|
-
function formatZodError(error: ZodError): Array<{
|
|
391
|
-
path: string;
|
|
392
|
-
message: string;
|
|
393
|
-
code: string;
|
|
394
|
-
}> {
|
|
395
|
-
return error.issues.map((issue) => ({
|
|
396
|
-
path: issue.path.join('.'),
|
|
397
|
-
message: issue.message,
|
|
398
|
-
code: issue.code,
|
|
399
|
-
}));
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
// Usage with Express
|
|
403
|
-
import express from 'express';
|
|
404
|
-
|
|
405
|
-
const app = express();
|
|
406
|
-
app.use(express.json());
|
|
407
|
-
|
|
408
|
-
// POST /api/scrape — validates body
|
|
409
|
-
app.post(
|
|
410
|
-
'/api/scrape',
|
|
411
|
-
validate(ScrapeOptions),
|
|
412
|
-
async (req, res) => {
|
|
413
|
-
// req.body is now typed as ScrapeOptions (validated)
|
|
414
|
-
const result = await scrape(req.body);
|
|
415
|
-
res.json(result);
|
|
416
|
-
}
|
|
417
|
-
);
|
|
418
|
-
|
|
419
|
-
// GET /api/jobs/:id — validates params
|
|
420
|
-
app.get(
|
|
421
|
-
'/api/jobs/:id',
|
|
422
|
-
validate(z.object({ id: z.string().uuid() }), 'params'),
|
|
423
|
-
async (req, res) => {
|
|
424
|
-
// req.params.id is validated as UUID
|
|
425
|
-
const job = await getJob(req.params.id);
|
|
426
|
-
res.json(job);
|
|
427
|
-
}
|
|
428
|
-
);
|
|
429
|
-
|
|
430
|
-
// GET /api/jobs — validates query
|
|
431
|
-
app.get(
|
|
432
|
-
'/api/jobs',
|
|
433
|
-
validate(
|
|
434
|
-
z.object({
|
|
435
|
-
page: z.coerce.number().int().min(1).default(1),
|
|
436
|
-
limit: z.coerce.number().int().min(1).max(100).default(20),
|
|
437
|
-
status: z.enum(['pending', 'running', 'completed', 'failed']).optional(),
|
|
438
|
-
}),
|
|
439
|
-
'query'
|
|
440
|
-
),
|
|
441
|
-
async (req, res) => {
|
|
442
|
-
// req.query is validated and coerced
|
|
443
|
-
const jobs = await listJobs(req.query);
|
|
444
|
-
res.json(jobs);
|
|
445
|
-
}
|
|
446
|
-
);
|
|
447
|
-
```
|
|
448
|
-
|
|
449
|
-
### Hono-Specific Pattern
|
|
450
|
-
|
|
451
|
-
```typescript
|
|
452
|
-
import { Hono } from 'hono';
|
|
453
|
-
import { zValidator } from '@hono/zod-validator';
|
|
454
|
-
|
|
455
|
-
const app = new Hono();
|
|
456
|
-
|
|
457
|
-
// Hono has built-in Zod validator
|
|
458
|
-
app.post(
|
|
459
|
-
'/api/scrape',
|
|
460
|
-
zValidator('json', ScrapeOptions, (result, c) => {
|
|
461
|
-
if (!result.success) {
|
|
462
|
-
return c.json({ error: result.error.issues }, 400);
|
|
463
|
-
}
|
|
464
|
-
}),
|
|
465
|
-
async (c) => {
|
|
466
|
-
const body = c.req.valid('json');
|
|
467
|
-
// body is fully typed and validated
|
|
468
|
-
const result = await scrape(body);
|
|
469
|
-
return c.json(result);
|
|
470
|
-
}
|
|
471
|
-
);
|
|
472
|
-
```
|
|
473
|
-
|
|
474
|
-
### Fastify Pattern
|
|
475
|
-
|
|
476
|
-
```typescript
|
|
477
|
-
import Fastify from 'fastify';
|
|
478
|
-
import { Type } from '@sinclair/typebox';
|
|
479
|
-
import { Value } from '@sinclair/typebox';
|
|
480
|
-
|
|
481
|
-
// Or use Zod with fastify-type-provider-zod
|
|
482
|
-
import { ZodTypeProvider } from 'fastify-type-provider-zod';
|
|
483
|
-
import { z } from 'zod';
|
|
484
|
-
|
|
485
|
-
const fastify = Fastify().withTypeProvider<ZodTypeProvider>();
|
|
486
|
-
|
|
487
|
-
fastify.post('/api/scrape', {
|
|
488
|
-
schema: {
|
|
489
|
-
body: ScrapeOptions,
|
|
490
|
-
response: {
|
|
491
|
-
200: z.object({
|
|
492
|
-
success: z.boolean(),
|
|
493
|
-
data: ScrapedItem,
|
|
494
|
-
}),
|
|
495
|
-
},
|
|
496
|
-
},
|
|
497
|
-
}, async (request, reply) => {
|
|
498
|
-
// request.body is typed
|
|
499
|
-
const result = await scrape(request.body);
|
|
500
|
-
return { success: true, data: result };
|
|
501
|
-
});
|
|
502
|
-
```
|
|
503
|
-
|
|
504
|
-
---
|
|
505
|
-
|
|
506
|
-
## 5. CLI Validation with z.coerce
|
|
507
|
-
|
|
508
|
-
### The Problem
|
|
509
|
-
|
|
510
|
-
CLI arguments are always strings. `z.coerce` converts them to the correct type during validation.
|
|
511
|
-
|
|
512
|
-
```typescript
|
|
513
|
-
import { z } from 'zod';
|
|
514
|
-
|
|
515
|
-
// CLI argument schema
|
|
516
|
-
const CliOptions = z.object({
|
|
517
|
-
// String arguments (no coercion needed)
|
|
518
|
-
url: z.string().url('Invalid URL'),
|
|
519
|
-
output: z.string().default('./output'),
|
|
520
|
-
|
|
521
|
-
// Coerced number arguments
|
|
522
|
-
concurrency: z.coerce.number()
|
|
523
|
-
.int()
|
|
524
|
-
.min(1, 'Concurrency must be at least 1')
|
|
525
|
-
.max(100, 'Concurrency cannot exceed 100')
|
|
526
|
-
.default(5),
|
|
527
|
-
|
|
528
|
-
timeout: z.coerce.number()
|
|
529
|
-
.int()
|
|
530
|
-
.min(1000, 'Timeout must be at least 1000ms')
|
|
531
|
-
.default(30_000),
|
|
532
|
-
|
|
533
|
-
maxRetries: z.coerce.number()
|
|
534
|
-
.int()
|
|
535
|
-
.min(0)
|
|
536
|
-
.max(10)
|
|
537
|
-
.default(3),
|
|
538
|
-
|
|
539
|
-
// Coerced boolean arguments
|
|
540
|
-
headless: z.coerce.boolean().default(true),
|
|
541
|
-
verbose: z.coerce.boolean().default(false),
|
|
542
|
-
followRedirects: z.coerce.boolean().default(true),
|
|
543
|
-
|
|
544
|
-
// Enum arguments
|
|
545
|
-
browser: z.enum(['chromium', 'firefox', 'webkit']).default('chromium'),
|
|
546
|
-
format: z.enum(['json', 'csv', 'html']).default('json'),
|
|
547
|
-
|
|
548
|
-
// Date coercion
|
|
549
|
-
since: z.coerce.date().optional(),
|
|
550
|
-
|
|
551
|
-
// Array arguments (comma-separated)
|
|
552
|
-
urls: z.string().transform((val) => val.split(',').map(u => u.trim())),
|
|
553
|
-
}).strict();
|
|
554
|
-
|
|
555
|
-
// Parse CLI args
|
|
556
|
-
function parseCliArgs(argv: string[]): z.infer<typeof CliOptions> {
|
|
557
|
-
// Simple arg parser (use commander/yargs in production)
|
|
558
|
-
const args: Record<string, string> = {};
|
|
559
|
-
|
|
560
|
-
for (let i = 2; i < argv.length; i++) {
|
|
561
|
-
const arg = argv[i];
|
|
562
|
-
if (arg.startsWith('--')) {
|
|
563
|
-
const [key, value] = arg.slice(2).split('=');
|
|
564
|
-
args[key] = value ?? argv[++i] ?? 'true';
|
|
565
|
-
}
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
const result = CliOptions.safeParse(args);
|
|
569
|
-
|
|
570
|
-
if (!result.success) {
|
|
571
|
-
console.error('Invalid arguments:');
|
|
572
|
-
for (const issue of result.error.issues) {
|
|
573
|
-
console.error(` --${issue.path.join('.')}: ${issue.message}`);
|
|
574
|
-
}
|
|
575
|
-
process.exit(1);
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
return result.data;
|
|
579
|
-
}
|
|
580
|
-
|
|
581
|
-
// Usage
|
|
582
|
-
const options = parseCliArgs(process.argv);
|
|
583
|
-
console.log(`Scraping ${options.url} with ${options.concurrency} concurrent requests`);
|
|
584
|
-
```
|
|
585
|
-
|
|
586
|
-
### Commander.js Integration
|
|
587
|
-
|
|
588
|
-
```typescript
|
|
589
|
-
import { Command } from 'commander';
|
|
590
|
-
import { z } from 'zod';
|
|
591
|
-
|
|
592
|
-
const program = new Command();
|
|
593
|
-
|
|
594
|
-
const scrapeSchema = z.object({
|
|
595
|
-
url: z.string().url(),
|
|
596
|
-
concurrency: z.coerce.number().int().min(1).max(100).default(5),
|
|
597
|
-
timeout: z.coerce.number().int().min(1000).default(30_000),
|
|
598
|
-
headless: z.coerce.boolean().default(true),
|
|
599
|
-
browser: z.enum(['chromium', 'firefox', 'webkit']).default('chromium'),
|
|
600
|
-
output: z.string().default('./output'),
|
|
601
|
-
verbose: z.coerce.boolean().default(false),
|
|
602
|
-
});
|
|
603
|
-
|
|
604
|
-
program
|
|
605
|
-
.name('scraper')
|
|
606
|
-
.description('Web scraper CLI')
|
|
607
|
-
.version('1.0.0')
|
|
608
|
-
.argument('<url>', 'URL to scrape')
|
|
609
|
-
.option('-c, --concurrency <number>', 'Concurrent requests', '5')
|
|
610
|
-
.option('-t, --timeout <number>', 'Request timeout in ms', '30000')
|
|
611
|
-
.option('--no-headless', 'Run browser in headed mode')
|
|
612
|
-
.option('-b, --browser <type>', 'Browser type', 'chromium')
|
|
613
|
-
.option('-o, --output <dir>', 'Output directory', './output')
|
|
614
|
-
.option('-v, --verbose', 'Verbose output')
|
|
615
|
-
.action(async (url, opts) => {
|
|
616
|
-
const result = scrapeSchema.safeParse({ url, ...opts });
|
|
617
|
-
|
|
618
|
-
if (!result.success) {
|
|
619
|
-
console.error('Validation error:');
|
|
620
|
-
result.error.issues.forEach(i => {
|
|
621
|
-
console.error(` ${i.path.join('.')}: ${i.message}`);
|
|
622
|
-
});
|
|
623
|
-
process.exit(1);
|
|
624
|
-
}
|
|
625
|
-
|
|
626
|
-
await runScraper(result.data);
|
|
627
|
-
});
|
|
628
|
-
|
|
629
|
-
program.parse();
|
|
630
|
-
```
|
|
631
|
-
|
|
632
|
-
---
|
|
633
|
-
|
|
634
|
-
## 6. Deriving TypeScript Types
|
|
635
|
-
|
|
636
|
-
### The z.infer Pattern
|
|
637
|
-
|
|
638
|
-
Zod schemas are the source of truth. TypeScript types are derived from them — never the other way around.
|
|
639
|
-
|
|
640
|
-
```typescript
|
|
641
|
-
import { z } from 'zod';
|
|
642
|
-
|
|
643
|
-
// Define the schema
|
|
644
|
-
const ScrapeOptions = z.object({
|
|
645
|
-
url: z.string().url(),
|
|
646
|
-
concurrency: z.number().int().default(5),
|
|
647
|
-
headless: z.boolean().default(true),
|
|
648
|
-
proxy: z.object({
|
|
649
|
-
url: z.string().url(),
|
|
650
|
-
username: z.string().optional(),
|
|
651
|
-
}).optional(),
|
|
652
|
-
});
|
|
653
|
-
|
|
654
|
-
// Derive the TypeScript type
|
|
655
|
-
type ScrapeOptions = z.infer<typeof ScrapeOptions>;
|
|
656
|
-
// {
|
|
657
|
-
// url: string;
|
|
658
|
-
// concurrency: number;
|
|
659
|
-
// headless: boolean;
|
|
660
|
-
// proxy?: { url: string; username?: string } | undefined;
|
|
661
|
-
// }
|
|
662
|
-
|
|
663
|
-
// For input (before defaults are applied)
|
|
664
|
-
type ScrapeOptionsInput = z.input<typeof ScrapeOptions>;
|
|
665
|
-
// { url: string; concurrency?: number; headless?: boolean; proxy?: ... }
|
|
666
|
-
|
|
667
|
-
// For output (after defaults, transforms, refinements)
|
|
668
|
-
type ScrapeOptionsOutput = z.output<typeof ScrapeOptions>;
|
|
669
|
-
// { url: string; concurrency: number; headless: boolean; proxy?: ... }
|
|
670
|
-
```
|
|
671
|
-
|
|
672
|
-
### Extracting Types from Complex Schemas
|
|
673
|
-
|
|
674
|
-
```typescript
|
|
675
|
-
const ScrapedItem = z.object({
|
|
676
|
-
url: z.string().url(),
|
|
677
|
-
title: z.string(),
|
|
678
|
-
data: z.record(z.string(), z.unknown()),
|
|
679
|
-
tags: z.array(z.string()),
|
|
680
|
-
metadata: z.object({
|
|
681
|
-
scrapedAt: z.string().datetime(),
|
|
682
|
-
duration: z.number(),
|
|
683
|
-
status: z.enum(['success', 'partial', 'failed']),
|
|
684
|
-
}),
|
|
685
|
-
});
|
|
686
|
-
|
|
687
|
-
// Full type
|
|
688
|
-
type ScrapedItem = z.infer<typeof ScrapedItem>;
|
|
689
|
-
|
|
690
|
-
// Partial type (all optional)
|
|
691
|
-
const PartialScrapedItem = ScrapedItem.partial();
|
|
692
|
-
type PartialScrapedItem = z.infer<typeof PartialScrapedItem>;
|
|
693
|
-
|
|
694
|
-
// Pick specific fields
|
|
695
|
-
const ScrapedItemSummary = ScrapedItem.pick({
|
|
696
|
-
url: true,
|
|
697
|
-
title: true,
|
|
698
|
-
metadata: true,
|
|
699
|
-
});
|
|
700
|
-
type ScrapedItemSummary = z.infer<typeof ScrapedItemSummary>;
|
|
701
|
-
|
|
702
|
-
// Omit specific fields
|
|
703
|
-
const ScrapedItemData = ScrapedItem.omit({ metadata: true });
|
|
704
|
-
type ScrapedItemData = z.infer<typeof ScrapedItemData>;
|
|
705
|
-
|
|
706
|
-
// Extend with additional fields
|
|
707
|
-
const ExtendedItem = ScrapedItem.extend({
|
|
708
|
-
screenshot: z.string().optional(),
|
|
709
|
-
pdf: z.string().optional(),
|
|
710
|
-
});
|
|
711
|
-
type ExtendedItem = z.infer<typeof ExtendedItem>;
|
|
712
|
-
```
|
|
713
|
-
|
|
714
|
-
### Utility Types from Zod
|
|
715
|
-
|
|
716
|
-
```typescript
|
|
717
|
-
// Make specific fields required
|
|
718
|
-
const RequiredConfig = BrowserAgentConfig.required({
|
|
719
|
-
browserPool: true,
|
|
720
|
-
sessions: true,
|
|
721
|
-
});
|
|
722
|
-
|
|
723
|
-
// Deep partial (recursive)
|
|
724
|
-
const DeepPartialScrapeOptions = ScrapeOptions.deepPartial();
|
|
725
|
-
type DeepPartialScrapeOptions = z.infer<typeof DeepPartialScrapeOptions>;
|
|
726
|
-
|
|
727
|
-
// Brand types for nominal typing
|
|
728
|
-
const UserId = z.string().uuid().brand<'UserId'>();
|
|
729
|
-
type UserId = z.infer<typeof UserId>;
|
|
730
|
-
|
|
731
|
-
function getUser(id: UserId) { /* ... */ }
|
|
732
|
-
|
|
733
|
-
// ✅ Works
|
|
734
|
-
const id = UserId.parse('550e8400-e29b-41d4-a716-446655440000');
|
|
735
|
-
getUser(id);
|
|
736
|
-
|
|
737
|
-
// ❌ Compile error — can't pass random string
|
|
738
|
-
getUser('550e8400-e29b-41d4-a716-446655440000');
|
|
739
|
-
```
|
|
740
|
-
|
|
741
|
-
---
|
|
742
|
-
|
|
743
|
-
## 7. Best Practices
|
|
744
|
-
|
|
745
|
-
### Validate at Boundaries, Trust Internally
|
|
746
|
-
|
|
747
|
-
```typescript
|
|
748
|
-
// ❌ BAD: Validating everywhere
|
|
749
|
-
function processItem(item: ScrapedItem) {
|
|
750
|
-
const validated = ScrapedItem.parse(item); // Unnecessary!
|
|
751
|
-
return validated.title;
|
|
752
|
-
}
|
|
753
|
-
|
|
754
|
-
// ✅ GOOD: Validate once at the boundary
|
|
755
|
-
async function scrapeAndProcess(url: string): Promise<ProcessedItem> {
|
|
756
|
-
// Boundary: incoming data from external source
|
|
757
|
-
const html = await fetch(url).then(r => r.text());
|
|
758
|
-
const raw = extractData(html); // Returns unknown
|
|
759
|
-
|
|
760
|
-
// Validate once
|
|
761
|
-
const item = ScrapedItem.parse(raw);
|
|
762
|
-
|
|
763
|
-
// Trust internally — no more validation needed
|
|
764
|
-
return processItem(item);
|
|
765
|
-
}
|
|
766
|
-
|
|
767
|
-
function processItem(item: ScrapedItem): ProcessedItem {
|
|
768
|
-
// item is guaranteed to be ScrapedItem
|
|
769
|
-
return { title: item.title.toUpperCase() };
|
|
770
|
-
}
|
|
771
|
-
```
|
|
772
|
-
|
|
773
|
-
### Use safeParse at Boundaries
|
|
774
|
-
|
|
775
|
-
```typescript
|
|
776
|
-
// For operations that shouldn't throw
|
|
777
|
-
function tryParseScrapeOptions(input: unknown): ScrapeOptions | null {
|
|
778
|
-
const result = ScrapeOptions.safeParse(input);
|
|
779
|
-
if (result.success) {
|
|
780
|
-
return result.data;
|
|
781
|
-
}
|
|
782
|
-
console.error('Invalid options:', result.error.format());
|
|
783
|
-
return null;
|
|
784
|
-
}
|
|
785
|
-
|
|
786
|
-
// For API responses
|
|
787
|
-
async function fetchAndValidate<T extends z.ZodSchema>(
|
|
788
|
-
url: string,
|
|
789
|
-
schema: T
|
|
790
|
-
): Promise<z.infer<T>> {
|
|
791
|
-
const res = await fetch(url);
|
|
792
|
-
const json = await res.json();
|
|
793
|
-
|
|
794
|
-
const result = schema.safeParse(json);
|
|
795
|
-
if (!result.success) {
|
|
796
|
-
throw new ScraperError('Invalid API response', {
|
|
797
|
-
code: 'RESPONSE_VALIDATION_FAILED',
|
|
798
|
-
metadata: { url, errors: result.error.issues },
|
|
799
|
-
});
|
|
800
|
-
}
|
|
801
|
-
|
|
802
|
-
return result.data;
|
|
803
|
-
}
|
|
804
|
-
```
|
|
805
|
-
|
|
806
|
-
### Compose Schemas
|
|
807
|
-
|
|
808
|
-
```typescript
|
|
809
|
-
// Build complex schemas from simple ones
|
|
810
|
-
const BaseEntity = z.object({
|
|
811
|
-
id: z.string().uuid(),
|
|
812
|
-
createdAt: z.string().datetime(),
|
|
813
|
-
updatedAt: z.string().datetime(),
|
|
814
|
-
});
|
|
815
|
-
|
|
816
|
-
const ScrapedPage = BaseEntity.extend({
|
|
817
|
-
url: z.string().url(),
|
|
818
|
-
title: z.string(),
|
|
819
|
-
content: z.string(),
|
|
820
|
-
links: z.array(z.string().url()),
|
|
821
|
-
});
|
|
822
|
-
|
|
823
|
-
const ScrapedProduct = BaseEntity.extend({
|
|
824
|
-
url: z.string().url(),
|
|
825
|
-
name: z.string(),
|
|
826
|
-
price: z.number().positive(),
|
|
827
|
-
inStock: z.boolean(),
|
|
828
|
-
images: z.array(z.string().url()),
|
|
829
|
-
});
|
|
830
|
-
|
|
831
|
-
// Discriminated union for different scraped types
|
|
832
|
-
const ScrapedEntity = z.discriminatedUnion('type', [
|
|
833
|
-
ScrapedPage.extend({ type: z.literal('page') }),
|
|
834
|
-
ScrapedProduct.extend({ type: z.literal('product') }),
|
|
835
|
-
]);
|
|
836
|
-
```
|
|
837
|
-
|
|
838
|
-
---
|
|
839
|
-
|
|
840
|
-
## 8. Response Validation Pattern
|
|
841
|
-
|
|
842
|
-
### Validating External API Responses
|
|
843
|
-
|
|
844
|
-
```typescript
|
|
845
|
-
import { z } from 'zod';
|
|
846
|
-
|
|
847
|
-
// Schema for a specific API
|
|
848
|
-
const ExternalApiResponse = z.object({
|
|
849
|
-
status: z.enum(['ok', 'error']),
|
|
850
|
-
data: z.object({
|
|
851
|
-
results: z.array(z.object({
|
|
852
|
-
id: z.number(),
|
|
853
|
-
title: z.string(),
|
|
854
|
-
body: z.string(),
|
|
855
|
-
})),
|
|
856
|
-
total: z.number(),
|
|
857
|
-
page: z.number(),
|
|
858
|
-
}).optional(),
|
|
859
|
-
error: z.string().optional(),
|
|
860
|
-
}).refine(
|
|
861
|
-
(res) => res.status === 'ok' ? !!res.data : !!res.error,
|
|
862
|
-
{ message: 'Response must have data when status is ok, or error when status is error' }
|
|
863
|
-
);
|
|
864
|
-
|
|
865
|
-
async function fetchExternalData(query: string) {
|
|
866
|
-
const res = await fetch(`https://api.example.com/search?q=${query}`);
|
|
867
|
-
const json = await res.json();
|
|
868
|
-
|
|
869
|
-
const result = ExternalApiResponse.safeParse(json);
|
|
870
|
-
|
|
871
|
-
if (!result.success) {
|
|
872
|
-
// Log the raw response for debugging
|
|
873
|
-
console.error('Unexpected API response format:', json);
|
|
874
|
-
throw new ScraperError('External API returned unexpected format', {
|
|
875
|
-
code: 'RESPONSE_VALIDATION_FAILED',
|
|
876
|
-
metadata: {
|
|
877
|
-
issues: result.error.issues,
|
|
878
|
-
rawResponse: json,
|
|
879
|
-
},
|
|
880
|
-
});
|
|
881
|
-
}
|
|
882
|
-
|
|
883
|
-
if (result.data.status === 'error') {
|
|
884
|
-
throw new ScraperError(`External API error: ${result.data.error}`, {
|
|
885
|
-
code: 'EXTERNAL_API_ERROR',
|
|
886
|
-
});
|
|
887
|
-
}
|
|
888
|
-
|
|
889
|
-
return result.data.data!;
|
|
890
|
-
}
|
|
891
|
-
```
|
|
892
|
-
|
|
893
|
-
### Validating Scraped HTML Structure
|
|
894
|
-
|
|
895
|
-
```typescript
|
|
896
|
-
// After extracting data from HTML, validate the structure
|
|
897
|
-
const HtmlExtractedData = z.object({
|
|
898
|
-
title: z.string().min(1),
|
|
899
|
-
price: z.string().transform((val) => {
|
|
900
|
-
const num = parseFloat(val.replace(/[^0-9.]/g, ''));
|
|
901
|
-
if (isNaN(num)) throw new Error(`Invalid price: ${val}`);
|
|
902
|
-
return num;
|
|
903
|
-
}),
|
|
904
|
-
rating: z.string().transform((val) => {
|
|
905
|
-
const num = parseFloat(val);
|
|
906
|
-
if (num < 0 || num > 5) throw new Error(`Rating out of range: ${val}`);
|
|
907
|
-
return num;
|
|
908
|
-
}),
|
|
909
|
-
reviewCount: z.string().transform((val) => {
|
|
910
|
-
const num = parseInt(val.replace(/[^0-9]/g, ''), 10);
|
|
911
|
-
if (isNaN(num)) throw new Error(`Invalid review count: ${val}`);
|
|
912
|
-
return num;
|
|
913
|
-
}),
|
|
914
|
-
images: z.array(z.string().url()),
|
|
915
|
-
inStock: z.string().transform((val) => {
|
|
916
|
-
const lower = val.toLowerCase();
|
|
917
|
-
return lower.includes('in stock') || lower.includes('available');
|
|
918
|
-
}),
|
|
919
|
-
});
|
|
920
|
-
|
|
921
|
-
// Transform and validate in one step
|
|
922
|
-
function parseProductPage(html: string): z.infer<typeof HtmlExtractedData> {
|
|
923
|
-
const raw = extractFromHtml(html); // Returns unknown
|
|
924
|
-
return HtmlExtractedData.parse(raw); // Throws on invalid data
|
|
925
|
-
}
|
|
926
|
-
```
|
|
927
|
-
|
|
928
|
-
---
|
|
929
|
-
|
|
930
|
-
## 9. JSON Schema Generation
|
|
931
|
-
|
|
932
|
-
### Zod v4: z.toJSONSchema
|
|
933
|
-
|
|
934
|
-
```typescript
|
|
935
|
-
import { z } from 'zod/v4';
|
|
936
|
-
|
|
937
|
-
const ScrapeOptions = z.object({
|
|
938
|
-
url: z.string().url(),
|
|
939
|
-
concurrency: z.number().int().min(1).max(100).default(5),
|
|
940
|
-
headless: z.boolean().default(true),
|
|
941
|
-
browser: z.enum(['chromium', 'firefox', 'webkit']).default('chromium'),
|
|
942
|
-
proxy: z.object({
|
|
943
|
-
url: z.string().url(),
|
|
944
|
-
type: z.enum(['http', 'https', 'socks5']).default('http'),
|
|
945
|
-
}).optional(),
|
|
946
|
-
selectors: z.record(z.string(), z.object({
|
|
947
|
-
selector: z.string(),
|
|
948
|
-
type: z.enum(['text', 'html', 'attribute']),
|
|
949
|
-
})).optional(),
|
|
950
|
-
});
|
|
951
|
-
|
|
952
|
-
// Generate JSON Schema
|
|
953
|
-
const jsonSchema = z.toJSONSchema(ScrapeOptions);
|
|
954
|
-
|
|
955
|
-
console.log(JSON.stringify(jsonSchema, null, 2));
|
|
956
|
-
// Output:
|
|
957
|
-
// {
|
|
958
|
-
// "type": "object",
|
|
959
|
-
// "properties": {
|
|
960
|
-
// "url": { "type": "string", "format": "uri" },
|
|
961
|
-
// "concurrency": { "type": "integer", "minimum": 1, "maximum": 100, "default": 5 },
|
|
962
|
-
// "headless": { "type": "boolean", "default": true },
|
|
963
|
-
// "browser": { "type": "string", "enum": ["chromium", "firefox", "webkit"], "default": "chromium" },
|
|
964
|
-
// "proxy": {
|
|
965
|
-
// "type": "object",
|
|
966
|
-
// "properties": {
|
|
967
|
-
// "url": { "type": "string", "format": "uri" },
|
|
968
|
-
// "type": { "type": "string", "enum": ["http", "https", "socks5"], "default": "http" }
|
|
969
|
-
// },
|
|
970
|
-
// "required": ["url"]
|
|
971
|
-
// },
|
|
972
|
-
// "selectors": {
|
|
973
|
-
// "type": "object",
|
|
974
|
-
// "additionalProperties": {
|
|
975
|
-
// "type": "object",
|
|
976
|
-
// "properties": {
|
|
977
|
-
// "selector": { "type": "string" },
|
|
978
|
-
// "type": { "type": "string", "enum": ["text", "html", "attribute"] }
|
|
979
|
-
// },
|
|
980
|
-
// "required": ["selector", "type"]
|
|
981
|
-
// }
|
|
982
|
-
// }
|
|
983
|
-
// },
|
|
984
|
-
// "required": ["url"],
|
|
985
|
-
// "additionalProperties": false
|
|
986
|
-
// }
|
|
987
|
-
```
|
|
988
|
-
|
|
989
|
-
### Use Cases for JSON Schema
|
|
990
|
-
|
|
991
|
-
```typescript
|
|
992
|
-
// 1. Generate OpenAPI docs
|
|
993
|
-
import { z } from 'zod/v4';
|
|
994
|
-
|
|
995
|
-
const apiSchema = z.object({
|
|
996
|
-
body: ScrapeOptions,
|
|
997
|
-
response: z.object({
|
|
998
|
-
success: z.boolean(),
|
|
999
|
-
data: ScrapedItem,
|
|
1000
|
-
}),
|
|
1001
|
-
});
|
|
1002
|
-
|
|
1003
|
-
const jsonSchema = z.toJSONSchema(apiSchema);
|
|
1004
|
-
// Use with swagger-ui or redoc
|
|
1005
|
-
|
|
1006
|
-
// 2. Generate VS Code settings schema
|
|
1007
|
-
const VsCodeSettings = z.object({
|
|
1008
|
-
'scraper.defaultBrowser': z.enum(['chromium', 'firefox', 'webkit']),
|
|
1009
|
-
'scraper.concurrency': z.number().int().min(1).max(100),
|
|
1010
|
-
'scraper.outputDir': z.string(),
|
|
1011
|
-
});
|
|
1012
|
-
|
|
1013
|
-
const vscodeSchema = z.toJSONSchema(VsCodeSettings);
|
|
1014
|
-
// Write to .vscode/settings.schema.json
|
|
1015
|
-
|
|
1016
|
-
// 3. Validate config files at runtime
|
|
1017
|
-
const configSchema = z.object({
|
|
1018
|
-
targets: z.array(z.object({
|
|
1019
|
-
url: z.string().url(),
|
|
1020
|
-
schedule: z.string(), // cron expression
|
|
1021
|
-
})),
|
|
1022
|
-
global: BrowserAgentConfig,
|
|
1023
|
-
});
|
|
1024
|
-
|
|
1025
|
-
// Load and validate config
|
|
1026
|
-
const configPath = path.resolve('scraper.config.json');
|
|
1027
|
-
const configJson = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
1028
|
-
const config = configSchema.parse(configJson);
|
|
1029
|
-
|
|
1030
|
-
// 4. Client-side form validation
|
|
1031
|
-
// Use JSON Schema to generate form fields in React/Vue
|
|
1032
|
-
const formSchema = z.toJSONSchema(ScrapeOptions);
|
|
1033
|
-
// Pass to react-jsonschema-form or similar
|
|
1034
|
-
```
|
|
1035
|
-
|
|
1036
|
-
### OpenAPI Integration
|
|
1037
|
-
|
|
1038
|
-
```typescript
|
|
1039
|
-
import { z } from 'zod/v4';
|
|
1040
|
-
import { createDocument } from 'zod-openapi';
|
|
1041
|
-
|
|
1042
|
-
const app = z.object({
|
|
1043
|
-
openapi: z.literal('3.1.0'),
|
|
1044
|
-
info: z.object({
|
|
1045
|
-
title: 'Scraper API',
|
|
1046
|
-
version: z.string(),
|
|
1047
|
-
}),
|
|
1048
|
-
paths: z.record(z.string(), z.object({
|
|
1049
|
-
post: z.object({
|
|
1050
|
-
requestBody: z.object({
|
|
1051
|
-
content: z.record(z.string(), z.object({
|
|
1052
|
-
schema: z.toJSONSchema(ScrapeOptions),
|
|
1053
|
-
})),
|
|
1054
|
-
}),
|
|
1055
|
-
responses: z.record(z.string(), z.object({
|
|
1056
|
-
description: z.string(),
|
|
1057
|
-
content: z.record(z.string(), z.object({
|
|
1058
|
-
schema: z.toJSONSchema(ScrapedItem),
|
|
1059
|
-
})),
|
|
1060
|
-
})),
|
|
1061
|
-
}),
|
|
1062
|
-
})),
|
|
1063
|
-
});
|
|
1064
|
-
|
|
1065
|
-
// Generate OpenAPI spec
|
|
1066
|
-
const spec = app.parse({
|
|
1067
|
-
openapi: '3.1.0',
|
|
1068
|
-
info: { title: 'Scraper API', version: '1.0.0' },
|
|
1069
|
-
paths: {
|
|
1070
|
-
'/api/scrape': {
|
|
1071
|
-
post: {
|
|
1072
|
-
requestBody: {
|
|
1073
|
-
content: {
|
|
1074
|
-
'application/json': {
|
|
1075
|
-
schema: z.toJSONSchema(ScrapeOptions),
|
|
1076
|
-
},
|
|
1077
|
-
},
|
|
1078
|
-
},
|
|
1079
|
-
responses: {
|
|
1080
|
-
'200': {
|
|
1081
|
-
description: 'Success',
|
|
1082
|
-
content: {
|
|
1083
|
-
'application/json': {
|
|
1084
|
-
schema: z.toJSONSchema(ScrapedItem),
|
|
1085
|
-
},
|
|
1086
|
-
},
|
|
1087
|
-
},
|
|
1088
|
-
},
|
|
1089
|
-
},
|
|
1090
|
-
},
|
|
1091
|
-
},
|
|
1092
|
-
});
|
|
1093
|
-
```
|
|
1094
|
-
|
|
1095
|
-
---
|
|
1096
|
-
|
|
1097
|
-
## 10. Complete Code Examples
|
|
1098
|
-
|
|
1099
|
-
### Full Scraper Config Validation System
|
|
1100
|
-
|
|
1101
|
-
```typescript
|
|
1102
|
-
import { z } from 'zod';
|
|
1103
|
-
|
|
1104
|
-
// ============================================
|
|
1105
|
-
// Schema Definitions
|
|
1106
|
-
// ============================================
|
|
1107
|
-
|
|
1108
|
-
const UrlSchema = z.string().url().refine(
|
|
1109
|
-
(url) => {
|
|
1110
|
-
try {
|
|
1111
|
-
const parsed = new URL(url);
|
|
1112
|
-
return ['http:', 'https:'].includes(parsed.protocol);
|
|
1113
|
-
} catch {
|
|
1114
|
-
return false;
|
|
1115
|
-
}
|
|
1116
|
-
},
|
|
1117
|
-
{ message: 'URL must use http or https protocol' }
|
|
1118
|
-
);
|
|
1119
|
-
|
|
1120
|
-
const ProxySchema = z.object({
|
|
1121
|
-
url: UrlSchema,
|
|
1122
|
-
username: z.string().optional(),
|
|
1123
|
-
password: z.string().optional(),
|
|
1124
|
-
type: z.enum(['http', 'https', 'socks5']).default('http'),
|
|
1125
|
-
}).strict();
|
|
1126
|
-
|
|
1127
|
-
const SelectorSchema = z.object({
|
|
1128
|
-
selector: z.string().min(1, 'Selector cannot be empty'),
|
|
1129
|
-
timeout: z.number().int().min(100).max(60_000).default(5000),
|
|
1130
|
-
optional: z.boolean().default(false),
|
|
1131
|
-
waitForVisible: z.boolean().default(false),
|
|
1132
|
-
}).strict();
|
|
1133
|
-
|
|
1134
|
-
const ScrapeOptionsSchema = z.object({
|
|
1135
|
-
url: UrlSchema,
|
|
1136
|
-
method: z.enum(['GET', 'POST', 'PUT', 'PATCH', 'DELETE']).default('GET'),
|
|
1137
|
-
headers: z.record(z.string(), z.string()).default({
|
|
1138
|
-
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9',
|
|
1139
|
-
'Accept-Language': 'en-US,en;q=0.9',
|
|
1140
|
-
}),
|
|
1141
|
-
body: z.union([z.string(), z.record(z.unknown())]).optional(),
|
|
1142
|
-
browser: z.enum(['chromium', 'firefox', 'webkit']).default('chromium'),
|
|
1143
|
-
headless: z.boolean().default(true),
|
|
1144
|
-
viewport: z.object({
|
|
1145
|
-
width: z.number().int().min(320).max(7680).default(1920),
|
|
1146
|
-
height: z.number().int().min(240).max(4320).default(1080),
|
|
1147
|
-
}).default({}),
|
|
1148
|
-
timeout: z.number().int().min(1000).max(300_000).default(30_000),
|
|
1149
|
-
waitForSelector: SelectorSchema.optional(),
|
|
1150
|
-
waitAfterLoad: z.number().int().min(0).max(60_000).default(0),
|
|
1151
|
-
maxRetries: z.number().int().min(0).max(10).default(3),
|
|
1152
|
-
retryDelay: z.number().int().min(0).max(60_000).default(1000),
|
|
1153
|
-
retryBackoff: z.enum(['linear', 'exponential']).default('exponential'),
|
|
1154
|
-
proxy: ProxySchema.optional(),
|
|
1155
|
-
selectors: z.record(z.string(), SelectorSchema).optional(),
|
|
1156
|
-
sessionId: z.string().uuid().optional(),
|
|
1157
|
-
cookies: z.array(z.object({
|
|
1158
|
-
name: z.string(),
|
|
1159
|
-
value: z.string(),
|
|
1160
|
-
domain: z.string().optional(),
|
|
1161
|
-
path: z.string().default('/'),
|
|
1162
|
-
})).optional(),
|
|
1163
|
-
maxConcurrency: z.number().int().min(1).max(100).default(5),
|
|
1164
|
-
requestsPerSecond: z.number().positive().optional(),
|
|
1165
|
-
delayBetweenRequests: z.number().int().min(0).max(60_000).optional(),
|
|
1166
|
-
format: z.enum(['html', 'text', 'json', 'screenshot']).default('html'),
|
|
1167
|
-
screenshotFullPage: z.boolean().default(false),
|
|
1168
|
-
}).strict()
|
|
1169
|
-
.refine(
|
|
1170
|
-
(opts) => !(opts.requestsPerSecond && opts.delayBetweenRequests),
|
|
1171
|
-
{ message: 'Cannot set both requestsPerSecond and delayBetweenRequests' }
|
|
1172
|
-
)
|
|
1173
|
-
.refine(
|
|
1174
|
-
(opts) => opts.screenshotFullPage ? opts.format === 'screenshot' : true,
|
|
1175
|
-
{ message: 'screenshotFullPage requires format="screenshot"' }
|
|
1176
|
-
);
|
|
1177
|
-
|
|
1178
|
-
const BatchConfigSchema = z.object({
|
|
1179
|
-
urls: z.array(UrlSchema).min(1).max(10_000),
|
|
1180
|
-
defaultOptions: ScrapeOptionsSchema.partial().default({}),
|
|
1181
|
-
concurrency: z.number().int().min(1).max(100).default(10),
|
|
1182
|
-
rateLimit: z.object({
|
|
1183
|
-
requestsPerSecond: z.number().positive(),
|
|
1184
|
-
burstSize: z.number().int().positive().default(5),
|
|
1185
|
-
}).optional(),
|
|
1186
|
-
deadline: z.string().datetime().optional(),
|
|
1187
|
-
onProgress: z.function().optional(),
|
|
1188
|
-
}).strict();
|
|
1189
|
-
|
|
1190
|
-
const BrowserAgentConfigSchema = z.object({
|
|
1191
|
-
browserPool: z.object({
|
|
1192
|
-
minBrowsers: z.number().int().min(1).default(1),
|
|
1193
|
-
maxBrowsers: z.number().int().min(1).max(20).default(5),
|
|
1194
|
-
maxPagesPerBrowser: z.number().int().min(1).max(50).default(10),
|
|
1195
|
-
idleTimeout: z.number().int().min(1000).default(60_000),
|
|
1196
|
-
launchTimeout: z.number().int().min(1000).default(30_000),
|
|
1197
|
-
}).default({}),
|
|
1198
|
-
sessions: z.object({
|
|
1199
|
-
maxSessions: z.number().int().min(1).default(100),
|
|
1200
|
-
sessionTimeout: z.number().int().min(60_000).default(3_600_000),
|
|
1201
|
-
rotateOnBlock: z.boolean().default(true),
|
|
1202
|
-
}).default({}),
|
|
1203
|
-
stealth: z.object({
|
|
1204
|
-
enabled: z.boolean().default(true),
|
|
1205
|
-
randomizeViewport: z.boolean().default(true),
|
|
1206
|
-
randomizeTiming: z.boolean().default(true),
|
|
1207
|
-
minDelay: z.number().int().min(0).default(500),
|
|
1208
|
-
maxDelay: z.number().int().min(0).default(2000),
|
|
1209
|
-
}).default({}),
|
|
1210
|
-
errorHandling: z.object({
|
|
1211
|
-
maxConsecutiveErrors: z.number().int().min(1).default(5),
|
|
1212
|
-
circuitBreakerThreshold: z.number().min(0).max(1).default(0.5),
|
|
1213
|
-
circuitBreakerResetTimeout: z.number().int().min(1000).default(60_000),
|
|
1214
|
-
}).default({}),
|
|
1215
|
-
}).strict();
|
|
1216
|
-
|
|
1217
|
-
// ============================================
|
|
1218
|
-
// Validation Utility
|
|
1219
|
-
// ============================================
|
|
1220
|
-
|
|
1221
|
-
class ValidationError extends Error {
|
|
1222
|
-
public readonly issues: z.ZodIssue[];
|
|
1223
|
-
|
|
1224
|
-
constructor(error: z.ZodError) {
|
|
1225
|
-
const message = error.issues
|
|
1226
|
-
.map(i => `${i.path.join('.')}: ${i.message}`)
|
|
1227
|
-
.join('\n');
|
|
1228
|
-
super(`Validation failed:\n${message}`);
|
|
1229
|
-
this.name = 'ValidationError';
|
|
1230
|
-
this.issues = error.issues;
|
|
1231
|
-
}
|
|
1232
|
-
|
|
1233
|
-
toJSON() {
|
|
1234
|
-
return {
|
|
1235
|
-
name: this.name,
|
|
1236
|
-
message: this.message,
|
|
1237
|
-
issues: this.issues.map(i => ({
|
|
1238
|
-
path: i.path.join('.'),
|
|
1239
|
-
message: i.message,
|
|
1240
|
-
code: i.code,
|
|
1241
|
-
})),
|
|
1242
|
-
};
|
|
1243
|
-
}
|
|
1244
|
-
}
|
|
1245
|
-
|
|
1246
|
-
function validate<T extends z.ZodSchema>(schema: T, data: unknown): z.infer<T> {
|
|
1247
|
-
const result = schema.safeParse(data);
|
|
1248
|
-
if (!result.success) {
|
|
1249
|
-
throw new ValidationError(result.error);
|
|
1250
|
-
}
|
|
1251
|
-
return result.data;
|
|
1252
|
-
}
|
|
1253
|
-
|
|
1254
|
-
// ============================================
|
|
1255
|
-
// Usage
|
|
1256
|
-
// ============================================
|
|
1257
|
-
|
|
1258
|
-
// Single URL scrape
|
|
1259
|
-
const options = validate(ScrapeOptionsSchema, {
|
|
1260
|
-
url: 'https://example.com',
|
|
1261
|
-
concurrency: 3,
|
|
1262
|
-
proxy: {
|
|
1263
|
-
url: 'http://proxy.example.com:8080',
|
|
1264
|
-
type: 'http',
|
|
1265
|
-
},
|
|
1266
|
-
});
|
|
1267
|
-
|
|
1268
|
-
// Batch scrape
|
|
1269
|
-
const batch = validate(BatchConfigSchema, {
|
|
1270
|
-
urls: [
|
|
1271
|
-
'https://example.com/page1',
|
|
1272
|
-
'https://example.com/page2',
|
|
1273
|
-
'https://example.com/page3',
|
|
1274
|
-
],
|
|
1275
|
-
concurrency: 5,
|
|
1276
|
-
defaultOptions: {
|
|
1277
|
-
headless: true,
|
|
1278
|
-
maxRetries: 3,
|
|
1279
|
-
},
|
|
1280
|
-
});
|
|
1281
|
-
|
|
1282
|
-
// Browser agent config
|
|
1283
|
-
const agentConfig = validate(BrowserAgentConfigSchema, {
|
|
1284
|
-
browserPool: {
|
|
1285
|
-
maxBrowsers: 3,
|
|
1286
|
-
maxPagesPerBrowser: 15,
|
|
1287
|
-
},
|
|
1288
|
-
stealth: {
|
|
1289
|
-
enabled: true,
|
|
1290
|
-
randomizeTiming: true,
|
|
1291
|
-
},
|
|
1292
|
-
});
|
|
1293
|
-
|
|
1294
|
-
// CLI args validation
|
|
1295
|
-
const cliOptions = validate(
|
|
1296
|
-
z.object({
|
|
1297
|
-
url: z.string().url(),
|
|
1298
|
-
concurrency: z.coerce.number().int().min(1).max(100).default(5),
|
|
1299
|
-
headless: z.coerce.boolean().default(true),
|
|
1300
|
-
verbose: z.coerce.boolean().default(false),
|
|
1301
|
-
}),
|
|
1302
|
-
{
|
|
1303
|
-
url: process.argv[2],
|
|
1304
|
-
concurrency: process.argv[3],
|
|
1305
|
-
headless: process.argv[4],
|
|
1306
|
-
verbose: process.argv[5],
|
|
1307
|
-
}
|
|
1308
|
-
);
|
|
1309
|
-
|
|
1310
|
-
// API request validation (Express middleware)
|
|
1311
|
-
function validateBody<T extends z.ZodSchema>(schema: T) {
|
|
1312
|
-
return (req: any, res: any, next: any) => {
|
|
1313
|
-
const result = schema.safeParse(req.body);
|
|
1314
|
-
if (!result.success) {
|
|
1315
|
-
return res.status(400).json({
|
|
1316
|
-
error: 'Validation failed',
|
|
1317
|
-
details: result.error.issues.map(i => ({
|
|
1318
|
-
path: i.path.join('.'),
|
|
1319
|
-
message: i.message,
|
|
1320
|
-
})),
|
|
1321
|
-
});
|
|
1322
|
-
}
|
|
1323
|
-
req.body = result.data;
|
|
1324
|
-
next();
|
|
1325
|
-
};
|
|
1326
|
-
}
|
|
1327
|
-
|
|
1328
|
-
// app.post('/api/scrape', validateBody(ScrapeOptionsSchema), handler);
|
|
1329
|
-
```
|
|
1330
|
-
|
|
1331
|
-
---
|
|
1332
|
-
|
|
1333
|
-
## Summary: Quick Reference
|
|
1334
|
-
|
|
1335
|
-
| Task | Zod API | Example |
|
|
1336
|
-
|------|---------|---------|
|
|
1337
|
-
| Basic object | `z.object({...})` | `z.object({ url: z.string() })` |
|
|
1338
|
-
| String URL | `z.string().url()` | Validates URL format |
|
|
1339
|
-
| Number with bounds | `z.number().min().max()` | `z.number().int().min(1).max(100)` |
|
|
1340
|
-
| Enum | `z.enum([...])` | `z.enum(['a', 'b', 'c'])` |
|
|
1341
|
-
| Default values | `.default(value)` | `z.number().default(5)` |
|
|
1342
|
-
| Optional | `.optional()` | `z.string().optional()` |
|
|
1343
|
-
| Coerce from string | `z.coerce.number()` | CLI arguments |
|
|
1344
|
-
| Transform | `.transform(fn)` | Parse string to Date |
|
|
1345
|
-
| Refine | `.refine(fn, msg)` | Cross-field validation |
|
|
1346
|
-
| Discriminated union | `z.discriminatedUnion()` | Different response types |
|
|
1347
|
-
| Strict object | `z.strictObject()` | Reject unknown keys |
|
|
1348
|
-
| Derive TS type | `z.infer<typeof schema>` | Type from schema |
|
|
1349
|
-
| Safe parse | `.safeParse(data)` | Returns result, no throw |
|
|
1350
|
-
| Generate JSON Schema | `z.toJSONSchema()` | v4 only |
|