sindresorhus/ky#880

Let .extend() replace or remove inherited abort signals

4 files changed +79 −7

Summary

After this change, .extend() callers can drop signals inherited from a parent instance. signal: replaceOption(newSignal) replaces the parent signals, and signal: undefined removes them. Signal merging also now applies only to the root signal option. A nested key such as json.signal no longer gets pulled into the request signal.

In deepMergeInternal, a root signal key that is a replace marker or undefined empties the collected signals array. AbortSignal collection now requires isRoot. The readme and the KyInstance.extend JSDoc document the new behavior. Tests cover replace, removal, the nested JSON case, and an extra check that instance signals still combine with request signals.

Changes

Flow

.extend() and request options pass through deepMergeInternal. Root signal values are collected, cleared, or replaced, then combined into returnValue.signal.
Components and connections

Components

  • ky.extend / request call entry point
  • deepMergeInternal function, changed source/utils/merge.ts
  • signals array data, changed source/utils/merge.ts
  • returnValue.signal (AbortSignal.any or last) data source/utils/merge.ts

Connections

  • ky.extend / request call → deepMergeInternal (option sources)
  • deepMergeInternal → signals array (push / clear root signal)
  • signals array → returnValue.signal (AbortSignal.any or last) (combine)

Risks

0 high · 1 medium · 0 low

  1. Medium

    Per-request signal: undefined now drops instance signal

    source/utils/merge.ts:227–230

    The clear applies to any root-level merge, not only .extend(). The existing test shows request options merge with instance signals through this path. A call like api.get(url, {signal: opts.signal}) with an undefined opts.signal now empties signals. That silently discards the instance-level signal. The old code ignored a non-AbortSignal value here and kept the instance signal.

Test checklist

0 of 5 done