first commit
This commit is contained in:
110
docs/api-stack/api/api-plugin-event-bus/src/services/helpers.md
Normal file
110
docs/api-stack/api/api-plugin-event-bus/src/services/helpers.md
Normal file
@@ -0,0 +1,110 @@
|
||||
# `helpers.ts` — small utilities used by `event-bus-service.ts`
|
||||
|
||||
## Purpose
|
||||
|
||||
Holds the pure helpers extracted from the main service body so the
|
||||
service itself stays under the 300-line cap and each helper can be
|
||||
unit-tested in isolation. The helpers cover id generation, payload
|
||||
hashing, error projection, subscription projection, emit-option
|
||||
assembly, and OTel metric emission.
|
||||
|
||||
## Features
|
||||
|
||||
- `nextSubscriptionId` — collision-resistant id generator.
|
||||
- `stableHash` — deterministic-enough payload hash for default
|
||||
idempotency keying.
|
||||
- `toErrorReport` — converts upstream listener errors into the
|
||||
wire-level shape advertised on `PublishResultRecord.errors`.
|
||||
- `projectSubscription` — internal entry → public read-only view.
|
||||
- `buildEmitOptions` — sparse builder for `EventBus.emitAsync`
|
||||
options.
|
||||
- `recordPublishMetric` — one-call OTel emission for every publish.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import {
|
||||
nextSubscriptionId,
|
||||
stableHash,
|
||||
} from "@suaveplan/api-plugin-event-bus/services/event-bus-service";
|
||||
|
||||
const id = nextSubscriptionId();
|
||||
const key = stableHash({ x: 1 });
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
import {
|
||||
buildEmitOptions,
|
||||
recordPublishMetric,
|
||||
toErrorReport,
|
||||
} from "@suaveplan/api-plugin-event-bus/services/event-bus-service";
|
||||
|
||||
const emitOptions = buildEmitOptions({
|
||||
signal: AbortSignal.timeout(2000),
|
||||
timeoutMs: 2000,
|
||||
idempotencyKey: "k1",
|
||||
});
|
||||
|
||||
const t0 = Date.now();
|
||||
try {
|
||||
// ... emit
|
||||
recordPublishMetric("order.created", "success", Date.now() - t0);
|
||||
} catch (err) {
|
||||
recordPublishMetric("order.created", "error", Date.now() - t0);
|
||||
const report = toErrorReport(err);
|
||||
console.error(report.code, report.message);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `nextSubscriptionId(): string`
|
||||
|
||||
Returns `` `sub_${n}_${base36}` `` ids; the base36 suffix keeps two
|
||||
collocated service instances from colliding when the module-level
|
||||
counter desyncs across workers.
|
||||
|
||||
### `stableHash(payload: unknown): string`
|
||||
|
||||
JSON-stringifies the payload (falling back to `String(payload)` on
|
||||
throw). Used as the default idempotency-key derivation when a
|
||||
subscription is registered without an explicit `idempotencyKey`
|
||||
function.
|
||||
|
||||
### `toErrorReport(err: unknown): PublishErrorReport`
|
||||
|
||||
Projects a listener error into `{ code, message }`. Preserves
|
||||
`EventsError.code`; falls back to `EVENT_BUS.LISTENER_FAILED`.
|
||||
|
||||
### `projectSubscription(entry): SubscriptionRecordView`
|
||||
|
||||
Strips internal fields and conditionally omits `tenantId` when
|
||||
absent.
|
||||
|
||||
### `buildEmitOptions(opts): EmitAsyncOptions`
|
||||
|
||||
Sparse builder — only sets fields the caller explicitly provided.
|
||||
|
||||
### `recordPublishMetric(topic, outcome, durationMs): void`
|
||||
|
||||
Increments `event_bus.published_total{outcome,topic_root}` and
|
||||
records the publish duration on `event_bus.publish_duration_ms`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- `nextSubscriptionId`'s counter resets on module reload (test
|
||||
isolation) and per-process. Production collisions are guarded by
|
||||
the random suffix, which gives ~36⁸ ≈ 2.8e12 ids per counter
|
||||
value.
|
||||
- `stableHash` uses `JSON.stringify` which is not key-order stable
|
||||
across all engines for objects with non-enumerable properties.
|
||||
Callers requiring canonical hashing supply an explicit
|
||||
`idempotencyKey` function at subscribe time.
|
||||
- `toErrorReport`'s `EVENT_BUS.LISTENER_FAILED` envelope is a
|
||||
surface-level code — it is NOT registered in `EVENT_BUS_CODES`
|
||||
because nothing in the package throws it (it only appears in
|
||||
`PublishResultRecord.errors`).
|
||||
- `projectSubscription` returns a `readonly` shape so consumers
|
||||
cannot mutate the record they got from `listSubscriptions`.
|
||||
Reference in New Issue
Block a user