# Internal Events

Alepha uses a hook-based event system for lifecycle management and cross-service communication.

## Confusion Warning

- `$hook` is not a React Hook (e.g. `useState`). It is an event listener.
- `$hook` is not a pub/sub system. Use `$topic` for pub/sub.

## Lifecycle

When `alepha.start()` is called, the framework emits `configure`, `start` and `ready`, in that order; `alepha.stop()` emits `stop`. Each one waits for every hook of the previous one to finish. [The Alepha Instance](https://latest.alepha.dev/docs/guides-core-alepha-instance.md#lifecycle) draws the whole sequence, failure path included.

| Hook        | When                                    | Typical use                               |
| ----------- | --------------------------------------- | ----------------------------------------- |
| `configure` | Before start, after container is locked | Register providers, resolve configuration |
| `start`     | After configure                         | Connect to databases, start listeners     |
| `ready`     | After start                             | Application is fully operational          |
| `stop`      | On shutdown (SIGINT/SIGTERM or manual)  | Close connections, flush buffers          |

All four receive the `Alepha` instance as payload.

Hooks of one event run one after the other, each awaited before the next, never in parallel. A hook that throws during `configure`, `start` or `ready` aborts the boot. During `stop`, every hook runs even when one throws, and `stop()` reports the failures together once all of them have run.

## Using $hook

Register hooks with the `$hook` primitive. It must be a class property, on a singleton service: a `$hook` instantiated after the app has started (a scoped or transient service built during a request) throws, since a hook is never unregistered and would leak once per instantiation.

```typescript
import { $hook } from "alepha";
import { $logger } from "alepha/logger";

class DatabaseService {
  log = $logger();

  onStart = $hook({
    on: "start",
    handler: async () => {
      await this.connectToDatabase();
      this.log.info("Database connected");
    },
  });

  onStop = $hook({
    on: "stop",
    handler: async () => {
      await this.disconnectFromDatabase();
      this.log.info("Database disconnected");
    },
  });
}
```

### Hook options

```typescript
$hook({
  on: "start", // required: event name
  handler: async () => {
    /* ... */
  }, // required: callback
  priority: "first", // optional: "first" | "last" (default: insertion order)
});
```

`priority: "first"` places the hook at the front of the execution queue. `priority: "last"` places it at the end. Without a priority, hooks execute in registration order (which follows dependency order). For finer ordering relative to specific services, `before` and `after` accept a service class or instance (or an array of them) whose hooks this one must run before or after:

```typescript
class CacheWarmer {
  onStart = $hook({
    on: "start",
    after: DatabaseService, // the database is connected first
    handler: async () => {
      /* ... */
    },
  });
}
```

`priority` sorts first; `before` and `after` then order hooks within the same priority tier.

### Hook call tracking

Each `$hook` instance tracks how many times it has been called:

```typescript
const alepha = Alepha.create().with(App);
await alepha.start();

const app = alepha.inject(App);
console.log(app.onStart.called); // 1
```

> This is useful for testing to ensure hooks are called the expected number of times.

## Built-in Hooks

The core `Hooks` interface defines:

```typescript
interface Hooks {
  configure: Alepha; // configuration phase
  start: Alepha; // start phase
  ready: Alepha; // ready phase
  stop: Alepha; // shutdown phase
  "state:mutated": {
    // state change notification
    key: keyof State;
    value: any;
    prevValue: any;
  };
  "state:registered": { atom: Atom }; // an atom was registered
}
```

Other modules extend this interface. For example, `alepha/logger` adds `log:entry`, `alepha/server` adds server-related hooks, and so on.

## Custom Hooks

Define custom hooks using TypeScript module augmentation:

```typescript
declare module "alepha" {
  interface Hooks {
    "billing:invoice:created": {
      invoiceId: string;
      amount: number;
    };
  }
}
```

### Listening to custom hooks

As a class property with `$hook`:

```typescript
class NotificationService {
  onInvoice = $hook({
    on: "billing:invoice:created",
    handler: async ({ invoiceId, amount }) => {
      await this.sendReceipt(invoiceId, amount);
    },
  });
}
```

Or directly on the event manager:

```typescript
alepha.events.on("billing:invoice:created", ({ invoiceId, amount }) => {
  console.log(`Invoice ${invoiceId} created for ${amount}`);
});
```

`alepha.events.on()` returns an unsubscribe function:

```typescript
const unsubscribe = alepha.events.on("billing:invoice:created", handler);
// later...
unsubscribe();
```

### Emitting custom hooks

```typescript
await alepha.events.emit("billing:invoice:created", {
  invoiceId: "inv_123",
  amount: 99.99,
});
```

The `emit` method accepts options:

```typescript
await alepha.events.emit("billing:invoice:created", payload, {
  log: true, // log execution timing of each hook
  catch: true, // catch errors and log them instead of throwing
});
```

With `log: true`, a `report` object (`{ completed: [], failed: [] }`) you pass in the options is filled with the callers of the hooks that finished and, under `catch: true`, of those that threw (without `catch`, the first throw ends the emit). `alepha.events.callers(event)` lists who listens to an event, which is what to print when a hook is missing.

## Compiled Events (advanced)

`emit()` already compiles and caches an optimized executor per event, so there is no overhead
to avoid in normal code. `compile()` exists to hoist that one cache lookup out of a hot loop:

```typescript
// After all hooks are registered (e.g. after start)
const onRequest = alepha.events.compile("server:request:before", {
  catch: true,
});

// In the request handler - returns void if sync, Promise if async
const result = onRequest({ request, route });
if (result) await result;
```

## Naming hooks

Hook names follow `namespace[:subject]:verb[:phase]`, with lower camel case
inside each segment. Interceptable operations end in `before` and their later
observers in `after`. Observed operations use `begin`, `success`, `error` and
`end`. Facts use a past participle without a phase, for example
`payments:captured` or `cache:hit`. No `onX` or `beforeX` segment is allowed.
The only bare names are `configure`, `start`, `ready` and `stop`; log entries use
`log:entry`. The explicitly preserved job lifecycle/control family remains
`job:begin`, `job:success`, `job:error`, `job:end` and `job:cancel`.
`check:conventions` checks every `Hooks` declaration, tests and apps included.
Retired framework names fail immediately and name their replacement.

The complete migration table is in the changelog. New application facts can be
named `myApp:task:completed`; an interceptable operation can use
`myApp:task:run:before`. Register listeners before startup. A retired name is an
error even when TypeScript has been bypassed, so stale listeners fail at boot.
