{"_id":"@avatijs/memoize","_rev":"1-17f923499f5470f6e85622a55b733d92","name":"@avatijs/memoize","dist-tags":{"latest":"0.1.2"},"versions":{"0.1.1":{"name":"@avatijs/memoize","version":"0.1.1","keywords":["avati","scheduler","batch","task-queue","priority-queue","async","typescript","debounce","debouncer","throttle","throttling","delay","function control","rate limit","rate limiting","utility","helper","JavaScript","timing","event handling","event control","performance","optimization","function delay","input control","API calls"],"author":{"url":"https://khaled.ee/","name":"Khaled Sameer","email":"khaled.smq@hotmail.com"},"license":"MIT","_id":"@avatijs/memoize@0.1.1","maintainers":[{"name":"khaledsqm","email":"khaled.smq@hotmail.com"}],"homepage":"https://avati.io/","bugs":{"url":"https://github.com/KhaledSMQ/avati/issues","email":"khaled.smq@hotmail.com"},"dist":{"shasum":"506fa7e0dfbe69cf75ba50cdde935fecaafa0f22","tarball":"https://registry.npmjs.org/@avatijs/memoize/-/memoize-0.1.1.tgz","fileCount":12,"integrity":"sha512-Dh8FSbEXf0v2hDtljvjNYtypCy/oMn8uS0uKxTi2hNhCbsfud+Itgz3HNw2d66TQOIX9kbSyzgsG67HgUgym3A==","signatures":[{"sig":"MEQCIAl3ESc55c1/jvi3CUlicOOjR21er0d0nRIlZ7CL14vzAiAkTE0LPSpbkFZz2lz3KflTH4MvLtjgCL8ggKeIZpOm5A==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":34590},"main":"./dist/cjs/index.js","types":"./dist/types/index.d.ts","unpkg":"./dist/umd/index.min.js","module":"./dist/esm/index.js","browser":"./dist/umd/index.js","engines":{"node":">=14.0.0"},"exports":{".":{"types":"./dist/types/index.d.ts","import":"./dist/esm/index.js","default":"./dist/umd/index.js","require":"./dist/cjs/index.js"},"./package.json":"./package.json"},"gitHead":"c75b9a02559531f2bf2673359aa0dcf919ee3471","scripts":{"size":"webpack --mode production --json > stats.json && webpack-bundle-analyzer stats.json","test":"jest","build":"npm run clean && npm run build:types && npm run build:prod","clean":"rimraf dist","watch":"webpack --mode=development --watch","analyze":"npm run analyze:size && npm run analyze:deps && npm run analyze:duplicates","build:dev":"webpack --mode development --progress","typecheck":"tsc --noEmit","build:prod":"webpack --mode production --progress","size:check":"bundlesize","size:watch":"bundlesize --watch","analyze:why":"webpack --mode production --display-reasons","build:types":"tsc --emitDeclarationOnly --outDir dist/types","analyze:deps":"madge --circular --extensions ts ./src/index.ts","analyze:full":"webpack --mode production --stats detailed","analyze:size":"webpack --mode production --json > stats.json && webpack-bundle-analyzer stats.json","analyze:tree":"webpack --mode production --display-used-exports","watch:silent":"webpack --mode=development --watch --silent","prepublishOnly":"npm run build","analyze:modules":"webpack --mode production --display-modules","analyze:detailed":"webpack --config webpack.analysis.config.js","size:compression":"bundlesize --compression","analyze:duplicates":"jscpd src"},"_npmUser":{"name":"khaledsqm","email":"khaled.smq@hotmail.com"},"jsdelivr":"./dist/umd/index.min.js","bundlesize":[{"path":"./dist/esm/index.js","maxSize":"3 kB","compression":"brotli"},{"path":"./dist/cjs/index.js","maxSize":"3 kB","compression":"brotli"},{"path":"./dist/umd/*.js","maxSize":"3 kB","compression":"brotli"}],"repository":{"url":"git+https://github.com/KhaledSMQ/avati.git","type":"git","directory":"packages/memoize"},"_npmVersion":"10.9.0","description":"Memoize package part of Avati project","directories":{},"sideEffects":false,"_nodeVersion":"20.14.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","jscpd":"^4.0.5","madge":"^8.0.0","eslint":"^8.57.1","rimraf":"^6.0.1","ts-jest":"^29.2.5","webpack":"^5.96.1","ts-loader":"^9.5.1","bundlesize":"^0.18.2","typescript":"^5.6.3","@types/jest":"^29.5.14","@types/node":"^22.9.1","webpack-cli":"^5.1.4","dependency-cruiser":"^16.6.0","source-map-explorer":"^2.5.3","terser-webpack-plugin":"^5.3.10","webpack-bundle-analyzer":"^4.10.2","@typescript-eslint/parser":"^8.15.0","@typescript-eslint/eslint-plugin":"^8.15.0"},"_npmOperationalInternal":{"tmp":"tmp/memoize_0.1.1_1732138061873_0.8751702670094379","host":"s3://npm-registry-packages"}},"0.1.2":{"name":"@avatijs/memoize","version":"0.1.2","description":"Memoize package part of Avati project","main":"./dist/cjs/index.js","module":"./dist/esm/index.js","types":"./dist/types/index.d.ts","browser":"./dist/umd/index.js","unpkg":"./dist/umd/index.min.js","jsdelivr":"./dist/umd/index.min.js","exports":{".":{"types":"./dist/types/index.d.ts","import":"./dist/esm/index.js","require":"./dist/cjs/index.js","default":"./dist/umd/index.js"},"./package.json":"./package.json"},"sideEffects":false,"engines":{"node":">=18.20.5"},"bundlesize":[{"path":"./dist/esm/index.js","maxSize":"3 kB","compression":"brotli"},{"path":"./dist/cjs/index.js","maxSize":"3 kB","compression":"brotli"},{"path":"./dist/umd/*.js","maxSize":"3 kB","compression":"brotli"}],"scripts":{"build":"npm run clean && npm run build:types && npm run build:prod","build:dev":"webpack --mode development --progress","build:prod":"webpack --mode production","build:types":"tsc --emitDeclarationOnly","watch":"webpack --mode=development --watch","watch:silent":"webpack --mode=development --watch --silent","clean":"rimraf dist","prepublishOnly":"npm run build","test":"jest --config jest.config.js","typecheck":"tsc --noEmit","size":"webpack --mode production --json > stats.json && webpack-bundle-analyzer stats.json","analyze:deps":"madge --circular --extensions ts ./src/index.ts","size:check":"bundlesize","size:watch":"bundlesize --watch","size:compression":"bundlesize --compression","check-deps":"depcheck --config .depcheckrc","analyze":"npm run analyze:size && npm run analyze:deps && npm run analyze:duplicates","analyze:size":"webpack --mode production --json > stats.json && webpack-bundle-analyzer stats.json","analyze:detailed":"webpack --config webpack.analysis.config.js","analyze:duplicates":"jscpd src","analyze:why":"webpack --mode production --display-reasons","analyze:tree":"webpack --mode production --display-used-exports","analyze:modules":"webpack --mode production --display-modules","analyze:full":"webpack --mode production --stats detailed"},"author":{"name":"Khaled Sameer","email":"khaled.smq@hotmail.com","url":"https://khaled.ee/"},"homepage":"https://avati.io/","bugs":{"url":"https://github.com/KhaledSMQ/avati/issues","email":"khaled.smq@hotmail.com"},"repository":{"type":"git","url":"git+https://github.com/KhaledSMQ/avati.git","directory":"packages/memoize"},"license":"MIT","publishConfig":{"access":"public"},"keywords":["avati","scheduler","batch","task-queue","priority-queue","async","typescript","debounce","debouncer","throttle","throttling","delay","function control","rate limit","rate limiting","utility","helper","JavaScript","timing","event handling","event control","performance","optimization","function delay","input control","API calls"],"devDependencies":{"@types/jest":"^29.5.14","@types/node":"^22.9.1","@typescript-eslint/eslint-plugin":"^8.15.0","@typescript-eslint/parser":"^8.15.0","bundlesize":"^0.18.2","depcheck":"^1.4.7","dependency-cruiser":"^16.6.0","eslint":"^8.57.1","jest":"^29.7.0","jscpd":"^4.0.5","madge":"^8.0.0","rimraf":"^6.0.1","source-map-explorer":"^2.5.3","terser-webpack-plugin":"^5.3.10","ts-jest":"^29.2.5","ts-loader":"^9.5.1","typescript":"^5.6.3","webpack":"^5.96.1","webpack-bundle-analyzer":"^4.10.2","webpack-cli":"^5.1.4"},"_id":"@avatijs/memoize@0.1.2","gitHead":"45902cf3fc3c657b7e7d4055600c78870d4dbabb","_nodeVersion":"20.14.0","_npmVersion":"10.9.0","dist":{"integrity":"sha512-zbxp6Cpp4GtuWFqUnRfNaYLzQYdmXkVIsvd1T6oNq/8eM8B8NL5yFuxs+eT3OpQEOvY6ZtPbkPOa7VO4O3WWwg==","shasum":"064bb49747e26b3ddbfc88f89564ed349198b6d2","tarball":"https://registry.npmjs.org/@avatijs/memoize/-/memoize-0.1.2.tgz","fileCount":18,"unpackedSize":72970,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFvqw6GA9E/ij0QyFYGmF0CoTxJycCgY7V/v039WPDMlAiEA+dGXad4BPxNCKUnjrHQHfOH/CN9os5JYPaSeHTBpC+g="}]},"_npmUser":{"name":"khaledsqm","email":"khaled.smq@hotmail.com"},"directories":{},"maintainers":[{"name":"khaledsqm","email":"khaled.smq@hotmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/memoize_0.1.2_1732354363207_0.7802196159090664"},"_hasShrinkwrap":false}},"time":{"created":"2024-11-20T21:27:41.731Z","modified":"2024-11-23T09:32:43.670Z","0.1.1":"2024-11-20T21:27:42.062Z","0.1.2":"2024-11-23T09:32:43.464Z"},"bugs":{"url":"https://github.com/KhaledSMQ/avati/issues","email":"khaled.smq@hotmail.com"},"author":{"name":"Khaled Sameer","email":"khaled.smq@hotmail.com","url":"https://khaled.ee/"},"license":"MIT","homepage":"https://avati.io/","keywords":["avati","scheduler","batch","task-queue","priority-queue","async","typescript","debounce","debouncer","throttle","throttling","delay","function control","rate limit","rate limiting","utility","helper","JavaScript","timing","event handling","event control","performance","optimization","function delay","input control","API calls"],"repository":{"type":"git","url":"git+https://github.com/KhaledSMQ/avati.git","directory":"packages/memoize"},"description":"Memoize package part of Avati project","maintainers":[{"name":"khaledsqm","email":"khaled.smq@hotmail.com"}],"readme":"# Memoize Function Utility\n\nA robust and efficient TypeScript utility for memoizing functions with support for:\n\n- **Cache Size Limits**: Control the maximum number of cached entries.\n- **Time-to-Live (TTL)**: Set expiration time for cached entries.\n- **LRU Cache Eviction**: Automatically evict least recently used items when the cache limit is reached.\n- **Complex Argument Handling**: Safely memoize functions with complex arguments, including objects with circular references.\n\n---\n\n## Table of Contents\n\n- [Installation](#installation)\n- [Usage](#usage)\n    - [Basic Memoization](#basic-memoization)\n    - [Cache Size Limit](#cache-size-limit)\n    - [Time-to-Live (TTL)](#time-to-live-ttl)\n    - [Handling Complex Arguments](#handling-complex-arguments)\n    - [Preserving `this` Context](#preserving-this-context)\n    - [Memoizing Asynchronous Functions](#memoizing-asynchronous-functions)\n- [API](#api)\n    - [`memoize`](#memoize)\n    - [`MemoizeOptions`](#memoizeoptions)\n- [Implementation Details](#implementation-details)\n    - [LRU Cache](#lru-cache)\n    - [Key Generation](#key-generation)\n- [Testing](#testing)\n- [License](#license)\n\n---\n\n## Installation\n\nYou can install this utility via npm:\n\n```bash\nnpm install @avatijs/memoize\n```\n\n---\n\n## Usage\n\nImport the `memoize` function into your project:\n\n```typescript\nimport { memoize } from '@avatijs/memoize';\n```\n\n### Basic Memoization\n\nMemoize a simple function to cache its results:\n\n```typescript\nfunction add(a: number, b: number): number {\n  console.log('Computing...');\n  return a + b;\n}\n\nconst memoizedAdd = memoize(add);\n\nconsole.log(memoizedAdd(1, 2)); // Outputs: Computing... 3\nconsole.log(memoizedAdd(1, 2)); // Outputs: 3 (cached result)\n```\n\n### Cache Size Limit\n\nLimit the cache size using the `maxCacheSize` option:\n\n```typescript\nconst memoizedAddLimited = memoize(add, { maxCacheSize: 2 });\n\nmemoizedAddLimited(1, 2); // Cached\nmemoizedAddLimited(2, 3); // Cached\nmemoizedAddLimited(3, 4); // Cached, cache size exceeds limit, least recently used item is evicted\n```\n\n### Time-to-Live (TTL)\n\nSet a TTL (in milliseconds) after which cached entries expire:\n\n```typescript\nconst memoizedAddWithTTL = memoize(add, { ttl: 5000 }); // Entries expire after 5 seconds\n\nmemoizedAddWithTTL(1, 2); // Cached\nsetTimeout(() => {\n  memoizedAddWithTTL(1, 2); // Recomputed after TTL expires\n}, 6000);\n```\n\n### Handling Complex Arguments\n\nMemoize functions that accept complex arguments, including objects and arrays:\n\n```typescript\nfunction processData(data: { id: number; value: string }): string {\n  console.log('Processing data...');\n  return `ID: ${data.id}, Value: ${data.value}`;\n}\n\nconst memoizedProcessData = memoize(processData);\n\nconst data = { id: 1, value: 'Test' };\nmemoizedProcessData(data); // Outputs: Processing data... 'ID: 1, Value: Test'\nmemoizedProcessData(data); // Cached result\n```\n\n### Preserving `this` Context\n\nMemoize methods that rely on the `this` context:\n\n```typescript\nclass Multiplier {\n  factor = 2;\n\n  multiply = memoize(function (this: Multiplier, x: number) {\n    return x * this.factor;\n  });\n}\n\nconst multiplier = new Multiplier();\nconsole.log(multiplier.multiply(5)); // Outputs: 10\n```\n\n### Memoizing Asynchronous Functions\n\nMemoize functions that return promises:\n\n```typescript\nasync function fetchData(url: string): Promise<string> {\n  const response = await fetch(url);\n  return response.text();\n}\n\nconst memoizedFetchData = memoize(fetchData);\n\nmemoizedFetchData('https://api.example.com/data').then(console.log);\n```\n\n---\n\n## API\n\n### `memoize`\n\nCreates a memoized version of a function with optional cache size limit and TTL.\n\n#### Signature\n\n```typescript\nfunction memoize<Args extends any[], Return>(\n  fn: (...args: Args) => Return,\n  options?: MemoizeOptions\n): (...args: Args) => Return;\n```\n\n#### Parameters\n\n- `fn`: The function to memoize.\n- `options` (optional): An object specifying memoization options.\n\n### `MemoizeOptions`\n\nAn interface defining the memoization options.\n\n#### Properties\n\n- `maxCacheSize` (optional): `number`\n    - Maximum number of entries to store in the cache.\n    - Default: `Infinity`\n- `ttl` (optional): `number`\n    - Time-to-live in milliseconds for cached entries.\n    - Entries expire after `ttl` milliseconds.\n    - Default: `undefined` (no expiration)\n\n---\n\n## Implementation Details\n\n### LRU Cache\n\nAn internal Least Recently Used (LRU) cache is used to manage cached entries efficiently. When the cache size exceeds `maxCacheSize`, the least recently used item is evicted.\n\n### Key Generation\n\nA robust key generation function handles complex arguments, including:\n\n- **Primitives**: Compared by value.\n- **Objects**: Compared by identity using a unique ID assigned via a `WeakMap`.\n- **Functions**: Stringified to include their code in the key.\n- **Circular References**: Safely handled without causing errors.\n\n---\n\n## Testing\n\nExtensive test cases have been written using Jest to ensure reliability.\n\n### Running Tests\n \nRun the tests:\n\n```bash\nnpx jest\n```\n\n### Test Cases Covered\n\n- **Caching Basic Function Calls**\n- **Handling Different Arguments**\n- **Max Cache Size Limit**\n- **Time-to-Live (TTL) Expiration**\n- **Complex Arguments and Circular References**\n- **Functions with Side Effects**\n- **Preservation of `this` Context**\n- **Asynchronous Functions**\n- **Exception Handling**\n- **Non-Serializable Arguments**\n- **Zero TTL (No Caching)**\n- **Multiple Memoized Functions**\n\n---\n\n\n## Changelog\n\nPlease see [CHANGELOG](./CHANGELOG.md) for more information what has changed recently.\n\n## Contributing\n\nI welcome contributions from developers of all experience levels. If you have an idea, found a bug, or want to improve something, I encourage you to get involved!\n\n### How to Contribute\n1. Read [Contributing Guide](https://github.com/KhaledSMQ/avati/blob/master/Contributing.md) for details on how to get started.\n2. Fork the repository and make your changes.\n3. Submit a pull request, and we’ll review it as soon as possible.\n\n## License\n\n[![MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/KhaledSMQ/avati/blob/master/LICENSE)\n\nAvati is open-source and distributed under the [MIT License](https://github.com/KhaledSMQ/avati/blob/master/LICENSE).\n\n---\n<div align=\"center\">\n\n[![Follow on Twitter](https://img.shields.io/twitter/follow/KhaledSMQ.svg?style=social)](https://x.com/khaledsmq_)\n[![Follow on LinkedIn](https://img.shields.io/badge/LinkedIn-Connect-blue.svg)](https://www.linkedin.com/in/khaledsmq/)\n[![Follow on Medium](https://img.shields.io/badge/Medium-Follow-black.svg)](https://medium.com/@khaled.smq)\n[![Made with ❤️](https://img.shields.io/badge/Made%20with-❤️-red.svg)](https://github.com/KhaledSMQ)\n[![Star on GitHub](https://img.shields.io/github/stars/KhaledSMQ/avati.svg?style=social)](https://github.com/KhaledSMQ/avati/stargazers)\n[![Follow on GitHub](https://img.shields.io/github/followers/KhaledSMQ.svg?style=social&label=Follow)](https://github.com/KhaledSMQ)\n\n</div>\n","readmeFilename":"README.md"}