Initial Commit
This commit is contained in:
@@ -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.
|
||||
Reference in New Issue
Block a user