Initial Commit

This commit is contained in:
2026-05-27 23:29:53 +01:00
commit fbc70f96e6
10000 changed files with 1102152 additions and 0 deletions

View File

@@ -0,0 +1,64 @@
# `blue-green-slot` — hot-reload primitive for the in-memory router
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
`InMemoryBlueGreenSlot` is the `BlueGreenSlot` implementation paired with `InMemoryRouter`. It is a thin extension of `@suaveplan/api-router`'s reference `InMemoryBlueGreenSlot`, kept locally so consumers of `@suaveplan/api-router-in-memory` import their router adapter AND their slot from a single package — symmetric with `@suaveplan/api-router-hono`'s `HonoBlueGreenSlot`.
The reference implementation in `api-router` already satisfies every invariant the conformance suite asserts (set/flip/drain/dispose/abort), so this class adds no behaviour; it exists solely to keep the public surface uniform.
## Features
- Inherits the reference impl's three-position state machine (`live` / `shadow` / `draining`).
- `flip()` returns a `DrainingHandle` whose `settled` promise resolves once all in-flight requests against the previous live slot complete.
- `dispose(slotId, { timeoutMs })` races a timer against drain settle; on timeout, aborts the slot's `AbortController` and throws `ROUTER.SLOT_DISPOSE_TIMEOUT`.
- `acquire(slotId)` / `release(slotId)` are wired by the router so the pending counter reflects in-flight dispatch.
## Usage: Basic
```ts
import { InMemoryBlueGreenSlot } from "@suaveplan/api-router-in-memory/blue-green-slot";
const slot = new InMemoryBlueGreenSlot("billing", () => "v1");
slot.setShadow(() => "v2");
const draining = slot.flip();
// slot.live now serves v2; draining points at the previous v1 slot.
await draining.settled;
```
## Usage: Advanced
Drive a hot-reload from a manifest-driven loader, with abort-on-timeout safety:
```ts
import {
createInMemoryBlueGreenSlot,
} from "@suaveplan/api-router-in-memory/blue-green-slot";
const slot = createInMemoryBlueGreenSlot("billing", () => "v1");
slot.setShadow(() => "v2");
const drain = slot.flip();
try {
await slot.dispose(drain.slotId, { timeoutMs: 50 });
} catch (err) {
// dispose timed out — the AbortController on the previous slot has
// already fired; in-flight handlers should observe their AbortSignal
// and unwind. Bubble the error up to the reload driver.
throw err;
}
```
## API Reference
- `class InMemoryBlueGreenSlot extends @suaveplan/api-router.InMemoryBlueGreenSlot`
- `constructor(plugin: string, initialHandler: SlotHandler)`
- Inherits `live`, `shadow`, `setShadow`, `flip`, `dispose`, `acquire`, `release`.
- `createInMemoryBlueGreenSlot(plugin, initialHandler): BlueGreenSlot` — convenience factory typed as the abstract interface.
## Implementation Notes
The subclass exists to keep the package's exported surface aligned with `api-router-hono` (HonoBlueGreenSlot is its own concrete class because the live pointer is one mutable field that swaps atomically — see that package's notes). For the in-memory adapter there is no driver-specific work, so this is intentionally a no-op extension. The conformance suite in `api-router/testing` accepts either the parent class or this subclass.