remeda/remeda
github.com/remeda/remeda ↗ · 1116 files · 2.3 MB
Remeda The first "data-first" and "data-last" utility library designed especially for TypeScript.
Overview
Remeda is a TypeScript utility library for developers who want precise inferred types and composable data transformations. Its central mechanism supports both data-first and data-last calls, while pipe combines compatible operations into item-by-item evaluation with early termination. This npm workspace also contains an Astro documentation site generated from library signatures and JSDoc, migration guides, a StackBlitz playground, and brand asset generators. This brief reflects source inspection at commit 9955bb0eca98cc5d1bbb17dc4314dd38a10a28d5; it does not establish executable correctness.
System map
The repository separates the published library from documentation, consumer examples, and brand tooling through npm workspaces. The library runs inside its caller's process, while the website performs content extraction at build or development time and hydrates selected React islands in the browser. State ownership is correspondingly divided between utility invocation state, Astro content storage, generated files, and browser preferences. Distribution includes ESM and CJS library artifacts, a static Netlify site, and automated registry releases.
- The private root package defines four npm workspaces: remeda, docs, stackblitz-template, and brand; its docs:build script invokes workspace builds.
- The library's public barrel exports 163 utility modules, including purry, pipe, piped, funnel, collection transforms, object operations, predicates, and string utilities.
- Consumers import or require the package root through dist/index.js or dist/index.cjs; only dist files are packaged, and the manifest declares sideEffects: false.
- tsdown first bundles declarations from src/index.ts, then runs a separate runtime build with config loading disabled, cleaning disabled, per-utility entries, minification, source maps, attw, and publint.
- The docs site integrates Astro, React, MDX, sitemap generation, and Tailwind; Netlify builds the library and docs workspaces and publishes packages/docs/dist.
- Inspected state owners include per-pipe evaluator arrays, funnel closure timers and reduced data, Astro's content store, and the browser's theme localStorage entry.
- Library scripts expose source and distribution type checks, three Vitest projects, coverage collection, tsdown builds, JSR publishing, and pkg-pr-new previews using the StackBlitz template.
- Main-branch releases depend on type, runtime, property, lint, formatting, and slow-type jobs; semantic-release publishes artifacts, followed by conditional signing, provenance attestation, and JSR publishing.
Feature catalog
The primary user-facing product is the exported utility API, spanning arrays, objects, strings, arithmetic, predicates, and function composition. Selected operations offer stronger output typing and lazy pipeline execution in addition to their runtime transformations. The website presents generated API documentation, migration content, search, and theme controls. Developer-facing companion features include a console playground, PR preview template, and reproducible brand assets.
- map returns a newly mapped array with a Mapped<T,U> output type; filter supports type predicates and FilteredArray outputs; both expose eager and lazy implementations.
- Array APIs also export chunking, flattening, set operations, sorting, ranking, sampling, slicing, partitioning, and zipping; groupBy specifically permits undefined keys to exclude items.
- Object APIs include pick, omit, merge, entries, and path operations; prop accepts variadic keys with nullish early return, while setPath clones each traversed object or array branch.
- The public API exports arithmetic and aggregation, random number/string helpers, case conversion, trimming-related truncation, and type predicates; conditional evaluates the first matching case and throws when no case or fallback matches.
- funnel provides configurable debouncing, throttling, and batching through triggerAt, minQuietPeriodMs, maxBurstDurationMs, minGapMs, and an optional reducer; callers receive call, cancel, flush, and isIdle.
- Website routes include the homepage, a single-page function reference with anchors, generated per-library migration pages, and legacy V1 documentation; /v1 redirects to /migrate/v1.
- The header provides Algolia DocSearch and light/dark/system selection; missing search results generate a GitHub issue URL, and function cards display calling-style signatures, parameters, return labels, examples, and source links.
- The website exposes Remeda as window.R for console experimentation, the StackBlitz start script loads exports into a Node REPL, and brand scripts generate SVG lockups plus favicon, social-preview, and avatar assets.
Data flow
Library input crosses an overload boundary into either immediate execution or a curried function carrying optional lazy metadata. Pipeline transformations keep their working state within the invocation and produce arrays or single values without a persistence service. Documentation follows a separate path from TypeScript reflections through validated Astro records into rendered HTML. Browser theme preferences form the concrete persistent user-state path inspected here.
- purry computes fn.length minus args.length: zero invokes fn immediately, one creates a data-last wrapper, and any other difference throws Wrong number of arguments.
- Direct map and filter calls delegate to native array methods with the original input array available to callbacks; their data-last functions additionally carry lazy and lazyArgs metadata when constructed through purry.
- pipe instantiates evaluators once per invocation, groups adjacent lazy steps for iterable outputs, accumulates their results, and unwraps the first accumulated item when the terminal evaluator is marked single.
- Each lazy step records its incoming values before calling its evaluator; hasNext controls forwarding, hasMany recursively processes expanded values, and done propagates early termination to the outer iterable loop.
- groupBy invokes its callback with item, index, and full input, skips undefined keys, accumulates nonempty arrays in a null-prototype object, then restores Object.prototype before returning.
- The functions collection resolves the installed workspace package to a real path and passes its tsconfig and src/index.ts to TypeDoc; function reflections are parsed into named, digested Astro store entries.
- getFunctions groups stored functions by the first signature's category tag; cards deduplicate signatures by @signature and place data-last variants last, while description Markdown passes through marked and DOMPurify before HTML insertion.
- Theme selection changes the document's dark class and DocSearch data-theme attribute; explicit light/dark choices persist under localStorage key theme, while system selection removes that entry and consults matchMedia.
Behavior traces
The inspected flows cover documentation startup and rendering, lazy data transformation, browser theme persistence, search navigation, timed callback execution, and playground initialization. These are ordered source walkthroughs rather than observed executions. Branches such as eager pipeline boundaries, TypeDoc watch updates, and system-theme selection materially change behavior. The traces expose where execution state is initialized and where outputs cross into HTML, navigation, timers, or interactive globals.
- Docs startup: Astro loads the functions collection, internalPackageDir resolves remeda, typedocLoader.load bootstraps TypeDoc, and app.convert feeds cleanLoad; with a watcher, convertAndWatch uses cleanLoad initially and incrementalLoad thereafter.
- API rendering: docs.astro calls getFunctions, gathers internal function names, constructs navbar entries, and renders FunctionCard; each card renders the description and deduplicated FunctionApi signatures.
- Lazy primary action: map(callback), filter(predicate), and take(2) each call purry and receive lazy metadata; pipe calls extractLazySequence, processIterable, and processItem until take's remaining counter reports done.
- Pipeline boundary branch: an ordinary function ends a lazy sequence, so preceding operations finish before the next batch; take(0) reports completion only after an item reaches it, allowing one upstream mapper invocation.
- Theme startup and persistence: base.astro loads init-theme.js in the head, init-theme.ts calls initTheme, and initTheme reads storage plus media preference; after hydration, ThemeItem calls switchTheme to update DOM state and storage.
- Search navigation: DocSearch supplies itemUrl to navigate, which retains pathname/query/hash but substitutes location.origin; it defers location.assign by one timer tick only when changing an existing hash on the same pathname.
- Funnel execution: call checks idle state, conditionally reduces arguments, resets a capped burst timer, and optionally invokes immediately; timer-end handlers invoke only after the other timer clears, while cancel discards data and flush calls both end handlers.
- Playground startup: npm start runs interactive.js, start creates a Node REPL, dynamic import loads remeda, Object.assign installs its exports into repl.context, and the script then displays the Remeda> prompt.
Testing signals
Tests and CI were inspected, not executed; no passing result or measured coverage is asserted. Vitest separates runtime, type, and property suites, with representative tests targeting dispatch, lazy execution, callback typing, grouping invariants, and timers. CI adds declared Node and TypeScript compatibility matrices plus distribution checks and security-analysis workflows. This is a bounded inventory, and the docs package explicitly documents a lack of tests.
- Vitest defines runtime for src/**/*.test.ts, types for src/**/*.test-d.ts with typecheck-only mode and ignored source errors, and prop for src/**/*.test-prop.ts; runtime and property projects disable isolation.
- Runtime coverage includes src but excludes type tests, property tests, the public barrel, and internal type utilities; the Claude stop hook supplies explicit 100% thresholds, whereas vitest.config.ts itself specifies no thresholds.
- purry tests assert direct calls, one-missing-argument currying, and wrong-arity errors; pipe tests assert mapper invocation counts, filtering before take, ordinary-function boundaries, zero-count early exit, and multi-value lazy results.
- filter type tests assert predicate narrowing and literal behavior, and distinguish a complete data-first callback tuple from the partially reconstructed tuple passed to data-last callbacks.
- groupBy property tests use @fast-check/vitest to check preservation, computed keys, nonempty groups, calling-style equivalence, relative order, and exclusion of undefined-key items.
- The inspected funnel tests use real sleep intervals with UT=16 milliseconds and Vitest mocks to check leading/trailing execution, argument reduction, and suppression when the reducer returns undefined.
- Library CI declares TypeScript 5.1–7.0 type and distribution checks, Node 18/20/22/24/26 runtime tests, and a subsequent property job; scheduled canaries test next/latest/rc/beta, while Codecov collects and uploads runtime coverage.
- Docs instructions state that the package has no tests; its CI performs library build, Astro checking, syncing, linting, and formatting. Separate CodeQL and Scorecard workflows inspect security and supply-chain signals, without proving browser-flow correctness.
Risk map
The most consequential source boundaries are dynamic utility dispatch, declaration rewriting, browser-loaded dependencies, HTML insertion, and agent hooks that execute commands or write files. These observations do not establish vulnerabilities. Two plausible behavior or maintenance risks are explicitly identified as hypotheses requiring verification. Browser interactions have less executable evidence than the library because the docs package documents no tests.
- purry explicitly describes itself as dynamically typed and depends on fn.length for dispatch; changing implementation arity can therefore alter both calling styles across utilities that delegate to it.
- randomString uses Math.random to select alphanumeric characters and explicitly warns that it is not cryptographically secure; using it for authentication tokens would violate its documented capability.
- Hypothesis: the website console sandbox can differ from the locally built documentation API because every base page loads sandbox.js, which imports an unversioned remeda module from esm.sh rather than the workspace artifact. (inferred)
- Hypothesis: system-preference changes mishandle theme overrides because initTheme's change listener enables dark mode when a stored choice exists, whereas initial selection follows the media query only when storage is absent; no browser reproduction was performed. (inferred)
- Declaration postprocessing relies on regex extraction and an export-statement insertion pattern; it throws when polyfills or an insertion point are missing and also rewrites Unicode separators for older TypeScript compatibility.
- Markdown rendering crosses an explicit set:html boundary protected by marked-to-DOMPurify sanitization; the docs package documents no tests, so this inspection does not establish sanitization regression coverage.
- Agent stop hooks execute commands through spawnSync with inherited process environment and snapshot dirty files beneath an OS temporary directory; CLAUDE_PROJECT_DIR supplies package-root resolution, making this tooling a separate execution and filesystem boundary.
- The post-edit formatter reads and rewrites tool_input.file_path after existence and parser checks; this handler contains no explicit repository-root containment check, so its allowed write boundary depends on the host tool and environment.
Extension points
Remeda grows through explicit exports and utility implementations rather than a runtime plugin registry. Lazy metadata is the protocol connecting utilities to pipe, while overload JSDoc connects public APIs to documentation. Astro collections and mapping directory conventions provide structured content extension points. Static browser scripts and parametric brand generators offer additional narrowly scoped tooling hooks.
- A new utility becomes public by adding an alphabetical export in src/index.ts; contributor guidance couples that addition to runtime/type tests, JSDoc, and applicable migration pages.
- Custom lazy operations integrate through lazy and lazyArgs metadata plus a LazyEvaluator result protocol; pipe recognizes that metadata, including an optional single marker on the lazy factory.
- purryFromLazy supports utilities whose primary implementation is lazy: it manufactures a LazyDefinition and delegates both direct and curried calls to pipe, rejecting unsupported argument counts.
- Overload documentation extends the website through @dataFirst, @dataLast, @lazy, @signature, and @category tags; the TypeDoc configuration explicitly registers custom tags, and cards consume them for grouping and labels.
- src/content.config.ts is the collection registration table; each collection supplies its loader and schema, and the functions schema requires at least one signature plus validated source URLs.
- Migration routes expand from discovered mapping library prefixes; filenames retain case, underscore-prefixed Markdown is excluded, and mappings must sit exactly one library directory below the content root.
- Adding a .ts file under src/scripts plugs into staticScriptsPlugin, which discovers entries and emits stable [name].js files under public/dist/scripts; these scripts require a server restart rather than normal Astro HMR.
- Brand generation reads geometry, color, glyph, and wordmark parameters from spec.ts through generate.ts; verified SVG outputs feed generate-assets.ts for documentation and manually uploaded GitHub surfaces.
Contributor handoff
Start with contributor policy and the target utility's complete public contract before changing implementation. Keep runtime, inferred types, lazy semantics, generated docs, exports, and migration guidance aligned. Tests and CI were inspected, not executed, and this brief is a bounded source inventory rather than comprehensive coverage or a clean bill of health. For this investigation, repository commands remain descriptions only: no installs, tests, builds, hooks, publishing, or file writes were authorized.
- Read AGENTS.md and CONTRIBUTING.md first; PR-directed work requires confirmation of a human operator's personal stake and commitment to participate through review and merge.
- Use the remeda-utility skill for utility or mapping edits, and account for five coupled surfaces: implementation, tests, JSDoc, public exports, and applicable migration mappings.
- Preserve exactOptionalPropertyTypes, inferred API types, and interface/class compatibility; suppressions require explanations and TypeScript error codes rather than untracked casts hiding type problems.
- For an authorized code change, use the package's test:runtime, test:types, and test:prop scripts as relevant; separate runtime and type assertions, cover both calling styles, and red-green verify bug regressions.
- The checks described here were inspected, not executed: Vitest project configuration and CI matrices establish intended checks only, and the docs package's declared lack of tests limits browser-flow assurance.
- Before touching distribution or compiler compatibility, read tsdown.config.ts and the CI matrix: TypeScript 5.1 is the declared minimum, declaration polyfills are injected, and declaration generation must precede runtime generation.
- Do not assume eager and lazy callback contexts are interchangeable: pipe supplies processed prefixes, ordinary callbacks terminate batches, and the existing take(0) test expects one upstream mapper call.
- Review formatter and lint-generated diffs when those hooks are active, restart docs development after static-script edits, and use Remeda's inverted release labels: feat produces patch releases and fix produces minor releases.
Languages
| Language | Files | Share of source |
|---|---|---|
| TypeScript | 616 | 99% |
| Markdown | 412 | docs/config |
| JSON | 18 | docs/config |
| YAML | 10 | docs/config |
| JavaScript | 6 | 1% |
| CSS | 1 | docs/config |
| TOML | 1 | 0% |
Start reading here
Probable source entrypoint: packages/ — inferred from the directory tree and manifest, not the upstream docs.
Source roots
packages/ (1056 files)
Source layout — packages/
| Path | Files |
|---|---|
packages/brand/ | 17 |
packages/docs/ | 298 |
packages/stackblitz-template/ | 12 |
Repository structure
| Top-level path | Files |
|---|---|
packages/ | 1056 |
.agents/ | 19 |
.claude/ | 14 |
.github/ | 11 |
.husky/ | 1 |
Docs found: AGENTS.mdCLAUDE.mdCONTRIBUTING.mdREADME.md
Package
package.json — remeda v2.0.0, 7 declared dependencies.
@vitest/coverage-v8huskylint-stagedprettierprettier-plugin-pkgprettier-plugin-shtypescript-language-server
CI & config entrypoints
README.md— Repository readmeAGENTS.md— Agent instructionsCLAUDE.md— Claude instructions.github/workflows/codecov.yml— GitHub Actions workflow.github/workflows/canaries.yml— GitHub Actions workflow.github/workflows/codeql.yml— GitHub Actions workflow.github/workflows/pr_titles.yml— GitHub Actions workflow.github/workflows/ci-library.yml— GitHub Actions workflow.github/workflows/ci-docs.yml— GitHub Actions workflow.github/workflows/scorecard.yml— GitHub Actions workflow
Structural health signals
Average score 8/10 across 96 analyzed source files; 6 hotspots surfaced.
packages/remeda/src/pick.test-d.ts— 1117 lines, score 5.5 (Large file, Deep nesting)packages/remeda/eslint.config.ts— 584 lines, score 5.5 (Large file, Deep nesting)packages/remeda/src/internal/types/FilteredArray.test-d.ts— 1224 lines, score 6.2 (Large file, Long function/block)
These are deterministic review leads — size, churn, and structural signals — not proof of a bug in this project.
Recent history
9955bb0eca982026-10-01 — chore(deps): bump devalue from 5.9.0 to 5.9.4 (#1445)e8292ddf03f82026-09-16 — chore: reformat repo with recent prettier changes (#1443)b9ec4b0ac5ba2026-09-14 — fix(isNot): resolve trivial fixed-value conditions (#1441)5d7dc5fc95b62026-09-14 — fix(endsWith, startsWith): reject disjoint prefix/suffix instead of disabling the return type (#1439)845c7b17dc282026-09-09 — fix(startsWith, endsWith): reject disjoint literal prefixes/suffixes at compile time (#1434)