or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

index.md

index.mddocs/

0

# Watchr

1

2

Watchr provides a normalized API for file system watching across different Node.js versions. It supports nested/recursive file and directory watching with accurate detailed events for file/directory creations, updates, and deletions. The library offers two main concepts: Watcher (wraps native file system watching with reliability and deep watching support) and Stalker (manages multiple watchers for the same path efficiently).

3

4

## Package Information

5

6

- **Package Name**: watchr

7

- **Package Type**: npm

8

- **Language**: JavaScript (with Flow type annotations)

9

- **Installation**: `npm install watchr`

10

11

## Core Imports

12

13

```javascript

14

const { open, create, Stalker, Watcher } = require('watchr');

15

```

16

17

For destructuring specific components:

18

19

```javascript

20

const { open } = require('watchr'); // Simple usage

21

const { create } = require('watchr'); // Advanced usage

22

const { Stalker, Watcher } = require('watchr'); // Direct class access

23

```

24

25

## Basic Usage

26

27

Simple file watching with the convenience function:

28

29

```javascript

30

const watchr = require('watchr');

31

32

// Define change listener

33

function listener(changeType, fullPath, currentStat, previousStat) {

34

switch (changeType) {

35

case 'update':

36

console.log('File updated:', fullPath);

37

break;

38

case 'create':

39

console.log('File created:', fullPath);

40

break;

41

case 'delete':

42

console.log('File deleted:', fullPath);

43

break;

44

}

45

}

46

47

// Start watching with error handling

48

function next(err) {

49

if (err) return console.log('Watch failed:', err);

50

console.log('Watch successful');

51

}

52

53

// Watch the current directory

54

const stalker = watchr.open(process.cwd(), listener, next);

55

56

// Stop watching when done

57

stalker.close();

58

```

59

60

Advanced usage with configuration:

61

62

```javascript

63

const { create } = require('watchr');

64

65

const stalker = create('/path/to/watch');

66

67

// Configure watching options

68

stalker.setConfig({

69

stat: null,

70

interval: 5007,

71

persistent: true,

72

catchupDelay: 2000,

73

preferredMethods: ['watch', 'watchFile'],

74

followLinks: true,

75

ignorePaths: false,

76

ignoreHiddenFiles: false,

77

ignoreCommonPatterns: true,

78

ignoreCustomPatterns: null

79

});

80

81

// Set up event listeners

82

stalker.on('change', listener);

83

stalker.on('log', console.log);

84

stalker.once('close', (reason) => {

85

console.log('Watcher closed:', reason);

86

});

87

88

// Start watching

89

stalker.watch(next);

90

```

91

92

## Architecture

93

94

Watchr uses a two-layer architecture:

95

96

- **Stalker Layer**: Multiple Stalkers can watch the same path, providing a many-to-one relationship for event listeners

97

- **Watcher Layer**: Only one Watcher exists per path, handling the actual file system watching

98

- **Event Bubbling**: Events from child watchers bubble up to parent watchers

99

- **Method Fallback**: Prefers `fs.watch`, automatically falls back to `fs.watchFile` if needed

100

- **Cross-platform Compatibility**: Handles differences between Node.js versions and operating systems

101

102

## Capabilities

103

104

### Simple File Watching

105

106

Quick setup for basic file watching needs using the convenience function.

107

108

```javascript { .api }

109

/**

110

* Alias for creating a new Stalker with basic configuration and immediate watching

111

* @param {string} path - The path to watch

112

* @param {function} changeListener - The change listener for the Watcher

113

* @param {function} next - The completion callback for Watcher#watch

114

* @returns {Stalker} stalker instance

115

*/

116

function open(path, changeListener, next);

117

```

118

119

### Advanced File Watching

120

121

Create stalkers with full configuration control for complex watching scenarios.

122

123

```javascript { .api }

124

/**

125

* Alias for creating a new Stalker instance

126

* @param {...any} args - Arguments passed to Stalker constructor

127

* @returns {Stalker} stalker instance

128

*/

129

function create(...args);

130

```

131

132

### Stalker Management

133

134

The Stalker class manages multiple watchers for the same path, ensuring efficient resource usage.

