npm package: @jfk87/temp-dir
Description: Create and cleanup temporary directories safely.
Latest version: 0.1.0

# temp-dir

Create and clean up temporary directories safely for Node.js (TypeScript, ESM + CJS).

- Guaranteed cleanup (even if your callback throws)
- Windows-friendly cleanup retries (handles common EPERM/EBUSY races)
- Optional process exit + signal hooks
- Tiny, dependency-free

## Install

```bash
npm i temp-dir
```

## Quick start

```ts
import { withTempDir } from "temp-dir";

await withTempDir(async (dir) => {
  console.log("temp:", dir);
});
```

## Want the full object?

```ts
import { withTempDir } from "temp-dir";

await withTempDir(
  async (tmp) => {
    console.log(tmp.path);
    await tmp.writeFile("nested/hello.txt", "world");
  },
  { passObject: true, prefix: "myapp-" }
);
```

## Manual control

```ts
import { createTempDir } from "temp-dir";

const tmp = await createTempDir({ prefix: "manual-" });

try {
  await tmp.writeFile("data.json", JSON.stringify({ ok: true }));
} finally {
  await tmp.cleanup();
}
```

---

## API

### `createTempDir(options?): Promise<TempDir>`

Creates a unique temp directory and returns a handle.

`TempDir`:

- `path: string` — absolute path to the directory
- `cleanup(reason?): Promise<void>` — deletes the directory (safe to call multiple times)
- `writeFile(relativePath, data): Promise<string>` — writes a file inside the temp dir (creates parent dirs)

### `withTempDir(fn, options?): Promise<T>`

Runs `fn` with a temp dir, then cleans up in a `finally` block.

By default, `fn` receives a `string` path.

If `passObject: true`, `fn` receives the full `TempDir` object.

---

## Options

### `TempDirOptions`

| Option | Type | Default | Description |
|---|---|---:|---|
| `baseDir` | `string` | `os.tmpdir()` | Base directory where temp dirs are created |
| `prefix` | `string` | `"tmp-"` | Prefix passed to `mkdtemp()` (random suffix is appended) |
| `keep` | `boolean` | `false` | If `true`, cleanup does nothing (useful for debugging) |
| `recursive` | `boolean` | `true` | Cleanup removes recursively |
| `force` | `boolean` | `true` | Cleanup ignores missing paths where possible |
| `cleanupOnExit` | `boolean` | `true` | Register best-effort cleanup on process exit/crash |
| `cleanupOnSignals` | `boolean` | `true` | Also hook SIGINT/SIGTERM (only if `cleanupOnExit`) |
| `retries` | `number` | `8` | Cleanup retries for transient lock errors (Windows) |
| `retryDelayMs` | `number` | `15` | Initial delay in ms; uses exponential backoff |
| `ensureBaseDir` | `boolean` | `false` | Create `baseDir` if it doesn't exist (`mkdir -p`) |
| `isRetryableError` | `(err) => boolean` | built-in | Decide which errors are retryable (default: EPERM/EBUSY/ENOTEMPTY) |
| `onCleanupError` | `(err, ctx) => void` | `undefined` | Called when cleanup fails (useful for logging) |

### `WithTempDirOptions`

All `TempDirOptions` plus:

| Option | Type | Default | Description |
|---|---|---:|---|
| `passObject` | `boolean` | `false` | If true, pass the full `TempDir` object to the callback |

---

## Notes

### About `baseDir`

If you set a custom `baseDir` and enable `ensureBaseDir`, the parent directory may remain after cleanup.  
That directory is user-owned, so the library only deletes the specific temp directory it created.

### TypeScript inference tip

If your editor treats the callback param as a `string` while using `passObject: true`, annotate it:

```ts
import type { TempDir } from "temp-dir";

await withTempDir(async (tmp: TempDir) => {
  // ...
}, { passObject: true });
```

## License

MIT