Skip to content

Commit 7d73daa

Browse files
authored
Performance & Tooling Modernization: oxlint, Native Testing, and Documentation Overhaul (#402)
* Add CLAUDE.md and AGENTS.md, replace eslint with oxfmt/oxlint * Remove .cursor directory * Update AGENTS.md with project info, remove c8 dependency * Remove mocha and c8 dependencies, simplify test script * Update oxfmt and oxlint versions * Use Node's native test runner * Replace new Array() with Array.from() to fix lint warnings * Audit fixes: has() TTL check, evict() null guard, lazy default params, setWithEvicted cleanup * Rewrite README.md for new and experienced developers * Add docs/API.md with complete API reference * Update CODE_STYLE_GUIDE.md with actual coding conventions * Update TECHNICAL_DOCUMENTATION.md to reflect actual implementation * Revert TECHNICAL_DOCUMENTATION.md changes * Fix TECHNICAL_DOCUMENTATION.md to accurately reflect implementation * Fix Mathematical Foundation section in TECHNICAL_DOCUMENTATION.md - Add removeFromList definition with all 4 prev/next cases - Expand moveToEnd formula and document 'only node' edge case - Fix TTL validity invariant to handle expiry=0 when ttl=0 * Fix memory leak, stale data, and improve performance - Clear prev/next pointers in delete() and evict() to prevent memory leaks - Fix entries() and values() to not pollute LRU order (access items directly instead of calling get()) - Fix entries() and values() returning stale data with TTL (direct item access instead of get() which can delete expired items) - Fix expiresAt() to return expiry for expired-but-not-yet-deleted items - Optimize setWithEvicted() to avoid redundant has()+set() calls - Remove dead code in moveToEnd() (unreachable edge case) - Remove obsolete moveToEnd edge case test * Update AGENTS.md with common issues and implementation notes * Remove dead code in moveToEnd() * Update lint script to check formatting * Replace fmt script with fix script * Apply formatting fixes * Add coverage script and rename test file * Add tests for 100% line coverage * Add test coverage info to README * Remove unused eslint config * Fix Mathematical Foundation section to match implementation * Update TypeScript definitions to match implementation * Rebuild distribution files * fix: rename cache variables in factory function example to avoid redeclaration * fix: correct values() example comment to reflect input key order * feat: add oxlint.json configuration * feat: add .oxfmtrc.json configuration * feat: update pre-commit hook to run fix, coverage, and stage all changes * build: update dist files * fix: clarify entries() and values() ordering in JSDoc * build: update dist files * docs: add development principles (DRY, YAGNI, SOLID, OWASP) * refactor: extract unlink() to eliminate linked list duplication * build: update dist files * refactor: remove bypass parameter from set() to simplify API * refactor: simplify set() and setWithEvicted() API by removing resetTtl parameter * build: update dist files * docs: sort methods and properties alphabetically in README * docs: add badges, TypeScript example, comparison, and contributing guide * docs: update copyright year to 2026 * docs: fix build badge URL to point to ci.yml * docs: restore coverage badge * docs: update package.json description to match README tagline * docs: optimize package.json keywords for discoverability * chore: update files array to explicitly list lru.d.ts * docs: add contributing guide * docs: update AGENTS.md with correct npm scripts * docs: update AGENTS.md development workflow section * docs: rename ESLint Configuration to Lint Configuration * types: update set() and setWithEvicted() signatures to remove unused parameters * docs: fix TypeScript signatures in TECHNICAL_DOCUMENTATION.md to match types/lru.d.ts * docs: fix TTL reset invariant and expiry formula in technical documentation - Add setWithEvicted() to resetTtl description in Core Components - Update create() formula to show conditional TTL handling (ttl > 0 ? t_now + ttl : 0) - Fix TTL Reset Invariant to remove incorrect bypass parameter reference * perf: remove bypass parameter from evict() to avoid V8 deoptimization - Simplified evict() signature by removing unused bypass parameter - All internal callers already guarantee size > 0 before calling evict() - Reduced code complexity and eliminated unnecessary conditional branch - Updated TypeScript definitions and documentation BREAKING CHANGE: evict() no longer accepts bypass parameter * docs: fix resetTtl description and setWithEvicted return type - Correct resetTtl description: resets TTL on set() updates, not get() - Fix setWithEvicted return type: {key, value, expiry} only (no prev/next) All 54 tests passing. * build: update distribution files * refactor: make unlink() private and update Node.js version requirement - Convert unlink() to private field (#unlink) for true encapsulation - Update engines.node from >=12 to >=14 to support private class fields - All 54 tests passing * refactor: simplify JSDoc by removing @example, @see, @SInCE, @method, @memberof - Removed @example, @see, and @SInCE tags from all method docblocks - Removed @method and @memberof tags (redundant for class methods) - Kept @param, @returns, @throws, @Private, and @constructor where needed - Updated distribution files via build - All 54 tests passing * test: update tests to match current API and add edge case coverage - Remove outdated tests using removed resetTtl parameter from set()/setWithEvicted() - Add tests for garbage collection (prev/next pointer cleanup) - Add tests for first/last pointer consistency after deletions - Add tests for different value types (functions, objects, arrays, null, undefined) - Add tests for unlimited cache edge cases - Add tests for array methods with non-existent keys - Add test verifying get() does NOT reset TTL - Remove object key test (not supported - keys coerced to strings) Total: 69 tests, 14 suites, all passing * docs: update Node.js version requirement to 14+ and fix test runner reference - Update README.md to require Node.js ≥14 (for private class fields) - Update docs/TECHNICAL_DOCUMENTATION.md to reference Node.js test runner instead of mocha
1 parent d3ca4f3 commit 7d73daa

31 files changed

+3634
-5199
lines changed

.cursor/rules/javascript.mdc

Lines changed: 0 additions & 14 deletions
This file was deleted.

.husky/pre-commit

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11

2-
npm test
2+
npm run fix && npm run coverage && git add -A

.oxfmtrc.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"$schema": "./node_modules/oxfmt/configuration_schema.json",
3+
"ignorePatterns": [],
4+
"useTabs": true
5+
}

