Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 21 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,30 @@

All notable changes to this project will be documented in this file.


The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.3.0] - 2025-09-11

### Added

- **Promise/Async Support** - Full async/await API alongside callback methods
- All CRUD methods now have async variants (createAsync, readAllAsync, etc.)
- Automatic promisification wrapper for callback-based methods
- Complete TypeScript definitions for async methods
- New async usage examples and comprehensive test coverage
- **Enhanced Examples** - Added async/await usage demonstration

## [1.2.0] - 2025-07-08

### Added
- **TypeScript Definitions** - Bundled `lib/json-file-crud.d.ts` for IDE support

- **TypeScript Definitions** - Bundled `lib/json-file-crud.d.ts` for IDE support

## [1.1.0] - 2025-07-07

### Added

- **Unique Fields Support** - Prevent duplicate values in specified fields
- Configure unique fields via `uniqueFields` option
- Automatic validation on create and update operations
Expand All @@ -33,6 +44,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- No need to manually create directories before using the library

### Enhanced

- **Test Suite Reorganization** - Improved test structure
- Split tests into logical files by functionality
- `test-basic.js` - Basic functionality and convenience features
Expand All @@ -45,10 +57,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Backward compatible with existing code

### Changed

- Package description updated to reflect new features
- Test scripts updated for reorganized test structure

### Technical Details

- All new features maintain backward compatibility
- Thread-safe operations through existing queue system
- Comprehensive error handling for all new features
Expand All @@ -57,6 +71,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [1.0.0] - 2025-07-07

### Added

- Initial release of JsonFileCRUD
- Complete CRUD operations (Create, Read, Update, Delete)
- Auto-ID assignment with duplicate prevention
Expand All @@ -74,6 +89,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Contributing guidelines with improvement ideas

### Features

- **create(item, callback)** - Create new items with auto-ID
- **readAll(callback)** - Read all items from file
- **findById(id, callback)** - Find item by ID
Expand All @@ -84,12 +100,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- **writeAll(items, callback)** - Replace all data

### Performance

- Optimized for small to medium datasets (up to ~10,000 items)
- Sequential operations prevent race conditions
- Automatic file creation on first write
- Memory-efficient data handling

### Documentation

- Complete README with API reference
- Multiple practical examples
- Error handling guide
Expand All @@ -99,6 +117,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]

### Future Improvements

- Promise-based API (async/await)
- Batch operations (createMany, updateMany, deleteMany)
- File locking for multi-process safety
Expand Down
52 changes: 50 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ A simple, robust, and thread-safe CRUD library for managing JSON objects in file
## Features

- **Simple API** - Easy to use CRUD operations
- **Promise/Async Support** - Full async/await API alongside callback methods ✨ *New in v1.3*
- **Thread-safe** - Sequential operations with automatic queuing
- **Auto-ID assignment** - Automatic ID generation for new items (configurable)
- **Unique Fields** - Prevent duplicate values in specified fields ✨ *New in v1.1*
Expand Down Expand Up @@ -74,6 +75,27 @@ db.delete(1, (err, deletedItem) => {
});
```

### Async/Await Usage

```javascript
// Same operations using async/await
try {
const user = await db.createAsync({ name: 'John', age: 30 });
console.log('Created:', user);

const foundUser = await db.findByIdAsync(user.id);
console.log('Found:', foundUser);

const updatedUser = await db.updateAsync(user.id, { age: 31 });
console.log('Updated:', updatedUser);

const deletedUser = await db.deleteAsync(user.id);
console.log('Deleted:', deletedUser);
} catch (error) {
console.error('Error:', error.message);
}
```

## API Reference

### Constructor
Expand Down Expand Up @@ -248,6 +270,33 @@ db.writeAll(newData, (err) => {
});
```

### Promise-based API

All methods have async counterparts that return Promises. Simply add `Async` to the method name:

#### Async Method Names

All callback methods have async counterparts. Add `Async` to the method name:

```javascript
// Using async/await
try {
const user = await db.createAsync({ name: 'John', age: 30 });
const allUsers = await db.readAllAsync();
const foundUser = await db.findByIdAsync(user.id);
const updatedUser = await db.updateAsync(user.id, { age: 31 });
await db.deleteAsync(user.id);
} catch (error) {
console.error('Operation failed:', error.message);
}

// Using Promises
db.createAsync({ name: 'Jane', age: 25 })
.then(user => db.findByIdAsync(user.id))
.then(foundUser => console.log('Found:', foundUser))
.catch(error => console.error('Error:', error.message));
```

## Advanced Features

### Unique Fields
Expand Down Expand Up @@ -297,6 +346,7 @@ For comprehensive examples, see the [examples](./examples/) directory:
- **[Basic Usage](./examples/basic-usage.js)** - Simple CRUD operations
- **[Advanced Features](./examples/advanced-usage.js)** - Concurrent operations, filtering, custom ID fields
- **[User Management](./examples/user-management.js)** - Real-world application with unique fields validation
- **[Async/Await Demo](./examples/async-demo.js)** - Promise-based API usage with async/await

### Quick Examples

Expand Down Expand Up @@ -360,7 +410,6 @@ Contributions are welcome! Here are some ways you can help improve JsonFileCRUD:

### Ideas for Contributions

- **Async/Await Support**: Add Promise-based API alongside callbacks
- **Batch Operations**: Add bulk insert/update/delete operations
- **File Locking**: Add file locking for multi-process safety
- **Enhanced Documentation**: Improve documentation and add more examples
Expand All @@ -377,4 +426,3 @@ Contributions are welcome! Here are some ways you can help improve JsonFileCRUD:
## License

MIT License - see [LICENSE](./LICENSE) file for details.

68 changes: 68 additions & 0 deletions examples/async-demo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/**
* Async/Await Usage Example
*
* This example demonstrates the new Promise-based API
* alongside the traditional callback-based methods
*/

import JsonFileCRUD from "../lib/json-file-crud.js";

const crud = new JsonFileCRUD("./examples/data/async-demo.json");

console.log("Async/Await API Demo\n");

async function asyncDemo() {
try {
// Clean start
await crud.writeAllAsync([]);

// Create items using async/await
console.log("Creating users with async/await...");
const user1 = await crud.createAsync({ name: "Ariel", age: 30 });
console.log("Created:", user1);

const user2 = await crud.createAsync({ name: "John", age: 25 });
console.log("Created:", user2);

// Read all users
console.log("\nReading all users...");
const allUsers = await crud.readAllAsync();
console.log("All users:", allUsers);

// Find by ID
console.log("\nFinding user by ID...");
const foundUser = await crud.findByIdAsync(user1.id);
console.log("Found user:", foundUser);

// Update user
console.log("\nUpdating user...");
const updatedUser = await crud.updateAsync(user1.id, { age: 31 });
console.log("Updated user:", updatedUser);

// Filter users
console.log("\nFiltering users over 30...");
const adults = await crud.findByAsync((user) => user.age > 30);
console.log("Adults:", adults);

// Count users
console.log("\nCounting users...");
const count = await crud.countAsync();
console.log("Total users:", count);

// Delete user
console.log("\nDeleting user...");
const deletedUser = await crud.deleteAsync(user2.id);
console.log("Deleted user:", deletedUser);

// Final count
const finalCount = await crud.countAsync();
console.log("Final count:", finalCount);

console.log("\nAsync/Await demo completed successfully!");
} catch (error) {
console.error("Error in async demo:", error.message);
}
}

// Run the async demo
asyncDemo();
56 changes: 56 additions & 0 deletions lib/async-wrapper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/**
* Async wrapper utilities for JsonFileCRUD
* Provides Promise-based API alongside callback-based methods
*/

/**
* Converts a callback-based method to Promise-based
* @param {Function} method - The callback-based method to wrap
* @param {Object} context - The context (this) to bind to the method
* @returns {Function} Promise-based version of the method
*/
export function promisify(method, context) {
return function (...args) {
return new Promise((resolve, reject) => {
// Add callback that resolves or rejects the promise
const callback = (error, result) => {
if (error) {
reject(error);
} else {
resolve(result);
}
};

// Call the original method with callback
method.call(context, ...args, callback);
});
};
}

/**
* Creates async versions of all CRUD methods
* @param {Object} crudInstance - Instance of JsonFileCRUD
* @returns {Object} Object with async method variants
*/
export function createAsyncMethods(crudInstance) {
return {
// Create methods
createAsync: promisify(crudInstance.create, crudInstance),

// Read methods
readAllAsync: promisify(crudInstance.readAll, crudInstance),
findByIdAsync: promisify(crudInstance.findById, crudInstance),
findByAsync: promisify(crudInstance.findBy, crudInstance),
countAsync: promisify(crudInstance.count, crudInstance),

// Update methods
updateAsync: promisify(crudInstance.update, crudInstance),

// Delete methods
deleteAsync: promisify(crudInstance.delete, crudInstance),
deleteAllAsync: promisify(crudInstance.deleteAll, crudInstance),

// Utility methods
writeAllAsync: promisify(crudInstance.writeAll, crudInstance),
};
}
12 changes: 12 additions & 0 deletions lib/json-file-crud.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export default class JsonFileCRUD<T = any> {
readonly autoId: boolean;
readonly uniqueFields: string[];

// Callback-based methods
create(item: T, callback: Callback<T>): void;
readAll(callback: Callback<T[]>): void;
findById(id: any, callback: Callback<T>): void;
Expand All @@ -24,6 +25,17 @@ export default class JsonFileCRUD<T = any> {
deleteAll(callback: (err: Error | null) => void): void;
writeAll(items: T[], callback: (err: Error | null) => void): void;
processWriteQueue(): void;

// Promise-based methods
createAsync(item: T): Promise<T>;
readAllAsync(): Promise<T[]>;
findByIdAsync(id: any): Promise<T>;
findByAsync(filterFn: (item: T) => boolean): Promise<T[]>;
countAsync(): Promise<number>;
updateAsync(id: any, data: Partial<T>): Promise<T>;
deleteAsync(id: any): Promise<T>;
deleteAllAsync(): Promise<void>;
writeAllAsync(items: T[]): Promise<void>;
}

export function createCrud<T = any>(filePath: string, options?: CrudOptions): JsonFileCRUD<T>;
Loading