135

136

```javascript { .api }

137

/**

138

* A watcher of the watchers. Events listened to on the stalker are proxied to the attached watcher.

139

* When the watcher is closed, the stalker's listeners are removed.

140

* When all stalkers for a watcher are removed, the watcher will close.

141

*/

142

class Stalker extends EventEmitter {

143

/**

144

* Create a new Stalker for the given path

145

* @param {string} path - The path to watch

146

*/

147

constructor(path);

148

149

/**

150

* Close the stalker, and if it is the last stalker for the path, close the watcher too

151

* @param {string} [reason] - Optional reason to provide for closure

152

* @returns {Stalker} this instance for chaining

153

*/

154

close(reason);

155

156

/**

157

* Configure the underlying watcher with various options

158

* @param {...any} args - Arguments passed to watcher's setConfig method

159

* @returns {Stalker} this instance for chaining

160

*/

161

setConfig(...args);

162

163

/**

164

* Start watching the path and its children

165

* @param {...any} args - Arguments passed to watcher's watch method

166

* @returns {Stalker} this instance for chaining

167

*/

168

watch(...args);

169

}

170

```

171

172

### Low-Level Watching

173

174

Direct access to the Watcher class for specialized use cases.

175

176

```javascript { .api }

177

/**

178

* Watches a path and if it's a directory, its children too.

179

* Emits change events for updates, deletions, and creations.

180

*/

181

class Watcher extends EventEmitter {

182

/**

183

* Create a new Watcher for the given path

184

* @param {string} path - The path to watch

185

*/

186

constructor(path);

187

188

/**

189

* Configure the Watcher with various options

190

* @param {WatcherOpts} opts - Configuration options

191

* @returns {Watcher} this instance for chaining

192

*/

193

setConfig(opts);

194

195

/**

196

* Setup watching for the path and its children

197

* @param {ResetOpts} [opts] - Watch options

198

* @param {function} next - Completion callback with signature (error) => void

199

* @returns {Watcher} this instance for chaining

200

*/

201

watch(opts, next);

202

watch(next);

203

204

/**

205

* Close the watching abilities of this watcher and its children

206

* @param {string} [reason='unknown'] - Reason for closure

207

* @returns {Watcher} this instance for chaining

208

*/

209

close(reason);

210

211

/**

212

* Get the stat for the path of the watcher

213

* @param {ResetOpts} opts - Options

214

* @param {function} next - Callback with signature (error, stat) => void

215

* @returns {Watcher} this instance for chaining

216

*/

217

getStat(opts, next);

218

219

/**

220

* Emit a log event with the given arguments

221

* @param {...any} args - Arguments for logging

222

* @returns {Watcher} this instance for chaining

223

*/

224

log(...args);

225

}

226

```

227

228

## Events

229

230

Both Stalker and Watcher classes extend EventEmitter and emit the following events:

231

232

### Change Event

233

234

```javascript { .api }

235

/**

236

* Emitted when a file or directory change is detected

237

* @param {string} changeType - 'update', 'create', or 'delete'

238

* @param {string} fullPath - Full path to the changed file/directory

239

* @param {Stats|null} currentStat - Current Stats object (null for deletions)

240

* @param {Stats|null} previousStat - Previous Stats object (null for creations)

241

*/

242

stalker.on('change', (changeType, fullPath, currentStat, previousStat) => {

243

// Handle the change

244

});

245

```

246

247

### Close Event

248

249

```javascript { .api }

250

/**

251

* Emitted when the watcher is closed

252

* @param {string} reason - String describing why the watcher was closed

253

*/

254

stalker.on('close', (reason) => {

255

// Handle closure

256

});

257

```

258

259

### Log Event

260

261

```javascript { .api }

262

/**

263

* Emitted for debugging information

264

* @param {string} logLevel - Log level string

265

* @param {...any} args - Additional logging arguments

266

*/

267

stalker.on('log', (logLevel, ...args) => {

268

// Handle log message

269

});

270

```

271

272

### Error Event

273

274

