@muspellheim/shared 0.16.0 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/shared.d.ts CHANGED
@@ -132,8 +132,13 @@ export declare class ConfigurableResponses<T = unknown> {
132
132
  */
133
133
  export declare class ConsoleLog extends EventTarget implements Log {
134
134
  #private;
135
- static create(): ConsoleLog;
136
- static createNull(): ConsoleLog;
135
+ static create({ name }?: {
136
+ name?: string;
137
+ }): ConsoleLog;
138
+ static createNull({ name }?: {
139
+ name?: string;
140
+ }): ConsoleLog;
141
+ name?: string;
137
142
  level: LogLevel;
138
143
  private constructor();
139
144
  log(...data: unknown[]): void;
package/dist/shared.js CHANGED
@@ -26,9 +26,9 @@ class c {
26
26
  static offset(t, e) {
27
27
  return new c(new Date(t.millis() + e));
28
28
  }
29
- #t;
29
+ #e;
30
30
  constructor(t) {
31
- this.#t = t;
31
+ this.#e = t;
32
32
  }
33
33
  /**
34
34
  * Return the current timestamp of the clock.
@@ -36,7 +36,7 @@ class c {
36
36
  * @return The current timestamp.
37
37
  */
38
38
  date() {
39
- return this.#t ? new Date(this.#t) : /* @__PURE__ */ new Date();
39
+ return this.#e ? new Date(this.#e) : /* @__PURE__ */ new Date();
40
40
  }
41
41
  /**
42
42
  * Return the current timestamp of the clock in milliseconds.
@@ -57,8 +57,8 @@ class f {
57
57
  static create(t, ...e) {
58
58
  return new f(t, e);
59
59
  }
60
- #t;
61
60
  #e;
61
+ #t;
62
62
  #s;
63
63
  #r;
64
64
  /**
@@ -68,8 +68,8 @@ class f {
68
68
  * @param event The event name to track.
69
69
  */
70
70
  constructor(t, e) {
71
- this.#t = t, this.#e = e, this.#s = [], this.#r = (s) => this.#s.push(s), this.#e.forEach(
72
- (s) => this.#t.addEventListener(s, this.#r)
71
+ this.#e = t, this.#t = e, this.#s = [], this.#r = (s) => this.#s.push(s), this.#t.forEach(
72
+ (s) => this.#e.addEventListener(s, this.#r)
73
73
  );
74
74
  }
75
75
  /**
@@ -93,8 +93,8 @@ class f {
93
93
  * Stop tracking.
94
94
  */
95
95
  stop() {
96
- this.#e.forEach(
97
- (t) => this.#t.removeEventListener(t, this.#r)
96
+ this.#t.forEach(
97
+ (t) => this.#e.removeEventListener(t, this.#r)
98
98
  );
99
99
  }
100
100
  /**
@@ -105,12 +105,12 @@ class f {
105
105
  async waitFor(t = 1) {
106
106
  return new Promise((e) => {
107
107
  const s = () => {
108
- this.#s.length >= t && (this.#e.forEach(
109
- (r) => this.#t.removeEventListener(r, s)
108
+ this.#s.length >= t && (this.#t.forEach(
109
+ (r) => this.#e.removeEventListener(r, s)
110
110
  ), e(this.events));
111
111
  };
112
- this.#e.forEach(
113
- (r) => this.#t.addEventListener(r, s)
112
+ this.#t.forEach(
113
+ (r) => this.#e.addEventListener(r, s)
114
114
  ), s();
115
115
  });
116
116
  }
@@ -122,7 +122,7 @@ class S {
122
122
  this.result = t;
123
123
  }
124
124
  }
125
- class L {
125
+ class N {
126
126
  isSuccess = !1;
127
127
  errorMessage;
128
128
  /**
@@ -161,8 +161,8 @@ class l {
161
161
  });
162
162
  return Object.fromEntries(r);
163
163
  }
164
- #t;
165
164
  #e;
165
+ #t;
166
166
  /**
167
167
  * Create a list of responses (by providing an array), or a single repeating
168
168
  * response (by providing any other type). 'Name' is optional and used in
@@ -172,7 +172,7 @@ class l {
172
172
  * @param name An optional name for the responses.
173
173
  */
174
174
  constructor(t, e) {
175
- this.#t = e == null ? "" : ` in ${e}`, this.#e = Array.isArray(t) ? [...t] : t;
175
+ this.#e = e == null ? "" : ` in ${e}`, this.#t = Array.isArray(t) ? [...t] : t;
176
176
  }
177
177
  /**
178
178
  * Get the next configured response. Throws an error when configured with a list
@@ -181,9 +181,9 @@ class l {
181
181
  * @return The next response.
182
182
  */
183
183
  next() {
184
- const t = Array.isArray(this.#e) ? this.#e.shift() : this.#e;
184
+ const t = Array.isArray(this.#t) ? this.#t.shift() : this.#t;
185
185
  if (t === void 0)
186
- throw new Error(`No more responses configured${this.#t}.`);
186
+ throw new Error(`No more responses configured${this.#e}.`);
187
187
  return t;
188
188
  }
189
189
  }
@@ -197,8 +197,8 @@ class u {
197
197
  static create(t, e) {
198
198
  return new u(t, e);
199
199
  }
200
- #t;
201
200
  #e;
201
+ #t;
202
202
  #s;
203
203
  #r;
204
204
  /**
@@ -208,7 +208,7 @@ class u {
208
208
  * @param event The event name to track.
209
209
  */
210
210
  constructor(t, e) {
211
- this.#t = t, this.#e = e, this.#s = [], this.#r = (s) => this.#s.push(s.detail), this.#t.addEventListener(this.#e, this.#r);
211
+ this.#e = t, this.#t = e, this.#s = [], this.#r = (s) => this.#s.push(s.detail), this.#e.addEventListener(this.#t, this.#r);
212
212
  }
213
213
  /**
214
214
  * Return the tracked data.
@@ -231,59 +231,60 @@ class u {
231
231
  * Stop tracking.
232
232
  */
233
233
  stop() {
234
- this.#t.removeEventListener(this.#e, this.#r);
234
+ this.#e.removeEventListener(this.#t, this.#r);
235
235
  }
236
236
  }
237
237
  const i = "message";
238
238
  class E extends EventTarget {
239
- static create() {
240
- return new E(globalThis.console);
239
+ static create({ name: t } = {}) {
240
+ return new E(globalThis.console, t);
241
241
  }
242
- static createNull() {
243
- return new E(new p());
242
+ static createNull({ name: t } = {}) {
243
+ return new E(new p(), t);
244
244
  }
245
+ name;
245
246
  level = "info";
246
- #t;
247
- constructor(t) {
248
- super(), this.#t = t;
247
+ #e;
248
+ constructor(t, e) {
249
+ super(), this.name = e, this.#e = t;
249
250
  }
250
251
  log(...t) {
251
- this.isLoggable("log") && (this.#t.log(...t), this.dispatchEvent(
252
+ this.isLoggable("log") && (t = this.#t(t), this.#e.log(...t), this.dispatchEvent(
252
253
  new CustomEvent(i, {
253
254
  detail: { level: "log", message: t }
254
255
  })
255
256
  ));
256
257
  }
257
258
  error(...t) {
258
- this.isLoggable("error") && (this.#t.error(...t), this.dispatchEvent(
259
+ this.isLoggable("error") && (t = this.#t(t), this.#e.error(...t), this.dispatchEvent(
259
260
  new CustomEvent(i, {
260
261
  detail: { level: "error", message: t }
261
262
  })
262
263
  ));
263
264
  }
264
265
  warn(...t) {
265
- this.isLoggable("warn") && (this.#t.warn(...t), this.dispatchEvent(
266
+ this.isLoggable("warn") && (t = this.#t(t), this.#e.warn(...t), this.dispatchEvent(
266
267
  new CustomEvent(i, {
267
268
  detail: { level: "warn", message: t }
268
269
  })
269
270
  ));
270
271
  }
271
272
  info(...t) {
272
- this.isLoggable("info") && (this.#t.info(...t), this.dispatchEvent(
273
+ this.isLoggable("info") && (t = this.#t(t), this.#e.info(...t), this.dispatchEvent(
273
274
  new CustomEvent(i, {
274
275
  detail: { level: "info", message: t }
275
276
  })
276
277
  ));
277
278
  }
278
279
  debug(...t) {
279
- this.isLoggable("debug") && (this.#t.debug(...t), this.dispatchEvent(
280
+ this.isLoggable("debug") && (t = this.#t(t), this.#e.debug(...t), this.dispatchEvent(
280
281
  new CustomEvent(i, {
281
282
  detail: { level: "debug", message: t }
282
283
  })
283
284
  ));
284
285
  }
285
286
  trace(...t) {
286
- this.isLoggable("trace") && (this.#t.trace(...t), this.dispatchEvent(
287
+ this.isLoggable("trace") && (t = this.#t(t), this.#e.trace(...t), this.dispatchEvent(
287
288
  new CustomEvent(i, {
288
289
  detail: { level: "trace", message: t }
289
290
  })
@@ -306,6 +307,9 @@ class E extends EventTarget {
306
307
  ], r = s.indexOf(e(this.level));
307
308
  return s.indexOf(e(t)) <= r;
308
309
  }
310
+ #t(t) {
311
+ return this.name == null ? t : [`${this.name}`, ...t];
312
+ }
309
313
  }
310
314
  class p {
311
315
  log(...t) {
@@ -321,7 +325,7 @@ class p {
321
325
  trace(...t) {
322
326
  }
323
327
  }
324
- function N(a) {
328
+ function L(a) {
325
329
  const t = l.create(a);
326
330
  return async function() {
327
331
  const e = t.next();
@@ -331,20 +335,20 @@ function N(a) {
331
335
  };
332
336
  }
333
337
  class y {
334
- #t;
335
338
  #e;
339
+ #t;
336
340
  #s;
337
341
  constructor({ status: t, statusText: e, body: s = null }) {
338
- this.#t = t, this.#e = e, this.#s = s;
342
+ this.#e = t, this.#t = e, this.#s = s;
339
343
  }
340
344
  get ok() {
341
345
  return this.status >= 200 && this.status < 300;
342
346
  }
343
347
  get status() {
344
- return this.#t;
348
+ return this.#e;
345
349
  }
346
350
  get statusText() {
347
- return this.#e;
351
+ return this.#t;
348
352
  }
349
353
  async blob() {
350
354
  if (this.#s == null)
@@ -378,16 +382,16 @@ class v extends EventTarget {
378
382
  static createNull() {
379
383
  return new v(o);
380
384
  }
381
- #t;
382
385
  #e;
386
+ #t;
383
387
  constructor(t) {
384
- super(), this.#t = t;
388
+ super(), this.#e = t;
385
389
  }
386
390
  get isConnected() {
387
- return this.#e?.readyState === this.#t.OPEN;
391
+ return this.#t?.readyState === this.#e.OPEN;
388
392
  }
389
393
  get url() {
390
- return this.#e?.url;
394
+ return this.#t?.url;
391
395
  }
392
396
  async connect(t, e = "message", ...s) {
393
397
  await new Promise((r, h) => {
@@ -396,18 +400,18 @@ class v extends EventTarget {
396
400
  return;
397
401
  }
398
402
  try {
399
- this.#e = new this.#t(t), this.#e.addEventListener("open", (n) => {
403
+ this.#t = new this.#e(t), this.#t.addEventListener("open", (n) => {
400
404
  this.#s(n), r();
401
- }), this.#e.addEventListener(
405
+ }), this.#t.addEventListener(
402
406
  e,
403
407
  (n) => this.#r(n)
404
408
  );
405
409
  for (const n of s)
406
- this.#e.addEventListener(
410
+ this.#t.addEventListener(
407
411
  n,
408
412
  (d) => this.#r(d)
409
413
  );
410
- this.#e.addEventListener(
414
+ this.#t.addEventListener(
411
415
  "error",
412
416
  (n) => this.#n(n)
413
417
  );
@@ -426,7 +430,7 @@ class v extends EventTarget {
426
430
  return;
427
431
  }
428
432
  try {
429
- this.#e.close(), t();
433
+ this.#t.close(), t();
430
434
  } catch (s) {
431
435
  e(s);
432
436
  }
@@ -506,14 +510,14 @@ class g extends EventTarget {
506
510
  b
507
511
  );
508
512
  }
509
- #t;
510
513
  #e;
514
+ #t;
511
515
  #s;
512
516
  #r;
513
517
  #n;
514
518
  #i;
515
519
  constructor(t, e, s) {
516
- super(), this.#t = t, this.#e = e, this.#s = s;
520
+ super(), this.#e = t, this.#t = e, this.#s = s;
517
521
  }
518
522
  get isConnected() {
519
523
  return this.#r?.readyState === WebSocket.OPEN;
@@ -615,18 +619,18 @@ class g extends EventTarget {
615
619
  this.dispatchEvent(new Event(t.type, t)), this.#d();
616
620
  }
617
621
  #d() {
618
- this.#e <= 0 || (this.#i = setInterval(
622
+ this.#t <= 0 || (this.#i = setInterval(
619
623
  () => this.connect(this.#r.url),
620
- this.#e
624
+ this.#t
621
625
  ));
622
626
  }
623
627
  #c() {
624
628
  clearInterval(this.#i), this.#i = void 0;
625
629
  }
626
630
  #E() {
627
- this.#t <= 0 || (this.#n = setInterval(
631
+ this.#e <= 0 || (this.#n = setInterval(
628
632
  () => this.#l(),
629
- this.#t
633
+ this.#e
630
634
  ));
631
635
  }
632
636
  #v() {
@@ -655,12 +659,12 @@ export {
655
659
  l as ConfigurableResponses,
656
660
  E as ConsoleLog,
657
661
  f as EventTracker,
658
- L as Failure,
662
+ N as Failure,
659
663
  m as HEARTBEAT_TYPE,
660
664
  u as OutputTracker,
661
665
  v as SseClient,
662
666
  S as Success,
663
667
  g as WebSocketClient,
664
- N as createFetchStub
668
+ L as createFetchStub
665
669
  };
666
670
  //# sourceMappingURL=shared.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"shared.js","sources":["../src/common/clock.ts","../src/common/event_tracker.ts","../src/domain/messages.ts","../src/infrastructure/configurable_responses.ts","../src/infrastructure/output_tracker.ts","../src/infrastructure/console_log.ts","../src/infrastructure/fetch_stub.ts","../src/infrastructure/sse_client.ts","../src/infrastructure/web_socket_client.ts"],"sourcesContent":["// Copyright (c) 2025 Falko Schumann. All rights reserved. MIT license.\n\n/**\n * A clock provides access to the current timestamp.\n */\nexport class Clock {\n /**\n * Create a clock using system the clock.\n *\n * @return A clock that uses the system clock.\n */\n static system(): Clock {\n return new Clock();\n }\n\n /**\n * Create a clock using a fixed date.\n *\n * @param date The fixed date of the clock.\n * @return A clock that always returns a fixed date.\n */\n static fixed(date: Date | string | number): Clock {\n return new Clock(new Date(date));\n }\n\n /**\n * Create a clock that returns a fixed offset from the given clock.\n *\n * @param clock The clock to offset from.\n * @param offsetMillis The offset in milliseconds.\n * @return A clock that returns a fixed offset from the given clock.\n */\n static offset(clock: Clock, offsetMillis: number): Clock {\n return new Clock(new Date(clock.millis() + offsetMillis));\n }\n\n readonly #date?: Date;\n\n private constructor(date?: Date) {\n this.#date = date;\n }\n\n /**\n * Return the current timestamp of the clock.\n *\n * @return The current timestamp.\n */\n date(): Date {\n return this.#date ? new Date(this.#date) : new Date();\n }\n\n /**\n * Return the current timestamp of the clock in milliseconds.\n *\n * @return The current timestamp in milliseconds.\n */\n millis(): number {\n return this.date().getTime();\n }\n}\n","// Copyright (c) 2025 Falko Schumann. All rights reserved. MIT license.\n\n// TODO monitor multiple events\n\n/**\n * Track events from an event target.\n *\n * Wait asynchronously for events. Useful in test code.\n */\nexport class EventTracker<T extends Event> {\n /**\n * Create a tracker for a specific event of an event target.\n *\n * @param eventTarget The target to track.\n * @param event The event name to track.\n */\n static create<T extends Event>(eventTarget: EventTarget, ...event: string[]) {\n return new EventTracker<T>(eventTarget, event);\n }\n\n readonly #eventTarget;\n readonly #event;\n readonly #events: T[];\n readonly #tracker;\n\n /**\n * Create a tracker for a specific event of an event target.\n *\n * @param eventTarget The target to track.\n * @param event The event name to track.\n */\n constructor(eventTarget: EventTarget, event: string[]) {\n this.#eventTarget = eventTarget;\n this.#event = event;\n this.#events = [];\n this.#tracker = (event: Event) => this.#events.push(event as T);\n\n this.#event.forEach((event) =>\n this.#eventTarget.addEventListener(event, this.#tracker),\n );\n }\n\n /**\n * Return the tracked events.\n *\n * @return The tracked events.\n */\n get events(): T[] {\n return this.#events;\n }\n\n /**\n * Clear the tracked events and return the cleared events.\n *\n * @return The cleared events.\n */\n clear(): T[] {\n const result = [...this.#events];\n this.#events.length = 0;\n return result;\n }\n\n /**\n * Stop tracking.\n */\n stop() {\n this.#event.forEach((event) =>\n this.#eventTarget.removeEventListener(event, this.#tracker),\n );\n }\n\n /**\n * Wait asynchronously for a number of events.\n *\n * @param count number of events, default 1.\n */\n async waitFor(count = 1) {\n return new Promise<T[]>((resolve) => {\n const checkEvents = () => {\n if (this.#events.length >= count) {\n this.#event.forEach((event) =>\n this.#eventTarget.removeEventListener(event, checkEvents),\n );\n resolve(this.events);\n }\n };\n\n this.#event.forEach((event) =>\n this.#eventTarget.addEventListener(event, checkEvents),\n );\n checkEvents();\n });\n }\n}\n","// Copyright (c) 2025 Falko Schumann. All rights reserved. MIT license.\n\n/**\n * Provides CQNS features.\n *\n * The Command Query Notification Separation principle is a software design\n * principle that separates the concerns of commands, queries, and\n * notifications.\n *\n * Message hierarchy:\n *\n * - Message\n * - Incoming / outgoing\n * - Request (outgoing) -> response (incoming)\n * - Command -> command status\n * - Query -> query result\n * - Notification\n * - Incoming: notification -> commands\n * - Outgoing\n * - Event (internal)\n *\n * @see https://ralfw.de/command-query-notification-separation-cqns/\n * @module\n */\n\n/**\n * The status returned by a command handler.\n */\nexport type CommandStatus = Success | Failure;\n\n/**\n * A successful status.\n */\nexport class Success<T = unknown> {\n readonly isSuccess = true;\n readonly result?: T;\n\n constructor(result?: T) {\n this.result = result;\n }\n}\n\n/**\n * A failed status.\n */\nexport class Failure<T = string> {\n readonly isSuccess = false;\n readonly errorMessage: T;\n\n /**\n * Creates a failed status.\n *\n * @param errorMessage\n */\n constructor(errorMessage: T) {\n this.errorMessage = errorMessage;\n }\n}\n","// Copyright (c) 2025 Falko Schumann. All rights reserved. MIT license.\n// Copyright 2023 Titanium I.T. LLC. MIT License.\n\n/**\n * Handle returning pre-configured responses.\n *\n * This is one of the nullability patterns from James Shore's article on\n * [testing without mocks](https://www.jamesshore.com/v2/projects/nullables/testing-without-mocks#configurable-responses).\n *\n * Example usage for stubbing `fetch` function:\n *\n * ```javascript\n * function createFetchStub(responses) {\n * const configurableResponses = ConfigurableResponses.create(responses);\n * return async function () {\n * const response = configurableResponses.next();\n * return {\n * status: response.status,\n * json: async () => response.body,\n * };\n * };\n * }\n * ```\n */\nexport class ConfigurableResponses<T = unknown> {\n /**\n * Create a list of responses (by providing an array), or a single repeating\n * response (by providing any other type). 'Name' is optional and used in\n * error messages.\n *\n * @param responses A single response or an array of responses.\n * @param name An optional name for the responses.\n */\n static create<T>(responses?: T | T[], name?: string) {\n return new ConfigurableResponses<T>(responses, name);\n }\n\n /**\n * Convert all properties in an object into ConfigurableResponse instances.\n * For example, { a: 1 } becomes { a: ConfigurableResponses.create(1) }.\n * 'Name' is optional and used in error messages.\n *\n * @param responseObject An object with single response or an array of responses.\n * @param name An optional name for the responses.\n */\n static mapObject<T extends Record<string, unknown>>(\n responseObject: T,\n name?: string,\n ) {\n const entries = Object.entries(responseObject);\n const translatedEntries = entries.map(([key, value]) => {\n const translatedName = name === undefined ? undefined : `${name}: ${key}`;\n return [key, ConfigurableResponses.create(value, translatedName)];\n });\n return Object.fromEntries(translatedEntries);\n }\n\n readonly #description;\n readonly #responses;\n\n /**\n * Create a list of responses (by providing an array), or a single repeating\n * response (by providing any other type). 'Name' is optional and used in\n * error messages.\n *\n * @param responses A single response or an array of responses.\n * @param name An optional name for the responses.\n */\n constructor(responses?: T | T[], name?: string) {\n this.#description = name == null ? \"\" : ` in ${name}`;\n this.#responses = Array.isArray(responses) ? [...responses] : responses;\n }\n\n /**\n * Get the next configured response. Throws an error when configured with a list\n * of responses and no more responses remain.\n *\n * @return The next response.\n */\n next(): T {\n const response = Array.isArray(this.#responses)\n ? this.#responses.shift()\n : this.#responses;\n if (response === undefined) {\n throw new Error(`No more responses configured${this.#description}.`);\n }\n\n return response;\n }\n}\n","// Copyright (c) 2025 Falko Schumann. All rights reserved. MIT license.\n// Copyright 2020-2022 Titanium I.T. LLC. MIT License.\n\n/**\n * Track output events.\n *\n * This is one of the nullability patterns from James Shore's article on\n * [testing without mocks](https://www.jamesshore.com/v2/projects/nullables/testing-without-mocks#output-tracking).\n *\n * Example implementation of an event store:\n *\n * ```javascript\n * async record(event) {\n * // ...\n * this.dispatchEvent(new CustomEvent(\"eventRecorded\", { detail: event }));\n * }\n *\n * trackEventsRecorded() {\n * return new OutputTracker(this, \"eventRecorded\");\n * }\n * ```\n *\n * Example usage:\n *\n * ```javascript\n * const eventsRecorded = eventStore.trackEventsRecorded();\n * // ...\n * const data = eventsRecorded.data(); // [event1, event2, ...]\n * ```\n */\nexport class OutputTracker<T = unknown> {\n /**\n * Create a tracker for a specific event of an event target.\n *\n * @param eventTarget The target to track.\n * @param event The event name to track.\n */\n static create<T>(eventTarget: EventTarget, event: string) {\n return new OutputTracker<T>(eventTarget, event);\n }\n\n readonly #eventTarget;\n readonly #event;\n readonly #data: T[];\n readonly #tracker;\n\n /**\n * Create a tracker for a specific event of an event target.\n *\n * @param eventTarget The target to track.\n * @param event The event name to track.\n */\n constructor(eventTarget: EventTarget, event: string) {\n this.#eventTarget = eventTarget;\n this.#event = event;\n this.#data = [];\n this.#tracker = (event: Event) =>\n this.#data.push((event as CustomEvent<T>).detail);\n\n this.#eventTarget.addEventListener(this.#event, this.#tracker);\n }\n\n /**\n * Return the tracked data.\n *\n * @return The tracked data.\n */\n get data(): T[] {\n return this.#data;\n }\n\n /**\n * Clear the tracked data and return the cleared data.\n *\n * @return The cleared data.\n */\n clear(): T[] {\n const result = [...this.#data];\n this.#data.length = 0;\n return result;\n }\n\n /**\n * Stop tracking.\n */\n stop() {\n this.#eventTarget.removeEventListener(this.#event, this.#tracker);\n }\n}\n","// Copyright (c) 2026 Falko Schumann. All rights reserved. MIT license.\n\nimport type { Log, LogLevel } from \"../common/log\";\nimport { OutputTracker } from \"./output_tracker\";\n\nconst MESSAGE_EVENT = \"message\";\n\nexport interface ConsoleMessage {\n level: LogLevel;\n message: unknown[];\n}\n\n/**\n * Wraps the console interface and allow setting the log level.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/API/Console_API\n */\nexport class ConsoleLog extends EventTarget implements Log {\n static create() {\n return new ConsoleLog(globalThis.console);\n }\n\n static createNull() {\n return new ConsoleLog(new ConsoleStub());\n }\n\n level: LogLevel = \"info\";\n\n #console;\n\n private constructor(console: Log) {\n super();\n this.#console = console;\n }\n\n log(...data: unknown[]) {\n if (!this.isLoggable(\"log\")) {\n return;\n }\n\n this.#console.log(...data);\n this.dispatchEvent(\n new CustomEvent(MESSAGE_EVENT, {\n detail: { level: \"log\", message: data },\n }),\n );\n }\n\n error(...data: unknown[]) {\n if (!this.isLoggable(\"error\")) {\n return;\n }\n\n this.#console.error(...data);\n this.dispatchEvent(\n new CustomEvent(MESSAGE_EVENT, {\n detail: { level: \"error\", message: data },\n }),\n );\n }\n\n warn(...data: unknown[]) {\n if (!this.isLoggable(\"warn\")) {\n return;\n }\n\n this.#console.warn(...data);\n this.dispatchEvent(\n new CustomEvent(MESSAGE_EVENT, {\n detail: { level: \"warn\", message: data },\n }),\n );\n }\n\n info(...data: unknown[]) {\n if (!this.isLoggable(\"info\")) {\n return;\n }\n\n this.#console.info(...data);\n this.dispatchEvent(\n new CustomEvent(MESSAGE_EVENT, {\n detail: { level: \"info\", message: data },\n }),\n );\n }\n\n debug(...data: unknown[]) {\n if (!this.isLoggable(\"debug\")) {\n return;\n }\n\n this.#console.debug(...data);\n this.dispatchEvent(\n new CustomEvent(MESSAGE_EVENT, {\n detail: { level: \"debug\", message: data },\n }),\n );\n }\n\n trace(...data: unknown[]) {\n if (!this.isLoggable(\"trace\")) {\n return;\n }\n\n this.#console.trace(...data);\n this.dispatchEvent(\n new CustomEvent(MESSAGE_EVENT, {\n detail: { level: \"trace\", message: data },\n }),\n );\n }\n\n /**\n * Track the console messages.\n */\n trackMessages() {\n return new OutputTracker<ConsoleMessage>(this, MESSAGE_EVENT);\n }\n\n isLoggable(level: LogLevel) {\n const normalize = (level: LogLevel) => (level === \"log\" ? \"info\" : level);\n const levels: LogLevel[] = [\n \"off\",\n \"error\",\n \"warn\",\n \"info\",\n \"debug\",\n \"trace\",\n ];\n const currentLevelIndex = levels.indexOf(normalize(this.level));\n const messageLevelIndex = levels.indexOf(normalize(level));\n return messageLevelIndex <= currentLevelIndex;\n }\n}\n\nclass ConsoleStub implements Log {\n log(..._data: unknown[]) {}\n error(..._data: unknown[]) {}\n warn(..._data: unknown[]) {}\n info(..._data: unknown[]) {}\n debug(..._data: unknown[]) {}\n trace(..._data: unknown[]) {}\n}\n","// Copyright (c) 2025 Falko Schumann. All rights reserved. MIT license.\n\nimport { ConfigurableResponses } from \"./configurable_responses\";\n\n/**\n * This data object configures the response of a fetch stub call.\n */\nexport interface ResponseData {\n /** The HTTP status code. */\n status: number;\n\n /** The HTTP status text. */\n statusText: string;\n\n /** The optional response body. */\n body?: Blob | object | string | null;\n}\n\n/**\n * Create a fetch stub.\n *\n * The stub returns a response from the provided response data or throws an provided error.\n *\n * @param responses A single response or an array of responses.\n * @returns The fetch stub.\n */\nexport function createFetchStub(\n responses?: ResponseData | Error | (ResponseData | Error)[],\n): () => Promise<Response> {\n const configurableResponses = ConfigurableResponses.create(responses);\n return async function () {\n const response = configurableResponses.next();\n if (response instanceof Error) {\n throw response;\n }\n\n return new ResponseStub(response) as unknown as Response;\n };\n}\n\nclass ResponseStub {\n #status: number;\n #statusText: string;\n #body?: Blob | object | string | null;\n\n constructor({ status, statusText, body = null }: ResponseData) {\n this.#status = status;\n this.#statusText = statusText;\n this.#body = body;\n }\n\n get ok() {\n return this.status >= 200 && this.status < 300;\n }\n\n get status() {\n return this.#status;\n }\n\n get statusText() {\n return this.#statusText;\n }\n\n async blob() {\n if (this.#body == null) {\n return null;\n }\n\n if (this.#body instanceof Blob) {\n return this.#body;\n }\n\n throw new TypeError(\"Body is not a Blob.\");\n }\n\n async json() {\n const json =\n typeof this.#body === \"string\" ? this.#body : JSON.stringify(this.#body);\n return Promise.resolve(JSON.parse(json));\n }\n\n async text() {\n if (this.#body == null) {\n return \"\";\n }\n\n return String(this.#body);\n }\n}\n","// Copyright (c) 2025 Falko Schumann. All rights reserved. MIT license.\n\nimport type { MessageClient } from \"./message_client\";\n\n/**\n * A client for the server-sent events protocol.\n */\nexport class SseClient extends EventTarget implements MessageClient {\n /**\n * Create an SSE client.\n *\n * @return A new SSE client.\n */\n static create(): SseClient {\n return new SseClient(EventSource);\n }\n\n /**\n * Create a nulled SSE client.\n *\n * @return A new SSE client.\n */\n static createNull(): SseClient {\n return new SseClient(EventSourceStub as typeof EventSource);\n }\n\n readonly #eventSourceConstructor: typeof EventSource;\n\n #eventSource?: EventSource;\n\n private constructor(eventSourceConstructor: typeof EventSource) {\n super();\n this.#eventSourceConstructor = eventSourceConstructor;\n }\n\n get isConnected(): boolean {\n return this.#eventSource?.readyState === this.#eventSourceConstructor.OPEN;\n }\n\n get url(): string | undefined {\n return this.#eventSource?.url;\n }\n\n async connect(\n url: string | URL,\n eventName = \"message\",\n ...otherEvents: string[]\n ): Promise<void> {\n await new Promise<void>((resolve, reject) => {\n if (this.isConnected) {\n reject(new Error(\"Already connected.\"));\n return;\n }\n\n try {\n this.#eventSource = new this.#eventSourceConstructor(url);\n this.#eventSource.addEventListener(\"open\", (e) => {\n this.#handleOpen(e);\n resolve();\n });\n this.#eventSource.addEventListener(eventName, (e) =>\n this.#handleMessage(e),\n );\n for (const otherEvent of otherEvents) {\n this.#eventSource.addEventListener(otherEvent, (e) =>\n this.#handleMessage(e),\n );\n }\n this.#eventSource.addEventListener(\"error\", (e) =>\n this.#handleError(e),\n );\n } catch (error) {\n reject(error);\n }\n });\n }\n\n send(_message: string, _type?: string): Promise<void> {\n throw new Error(\"Method not implemented.\");\n }\n\n async close(): Promise<void> {\n await new Promise<void>((resolve, reject) => {\n if (!this.isConnected) {\n resolve();\n return;\n }\n\n try {\n this.#eventSource!.close();\n resolve();\n } catch (error) {\n reject(error);\n }\n });\n }\n\n /**\n * Simulate a message event from the server.\n *\n * @param message The message to receive.\n * @param eventName The optional event type.\n * @param lastEventId The optional last event ID.\n */\n simulateMessage(\n message: string | number | boolean | object | null,\n eventName = \"message\",\n lastEventId?: string,\n ) {\n if (typeof message !== \"string\") {\n message = JSON.stringify(message);\n }\n this.#handleMessage(\n new MessageEvent(eventName, { data: message, lastEventId }),\n );\n }\n\n /**\n * Simulate an error event.\n */\n simulateError() {\n this.#handleError(new Event(\"error\"));\n }\n\n #handleOpen(event: Event) {\n this.dispatchEvent(new Event(event.type, event));\n }\n\n #handleMessage(event: MessageEvent) {\n this.dispatchEvent(\n new MessageEvent(event.type, event as unknown as MessageEventInit),\n );\n }\n\n #handleError(event: Event) {\n this.dispatchEvent(new Event(event.type, event));\n }\n}\n\nclass EventSourceStub extends EventTarget {\n // The constants have to be defined here because Node.js support is currently\n // experimental only.\n static CONNECTING = 0;\n static OPEN = 1;\n static CLOSED = 2;\n\n url: string;\n readyState = EventSourceStub.CONNECTING;\n\n constructor(url: string | URL) {\n super();\n this.url = url.toString();\n setTimeout(() => {\n this.readyState = EventSourceStub.OPEN;\n this.dispatchEvent(new Event(\"open\"));\n }, 0);\n }\n\n close() {\n this.readyState = EventSourceStub.CLOSED;\n }\n}\n","// Copyright (c) 2025 Falko Schumann. All rights reserved. MIT license.\n\nimport { OutputTracker } from \"./output_tracker\";\nimport type { MessageClient } from \"./message_client\";\n\nexport const HEARTBEAT_TYPE = \"heartbeat\";\n\nconst MESSAGE_SENT_EVENT = \"message-sent\";\n\n/**\n * Options for the WebSocket client.\n */\nexport interface WebSocketOptions {\n /**\n * The heartbeat interval in milliseconds. A value <= 0 disables the\n * heartbeat.\n */\n heartbeat?: number;\n\n /**\n * The time in milliseconds to wait before retrying a connection after an\n * error. A value <= 0 disables automatic retries.\n */\n retry?: number;\n}\n\n/**\n * A client for the WebSocket protocol.\n */\nexport class WebSocketClient extends EventTarget implements MessageClient {\n /**\n * Create a WebSocket client.\n *\n * @param options The options for the WebSocket client.\n * @return A new WebSocket client.\n */\n static create({\n heartbeat = 30000,\n retry = 1000,\n }: WebSocketOptions = {}): WebSocketClient {\n return new WebSocketClient(heartbeat, retry, WebSocket);\n }\n\n /**\n * Create a nulled WebSocket client.\n *\n * @param options The options for the WebSocket client.\n * @return A new nulled WebSocket client.\n */\n static createNull({ heartbeat = 0, retry = 0 }: WebSocketOptions = {}) {\n return new WebSocketClient(\n heartbeat,\n retry,\n WebSocketStub as unknown as typeof WebSocket,\n );\n }\n\n readonly #heartbeat: number;\n readonly #retry: number;\n readonly #webSocketConstructor: typeof WebSocket;\n\n #webSocket?: WebSocket;\n #heartbeatId?: ReturnType<typeof setTimeout>;\n #retryId?: ReturnType<typeof setTimeout>;\n\n private constructor(\n heartbeat: number,\n retry: number,\n webSocketConstructor: typeof WebSocket,\n ) {\n super();\n this.#heartbeat = heartbeat;\n this.#retry = retry;\n this.#webSocketConstructor = webSocketConstructor;\n }\n\n get isConnected(): boolean {\n return this.#webSocket?.readyState === WebSocket.OPEN;\n }\n\n get url(): string | undefined {\n return this.#webSocket?.url;\n }\n\n async connect(url: string | URL): Promise<void> {\n await new Promise<void>((resolve, reject) => {\n this.#stopRetry();\n\n if (this.isConnected) {\n reject(new Error(\"Already connected.\"));\n return;\n }\n\n try {\n this.#webSocket = new this.#webSocketConstructor(url);\n this.#webSocket.addEventListener(\"open\", (e) => {\n this.#handleOpen(e);\n resolve();\n });\n this.#webSocket.addEventListener(\"message\", (e) =>\n this.#handleMessage(e),\n );\n this.#webSocket.addEventListener(\"close\", (e) => this.#handleClose(e));\n this.#webSocket.addEventListener(\"error\", (e) => this.#handleError(e));\n } catch (error) {\n reject(error);\n }\n });\n }\n\n async send(\n message: string | ArrayBuffer | Blob | ArrayBufferView,\n ): Promise<void> {\n if (!this.isConnected) {\n throw new Error(\"Not connected.\");\n }\n\n this.#webSocket!.send(message);\n this.dispatchEvent(\n new CustomEvent(MESSAGE_SENT_EVENT, { detail: message }),\n );\n await Promise.resolve();\n }\n\n /**\n * Return a tracker for messages sent.\n *\n * @return A new output tracker.\n */\n trackMessageSent(): OutputTracker<string> {\n return OutputTracker.create(this, MESSAGE_SENT_EVENT);\n }\n\n /**\n * Close the connection.\n *\n * If a code is provided, also a reason should be provided.\n *\n * @param code An optional code.\n * @param reason An optional reason.\n */\n async close(code?: number, reason?: string): Promise<void> {\n await new Promise<void>((resolve) => {\n this.#stopRetry();\n\n if (!this.isConnected) {\n resolve();\n return;\n }\n\n this.#webSocket!.addEventListener(\"close\", () => resolve());\n this.#webSocket!.close(code, reason);\n });\n }\n\n /**\n * Simulate a message event from the server.\n *\n * @param message The message to receive.\n */\n simulateMessage(\n message: string | number | boolean | object | null | Blob | ArrayBuffer,\n ) {\n if (\n typeof message !== \"string\" &&\n !(message instanceof Blob) &&\n !(message instanceof ArrayBuffer)\n ) {\n message = JSON.stringify(message);\n }\n this.#handleMessage(new MessageEvent(\"message\", { data: message }));\n }\n\n /**\n * Simulate a heartbeat.\n */\n simulateHeartbeat() {\n this.#sendHeartbeat();\n }\n\n /**\n * Simulate a close event.\n *\n * @param code An optional code.\n * @param reason An optional reason.\n */\n simulateClose(code?: number, reason?: string) {\n this.#handleClose(new CloseEvent(\"close\", { code, reason }));\n }\n\n /**\n * Simulate an error event.\n */\n simulateError() {\n this.#webSocket?.close();\n this.#handleError(new Event(\"error\"));\n }\n\n #handleOpen(event: Event) {\n this.dispatchEvent(new Event(event.type, event));\n this.#startHeartbeat();\n }\n\n #handleMessage(event: MessageEvent) {\n this.dispatchEvent(\n new MessageEvent(event.type, event as unknown as MessageEventInit),\n );\n }\n\n #handleClose(event: CloseEvent) {\n this.#stopHeartbeat();\n this.dispatchEvent(new CloseEvent(event.type, event));\n }\n\n #handleError(event: Event) {\n this.dispatchEvent(new Event(event.type, event));\n this.#startRetry();\n }\n\n #startRetry() {\n if (this.#retry <= 0) {\n return;\n }\n this.#retryId = setInterval(\n () => this.connect(this.#webSocket!.url),\n this.#retry,\n );\n }\n\n #stopRetry() {\n clearInterval(this.#retryId);\n this.#retryId = undefined;\n }\n\n #startHeartbeat() {\n if (this.#heartbeat <= 0) {\n return;\n }\n\n this.#heartbeatId = setInterval(\n () => this.#sendHeartbeat(),\n this.#heartbeat,\n );\n }\n\n #stopHeartbeat() {\n clearInterval(this.#heartbeatId);\n this.#heartbeatId = undefined;\n }\n\n #sendHeartbeat() {\n if (this.#heartbeatId == null) {\n return;\n }\n\n void this.send(HEARTBEAT_TYPE);\n }\n}\n\nclass WebSocketStub extends EventTarget {\n url: string;\n readyState: number = WebSocket.CONNECTING;\n\n constructor(url: string | URL) {\n super();\n this.url = url.toString();\n setTimeout(() => {\n this.readyState = WebSocket.OPEN;\n this.dispatchEvent(new Event(\"open\"));\n }, 0);\n }\n\n send() {}\n\n close() {\n this.readyState = WebSocket.CLOSED;\n this.dispatchEvent(new Event(\"close\"));\n }\n}\n"],"names":["Clock","date","clock","offsetMillis","#date","EventTracker","eventTarget","event","#eventTarget","#event","#events","#tracker","result","count","resolve","checkEvents","Success","Failure","errorMessage","ConfigurableResponses","responses","name","responseObject","translatedEntries","key","value","translatedName","#description","#responses","response","OutputTracker","#data","MESSAGE_EVENT","ConsoleLog","ConsoleStub","#console","console","data","level","normalize","levels","currentLevelIndex","_data","createFetchStub","configurableResponses","ResponseStub","#status","#statusText","#body","status","statusText","body","json","SseClient","EventSourceStub","#eventSourceConstructor","#eventSource","eventSourceConstructor","url","eventName","otherEvents","reject","e","#handleOpen","#handleMessage","otherEvent","#handleError","error","_message","_type","message","lastEventId","HEARTBEAT_TYPE","MESSAGE_SENT_EVENT","WebSocketClient","heartbeat","retry","WebSocketStub","#heartbeat","#retry","#webSocketConstructor","#webSocket","#heartbeatId","#retryId","webSocketConstructor","#stopRetry","#handleClose","code","reason","#sendHeartbeat","#startHeartbeat","#stopHeartbeat","#startRetry"],"mappings":"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"}
1
+ {"version":3,"file":"shared.js","sources":["../src/common/clock.ts","../src/common/event_tracker.ts","../src/domain/messages.ts","../src/infrastructure/configurable_responses.ts","../src/infrastructure/output_tracker.ts","../src/infrastructure/console_log.ts","../src/infrastructure/fetch_stub.ts","../src/infrastructure/sse_client.ts","../src/infrastructure/web_socket_client.ts"],"sourcesContent":["// Copyright (c) 2025 Falko Schumann. All rights reserved. MIT license.\n\n/**\n * A clock provides access to the current timestamp.\n */\nexport class Clock {\n /**\n * Create a clock using system the clock.\n *\n * @return A clock that uses the system clock.\n */\n static system(): Clock {\n return new Clock();\n }\n\n /**\n * Create a clock using a fixed date.\n *\n * @param date The fixed date of the clock.\n * @return A clock that always returns a fixed date.\n */\n static fixed(date: Date | string | number): Clock {\n return new Clock(new Date(date));\n }\n\n /**\n * Create a clock that returns a fixed offset from the given clock.\n *\n * @param clock The clock to offset from.\n * @param offsetMillis The offset in milliseconds.\n * @return A clock that returns a fixed offset from the given clock.\n */\n static offset(clock: Clock, offsetMillis: number): Clock {\n return new Clock(new Date(clock.millis() + offsetMillis));\n }\n\n readonly #date?: Date;\n\n private constructor(date?: Date) {\n this.#date = date;\n }\n\n /**\n * Return the current timestamp of the clock.\n *\n * @return The current timestamp.\n */\n date(): Date {\n return this.#date ? new Date(this.#date) : new Date();\n }\n\n /**\n * Return the current timestamp of the clock in milliseconds.\n *\n * @return The current timestamp in milliseconds.\n */\n millis(): number {\n return this.date().getTime();\n }\n}\n","// Copyright (c) 2025 Falko Schumann. All rights reserved. MIT license.\n\n// TODO monitor multiple events\n\n/**\n * Track events from an event target.\n *\n * Wait asynchronously for events. Useful in test code.\n */\nexport class EventTracker<T extends Event> {\n /**\n * Create a tracker for a specific event of an event target.\n *\n * @param eventTarget The target to track.\n * @param event The event name to track.\n */\n static create<T extends Event>(eventTarget: EventTarget, ...event: string[]) {\n return new EventTracker<T>(eventTarget, event);\n }\n\n readonly #eventTarget;\n readonly #event;\n readonly #events: T[];\n readonly #tracker;\n\n /**\n * Create a tracker for a specific event of an event target.\n *\n * @param eventTarget The target to track.\n * @param event The event name to track.\n */\n constructor(eventTarget: EventTarget, event: string[]) {\n this.#eventTarget = eventTarget;\n this.#event = event;\n this.#events = [];\n this.#tracker = (event: Event) => this.#events.push(event as T);\n\n this.#event.forEach((event) =>\n this.#eventTarget.addEventListener(event, this.#tracker),\n );\n }\n\n /**\n * Return the tracked events.\n *\n * @return The tracked events.\n */\n get events(): T[] {\n return this.#events;\n }\n\n /**\n * Clear the tracked events and return the cleared events.\n *\n * @return The cleared events.\n */\n clear(): T[] {\n const result = [...this.#events];\n this.#events.length = 0;\n return result;\n }\n\n /**\n * Stop tracking.\n */\n stop() {\n this.#event.forEach((event) =>\n this.#eventTarget.removeEventListener(event, this.#tracker),\n );\n }\n\n /**\n * Wait asynchronously for a number of events.\n *\n * @param count number of events, default 1.\n */\n async waitFor(count = 1) {\n return new Promise<T[]>((resolve) => {\n const checkEvents = () => {\n if (this.#events.length >= count) {\n this.#event.forEach((event) =>\n this.#eventTarget.removeEventListener(event, checkEvents),\n );\n resolve(this.events);\n }\n };\n\n this.#event.forEach((event) =>\n this.#eventTarget.addEventListener(event, checkEvents),\n );\n checkEvents();\n });\n }\n}\n","// Copyright (c) 2025 Falko Schumann. All rights reserved. MIT license.\n\n/**\n * Provides CQNS features.\n *\n * The Command Query Notification Separation principle is a software design\n * principle that separates the concerns of commands, queries, and\n * notifications.\n *\n * Message hierarchy:\n *\n * - Message\n * - Incoming / outgoing\n * - Request (outgoing) -> response (incoming)\n * - Command -> command status\n * - Query -> query result\n * - Notification\n * - Incoming: notification -> commands\n * - Outgoing\n * - Event (internal)\n *\n * @see https://ralfw.de/command-query-notification-separation-cqns/\n * @module\n */\n\n/**\n * The status returned by a command handler.\n */\nexport type CommandStatus = Success | Failure;\n\n/**\n * A successful status.\n */\nexport class Success<T = unknown> {\n readonly isSuccess = true;\n readonly result?: T;\n\n constructor(result?: T) {\n this.result = result;\n }\n}\n\n/**\n * A failed status.\n */\nexport class Failure<T = string> {\n readonly isSuccess = false;\n readonly errorMessage: T;\n\n /**\n * Creates a failed status.\n *\n * @param errorMessage\n */\n constructor(errorMessage: T) {\n this.errorMessage = errorMessage;\n }\n}\n","// Copyright (c) 2025 Falko Schumann. All rights reserved. MIT license.\n// Copyright 2023 Titanium I.T. LLC. MIT License.\n\n/**\n * Handle returning pre-configured responses.\n *\n * This is one of the nullability patterns from James Shore's article on\n * [testing without mocks](https://www.jamesshore.com/v2/projects/nullables/testing-without-mocks#configurable-responses).\n *\n * Example usage for stubbing `fetch` function:\n *\n * ```javascript\n * function createFetchStub(responses) {\n * const configurableResponses = ConfigurableResponses.create(responses);\n * return async function () {\n * const response = configurableResponses.next();\n * return {\n * status: response.status,\n * json: async () => response.body,\n * };\n * };\n * }\n * ```\n */\nexport class ConfigurableResponses<T = unknown> {\n /**\n * Create a list of responses (by providing an array), or a single repeating\n * response (by providing any other type). 'Name' is optional and used in\n * error messages.\n *\n * @param responses A single response or an array of responses.\n * @param name An optional name for the responses.\n */\n static create<T>(responses?: T | T[], name?: string) {\n return new ConfigurableResponses<T>(responses, name);\n }\n\n /**\n * Convert all properties in an object into ConfigurableResponse instances.\n * For example, { a: 1 } becomes { a: ConfigurableResponses.create(1) }.\n * 'Name' is optional and used in error messages.\n *\n * @param responseObject An object with single response or an array of responses.\n * @param name An optional name for the responses.\n */\n static mapObject<T extends Record<string, unknown>>(\n responseObject: T,\n name?: string,\n ) {\n const entries = Object.entries(responseObject);\n const translatedEntries = entries.map(([key, value]) => {\n const translatedName = name === undefined ? undefined : `${name}: ${key}`;\n return [key, ConfigurableResponses.create(value, translatedName)];\n });\n return Object.fromEntries(translatedEntries);\n }\n\n readonly #description;\n readonly #responses;\n\n /**\n * Create a list of responses (by providing an array), or a single repeating\n * response (by providing any other type). 'Name' is optional and used in\n * error messages.\n *\n * @param responses A single response or an array of responses.\n * @param name An optional name for the responses.\n */\n constructor(responses?: T | T[], name?: string) {\n this.#description = name == null ? \"\" : ` in ${name}`;\n this.#responses = Array.isArray(responses) ? [...responses] : responses;\n }\n\n /**\n * Get the next configured response. Throws an error when configured with a list\n * of responses and no more responses remain.\n *\n * @return The next response.\n */\n next(): T {\n const response = Array.isArray(this.#responses)\n ? this.#responses.shift()\n : this.#responses;\n if (response === undefined) {\n throw new Error(`No more responses configured${this.#description}.`);\n }\n\n return response;\n }\n}\n","// Copyright (c) 2025 Falko Schumann. All rights reserved. MIT license.\n// Copyright 2020-2022 Titanium I.T. LLC. MIT License.\n\n/**\n * Track output events.\n *\n * This is one of the nullability patterns from James Shore's article on\n * [testing without mocks](https://www.jamesshore.com/v2/projects/nullables/testing-without-mocks#output-tracking).\n *\n * Example implementation of an event store:\n *\n * ```javascript\n * async record(event) {\n * // ...\n * this.dispatchEvent(new CustomEvent(\"eventRecorded\", { detail: event }));\n * }\n *\n * trackEventsRecorded() {\n * return new OutputTracker(this, \"eventRecorded\");\n * }\n * ```\n *\n * Example usage:\n *\n * ```javascript\n * const eventsRecorded = eventStore.trackEventsRecorded();\n * // ...\n * const data = eventsRecorded.data(); // [event1, event2, ...]\n * ```\n */\nexport class OutputTracker<T = unknown> {\n /**\n * Create a tracker for a specific event of an event target.\n *\n * @param eventTarget The target to track.\n * @param event The event name to track.\n */\n static create<T>(eventTarget: EventTarget, event: string) {\n return new OutputTracker<T>(eventTarget, event);\n }\n\n readonly #eventTarget;\n readonly #event;\n readonly #data: T[];\n readonly #tracker;\n\n /**\n * Create a tracker for a specific event of an event target.\n *\n * @param eventTarget The target to track.\n * @param event The event name to track.\n */\n constructor(eventTarget: EventTarget, event: string) {\n this.#eventTarget = eventTarget;\n this.#event = event;\n this.#data = [];\n this.#tracker = (event: Event) =>\n this.#data.push((event as CustomEvent<T>).detail);\n\n this.#eventTarget.addEventListener(this.#event, this.#tracker);\n }\n\n /**\n * Return the tracked data.\n *\n * @return The tracked data.\n */\n get data(): T[] {\n return this.#data;\n }\n\n /**\n * Clear the tracked data and return the cleared data.\n *\n * @return The cleared data.\n */\n clear(): T[] {\n const result = [...this.#data];\n this.#data.length = 0;\n return result;\n }\n\n /**\n * Stop tracking.\n */\n stop() {\n this.#eventTarget.removeEventListener(this.#event, this.#tracker);\n }\n}\n","// Copyright (c) 2026 Falko Schumann. All rights reserved. MIT license.\n\nimport type { Log, LogLevel } from \"../common/log\";\nimport { OutputTracker } from \"./output_tracker\";\n\nconst MESSAGE_EVENT = \"message\";\n\nexport interface ConsoleMessage {\n level: LogLevel;\n message: unknown[];\n}\n\n/**\n * Wraps the console interface and allow setting the log level.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/API/Console_API\n */\nexport class ConsoleLog extends EventTarget implements Log {\n static create({ name }: { name?: string } = {}) {\n return new ConsoleLog(globalThis.console, name);\n }\n\n static createNull({ name }: { name?: string } = {}) {\n return new ConsoleLog(new ConsoleStub(), name);\n }\n\n name?: string;\n level: LogLevel = \"info\";\n\n #console;\n\n private constructor(console: Log, name?: string) {\n super();\n this.name = name;\n this.#console = console;\n }\n\n log(...data: unknown[]) {\n if (!this.isLoggable(\"log\")) {\n return;\n }\n\n data = this.#applyName(data);\n this.#console.log(...data);\n this.dispatchEvent(\n new CustomEvent(MESSAGE_EVENT, {\n detail: { level: \"log\", message: data },\n }),\n );\n }\n\n error(...data: unknown[]) {\n if (!this.isLoggable(\"error\")) {\n return;\n }\n\n data = this.#applyName(data);\n this.#console.error(...data);\n this.dispatchEvent(\n new CustomEvent(MESSAGE_EVENT, {\n detail: { level: \"error\", message: data },\n }),\n );\n }\n\n warn(...data: unknown[]) {\n if (!this.isLoggable(\"warn\")) {\n return;\n }\n\n data = this.#applyName(data);\n this.#console.warn(...data);\n this.dispatchEvent(\n new CustomEvent(MESSAGE_EVENT, {\n detail: { level: \"warn\", message: data },\n }),\n );\n }\n\n info(...data: unknown[]) {\n if (!this.isLoggable(\"info\")) {\n return;\n }\n\n data = this.#applyName(data);\n this.#console.info(...data);\n this.dispatchEvent(\n new CustomEvent(MESSAGE_EVENT, {\n detail: { level: \"info\", message: data },\n }),\n );\n }\n\n debug(...data: unknown[]) {\n if (!this.isLoggable(\"debug\")) {\n return;\n }\n\n data = this.#applyName(data);\n this.#console.debug(...data);\n this.dispatchEvent(\n new CustomEvent(MESSAGE_EVENT, {\n detail: { level: \"debug\", message: data },\n }),\n );\n }\n\n trace(...data: unknown[]) {\n if (!this.isLoggable(\"trace\")) {\n return;\n }\n\n data = this.#applyName(data);\n this.#console.trace(...data);\n this.dispatchEvent(\n new CustomEvent(MESSAGE_EVENT, {\n detail: { level: \"trace\", message: data },\n }),\n );\n }\n\n /**\n * Track the console messages.\n */\n trackMessages() {\n return new OutputTracker<ConsoleMessage>(this, MESSAGE_EVENT);\n }\n\n isLoggable(level: LogLevel) {\n const normalize = (level: LogLevel) => (level === \"log\" ? \"info\" : level);\n const levels: LogLevel[] = [\n \"off\",\n \"error\",\n \"warn\",\n \"info\",\n \"debug\",\n \"trace\",\n ];\n const currentLevelIndex = levels.indexOf(normalize(this.level));\n const messageLevelIndex = levels.indexOf(normalize(level));\n return messageLevelIndex <= currentLevelIndex;\n }\n\n #applyName(data: unknown[]) {\n if (this.name == null) {\n return data;\n }\n\n return [`${this.name}`, ...data];\n }\n}\n\nclass ConsoleStub implements Log {\n log(..._data: unknown[]) {}\n error(..._data: unknown[]) {}\n warn(..._data: unknown[]) {}\n info(..._data: unknown[]) {}\n debug(..._data: unknown[]) {}\n trace(..._data: unknown[]) {}\n}\n","// Copyright (c) 2025 Falko Schumann. All rights reserved. MIT license.\n\nimport { ConfigurableResponses } from \"./configurable_responses\";\n\n/**\n * This data object configures the response of a fetch stub call.\n */\nexport interface ResponseData {\n /** The HTTP status code. */\n status: number;\n\n /** The HTTP status text. */\n statusText: string;\n\n /** The optional response body. */\n body?: Blob | object | string | null;\n}\n\n/**\n * Create a fetch stub.\n *\n * The stub returns a response from the provided response data or throws an provided error.\n *\n * @param responses A single response or an array of responses.\n * @returns The fetch stub.\n */\nexport function createFetchStub(\n responses?: ResponseData | Error | (ResponseData | Error)[],\n): () => Promise<Response> {\n const configurableResponses = ConfigurableResponses.create(responses);\n return async function () {\n const response = configurableResponses.next();\n if (response instanceof Error) {\n throw response;\n }\n\n return new ResponseStub(response) as unknown as Response;\n };\n}\n\nclass ResponseStub {\n #status: number;\n #statusText: string;\n #body?: Blob | object | string | null;\n\n constructor({ status, statusText, body = null }: ResponseData) {\n this.#status = status;\n this.#statusText = statusText;\n this.#body = body;\n }\n\n get ok() {\n return this.status >= 200 && this.status < 300;\n }\n\n get status() {\n return this.#status;\n }\n\n get statusText() {\n return this.#statusText;\n }\n\n async blob() {\n if (this.#body == null) {\n return null;\n }\n\n if (this.#body instanceof Blob) {\n return this.#body;\n }\n\n throw new TypeError(\"Body is not a Blob.\");\n }\n\n async json() {\n const json =\n typeof this.#body === \"string\" ? this.#body : JSON.stringify(this.#body);\n return Promise.resolve(JSON.parse(json));\n }\n\n async text() {\n if (this.#body == null) {\n return \"\";\n }\n\n return String(this.#body);\n }\n}\n","// Copyright (c) 2025 Falko Schumann. All rights reserved. MIT license.\n\nimport type { MessageClient } from \"./message_client\";\n\n/**\n * A client for the server-sent events protocol.\n */\nexport class SseClient extends EventTarget implements MessageClient {\n /**\n * Create an SSE client.\n *\n * @return A new SSE client.\n */\n static create(): SseClient {\n return new SseClient(EventSource);\n }\n\n /**\n * Create a nulled SSE client.\n *\n * @return A new SSE client.\n */\n static createNull(): SseClient {\n return new SseClient(EventSourceStub as typeof EventSource);\n }\n\n readonly #eventSourceConstructor: typeof EventSource;\n\n #eventSource?: EventSource;\n\n private constructor(eventSourceConstructor: typeof EventSource) {\n super();\n this.#eventSourceConstructor = eventSourceConstructor;\n }\n\n get isConnected(): boolean {\n return this.#eventSource?.readyState === this.#eventSourceConstructor.OPEN;\n }\n\n get url(): string | undefined {\n return this.#eventSource?.url;\n }\n\n async connect(\n url: string | URL,\n eventName = \"message\",\n ...otherEvents: string[]\n ): Promise<void> {\n await new Promise<void>((resolve, reject) => {\n if (this.isConnected) {\n reject(new Error(\"Already connected.\"));\n return;\n }\n\n try {\n this.#eventSource = new this.#eventSourceConstructor(url);\n this.#eventSource.addEventListener(\"open\", (e) => {\n this.#handleOpen(e);\n resolve();\n });\n this.#eventSource.addEventListener(eventName, (e) =>\n this.#handleMessage(e),\n );\n for (const otherEvent of otherEvents) {\n this.#eventSource.addEventListener(otherEvent, (e) =>\n this.#handleMessage(e),\n );\n }\n this.#eventSource.addEventListener(\"error\", (e) =>\n this.#handleError(e),\n );\n } catch (error) {\n reject(error);\n }\n });\n }\n\n send(_message: string, _type?: string): Promise<void> {\n throw new Error(\"Method not implemented.\");\n }\n\n async close(): Promise<void> {\n await new Promise<void>((resolve, reject) => {\n if (!this.isConnected) {\n resolve();\n return;\n }\n\n try {\n this.#eventSource!.close();\n resolve();\n } catch (error) {\n reject(error);\n }\n });\n }\n\n /**\n * Simulate a message event from the server.\n *\n * @param message The message to receive.\n * @param eventName The optional event type.\n * @param lastEventId The optional last event ID.\n */\n simulateMessage(\n message: string | number | boolean | object | null,\n eventName = \"message\",\n lastEventId?: string,\n ) {\n if (typeof message !== \"string\") {\n message = JSON.stringify(message);\n }\n this.#handleMessage(\n new MessageEvent(eventName, { data: message, lastEventId }),\n );\n }\n\n /**\n * Simulate an error event.\n */\n simulateError() {\n this.#handleError(new Event(\"error\"));\n }\n\n #handleOpen(event: Event) {\n this.dispatchEvent(new Event(event.type, event));\n }\n\n #handleMessage(event: MessageEvent) {\n this.dispatchEvent(\n new MessageEvent(event.type, event as unknown as MessageEventInit),\n );\n }\n\n #handleError(event: Event) {\n this.dispatchEvent(new Event(event.type, event));\n }\n}\n\nclass EventSourceStub extends EventTarget {\n // The constants have to be defined here because Node.js support is currently\n // experimental only.\n static CONNECTING = 0;\n static OPEN = 1;\n static CLOSED = 2;\n\n url: string;\n readyState = EventSourceStub.CONNECTING;\n\n constructor(url: string | URL) {\n super();\n this.url = url.toString();\n setTimeout(() => {\n this.readyState = EventSourceStub.OPEN;\n this.dispatchEvent(new Event(\"open\"));\n }, 0);\n }\n\n close() {\n this.readyState = EventSourceStub.CLOSED;\n }\n}\n","// Copyright (c) 2025 Falko Schumann. All rights reserved. MIT license.\n\nimport { OutputTracker } from \"./output_tracker\";\nimport type { MessageClient } from \"./message_client\";\n\nexport const HEARTBEAT_TYPE = \"heartbeat\";\n\nconst MESSAGE_SENT_EVENT = \"message-sent\";\n\n/**\n * Options for the WebSocket client.\n */\nexport interface WebSocketOptions {\n /**\n * The heartbeat interval in milliseconds. A value <= 0 disables the\n * heartbeat.\n */\n heartbeat?: number;\n\n /**\n * The time in milliseconds to wait before retrying a connection after an\n * error. A value <= 0 disables automatic retries.\n */\n retry?: number;\n}\n\n/**\n * A client for the WebSocket protocol.\n */\nexport class WebSocketClient extends EventTarget implements MessageClient {\n /**\n * Create a WebSocket client.\n *\n * @param options The options for the WebSocket client.\n * @return A new WebSocket client.\n */\n static create({\n heartbeat = 30000,\n retry = 1000,\n }: WebSocketOptions = {}): WebSocketClient {\n return new WebSocketClient(heartbeat, retry, WebSocket);\n }\n\n /**\n * Create a nulled WebSocket client.\n *\n * @param options The options for the WebSocket client.\n * @return A new nulled WebSocket client.\n */\n static createNull({ heartbeat = 0, retry = 0 }: WebSocketOptions = {}) {\n return new WebSocketClient(\n heartbeat,\n retry,\n WebSocketStub as unknown as typeof WebSocket,\n );\n }\n\n readonly #heartbeat: number;\n readonly #retry: number;\n readonly #webSocketConstructor: typeof WebSocket;\n\n #webSocket?: WebSocket;\n #heartbeatId?: ReturnType<typeof setTimeout>;\n #retryId?: ReturnType<typeof setTimeout>;\n\n private constructor(\n heartbeat: number,\n retry: number,\n webSocketConstructor: typeof WebSocket,\n ) {\n super();\n this.#heartbeat = heartbeat;\n this.#retry = retry;\n this.#webSocketConstructor = webSocketConstructor;\n }\n\n get isConnected(): boolean {\n return this.#webSocket?.readyState === WebSocket.OPEN;\n }\n\n get url(): string | undefined {\n return this.#webSocket?.url;\n }\n\n async connect(url: string | URL): Promise<void> {\n await new Promise<void>((resolve, reject) => {\n this.#stopRetry();\n\n if (this.isConnected) {\n reject(new Error(\"Already connected.\"));\n return;\n }\n\n try {\n this.#webSocket = new this.#webSocketConstructor(url);\n this.#webSocket.addEventListener(\"open\", (e) => {\n this.#handleOpen(e);\n resolve();\n });\n this.#webSocket.addEventListener(\"message\", (e) =>\n this.#handleMessage(e),\n );\n this.#webSocket.addEventListener(\"close\", (e) => this.#handleClose(e));\n this.#webSocket.addEventListener(\"error\", (e) => this.#handleError(e));\n } catch (error) {\n reject(error);\n }\n });\n }\n\n async send(\n message: string | ArrayBuffer | Blob | ArrayBufferView,\n ): Promise<void> {\n if (!this.isConnected) {\n throw new Error(\"Not connected.\");\n }\n\n this.#webSocket!.send(message);\n this.dispatchEvent(\n new CustomEvent(MESSAGE_SENT_EVENT, { detail: message }),\n );\n await Promise.resolve();\n }\n\n /**\n * Return a tracker for messages sent.\n *\n * @return A new output tracker.\n */\n trackMessageSent(): OutputTracker<string> {\n return OutputTracker.create(this, MESSAGE_SENT_EVENT);\n }\n\n /**\n * Close the connection.\n *\n * If a code is provided, also a reason should be provided.\n *\n * @param code An optional code.\n * @param reason An optional reason.\n */\n async close(code?: number, reason?: string): Promise<void> {\n await new Promise<void>((resolve) => {\n this.#stopRetry();\n\n if (!this.isConnected) {\n resolve();\n return;\n }\n\n this.#webSocket!.addEventListener(\"close\", () => resolve());\n this.#webSocket!.close(code, reason);\n });\n }\n\n /**\n * Simulate a message event from the server.\n *\n * @param message The message to receive.\n */\n simulateMessage(\n message: string | number | boolean | object | null | Blob | ArrayBuffer,\n ) {\n if (\n typeof message !== \"string\" &&\n !(message instanceof Blob) &&\n !(message instanceof ArrayBuffer)\n ) {\n message = JSON.stringify(message);\n }\n this.#handleMessage(new MessageEvent(\"message\", { data: message }));\n }\n\n /**\n * Simulate a heartbeat.\n */\n simulateHeartbeat() {\n this.#sendHeartbeat();\n }\n\n /**\n * Simulate a close event.\n *\n * @param code An optional code.\n * @param reason An optional reason.\n */\n simulateClose(code?: number, reason?: string) {\n this.#handleClose(new CloseEvent(\"close\", { code, reason }));\n }\n\n /**\n * Simulate an error event.\n */\n simulateError() {\n this.#webSocket?.close();\n this.#handleError(new Event(\"error\"));\n }\n\n #handleOpen(event: Event) {\n this.dispatchEvent(new Event(event.type, event));\n this.#startHeartbeat();\n }\n\n #handleMessage(event: MessageEvent) {\n this.dispatchEvent(\n new MessageEvent(event.type, event as unknown as MessageEventInit),\n );\n }\n\n #handleClose(event: CloseEvent) {\n this.#stopHeartbeat();\n this.dispatchEvent(new CloseEvent(event.type, event));\n }\n\n #handleError(event: Event) {\n this.dispatchEvent(new Event(event.type, event));\n this.#startRetry();\n }\n\n #startRetry() {\n if (this.#retry <= 0) {\n return;\n }\n this.#retryId = setInterval(\n () => this.connect(this.#webSocket!.url),\n this.#retry,\n );\n }\n\n #stopRetry() {\n clearInterval(this.#retryId);\n this.#retryId = undefined;\n }\n\n #startHeartbeat() {\n if (this.#heartbeat <= 0) {\n return;\n }\n\n this.#heartbeatId = setInterval(\n () => this.#sendHeartbeat(),\n this.#heartbeat,\n );\n }\n\n #stopHeartbeat() {\n clearInterval(this.#heartbeatId);\n this.#heartbeatId = undefined;\n }\n\n #sendHeartbeat() {\n if (this.#heartbeatId == null) {\n return;\n }\n\n void this.send(HEARTBEAT_TYPE);\n }\n}\n\nclass WebSocketStub extends EventTarget {\n url: string;\n readyState: number = WebSocket.CONNECTING;\n\n constructor(url: string | URL) {\n super();\n this.url = url.toString();\n setTimeout(() => {\n this.readyState = WebSocket.OPEN;\n this.dispatchEvent(new Event(\"open\"));\n }, 0);\n }\n\n send() {}\n\n close() {\n this.readyState = WebSocket.CLOSED;\n this.dispatchEvent(new Event(\"close\"));\n }\n}\n"],"names":["Clock","date","clock","offsetMillis","#date","EventTracker","eventTarget","event","#eventTarget","#event","#events","#tracker","result","count","resolve","checkEvents","Success","Failure","errorMessage","ConfigurableResponses","responses","name","responseObject","translatedEntries","key","value","translatedName","#description","#responses","response","OutputTracker","#data","MESSAGE_EVENT","ConsoleLog","ConsoleStub","#console","console","data","#applyName","level","normalize","levels","currentLevelIndex","_data","createFetchStub","configurableResponses","ResponseStub","#status","#statusText","#body","status","statusText","body","json","SseClient","EventSourceStub","#eventSourceConstructor","#eventSource","eventSourceConstructor","url","eventName","otherEvents","reject","e","#handleOpen","#handleMessage","otherEvent","#handleError","error","_message","_type","message","lastEventId","HEARTBEAT_TYPE","MESSAGE_SENT_EVENT","WebSocketClient","heartbeat","retry","WebSocketStub","#heartbeat","#retry","#webSocketConstructor","#webSocket","#heartbeatId","#retryId","webSocketConstructor","#stopRetry","#handleClose","code","reason","#sendHeartbeat","#startHeartbeat","#stopHeartbeat","#startRetry"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- (function(n,a){typeof exports=="object"&&typeof module<"u"?a(exports):typeof define=="function"&&define.amd?define(["exports"],a):(n=typeof globalThis<"u"?globalThis:n||self,a(n.Shared={}))})(this,(function(n){"use strict";class a{static system(){return new a}static fixed(t){return new a(new Date(t))}static offset(t,e){return new a(new Date(t.millis()+e))}#t;constructor(t){this.#t=t}date(){return this.#t?new Date(this.#t):new Date}millis(){return this.date().getTime()}}class f{static create(t,...e){return new f(t,e)}#t;#e;#s;#r;constructor(t,e){this.#t=t,this.#e=e,this.#s=[],this.#r=s=>this.#s.push(s),this.#e.forEach(s=>this.#t.addEventListener(s,this.#r))}get events(){return this.#s}clear(){const t=[...this.#s];return this.#s.length=0,t}stop(){this.#e.forEach(t=>this.#t.removeEventListener(t,this.#r))}async waitFor(t=1){return new Promise(e=>{const s=()=>{this.#s.length>=t&&(this.#e.forEach(r=>this.#t.removeEventListener(r,s)),e(this.events))};this.#e.forEach(r=>this.#t.addEventListener(r,s)),s()})}}class b{isSuccess=!0;result;constructor(t){this.result=t}}class m{isSuccess=!1;errorMessage;constructor(t){this.errorMessage=t}}class l{static create(t,e){return new l(t,e)}static mapObject(t,e){const r=Object.entries(t).map(([c,i])=>{const w=e===void 0?void 0:`${e}: ${c}`;return[c,l.create(i,w)]});return Object.fromEntries(r)}#t;#e;constructor(t,e){this.#t=e==null?"":` in ${e}`,this.#e=Array.isArray(t)?[...t]:t}next(){const t=Array.isArray(this.#e)?this.#e.shift():this.#e;if(t===void 0)throw new Error(`No more responses configured${this.#t}.`);return t}}class u{static create(t,e){return new u(t,e)}#t;#e;#s;#r;constructor(t,e){this.#t=t,this.#e=e,this.#s=[],this.#r=s=>this.#s.push(s.detail),this.#t.addEventListener(this.#e,this.#r)}get data(){return this.#s}clear(){const t=[...this.#s];return this.#s.length=0,t}stop(){this.#t.removeEventListener(this.#e,this.#r)}}const o="message";class E extends EventTarget{static create(){return new E(globalThis.console)}static createNull(){return new E(new S)}level="info";#t;constructor(t){super(),this.#t=t}log(...t){this.isLoggable("log")&&(this.#t.log(...t),this.dispatchEvent(new CustomEvent(o,{detail:{level:"log",message:t}})))}error(...t){this.isLoggable("error")&&(this.#t.error(...t),this.dispatchEvent(new CustomEvent(o,{detail:{level:"error",message:t}})))}warn(...t){this.isLoggable("warn")&&(this.#t.warn(...t),this.dispatchEvent(new CustomEvent(o,{detail:{level:"warn",message:t}})))}info(...t){this.isLoggable("info")&&(this.#t.info(...t),this.dispatchEvent(new CustomEvent(o,{detail:{level:"info",message:t}})))}debug(...t){this.isLoggable("debug")&&(this.#t.debug(...t),this.dispatchEvent(new CustomEvent(o,{detail:{level:"debug",message:t}})))}trace(...t){this.isLoggable("trace")&&(this.#t.trace(...t),this.dispatchEvent(new CustomEvent(o,{detail:{level:"trace",message:t}})))}trackMessages(){return new u(this,o)}isLoggable(t){const e=i=>i==="log"?"info":i,s=["off","error","warn","info","debug","trace"],r=s.indexOf(e(this.level));return s.indexOf(e(t))<=r}}class S{log(...t){}error(...t){}warn(...t){}info(...t){}debug(...t){}trace(...t){}}function T(h){const t=l.create(h);return async function(){const e=t.next();if(e instanceof Error)throw e;return new C(e)}}class C{#t;#e;#s;constructor({status:t,statusText:e,body:s=null}){this.#t=t,this.#e=e,this.#s=s}get ok(){return this.status>=200&&this.status<300}get status(){return this.#t}get statusText(){return this.#e}async blob(){if(this.#s==null)return null;if(this.#s instanceof Blob)return this.#s;throw new TypeError("Body is not a Blob.")}async json(){const t=typeof this.#s=="string"?this.#s:JSON.stringify(this.#s);return Promise.resolve(JSON.parse(t))}async text(){return this.#s==null?"":String(this.#s)}}class v extends EventTarget{static create(){return new v(EventSource)}static createNull(){return new v(d)}#t;#e;constructor(t){super(),this.#t=t}get isConnected(){return this.#e?.readyState===this.#t.OPEN}get url(){return this.#e?.url}async connect(t,e="message",...s){await new Promise((r,c)=>{if(this.isConnected){c(new Error("Already connected."));return}try{this.#e=new this.#t(t),this.#e.addEventListener("open",i=>{this.#s(i),r()}),this.#e.addEventListener(e,i=>this.#r(i));for(const i of s)this.#e.addEventListener(i,w=>this.#r(w));this.#e.addEventListener("error",i=>this.#n(i))}catch(i){c(i)}})}send(t,e){throw new Error("Method not implemented.")}async close(){await new Promise((t,e)=>{if(!this.isConnected){t();return}try{this.#e.close(),t()}catch(s){e(s)}})}simulateMessage(t,e="message",s){typeof t!="string"&&(t=JSON.stringify(t)),this.#r(new MessageEvent(e,{data:t,lastEventId:s}))}simulateError(){this.#n(new Event("error"))}#s(t){this.dispatchEvent(new Event(t.type,t))}#r(t){this.dispatchEvent(new MessageEvent(t.type,t))}#n(t){this.dispatchEvent(new Event(t.type,t))}}class d extends EventTarget{static CONNECTING=0;static OPEN=1;static CLOSED=2;url;readyState=d.CONNECTING;constructor(t){super(),this.url=t.toString(),setTimeout(()=>{this.readyState=d.OPEN,this.dispatchEvent(new Event("open"))},0)}close(){this.readyState=d.CLOSED}}const p="heartbeat",y="message-sent";class g extends EventTarget{static create({heartbeat:t=3e4,retry:e=1e3}={}){return new g(t,e,WebSocket)}static createNull({heartbeat:t=0,retry:e=0}={}){return new g(t,e,L)}#t;#e;#s;#r;#n;#i;constructor(t,e,s){super(),this.#t=t,this.#e=e,this.#s=s}get isConnected(){return this.#r?.readyState===WebSocket.OPEN}get url(){return this.#r?.url}async connect(t){await new Promise((e,s)=>{if(this.#c(),this.isConnected){s(new Error("Already connected."));return}try{this.#r=new this.#s(t),this.#r.addEventListener("open",r=>{this.#u(r),e()}),this.#r.addEventListener("message",r=>this.#a(r)),this.#r.addEventListener("close",r=>this.#o(r)),this.#r.addEventListener("error",r=>this.#h(r))}catch(r){s(r)}})}async send(t){if(!this.isConnected)throw new Error("Not connected.");this.#r.send(t),this.dispatchEvent(new CustomEvent(y,{detail:t})),await Promise.resolve()}trackMessageSent(){return u.create(this,y)}async close(t,e){await new Promise(s=>{if(this.#c(),!this.isConnected){s();return}this.#r.addEventListener("close",()=>s()),this.#r.close(t,e)})}simulateMessage(t){typeof t!="string"&&!(t instanceof Blob)&&!(t instanceof ArrayBuffer)&&(t=JSON.stringify(t)),this.#a(new MessageEvent("message",{data:t}))}simulateHeartbeat(){this.#l()}simulateClose(t,e){this.#o(new CloseEvent("close",{code:t,reason:e}))}simulateError(){this.#r?.close(),this.#h(new Event("error"))}#u(t){this.dispatchEvent(new Event(t.type,t)),this.#E()}#a(t){this.dispatchEvent(new MessageEvent(t.type,t))}#o(t){this.#v(),this.dispatchEvent(new CloseEvent(t.type,t))}#h(t){this.dispatchEvent(new Event(t.type,t)),this.#d()}#d(){this.#e<=0||(this.#i=setInterval(()=>this.connect(this.#r.url),this.#e))}#c(){clearInterval(this.#i),this.#i=void 0}#E(){this.#t<=0||(this.#n=setInterval(()=>this.#l(),this.#t))}#v(){clearInterval(this.#n),this.#n=void 0}#l(){this.#n!=null&&this.send(p)}}class L extends EventTarget{url;readyState=WebSocket.CONNECTING;constructor(t){super(),this.url=t.toString(),setTimeout(()=>{this.readyState=WebSocket.OPEN,this.dispatchEvent(new Event("open"))},0)}send(){}close(){this.readyState=WebSocket.CLOSED,this.dispatchEvent(new Event("close"))}}n.Clock=a,n.ConfigurableResponses=l,n.ConsoleLog=E,n.EventTracker=f,n.Failure=m,n.HEARTBEAT_TYPE=p,n.OutputTracker=u,n.SseClient=v,n.Success=b,n.WebSocketClient=g,n.createFetchStub=T,Object.defineProperty(n,Symbol.toStringTag,{value:"Module"})}));
1
+ (function(n,a){typeof exports=="object"&&typeof module<"u"?a(exports):typeof define=="function"&&define.amd?define(["exports"],a):(n=typeof globalThis<"u"?globalThis:n||self,a(n.Shared={}))})(this,(function(n){"use strict";class a{static system(){return new a}static fixed(t){return new a(new Date(t))}static offset(t,e){return new a(new Date(t.millis()+e))}#e;constructor(t){this.#e=t}date(){return this.#e?new Date(this.#e):new Date}millis(){return this.date().getTime()}}class f{static create(t,...e){return new f(t,e)}#e;#t;#s;#r;constructor(t,e){this.#e=t,this.#t=e,this.#s=[],this.#r=s=>this.#s.push(s),this.#t.forEach(s=>this.#e.addEventListener(s,this.#r))}get events(){return this.#s}clear(){const t=[...this.#s];return this.#s.length=0,t}stop(){this.#t.forEach(t=>this.#e.removeEventListener(t,this.#r))}async waitFor(t=1){return new Promise(e=>{const s=()=>{this.#s.length>=t&&(this.#t.forEach(r=>this.#e.removeEventListener(r,s)),e(this.events))};this.#t.forEach(r=>this.#e.addEventListener(r,s)),s()})}}class m{isSuccess=!0;result;constructor(t){this.result=t}}class b{isSuccess=!1;errorMessage;constructor(t){this.errorMessage=t}}class l{static create(t,e){return new l(t,e)}static mapObject(t,e){const r=Object.entries(t).map(([c,i])=>{const w=e===void 0?void 0:`${e}: ${c}`;return[c,l.create(i,w)]});return Object.fromEntries(r)}#e;#t;constructor(t,e){this.#e=e==null?"":` in ${e}`,this.#t=Array.isArray(t)?[...t]:t}next(){const t=Array.isArray(this.#t)?this.#t.shift():this.#t;if(t===void 0)throw new Error(`No more responses configured${this.#e}.`);return t}}class u{static create(t,e){return new u(t,e)}#e;#t;#s;#r;constructor(t,e){this.#e=t,this.#t=e,this.#s=[],this.#r=s=>this.#s.push(s.detail),this.#e.addEventListener(this.#t,this.#r)}get data(){return this.#s}clear(){const t=[...this.#s];return this.#s.length=0,t}stop(){this.#e.removeEventListener(this.#t,this.#r)}}const h="message";class E extends EventTarget{static create({name:t}={}){return new E(globalThis.console,t)}static createNull({name:t}={}){return new E(new S,t)}name;level="info";#e;constructor(t,e){super(),this.name=e,this.#e=t}log(...t){this.isLoggable("log")&&(t=this.#t(t),this.#e.log(...t),this.dispatchEvent(new CustomEvent(h,{detail:{level:"log",message:t}})))}error(...t){this.isLoggable("error")&&(t=this.#t(t),this.#e.error(...t),this.dispatchEvent(new CustomEvent(h,{detail:{level:"error",message:t}})))}warn(...t){this.isLoggable("warn")&&(t=this.#t(t),this.#e.warn(...t),this.dispatchEvent(new CustomEvent(h,{detail:{level:"warn",message:t}})))}info(...t){this.isLoggable("info")&&(t=this.#t(t),this.#e.info(...t),this.dispatchEvent(new CustomEvent(h,{detail:{level:"info",message:t}})))}debug(...t){this.isLoggable("debug")&&(t=this.#t(t),this.#e.debug(...t),this.dispatchEvent(new CustomEvent(h,{detail:{level:"debug",message:t}})))}trace(...t){this.isLoggable("trace")&&(t=this.#t(t),this.#e.trace(...t),this.dispatchEvent(new CustomEvent(h,{detail:{level:"trace",message:t}})))}trackMessages(){return new u(this,h)}isLoggable(t){const e=i=>i==="log"?"info":i,s=["off","error","warn","info","debug","trace"],r=s.indexOf(e(this.level));return s.indexOf(e(t))<=r}#t(t){return this.name==null?t:[`${this.name}`,...t]}}class S{log(...t){}error(...t){}warn(...t){}info(...t){}debug(...t){}trace(...t){}}function T(o){const t=l.create(o);return async function(){const e=t.next();if(e instanceof Error)throw e;return new C(e)}}class C{#e;#t;#s;constructor({status:t,statusText:e,body:s=null}){this.#e=t,this.#t=e,this.#s=s}get ok(){return this.status>=200&&this.status<300}get status(){return this.#e}get statusText(){return this.#t}async blob(){if(this.#s==null)return null;if(this.#s instanceof Blob)return this.#s;throw new TypeError("Body is not a Blob.")}async json(){const t=typeof this.#s=="string"?this.#s:JSON.stringify(this.#s);return Promise.resolve(JSON.parse(t))}async text(){return this.#s==null?"":String(this.#s)}}class v extends EventTarget{static create(){return new v(EventSource)}static createNull(){return new v(d)}#e;#t;constructor(t){super(),this.#e=t}get isConnected(){return this.#t?.readyState===this.#e.OPEN}get url(){return this.#t?.url}async connect(t,e="message",...s){await new Promise((r,c)=>{if(this.isConnected){c(new Error("Already connected."));return}try{this.#t=new this.#e(t),this.#t.addEventListener("open",i=>{this.#s(i),r()}),this.#t.addEventListener(e,i=>this.#r(i));for(const i of s)this.#t.addEventListener(i,w=>this.#r(w));this.#t.addEventListener("error",i=>this.#n(i))}catch(i){c(i)}})}send(t,e){throw new Error("Method not implemented.")}async close(){await new Promise((t,e)=>{if(!this.isConnected){t();return}try{this.#t.close(),t()}catch(s){e(s)}})}simulateMessage(t,e="message",s){typeof t!="string"&&(t=JSON.stringify(t)),this.#r(new MessageEvent(e,{data:t,lastEventId:s}))}simulateError(){this.#n(new Event("error"))}#s(t){this.dispatchEvent(new Event(t.type,t))}#r(t){this.dispatchEvent(new MessageEvent(t.type,t))}#n(t){this.dispatchEvent(new Event(t.type,t))}}class d extends EventTarget{static CONNECTING=0;static OPEN=1;static CLOSED=2;url;readyState=d.CONNECTING;constructor(t){super(),this.url=t.toString(),setTimeout(()=>{this.readyState=d.OPEN,this.dispatchEvent(new Event("open"))},0)}close(){this.readyState=d.CLOSED}}const p="heartbeat",y="message-sent";class g extends EventTarget{static create({heartbeat:t=3e4,retry:e=1e3}={}){return new g(t,e,WebSocket)}static createNull({heartbeat:t=0,retry:e=0}={}){return new g(t,e,L)}#e;#t;#s;#r;#n;#i;constructor(t,e,s){super(),this.#e=t,this.#t=e,this.#s=s}get isConnected(){return this.#r?.readyState===WebSocket.OPEN}get url(){return this.#r?.url}async connect(t){await new Promise((e,s)=>{if(this.#c(),this.isConnected){s(new Error("Already connected."));return}try{this.#r=new this.#s(t),this.#r.addEventListener("open",r=>{this.#u(r),e()}),this.#r.addEventListener("message",r=>this.#a(r)),this.#r.addEventListener("close",r=>this.#h(r)),this.#r.addEventListener("error",r=>this.#o(r))}catch(r){s(r)}})}async send(t){if(!this.isConnected)throw new Error("Not connected.");this.#r.send(t),this.dispatchEvent(new CustomEvent(y,{detail:t})),await Promise.resolve()}trackMessageSent(){return u.create(this,y)}async close(t,e){await new Promise(s=>{if(this.#c(),!this.isConnected){s();return}this.#r.addEventListener("close",()=>s()),this.#r.close(t,e)})}simulateMessage(t){typeof t!="string"&&!(t instanceof Blob)&&!(t instanceof ArrayBuffer)&&(t=JSON.stringify(t)),this.#a(new MessageEvent("message",{data:t}))}simulateHeartbeat(){this.#l()}simulateClose(t,e){this.#h(new CloseEvent("close",{code:t,reason:e}))}simulateError(){this.#r?.close(),this.#o(new Event("error"))}#u(t){this.dispatchEvent(new Event(t.type,t)),this.#E()}#a(t){this.dispatchEvent(new MessageEvent(t.type,t))}#h(t){this.#v(),this.dispatchEvent(new CloseEvent(t.type,t))}#o(t){this.dispatchEvent(new Event(t.type,t)),this.#d()}#d(){this.#t<=0||(this.#i=setInterval(()=>this.connect(this.#r.url),this.#t))}#c(){clearInterval(this.#i),this.#i=void 0}#E(){this.#e<=0||(this.#n=setInterval(()=>this.#l(),this.#e))}#v(){clearInterval(this.#n),this.#n=void 0}#l(){this.#n!=null&&this.send(p)}}class L extends EventTarget{url;readyState=WebSocket.CONNECTING;constructor(t){super(),this.url=t.toString(),setTimeout(()=>{this.readyState=WebSocket.OPEN,this.dispatchEvent(new Event("open"))},0)}send(){}close(){this.readyState=WebSocket.CLOSED,this.dispatchEvent(new Event("close"))}}n.Clock=a,n.ConfigurableResponses=l,n.ConsoleLog=E,n.EventTracker=f,n.Failure=b,n.HEARTBEAT_TYPE=p,n.OutputTracker=u,n.SseClient=v,n.Success=m,n.WebSocketClient=g,n.createFetchStub=T,Object.defineProperty(n,Symbol.toStringTag,{value:"Module"})}));
2
2
  //# sourceMappingURL=shared.umd.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"shared.umd.cjs","sources":["../src/common/clock.ts","../src/common/event_tracker.ts","../src/domain/messages.ts","../src/infrastructure/configurable_responses.ts","../src/infrastructure/output_tracker.ts","../src/infrastructure/console_log.ts","../src/infrastructure/fetch_stub.ts","../src/infrastructure/sse_client.ts","../src/infrastructure/web_socket_client.ts"],"sourcesContent":["// Copyright (c) 2025 Falko Schumann. All rights reserved. MIT license.\n\n/**\n * A clock provides access to the current timestamp.\n */\nexport class Clock {\n /**\n * Create a clock using system the clock.\n *\n * @return A clock that uses the system clock.\n */\n static system(): Clock {\n return new Clock();\n }\n\n /**\n * Create a clock using a fixed date.\n *\n * @param date The fixed date of the clock.\n * @return A clock that always returns a fixed date.\n */\n static fixed(date: Date | string | number): Clock {\n return new Clock(new Date(date));\n }\n\n /**\n * Create a clock that returns a fixed offset from the given clock.\n *\n * @param clock The clock to offset from.\n * @param offsetMillis The offset in milliseconds.\n * @return A clock that returns a fixed offset from the given clock.\n */\n static offset(clock: Clock, offsetMillis: number): Clock {\n return new Clock(new Date(clock.millis() + offsetMillis));\n }\n\n readonly #date?: Date;\n\n private constructor(date?: Date) {\n this.#date = date;\n }\n\n /**\n * Return the current timestamp of the clock.\n *\n * @return The current timestamp.\n */\n date(): Date {\n return this.#date ? new Date(this.#date) : new Date();\n }\n\n /**\n * Return the current timestamp of the clock in milliseconds.\n *\n * @return The current timestamp in milliseconds.\n */\n millis(): number {\n return this.date().getTime();\n }\n}\n","// Copyright (c) 2025 Falko Schumann. All rights reserved. MIT license.\n\n// TODO monitor multiple events\n\n/**\n * Track events from an event target.\n *\n * Wait asynchronously for events. Useful in test code.\n */\nexport class EventTracker<T extends Event> {\n /**\n * Create a tracker for a specific event of an event target.\n *\n * @param eventTarget The target to track.\n * @param event The event name to track.\n */\n static create<T extends Event>(eventTarget: EventTarget, ...event: string[]) {\n return new EventTracker<T>(eventTarget, event);\n }\n\n readonly #eventTarget;\n readonly #event;\n readonly #events: T[];\n readonly #tracker;\n\n /**\n * Create a tracker for a specific event of an event target.\n *\n * @param eventTarget The target to track.\n * @param event The event name to track.\n */\n constructor(eventTarget: EventTarget, event: string[]) {\n this.#eventTarget = eventTarget;\n this.#event = event;\n this.#events = [];\n this.#tracker = (event: Event) => this.#events.push(event as T);\n\n this.#event.forEach((event) =>\n this.#eventTarget.addEventListener(event, this.#tracker),\n );\n }\n\n /**\n * Return the tracked events.\n *\n * @return The tracked events.\n */\n get events(): T[] {\n return this.#events;\n }\n\n /**\n * Clear the tracked events and return the cleared events.\n *\n * @return The cleared events.\n */\n clear(): T[] {\n const result = [...this.#events];\n this.#events.length = 0;\n return result;\n }\n\n /**\n * Stop tracking.\n */\n stop() {\n this.#event.forEach((event) =>\n this.#eventTarget.removeEventListener(event, this.#tracker),\n );\n }\n\n /**\n * Wait asynchronously for a number of events.\n *\n * @param count number of events, default 1.\n */\n async waitFor(count = 1) {\n return new Promise<T[]>((resolve) => {\n const checkEvents = () => {\n if (this.#events.length >= count) {\n this.#event.forEach((event) =>\n this.#eventTarget.removeEventListener(event, checkEvents),\n );\n resolve(this.events);\n }\n };\n\n this.#event.forEach((event) =>\n this.#eventTarget.addEventListener(event, checkEvents),\n );\n checkEvents();\n });\n }\n}\n","// Copyright (c) 2025 Falko Schumann. All rights reserved. MIT license.\n\n/**\n * Provides CQNS features.\n *\n * The Command Query Notification Separation principle is a software design\n * principle that separates the concerns of commands, queries, and\n * notifications.\n *\n * Message hierarchy:\n *\n * - Message\n * - Incoming / outgoing\n * - Request (outgoing) -> response (incoming)\n * - Command -> command status\n * - Query -> query result\n * - Notification\n * - Incoming: notification -> commands\n * - Outgoing\n * - Event (internal)\n *\n * @see https://ralfw.de/command-query-notification-separation-cqns/\n * @module\n */\n\n/**\n * The status returned by a command handler.\n */\nexport type CommandStatus = Success | Failure;\n\n/**\n * A successful status.\n */\nexport class Success<T = unknown> {\n readonly isSuccess = true;\n readonly result?: T;\n\n constructor(result?: T) {\n this.result = result;\n }\n}\n\n/**\n * A failed status.\n */\nexport class Failure<T = string> {\n readonly isSuccess = false;\n readonly errorMessage: T;\n\n /**\n * Creates a failed status.\n *\n * @param errorMessage\n */\n constructor(errorMessage: T) {\n this.errorMessage = errorMessage;\n }\n}\n","// Copyright (c) 2025 Falko Schumann. All rights reserved. MIT license.\n// Copyright 2023 Titanium I.T. LLC. MIT License.\n\n/**\n * Handle returning pre-configured responses.\n *\n * This is one of the nullability patterns from James Shore's article on\n * [testing without mocks](https://www.jamesshore.com/v2/projects/nullables/testing-without-mocks#configurable-responses).\n *\n * Example usage for stubbing `fetch` function:\n *\n * ```javascript\n * function createFetchStub(responses) {\n * const configurableResponses = ConfigurableResponses.create(responses);\n * return async function () {\n * const response = configurableResponses.next();\n * return {\n * status: response.status,\n * json: async () => response.body,\n * };\n * };\n * }\n * ```\n */\nexport class ConfigurableResponses<T = unknown> {\n /**\n * Create a list of responses (by providing an array), or a single repeating\n * response (by providing any other type). 'Name' is optional and used in\n * error messages.\n *\n * @param responses A single response or an array of responses.\n * @param name An optional name for the responses.\n */\n static create<T>(responses?: T | T[], name?: string) {\n return new ConfigurableResponses<T>(responses, name);\n }\n\n /**\n * Convert all properties in an object into ConfigurableResponse instances.\n * For example, { a: 1 } becomes { a: ConfigurableResponses.create(1) }.\n * 'Name' is optional and used in error messages.\n *\n * @param responseObject An object with single response or an array of responses.\n * @param name An optional name for the responses.\n */\n static mapObject<T extends Record<string, unknown>>(\n responseObject: T,\n name?: string,\n ) {\n const entries = Object.entries(responseObject);\n const translatedEntries = entries.map(([key, value]) => {\n const translatedName = name === undefined ? undefined : `${name}: ${key}`;\n return [key, ConfigurableResponses.create(value, translatedName)];\n });\n return Object.fromEntries(translatedEntries);\n }\n\n readonly #description;\n readonly #responses;\n\n /**\n * Create a list of responses (by providing an array), or a single repeating\n * response (by providing any other type). 'Name' is optional and used in\n * error messages.\n *\n * @param responses A single response or an array of responses.\n * @param name An optional name for the responses.\n */\n constructor(responses?: T | T[], name?: string) {\n this.#description = name == null ? \"\" : ` in ${name}`;\n this.#responses = Array.isArray(responses) ? [...responses] : responses;\n }\n\n /**\n * Get the next configured response. Throws an error when configured with a list\n * of responses and no more responses remain.\n *\n * @return The next response.\n */\n next(): T {\n const response = Array.isArray(this.#responses)\n ? this.#responses.shift()\n : this.#responses;\n if (response === undefined) {\n throw new Error(`No more responses configured${this.#description}.`);\n }\n\n return response;\n }\n}\n","// Copyright (c) 2025 Falko Schumann. All rights reserved. MIT license.\n// Copyright 2020-2022 Titanium I.T. LLC. MIT License.\n\n/**\n * Track output events.\n *\n * This is one of the nullability patterns from James Shore's article on\n * [testing without mocks](https://www.jamesshore.com/v2/projects/nullables/testing-without-mocks#output-tracking).\n *\n * Example implementation of an event store:\n *\n * ```javascript\n * async record(event) {\n * // ...\n * this.dispatchEvent(new CustomEvent(\"eventRecorded\", { detail: event }));\n * }\n *\n * trackEventsRecorded() {\n * return new OutputTracker(this, \"eventRecorded\");\n * }\n * ```\n *\n * Example usage:\n *\n * ```javascript\n * const eventsRecorded = eventStore.trackEventsRecorded();\n * // ...\n * const data = eventsRecorded.data(); // [event1, event2, ...]\n * ```\n */\nexport class OutputTracker<T = unknown> {\n /**\n * Create a tracker for a specific event of an event target.\n *\n * @param eventTarget The target to track.\n * @param event The event name to track.\n */\n static create<T>(eventTarget: EventTarget, event: string) {\n return new OutputTracker<T>(eventTarget, event);\n }\n\n readonly #eventTarget;\n readonly #event;\n readonly #data: T[];\n readonly #tracker;\n\n /**\n * Create a tracker for a specific event of an event target.\n *\n * @param eventTarget The target to track.\n * @param event The event name to track.\n */\n constructor(eventTarget: EventTarget, event: string) {\n this.#eventTarget = eventTarget;\n this.#event = event;\n this.#data = [];\n this.#tracker = (event: Event) =>\n this.#data.push((event as CustomEvent<T>).detail);\n\n this.#eventTarget.addEventListener(this.#event, this.#tracker);\n }\n\n /**\n * Return the tracked data.\n *\n * @return The tracked data.\n */\n get data(): T[] {\n return this.#data;\n }\n\n /**\n * Clear the tracked data and return the cleared data.\n *\n * @return The cleared data.\n */\n clear(): T[] {\n const result = [...this.#data];\n this.#data.length = 0;\n return result;\n }\n\n /**\n * Stop tracking.\n */\n stop() {\n this.#eventTarget.removeEventListener(this.#event, this.#tracker);\n }\n}\n","// Copyright (c) 2026 Falko Schumann. All rights reserved. MIT license.\n\nimport type { Log, LogLevel } from \"../common/log\";\nimport { OutputTracker } from \"./output_tracker\";\n\nconst MESSAGE_EVENT = \"message\";\n\nexport interface ConsoleMessage {\n level: LogLevel;\n message: unknown[];\n}\n\n/**\n * Wraps the console interface and allow setting the log level.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/API/Console_API\n */\nexport class ConsoleLog extends EventTarget implements Log {\n static create() {\n return new ConsoleLog(globalThis.console);\n }\n\n static createNull() {\n return new ConsoleLog(new ConsoleStub());\n }\n\n level: LogLevel = \"info\";\n\n #console;\n\n private constructor(console: Log) {\n super();\n this.#console = console;\n }\n\n log(...data: unknown[]) {\n if (!this.isLoggable(\"log\")) {\n return;\n }\n\n this.#console.log(...data);\n this.dispatchEvent(\n new CustomEvent(MESSAGE_EVENT, {\n detail: { level: \"log\", message: data },\n }),\n );\n }\n\n error(...data: unknown[]) {\n if (!this.isLoggable(\"error\")) {\n return;\n }\n\n this.#console.error(...data);\n this.dispatchEvent(\n new CustomEvent(MESSAGE_EVENT, {\n detail: { level: \"error\", message: data },\n }),\n );\n }\n\n warn(...data: unknown[]) {\n if (!this.isLoggable(\"warn\")) {\n return;\n }\n\n this.#console.warn(...data);\n this.dispatchEvent(\n new CustomEvent(MESSAGE_EVENT, {\n detail: { level: \"warn\", message: data },\n }),\n );\n }\n\n info(...data: unknown[]) {\n if (!this.isLoggable(\"info\")) {\n return;\n }\n\n this.#console.info(...data);\n this.dispatchEvent(\n new CustomEvent(MESSAGE_EVENT, {\n detail: { level: \"info\", message: data },\n }),\n );\n }\n\n debug(...data: unknown[]) {\n if (!this.isLoggable(\"debug\")) {\n return;\n }\n\n this.#console.debug(...data);\n this.dispatchEvent(\n new CustomEvent(MESSAGE_EVENT, {\n detail: { level: \"debug\", message: data },\n }),\n );\n }\n\n trace(...data: unknown[]) {\n if (!this.isLoggable(\"trace\")) {\n return;\n }\n\n this.#console.trace(...data);\n this.dispatchEvent(\n new CustomEvent(MESSAGE_EVENT, {\n detail: { level: \"trace\", message: data },\n }),\n );\n }\n\n /**\n * Track the console messages.\n */\n trackMessages() {\n return new OutputTracker<ConsoleMessage>(this, MESSAGE_EVENT);\n }\n\n isLoggable(level: LogLevel) {\n const normalize = (level: LogLevel) => (level === \"log\" ? \"info\" : level);\n const levels: LogLevel[] = [\n \"off\",\n \"error\",\n \"warn\",\n \"info\",\n \"debug\",\n \"trace\",\n ];\n const currentLevelIndex = levels.indexOf(normalize(this.level));\n const messageLevelIndex = levels.indexOf(normalize(level));\n return messageLevelIndex <= currentLevelIndex;\n }\n}\n\nclass ConsoleStub implements Log {\n log(..._data: unknown[]) {}\n error(..._data: unknown[]) {}\n warn(..._data: unknown[]) {}\n info(..._data: unknown[]) {}\n debug(..._data: unknown[]) {}\n trace(..._data: unknown[]) {}\n}\n","// Copyright (c) 2025 Falko Schumann. All rights reserved. MIT license.\n\nimport { ConfigurableResponses } from \"./configurable_responses\";\n\n/**\n * This data object configures the response of a fetch stub call.\n */\nexport interface ResponseData {\n /** The HTTP status code. */\n status: number;\n\n /** The HTTP status text. */\n statusText: string;\n\n /** The optional response body. */\n body?: Blob | object | string | null;\n}\n\n/**\n * Create a fetch stub.\n *\n * The stub returns a response from the provided response data or throws an provided error.\n *\n * @param responses A single response or an array of responses.\n * @returns The fetch stub.\n */\nexport function createFetchStub(\n responses?: ResponseData | Error | (ResponseData | Error)[],\n): () => Promise<Response> {\n const configurableResponses = ConfigurableResponses.create(responses);\n return async function () {\n const response = configurableResponses.next();\n if (response instanceof Error) {\n throw response;\n }\n\n return new ResponseStub(response) as unknown as Response;\n };\n}\n\nclass ResponseStub {\n #status: number;\n #statusText: string;\n #body?: Blob | object | string | null;\n\n constructor({ status, statusText, body = null }: ResponseData) {\n this.#status = status;\n this.#statusText = statusText;\n this.#body = body;\n }\n\n get ok() {\n return this.status >= 200 && this.status < 300;\n }\n\n get status() {\n return this.#status;\n }\n\n get statusText() {\n return this.#statusText;\n }\n\n async blob() {\n if (this.#body == null) {\n return null;\n }\n\n if (this.#body instanceof Blob) {\n return this.#body;\n }\n\n throw new TypeError(\"Body is not a Blob.\");\n }\n\n async json() {\n const json =\n typeof this.#body === \"string\" ? this.#body : JSON.stringify(this.#body);\n return Promise.resolve(JSON.parse(json));\n }\n\n async text() {\n if (this.#body == null) {\n return \"\";\n }\n\n return String(this.#body);\n }\n}\n","// Copyright (c) 2025 Falko Schumann. All rights reserved. MIT license.\n\nimport type { MessageClient } from \"./message_client\";\n\n/**\n * A client for the server-sent events protocol.\n */\nexport class SseClient extends EventTarget implements MessageClient {\n /**\n * Create an SSE client.\n *\n * @return A new SSE client.\n */\n static create(): SseClient {\n return new SseClient(EventSource);\n }\n\n /**\n * Create a nulled SSE client.\n *\n * @return A new SSE client.\n */\n static createNull(): SseClient {\n return new SseClient(EventSourceStub as typeof EventSource);\n }\n\n readonly #eventSourceConstructor: typeof EventSource;\n\n #eventSource?: EventSource;\n\n private constructor(eventSourceConstructor: typeof EventSource) {\n super();\n this.#eventSourceConstructor = eventSourceConstructor;\n }\n\n get isConnected(): boolean {\n return this.#eventSource?.readyState === this.#eventSourceConstructor.OPEN;\n }\n\n get url(): string | undefined {\n return this.#eventSource?.url;\n }\n\n async connect(\n url: string | URL,\n eventName = \"message\",\n ...otherEvents: string[]\n ): Promise<void> {\n await new Promise<void>((resolve, reject) => {\n if (this.isConnected) {\n reject(new Error(\"Already connected.\"));\n return;\n }\n\n try {\n this.#eventSource = new this.#eventSourceConstructor(url);\n this.#eventSource.addEventListener(\"open\", (e) => {\n this.#handleOpen(e);\n resolve();\n });\n this.#eventSource.addEventListener(eventName, (e) =>\n this.#handleMessage(e),\n );\n for (const otherEvent of otherEvents) {\n this.#eventSource.addEventListener(otherEvent, (e) =>\n this.#handleMessage(e),\n );\n }\n this.#eventSource.addEventListener(\"error\", (e) =>\n this.#handleError(e),\n );\n } catch (error) {\n reject(error);\n }\n });\n }\n\n send(_message: string, _type?: string): Promise<void> {\n throw new Error(\"Method not implemented.\");\n }\n\n async close(): Promise<void> {\n await new Promise<void>((resolve, reject) => {\n if (!this.isConnected) {\n resolve();\n return;\n }\n\n try {\n this.#eventSource!.close();\n resolve();\n } catch (error) {\n reject(error);\n }\n });\n }\n\n /**\n * Simulate a message event from the server.\n *\n * @param message The message to receive.\n * @param eventName The optional event type.\n * @param lastEventId The optional last event ID.\n */\n simulateMessage(\n message: string | number | boolean | object | null,\n eventName = \"message\",\n lastEventId?: string,\n ) {\n if (typeof message !== \"string\") {\n message = JSON.stringify(message);\n }\n this.#handleMessage(\n new MessageEvent(eventName, { data: message, lastEventId }),\n );\n }\n\n /**\n * Simulate an error event.\n */\n simulateError() {\n this.#handleError(new Event(\"error\"));\n }\n\n #handleOpen(event: Event) {\n this.dispatchEvent(new Event(event.type, event));\n }\n\n #handleMessage(event: MessageEvent) {\n this.dispatchEvent(\n new MessageEvent(event.type, event as unknown as MessageEventInit),\n );\n }\n\n #handleError(event: Event) {\n this.dispatchEvent(new Event(event.type, event));\n }\n}\n\nclass EventSourceStub extends EventTarget {\n // The constants have to be defined here because Node.js support is currently\n // experimental only.\n static CONNECTING = 0;\n static OPEN = 1;\n static CLOSED = 2;\n\n url: string;\n readyState = EventSourceStub.CONNECTING;\n\n constructor(url: string | URL) {\n super();\n this.url = url.toString();\n setTimeout(() => {\n this.readyState = EventSourceStub.OPEN;\n this.dispatchEvent(new Event(\"open\"));\n }, 0);\n }\n\n close() {\n this.readyState = EventSourceStub.CLOSED;\n }\n}\n","// Copyright (c) 2025 Falko Schumann. All rights reserved. MIT license.\n\nimport { OutputTracker } from \"./output_tracker\";\nimport type { MessageClient } from \"./message_client\";\n\nexport const HEARTBEAT_TYPE = \"heartbeat\";\n\nconst MESSAGE_SENT_EVENT = \"message-sent\";\n\n/**\n * Options for the WebSocket client.\n */\nexport interface WebSocketOptions {\n /**\n * The heartbeat interval in milliseconds. A value <= 0 disables the\n * heartbeat.\n */\n heartbeat?: number;\n\n /**\n * The time in milliseconds to wait before retrying a connection after an\n * error. A value <= 0 disables automatic retries.\n */\n retry?: number;\n}\n\n/**\n * A client for the WebSocket protocol.\n */\nexport class WebSocketClient extends EventTarget implements MessageClient {\n /**\n * Create a WebSocket client.\n *\n * @param options The options for the WebSocket client.\n * @return A new WebSocket client.\n */\n static create({\n heartbeat = 30000,\n retry = 1000,\n }: WebSocketOptions = {}): WebSocketClient {\n return new WebSocketClient(heartbeat, retry, WebSocket);\n }\n\n /**\n * Create a nulled WebSocket client.\n *\n * @param options The options for the WebSocket client.\n * @return A new nulled WebSocket client.\n */\n static createNull({ heartbeat = 0, retry = 0 }: WebSocketOptions = {}) {\n return new WebSocketClient(\n heartbeat,\n retry,\n WebSocketStub as unknown as typeof WebSocket,\n );\n }\n\n readonly #heartbeat: number;\n readonly #retry: number;\n readonly #webSocketConstructor: typeof WebSocket;\n\n #webSocket?: WebSocket;\n #heartbeatId?: ReturnType<typeof setTimeout>;\n #retryId?: ReturnType<typeof setTimeout>;\n\n private constructor(\n heartbeat: number,\n retry: number,\n webSocketConstructor: typeof WebSocket,\n ) {\n super();\n this.#heartbeat = heartbeat;\n this.#retry = retry;\n this.#webSocketConstructor = webSocketConstructor;\n }\n\n get isConnected(): boolean {\n return this.#webSocket?.readyState === WebSocket.OPEN;\n }\n\n get url(): string | undefined {\n return this.#webSocket?.url;\n }\n\n async connect(url: string | URL): Promise<void> {\n await new Promise<void>((resolve, reject) => {\n this.#stopRetry();\n\n if (this.isConnected) {\n reject(new Error(\"Already connected.\"));\n return;\n }\n\n try {\n this.#webSocket = new this.#webSocketConstructor(url);\n this.#webSocket.addEventListener(\"open\", (e) => {\n this.#handleOpen(e);\n resolve();\n });\n this.#webSocket.addEventListener(\"message\", (e) =>\n this.#handleMessage(e),\n );\n this.#webSocket.addEventListener(\"close\", (e) => this.#handleClose(e));\n this.#webSocket.addEventListener(\"error\", (e) => this.#handleError(e));\n } catch (error) {\n reject(error);\n }\n });\n }\n\n async send(\n message: string | ArrayBuffer | Blob | ArrayBufferView,\n ): Promise<void> {\n if (!this.isConnected) {\n throw new Error(\"Not connected.\");\n }\n\n this.#webSocket!.send(message);\n this.dispatchEvent(\n new CustomEvent(MESSAGE_SENT_EVENT, { detail: message }),\n );\n await Promise.resolve();\n }\n\n /**\n * Return a tracker for messages sent.\n *\n * @return A new output tracker.\n */\n trackMessageSent(): OutputTracker<string> {\n return OutputTracker.create(this, MESSAGE_SENT_EVENT);\n }\n\n /**\n * Close the connection.\n *\n * If a code is provided, also a reason should be provided.\n *\n * @param code An optional code.\n * @param reason An optional reason.\n */\n async close(code?: number, reason?: string): Promise<void> {\n await new Promise<void>((resolve) => {\n this.#stopRetry();\n\n if (!this.isConnected) {\n resolve();\n return;\n }\n\n this.#webSocket!.addEventListener(\"close\", () => resolve());\n this.#webSocket!.close(code, reason);\n });\n }\n\n /**\n * Simulate a message event from the server.\n *\n * @param message The message to receive.\n */\n simulateMessage(\n message: string | number | boolean | object | null | Blob | ArrayBuffer,\n ) {\n if (\n typeof message !== \"string\" &&\n !(message instanceof Blob) &&\n !(message instanceof ArrayBuffer)\n ) {\n message = JSON.stringify(message);\n }\n this.#handleMessage(new MessageEvent(\"message\", { data: message }));\n }\n\n /**\n * Simulate a heartbeat.\n */\n simulateHeartbeat() {\n this.#sendHeartbeat();\n }\n\n /**\n * Simulate a close event.\n *\n * @param code An optional code.\n * @param reason An optional reason.\n */\n simulateClose(code?: number, reason?: string) {\n this.#handleClose(new CloseEvent(\"close\", { code, reason }));\n }\n\n /**\n * Simulate an error event.\n */\n simulateError() {\n this.#webSocket?.close();\n this.#handleError(new Event(\"error\"));\n }\n\n #handleOpen(event: Event) {\n this.dispatchEvent(new Event(event.type, event));\n this.#startHeartbeat();\n }\n\n #handleMessage(event: MessageEvent) {\n this.dispatchEvent(\n new MessageEvent(event.type, event as unknown as MessageEventInit),\n );\n }\n\n #handleClose(event: CloseEvent) {\n this.#stopHeartbeat();\n this.dispatchEvent(new CloseEvent(event.type, event));\n }\n\n #handleError(event: Event) {\n this.dispatchEvent(new Event(event.type, event));\n this.#startRetry();\n }\n\n #startRetry() {\n if (this.#retry <= 0) {\n return;\n }\n this.#retryId = setInterval(\n () => this.connect(this.#webSocket!.url),\n this.#retry,\n );\n }\n\n #stopRetry() {\n clearInterval(this.#retryId);\n this.#retryId = undefined;\n }\n\n #startHeartbeat() {\n if (this.#heartbeat <= 0) {\n return;\n }\n\n this.#heartbeatId = setInterval(\n () => this.#sendHeartbeat(),\n this.#heartbeat,\n );\n }\n\n #stopHeartbeat() {\n clearInterval(this.#heartbeatId);\n this.#heartbeatId = undefined;\n }\n\n #sendHeartbeat() {\n if (this.#heartbeatId == null) {\n return;\n }\n\n void this.send(HEARTBEAT_TYPE);\n }\n}\n\nclass WebSocketStub extends EventTarget {\n url: string;\n readyState: number = WebSocket.CONNECTING;\n\n constructor(url: string | URL) {\n super();\n this.url = url.toString();\n setTimeout(() => {\n this.readyState = WebSocket.OPEN;\n this.dispatchEvent(new Event(\"open\"));\n }, 0);\n }\n\n send() {}\n\n close() {\n this.readyState = WebSocket.CLOSED;\n this.dispatchEvent(new Event(\"close\"));\n }\n}\n"],"names":["Clock","date","clock","offsetMillis","#date","EventTracker","eventTarget","event","#eventTarget","#event","#events","#tracker","result","count","resolve","checkEvents","Success","Failure","errorMessage","ConfigurableResponses","responses","name","responseObject","translatedEntries","key","value","translatedName","#description","#responses","response","OutputTracker","#data","MESSAGE_EVENT","ConsoleLog","ConsoleStub","#console","console","data","level","normalize","levels","currentLevelIndex","_data","createFetchStub","configurableResponses","ResponseStub","#status","#statusText","#body","status","statusText","body","json","SseClient","EventSourceStub","#eventSourceConstructor","#eventSource","eventSourceConstructor","url","eventName","otherEvents","reject","e","#handleOpen","#handleMessage","otherEvent","#handleError","error","_message","_type","message","lastEventId","HEARTBEAT_TYPE","MESSAGE_SENT_EVENT","WebSocketClient","heartbeat","retry","WebSocketStub","#heartbeat","#retry","#webSocketConstructor","#webSocket","#heartbeatId","#retryId","webSocketConstructor","#stopRetry","#handleClose","code","reason","#sendHeartbeat","#startHeartbeat","#stopHeartbeat","#startRetry"],"mappings":"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"}
1
+ {"version":3,"file":"shared.umd.cjs","sources":["../src/common/clock.ts","../src/common/event_tracker.ts","../src/domain/messages.ts","../src/infrastructure/configurable_responses.ts","../src/infrastructure/output_tracker.ts","../src/infrastructure/console_log.ts","../src/infrastructure/fetch_stub.ts","../src/infrastructure/sse_client.ts","../src/infrastructure/web_socket_client.ts"],"sourcesContent":["// Copyright (c) 2025 Falko Schumann. All rights reserved. MIT license.\n\n/**\n * A clock provides access to the current timestamp.\n */\nexport class Clock {\n /**\n * Create a clock using system the clock.\n *\n * @return A clock that uses the system clock.\n */\n static system(): Clock {\n return new Clock();\n }\n\n /**\n * Create a clock using a fixed date.\n *\n * @param date The fixed date of the clock.\n * @return A clock that always returns a fixed date.\n */\n static fixed(date: Date | string | number): Clock {\n return new Clock(new Date(date));\n }\n\n /**\n * Create a clock that returns a fixed offset from the given clock.\n *\n * @param clock The clock to offset from.\n * @param offsetMillis The offset in milliseconds.\n * @return A clock that returns a fixed offset from the given clock.\n */\n static offset(clock: Clock, offsetMillis: number): Clock {\n return new Clock(new Date(clock.millis() + offsetMillis));\n }\n\n readonly #date?: Date;\n\n private constructor(date?: Date) {\n this.#date = date;\n }\n\n /**\n * Return the current timestamp of the clock.\n *\n * @return The current timestamp.\n */\n date(): Date {\n return this.#date ? new Date(this.#date) : new Date();\n }\n\n /**\n * Return the current timestamp of the clock in milliseconds.\n *\n * @return The current timestamp in milliseconds.\n */\n millis(): number {\n return this.date().getTime();\n }\n}\n","// Copyright (c) 2025 Falko Schumann. All rights reserved. MIT license.\n\n// TODO monitor multiple events\n\n/**\n * Track events from an event target.\n *\n * Wait asynchronously for events. Useful in test code.\n */\nexport class EventTracker<T extends Event> {\n /**\n * Create a tracker for a specific event of an event target.\n *\n * @param eventTarget The target to track.\n * @param event The event name to track.\n */\n static create<T extends Event>(eventTarget: EventTarget, ...event: string[]) {\n return new EventTracker<T>(eventTarget, event);\n }\n\n readonly #eventTarget;\n readonly #event;\n readonly #events: T[];\n readonly #tracker;\n\n /**\n * Create a tracker for a specific event of an event target.\n *\n * @param eventTarget The target to track.\n * @param event The event name to track.\n */\n constructor(eventTarget: EventTarget, event: string[]) {\n this.#eventTarget = eventTarget;\n this.#event = event;\n this.#events = [];\n this.#tracker = (event: Event) => this.#events.push(event as T);\n\n this.#event.forEach((event) =>\n this.#eventTarget.addEventListener(event, this.#tracker),\n );\n }\n\n /**\n * Return the tracked events.\n *\n * @return The tracked events.\n */\n get events(): T[] {\n return this.#events;\n }\n\n /**\n * Clear the tracked events and return the cleared events.\n *\n * @return The cleared events.\n */\n clear(): T[] {\n const result = [...this.#events];\n this.#events.length = 0;\n return result;\n }\n\n /**\n * Stop tracking.\n */\n stop() {\n this.#event.forEach((event) =>\n this.#eventTarget.removeEventListener(event, this.#tracker),\n );\n }\n\n /**\n * Wait asynchronously for a number of events.\n *\n * @param count number of events, default 1.\n */\n async waitFor(count = 1) {\n return new Promise<T[]>((resolve) => {\n const checkEvents = () => {\n if (this.#events.length >= count) {\n this.#event.forEach((event) =>\n this.#eventTarget.removeEventListener(event, checkEvents),\n );\n resolve(this.events);\n }\n };\n\n this.#event.forEach((event) =>\n this.#eventTarget.addEventListener(event, checkEvents),\n );\n checkEvents();\n });\n }\n}\n","// Copyright (c) 2025 Falko Schumann. All rights reserved. MIT license.\n\n/**\n * Provides CQNS features.\n *\n * The Command Query Notification Separation principle is a software design\n * principle that separates the concerns of commands, queries, and\n * notifications.\n *\n * Message hierarchy:\n *\n * - Message\n * - Incoming / outgoing\n * - Request (outgoing) -> response (incoming)\n * - Command -> command status\n * - Query -> query result\n * - Notification\n * - Incoming: notification -> commands\n * - Outgoing\n * - Event (internal)\n *\n * @see https://ralfw.de/command-query-notification-separation-cqns/\n * @module\n */\n\n/**\n * The status returned by a command handler.\n */\nexport type CommandStatus = Success | Failure;\n\n/**\n * A successful status.\n */\nexport class Success<T = unknown> {\n readonly isSuccess = true;\n readonly result?: T;\n\n constructor(result?: T) {\n this.result = result;\n }\n}\n\n/**\n * A failed status.\n */\nexport class Failure<T = string> {\n readonly isSuccess = false;\n readonly errorMessage: T;\n\n /**\n * Creates a failed status.\n *\n * @param errorMessage\n */\n constructor(errorMessage: T) {\n this.errorMessage = errorMessage;\n }\n}\n","// Copyright (c) 2025 Falko Schumann. All rights reserved. MIT license.\n// Copyright 2023 Titanium I.T. LLC. MIT License.\n\n/**\n * Handle returning pre-configured responses.\n *\n * This is one of the nullability patterns from James Shore's article on\n * [testing without mocks](https://www.jamesshore.com/v2/projects/nullables/testing-without-mocks#configurable-responses).\n *\n * Example usage for stubbing `fetch` function:\n *\n * ```javascript\n * function createFetchStub(responses) {\n * const configurableResponses = ConfigurableResponses.create(responses);\n * return async function () {\n * const response = configurableResponses.next();\n * return {\n * status: response.status,\n * json: async () => response.body,\n * };\n * };\n * }\n * ```\n */\nexport class ConfigurableResponses<T = unknown> {\n /**\n * Create a list of responses (by providing an array), or a single repeating\n * response (by providing any other type). 'Name' is optional and used in\n * error messages.\n *\n * @param responses A single response or an array of responses.\n * @param name An optional name for the responses.\n */\n static create<T>(responses?: T | T[], name?: string) {\n return new ConfigurableResponses<T>(responses, name);\n }\n\n /**\n * Convert all properties in an object into ConfigurableResponse instances.\n * For example, { a: 1 } becomes { a: ConfigurableResponses.create(1) }.\n * 'Name' is optional and used in error messages.\n *\n * @param responseObject An object with single response or an array of responses.\n * @param name An optional name for the responses.\n */\n static mapObject<T extends Record<string, unknown>>(\n responseObject: T,\n name?: string,\n ) {\n const entries = Object.entries(responseObject);\n const translatedEntries = entries.map(([key, value]) => {\n const translatedName = name === undefined ? undefined : `${name}: ${key}`;\n return [key, ConfigurableResponses.create(value, translatedName)];\n });\n return Object.fromEntries(translatedEntries);\n }\n\n readonly #description;\n readonly #responses;\n\n /**\n * Create a list of responses (by providing an array), or a single repeating\n * response (by providing any other type). 'Name' is optional and used in\n * error messages.\n *\n * @param responses A single response or an array of responses.\n * @param name An optional name for the responses.\n */\n constructor(responses?: T | T[], name?: string) {\n this.#description = name == null ? \"\" : ` in ${name}`;\n this.#responses = Array.isArray(responses) ? [...responses] : responses;\n }\n\n /**\n * Get the next configured response. Throws an error when configured with a list\n * of responses and no more responses remain.\n *\n * @return The next response.\n */\n next(): T {\n const response = Array.isArray(this.#responses)\n ? this.#responses.shift()\n : this.#responses;\n if (response === undefined) {\n throw new Error(`No more responses configured${this.#description}.`);\n }\n\n return response;\n }\n}\n","// Copyright (c) 2025 Falko Schumann. All rights reserved. MIT license.\n// Copyright 2020-2022 Titanium I.T. LLC. MIT License.\n\n/**\n * Track output events.\n *\n * This is one of the nullability patterns from James Shore's article on\n * [testing without mocks](https://www.jamesshore.com/v2/projects/nullables/testing-without-mocks#output-tracking).\n *\n * Example implementation of an event store:\n *\n * ```javascript\n * async record(event) {\n * // ...\n * this.dispatchEvent(new CustomEvent(\"eventRecorded\", { detail: event }));\n * }\n *\n * trackEventsRecorded() {\n * return new OutputTracker(this, \"eventRecorded\");\n * }\n * ```\n *\n * Example usage:\n *\n * ```javascript\n * const eventsRecorded = eventStore.trackEventsRecorded();\n * // ...\n * const data = eventsRecorded.data(); // [event1, event2, ...]\n * ```\n */\nexport class OutputTracker<T = unknown> {\n /**\n * Create a tracker for a specific event of an event target.\n *\n * @param eventTarget The target to track.\n * @param event The event name to track.\n */\n static create<T>(eventTarget: EventTarget, event: string) {\n return new OutputTracker<T>(eventTarget, event);\n }\n\n readonly #eventTarget;\n readonly #event;\n readonly #data: T[];\n readonly #tracker;\n\n /**\n * Create a tracker for a specific event of an event target.\n *\n * @param eventTarget The target to track.\n * @param event The event name to track.\n */\n constructor(eventTarget: EventTarget, event: string) {\n this.#eventTarget = eventTarget;\n this.#event = event;\n this.#data = [];\n this.#tracker = (event: Event) =>\n this.#data.push((event as CustomEvent<T>).detail);\n\n this.#eventTarget.addEventListener(this.#event, this.#tracker);\n }\n\n /**\n * Return the tracked data.\n *\n * @return The tracked data.\n */\n get data(): T[] {\n return this.#data;\n }\n\n /**\n * Clear the tracked data and return the cleared data.\n *\n * @return The cleared data.\n */\n clear(): T[] {\n const result = [...this.#data];\n this.#data.length = 0;\n return result;\n }\n\n /**\n * Stop tracking.\n */\n stop() {\n this.#eventTarget.removeEventListener(this.#event, this.#tracker);\n }\n}\n","// Copyright (c) 2026 Falko Schumann. All rights reserved. MIT license.\n\nimport type { Log, LogLevel } from \"../common/log\";\nimport { OutputTracker } from \"./output_tracker\";\n\nconst MESSAGE_EVENT = \"message\";\n\nexport interface ConsoleMessage {\n level: LogLevel;\n message: unknown[];\n}\n\n/**\n * Wraps the console interface and allow setting the log level.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/API/Console_API\n */\nexport class ConsoleLog extends EventTarget implements Log {\n static create({ name }: { name?: string } = {}) {\n return new ConsoleLog(globalThis.console, name);\n }\n\n static createNull({ name }: { name?: string } = {}) {\n return new ConsoleLog(new ConsoleStub(), name);\n }\n\n name?: string;\n level: LogLevel = \"info\";\n\n #console;\n\n private constructor(console: Log, name?: string) {\n super();\n this.name = name;\n this.#console = console;\n }\n\n log(...data: unknown[]) {\n if (!this.isLoggable(\"log\")) {\n return;\n }\n\n data = this.#applyName(data);\n this.#console.log(...data);\n this.dispatchEvent(\n new CustomEvent(MESSAGE_EVENT, {\n detail: { level: \"log\", message: data },\n }),\n );\n }\n\n error(...data: unknown[]) {\n if (!this.isLoggable(\"error\")) {\n return;\n }\n\n data = this.#applyName(data);\n this.#console.error(...data);\n this.dispatchEvent(\n new CustomEvent(MESSAGE_EVENT, {\n detail: { level: \"error\", message: data },\n }),\n );\n }\n\n warn(...data: unknown[]) {\n if (!this.isLoggable(\"warn\")) {\n return;\n }\n\n data = this.#applyName(data);\n this.#console.warn(...data);\n this.dispatchEvent(\n new CustomEvent(MESSAGE_EVENT, {\n detail: { level: \"warn\", message: data },\n }),\n );\n }\n\n info(...data: unknown[]) {\n if (!this.isLoggable(\"info\")) {\n return;\n }\n\n data = this.#applyName(data);\n this.#console.info(...data);\n this.dispatchEvent(\n new CustomEvent(MESSAGE_EVENT, {\n detail: { level: \"info\", message: data },\n }),\n );\n }\n\n debug(...data: unknown[]) {\n if (!this.isLoggable(\"debug\")) {\n return;\n }\n\n data = this.#applyName(data);\n this.#console.debug(...data);\n this.dispatchEvent(\n new CustomEvent(MESSAGE_EVENT, {\n detail: { level: \"debug\", message: data },\n }),\n );\n }\n\n trace(...data: unknown[]) {\n if (!this.isLoggable(\"trace\")) {\n return;\n }\n\n data = this.#applyName(data);\n this.#console.trace(...data);\n this.dispatchEvent(\n new CustomEvent(MESSAGE_EVENT, {\n detail: { level: \"trace\", message: data },\n }),\n );\n }\n\n /**\n * Track the console messages.\n */\n trackMessages() {\n return new OutputTracker<ConsoleMessage>(this, MESSAGE_EVENT);\n }\n\n isLoggable(level: LogLevel) {\n const normalize = (level: LogLevel) => (level === \"log\" ? \"info\" : level);\n const levels: LogLevel[] = [\n \"off\",\n \"error\",\n \"warn\",\n \"info\",\n \"debug\",\n \"trace\",\n ];\n const currentLevelIndex = levels.indexOf(normalize(this.level));\n const messageLevelIndex = levels.indexOf(normalize(level));\n return messageLevelIndex <= currentLevelIndex;\n }\n\n #applyName(data: unknown[]) {\n if (this.name == null) {\n return data;\n }\n\n return [`${this.name}`, ...data];\n }\n}\n\nclass ConsoleStub implements Log {\n log(..._data: unknown[]) {}\n error(..._data: unknown[]) {}\n warn(..._data: unknown[]) {}\n info(..._data: unknown[]) {}\n debug(..._data: unknown[]) {}\n trace(..._data: unknown[]) {}\n}\n","// Copyright (c) 2025 Falko Schumann. All rights reserved. MIT license.\n\nimport { ConfigurableResponses } from \"./configurable_responses\";\n\n/**\n * This data object configures the response of a fetch stub call.\n */\nexport interface ResponseData {\n /** The HTTP status code. */\n status: number;\n\n /** The HTTP status text. */\n statusText: string;\n\n /** The optional response body. */\n body?: Blob | object | string | null;\n}\n\n/**\n * Create a fetch stub.\n *\n * The stub returns a response from the provided response data or throws an provided error.\n *\n * @param responses A single response or an array of responses.\n * @returns The fetch stub.\n */\nexport function createFetchStub(\n responses?: ResponseData | Error | (ResponseData | Error)[],\n): () => Promise<Response> {\n const configurableResponses = ConfigurableResponses.create(responses);\n return async function () {\n const response = configurableResponses.next();\n if (response instanceof Error) {\n throw response;\n }\n\n return new ResponseStub(response) as unknown as Response;\n };\n}\n\nclass ResponseStub {\n #status: number;\n #statusText: string;\n #body?: Blob | object | string | null;\n\n constructor({ status, statusText, body = null }: ResponseData) {\n this.#status = status;\n this.#statusText = statusText;\n this.#body = body;\n }\n\n get ok() {\n return this.status >= 200 && this.status < 300;\n }\n\n get status() {\n return this.#status;\n }\n\n get statusText() {\n return this.#statusText;\n }\n\n async blob() {\n if (this.#body == null) {\n return null;\n }\n\n if (this.#body instanceof Blob) {\n return this.#body;\n }\n\n throw new TypeError(\"Body is not a Blob.\");\n }\n\n async json() {\n const json =\n typeof this.#body === \"string\" ? this.#body : JSON.stringify(this.#body);\n return Promise.resolve(JSON.parse(json));\n }\n\n async text() {\n if (this.#body == null) {\n return \"\";\n }\n\n return String(this.#body);\n }\n}\n","// Copyright (c) 2025 Falko Schumann. All rights reserved. MIT license.\n\nimport type { MessageClient } from \"./message_client\";\n\n/**\n * A client for the server-sent events protocol.\n */\nexport class SseClient extends EventTarget implements MessageClient {\n /**\n * Create an SSE client.\n *\n * @return A new SSE client.\n */\n static create(): SseClient {\n return new SseClient(EventSource);\n }\n\n /**\n * Create a nulled SSE client.\n *\n * @return A new SSE client.\n */\n static createNull(): SseClient {\n return new SseClient(EventSourceStub as typeof EventSource);\n }\n\n readonly #eventSourceConstructor: typeof EventSource;\n\n #eventSource?: EventSource;\n\n private constructor(eventSourceConstructor: typeof EventSource) {\n super();\n this.#eventSourceConstructor = eventSourceConstructor;\n }\n\n get isConnected(): boolean {\n return this.#eventSource?.readyState === this.#eventSourceConstructor.OPEN;\n }\n\n get url(): string | undefined {\n return this.#eventSource?.url;\n }\n\n async connect(\n url: string | URL,\n eventName = \"message\",\n ...otherEvents: string[]\n ): Promise<void> {\n await new Promise<void>((resolve, reject) => {\n if (this.isConnected) {\n reject(new Error(\"Already connected.\"));\n return;\n }\n\n try {\n this.#eventSource = new this.#eventSourceConstructor(url);\n this.#eventSource.addEventListener(\"open\", (e) => {\n this.#handleOpen(e);\n resolve();\n });\n this.#eventSource.addEventListener(eventName, (e) =>\n this.#handleMessage(e),\n );\n for (const otherEvent of otherEvents) {\n this.#eventSource.addEventListener(otherEvent, (e) =>\n this.#handleMessage(e),\n );\n }\n this.#eventSource.addEventListener(\"error\", (e) =>\n this.#handleError(e),\n );\n } catch (error) {\n reject(error);\n }\n });\n }\n\n send(_message: string, _type?: string): Promise<void> {\n throw new Error(\"Method not implemented.\");\n }\n\n async close(): Promise<void> {\n await new Promise<void>((resolve, reject) => {\n if (!this.isConnected) {\n resolve();\n return;\n }\n\n try {\n this.#eventSource!.close();\n resolve();\n } catch (error) {\n reject(error);\n }\n });\n }\n\n /**\n * Simulate a message event from the server.\n *\n * @param message The message to receive.\n * @param eventName The optional event type.\n * @param lastEventId The optional last event ID.\n */\n simulateMessage(\n message: string | number | boolean | object | null,\n eventName = \"message\",\n lastEventId?: string,\n ) {\n if (typeof message !== \"string\") {\n message = JSON.stringify(message);\n }\n this.#handleMessage(\n new MessageEvent(eventName, { data: message, lastEventId }),\n );\n }\n\n /**\n * Simulate an error event.\n */\n simulateError() {\n this.#handleError(new Event(\"error\"));\n }\n\n #handleOpen(event: Event) {\n this.dispatchEvent(new Event(event.type, event));\n }\n\n #handleMessage(event: MessageEvent) {\n this.dispatchEvent(\n new MessageEvent(event.type, event as unknown as MessageEventInit),\n );\n }\n\n #handleError(event: Event) {\n this.dispatchEvent(new Event(event.type, event));\n }\n}\n\nclass EventSourceStub extends EventTarget {\n // The constants have to be defined here because Node.js support is currently\n // experimental only.\n static CONNECTING = 0;\n static OPEN = 1;\n static CLOSED = 2;\n\n url: string;\n readyState = EventSourceStub.CONNECTING;\n\n constructor(url: string | URL) {\n super();\n this.url = url.toString();\n setTimeout(() => {\n this.readyState = EventSourceStub.OPEN;\n this.dispatchEvent(new Event(\"open\"));\n }, 0);\n }\n\n close() {\n this.readyState = EventSourceStub.CLOSED;\n }\n}\n","// Copyright (c) 2025 Falko Schumann. All rights reserved. MIT license.\n\nimport { OutputTracker } from \"./output_tracker\";\nimport type { MessageClient } from \"./message_client\";\n\nexport const HEARTBEAT_TYPE = \"heartbeat\";\n\nconst MESSAGE_SENT_EVENT = \"message-sent\";\n\n/**\n * Options for the WebSocket client.\n */\nexport interface WebSocketOptions {\n /**\n * The heartbeat interval in milliseconds. A value <= 0 disables the\n * heartbeat.\n */\n heartbeat?: number;\n\n /**\n * The time in milliseconds to wait before retrying a connection after an\n * error. A value <= 0 disables automatic retries.\n */\n retry?: number;\n}\n\n/**\n * A client for the WebSocket protocol.\n */\nexport class WebSocketClient extends EventTarget implements MessageClient {\n /**\n * Create a WebSocket client.\n *\n * @param options The options for the WebSocket client.\n * @return A new WebSocket client.\n */\n static create({\n heartbeat = 30000,\n retry = 1000,\n }: WebSocketOptions = {}): WebSocketClient {\n return new WebSocketClient(heartbeat, retry, WebSocket);\n }\n\n /**\n * Create a nulled WebSocket client.\n *\n * @param options The options for the WebSocket client.\n * @return A new nulled WebSocket client.\n */\n static createNull({ heartbeat = 0, retry = 0 }: WebSocketOptions = {}) {\n return new WebSocketClient(\n heartbeat,\n retry,\n WebSocketStub as unknown as typeof WebSocket,\n );\n }\n\n readonly #heartbeat: number;\n readonly #retry: number;\n readonly #webSocketConstructor: typeof WebSocket;\n\n #webSocket?: WebSocket;\n #heartbeatId?: ReturnType<typeof setTimeout>;\n #retryId?: ReturnType<typeof setTimeout>;\n\n private constructor(\n heartbeat: number,\n retry: number,\n webSocketConstructor: typeof WebSocket,\n ) {\n super();\n this.#heartbeat = heartbeat;\n this.#retry = retry;\n this.#webSocketConstructor = webSocketConstructor;\n }\n\n get isConnected(): boolean {\n return this.#webSocket?.readyState === WebSocket.OPEN;\n }\n\n get url(): string | undefined {\n return this.#webSocket?.url;\n }\n\n async connect(url: string | URL): Promise<void> {\n await new Promise<void>((resolve, reject) => {\n this.#stopRetry();\n\n if (this.isConnected) {\n reject(new Error(\"Already connected.\"));\n return;\n }\n\n try {\n this.#webSocket = new this.#webSocketConstructor(url);\n this.#webSocket.addEventListener(\"open\", (e) => {\n this.#handleOpen(e);\n resolve();\n });\n this.#webSocket.addEventListener(\"message\", (e) =>\n this.#handleMessage(e),\n );\n this.#webSocket.addEventListener(\"close\", (e) => this.#handleClose(e));\n this.#webSocket.addEventListener(\"error\", (e) => this.#handleError(e));\n } catch (error) {\n reject(error);\n }\n });\n }\n\n async send(\n message: string | ArrayBuffer | Blob | ArrayBufferView,\n ): Promise<void> {\n if (!this.isConnected) {\n throw new Error(\"Not connected.\");\n }\n\n this.#webSocket!.send(message);\n this.dispatchEvent(\n new CustomEvent(MESSAGE_SENT_EVENT, { detail: message }),\n );\n await Promise.resolve();\n }\n\n /**\n * Return a tracker for messages sent.\n *\n * @return A new output tracker.\n */\n trackMessageSent(): OutputTracker<string> {\n return OutputTracker.create(this, MESSAGE_SENT_EVENT);\n }\n\n /**\n * Close the connection.\n *\n * If a code is provided, also a reason should be provided.\n *\n * @param code An optional code.\n * @param reason An optional reason.\n */\n async close(code?: number, reason?: string): Promise<void> {\n await new Promise<void>((resolve) => {\n this.#stopRetry();\n\n if (!this.isConnected) {\n resolve();\n return;\n }\n\n this.#webSocket!.addEventListener(\"close\", () => resolve());\n this.#webSocket!.close(code, reason);\n });\n }\n\n /**\n * Simulate a message event from the server.\n *\n * @param message The message to receive.\n */\n simulateMessage(\n message: string | number | boolean | object | null | Blob | ArrayBuffer,\n ) {\n if (\n typeof message !== \"string\" &&\n !(message instanceof Blob) &&\n !(message instanceof ArrayBuffer)\n ) {\n message = JSON.stringify(message);\n }\n this.#handleMessage(new MessageEvent(\"message\", { data: message }));\n }\n\n /**\n * Simulate a heartbeat.\n */\n simulateHeartbeat() {\n this.#sendHeartbeat();\n }\n\n /**\n * Simulate a close event.\n *\n * @param code An optional code.\n * @param reason An optional reason.\n */\n simulateClose(code?: number, reason?: string) {\n this.#handleClose(new CloseEvent(\"close\", { code, reason }));\n }\n\n /**\n * Simulate an error event.\n */\n simulateError() {\n this.#webSocket?.close();\n this.#handleError(new Event(\"error\"));\n }\n\n #handleOpen(event: Event) {\n this.dispatchEvent(new Event(event.type, event));\n this.#startHeartbeat();\n }\n\n #handleMessage(event: MessageEvent) {\n this.dispatchEvent(\n new MessageEvent(event.type, event as unknown as MessageEventInit),\n );\n }\n\n #handleClose(event: CloseEvent) {\n this.#stopHeartbeat();\n this.dispatchEvent(new CloseEvent(event.type, event));\n }\n\n #handleError(event: Event) {\n this.dispatchEvent(new Event(event.type, event));\n this.#startRetry();\n }\n\n #startRetry() {\n if (this.#retry <= 0) {\n return;\n }\n this.#retryId = setInterval(\n () => this.connect(this.#webSocket!.url),\n this.#retry,\n );\n }\n\n #stopRetry() {\n clearInterval(this.#retryId);\n this.#retryId = undefined;\n }\n\n #startHeartbeat() {\n if (this.#heartbeat <= 0) {\n return;\n }\n\n this.#heartbeatId = setInterval(\n () => this.#sendHeartbeat(),\n this.#heartbeat,\n );\n }\n\n #stopHeartbeat() {\n clearInterval(this.#heartbeatId);\n this.#heartbeatId = undefined;\n }\n\n #sendHeartbeat() {\n if (this.#heartbeatId == null) {\n return;\n }\n\n void this.send(HEARTBEAT_TYPE);\n }\n}\n\nclass WebSocketStub extends EventTarget {\n url: string;\n readyState: number = WebSocket.CONNECTING;\n\n constructor(url: string | URL) {\n super();\n this.url = url.toString();\n setTimeout(() => {\n this.readyState = WebSocket.OPEN;\n this.dispatchEvent(new Event(\"open\"));\n }, 0);\n }\n\n send() {}\n\n close() {\n this.readyState = WebSocket.CLOSED;\n this.dispatchEvent(new Event(\"close\"));\n }\n}\n"],"names":["Clock","date","clock","offsetMillis","#date","EventTracker","eventTarget","event","#eventTarget","#event","#events","#tracker","result","count","resolve","checkEvents","Success","Failure","errorMessage","ConfigurableResponses","responses","name","responseObject","translatedEntries","key","value","translatedName","#description","#responses","response","OutputTracker","#data","MESSAGE_EVENT","ConsoleLog","ConsoleStub","#console","console","data","#applyName","level","normalize","levels","currentLevelIndex","_data","createFetchStub","configurableResponses","ResponseStub","#status","#statusText","#body","status","statusText","body","json","SseClient","EventSourceStub","#eventSourceConstructor","#eventSource","eventSourceConstructor","url","eventName","otherEvents","reject","e","#handleOpen","#handleMessage","otherEvent","#handleError","error","_message","_type","message","lastEventId","HEARTBEAT_TYPE","MESSAGE_SENT_EVENT","WebSocketClient","heartbeat","retry","WebSocketStub","#heartbeat","#retry","#webSocketConstructor","#webSocket","#heartbeatId","#retryId","webSocketConstructor","#stopRetry","#handleClose","code","reason","#sendHeartbeat","#startHeartbeat","#stopHeartbeat","#startRetry"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@muspellheim/shared",
3
- "version": "0.16.0",
3
+ "version": "0.17.0",
4
4
  "author": "Falko Schumann",
5
5
  "license": "MIT",
6
6
  "engines": {
@@ -16,20 +16,22 @@ export interface ConsoleMessage {
16
16
  * @see https://developer.mozilla.org/en-US/docs/Web/API/Console_API
17
17
  */
18
18
  export class ConsoleLog extends EventTarget implements Log {
19
- static create() {
20
- return new ConsoleLog(globalThis.console);
19
+ static create({ name }: { name?: string } = {}) {
20
+ return new ConsoleLog(globalThis.console, name);
21
21
  }
22
22
 
23
- static createNull() {
24
- return new ConsoleLog(new ConsoleStub());
23
+ static createNull({ name }: { name?: string } = {}) {
24
+ return new ConsoleLog(new ConsoleStub(), name);
25
25
  }
26
26
 
27
+ name?: string;
27
28
  level: LogLevel = "info";
28
29
 
29
30
  #console;
30
31
 
31
- private constructor(console: Log) {
32
+ private constructor(console: Log, name?: string) {
32
33
  super();
34
+ this.name = name;
33
35
  this.#console = console;
34
36
  }
35
37
 
@@ -38,6 +40,7 @@ export class ConsoleLog extends EventTarget implements Log {
38
40
  return;
39
41
  }
40
42
 
43
+ data = this.#applyName(data);
41
44
  this.#console.log(...data);
42
45
  this.dispatchEvent(
43
46
  new CustomEvent(MESSAGE_EVENT, {
@@ -51,6 +54,7 @@ export class ConsoleLog extends EventTarget implements Log {
51
54
  return;
52
55
  }
53
56
 
57
+ data = this.#applyName(data);
54
58
  this.#console.error(...data);
55
59
  this.dispatchEvent(
56
60
  new CustomEvent(MESSAGE_EVENT, {
@@ -64,6 +68,7 @@ export class ConsoleLog extends EventTarget implements Log {
64
68
  return;
65
69
  }
66
70
 
71
+ data = this.#applyName(data);
67
72
  this.#console.warn(...data);
68
73
  this.dispatchEvent(
69
74
  new CustomEvent(MESSAGE_EVENT, {
@@ -77,6 +82,7 @@ export class ConsoleLog extends EventTarget implements Log {
77
82
  return;
78
83
  }
79
84
 
85
+ data = this.#applyName(data);
80
86
  this.#console.info(...data);
81
87
  this.dispatchEvent(
82
88
  new CustomEvent(MESSAGE_EVENT, {
@@ -90,6 +96,7 @@ export class ConsoleLog extends EventTarget implements Log {
90
96
  return;
91
97
  }
92
98
 
99
+ data = this.#applyName(data);
93
100
  this.#console.debug(...data);
94
101
  this.dispatchEvent(
95
102
  new CustomEvent(MESSAGE_EVENT, {
@@ -103,6 +110,7 @@ export class ConsoleLog extends EventTarget implements Log {
103
110
  return;
104
111
  }
105
112
 
113
+ data = this.#applyName(data);
106
114
  this.#console.trace(...data);
107
115
  this.dispatchEvent(
108
116
  new CustomEvent(MESSAGE_EVENT, {
@@ -132,6 +140,14 @@ export class ConsoleLog extends EventTarget implements Log {
132
140
  const messageLevelIndex = levels.indexOf(normalize(level));
133
141
  return messageLevelIndex <= currentLevelIndex;
134
142
  }
143
+
144
+ #applyName(data: unknown[]) {
145
+ if (this.name == null) {
146
+ return data;
147
+ }
148
+
149
+ return [`${this.name}`, ...data];
150
+ }
135
151
  }
136
152
 
137
153
  class ConsoleStub implements Log {