Changelog
All notable changes to this project are documented in this file. The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
[Unreleased]
Tooling
- Documentation site: published the existing
docs/*.mdandCHANGELOG.mdto https://ramongr.github.io/op-array via an Astro Starlight project undersite/. The root markdown remains the source of truth and is synced into the site at build time bysite/scripts/sync-docs.mjs. Deploys run from.github/workflows/docs.ymlon every push tomainthat touchesdocs/,CHANGELOG.md, orsite/; PRs run a build smoke check. New scripts:npm run docs:install,docs:dev,docs:build,docs:preview.
[2.2.0] - 2026-05-05
Added
symmetricDifference(left, right): items present in exactly one of the two arrays (xor). Left-first then right ordering, duplicates collapsed viaSet. O(n + m).isSubset(left, right):truewhen every distinct element ofleftis inright. Emptyleftis vacuously a subset. O(n + m).isSuperset(left, right):truewhen every distinct element ofrightis inleft. Anyleftis a superset of[]. O(n + m).isDisjoint(left, right):truewhenleftandrightshare no elements. Either side empty is vacuously disjoint. O(n + m).range(values): difference between the largest and smallest value (a.k.a. extent / spread). Single pass. ThrowsTypeErroron empty input.variance(values, mode?): population ('population', default) or sample ('sample', Bessel’s correction) variance. ThrowsTypeErroron empty input or on single-element input whenmode === 'sample'.standardDeviation(values, mode?):Math.sqrt(variance(values, mode)). Same modes and throw conditions asvariance.quantile(values, q): quantile via linear interpolation between adjacent ordered values (R-7 / Excel / NumPy-default). Generalisesmedian(quantile(values, 0.5)). ThrowsTypeErroron empty input andRangeErrorwhenqis outside[0, 1]or isNaN.cumulativeSum(values): running totals. Returns an array of the same length where each element is the sum of all input values up to and including that index. Returns[]for empty input.minBy(collection, key): item with the smallest numeric value at the dot-delimitedkey. Returnsundefinedon empty input or when no item has a comparable numeric value. Items whose resolved value is missing,NaN, or not a number are excluded; ties resolve to the first occurrence.maxBy(collection, key): symmetric tominBy— item with the largest numeric value at the dot-delimitedkey. Same exclusion rules and tie-breaking.sumBy(collection, key): sum of the numeric values at the dot-delimitedkey. Empty input returns0(matchessum). Strict: throwsTypeErrorif any item is missing the path or resolves to a non-number.NaNpropagates.averageBy(collection, key): arithmetic mean of the numeric values at the dot-delimitedkey. Empty input returnsNaN(matchesaverage). Same strict contract assumBy.
Tooling
release.ymlCHANGELOG-heading validator now accepts the Keep-a-Changelog## [VERSION] - Unreleasedform (previously only bare## [VERSION]was accepted, causing the v2.2 release run to fail at the date-stamp step).- Replaced ESLint +
typescript-eslintwith oxlint. Lint config moved fromeslint.config.jsto.oxlintrc.json. The rule set was tightened:correctness,suspicious, andperfcategories run as errors withdenyWarnings: true, plus a curated list of TypeScript, import, unicorn, and core ESLint rules (no-non-null-assertion,consistent-type-exports,no-param-reassign,prefer-template,import/no-default-export,import/no-cycle,unicorn/prefer-at,unicorn/no-array-sort, etc.). Tests get a small relaxed override. - Bumped
tsconfig.jsonlibtoES2023so the codebase can useArray.prototype.toSorted(Node ≥20 already required at runtime). - CI matrix updated to Node 22 (Maintenance LTS), 24 (Active LTS), and 25 (Current). Node 20 dropped now that it has reached EOL.
- Hardened the milestone-driven release pipeline (
release.yml,tag-on-release-merge.yml,publish-on-tag.yml) so the v2.2 release runs end-to-end without manual intervention:release.ymlattaches the release PR to its (already-closed) milestone via the REST API, aftergh pr create(which only resolves open milestones by title).release.ymlswitched frommise+ Node 22 toactions/setup- node@v4Node 24, matchingpublish-on-tag.ymland avoiding the broken bundled npm in older Node 22.x.x images.tag-on-release-merge.ymlnow explicitly invokespublish-on-tag.ymlviagh workflow runafter pushing the tag (tags pushed usingGITHUB_TOKENcannot fire downstreamon: push: tagstriggers — anti-recursion rule). No PAT or GitHub App token required; npm Trusted Publishing via OIDC still handles registry auth.- Added
docs/release.mddocumenting the pipeline contracts and manual recovery paths for each stage.
Changed
-
Minimum supported Node version is now 22.
package.jsonengines.nodebumped from>=20to>=22now that Node 20 has reached end-of-life. Consumers still on Node 20 will see anEBADENGINEwarning from npm on install. -
compactuses the idiomatic.filter(Boolean) as NonNullable<T>[]. -
unique,union,occurrencesuse spread instead ofArray.from. -
medianusesArray.prototype.toSortedinstead of[...values].sort(...). -
lastusesArray.prototype.at(-1). -
flatdrops a redundant inferable type annotation.These are pure refactors; behaviour is unchanged and existing tests still pass at 100% coverage.
[2.1.0] - 2026-05-04
Added
existsAny(source, items): disjunctive sibling ofexistsAll.equals(left, right): order- and duplicate-insensitive set-equality for arrays.partition(collection, predicate): single-pass split into{ pass, fail }buckets.pluck(collection, key): project a single dot-path field to a flat array of values.keyBy(collection, key): index a collection by a dot-path value into a single-item lookup.groupBy(collection, key): group items by a dot-path value into per-bucket arrays.countBy(collection, key): per-bucket counts indexed by a dot-path value.uniqueBy(values, key): dedupe a collection by a dot-path value, first occurrence wins.
Documentation
- Documented the dot-path convention for every key-taking function in a
new “Conventions” section of the README, with an explicit note that
extractremains top-level-only (deferred to v3 to avoid a v2 breaking change).
Tests
- Added nested-path coverage for
whereto lock in the convention.
Tooling
- Added internal
pathResolver(path)helper insrc/shared/to centralise dot-path access for upcoming*Byhelpers. - Added milestone-driven release automation. Closing a
vMAJOR.MINORmilestone now opens achore(release): MAJOR.MINOR.0PR (auto-merge enabled) which on merge tagsvMAJOR.MINOR.0and triggersnpm publish --provenance --access publicplus a GitHub Release with notes auto-generated from the milestone’s PRs. See.github/workflows/release.yml,tag-on-release-merge.yml, andpublish-on-tag.yml. - Authenticated
npm publishvia npm Trusted Publishing (OIDC) inside thenpm-publishGitHub Environment — no long-livedNPM_TOKENstored in the repo.
[2.0.0] - 2026-05-04
Breaking changes
- Dropped
Array.prototypeextension entirely. All operations are now exported as standalone functions. Import them by name and call them with the array as the first argument:// v1import 'op-array/dist/numerical';[1, 2, 3].sum();// v2import { sum } from 'op-array';sum([1, 2, 3]); - Renamed
subtraction->subtract. - Renamed
isEvenLength->hasEvenLength. first,second,third,lastare now functions instead of getters.existsno longer accepts arrays. UseexistsAllfor the array form.subtract,product, andmediannow throwTypeErroron empty input instead of silently returningundefined/NaN.
Added
- TypeScript source with full
.d.tsdeclarations. - Dual ESM + CJS build via tsup.
- Per-category subpath imports:
op-array/collections,/logical,/numerical,/positional,/transformations. existsAll(arr, items): batch membership check.inGroupsOf(arr, size): split into groups of a fixed size.compactNullish(arr): removes onlynull/undefined.flat(arr, depth?): depth parameter mirroring nativeArray.prototype.flat.
Fixed
medianno longer mutates its input and now sorts numerically (was lexicographic).flatcorrectly handles single-nested arrays and empty arrays.compactuses a clearBooleanpredicate instead of theJSON.stringify(value) !== '{}'workaround.intersection,except,union,uniquenow run in O(n + m) usingSet.
Tooling
- Migrated to TypeScript (strict,
noUncheckedIndexedAccess). - Switched test runner from Jest to Vitest.
- Switched lint config to ESLint v9 flat config +
typescript-eslint. - Node version managed via mise (
mise.toml). - Switched package manager to npm.
- Removed Babel toolchain.
- Switched license file/declaration from ISC to MIT.