```javascript { .api }

275

/**

276

* Emitted when an error occurs

277

* @param {Error} error - Error object

278

*/

279

stalker.on('error', (error) => {

280

// Handle error

281

});

282

```

283

284

## Configuration Types

285

286

```javascript { .api }

287

/**

288

* Configuration options for Watcher

289

*/

290

interface WatcherOpts {

291

/** Pre-existing stat object for the path */

292

stat?: Stats;

293

/** Polling interval for watchFile method (default: 5007) */

294

interval?: number;

295

/** Whether watching should keep the process alive (default: true) */

296

persistent?: boolean;

297

/** Delay after change events for accurate detection (default: 2000) */

298

catchupDelay?: number;

299

/** Order of watch methods to attempt (default: ['watch', 'watchFile']) */

300

preferredMethods?: Array<'watch' | 'watchFile'>;

301

/** Whether to follow symlinks (default: true) */

302

followLinks?: boolean;

303

/** Array of paths to ignore or false to ignore none (default: false) */

304

ignorePaths?: Array<string> | false;

305

/** Whether to ignore files/dirs starting with '.' (default: false) */

306

ignoreHiddenFiles?: boolean;

307

/** Whether to ignore common patterns like .git, .svn (default: true) */

308

ignoreCommonPatterns?: boolean;

309

/** Custom regex for ignoring paths */

310

ignoreCustomPatterns?: RegExp;

311

}

312

313

/**

314

* Options for watch operations

315

*/

316

interface ResetOpts {

317

/** Whether to close existing watchers and setup new ones (default: false) */

318

reset?: boolean;

319

}

320

```

321

322

## Error Handling

323

324

Watchr provides comprehensive error handling through events and callbacks:

325

326

```javascript

327

const stalker = create('/path/to/watch');

328

329

// Handle errors through events

330

stalker.on('error', (error) => {

331

console.error('Watch error:', error.message);

332

// Implement error recovery logic

333

});

334

335

// Handle errors in watch callback

336

stalker.watch((error) => {

337

if (error) {

338

console.error('Failed to start watching:', error.message);

339

return;

340

}

341

console.log('Watching started successfully');

342

});

343

```

344

345

Common error scenarios:

346

- **Permission errors**: Insufficient permissions to watch the path

347

- **Path not found**: The specified path doesn't exist

348

- **Method failures**: Both `fs.watch` and `fs.watchFile` fail

349

- **System limits**: Too many open file watchers (platform-dependent)

350

351

## Usage Examples

352

353

**Watching with custom ignore patterns:**

354

355

```javascript

356

const stalker = create('/project/directory');

357

358

stalker.setConfig({

359

ignoreHiddenFiles: true,

360

ignoreCommonPatterns: true,

361

ignoreCustomPatterns: /\.(log|tmp)$/,

362

ignorePaths: ['/project/directory/node_modules']

363

});

364

365

stalker.on('change', (changeType, fullPath) => {

366

console.log(`${changeType}: ${fullPath}`);

367

});

368

369

stalker.watch((err) => {

370

if (err) throw err;

371

console.log('Watching project directory...');

372

});

373

```

374

375

**Watching with specific method preference:**

376

377

```javascript

378

const stalker = create('/slow/network/path');

379

380

// Prefer polling for network paths

381

stalker.setConfig({

382

preferredMethods: ['watchFile'],

383

interval: 2000, // Check every 2 seconds

384

persistent: false // Don't keep process alive

385

});

386

387

stalker.watch((err) => {

388

if (err) throw err;

389

console.log('Polling network path...');

390

});

391

```

392

393

**Multiple watchers on same path:**

394

395

```javascript

396

// Multiple stalkers can watch the same path efficiently

397

const stalker1 = create('/shared/path');

398

const stalker2 = create('/shared/path');

399

400

stalker1.on('change', (type, path) => {

401

console.log('Stalker 1 detected:', type, path);

402

});

403

404

stalker2.on('change', (type, path) => {

405

console.log('Stalker 2 detected:', type, path);

406

});

407

408

// Only one underlying watcher is created

409

stalker1.watch(() => console.log('Stalker 1 ready'));

410

stalker2.watch(() => console.log('Stalker 2 ready'));

411

```