* 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
Treat successful DataForSEO keyword responses with null items as empty results so auto mode can fall back cleanly. Simplify the no-results state by removing dead-end actions and top-aligning the empty card.