• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

live-input-vector-output-node / livon-ts / 26078594527

19 May 2026 05:40AM UTC coverage: 69.413% (-5.6%) from 74.971%
26078594527

push

github

cr15p1
Remove remaining duplicated Sonar blocks

1990 of 3317 branches covered (59.99%)

Branch coverage included in aggregate %.

9 of 23 new or added lines in 3 files covered. (39.13%)

304 existing lines in 15 files now uncovered.

4321 of 5775 relevant lines covered (74.82%)

25.6 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

84.04
/packages/runtime/src/runtime.ts
1
import type {
2
  EmitInput,
3
  EventStatus,
4
  EventError,
5
  EventAck,
6
  EventEnvelope,
7
  RuntimeEventContext,
8
  RuntimeContext,
9
  RuntimeHook,
10
  RuntimeModule,
11
  RuntimeInput,
12
  RuntimeHookRegister,
13
  RuntimeOnError,
14
  RuntimeOnErrorRegister,
15
  RuntimeRegistry,
16
  RuntimeStart,
17
  RuntimeNext,
18
  PartialEventEnvelope,
19
} from './types.js';
20

21
interface RuntimeHookStore {
22
  onReceive: RuntimeHook[];
23
  onSend: RuntimeHook[];
24
  onError: RuntimeOnError[];
25
}
26

27
const createHookStore = (): RuntimeHookStore => ({
11✔
28
  onReceive: [],
29
  onSend: [],
30
  onError: [],
31
});
32

33
interface RuntimeEventContextRecord extends RuntimeEventContext, Record<string, unknown> {}
34

35
interface ErrorLike {
36
  message?: unknown;
37
  name?: unknown;
38
  stack?: unknown;
39
  context?: unknown;
40
}
41

42
interface GlobalCryptoLike {
43
  crypto?: {
44
    randomUUID?: () => string;
45
  };
46
}
47

48
const createState = () => {
1✔
49
  const store = new Map<string, unknown>();
11✔
50

51
  const get = <T = unknown>(key: string) => store.get(key) as T | undefined;
11✔
52

53
  const set = <T = unknown>(key: string, value: T) => {
11✔
54
    store.set(key, value);
×
55
  };
56

57
  return { get, set };
11✔
58
};
59

60
const mergeMetadata = (
1✔
61
  base?: Readonly<Record<string, unknown>>,
62
  extra?: Readonly<Record<string, unknown>>,
63
) => {
64
  if (!base && !extra) {
14✔
65
    return undefined;
11✔
66
  }
67
  return { ...(base ?? {}), ...(extra ?? {}) };
3✔
68
};
69

70
let fallbackEventIdCounter = 0;
1✔
71

72
const createEventId = () => {
1✔
73
  const cryptoValue = (globalThis as GlobalCryptoLike).crypto;
11✔
74
  if (cryptoValue?.randomUUID) {
11!
75
    return cryptoValue.randomUUID();
11✔
76
  }
77
  fallbackEventIdCounter = fallbackEventIdCounter + 1;
×
78
  return `evt_${Date.now().toString(36)}_${fallbackEventIdCounter.toString(36)}`;
×
79
};
80

81
const isContextRecord = (value: unknown): value is RuntimeEventContextRecord =>
1✔
82
  typeof value === 'object' && value !== null && !Array.isArray(value);
5✔
83

84
const contextRecordFrom = (value?: RuntimeEventContext): RuntimeEventContextRecord | undefined => {
1✔
85
  if (!value || !isContextRecord(value)) {
12✔
86
    return undefined;
10✔
87
  }
88
  return value;
2✔
89
};
90

91
const errorContextFrom = (error: ErrorLike): RuntimeEventContextRecord | undefined =>
1✔
92
  isContextRecord(error.context) ? error.context : undefined;
3✔
93

94
const isErrorLike = (error: unknown): error is ErrorLike =>
1✔
95
  typeof error === 'object' && error !== null;
1✔
96

97
const eventErrorFromError = (error: Error): EventError => {
1✔
98
  const context = errorContextFrom(error);
2✔
99
  return {
2✔
100
    message: error.message,
101
    ...(error.name ? { name: error.name } : {}),
2!
102
    ...(error.stack ? { stack: error.stack } : {}),
2!
103
    ...(context ? { context } : {}),
2!
104
  };
105
};
106

