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,70 @@
# `subscriptions.ts`
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
Bridges the `WsBroker` contract from `@suaveplan/api-ws` to GraphQL
`Subscription` resolvers via `AsyncIterable<Payload>`. The Yoga engine
wires `graphql-ws` over the host's already-mounted WebSocket transport;
this module is the per-subscription helper that converts a broker `topic`
+ `tenant` pair into an iterator the engine can consume.
Tenant filtering is enforced at the bridge: only `BrokerEnvelope` items
whose `tenant === tenantArg` reach the GraphQL iterator. Closing the
iterator (`return()` or the explicit `close()` method) calls
`subscription.unsubscribe()` on the broker so the WS slot is released
within one tick.
## Features
- Returns `AsyncIterable<Payload>` with an explicit `close()` accessor.
- Queues payloads when no consumer is awaiting; resolves immediately when
a consumer is parked.
- Calls `GraphqlYogaError("SUBSCRIPTION_TRANSPORT_UNAVAILABLE")` when the
host hands a `null` broker, satisfying the design doc §10.4 contract.
## Usage: Basic
```ts
import { createInMemoryWsBroker } from "@suaveplan/api-ws-broker-in-memory";
import { subscribeBrokerTopic } from "@suaveplan/api-graphql-yoga";
const broker = createInMemoryWsBroker();
const iterable = subscribeBrokerTopic(
broker,
"billing.invoice.created",
"tenant-a" as never
);
for await (const event of iterable) {
console.log(event);
break;
}
await iterable.close();
```
## Usage: Advanced
```ts
import { subscribeBrokerTopic } from "@suaveplan/api-graphql-yoga";
const subscription = {
invoiceCreated(_parent: unknown, _args: unknown, ctx: { broker: never; tenantId: never }) {
return subscribeBrokerTopic(ctx.broker, "billing.invoice.created", ctx.tenantId);
},
};
```
## API Reference
- `subscribeBrokerTopic(broker, topic, tenant)` — returns
`AsyncIterable<Payload> & { close(): Promise<void> }`.
## Implementation Notes
The internal `queue` + `waiters` arrays form a simple bounded mailbox.
Backpressure (queue cap, drop oldest, etc.) is intentionally not modelled
here — the in-memory broker enforces its own bounds, and the Redis broker
will inherit the same back-end contract.