Conversation
5d96742 to
c1767b3
Compare
f25251a to
4301f38
Compare
Additional findings (not anchorable inline — files outside this PR's diff)Security gap: This PR's Confirmed exploitable, not theoretical: const malicious = JSON.parse('{"a":1,"__proto__":{"polluted":"yes"}}');
// JSON.parse creates an own enumerable "__proto__" key (doesn't touch the real prototype)
compact(malicious).polluted // => "yes", via the result object's silently-swapped prototype
Object.keys(compact(malicious)) // => ['a'] — "polluted" isn't even an own key, just resolves through the chainSo Recommend adding the same
|
Welcome to Codecov 🎉Once you merge this PR into your default branch, you're all set! Codecov will compare coverage reports and display results in all future pull requests. ℹ️ You can also turn on project coverage checks and project coverage reporting on Pull Request comment Thanks for integrating Codecov - We've got you covered ☂️ |
Deleted `isEmpty` and all associated tests/examples from the `type` category. The per-category replacements (`array/isEmpty`, `string/isEmpty`, `object/isEmpty`) have been available since 2.0.0 and carry the same JSDoc. Breaking change for v3. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Deleted `safeDate` and `dateToISOString` (both in `date/safeDate.ts`) along with their test file. Replacements available since v1.9.0: - `safeDate` → `ensureDate` - `dateToISOString` → `toISO8601` (date/format) Breaking change for v3. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Removed the `daysDifference` function from `difference.ts`, deleted its dedicated example file, and stripped all daysDifference describe blocks from `.test.ts` and `.spec.ts`. The replacement `difference` (available since 2.0.0) covers the same use-case with a richer API (unit, sign). Breaking change for v3. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Removed the `deepClone` re-export alias from `cloneDeep.ts`. Replacement `cloneDeep` has been the canonical name since the symbol was deprecated (marked `@deprecated` since v1.9.0). Breaking change for v3. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Removed the `deepMerge` re-export alias from `mergeDeep.ts`. Replacement `mergeDeep` has been the canonical name since the symbol was deprecated (marked `@deprecated` since v1.9.0). Breaking change for v3. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Created `helpers/_shared/_unsafeKeys.ts` as the single source of truth for the prototype-pollution guard keys. All consumers (`array/countBy`, `object/map|cloneDeep|mergeDeep|groupBy|set|invert|_types`) now import from `'../_shared/_unsafeKeys.js'`; the file is inlined into each category bundle at compile time so there is no runtime cross-package dependency. Deleted the two duplicated copies `array/_unsafeKeys.ts` and `object/_unsafeKeys.ts`. The build script now explicitly skips any directory whose name starts with `_` to prevent accidental packaging of internal shared directories. Breaking change for v3. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Moved all 141 runtime type-guard files (`is*`) from `helpers/type/` to the new `helpers/guard/` category. The compile-time utility types (`DeepPartial`, `DeepWritable`, `Maybe`) remain in `helpers/type/`, which now has a focused purpose. - New package: `@helpers4/guard` (was `@helpers4/type` for runtime guards) - `helpers/guard/config.json` created - `helpers/type/config.json` updated to reflect compile-time-only purpose Breaking change for v3: consumers of runtime guards must update their import from `@helpers4/type` to `@helpers4/guard`. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The `helpers/type/` category is now exclusively compile-time TypeScript utility types — zero runtime footprint, always imported with `import type`. Existing types kept: - `DeepPartial`, `DeepWritable`, `Maybe` Promoted from internal `object/_types.ts` (standalone, no cross-package dep): - `UnionToIntersection<U>` — collapses A | B | C → A & B & C - `DeepGet<T, Path>` — value type at a path within T - `DeepSet<T, Path, V>` — produces T with value at Path replaced by V (`ParsePath` stays internal to object/ — too path-specific) New utility types added: - `Brand<T, B>` — nominal typing via phantom tag - `Prettify<T>` — flattens intersections for IDE readability - `Nullable<T>` / `Nullish<T>` — `T | null` / `T | null | undefined` - `ValueOf<T>` — union of all value types of an object type - `KeysOfType<T, V>` — keys whose values extend V - `PickByValue<T, V>` / `OmitByValue<T, V>` — filter by value type - `RequiredKeys<T>` / `OptionalKeys<T>` — required/optional key sets Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The inner callbacks of `truthyPromiseOrThrow`, `falsyPromiseOrThrow`, and `meaningPromiseOrThrow` previously typed `data` as `unknown` and returned it with `as T` — an unsound cast invisible at the call site. Fix: type `data` as `T` directly in the returned closure (matching the outer overload signature). No cast needed; TypeScript infers the return correctly. The now-redundant `as T` / `as any[]` / `as object` annotations and their `eslint-disable` suppressions are removed. `meaningPromiseOrThrow` additionally extracts the predicate into a private `isMeaningless(value: unknown): boolean` helper, eliminating all remaining casts in the function body. Only `functional/no-throw-statement` comments remain — they suppress a rule about `throw` style, not a type issue. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
helpers/_shared/ is an internal utility folder — not a publishable category and has no config.json by design. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- simplify repository purpose and stack - refine key commands and rules - remove critical restrictions and organization context
scopes.json had a missing comma making the whole file invalid JSON, breaking any tooling that parses it for commit-scope validation. Also reconciles the list against `git log main..v3 --format=%s`: adds agents, coherency, guard, release, scorecard, security, shared — all used by commits on this branch but absent from the list. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The helper-placement rule still said runtime type predicates go to type/, contradicting the guard/type category split this PR introduces (type predicates -> guard/, compile-time-only utility types -> type/). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The removed daysDifference always rounded to a whole number of days. Its replacement, difference() with the default 'days' unit, returns the exact fractional value instead — silently breaking `=== 0`-style migrations from daysDifference. Documents this explicitly in the JSDoc and locks it in with a test. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Add MIGRATION.md documenting the six breaking changes already merged to v3 (helpers/type -> helpers/guard package split, and five removed deprecated aliases). None of the original refactor commits used the Conventional Commits BREAKING CHANGE footer syntax, so git-cliff's CHANGELOG automation was silently missing all of them - this commit carries the correctly-formatted footer so the next changelog regeneration renders them with the [**BREAKING**] marker. BREAKING CHANGE: @helpers4/type -> @helpers4/guard for runtime type guards (isString, isArray, isDefined, ...); object.deepMerge -> object.mergeDeep; object.deepClone -> object.cloneDeep; date.daysDifference -> date.difference; date.safeDate -> date.ensureDate; date.dateToISOString -> date.toISO8601; type.isEmpty -> array.isEmpty / string.isEmpty / object.isEmpty. See MIGRATION.md. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Unreleased had been stale since 2.0.0-alpha.11 (2026-04-13) - every release from 2.0.0-alpha.14 through 2.1.0, plus the whole v3 branch's work, was missing. Regenerated via the project's own 'pnpm run changelog' (git-cliff), picking up the properly-tagged BREAKING CHANGE footer from the previous commit. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
… finding Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Add a root llms.txt (llmstxt.org format) summarizing packages, conventions, and doc links for LLM/agent consumption. While rebuilding the package list from helpers/*/config.json, found the README's package table was missing 4 already-published packages (@helpers4/ci, @helpers4/commit, @helpers4/id, @helpers4/markdown) — added them. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
AGENTS.md said cross-package imports 'break tree-shaking' as the reason not to merge compact/equalsShallow. That's false and contradicted by 20+ existing cross-category imports already in the codebase (array/sample.ts -> number/clamp, object/diff.ts -> array/equalsDeep + date/compare, etc.) - Rollup inlines them per-package, verified via build/<category>/package.json having no dependencies field. Replaced with the real reason (genuinely different implementations) and documented the actual safe pattern. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Operationalizes CONTRIBUTING.md's 'Creating a new helper' walkthrough as an invokable skill, plus bakes in conventions that live only in AGENTS.md or in this session's history (naming: avoid lodash/math jargon, cross-category imports are safe, per-turn commit authorization). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…in listing) Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Also adds commit/ category benchmarks in the same commit (small batch, same review pass). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Also adds id/ and url/ category benchmarks in the same commit (small batches, same review pass). Skipped observable/combine and observable/combineLatest — thin RxJS operator wrappers, not meaningfully sync-benchable, same exclusion as promise/* wrappers. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…y imports break per-package publishing Tested every category with a real jsr publish --dry-run. The slow types check (id/uuid7 passed cleanly) turned out to be the easy part. The real blocker: 7/18 categories have cross-category imports that Rollup inlines fine for npm but JSR (no bundler, raw source per package) rejects as excluded-module. Documented the exact file list per category and the two viable fix paths (duplicate transitive deps per package vs real JSR-to-JSR deps + import rewriting). No jsr.json or pipeline changes committed - investigation only. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Description
Please include a summary of what this PR does and why it's needed.
Type of Change
Related Issues
Closes #(issue number)
How Has This Been Tested?
Describe the tests you ran and how to reproduce them:
Checklist
Screenshots (if applicable)
Add screenshots for UI changes.
Additional Context
Add any other context about the PR here.