# Resource management

For automatic cleanup of resources

Resources like database connections, file handles, and locks need cleanup. Traditional approaches are error-prone:

```ts
// 🚨 Manual cleanup is easy to forget
const conn = openConnection();
doWork(conn);
conn.close(); // What if doWork throws?
```

```ts
// 🚨 try/finally is verbose and doesn't compose
const conn = openConnection();
try {
  doWork(conn);
} finally {
  conn.close();
}
```

The [`using`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/using) declaration is a new JavaScript feature that automatically disposes resources when they go out of scope:

```ts
const process = () => {
  using conn = openConnection();
  doWork(conn);
}; // conn is automatically disposed here
```

This works even if `doWork` throws—disposal is guaranteed.

## Disposable resources

A resource is disposable if it has a `[Symbol.dispose]` method:

```ts
interface Disposable {
  [Symbol.dispose](): void;
}
```

For async cleanup, use `[Symbol.asyncDispose]` with `await using`:

```ts
interface AsyncDisposable {
  [Symbol.asyncDispose](): PromiseLike<void>;
}
```

## Block scopes

Use block scopes to control exactly when resources are disposed:

```ts
const createLock = (name: string): Disposable => ({
  [Symbol.dispose]: () => {
    console.log(`unlock:${name}`);
  },
});

const process = () => {
  console.log("start");

  {
    using lock = createLock("a");
    console.log("critical-section-a");
  } // lock "a" released here

  console.log("between");

  {
    using lock = createLock("b");
    console.log("critical-section-b");
  } // lock "b" released here

  console.log("end");
};

// Output:
// "start"
// "critical-section-a"
// "unlock:a"
// "between"
// "critical-section-b"
// "unlock:b"
// "end"
```

## Combining with Result

`Result` and `Disposable` are orthogonal:

- **Result** answers: "Did the operation succeed?"
- **Disposable** answers: "When do we clean up resources?"

Early returns from `Result` checks don't bypass `using`—disposal is guaranteed on any exit path (see below).

## DisposableStack

When acquiring multiple resources, use [`DisposableStack`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DisposableStack) to ensure all are cleaned up:

```ts
const processResources = (): Result<string, CreateResourceError> => {
  using stack = new DisposableStack();

  const db = createResource("db");
  if (!db.ok) return db; // stack disposes nothing yet

  stack.use(db.value);

  const file = createResource("file");
  if (!file.ok) return file; // stack disposes db

  stack.use(file.value);

  return ok("processed");
}; // stack disposes file, then db (reverse order)
```

The pattern is simple:

1. Create a `DisposableStack` with `using`
2. Try to create a resource (returns `Result`)
3. If failed, return early—stack disposes what's been acquired
4. If succeeded, add to stack with `stack.use()`
5. Repeat for additional resources

For async resources, use [`AsyncDisposableStack`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/AsyncDisposableStack) with `await using`.

API overview:

- `stack.use(resource)` — adds a disposable resource
- `stack.defer(fn)` — adds a cleanup function (like Go's `defer`)
- `stack.adopt(value, cleanup)` — wraps a non-disposable value with cleanup
- `stack.move()` — transfers ownership to caller

### The use-and-move pattern

When a factory function creates resources for use elsewhere, use `move()` to transfer ownership:

```ts
interface OpenFiles extends Disposable {
  readonly handles: ReadonlyArray<FileHandle>;
}

const openFiles = (
  paths: ReadonlyArray<string>,
): Result<OpenFiles, OpenFileError> => {
  using stack = new DisposableStack();

  const handles: Array<FileHandle> = [];
  for (const path of paths) {
    const file = open(path);
    if (!file.ok) return file; // Error: stack cleans up opened files

    stack.use(file.value);
    handles.push(file.value);
  }

  // Success: transfer ownership to caller
  const cleanup = stack.move();
  return ok({
    handles,
    [Symbol.dispose]: () => cleanup.dispose(),
  });
};

const processFiles = (): Result<void, MyError> => {
  const result = openFiles(["a.txt", "b.txt", "c.txt"]);
  if (!result.ok) return result;

  using files = result.value;

  // ... use files.handles ...

  return ok();
}; // files cleaned up here
```

Without `move()`, the stack would dispose files when `openFiles` returns—even on success.

## Ready to use

[TypeScript 5.2+](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-2.html#using-declarations-and-explicit-resource-management) implements the `using` keyword, and Evolu polyfills runtime resource management for environments that still need it (Safari and React Native), see [polyfills setup](https://evolu.dev/docs/library#polyfills).

## Learn more

- [MDN: Resource management](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Resource_management)
- [MDN: using statement](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/using)
- [MDN: DisposableStack](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DisposableStack)
- [MDN: AsyncDisposableStack](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/AsyncDisposableStack)
- [`Result.test.ts`](https://github.com/evoluhq/evolu/blob/main/packages/common/test/Result.test.ts) for comprehensive usage patterns
- [Resources](https://evolu.dev/docs/api-reference/common/Resources) for reference-counted shared resources with delayed disposal