@muspellheim/shared 0.9.3 → 0.11.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 +4 -4
- package/dist/shared.js +64 -56
- package/dist/shared.js.map +1 -0
- package/dist/shared.umd.cjs +2 -1
- package/dist/shared.umd.cjs.map +1 -0
- package/package.json +8 -7
- package/src/common/clock.ts +60 -0
- package/src/common/configurable_responses.ts +90 -0
- package/src/common/log.ts +17 -0
- package/src/common/mod.ts +6 -0
- package/src/common/output_tracker.ts +89 -0
- package/src/domain/messages.ts +53 -0
- package/src/domain/mod.ts +3 -0
- package/src/infrastructure/console_stub.ts +67 -0
- package/src/infrastructure/fetch_stub.ts +89 -0
- package/src/infrastructure/message_client.ts +50 -0
- package/src/infrastructure/mod.ts +7 -0
- package/src/infrastructure/sse_client.ts +162 -0
- package/src/infrastructure/web_socket_client.ts +273 -0
- package/src/mod.ts +5 -0
- package/src/vite-env.d.ts +3 -0
package/dist/shared.d.ts
CHANGED
|
@@ -323,7 +323,7 @@ export declare class SseClient extends EventTarget implements MessageClient {
|
|
|
323
323
|
private constructor();
|
|
324
324
|
get isConnected(): boolean;
|
|
325
325
|
get url(): string | undefined;
|
|
326
|
-
connect(url: string | URL, eventName?: string): Promise<void>;
|
|
326
|
+
connect(url: string | URL, eventName?: string, ...otherEvents: string[]): Promise<void>;
|
|
327
327
|
send(_message: string, _type?: string): Promise<void>;
|
|
328
328
|
close(): Promise<void>;
|
|
329
329
|
/**
|
|
@@ -333,7 +333,7 @@ export declare class SseClient extends EventTarget implements MessageClient {
|
|
|
333
333
|
* @param eventName The optional event type.
|
|
334
334
|
* @param lastEventId The optional last event ID.
|
|
335
335
|
*/
|
|
336
|
-
simulateMessage(message: string, eventName?: string, lastEventId?: string): void;
|
|
336
|
+
simulateMessage(message: string | number | boolean | object | null, eventName?: string, lastEventId?: string): void;
|
|
337
337
|
/**
|
|
338
338
|
* Simulate an error event.
|
|
339
339
|
*/
|
|
@@ -370,7 +370,7 @@ export declare class WebSocketClient extends EventTarget implements MessageClien
|
|
|
370
370
|
get isConnected(): boolean;
|
|
371
371
|
get url(): string | undefined;
|
|
372
372
|
connect(url: string | URL): Promise<void>;
|
|
373
|
-
send(message: string): Promise<void>;
|
|
373
|
+
send(message: string | ArrayBuffer | Blob | ArrayBufferView): Promise<void>;
|
|
374
374
|
/**
|
|
375
375
|
* Return a tracker for messages sent.
|
|
376
376
|
*
|
|
@@ -391,7 +391,7 @@ export declare class WebSocketClient extends EventTarget implements MessageClien
|
|
|
391
391
|
*
|
|
392
392
|
* @param message The message to receive.
|
|
393
393
|
*/
|
|
394
|
-
simulateMessage(message: string): void;
|
|
394
|
+
simulateMessage(message: string | number | boolean | object | null): void;
|
|
395
395
|
/**
|
|
396
396
|
* Simulate a heartbeat.
|
|
397
397
|
*/
|
package/dist/shared.js
CHANGED
|
@@ -47,7 +47,7 @@ class c {
|
|
|
47
47
|
return this.date().getTime();
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
|
-
class
|
|
50
|
+
class d {
|
|
51
51
|
/**
|
|
52
52
|
* Create a list of responses (by providing an array), or a single repeating
|
|
53
53
|
* response (by providing any other type). 'Name' is optional and used in
|
|
@@ -57,7 +57,7 @@ class h {
|
|
|
57
57
|
* @param name An optional name for the responses.
|
|
58
58
|
*/
|
|
59
59
|
static create(t, e) {
|
|
60
|
-
return new
|
|
60
|
+
return new d(t, e);
|
|
61
61
|
}
|
|
62
62
|
/**
|
|
63
63
|
* Convert all properties in an object into ConfigurableResponse instances.
|
|
@@ -68,9 +68,9 @@ class h {
|
|
|
68
68
|
* @param name An optional name for the responses.
|
|
69
69
|
*/
|
|
70
70
|
static mapObject(t, e) {
|
|
71
|
-
const r = Object.entries(t).map(([
|
|
72
|
-
const
|
|
73
|
-
return [
|
|
71
|
+
const r = Object.entries(t).map(([h, n]) => {
|
|
72
|
+
const l = e === void 0 ? void 0 : `${e}: ${h}`;
|
|
73
|
+
return [h, d.create(n, l)];
|
|
74
74
|
});
|
|
75
75
|
return Object.fromEntries(r);
|
|
76
76
|
}
|
|
@@ -147,10 +147,10 @@ class u {
|
|
|
147
147
|
this.#e.removeEventListener(this.#t, this.#r);
|
|
148
148
|
}
|
|
149
149
|
}
|
|
150
|
-
class
|
|
150
|
+
class m {
|
|
151
151
|
isSuccess = !0;
|
|
152
152
|
}
|
|
153
|
-
class
|
|
153
|
+
class f {
|
|
154
154
|
isSuccess = !1;
|
|
155
155
|
errorMessage;
|
|
156
156
|
/**
|
|
@@ -163,7 +163,7 @@ class S {
|
|
|
163
163
|
}
|
|
164
164
|
}
|
|
165
165
|
const i = "message";
|
|
166
|
-
class
|
|
166
|
+
class S extends EventTarget {
|
|
167
167
|
log(...t) {
|
|
168
168
|
this.dispatchEvent(
|
|
169
169
|
new CustomEvent(i, {
|
|
@@ -214,7 +214,7 @@ class f extends EventTarget {
|
|
|
214
214
|
}
|
|
215
215
|
}
|
|
216
216
|
function b(a) {
|
|
217
|
-
const t =
|
|
217
|
+
const t = d.create(a);
|
|
218
218
|
return async function() {
|
|
219
219
|
const e = t.next();
|
|
220
220
|
if (e instanceof Error)
|
|
@@ -253,14 +253,14 @@ class p {
|
|
|
253
253
|
return this.#s == null ? "" : String(this.#s);
|
|
254
254
|
}
|
|
255
255
|
}
|
|
256
|
-
class
|
|
256
|
+
class E extends EventTarget {
|
|
257
257
|
/**
|
|
258
258
|
* Create an SSE client.
|
|
259
259
|
*
|
|
260
260
|
* @return A new SSE client.
|
|
261
261
|
*/
|
|
262
262
|
static create() {
|
|
263
|
-
return new
|
|
263
|
+
return new E(EventSource);
|
|
264
264
|
}
|
|
265
265
|
/**
|
|
266
266
|
* Create a nulled SSE client.
|
|
@@ -268,7 +268,7 @@ class d extends EventTarget {
|
|
|
268
268
|
* @return A new SSE client.
|
|
269
269
|
*/
|
|
270
270
|
static createNull() {
|
|
271
|
-
return new
|
|
271
|
+
return new E(o);
|
|
272
272
|
}
|
|
273
273
|
#e;
|
|
274
274
|
#t;
|
|
@@ -281,24 +281,30 @@ class d extends EventTarget {
|
|
|
281
281
|
get url() {
|
|
282
282
|
return this.#t?.url;
|
|
283
283
|
}
|
|
284
|
-
async connect(t, e = "message") {
|
|
285
|
-
await new Promise((
|
|
284
|
+
async connect(t, e = "message", ...s) {
|
|
285
|
+
await new Promise((r, h) => {
|
|
286
286
|
if (this.isConnected) {
|
|
287
|
-
|
|
287
|
+
h(new Error("Already connected."));
|
|
288
288
|
return;
|
|
289
289
|
}
|
|
290
290
|
try {
|
|
291
291
|
this.#t = new this.#e(t), this.#t.addEventListener("open", (n) => {
|
|
292
|
-
this.#s(n),
|
|
292
|
+
this.#s(n), r();
|
|
293
293
|
}), this.#t.addEventListener(
|
|
294
294
|
e,
|
|
295
295
|
(n) => this.#r(n)
|
|
296
|
-
)
|
|
296
|
+
);
|
|
297
|
+
for (const n of s)
|
|
298
|
+
this.#t.addEventListener(
|
|
299
|
+
n,
|
|
300
|
+
(l) => this.#r(l)
|
|
301
|
+
);
|
|
302
|
+
this.#t.addEventListener(
|
|
297
303
|
"error",
|
|
298
304
|
(n) => this.#n(n)
|
|
299
305
|
);
|
|
300
306
|
} catch (n) {
|
|
301
|
-
|
|
307
|
+
h(n);
|
|
302
308
|
}
|
|
303
309
|
});
|
|
304
310
|
}
|
|
@@ -326,7 +332,7 @@ class d extends EventTarget {
|
|
|
326
332
|
* @param lastEventId The optional last event ID.
|
|
327
333
|
*/
|
|
328
334
|
simulateMessage(t, e = "message", s) {
|
|
329
|
-
this.#r(
|
|
335
|
+
typeof t != "string" && (t = JSON.stringify(t)), this.#r(
|
|
330
336
|
new MessageEvent(e, { data: t, lastEventId: s })
|
|
331
337
|
);
|
|
332
338
|
}
|
|
@@ -337,13 +343,15 @@ class d extends EventTarget {
|
|
|
337
343
|
this.#n(new Event("error"));
|
|
338
344
|
}
|
|
339
345
|
#s(t) {
|
|
340
|
-
this.dispatchEvent(new
|
|
346
|
+
this.dispatchEvent(new Event(t.type, t));
|
|
341
347
|
}
|
|
342
348
|
#r(t) {
|
|
343
|
-
this.dispatchEvent(
|
|
349
|
+
this.dispatchEvent(
|
|
350
|
+
new MessageEvent(t.type, t)
|
|
351
|
+
);
|
|
344
352
|
}
|
|
345
353
|
#n(t) {
|
|
346
|
-
this.dispatchEvent(new
|
|
354
|
+
this.dispatchEvent(new Event(t.type, t));
|
|
347
355
|
}
|
|
348
356
|
}
|
|
349
357
|
class o extends EventTarget {
|
|
@@ -363,8 +371,8 @@ class o extends EventTarget {
|
|
|
363
371
|
this.readyState = o.CLOSED;
|
|
364
372
|
}
|
|
365
373
|
}
|
|
366
|
-
const g = "heartbeat",
|
|
367
|
-
class
|
|
374
|
+
const g = "heartbeat", w = "message-sent";
|
|
375
|
+
class v extends EventTarget {
|
|
368
376
|
/**
|
|
369
377
|
* Create a WebSocket client.
|
|
370
378
|
*
|
|
@@ -375,7 +383,7 @@ class l extends EventTarget {
|
|
|
375
383
|
heartbeat: t = 3e4,
|
|
376
384
|
retry: e = 1e3
|
|
377
385
|
} = {}) {
|
|
378
|
-
return new
|
|
386
|
+
return new v(t, e, WebSocket);
|
|
379
387
|
}
|
|
380
388
|
/**
|
|
381
389
|
* Create a nulled WebSocket client.
|
|
@@ -384,10 +392,10 @@ class l extends EventTarget {
|
|
|
384
392
|
* @return A new nulled WebSocket client.
|
|
385
393
|
*/
|
|
386
394
|
static createNull({ heartbeat: t = 0, retry: e = 0 } = {}) {
|
|
387
|
-
return new
|
|
395
|
+
return new v(
|
|
388
396
|
t,
|
|
389
397
|
e,
|
|
390
|
-
|
|
398
|
+
y
|
|
391
399
|
);
|
|
392
400
|
}
|
|
393
401
|
#e;
|
|
@@ -407,17 +415,17 @@ class l extends EventTarget {
|
|
|
407
415
|
}
|
|
408
416
|
async connect(t) {
|
|
409
417
|
await new Promise((e, s) => {
|
|
410
|
-
if (this.#
|
|
418
|
+
if (this.#c(), this.isConnected) {
|
|
411
419
|
s(new Error("Already connected."));
|
|
412
420
|
return;
|
|
413
421
|
}
|
|
414
422
|
try {
|
|
415
423
|
this.#r = new this.#s(t), this.#r.addEventListener("open", (r) => {
|
|
416
|
-
this.#
|
|
424
|
+
this.#u(r), e();
|
|
417
425
|
}), this.#r.addEventListener(
|
|
418
426
|
"message",
|
|
419
427
|
(r) => this.#a(r)
|
|
420
|
-
), this.#r.addEventListener("close", (r) => this.#
|
|
428
|
+
), this.#r.addEventListener("close", (r) => this.#h(r)), this.#r.addEventListener("error", (r) => this.#o(r));
|
|
421
429
|
} catch (r) {
|
|
422
430
|
s(r);
|
|
423
431
|
}
|
|
@@ -427,7 +435,7 @@ class l extends EventTarget {
|
|
|
427
435
|
if (!this.isConnected)
|
|
428
436
|
throw new Error("Not connected.");
|
|
429
437
|
this.#r.send(t), this.dispatchEvent(
|
|
430
|
-
new CustomEvent(
|
|
438
|
+
new CustomEvent(w, { detail: t })
|
|
431
439
|
), await Promise.resolve();
|
|
432
440
|
}
|
|
433
441
|
/**
|
|
@@ -436,7 +444,7 @@ class l extends EventTarget {
|
|
|
436
444
|
* @return A new output tracker.
|
|
437
445
|
*/
|
|
438
446
|
trackMessageSent() {
|
|
439
|
-
return u.create(this,
|
|
447
|
+
return u.create(this, w);
|
|
440
448
|
}
|
|
441
449
|
/**
|
|
442
450
|
* Close the connection.
|
|
@@ -448,7 +456,7 @@ class l extends EventTarget {
|
|
|
448
456
|
*/
|
|
449
457
|
async close(t, e) {
|
|
450
458
|
await new Promise((s) => {
|
|
451
|
-
if (this.#
|
|
459
|
+
if (this.#c(), !this.isConnected) {
|
|
452
460
|
s();
|
|
453
461
|
return;
|
|
454
462
|
}
|
|
@@ -461,13 +469,13 @@ class l extends EventTarget {
|
|
|
461
469
|
* @param message The message to receive.
|
|
462
470
|
*/
|
|
463
471
|
simulateMessage(t) {
|
|
464
|
-
this.#a(new MessageEvent("message", { data: t }));
|
|
472
|
+
typeof t != "string" && (t = JSON.stringify(t)), this.#a(new MessageEvent("message", { data: t }));
|
|
465
473
|
}
|
|
466
474
|
/**
|
|
467
475
|
* Simulate a heartbeat.
|
|
468
476
|
*/
|
|
469
477
|
simulateHeartbeat() {
|
|
470
|
-
this.#
|
|
478
|
+
this.#d();
|
|
471
479
|
}
|
|
472
480
|
/**
|
|
473
481
|
* Simulate a close event.
|
|
@@ -476,28 +484,27 @@ class l extends EventTarget {
|
|
|
476
484
|
* @param reason An optional reason.
|
|
477
485
|
*/
|
|
478
486
|
simulateClose(t, e) {
|
|
479
|
-
this.#
|
|
487
|
+
this.#h(new CloseEvent("close", { code: t, reason: e }));
|
|
480
488
|
}
|
|
481
489
|
/**
|
|
482
490
|
* Simulate an error event.
|
|
483
491
|
*/
|
|
484
492
|
simulateError() {
|
|
485
|
-
this.#r?.close(), this.#
|
|
493
|
+
this.#r?.close(), this.#o(new Event("error"));
|
|
486
494
|
}
|
|
487
|
-
#
|
|
488
|
-
this.dispatchEvent(new
|
|
495
|
+
#u(t) {
|
|
496
|
+
this.dispatchEvent(new Event(t.type, t)), this.#E();
|
|
489
497
|
}
|
|
490
498
|
#a(t) {
|
|
491
499
|
this.dispatchEvent(
|
|
492
|
-
|
|
493
|
-
new t.constructor(t.type, t)
|
|
500
|
+
new MessageEvent(t.type, t)
|
|
494
501
|
);
|
|
495
502
|
}
|
|
496
|
-
#
|
|
497
|
-
this.#
|
|
503
|
+
#h(t) {
|
|
504
|
+
this.#v(), this.dispatchEvent(new CloseEvent(t.type, t));
|
|
498
505
|
}
|
|
499
|
-
#
|
|
500
|
-
this.dispatchEvent(new
|
|
506
|
+
#o(t) {
|
|
507
|
+
this.dispatchEvent(new Event(t.type, t)), this.#l();
|
|
501
508
|
}
|
|
502
509
|
#l() {
|
|
503
510
|
this.#t <= 0 || (this.#i = setInterval(
|
|
@@ -505,23 +512,23 @@ class l extends EventTarget {
|
|
|
505
512
|
this.#t
|
|
506
513
|
));
|
|
507
514
|
}
|
|
508
|
-
#
|
|
515
|
+
#c() {
|
|
509
516
|
clearInterval(this.#i), this.#i = void 0;
|
|
510
517
|
}
|
|
511
518
|
#E() {
|
|
512
519
|
this.#e <= 0 || (this.#n = setInterval(
|
|
513
|
-
() => this.#
|
|
520
|
+
() => this.#d(),
|
|
514
521
|
this.#e
|
|
515
522
|
));
|
|
516
523
|
}
|
|
517
|
-
#
|
|
524
|
+
#v() {
|
|
518
525
|
clearInterval(this.#n), this.#n = void 0;
|
|
519
526
|
}
|
|
520
|
-
#
|
|
527
|
+
#d() {
|
|
521
528
|
this.#n != null && this.send(g);
|
|
522
529
|
}
|
|
523
530
|
}
|
|
524
|
-
class
|
|
531
|
+
class y extends EventTarget {
|
|
525
532
|
url;
|
|
526
533
|
readyState = WebSocket.CONNECTING;
|
|
527
534
|
constructor(t) {
|
|
@@ -537,13 +544,14 @@ class m extends EventTarget {
|
|
|
537
544
|
}
|
|
538
545
|
export {
|
|
539
546
|
c as Clock,
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
547
|
+
d as ConfigurableResponses,
|
|
548
|
+
S as ConsoleStub,
|
|
549
|
+
f as Failure,
|
|
543
550
|
g as HEARTBEAT_TYPE,
|
|
544
551
|
u as OutputTracker,
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
552
|
+
E as SseClient,
|
|
553
|
+
m as Success,
|
|
554
|
+
v as WebSocketClient,
|
|
548
555
|
b as createFetchStub
|
|
549
556
|
};
|
|
557
|
+
//# sourceMappingURL=shared.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shared.js","sources":["../src/common/clock.ts","../src/common/configurable_responses.ts","../src/common/output_tracker.ts","../src/domain/messages.ts","../src/infrastructure/console_stub.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// 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) 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 {\n readonly isSuccess = true;\n}\n\n/**\n * A failed status.\n */\nexport class Failure {\n readonly isSuccess = false;\n errorMessage: string;\n\n /**\n * Creates a failed status.\n *\n * @param errorMessage\n */\n constructor(errorMessage: string) {\n this.errorMessage = errorMessage;\n }\n}\n","// Copyright (c) 2025 Falko Schumann. All rights reserved. MIT license.\n\nimport { OutputTracker } from \"../common/output_tracker\";\n\nconst MESSAGE_EVENT = \"message\";\n\n/**\n * A stub for the console interface.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/API/Console_API\n */\nexport class ConsoleStub extends EventTarget {\n log(...data: unknown[]) {\n this.dispatchEvent(\n new CustomEvent(MESSAGE_EVENT, {\n detail: { level: \"log\", message: data },\n }),\n );\n }\n\n error(...data: unknown[]) {\n this.dispatchEvent(\n new CustomEvent(MESSAGE_EVENT, {\n detail: { level: \"error\", message: data },\n }),\n );\n }\n\n warn(...data: unknown[]) {\n this.dispatchEvent(\n new CustomEvent(MESSAGE_EVENT, {\n detail: { level: \"warn\", message: data },\n }),\n );\n }\n\n info(...data: unknown[]) {\n this.dispatchEvent(\n new CustomEvent(MESSAGE_EVENT, {\n detail: { level: \"info\", message: data },\n }),\n );\n }\n\n debug(...data: unknown[]) {\n this.dispatchEvent(\n new CustomEvent(MESSAGE_EVENT, {\n detail: { level: \"debug\", message: data },\n }),\n );\n }\n\n trace(...data: unknown[]) {\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(this, MESSAGE_EVENT);\n }\n}\n","// Copyright (c) 2025 Falko Schumann. All rights reserved. MIT license.\n\nimport { ConfigurableResponses } from \"../common/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 \"../common/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(message: string | number | boolean | object | null) {\n if (typeof message !== \"string\") {\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","ConfigurableResponses","responses","name","responseObject","translatedEntries","key","value","translatedName","#description","#responses","response","OutputTracker","eventTarget","event","#eventTarget","#event","#data","#tracker","result","Success","Failure","errorMessage","MESSAGE_EVENT","ConsoleStub","data","createFetchStub","configurableResponses","ResponseStub","#status","#statusText","#body","status","statusText","body","json","SseClient","EventSourceStub","#eventSourceConstructor","#eventSource","eventSourceConstructor","url","eventName","otherEvents","resolve","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/dist/shared.umd.cjs
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
(function(
|
|
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 c{static create(t,e){return new c(t,e)}static mapObject(t,e){const r=Object.entries(t).map(([l,i])=>{const w=e===void 0?void 0:`${e}: ${l}`;return[l,c.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;#n;constructor(t,e){this.#e=t,this.#t=e,this.#s=[],this.#n=s=>this.#s.push(s.detail),this.#e.addEventListener(this.#t,this.#n)}get data(){return this.#s}clear(){const t=[...this.#s];return this.#s.length=0,t}stop(){this.#e.removeEventListener(this.#t,this.#n)}}class y{isSuccess=!0}class f{isSuccess=!1;errorMessage;constructor(t){this.errorMessage=t}}const h="message";class S extends EventTarget{log(...t){this.dispatchEvent(new CustomEvent(h,{detail:{level:"log",message:t}}))}error(...t){this.dispatchEvent(new CustomEvent(h,{detail:{level:"error",message:t}}))}warn(...t){this.dispatchEvent(new CustomEvent(h,{detail:{level:"warn",message:t}}))}info(...t){this.dispatchEvent(new CustomEvent(h,{detail:{level:"info",message:t}}))}debug(...t){this.dispatchEvent(new CustomEvent(h,{detail:{level:"debug",message:t}}))}trace(...t){this.dispatchEvent(new CustomEvent(h,{detail:{level:"trace",message:t}}))}trackMessages(){return new u(this,h)}}function m(o){const t=c.create(o);return async function(){const e=t.next();if(e instanceof Error)throw e;return new b(e)}}class b{#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 E extends EventTarget{static create(){return new E(EventSource)}static createNull(){return new E(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,l)=>{if(this.isConnected){l(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.#n(i));for(const i of s)this.#t.addEventListener(i,w=>this.#n(w));this.#t.addEventListener("error",i=>this.#r(i))}catch(i){l(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.#n(new MessageEvent(e,{data:t,lastEventId:s}))}simulateError(){this.#r(new Event("error"))}#s(t){this.dispatchEvent(new Event(t.type,t))}#n(t){this.dispatchEvent(new MessageEvent(t.type,t))}#r(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 g="heartbeat",p="message-sent";class v extends EventTarget{static create({heartbeat:t=3e4,retry:e=1e3}={}){return new v(t,e,WebSocket)}static createNull({heartbeat:t=0,retry:e=0}={}){return new v(t,e,C)}#e;#t;#s;#n;#r;#i;constructor(t,e,s){super(),this.#e=t,this.#t=e,this.#s=s}get isConnected(){return this.#n?.readyState===WebSocket.OPEN}get url(){return this.#n?.url}async connect(t){await new Promise((e,s)=>{if(this.#c(),this.isConnected){s(new Error("Already connected."));return}try{this.#n=new this.#s(t),this.#n.addEventListener("open",r=>{this.#d(r),e()}),this.#n.addEventListener("message",r=>this.#a(r)),this.#n.addEventListener("close",r=>this.#h(r)),this.#n.addEventListener("error",r=>this.#o(r))}catch(r){s(r)}})}async send(t){if(!this.isConnected)throw new Error("Not connected.");this.#n.send(t),this.dispatchEvent(new CustomEvent(p,{detail:t})),await Promise.resolve()}trackMessageSent(){return u.create(this,p)}async close(t,e){await new Promise(s=>{if(this.#c(),!this.isConnected){s();return}this.#n.addEventListener("close",()=>s()),this.#n.close(t,e)})}simulateMessage(t){typeof t!="string"&&(t=JSON.stringify(t)),this.#a(new MessageEvent("message",{data:t}))}simulateHeartbeat(){this.#u()}simulateClose(t,e){this.#h(new CloseEvent("close",{code:t,reason:e}))}simulateError(){this.#n?.close(),this.#o(new Event("error"))}#d(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.#l()}#l(){this.#t<=0||(this.#i=setInterval(()=>this.connect(this.#n.url),this.#t))}#c(){clearInterval(this.#i),this.#i=void 0}#E(){this.#e<=0||(this.#r=setInterval(()=>this.#u(),this.#e))}#v(){clearInterval(this.#r),this.#r=void 0}#u(){this.#r!=null&&this.send(g)}}class C 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=c,n.ConsoleStub=S,n.Failure=f,n.HEARTBEAT_TYPE=g,n.OutputTracker=u,n.SseClient=E,n.Success=y,n.WebSocketClient=v,n.createFetchStub=m,Object.defineProperty(n,Symbol.toStringTag,{value:"Module"})}));
|
|
2
|
+
//# sourceMappingURL=shared.umd.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shared.umd.cjs","sources":["../src/common/clock.ts","../src/common/configurable_responses.ts","../src/common/output_tracker.ts","../src/domain/messages.ts","../src/infrastructure/console_stub.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// 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) 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 {\n readonly isSuccess = true;\n}\n\n/**\n * A failed status.\n */\nexport class Failure {\n readonly isSuccess = false;\n errorMessage: string;\n\n /**\n * Creates a failed status.\n *\n * @param errorMessage\n */\n constructor(errorMessage: string) {\n this.errorMessage = errorMessage;\n }\n}\n","// Copyright (c) 2025 Falko Schumann. All rights reserved. MIT license.\n\nimport { OutputTracker } from \"../common/output_tracker\";\n\nconst MESSAGE_EVENT = \"message\";\n\n/**\n * A stub for the console interface.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/API/Console_API\n */\nexport class ConsoleStub extends EventTarget {\n log(...data: unknown[]) {\n this.dispatchEvent(\n new CustomEvent(MESSAGE_EVENT, {\n detail: { level: \"log\", message: data },\n }),\n );\n }\n\n error(...data: unknown[]) {\n this.dispatchEvent(\n new CustomEvent(MESSAGE_EVENT, {\n detail: { level: \"error\", message: data },\n }),\n );\n }\n\n warn(...data: unknown[]) {\n this.dispatchEvent(\n new CustomEvent(MESSAGE_EVENT, {\n detail: { level: \"warn\", message: data },\n }),\n );\n }\n\n info(...data: unknown[]) {\n this.dispatchEvent(\n new CustomEvent(MESSAGE_EVENT, {\n detail: { level: \"info\", message: data },\n }),\n );\n }\n\n debug(...data: unknown[]) {\n this.dispatchEvent(\n new CustomEvent(MESSAGE_EVENT, {\n detail: { level: \"debug\", message: data },\n }),\n );\n }\n\n trace(...data: unknown[]) {\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(this, MESSAGE_EVENT);\n }\n}\n","// Copyright (c) 2025 Falko Schumann. All rights reserved. MIT license.\n\nimport { ConfigurableResponses } from \"../common/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 \"../common/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(message: string | number | boolean | object | null) {\n if (typeof message !== \"string\") {\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","ConfigurableResponses","responses","name","responseObject","translatedEntries","key","value","translatedName","#description","#responses","response","OutputTracker","eventTarget","event","#eventTarget","#event","#data","#tracker","result","Success","Failure","errorMessage","MESSAGE_EVENT","ConsoleStub","data","createFetchStub","configurableResponses","ResponseStub","#status","#statusText","#body","status","statusText","body","json","SseClient","EventSourceStub","#eventSourceConstructor","#eventSource","eventSourceConstructor","url","eventName","otherEvents","resolve","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.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"author": "Falko Schumann",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"engines": {
|
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
},
|
|
9
9
|
"type": "module",
|
|
10
10
|
"files": [
|
|
11
|
-
"dist"
|
|
11
|
+
"dist",
|
|
12
|
+
"src"
|
|
12
13
|
],
|
|
13
14
|
"main": "./dist/shared.umd.cjs",
|
|
14
15
|
"module": "./dist/shared.js",
|
|
@@ -24,18 +25,18 @@
|
|
|
24
25
|
"test": "vitest"
|
|
25
26
|
},
|
|
26
27
|
"devDependencies": {
|
|
27
|
-
"@eslint/js": "9.
|
|
28
|
-
"@types/node": "22.18.
|
|
28
|
+
"@eslint/js": "9.36.0",
|
|
29
|
+
"@types/node": "22.18.6",
|
|
29
30
|
"@vitest/coverage-v8": "3.2.4",
|
|
30
|
-
"eslint": "9.
|
|
31
|
+
"eslint": "9.36.0",
|
|
31
32
|
"eslint-plugin-headers": "1.3.3",
|
|
32
33
|
"jsdom": "27.0.0",
|
|
33
34
|
"globals": "16.4.0",
|
|
34
35
|
"prettier": "3.6.2",
|
|
35
36
|
"typedoc": "0.28.13",
|
|
36
37
|
"typescript": "5.9.2",
|
|
37
|
-
"typescript-eslint": "8.
|
|
38
|
-
"vite": "7.1.
|
|
38
|
+
"typescript-eslint": "8.44.0",
|
|
39
|
+
"vite": "7.1.6",
|
|
39
40
|
"vitest": "3.2.4",
|
|
40
41
|
"vite-plugin-dts": "4.5.4"
|
|
41
42
|
}
|