@enjoys/react-api
v0.0.8
Published
[](https://www.npmjs.com/package/@enjoys/react-api) [](./LICENSE.md) [
Downloads
49
Maintainers
Keywords
Readme
@enjoys/react-api
A collection of type-safe React hooks and utilities for browser-native APIs — IndexedDB, Cache Storage, File System Access, and Custom Events.
Features
| Module | Description | Import |
|---|---|---|
| IDB | IndexedDB via Dexie — typed CRUD, QueryBuilder, operators, nested objects, TTL pruning | @enjoys/react-api/idb |
| Cache Storage | Browser Cache API — JSON, Blob, File, HTML, ArrayBuffer with TTL & metadata | @enjoys/react-api/cache-storage |
| Events | Cross-component CustomEvent hook — emit, listen, debounce, fire-once | @enjoys/react-api/events |
| OFS | File System Access API — read/write/delete files with nested directory support | @enjoys/react-api/ofs |
Installation
npm install @enjoys/react-apiPeer Dependencies
| Package | Required | Used by |
|---|---|---|
| react ^18 || ^19 | Yes | All modules |
| react-dom ^18 || ^19 | Yes | All modules |
| dexie ^4 | Optional | IDB module |
| dot-object ^2 | Optional | IDB nested operations |
# Install peer deps for IDB module
npm install dexie dot-objectQuick Start
IndexedDB
import { IDB, type TableSchema, createQueryBuilder, equals, above } from '@enjoys/react-api/idb';
import { type EntityTable } from 'dexie';
// Step 1: Define your entity
interface User {
id: string;
name: string;
age: number;
}
// Step 2: Define Tables — pick the primary key per table via EntityTable<Entity, PK>
// PK must be a key of the entity ('id' | 'name' | 'age' here)
type Tables = {
users: EntityTable<User, 'id'>; // ← 'id' is the chosen primary key
};
// Step 3: Define schema — TypeScript auto-enforces the PK from Step 2
// '++' prefix = auto-increment; remaining fields = indexed columns
const schema = {
users: '++id,name,age', // ← 'id' must appear first; 'name','age' are optional indexes
} satisfies TableSchema<Tables>;
const idb = new IDB<Tables>(schema, 'my-app', 1);
// CRUD
await idb.addItem('users', { id: '1', name: 'Alice', age: 30 });
const user = await idb.getItemByKey('users', '1');
// QueryBuilder
const qb = createQueryBuilder(idb.getRawDb());
const adults = await qb.query('users').where({ age: above(18) }).findMany();Cache Storage
import { useCache } from '@enjoys/react-api/cache-storage';
function App() {
const cache = useCache('my-app');
const load = async () => {
await cache.put('user', { name: 'Alice' }, 3600);
const user = await cache.get('user');
const fresh = await cache.getOrSet('profile', () => fetch('/api/me').then(r => r.json()), 600);
};
return <button onClick={load}>Load</button>;
}Events
import { useReactEvent } from '@enjoys/react-api/events';
function Sender() {
const { emit } = useReactEvent('notify');
return <button onClick={() => emit({ msg: 'hello' })}>Send</button>;
}
function Receiver() {
const { listen } = useReactEvent('notify');
useEffect(() => listen((data) => console.log(data)), [listen]);
return null;
}Documentation
| Module | Guide | |---|---| | IndexedDB | IDB Documentation | | Cache Storage | Cache Storage Documentation | | Events | Events Documentation | | OFS (File System) | OFS Documentation |
Tree-Shakable Imports
Each module is a separate entry point — only the code you import gets bundled:
import { IDB } from '@enjoys/react-api/idb'; // Only IDB
import { useCache } from '@enjoys/react-api/cache-storage'; // Only Cache
import { useReactEvent } from '@enjoys/react-api/events'; // Only Events
import { useOFS } from '@enjoys/react-api/ofs'; // Only OFSSupports both ESM (import) and CJS (require).
Contributing
Contributions are welcome! Please open an issue or submit a pull request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/my-feature) - Commit your changes (
git commit -m 'feat: add my feature') - Push to the branch (
git push origin feature/my-feature) - Open a Pull Request
