* fix: accept empty DataForSEO task results
Treat successful tasks with null items as empty payloads so empty ranked keyword responses do not fail billing validation.
* refactor: simplify DataForSEO null result handling
Add .nullable() to the existing structured result schema instead of
loosening the type to unknown[] and re-parsing in parseTaskItems.
* refactor: simplify backlinks zod parsing with structured result schema
Same pattern as the dataforseoSchemas fix: give taskSchema.result a
structured type with .items instead of z.unknown(), removing the
intermediate resultItemsSchema and two-step parsing in parseItems.
* refactor: replace manual type guards with zod schemas
- progress-kv.ts: replace isCrawledUrlEntry type guard with a zod
schema and use jsonCodec(z.array(...)) instead of parsing unknown
then filtering
- helpers.ts: tighten normalizeIntent param from unknown to
string | null | undefined to match actual call sites
- dataforseoBacklinksSupport.ts: allow null result elements to match
API responses where result contains [null]
* refactor: filter null result elements at the source
Filter out null elements from task.result in postBacklinks so
downstream functions receive clean BacklinksTaskResult[] instead
of (BacklinksTaskResult | null)[].
* save
* fix: use backlinks history for default trends
* simplify backlinks: remove filters, always use history endpoint
Remove the filter UI (status, subdomains, indirect links, exclude internal)
and hardcode defaults across the stack. Replace the conditional
timeseries_summary + timeseries_new_lost_summary fallback with a single
backlinks/history/live call for trend data. This reduces the overview from
5 parallel API calls to 3 and removes all conditional branching.
* improve charts
* fix: refresh backlinks cost docs