{"id":22489133,"url":"https://github.com/L422Y/megamap","last_synced_at":"2025-08-02T21:32:12.251Z","repository":{"id":206675204,"uuid":"717450663","full_name":"L422Y/megamap","owner":"L422Y","description":"Extended JavaScript Map with caching, loading, expiry features... and more","archived":false,"fork":false,"pushed_at":"2024-11-13T22:29:03.000Z","size":399,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2024-12-02T04:04:36.266Z","etag":null,"topics":["api","caching","database","javascript","map","nuxt","state-management","typescript","vue"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/L422Y.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-11-11T14:21:51.000Z","updated_at":"2024-11-13T22:29:07.000Z","dependencies_parsed_at":null,"dependency_job_id":"d93e984f-497e-419f-af24-9cc8e3a35f4d","html_url":"https://github.com/L422Y/megamap","commit_stats":{"total_commits":153,"total_committers":1,"mean_commits":153.0,"dds":0.0,"last_synced_commit":"6c95c9dcf012097d40e90ef17d41264f42ca07b7"},"previous_names":["l422y/megamap"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/L422Y%2Fmegamap","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/L422Y%2Fmegamap/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/L422Y%2Fmegamap/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/L422Y%2Fmegamap/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/L422Y","download_url":"https://codeload.github.com/L422Y/megamap/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":228500221,"owners_count":17930020,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api","caching","database","javascript","map","nuxt","state-management","typescript","vue"],"created_at":"2024-12-06T17:19:17.321Z","updated_at":"2024-12-06T17:21:30.372Z","avatar_url":"https://github.com/L422Y.png","language":"TypeScript","funding_links":[],"categories":["TypeScript"],"sub_categories":[],"readme":"# MegaMap\n\n\u003e A TypeScript-first map implementation that handles loading, caching, searching, filtering, and reactivity.\n\n[![wakatime](https://wakatime.com/badge/user/018b859f-13fa-41e2-9883-185549942dff/project/018bbdef-9ad5-44b2-a07f-56a02612b0e9.svg)](https://wakatime.com/badge/user/018b859f-13fa-41e2-9883-185549942dff/project/018bbdef-9ad5-44b2-a07f-56a02612b0e9)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n![Build Status](https://github.com/l422y/megamap/actions/workflows/main.yml/badge.svg)\n\n\nMegaMap is a TypeScript library designed for advanced key-value mapping. It extends the native JavaScript `Record` object\nand integrates additional features such as caching, loadability, and automatic expiry of entries. MegaMap is built upon\ntwo foundational classes, `LoadableMap` and `CachedLoadableMap`, each offering distinct capabilities. This library is\nideal for sophisticated mapping solutions where enhanced control over data storage and retrieval is needed.\n\nIdeally, this library is meant to be used in conjunction with a data store (e.g. a database, API, etc.),\nand/or state management solution, such as [pinia](https://github.com/vuejs/pinia). \n\n## Demo\n\nI've started creating a demo on usage of the library, you can find the source at [megamap-demo](https://github.com/l422y/megamap-demo)\n\nThere's a live demo at [megamap-demo.pages.dev](https://megamap-demo.pages.dev/)\n\n## Quick Example\n\n```typescript\nimport { MegaMap, ReactiveMegaMap } from 'megamap'\n\n// Basic loading and caching\nconst posts = new MegaMap({\n  loadOne: (id) =\u003e fetch(`/api/posts/${id}`).then(r =\u003e r.json()),\n  loadAll: () =\u003e fetch('/api/posts').then(r =\u003e r.json()),\n  expiryInterval: 5000, // Cache expires after 5 seconds\n})\n\n// Load and cache automatically\nawait posts.get('123')  // Fetches from API\nawait posts.get('123')  // Returns from cache\nawait posts.get('123')  // After 5s, fetches fresh data\n\n// Enable search functionality\nconst searchablePosts = new MegaMap({\n  ...postsConfig,\n  searchableFields: ['title', 'content', 'tags']\n})\n\n// Fuzzy search across all loaded items\nconst results = searchablePosts.searchItems('typescript')\n\n// Maintain filtered sublists automatically\nconst organizedPosts = new MegaMap({\n  ...postsConfig,\n  subListFilters: {\n    published: post =\u003e post.status === 'published',\n    draft: post =\u003e post.status === 'draft',\n    recent: post =\u003e isWithinLast7Days(post.date)\n  }\n})\n\n// Access filtered lists\nconsole.log(organizedPosts.subLists.published)  // Array of published posts\nconsole.log(organizedPosts.subLists.draft)     // Array of drafts\nconsole.log(organizedPosts.subLists.recent)    // Recent posts\n\n// Make it reactive (Vue.js)\nconst reactivePosts = new ReactiveMegaMap({\n  loadOne: (id) =\u003e fetch(`/api/posts/${id}`).then(r =\u003e r.json()),\n  loadAll: () =\u003e fetch('/api/posts').then(r =\u003e r.json()),\n  subListFilters: {\n    published: post =\u003e post.status === 'published',\n    draft: post =\u003e post.status === 'draft'\n  }\n})\n\n// Now it's Vue reactive - changes trigger component updates\nreactivePosts.set('123', { title: 'Updated' })\n```\n\n## Vue.js Example\n\n```vue\n\u003c!-- PostManager.vue --\u003e\n\u003ctemplate\u003e\n  \u003cdiv class=\"space-y-4\"\u003e\n    \u003c!-- Search --\u003e\n    \u003cinput v-model=\"search\" placeholder=\"Search posts...\" /\u003e\n    \n    \u003c!-- Posts Lists --\u003e\n    \u003cdiv class=\"grid grid-cols-2 gap-4\"\u003e\n      \u003cdiv\u003e\n        \u003ch2\u003ePublished ({{ postsMap.subLists.published.length }})\u003c/h2\u003e\n        \u003cdiv v-for=\"post in postsMap.subLists.published\" :key=\"post.id\"\u003e\n          {{ post.title }}\n        \u003c/div\u003e\n      \u003c/div\u003e\n      \u003cdiv\u003e\n        \u003ch2\u003eDrafts ({{ postsMap.subLists.draft.length }})\u003c/h2\u003e\n        \u003cdiv v-for=\"post in postsMap.subLists.draft\" :key=\"post.id\"\u003e\n          {{ post.title }}\n        \u003c/div\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/template\u003e\n\n\u003cscript setup lang=\"ts\"\u003e\nimport { ref, onMounted } from 'vue'\nimport { ReactiveMegaMap } from 'megamap'\n\ninterface Post {\n  id: string\n  title: string\n  status: 'draft' | 'published'\n}\n\nconst search = ref('')\nconst postsMap = new ReactiveMegaMap\u003cstring, Post\u003e({\n  loadOne: (id) =\u003e fetch(`/api/posts/${id}`).then(r =\u003e r.json()),\n  loadAll: () =\u003e fetch('/api/posts').then(r =\u003e r.json()),\n  searchableFields: ['title'],\n  subListFilters: {\n    published: post =\u003e post.status === 'published',\n    draft: post =\u003e post.status === 'draft'\n  }\n})\n\nonMounted(() =\u003e postsMap.getAll())\n\n// Update a post - UI automatically updates\nconst publishPost = (id: string) =\u003e {\n  postsMap.set(id, { ...postsMap.value[id], status: 'published' })\n}\n\u003c/script\u003e\n```\n\nThis example shows:\n- 🔄 Automatic reactive sublists\n- 🔍 Built-in search\n- ⚡️ TypeScript support\n- 📱 Simple grid layout\n\n## Features\n\nTL;DR:\n\n- 🚀 Automatic loading \u0026 caching\n- 🔍 Built-in fuzzy search (via Fuse.js)\n- 🔄 Auto-maintained filtered lists\n- ⚡️ Vue.js reactivity (via ReactiveMegaMap)\n- 📦 TypeScript-first\n\nMegaMap provides a suite of TypeScript classes for advanced key-value mapping, each offering unique capabilities:\n\n1. **LoadableMap**: This class extends basic map functionality with dynamic loading capabilities. Key features include:\n    - Asynchronous data loading for individual keys (`loadOne`).\n    - Optional bulk data loading (`loadAll`).\n    - Custom key properties (`keyProperty`) for complex data types.\n    - Callbacks for map updates (`onUpdated`).\n\n2. **CachedLoadableMap**: Building on LoadableMap, this class adds caching features with automatic expiry. It includes:\n    - All LoadableMap features.\n    - Configurable expiry intervals for cached data (`expiryInterval`).\n\n3. **MegaMap**: This is a comprehensive class combining loading, caching, and advanced manipulation. It includes:\n    - All CachedLoadableMap features.\n    - Support for secondary maps (`secondaryMaps`) for additional organizational capabilities.\n    - Customizable item filters (`filter`).\n    - Integration with Fuse.js for advanced search capabilities.\n\n4. **ReactiveMegaMap**: A specialized version of MegaMap designed for reactive frameworks like Vue.js. It offers:\n    - All MegaMap features.\n    - Reactive map properties, enabling seamless integration with reactive UI components.\n\n## Installation\n\n```bash\nnpm install megamap\n```\n\n## Usage\n\nIn the examples below we are implementing things outside of state management, and in\na vue template directly. This is not recommended for production use, but is done here for the sake of simplicity. We are also using some fake data loader functions, [check those out here](FAKELOADERS.md).\n### Named Queries\n\nWe'll also set up some named queries, these are basically stored functions that can be called on the MegaMap instance,\nallowing you to easily retrieve data from the map without having to write the same code over and over again.\n\nThe format is meant to be clean and simple, so that you can easily read and understand what the query is doing.\n\n```typescript\n const namedQueries = {\n    byAuthor: async (authorId) =\u003e {\n        return fetch(`/api/posts/by-author/${authorId}`).then(res =\u003e res.json())\n    },\n    byTag: async (tag) =\u003e {\n        return fetch(`/api/posts/by-tag/${tag}`).then(res =\u003e res.json())\n    },\n}\n```\n\n### Sub-lists / Filters\n\n...and some filters that will be used in the `subListFilters` option to populate the `subLists` property of our MegaMap:\n\n```typescript\nconst subListFilters = {\n    active: (item: any) =\u003e item.status === \"active\",\n    draft: (item: any) =\u003e item.status === \"draft\",\n    inactive: (item: any) =\u003e item.status === \"inactive\",\n}\n```   \n\nWe'll refer to these functions in the examples below.\n\n### `MegaMap`/`ReactiveMegaMap` Classes\n\nCurrently, these two classes are almost identical. The only difference is that `ReactiveMegaMap` uses Vue `reactive`\nproperties, which enables reactivity for the sub-lists inside components/templates.\n\nFor `MegaMap`:\n\n```typescript\nimport { MegaMap } from \"megamap\"\n\n// create a new MegaMap\nconst allPosts = new MegaMap({\n    loadOne: fakeRecordLoad, // see above\n    loadAll: fakeMultipleRecordLoad, // see above,\n    subListFilters, // see above\n    namedQueries, // see above\n})\n\n// another MegaMap that will be populated with user's \"posts\"\nconst myPosts = MegaMap({\n    loadOne: fakeRecordLoad, // see above\n    loadAll: fakeMultipleRecordLoad, // see above,\n    subListFilters, // see above\n    secondaryMaps: [allPosts] // see below\n   \n})\n\n// simulate a record load every second\nsetInterval(async () =\u003e {\n    await megaMap.get(`key${Math.floor(Math.random() * 1000) + 1}`)\n}, 1000)\n\n```\n\nThe `MegaMap` class has a `secondaryMaps` option that allows you to specify other MegaMaps that new items should be\nadded to. This is useful for creating multiple views of the same data, for example, a \"My Posts\" view and an \"All Posts\"\n\nThis way, when a new item is loaded into `myPosts`, it will also be added to `allPosts`.\n\nand for `ReactiveMegaMap` in Vue:\n\n```vue\n\u003ctemplate\u003e\n   \u003cfieldset\u003e\n      \u003clegend\u003eACTIVE\u003c/legend\u003e\n      \u003cdiv\u003e\n         \u003cdiv v-for=\"item in megaMap.subLists.active\" :key=\"item._id\"\u003e\n            {{ item.data }}\n         \u003c/div\u003e\n      \u003c/div\u003e\n   \u003c/fieldset\u003e\n\u003c/template\u003e\n\u003cscript setup\u003e\n\n   import {ReactiveMegaMap} from \"megamap\"\n\n   const megaMap = new ReactiveMegaMap({\n      loadOne: fakeRecordLoad, // see above\n      loadAll: fakeMultipleRecordLoad, // see above,\n      subListFilters, // see above\n      namedQueries, // see above\n   })\n\n   // using the predefined named queries\n   const authorPosts = ref(await megaMap.query.byAuthor(\"00001\"))\n   const taggedPosts = ref(await megaMap.query.byTag(\"fake\"))\n\n   // simulate a record load every second\n   setInterval(async () =\u003e {\n      await megaMap.get(`key${Math.floor(Math.random() * 1008) + 1}`)\n   }, 1000)\n\n\u003c/script\u003e\n```\n\n## Additional Classes\n\nYou can also utilize the following classes independently of MegaMap:\n\n### `LoadableMap` Class\n\nThis class extends the native JavaScript `Record` object with dynamic loading capabilities. It does not include any of the\nadvanced features of `CachedLoadableMap` or `MegaMap`, but is useful for loading data into maps instead of handling this\nmanually.\n\n```typescript\nimport { LoadableMap } from \"megamap\"\n\nconst loadableMap = new LoadableMap\u003cstring, TFakePost\u003e({\n    loadOne: fakeRecordLoad, // see above\n    loadAll: fakeMultipleRecordLoad, // see above,\n    keyProperty: \"_id\", // default is \"_id\"\n    onUpdated: (updatedItem) =\u003e {\n        console.log(`Updated item: ${updatedItem.id}`)\n    }\n})\n```\n\n### `CachedLoadableMap` Class\n\nAdds caching and expiry features to `LoadableMap`, will automatically expire cached items after the specified interval,\nso that they will be reloaded on the next access.\n\nThis class is useful for caching data that is accessed often, but not expected to change frequently, but may need to be\nrefreshed.\n\n```typescript\nimport { CachedLoadableMap } from 'megamap';\n\nconst cachedMap = new CachedLoadableMap\u003cstring, DataType\u003e({\n    loadOne: fakeRecordLoad, // see above\n    loadAll: fakeMultipleRecordLoad, // see above,\n    keyProperty: \"_id\", // default is \"_id\"\n    expiryInterval: 1000 * 60 * 60 * 24, // 24 hours\n    onUpdated: (updatedItem) =\u003e {\n        console.log(`Updated item: ${updatedItem.id}`)\n    }\n});\n```\n\n## Contributing\n\nContributions to MegaMap are welcome. Please feel free to submit pull requests or suggest features.\n\n## License\n\nThis project is licensed under the MIT License.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FL422Y%2Fmegamap","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FL422Y%2Fmegamap","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FL422Y%2Fmegamap/lists"}