Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9b47e0d002 | |||
| 87710db139 | |||
| e993f6deb9 |
13
changelog.md
13
changelog.md
@@ -1,5 +1,18 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 2026-03-05 - 1.3.2 - fix(provider(node))
|
||||||
|
use synchronous readdir to avoid partial results on some filesystems (e.g., XFS) when the process receives signals
|
||||||
|
|
||||||
|
- Replaced async fs.readdir with fsSync.readdirSync in ts/providers/smartfs.provider.node.ts
|
||||||
|
- Added comments explaining that async readdir can return partial results on XFS/mounted filesystems when the process receives signals; synchronous readdirSync completes the getdents64 syscall without event-loop interruption
|
||||||
|
|
||||||
|
## 2025-12-16 - 1.3.1 - fix(docs)
|
||||||
|
docs(readme): add "Directory Copy & Move" section with examples and options
|
||||||
|
|
||||||
|
- Adds README documentation for recursive directory copy and move with usage examples (basic copy/move, copy with filter, overwrite, preserve timestamps, applyFilter).
|
||||||
|
- Documents conflict handling modes for copy/move: merge (default), error, and replace.
|
||||||
|
- Documentation-only change — no code or API changes; recommended patch version bump.
|
||||||
|
|
||||||
## 2025-12-16 - 1.3.0 - feat(smartfs.directory)
|
## 2025-12-16 - 1.3.0 - feat(smartfs.directory)
|
||||||
feat(smartfs.directory): add directory copy/move with conflict handling and options
|
feat(smartfs.directory): add directory copy/move with conflict handling and options
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@push.rocks/smartfs",
|
"name": "@push.rocks/smartfs",
|
||||||
"version": "1.3.0",
|
"version": "1.3.2",
|
||||||
"private": false,
|
"private": false,
|
||||||
"description": "a cross platform extendable fs module",
|
"description": "a cross platform extendable fs module",
|
||||||
"main": "dist_ts/index.js",
|
"main": "dist_ts/index.js",
|
||||||
|
|||||||
47
readme.md
47
readme.md
@@ -135,6 +135,53 @@ await fs.directory('/path/to/dir')
|
|||||||
const exists = await fs.directory('/path/to/dir').exists();
|
const exists = await fs.directory('/path/to/dir').exists();
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 📁 Directory Copy & Move
|
||||||
|
|
||||||
|
Copy or move entire directory trees with fine-grained control:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Basic copy - copies all files recursively
|
||||||
|
await fs.directory('/source').copy('/destination');
|
||||||
|
|
||||||
|
// Basic move - moves directory to new location
|
||||||
|
await fs.directory('/old-location').move('/new-location');
|
||||||
|
|
||||||
|
// Copy with options
|
||||||
|
await fs.directory('/source')
|
||||||
|
.filter(/\.ts$/) // Only copy TypeScript files
|
||||||
|
.overwrite(true) // Overwrite existing files
|
||||||
|
.preserveTimestamps(true) // Keep original timestamps
|
||||||
|
.copy('/destination');
|
||||||
|
|
||||||
|
// Copy all files (ignore filter setting)
|
||||||
|
await fs.directory('/source')
|
||||||
|
.filter('*.ts')
|
||||||
|
.applyFilter(false) // Ignore filter, copy everything
|
||||||
|
.copy('/destination');
|
||||||
|
|
||||||
|
// Handle target directory conflicts
|
||||||
|
await fs.directory('/source')
|
||||||
|
.onConflict('merge') // Default: merge contents
|
||||||
|
.copy('/destination');
|
||||||
|
|
||||||
|
await fs.directory('/source')
|
||||||
|
.onConflict('error') // Throw if target exists
|
||||||
|
.copy('/destination');
|
||||||
|
|
||||||
|
await fs.directory('/source')
|
||||||
|
.onConflict('replace') // Delete target first, then copy
|
||||||
|
.copy('/destination');
|
||||||
|
```
|
||||||
|
|
||||||
|
**Configuration Options:**
|
||||||
|
| Method | Default | Description |
|
||||||
|
|--------|---------|-------------|
|
||||||
|
| `filter(pattern)` | none | Filter files by glob, regex, or function |
|
||||||
|
| `applyFilter(bool)` | `true` | Whether to apply filter during copy/move |
|
||||||
|
| `overwrite(bool)` | `false` | Overwrite existing files at destination |
|
||||||
|
| `preserveTimestamps(bool)` | `false` | Preserve original file timestamps |
|
||||||
|
| `onConflict(mode)` | `'merge'` | `'merge'`, `'error'`, or `'replace'` |
|
||||||
|
|
||||||
### 🔐 Tree Hashing (Cache-Busting)
|
### 🔐 Tree Hashing (Cache-Busting)
|
||||||
|
|
||||||
Compute a deterministic hash of all files in a directory - perfect for cache invalidation:
|
Compute a deterministic hash of all files in a directory - perfect for cache invalidation:
|
||||||
|
|||||||
@@ -3,6 +3,6 @@
|
|||||||
*/
|
*/
|
||||||
export const commitinfo = {
|
export const commitinfo = {
|
||||||
name: '@push.rocks/smartfs',
|
name: '@push.rocks/smartfs',
|
||||||
version: '1.3.0',
|
version: '1.3.2',
|
||||||
description: 'a cross platform extendable fs module'
|
description: 'a cross platform extendable fs module'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -158,7 +158,11 @@ export class SmartFsProviderNode implements ISmartFsProvider {
|
|||||||
if (options?.recursive) {
|
if (options?.recursive) {
|
||||||
await this.listDirectoryRecursive(path, entries, options);
|
await this.listDirectoryRecursive(path, entries, options);
|
||||||
} else {
|
} else {
|
||||||
const dirents = await fs.readdir(path, { withFileTypes: true });
|
// Use readdirSync for reliability — async readdir can return partial results
|
||||||
|
// on XFS/mounted filesystems when the process receives signals (e.g., from
|
||||||
|
// SmartExit/smartshell process group management). The synchronous version
|
||||||
|
// completes the entire getdents64 syscall without event loop interruption.
|
||||||
|
const dirents = fsSync.readdirSync(path, { withFileTypes: true });
|
||||||
|
|
||||||
for (const dirent of dirents) {
|
for (const dirent of dirents) {
|
||||||
const entryPath = pathModule.join(path, dirent.name);
|
const entryPath = pathModule.join(path, dirent.name);
|
||||||
@@ -196,7 +200,8 @@ export class SmartFsProviderNode implements ISmartFsProvider {
|
|||||||
entries: IDirectoryEntry[],
|
entries: IDirectoryEntry[],
|
||||||
options?: IListOptions,
|
options?: IListOptions,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const dirents = await fs.readdir(path, { withFileTypes: true });
|
// Use readdirSync for reliability — see listDirectory comment
|
||||||
|
const dirents = fsSync.readdirSync(path, { withFileTypes: true });
|
||||||
|
|
||||||
for (const dirent of dirents) {
|
for (const dirent of dirents) {
|
||||||
const entryPath = pathModule.join(path, dirent.name);
|
const entryPath = pathModule.join(path, dirent.name);
|
||||||
|
|||||||
Reference in New Issue
Block a user