openvibe-sdk/service
Generated at from
openvibe-contracts v0.114.0 and
openvibe-sdk v0.35.1.
From types/service.d.ts (server only). Declarations are shown verbatim.
type StopHandle
A handle closed after the close steps: a function, or an object with close() (else stop(), end(), quit()).
export type StopHandle = (() => unknown) | { close(): unknown } | { stop(): unknown } | { end(): unknown } | { quit(): unknown };interface GracefulStopOptions
export interface GracefulStopOptions {
/** Log prefix, e.g. 'Network'. Default 'service'. */
name?: string;
/** The HTTP server to drain; leave it out for a worker without one. */
server?: Server | null;
/** Run first, in order (sync or async; a failure is logged and the stop goes on). */
stop?: Step[];
/** Run after the HTTP drain, in order. */
close?: Step[];
/** How long requests in flight may take before they are cut. Default 4000. */
drainMs?: number;
/** The whole stop; past it exit(deadlineExitCode). Default 5000. */
deadlineMs?: number;
/** Default 1 (Network, Community). The 5 s family and Media exit 0. */
deadlineExitCode?: number;
/** false: no SIGTERM/SIGINT handlers (tests; a caller that wires signals itself). Default true. */
signals?: boolean;
/** Default process.exit. Called once. */
exit?: (code: number) => unknown;
log?: Logger;
/** After the stop steps, before the server stops taking connections. */
beforeDrain?: (signal: string) => unknown;
/** Closed after the close steps; a rejection makes the exit code 1. */
handles?: StopHandle | StopHandle[];
}interface GracefulStop
export interface GracefulStop {
/** Starts the stop once; later calls return the same promise. Resolves with the exit code passed to exit. */
stop(signal?: string): Promise<number>;
/** True from the first stop() (or signal) on: a readiness check can answer 503. */
stopping(): boolean;
}function gracefulStop
export declare function gracefulStop(options?: GracefulStopOptions): GracefulStop;function within
`promise`, but no longer than `ms`; a rejection is swallowed (a best-effort step inside the deadline).
export declare function within<T>(ms: number, promise: Promise<T> | T): Promise<T | void>;const DRAIN_MS
export declare const DRAIN_MS: 4000;const DEADLINE_MS
export declare const DEADLINE_MS: 5000;interface ServiceErrorInstance
export interface ServiceErrorInstance extends Error {
status: number;
code: string;
detail?: string;
extra: Record<string, unknown> | null;
}interface ServiceErrorClass
export interface ServiceErrorClass {
new (status: number, code: string, detail?: string, extra?: Record<string, unknown> | null): ServiceErrorInstance;
readonly prototype: ServiceErrorInstance;
}interface ServiceErrorOptions
export interface ServiceErrorOptions {
/** Log prefix ([name]). Default 'service'. */
name?: string;
log?: Pick<Console, 'error'>;
/** 'spread' (default: Blog/Trade, Reviews/Wiki) or 'details' ({ details: extra } below 500: Tips/VIP). */
extra?: 'spread' | 'details';
/** The code of an unexpected error. Default 'internal.error'. */
internalCode?: string;
/** The detail of an unexpected error. Default 'Internal error'. */
internalDetail?: string;
/** Map openvibe-publishing errors and plain TypeErrors (Blog/Trade asApiError). */
publishing?: boolean;
/** The class `publishing` builds. Default ServiceError. */
ServiceError?: ServiceErrorClass;
/** The service's own refusals → a ServiceError (or null). */
map?: (err: unknown) => ServiceErrorInstance | null | undefined;
/** run(): Cache-Control: private, no-store on the answer. */
noStore?: boolean;
}const ServiceError
export declare const ServiceError: ServiceErrorClass;function createServiceError
export declare function createServiceError(name?: string): ServiceErrorClass;function asServiceError
export declare function asServiceError(err: unknown, options?: ServiceErrorOptions): ServiceErrorInstance | null;function sendError
Any error → problem+json. Returns the body sent, or null when the headers were already out.
export declare function sendError(res: Res, req: Req, err: unknown, log?: Pick<Console, 'error'> | ServiceErrorOptions, options?: ServiceErrorOptions): Record<string, unknown> | null;function run
export declare function run<T>(fn: (req: Req, res: Res) => T | Promise<T>, status?: number | ((out: T) => number), options?: ServiceErrorOptions | Pick<Console, 'error'>): (req: Req, res: Res) => Promise<void>;function wrap
export declare function wrap(fn: Handler, options?: ServiceErrorOptions): (req: Req, res: Res, next?: Next) => Promise<void>;interface JsonBodyOptions
export interface JsonBodyOptions {
/** '512kb' (default), '1mb', or bytes. */
limit?: string | number;
/** Use this parser (express.json({ limit })) and map its errors. */
parser?: (req: Req, res: Res, next: Next) => void;
}function jsonBody
export declare function jsonBody(options?: JsonBodyOptions): (req: Req, res: Res, next: Next) => void;function privateNoStore
export declare function privateNoStore<R extends Res>(res: R): R;interface JsonErrorsOptions
export interface JsonErrorsOptions extends ServiceErrorOptions {
/** Default ['/api/', '/internal/']. */
apiPrefix?: string | string[];
/** false: no 404 handler. */
notFound?: boolean;
notFoundText?: string;
errorText?: string;
}type JsonErrors
export type JsonErrors = [(req: Req, res: Res) => unknown, (err: unknown, req: Req, res: Res, next: Next) => unknown] & {
notFound: (req: Req, res: Res) => unknown;
errorHandler: (err: unknown, req: Req, res: Res, next: Next) => unknown;
};function jsonErrors
export declare function jsonErrors(options?: JsonErrorsOptions): JsonErrors;interface TelemetrySample
A platform.telemetry-sample@1 record. The schema allows only these fields; `extra` holds scalars.
export interface TelemetrySample {
service?: string;
project?: string;
subject?: string;
resource?: string;
provider?: string;
node?: string;
cell?: string;
region?: string;
operation?: string;
at?: string;
latency_ms?: number;
queue_delay_ms?: number;
ttfb_ms?: number;
throughput_per_second?: number;
bytes?: number;
status?: string;
cache_status?: string;
cost_estimate?: number;
route_epoch?: number;
trace_id?: string;
extra?: Record<string, string | number | boolean>;
}function telemetrySample
Build a record in the schema's field order; `at` defaults to now (ISO) and null/undefined are stripped.
export declare function telemetrySample(fields: Partial<TelemetrySample>): TelemetrySample;function validateTelemetrySample
`{ ok, errors }` against platform.telemetry-sample@1 (openvibe-contracts, required lazily; a missing package is never a claim of validity: `ok: false`, `errors: []`).
export declare function validateTelemetrySample(record: unknown): { ok: boolean; errors: unknown[] };interface TelemetrySignals
start/stop/lag; the defaults are the SDK's event-loop monitor (started by init, stopped by stop).
export interface TelemetrySignals {
start(): void;
stop(): void;
lag(): number | null;
}interface HttpTelemetryOptions
export interface HttpTelemetryOptions {
/** The schema's `service` — the only process identity (there is no `instance` field). */
service: string;
/** Receives one batch per flush: `await sink(samples)`. */
sink: (samples: TelemetrySample[]) => unknown;
/** The flush interval; also the rolling p95 window. Default 15000. */
intervalMs?: number;
now?: () => number;
log?: Logger;
maxBuffered?: number;
/** The route template for a request. Default: Express's `req.route.path` under `req.baseUrl`, else `'unmatched'`
* (a raw path is never used as a label). */
routeLabel?: (req: Req) => string;
/** Replaces telemetrySkipped entirely. */
skipped?: (req: Req) => boolean;
/** Paths never observed. Default: /api/health, /ready, /api/ready, /metrics. */
skipExact?: Set<string> | string[];
/** Prefixes never observed (a path segment). Default `['/shared']`; OpenVibe.Network passes
* `['/shared', '/api/chrome']`. Pass `[]` to disable. */
skipPrefixes?: string[];
/** An extra always-skip predicate. */
skip?: (req: Req) => boolean;
/** Distinct route|method|status_class keys kept per flush; further keys fold into the `'other'` route label.
* Default 500. */
maxRouteKeys?: number;
/** Override the module's signals for this collector (tests; a service with its own monitor). */
signals?: Partial<TelemetrySignals>;
}interface RequestObservation
export interface RequestObservation {
route?: string;
method?: string;
httpStatus?: number;
latencyMs?: number;
}interface HttpTelemetry
export interface HttpTelemetry {
/** A number becomes `latency_ms`; any other value becomes `extra[name]` (a scalar dimension). */
record(name: string, value: unknown, labels?: Record<string, unknown>): TelemetrySample;
gauge(name: string, value: number, labels?: Record<string, unknown>): TelemetrySample;
count(name: string, labels?: Record<string, unknown>): TelemetrySample;
/** Emit the interval's aggregation, then hand the SDK everything buffered. */
flush(): Promise<void>;
/** Emit once, clear the timer and flush (idempotent; a gracefulStop stop step). */
stop(): Promise<void>;
/** A request began: the in-flight peak is sampled here. */
requestStarted(): void;
/** A request ended: free the in-flight slot and fold it by route|method|status_class. */
requestFinished(info: RequestObservation): void;
observeRequest(info?: RequestObservation): void;
/** The live buffer sizes (diagnostics/tests): aggregation keys and sampled latencies held per flush. */
bufferStats(): { keys: number; latencies: number; maxKeyLatencies: number };
routeLabel(req: Req): string;
skipped(req: Req): boolean;
/** The per-request middleware for this collector. */
middleware(options?: { routeLabel?: (req: Req) => string; skipped?: (req: Req) => boolean }): (req: Req, res: Res, next: Next) => void;
}function createHttpTelemetry
export declare function createHttpTelemetry(options: HttpTelemetryOptions): HttpTelemetry;function createTelemetryMiddleware
The middleware for a collector; telemetry never breaks a request and next() runs exactly once.
export declare function createTelemetryMiddleware(collector: HttpTelemetry, options?: { routeLabel?: (req: Req) => string; skipped?: (req: Req) => boolean }): (req: Req, res: Res, next: Next) => void;function telemetrySkipped
Whether a request carries no product signal (probe paths, configured prefixes, static assets).
export declare function telemetrySkipped(req: Req, options?: { exact?: Set<string> | string[]; prefixes?: string[]; skip?: (req: Req) => boolean }): boolean;function defaultRouteLabel
The route label without openvibe-shared/metrics: the matched route template, else `'unmatched'`.
export declare function defaultRouteLabel(req: Req): string;function registerSignals
Merge signals over the current defaults (a service or test injecting start/stop/lag).
export declare function registerSignals(injected?: Partial<TelemetrySignals>): void;function startEventLoopMonitor
export declare function startEventLoopMonitor(): void;function stopEventLoopMonitor
export declare function stopEventLoopMonitor(): void;function eventLoopMonitorEnabled
Whether the monitor is running; requiring the kit starts nothing.
export declare function eventLoopMonitorEnabled(): boolean;function eventLoopLagMs
The event-loop lag since the last read, ms (the monitor resets per read); null before a sample.
export declare function eventLoopLagMs(): number | null;const telemetryMiddleware
The singleton's middleware; a no-op until `telemetry.init()`.
export declare const telemetryMiddleware: (req: Req, res: Res, next: Next) => void;const telemetry
The process-wide collector: init at boot, stop on shutdown, nothing at require time.
export declare const telemetry: {
init(options: HttpTelemetryOptions): HttpTelemetry;
record: HttpTelemetry['record'];
gauge: HttpTelemetry['gauge'];
count: HttpTelemetry['count'];
requestStarted: HttpTelemetry['requestStarted'];
requestFinished: HttpTelemetry['requestFinished'];
flush: HttpTelemetry['flush'];
stop: HttpTelemetry['stop'];
middleware: (req: Req, res: Res, next: Next) => void;
};const DEFAULT_INTERVAL_MS
export declare const DEFAULT_INTERVAL_MS: 15000;const DEFAULT_MAX_ROUTE_KEYS
export declare const DEFAULT_MAX_ROUTE_KEYS: 500;const LATENCY_SAMPLE_CAP
export declare const LATENCY_SAMPLE_CAP: 10000;const DEFAULT_SKIP_PREFIXES
export declare const DEFAULT_SKIP_PREFIXES: string[];const HTTP_METHODS
export declare const HTTP_METHODS: Set<string>;const createReadiness
openvibe-shared/ready
export declare const createReadiness: Fn;const skip
export declare const skip: Fn;const safeReason
export declare const safeReason: Fn;const createRegistry
openvibe-shared/metrics
export declare const createRegistry: Fn;const instrument
export declare const instrument: Fn;const metricsHandler
export declare const metricsHandler: Fn;const isLoopbackDirect
export declare const isLoopbackDirect: (req: Req) => boolean;const releaseInfo
export declare const releaseInfo: Fn;const createRelease
openvibe-shared/release
export declare const createRelease: Fn;interface ProblemOptions
export interface ProblemOptions {
title?: string;
detail?: string;
type?: string;
instance?: string;
ctx?: { requestId?: string; traceId?: string } | null;
errors?: unknown[];
extra?: Record<string, unknown>;
}function problem
openvibe-contracts http.problem
export declare function problem(status: number, code: string, options?: ProblemOptions): Record<string, unknown>;function sendProblem
openvibe-contracts http.sendProblem
export declare function sendProblem(res: Res, status: number, code: string, options?: ProblemOptions): Record<string, unknown>;