Skip to content

v4.0.0

Latest

Choose a tag to compare

@untemps untemps released this 05 Jun 18:58
· 1 commit to main since this release

4.0.0 (2026-06-05)

  • refactor(dom)!: Use Element.remove() in removeElement (#120) (c15677e)

Bug Fixes

  • array: Return extractByIndices values in indices order (#118) (b3ad542)
  • dom: Avoid unsafe DOMRect cast in createElement (#81) (6fda5d4)
  • dom: Harmonize missing-target behaviour between removeElement and modifyElement (#137) (a6be726)
  • dom: Harmonize null/undefined attribute handling in createElement (#154) (94a2b57)
  • dom: Honor explicit empty-string textContent in createElement (#140) (a3640bb)
  • dom: Reject null and undefined element input in modifyElement and removeElement (#139) (ffcca02)
  • dom: Replace for...in with Object.entries on attributes in modifyElement (#61) (0e30f71)
  • dom: Skip cross-origin stylesheets in getCSSDeclaration (#101) (b5c4094)
  • dom: Snapshot getCSSDeclaration return to prevent live stylesheet mutation (#155) (f0f7f0d)
  • dom: Throw ReferenceError when modifyElement selector matches nothing (#119) (8ca987b)
  • dom: Tolerate null attributes in modifyElement (#138) (fd55f8b)
  • number: Narrow getRandomInteger defaults to safe integer range (#117) (b2e284f)
  • object: Copy non-cloneable values by reference in deepMerge (#99) (f44a2d1)
  • object: Deep clone non-plain-object source values in deepMerge (#79) (c357715)
  • object: Make deepMerge immutable by default, add mutate flag (#60) (d56b2d9)
  • object: Preserve source-side aliasing in deepMerge (#141) (dccdfc6)
  • object: Return false instead of throwing for nullish values in isObject (#86) (10d7ef5)
  • string: Default interpolateLiteral tokens to empty object (#80) (ede8348)
  • string: Escape token keys when building the interpolate regex (#91) (9fcb42c)
  • string: Harmonize nil-value handling between interpolate and interpolateLiteral (#136) (bdf3a13)
  • string: Preserve delimiters in interpolate when token value is nil (#105) (3013b20)
  • string: Replace incomplete escapeDivider regex with full metacharacter set (#63) (264905f)
  • string: Return value unchanged when interpolate has no tokens (#100) (fe2346a)
  • string: Throw RangeError in generateText when dictionary is empty (#62) (e904575)

Code Refactoring

  • object: Infer DeepMerge return type from source and target (#157) (8b38c1c)
  • string: Replace new Function with regex in interpolateLiteral (#59) (2012626)

Features

  • Add debounce and throttle functions to async/ (#64) (467b924)
  • Add deepClone function to object/ (#65) (9172bb5)
  • dom: Match grouped selectors in getCSSDeclaration (#145) (db66049)
  • string: Harmonize missing-key behaviour between interpolate and interpolateLiteral (#124) (119f97e)

Performance Improvements

  • dom: Avoid array spread in resolveClassName reduce (#90) (d8fcb43)

BREAKING CHANGES

  • object: deepMerge no longer returns Record<string, unknown>. Callers that explicitly annotated the result with that type, or that assigned it to a Record<string, unknown>-typed variable, now get a narrower inferred type and must drop the annotation (or widen with as Record<string, unknown> if needed).

(cherry picked from commit 9fd4a40)

  • dom: getCSSDeclaration with returnText = false (the default) now returns a Record<string, string> snapshot instead of the live CSSStyleDeclaration. Callers relying on CSSStyleDeclaration methods (getPropertyValue, setProperty, ...) must either pass returnText = true and parse the string, switch to getComputedStyle, or update the stylesheet through document.styleSheets directly. Mutations on the returned object no longer propagate to the matched rule.

(cherry picked from commit 16c45fc)

  • dom: modifyElement(null), modifyElement(undefined), removeElement(null) and removeElement(undefined) now throw TypeError instead of returning undefined. Callers must either guard the input before calling or wrap the call in try/catch.

(cherry picked from commit e13bd89)

  • dom: removeElement no longer silently returns undefined when the string selector matches no element. It now throws a ReferenceError with the same message format as modifyElement. The null/undefined input path and the detached-element path are unchanged.

(cherry picked from commit b0a590c)

  • string: interpolateLiteral no longer coerces null and undefined token values to the literal strings "null" and "undefined". The placeholder is preserved instead. Non-nil values, including symbols, are still coerced via String(value).

(cherry picked from commit e0aeb05)

  • string: interpolateLiteral no longer throws ReferenceError when a placeholder key is missing from tokens. The placeholder is preserved in the result. Callers relying on the throw must pre-validate the keys against the placeholders found in the input string.

(cherry picked from commit 379b551)

  • removeElement now returns a detached element unchanged instead of undefined. Callers that distinguished 'nothing to remove' from 'element removed' via the undefined return must check the element's parent in advance.

(cherry picked from commit 7588ea0)

  • dom: modifyElement(selector, attrs) now throws ReferenceError when the selector does not match any element. Callers relying on the null return must catch the error or pre-check with document.querySelector.

(cherry picked from commit 941f091)

  • array: extractByIndices result order now follows the indices array. Callers relying on source-order output must sort indices upstream.

(cherry picked from commit 58279e2)

  • number: getRandomInteger() called without arguments now draws from a narrower default range — Math.ceil(Number.MIN_SAFE_INTEGER / 2) to Math.floor(Number.MAX_SAFE_INTEGER / 2) — instead of the previous ±Number.MAX_SAFE_INTEGER. The old full-safe-integer defaults overflowed the (max - min + 1) span and biased the distribution. Callers relying on the previous range must pass the bounds explicitly.

(cherry picked from commit 4c95cf1)

  • string: interpolate now preserves %key% when the token value is null or undefined, instead of returning the bare key.

(cherry picked from commit e55a131)

  • string: generateText now throws a RangeError when dictionary is null or empty instead of producing corrupt output or an implicit TypeError.
    Previously, passing dictionary: null caused an unspecified TypeError deep inside the function (null.length). Passing dictionary: [] silently returned a string composed entirely of the literal word "undefined".
    Both cases now throw RangeError('dictionary must not be empty') at the function entry point via a single !dictionary?.length guard.
    Migration:
    • Replace any reliance on the silent "undefined" output from an empty
      dictionary with explicit error handling.
    • Code catching TypeError for the null dictionary case must now catch
      RangeError instead.

(cherry picked from commit 3453c59)

  • object: deepMerge now returns a new object and no longer mutates its inputs.
    Previously, deepMerge merged source into target in place and returned the same target reference. Both source and target could be silently modified as a side effect.
    The new default behaviour (mutate = false) clones target as the result base via structuredClone — neither source nor target is touched, and the return value is always a distinct object. Source-only object values are also deep-cloned, so the result shares no references with source.
    Migration:
    • Replace any reliance on deepMerge(s, t) === t with the return value.
    • Replace any post-call inspection of t with the return value.
    • Pass mutate = true as the third argument to restore in-place merging.

(cherry picked from commit 0348da8)

  • string: Symbol token values no longer throw — they are stringified via String() (e.g. Symbol('x') → "Symbol(x)"). Previously this was a side-effect of the template literal inside new Function; the new behaviour is explicit and consistent.
    Expression placeholders such as ${foo + bar} or ${obj.prop} are no longer evaluated. The \w+ regex only matches simple identifiers. These were never documented or supported, but silently worked via eval. They now pass through unsubstituted.

(cherry picked from commit a900eb1)