Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,16 @@ Wabe gives you the ability to configure your `session` parameters. You can choos

The `refreshToken` and the `accessToken` are stored in the `Session` table in the database. The `refreshToken` and the `accessToken` are automatically rotated after each request when `cookieSession` is used to limit the possibilities in case of theft.

## Concurrent refresh protection

Wabe protects refresh-token rotation with a database-backed mutex (`_Mutex` internal class).

During `refresh`, Wabe tries to lock a mutex key derived from the current access/refresh token pair:
- if the lock is acquired, refresh rotation continues
- if the lock is not acquired, the refresh request fails immediately

This prevents concurrent refresh requests from rotating the same session at the same time across multiple instances.

```ts
import { Wabe } from "wabe";

Expand Down
15 changes: 13 additions & 2 deletions packages/wabe-documentation/docs/documentation/codegen.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,21 @@
# Codegen

With Wabe, you can configure when code generation is triggered with `codegen`, and provide your own generation pipeline with `onGenerateCodegen`. This inversion of control lets you run any tool you want (for example `oxfmt`, `prettier`, or a custom generator), without Wabe enforcing a formatter strategy.
Wabe can generate code artifacts from your schema when `codegen` is enabled.

Your callback is fully custom. In this example, we only format generated files with `oxfmt`.
By default, Wabe generates:
- `wabe.ts` (TypeScript schema types)
- `schema.graphql` (printed GraphQL schema)

`onGenerateCodegen` is optional and is called **after** default generation. Use it for post-processing (for example formatting or custom checks).

Codegen runs only when:
- `isProduction` is `false`
- `NODE_ENV !== "test"`
- `codegen.enabled` is `true`

```ts
import { Wabe } from "wabe";

import { resolve } from "node:path";

const run = async () => {
Expand All @@ -15,6 +25,7 @@ const run = async () => {
enabled: true,
path: `${import.meta.dirname}/../generated/`,
},
// Optional: called after wabe.ts/schema.graphql are generated
onGenerateCodegen: async ({ path }) => {
const process = Bun.spawn(["bunx", "oxfmt", "--write", resolve(path)]);
const exitCode = await process.exited;
Expand Down
53 changes: 53 additions & 0 deletions packages/wabe-documentation/docs/documentation/mutex.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Mutex

Wabe provides a database-backed mutex controller to coordinate concurrent operations across multiple server instances.

You can use it through `wabe.controllers.mutex` with three methods:
- `lockMutex(name: string): Promise<boolean>`
- `unlockMutex(name: string): Promise<boolean>`
- `getMutexStatus(name: string): Promise<boolean>`

## Behavior

- `lockMutex` uses an atomic compare-and-set under the hood.
- `unlockMutex` also uses an atomic compare-and-set.
- methods return `true` when the state transition is applied, `false` otherwise.
- mutex state is persisted in the internal `_Mutex` class.
- `_Mutex` is root-only (create/read/update/delete).

## Example

```ts
import { Wabe } from "wabe";

const run = async () => {
const wabe = new Wabe({
// ... other config fields
});

await wabe.start();

const mutexName = "daily-billing-sync";
const acquired = await wabe.controllers.mutex.lockMutex(mutexName);

if (!acquired) {
// Another process already runs this job
return;
}

try {
// Critical section
// run your job here
} finally {
await wabe.controllers.mutex.unlockMutex(mutexName);
}
};

await run();
```

## Notes

- choose deterministic names (for example `feature:userId`).
- always release in a `finally` block.
- if lock acquisition fails, decide explicitly whether to fail fast or retry in your own logic.
Loading
Loading