Files
skills/docs/api-stack/api/api-plugin-event-bus/src/services/helpers.md
2026-05-29 08:20:56 +01:00

3.5 KiB

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

import {
    nextSubscriptionId,
    stableHash,
} from "@suaveplan/api-plugin-event-bus/services/event-bus-service";

const id = nextSubscriptionId();
const key = stableHash({ x: 1 });

Usage: Advanced

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.