AGENTS.md

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
# AGENTS.md
2+
3+
## Project Overview
4+
5+
`tiny-lru` is a high-performance, lightweight LRU (Least Recently Used) cache library for JavaScript with O(1) operations and optional TTL support.
6+
7+
## Setup Commands
8+
9+
```bash
10+
npm install # Install dependencies
11+
npm run build # Lint and build (runs lint then rollup)
12+
npm run rollup # Build with rollup
13+
npm run test # Run lint and tests
14+
npm run coverage # Run tests with coverage reporting
15+
npm run fix # Fix linting and formatting issues
16+
npm run lint # Lint code with oxlint
17+
```
18+
19+
## Development Workflow
20+
21+
Source code is in `src/`.
22+
23+
- **lint**: Check and fix linting issues with oxlint
24+
- **fix**: Fix linting and formatting issues
25+
- **build**: Lint + rollup build
26+
- **coverage**: Run test suite with coverage reporting
27+
28+
## Project Structure
29+
30+
```
31+
├── src/lru.js # Main LRU cache implementation
32+
├── tests/ # Test files
33+
├── benchmarks/ # Performance benchmarks
34+
├── dist/ # Built distribution files
35+
├── types/ # TypeScript definitions
36+
├── docs/ # Documentation
37+
├── rollup.config.js # Build configuration
38+
└── package.json # Project configuration
39+
```
40+
41+
## Code Style
42+
43+
- Indentation: Tabs
44+
- Quotes: Double quotes
45+
- Semicolons: Required
46+
- Array constructor: Avoid `new Array()` (oxlint will warn)
47+
48+
## Development Principles
49+
50+
- **DRY (Don't Repeat Yourself)**: Extract common logic into reusable functions; avoid duplication
51+
- **YAGNI (You Ain't Gonna Need It)**: Implement only what's needed; avoid over-engineering
52+
- **SOLID**: Follow single responsibility, open/closed, and interface segregation principles
53+
- **OWASP**: Prioritize security; avoid unsafe operations, validate inputs, prevent injection risks
54+
55+
## API Reference
56+
57+
- `lru(max, ttl, resetTtl)` - Factory function to create cache
58+
- `LRU` class - Direct instantiation with `new LRU(max, ttl, resetTtl)`
59+
- Key methods: `get()`, `set()`, `delete()`, `has()`, `clear()`, `evict()`
60+
- Array methods: `keys()`, `values()`, `entries()`
61+
- Properties: `first`, `last`, `max`, `size`, `ttl`, `resetTtl`
62+
63+
## Testing
64+
65+
- Framework: Node.js built-in test runner (`node --test`)
66+
- Coverage: 100%
67+
- Test pattern: `tests/**/*.js`
68+
- All tests must pass with 100% coverage before merging
69+
- Run: `npm test` (lint + tests) or `npm run coverage` for coverage report
70+
71+
## Common Issues to Avoid
72+
73+
- **Memory leaks**: When removing items from the linked list, always clear `prev`/`next` pointers to allow garbage collection
74+
- **LRU order pollution**: Methods like `entries()` and `values()` should access items directly rather than calling `get()`, which moves items and can delete expired items mid-iteration
75+
- **TTL edge cases**: Direct property access (`this.items[key]`) should be used instead of `has()` when you need to inspect expired-but-not-yet-deleted items
76+
- **Dead code**: Always verify edge case code is actually reachable before adding special handling
77+
- **Constructor assignment**: Use `let` not `const` for variables that may be reassigned (e.g., in `setWithEvicted`)
78+
79+
## Implementation Notes
80+
81+
- The LRU uses a doubly-linked list with `first` and `last` pointers for O(1) operations
82+
- TTL is stored per-item as an `expiry` timestamp; `0` means no expiration
83+
- `moveToEnd()` is the core method for maintaining LRU order - item is always moved to the `last` position
84+
- `setWithEvicted()` optimizes updates by avoiding the full `set()` path for existing keys

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1059,7 +1059,7 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
10591059
> 10 February 2017
10601060
10611061
- Changing the signature of `remove()` to avoid edge case creation [`bb88a78`](https://github.com/avoidwork/tiny-lru/commit/bb88a78366f1ae4af209025810e0409f77ff67dc)
1062-
- Adding some tests double checking deleting things that don't exist won''t be an issue [`e208b76`](https://github.com/avoidwork/tiny-lru/commit/e208b7601c1b31e59ebca94cd320ad8d1b40a067)
1062+
- Adding some tests double checking deleting things that don't exist won''t be an issue [`e208b76`](https://github.com/avoidwork/tiny-lru/commit/e208b7601c1b31e59ebca94cd320ad8d1b40a067)
10631063

10641064
#### [1.4.2](https://github.com/avoidwork/tiny-lru/compare/1.4.1...1.4.2)
10651065

CLAUDE.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
@AGENTS.md

CONTRIBUTING.md

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
# Contributing to tiny-lru
2+
3+
Thank you for your interest in contributing to tiny-lru! This document outlines the process for contributing to the project.
4+
5+
## Code of Conduct
6+
7+
This project follows the [Contributor Covenant](https://www.contributor-covenant.org/) code of conduct. By participating, you are expected to uphold this code.
8+
9+
## Getting Started
10+
11+
1. Fork the repository
12+
2. Clone your fork: `git clone https://github.com/YOUR_USERNAME/tiny-lru.git`
13+
3. Install dependencies: `npm install`
14+
4. Create a branch: `git checkout -b feature/your-feature-name`
15+
16+
## Development
17+
18+
### Setup
19+
20+
```bash
21+
npm install
22+
```
23+
24+
### Running Tests
25+
26+
```bash
27+
npm test # Run lint and tests
28+
npm run mocha # Run tests with coverage
29+
```
30+
31+
### Code Style
32+
33+
- **Indentation**: Tabs
34+
- **Quotes**: Double quotes
35+
- **Semicolons**: Required
36+
- **Formatting**: Run `npm run fix` before committing
37+
38+
### Linting and Formatting
39+
40+
```bash
41+
npm run lint # Check linting and formatting
42+
npm run fix # Fix linting and formatting issues
43+
```
44+
45+
## Making Changes
46+
47+
### Guiding Principles
48+
49+
- **DRY (Don't Repeat Yourself)**: Extract common logic into reusable functions
50+
- **YAGNI (You Ain't Gonna Need It)**: Implement only what's needed
51+
- **SOLID**: Follow single responsibility, open/closed, and interface segregation principles
52+
- **OWASP**: Prioritize security; validate inputs, avoid unsafe operations
53+
54+
### Writing Tests
55+
56+
- All new features must include tests
57+
- Maintain 100% line coverage
58+
- Tests live in `tests/unit/`
59+
- Run `npm run mocha` to verify coverage
60+
61+
### Commit Messages
62+
63+
Follow conventional commits:
64+
65+
```
66+
type(scope): description
67+
68+
[optional body]
69+
70+
[optional footer]
71+
```
72+
73+
**Types**: `feat`, `fix`, `docs`, `style`, `refactor`, `test`, `chore`
74+
75+
**Examples**:
76+
```
77+
feat: add setWithEvicted method
78+
fix: correct TTL expiration check
79+
docs: update README API reference
80+
```
81+
82+
## Pull Request Process
83+
84+
1. Ensure all tests pass: `npm test`
85+
2. Ensure 100% test coverage: `npm run mocha`
86+
3. Update documentation if needed
87+
4. Squash commits if necessary
88+
5. Submit pull request with clear description
89+
90+
### PR Checklist
91+
92+
- [ ] Tests pass with `npm test`
93+
- [ ] 100% code coverage maintained
94+
- [ ] Linting passes with `npm run lint`
95+
- [ ] Documentation updated
96+
- [ ] No breaking changes (or clearly documented)
97+
98+
## Releasing
99+
100+
Releases are managed by the maintainers. If you're preparing a release:
101+
102+
1. Update version in `package.json`
103+
2. Update `CHANGELOG.md`
104+
3. Create release tag
105+
106+
## Questions?
107+
108+
Open an issue for any questions or discussions about contributions.
109+
110+
## License
111+
112+
By contributing, you agree that your contributions will be licensed under the BSD-3-Clause license.

0 commit comments

Comments
 (0)