2025-06-26 23:15:42 +00:00
# smartchok - Technical Hints
2025-11-30 03:04:49 +00:00
## Native File Watching (v2.0.0+)
2025-06-26 23:15:42 +00:00
2025-11-30 03:04:49 +00:00
The module now uses native file watching APIs instead of chokidar, providing cross-runtime support for Node.js, Deno, and Bun.
2025-11-30 17:32:44 +00:00
### Exported Class
The package exports the `Smartwatch` class (not `Smartchok` ):
```typescript
import { Smartwatch } from '@push .rocks/smartchok';
```
2025-11-30 03:04:49 +00:00
### Architecture
```
ts/
├── smartwatch.classes.smartwatch.ts # Main Smartwatch class
├── smartwatch.plugins.ts # Dependencies (smartenv, picomatch, etc.)
├── watchers/
│ ├── index.ts # Factory with runtime detection
│ ├── interfaces.ts # IWatcher interface and types
│ ├── watcher.node.ts # Node.js/Bun implementation (fs.watch)
│ └── watcher.deno.ts # Deno implementation (Deno.watchFs)
└── utils/
└── write-stabilizer.ts # awaitWriteFinish polling implementation
```
### Runtime Detection
Uses `@push.rocks/smartenv` v6.x for runtime detection:
- **Node.js/Bun**: Uses native `fs.watch()` with `{ recursive: true }`
- **Deno**: Uses `Deno.watchFs()` async iterable
2025-06-26 23:15:42 +00:00
### Dependencies
2025-11-30 03:04:49 +00:00
- **picomatch**: Glob pattern matching (zero deps, well-maintained)
- **@push .rocks/smartenv**: Runtime detection (Node.js, Deno, Bun)
- **@push .rocks/smartrx**: RxJS Subject/Observable management
- **@push .rocks/smartpromise**: Deferred promise utilities
- **@push .rocks/lik**: Stringmap for pattern storage
2025-06-26 23:15:42 +00:00
### Why picomatch?
2025-11-30 03:04:49 +00:00
Native file watching APIs don't support glob patterns. Picomatch provides glob pattern matching with:
- Zero dependencies
- 164M+ weekly downloads
- Excellent security profile
- Full glob syntax support
2025-06-26 23:15:42 +00:00
### Event Handling
2025-11-30 03:04:49 +00:00
Native events are normalized to a consistent interface:
| Node.js/Bun Event | Deno Event | Normalized Event |
|-------------------|------------|------------------|
| `rename` (file exists) | `create` | `add` |
| `rename` (file gone) | `remove` | `unlink` |
| `change` | `modify` | `change` |
### awaitWriteFinish Implementation
The `WriteStabilizer` class replaces chokidar's built-in write stabilization:
- Polls file size until stable (configurable threshold: 300ms default)
- Configurable poll interval (100ms default)
- Handles file deletion during write detection
### Platform Requirements
- **Node.js 20+**: Required for native recursive watching on all platforms
- **Deno**: Works on all versions with `Deno.watchFs()`
- **Bun**: Uses Node.js compatibility layer
2025-06-26 23:15:42 +00:00
2025-12-08 17:48:50 +00:00
### Robustness Features (v6.1.0+)
2025-12-08 19:31:48 +00:00
The Node.js watcher includes automatic recovery mechanisms based on learnings from [chokidar ](https://github.com/paulmillr/chokidar ) and known [fs.watch issues ](https://github.com/nodejs/node/issues/47058 ):
2025-12-08 17:48:50 +00:00
**Auto-restart on failure:**
- Watchers automatically restart when errors occur
- Exponential backoff (1s → 30s max)
- Maximum 3 retry attempts before giving up
2025-12-08 19:31:48 +00:00
**Inode tracking (critical for long-running watchers):**
- `fs.watch()` watches the **inode ** , not the path!
- When directories are replaced (git checkout, atomic saves), the inode changes
- Health check detects inode changes and restarts the watcher
- This is the most common cause of "watcher stops working after some time"
2025-12-08 17:48:50 +00:00
**Health check monitoring:**
- 30-second periodic health checks
- Detects when watched paths disappear
2025-12-08 19:31:48 +00:00
- Detects inode changes (directory replacement)
- Detects ENOSPC errors (inotify limit exceeded)
**ENOSPC detection (Linux inotify limit):**
- Detects when `/proc/sys/fs/inotify/max_user_watches` is exceeded
- Logs fix command: `echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p`
2025-12-08 17:48:50 +00:00
**Error isolation:**
- Subscriber errors don't crash the watcher
- All events emitted via `safeEmit()` with try-catch
**Verbose logging:**
- All lifecycle events logged with `[smartwatch]` prefix
- Helps debug watcher issues in production
Example log output:
```
[smartwatch] Starting watcher for 1 base path(s)...
[smartwatch] Started watching: ./test/assets/
[smartwatch] Starting health check (every 30s)
[smartwatch] Watcher started with 1 active watcher(s)
[smartwatch] Health check: 1 watchers active
2025-12-08 19:31:48 +00:00
[smartwatch] Inode changed for ./src: 12345 -> 67890
[smartwatch] fs.watch watches inode, not path - restarting watcher
2025-12-08 17:48:50 +00:00
```
2025-12-08 19:31:48 +00:00
### Known fs.watch Limitations
1. **Watches inode, not path ** - If a directory is replaced, watcher goes stale
2. **inotify limits on Linux ** - Default `max_user_watches` (8192) may be too low
3. **No events for some atomic writes ** - Some editors' save patterns may not trigger events
4. **Platform differences ** - Linux uses inotify, macOS uses FSEvents/kqueue
2025-06-26 23:15:42 +00:00
### Testing
2025-11-29 21:05:51 +00:00
2025-11-30 03:04:49 +00:00
```bash
pnpm test
```
Tests verify:
- Creating Smartwatch instance
- Adding glob patterns
- Receiving 'add' events for new files
- Graceful shutdown
## Dev Dependencies
- Using `@git.zone/tstest` v3.x with tapbundle
- Import from `@git.zone/tstest/tapbundle`