# smartwatch - Technical Hints

## Native File Watching (v6.0.0+)

The module provides cross-runtime file watching support:
- **Node.js/Bun**: Uses [chokidar](https://github.com/paulmillr/chokidar) v5
- **Deno**: Uses native `Deno.watchFs()`

### Exported Class

The package exports the `Smartwatch` class:
```typescript
import { Smartwatch } from '@push.rocks/smartwatch';
```

### Architecture

```
ts/
├── smartwatch.classes.smartwatch.ts   # Main Smartwatch class
├── smartwatch.plugins.ts              # Dependencies
├── watchers/
│   ├── index.ts                       # Factory with runtime detection
│   ├── interfaces.ts                  # IWatcher interface and types
│   ├── watcher.node.ts                # Node.js/Bun: chokidar wrapper
│   └── watcher.deno.ts                # Deno: Deno.watchFs wrapper
└── utils/
    └── write-stabilizer.ts            # awaitWriteFinish polling implementation
```

### Runtime Detection

Uses `@push.rocks/smartenv` for runtime detection:
- **Node.js/Bun**: Uses chokidar (battle-tested file watcher)
- **Deno**: Uses `Deno.watchFs()` async iterable

### Dependencies

- **chokidar**: Battle-tested file watcher for Node.js/Bun
- **picomatch**: Glob pattern matching (zero deps)
- **@push.rocks/smartenv**: Runtime detection
- **@push.rocks/smartrx**: RxJS Subject/Observable management
- **@push.rocks/smartpromise**: Deferred promise utilities
- **@push.rocks/lik**: Stringmap for pattern storage

### Chokidar Features (Node.js/Bun)

The Node.js watcher (`ts/watchers/watcher.node.ts`) is a thin ~100 line wrapper around chokidar v5:

```typescript
chokidar.watch(paths, {
  persistent: true,
  ignoreInitial: false,
  followSymlinks: options.followSymlinks,
  depth: options.depth,
  atomic: true,  // Handles atomic writes (delete+recreate, temp+rename)
  awaitWriteFinish: { stabilityThreshold: 300, pollInterval: 100 },
});
```

**Chokidar handles all edge cases:**
- Atomic writes (temp file + rename pattern) → emits single 'change' event
- Delete + recreate detection → emits single 'change' event
- Inode tracking
- Cross-platform differences (inotify, FSEvents, etc.)
- Debouncing
- Write stabilization
- ENOSPC (inotify limit) errors

### Event Handling

Events are normalized across all runtimes:

| Event | Description |
|-------|-------------|
| `add` | File added |
| `change` | File modified |
| `unlink` | File removed |
| `addDir` | Directory added |
| `unlinkDir` | Directory removed |
| `ready` | Initial scan complete |
| `error` | Error occurred |

### Platform Requirements

- **Node.js 20+**: Required for chokidar v5
- **Deno**: Works on all versions with `Deno.watchFs()`
- **Bun**: Uses Node.js compatibility layer with chokidar

### Testing

```bash
pnpm test
```

Test files:
- **test.basic.ts** - Core functionality (add, change, unlink events)
- **test.inode.ts** - Atomic write detection (delete+recreate, temp+rename)
- **test.stress.ts** - Rapid modifications, many files, interleaved operations

Tests verify:
- Creating Smartwatch instance
- Adding glob patterns
- Receiving 'add', 'change', 'unlink' events
- Atomic write detection (delete+recreate → change event)
- Temp file + rename pattern detection
- Rapid file modifications (debouncing)
- Many files created rapidly
- Interleaved add/change/delete operations
- Graceful shutdown

## Dev Dependencies

- Using `@git.zone/tstest` v3.x with tapbundle
- Import from `@git.zone/tstest/tapbundle`

## Static Rust Binaries

- The Rust watcher binaries in `dist_rust/` are statically linked (static-pie) via `"static": true` in the `@git.zone/tsrust` block of `.smartconfig.json` (tsrust >= 1.4.1). They run on both glibc (Debian/Ubuntu) and musl (Alpine) systems.
- tsrust was previously not wired into the build at all; `pnpm build` (and tstest via `test:before`) now produce `dist_rust/smartwatch-rust_linux_amd64` / `_linux_arm64`. The TS loader resolves suffixed names first with an unsuffixed fallback (ts/watchers/watcher.rust.ts).
- tsrust injects `RUSTFLAGS="-C target-feature=+crt-static"` only into its own per-target cargo invocations and verifies the result (no `PT_INTERP` ELF header). Keep `rust/.cargo/config.toml` free of `rustflags` entries: the injected env variable would replace them, and a repo-wide `rustflags` would also break plain `cargo test`/`cargo check` (proc-macros cannot build with `+crt-static` on linux-gnu without an explicit `--target`).
- `pnpm run test:rust` runs the Rust unit tests; plain `cargo test` in `rust/` works as well.
- Verify linkage manually with `ldd dist_rust/<binary>_linux_amd64` → "statically linked".
- The legacy `npmextra.json` was migrated to `.smartconfig.json` (modern release.targets.npm shape) in the same change; without it, `gitzone release` had no npm target.

## Tooling Constraint: tsbuild pinned at ^4.3.0

- Upgrading @git.zone/tsbuild to 4.4.x surfaces ~29 type errors from stale dependency typings (smartrust 1.3.2, smartpromise 4.2.3, smartrx 3.0.10, chokidar 5.0.0 d.ts) plus a few in ts/. The static-binary rollout (2026-06-10) kept tsbuild at 4.3.0; upgrading tsbuild needs a dependency modernization pass (notably smartrust ^1.4.x) first.