107
const eventErrorFromRecord = (error: ErrorLike): EventError => {
1✔
108
  const message = typeof error.message === 'string' ? error.message : 'Unknown error';
1!
109
  const name = typeof error.name === 'string' ? error.name : undefined;
1!
110
  const stack = typeof error.stack === 'string' ? error.stack : undefined;
1!
111
  const context = errorContextFrom(error);
1✔
112
  return {
1✔
113
    message,
114
    ...(name ? { name } : {}),
1!
115
    ...(stack ? { stack } : {}),
1!
116
    ...(context ? { context } : {}),
1!
117
  };
118
};
119

120
const errorFromUnknown = (error: unknown): EventError => {
1✔
121
  if (error instanceof Error) {
3✔
122
    return eventErrorFromError(error);
2✔
123
  }
124
  if (typeof error === 'string') {
1!
UNCOV
125
    return { message: error };
×
126
  }
127
  if (isErrorLike(error)) {
1!
128
    return eventErrorFromRecord(error);
1✔
129
  }
UNCOV
130
  return { message: 'Unknown error' };
×
131
};
132

133
const registerHook = (hooks: RuntimeHook[]): RuntimeHookRegister => (hook) => {
22✔
134
  hooks.push(hook);
10✔
135
  const unsub = () => {
10✔
136
    const index = hooks.indexOf(hook);
1✔
137
    if (index >= 0) {
1!
138
      hooks.splice(index, 1);
1✔
139
    }
140
  };
141
  return { unsub };
10✔
142
};
143

144
const registerErrorHook = (hooks: RuntimeOnError[]): RuntimeOnErrorRegister => (hook) => {
11✔
145
  hooks.push(hook);
5✔
146
  const unsub = () => {
5✔
UNCOV
147
    const index = hooks.indexOf(hook);
×
UNCOV
148
    if (index >= 0) {
×
UNCOV
149
      hooks.splice(index, 1);
×
150
    }
151
  };
152
  return { unsub };
5✔
153
};
154

155
interface EmitErrorHooksInput {
156
  runtimeError: unknown;
157
  eventEnvelope: EventEnvelope;
158
  runtimeContext: RuntimeContext;
159
}
160

161
interface BuildEnvelopeInput {
162
  emitInput: EmitInput;
163
  fallbackStatus: EventStatus;
164
  roomName?: string;
165
}
166

167
interface BuildFailedEnvelopeInput {
168
  runtimeError: unknown;
169
  eventEnvelope: EventEnvelope;
170
}
171

172
interface CreateEmitInput {
173
  runtimeContext: RuntimeContext;
174
  roomName?: string;
175
}
176

177
/**
178
 * runtime is part of the public LIVON API.
179
 *
180
 * @remarks
181
 * Parameter and return types are defined in the TypeScript signature.
182
 *
183
 * @see https://livon.tech/docs/packages/runtime
184
 *
185
 * @example
186
 * const result = runtime(undefined as never);
187
 */
188
export const runtime: RuntimeStart = (inputOrModule: RuntimeInput | RuntimeModule, ...rest: RuntimeModule[]) => {
1✔
189
  const input: RuntimeInput =
190
    'modules' in inputOrModule ? inputOrModule : { modules: [inputOrModule, ...rest] };
11✔
191

192
  const modules = input.modules;
11✔
193
  const state = createState();
11✔
194
  const hooks = createHookStore();
11✔
195

196
  const onReceive = registerHook(hooks.onReceive);
11✔
197
  const onSend = registerHook(hooks.onSend);
11✔
198
  const onError = registerErrorHook(hooks.onError);
11✔
199

200
  const emitErrorHooks = ({ runtimeError, eventEnvelope, runtimeContext }: EmitErrorHooksInput) => {
11✔
201
    hooks.onError.forEach((handler) => {
4✔
202
      try {
5✔
203
        handler(runtimeError, eventEnvelope, runtimeContext);
5✔
204
      } catch {
205
        // ignore secondary errors
206
      }
207
    });
208
  };
209

210
  const mergeContext = (
11✔
211
    base?: RuntimeEventContext,
212
    update?: RuntimeEventContext,
213
  ): RuntimeEventContext | undefined => {
214
    const left = contextRecordFrom(base);
6✔
215
    const right = contextRecordFrom(update);
6✔
216
    if (!left && !right) {
6✔
217
      return undefined;
5✔
218
    }
219
    return { ...(left ?? {}), ...(right ?? {}) };
1!
220
  };
221

222
  const payloadFromEvent = (event: PartialEventEnvelope | EventEnvelope): Uint8Array | undefined => event.payload;
11✔
223

224
  const errorFromEvent = (event: PartialEventEnvelope | EventEnvelope): EventError | undefined => event.error;
11✔
225

226
  const mergeEvent = (base: EventEnvelope, update?: PartialEventEnvelope): EventEnvelope => {
11✔
227
    if (!update) {
6✔
228
      return base;
3✔
229
    }
230
    const nextId = update.id ?? base.id;
3✔
231
    const nextEvent = update.event ?? base.event;
6✔
232
    const nextStatus = update.status ?? base.status;
6✔
233
    const nextMetadata = mergeMetadata(base.metadata, update.metadata);
6✔
234
    const nextContext = mergeContext(base.context, update.context);
6✔
235
    const nextPayload = payloadFromEvent(update) ?? payloadFromEvent(base);
6✔
236
    const nextError = errorFromEvent(update) ?? errorFromEvent(base);
6✔
237
    if (nextPayload === undefined && nextError === undefined) {
6!
238
      throw new Error('Event envelope must contain payload or error.');
×
239
    }
240
    const mergedEnvelopeBase = {
3✔
241
      ...base,
242
      id: nextId,
243
      event: nextEvent,
244
      status: nextStatus,
245
      metadata: nextMetadata,
246
      context: nextContext,
247
    };
248
    if (nextPayload !== undefined && nextError !== undefined) {
3!
UNCOV
249
      return {
×
250
        ...mergedEnvelopeBase,
251
        payload: nextPayload,
252
        error: nextError,
253
      };
254
    }
255
    if (nextPayload !== undefined) {
3!
256
      return {
3✔
257
        ...mergedEnvelopeBase,
258
        payload: nextPayload,
259
      };
260
    }
UNCOV
261
    const definedError = nextError as EventError;
×
UNCOV
262
    return {
×
263
      ...mergedEnvelopeBase,
264
      error: definedError,
265
    };
266
  };
267

268
  const buildEnvelope = ({ emitInput, fallbackStatus, roomName }: BuildEnvelopeInput): EventEnvelope => {
11✔
269
    const { id: inputId, event, status: inputStatus, metadata: inputMetadata, context, payload, error } = emitInput;
11✔
270
    const roomMeta = roomName ? { room: roomName } : undefined;
11✔
271
    const metadata = mergeMetadata(roomMeta, inputMetadata);
11✔
272
    const id = inputId ?? createEventId();
11✔
273
    const status = inputStatus ?? fallbackStatus;
11✔
274
    if (payload === undefined && error === undefined) {
11✔
275
      throw new Error('Emit input must contain payload or error.');
1✔
276
    }
277
    const envelopeBase = {
10✔
278
      id,
279
      event,
280
      status,
281
      metadata,
282
      context,
283
    };
284
    if (payload !== undefined && error !== undefined) {
10!
UNCOV
285
      return {
×
286
        ...envelopeBase,
287
        payload,
288
        error,
289
      };
290
    }
291
    if (payload !== undefined) {
10!
292
      return {
10✔
293
        ...envelopeBase,
294
        payload,
295
      };
296
    }
UNCOV
297
    const definedError = error as EventError;
×
UNCOV
298
    return {
×
299
      ...envelopeBase,
300
      error: definedError,
301
    };
302
  };
303

304
  const buildFailedEnvelope = ({ runtimeError, eventEnvelope }: BuildFailedEnvelopeInput): EventEnvelope & { error: EventError } => {
11✔
305
    const normalizedError = errorFromUnknown(runtimeError);
3✔
306
    const nextContext = mergeContext(eventEnvelope.context, normalizedError.context);
3✔
307
    return {
3✔
308
      ...eventEnvelope,
309
      error: normalizedError,
310
      context: nextContext,
311
    };
312
  };
313

314
  interface InvokeHookInput {
315
    hookChain: readonly RuntimeHook[];
316
    hookIndex: number;
317
    eventEnvelope: EventEnvelope;
318
    runtimeContext: RuntimeContext;
319
  }
320

321
  const invokeHook = (input: InvokeHookInput): Promise<EventEnvelope> => {
11✔
322
    const hook = input.hookChain[input.hookIndex];
15✔
323
    if (!hook) {
15✔
324
      return Promise.resolve(input.eventEnvelope);
6✔
325
    }
326
    let nextPromise: Promise<EventEnvelope> | undefined;
327
    let nextCalled = false;
9✔
328
    const next: RuntimeNext = (update) => {
9✔
329
      nextCalled = true;
6✔
330
      nextPromise = invokeHook({
6✔
331
        hookChain: input.hookChain,
332
        hookIndex: input.hookIndex + 1,
333
        eventEnvelope: mergeEvent(input.eventEnvelope, update),
334
        runtimeContext: input.runtimeContext,
335
      });
336
      return nextPromise;
6✔
337
    };
338

339
    const result = hook(input.eventEnvelope, input.runtimeContext, next);
9✔
340
    if (result === undefined && nextCalled) {
9!
UNCOV
341
      return nextPromise ?? Promise.resolve(input.eventEnvelope);
×
342
    }
343
    return Promise.resolve(result).then((value) => value ?? nextPromise ?? input.eventEnvelope);
6!
344
  };
345

346
  const runReceive = (eventEnvelope: EventEnvelope, runtimeContext: RuntimeContext) =>
11✔
347
    invokeHook({ hookChain: hooks.onReceive, hookIndex: 0, eventEnvelope, runtimeContext });
6✔
348

349
  const runSend = (eventEnvelope: EventEnvelope, runtimeContext: RuntimeContext) =>
11✔
350
    invokeHook({ hookChain: hooks.onSend, hookIndex: 0, eventEnvelope, runtimeContext });
3✔
351

352
  const createEmitError = ({ runtimeContext, roomName }: CreateEmitInput) => async (emitInput: EmitInput): Promise<EventAck> => {
12✔
353
    const envelope = buildEnvelope({ emitInput, fallbackStatus: 'sending', roomName });
1✔
354
    const fallbackError = envelope.error ?? { message: 'Unknown error' };
1✔
355
    const failedEnvelope: EventEnvelope = { ...envelope, error: fallbackError };
1✔
356
    emitErrorHooks({
1✔
357
      runtimeError: fallbackError,
358
      eventEnvelope: failedEnvelope,
359
      runtimeContext,
360
    });
361
    return { ok: true };
1✔
362
  };
363

364
  const createEmitReceive = ({ runtimeContext, roomName }: CreateEmitInput) => async (emitInput: EmitInput): Promise<EventAck> => {
12✔
365
    const envelope = buildEnvelope({ emitInput, fallbackStatus: 'receiving', roomName });
7✔
366
    try {
7✔
367
      await runReceive(envelope, runtimeContext);
7✔
368
      return { ok: true };
4✔
369
    } catch (error) {
370
      const failedEnvelope = buildFailedEnvelope({ runtimeError: error, eventEnvelope: envelope });
2✔
371
      emitErrorHooks({
2✔
372
        runtimeError: error,
373
        eventEnvelope: failedEnvelope,
374
        runtimeContext,
375
      });
376
      return { ok: false, error: failedEnvelope.error.message };
2✔
377
    }
378
  };
379

380
  const createEmitSend = ({ runtimeContext, roomName }: CreateEmitInput) => async (emitInput: EmitInput): Promise<EventAck> => {
13✔
381
    const envelope = buildEnvelope({ emitInput, fallbackStatus: 'sending', roomName });
3✔
382
    try {
3✔
383
      await runSend(envelope, runtimeContext);
3✔
384
      return { ok: true };
2✔
385
    } catch (error) {
386
      const failedEnvelope = buildFailedEnvelope({ runtimeError: error, eventEnvelope: envelope });
1✔
387
      emitErrorHooks({
1✔
388
        runtimeError: error,
389
        eventEnvelope: failedEnvelope,
390
        runtimeContext,
391
      });
392
      return { ok: false, error: failedEnvelope.error.message };
1✔
393
    }
394
  };
395

396
  const createEmitEvent = (input: CreateEmitInput) => async (eventInput: EmitInput): Promise<EventAck> =>
12✔
397
    createEmitSend(input)(eventInput);
1✔
398

399
  const createContext = (roomName?: string): RuntimeContext => {
11✔
400
    const runtimeContext = {} as RuntimeContext;
12✔
401
    const createEmitInput = { runtimeContext, roomName };
12✔
402
    runtimeContext.emitReceive = createEmitReceive(createEmitInput);
12✔
403
    runtimeContext.emitSend = createEmitSend(createEmitInput);
12✔
404
    runtimeContext.emitError = createEmitError(createEmitInput);
12✔
405
    runtimeContext.emitEvent = createEmitEvent(createEmitInput);
12✔
406
    runtimeContext.room = (nextRoomName) => createContext(nextRoomName);
12✔
407
    runtimeContext.state = state;
12✔
408
    return runtimeContext;
12✔
409
  };
410

411
  const baseContext = createContext();
11✔
412

413
  const registry: RuntimeRegistry = {
11✔
414
    emitReceive: baseContext.emitReceive,
415
    emitSend: baseContext.emitSend,
416
    emitError: baseContext.emitError,
417
    onReceive,
418
    onSend,
419
    onError,
420
    state,
421
  };
422

423
  modules.forEach((mod) => {
11✔
424
    mod.register(registry);
11✔
425
  });
426
};
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc