Skip to content

Fetch/Extract/Batch first-class, plus current logo and brand fix - #24

Open
AnderRV wants to merge 6 commits into
mainfrom
act-1546/repositioning
Open

Fetch/Extract/Batch first-class, plus current logo and brand fix#24
AnderRV wants to merge 6 commits into
mainfrom
act-1546/repositioning

Conversation

@AnderRV

@AnderRV AnderRV commented Aug 20, 2026

Copy link
Copy Markdown
Member

Summary

  • fetch()/get() unchanged in behavior; extract(url, config) added — thin typed wrapper over fetch() with the extract param set (defaults to "auto").
  • extract() falls back to Autoparse once on a 402 AUTH010 (domain not yet enabled for the Extract beta), matching the CLI's own extract adapter. Opt out with fallbackToAutoparse: false.
  • client.batch — new module covering the full Conveyor job/run/task lifecycle: submit (open/regular/scheduled), resource handles (JobRef/RunRef/ExportRef + their Handle subclasses), pagination, CSV upload, webhooks, HMAC key rotation, results export, jittered-backoff retries, cost estimation — full parity with the Python SDK's Batch client.
  • Security fix: those batch resource handles stored their client as a plain enumerable field — JSON.stringify(job)/console.log(job) leaked the raw API key. Switched to true #private fields; verified live that nothing beyond jobId/submitResponse is exposed now.
  • Current logo, "Zenrows" brand-spelling fixes, and a wording fix: Extract/Batch are beta, not invite-gated (confirmed live).

Test plan

  • pnpm run build — clean
  • pnpm exec vitest run — 120/120 pass
  • pnpm run check (biome) — clean
  • Live-API battery, not just mocks: fetch, extract (auto with real AUTH010→Autoparse fallback observed, fallback disabled → raw 402), batch.submitOpen() — all verified end to end, plus the key-leak fix re-verified against a real JobRef

AnderRV and others added 4 commits August 19, 2026 23:34
- fetch()/extract() as primary methods; get() kept as a deprecated alias
- New ZenRowsBatchClient (client.batch or standalone) covering the
  Conveyor open/closed job lifecycle: submit, addTasks, closeJob,
  list/get/delete job & run, results, task content. Accepts a baseURL
  override for parity with the Go/Python batch clients.
- Fixed 2 real bugs found while testing against the documented API
  contract: deleteJob/deleteRun threw on the documented 202-empty-body
  response (only 204 was handled); getTaskContent threw parsing raw
  scraped HTML as JSON.
- rerun()/addTasks() corrected to match the real request shape
  (status as query param + Idempotency-Key header; lastBatch option).
- Coverage: batch.ts + index.ts both 100% (44→46 tests).
- Replaced outdated logo SVGs with ZR-FE's current purple wordmark.

Known gap, not addressed here: Python's batch client (58 methods —
scheduling, webhook CRUD, HMAC rotation, CSV upload, exports,
pagination iterators) is far more complete than this 13-method
reference. Naming/constructor shape is now reconciled; method-count
parity is not, and needs its own scoped follow-up.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DTXXBERPGEnYiYP4Mt3FjF
…, exports

Ander's feedback: Python's batch client wasn't "more mature" by
accident — it's real, fully-specified design (immutable resource
handles, jittered-backoff retries, RFC 7807 errors, a client-side
cost estimator with an exact rate card). Ported it faithfully rather
than leaving Node at 13 core methods.

Restructured src/batch.ts into src/batch/{client,resources,schedule,
waiters,estimate,errors,transport,download,types}.ts:

- Resource handles matching Python's exact design: JobRef/JobHandle,
  RunRef/RunHandle, ExportRef/ExportHandle, plus CurrentRun and
  ScheduleControls sub-facets. Immutable-snapshot semantics preserved
  — mutating methods return a fresh handle, never mutate in place.
- Typed schedule builders (At/Rate/Calendar/Daily/Weekly/Monthly)
  with the same validation rules as Python (tz-naive datetime
  rejection, full-hour times, valid IANA timezones, day/range checks).
- Retry transport matching Python's exact constants: 250ms * 2^attempt
  backoff, +/-20% jitter, capped at 10s, retries 429/502/503/504 plus
  network errors on idempotent methods (or POST with an Idempotency-
  Key), honors Retry-After.
- RFC 7807 error parsing with code/extras, matching BatchAPIError.
- Cost estimation: exact rate card ported (base=1, js=5, premium=10,
  js+premium=25, auto=[1,25]), same tier-priority and per-task merge
  rules.
- Webhook CRUD, HMAC key rotation, CSV upload (presigned S3 PUT),
  results export + streaming download, task history, cursor-based
  async-generator pagination (iterJobs/iterRuns/iterResults), bulk/
  single downloads with concurrency + the same safety caps
  (maxFiles=100k, maxBytesPerFile=50MiB, etc.).

Coverage: 79.48% -> 92.59% (was thin right after the restructure —
closed gaps in download.ts and resources.ts specifically). 116 tests,
all real (error paths, boundary validation, retry-after-honoring,
immutable-handle-returns-fresh-instance behavior) — not padding.

Fixed one brand-prose miss ("ZenRows Batch API" -> "Zenrows Batch
API" in client.ts's docstring) — everything else in this port was
already class/type identifiers, correctly left as ZenRows-cased
per the existing published-export convention.

Known gap, not addressed here: no tqdm-equivalent progress bars
(UX sugar, not API parity). At's Date handling reads local wall-clock
fields since JS has no naive-datetime concept, unlike Python's
tz-naive datetime type.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DTXXBERPGEnYiYP4Mt3FjF
extract() (mode defaults to "auto") is a domain-gated open beta -
when the target domain isn't enabled yet, the API returns a 402 with
code AUTH010. Catch that and retry once with autoparse: true instead
of returning the error response, matching the CLI's own extract
adapter. Opt out with fallbackToAutoparse: false.

Also: Extract/Batch are beta, not invite-gated - confirmed live, no
enablement step needed. Fixes a wording overclaim in the README.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DTXXBERPGEnYiYP4Mt3FjF
JobRef/RunRef/ExportRef/CurrentRun/ScheduleControls stored their
client as a TS protected/private field - real at runtime, but still a
plain enumerable property. JSON.stringify(job), console.log(job), and
Object.keys(job) all walked into it and dumped the full client
(including apiKey) in plaintext. Switched to true JS #private fields,
which are invisible to all three. Verified live: none of them expose
anything beyond jobId/submitResponse now.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DTXXBERPGEnYiYP4Mt3FjF
@linear

linear Bot commented Aug 20, 2026

Copy link
Copy Markdown

ACT-1546

…e-merge-check

# Conflicts:
#	pnpm-lock.yaml
Targets that need js_render/premium_proxy used to fail extract()
with a 400 REQS002 unless the caller passed those manually - Zoopla
listing pages are a real example. mode: "auto" makes Zenrows escalate
automatically, on both the initial extract attempt and the Autoparse
fallback. Opt out with adaptiveStealth: false.

Verified live: extract(zoopla) now succeeds with zero extra params,
and mode=auto doesn't interfere with AUTH010 detection on domains
that are still gated.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DTXXBERPGEnYiYP4Mt3FjF
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant