@muspellheim/shared 0.16.0 → 0.18.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/mod.js +524 -0
- package/dist/types/common/clock.d.ts +40 -0
- package/dist/types/common/event_tracker.d.ts +44 -0
- package/dist/types/common/log.d.ts +17 -0
- package/{src/common/mod.ts → dist/types/common/mod.d.ts} +0 -2
- package/{src/domain/messages.ts → dist/types/domain/messages.d.ts} +13 -24
- package/dist/types/domain/mod.d.ts +1 -0
- package/dist/types/infrastructure/configurable_responses.d.ts +58 -0
- package/dist/types/infrastructure/console_log.d.ts +34 -0
- package/dist/types/infrastructure/fetch_stub.d.ts +20 -0
- package/dist/types/infrastructure/message_client.d.ts +44 -0
- package/{src/infrastructure/mod.ts → dist/types/infrastructure/mod.d.ts} +0 -2
- package/dist/types/infrastructure/output_tracker.d.ts +60 -0
- package/dist/types/infrastructure/sse_client.d.ts +37 -0
- package/dist/types/infrastructure/web_socket_client.d.ts +79 -0
- package/{src/mod.ts → dist/types/mod.d.ts} +0 -2
- package/package.json +13 -23
- package/dist/shared.d.ts +0 -489
- package/dist/shared.js +0 -666
- package/dist/shared.js.map +0 -1
- package/dist/shared.umd.cjs +0 -2
- package/dist/shared.umd.cjs.map +0 -1
- package/src/common/clock.ts +0 -60
- package/src/common/event_tracker.ts +0 -94
- package/src/common/log.ts +0 -27
- package/src/domain/mod.ts +0 -3
- package/src/infrastructure/configurable_responses.ts +0 -90
- package/src/infrastructure/console_log.ts +0 -144
- package/src/infrastructure/fetch_stub.ts +0 -89
- package/src/infrastructure/message_client.ts +0 -50
- package/src/infrastructure/output_tracker.ts +0 -89
- package/src/infrastructure/sse_client.ts +0 -162
- package/src/infrastructure/web_socket_client.ts +0 -279
- package/src/vite-env.d.ts +0 -3
package/dist/mod.js
ADDED
|
@@ -0,0 +1,524 @@
|
|
|
1
|
+
// src/common/clock.ts
|
|
2
|
+
class Clock {
|
|
3
|
+
static system() {
|
|
4
|
+
return new Clock;
|
|
5
|
+
}
|
|
6
|
+
static fixed(date) {
|
|
7
|
+
return new Clock(new Date(date));
|
|
8
|
+
}
|
|
9
|
+
static offset(clock, offsetMillis) {
|
|
10
|
+
return new Clock(new Date(clock.millis() + offsetMillis));
|
|
11
|
+
}
|
|
12
|
+
#date;
|
|
13
|
+
constructor(date) {
|
|
14
|
+
this.#date = date;
|
|
15
|
+
}
|
|
16
|
+
date() {
|
|
17
|
+
return this.#date ? new Date(this.#date) : new Date;
|
|
18
|
+
}
|
|
19
|
+
millis() {
|
|
20
|
+
return this.date().getTime();
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
// src/common/event_tracker.ts
|
|
24
|
+
class EventTracker {
|
|
25
|
+
static create(eventTarget, ...event) {
|
|
26
|
+
return new EventTracker(eventTarget, event);
|
|
27
|
+
}
|
|
28
|
+
#eventTarget;
|
|
29
|
+
#event;
|
|
30
|
+
#events;
|
|
31
|
+
#tracker;
|
|
32
|
+
constructor(eventTarget, event) {
|
|
33
|
+
this.#eventTarget = eventTarget;
|
|
34
|
+
this.#event = event;
|
|
35
|
+
this.#events = [];
|
|
36
|
+
this.#tracker = (event2) => this.#events.push(event2);
|
|
37
|
+
this.#event.forEach((event2) => this.#eventTarget.addEventListener(event2, this.#tracker));
|
|
38
|
+
}
|
|
39
|
+
get events() {
|
|
40
|
+
return this.#events;
|
|
41
|
+
}
|
|
42
|
+
clear() {
|
|
43
|
+
const result = [...this.#events];
|
|
44
|
+
this.#events.length = 0;
|
|
45
|
+
return result;
|
|
46
|
+
}
|
|
47
|
+
stop() {
|
|
48
|
+
this.#event.forEach((event) => this.#eventTarget.removeEventListener(event, this.#tracker));
|
|
49
|
+
}
|
|
50
|
+
async waitFor(count = 1) {
|
|
51
|
+
return new Promise((resolve) => {
|
|
52
|
+
const checkEvents = () => {
|
|
53
|
+
if (this.#events.length >= count) {
|
|
54
|
+
this.#event.forEach((event) => this.#eventTarget.removeEventListener(event, checkEvents));
|
|
55
|
+
resolve(this.events);
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
this.#event.forEach((event) => this.#eventTarget.addEventListener(event, checkEvents));
|
|
59
|
+
checkEvents();
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
// src/domain/messages.ts
|
|
64
|
+
class Success {
|
|
65
|
+
isSuccess = true;
|
|
66
|
+
result;
|
|
67
|
+
constructor(result) {
|
|
68
|
+
this.result = result;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
class Failure {
|
|
73
|
+
isSuccess = false;
|
|
74
|
+
errorMessage;
|
|
75
|
+
constructor(errorMessage) {
|
|
76
|
+
this.errorMessage = errorMessage;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
// src/infrastructure/configurable_responses.ts
|
|
80
|
+
class ConfigurableResponses {
|
|
81
|
+
static create(responses, name) {
|
|
82
|
+
return new ConfigurableResponses(responses, name);
|
|
83
|
+
}
|
|
84
|
+
static mapObject(responseObject, name) {
|
|
85
|
+
const entries = Object.entries(responseObject);
|
|
86
|
+
const translatedEntries = entries.map(([key, value]) => {
|
|
87
|
+
const translatedName = name === undefined ? undefined : `${name}: ${key}`;
|
|
88
|
+
return [key, ConfigurableResponses.create(value, translatedName)];
|
|
89
|
+
});
|
|
90
|
+
return Object.fromEntries(translatedEntries);
|
|
91
|
+
}
|
|
92
|
+
#description;
|
|
93
|
+
#responses;
|
|
94
|
+
constructor(responses, name) {
|
|
95
|
+
this.#description = name == null ? "" : ` in ${name}`;
|
|
96
|
+
this.#responses = Array.isArray(responses) ? [...responses] : responses;
|
|
97
|
+
}
|
|
98
|
+
next() {
|
|
99
|
+
const response = Array.isArray(this.#responses) ? this.#responses.shift() : this.#responses;
|
|
100
|
+
if (response === undefined) {
|
|
101
|
+
throw new Error(`No more responses configured${this.#description}.`);
|
|
102
|
+
}
|
|
103
|
+
return response;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
// src/infrastructure/output_tracker.ts
|
|
107
|
+
class OutputTracker {
|
|
108
|
+
static create(eventTarget, event) {
|
|
109
|
+
return new OutputTracker(eventTarget, event);
|
|
110
|
+
}
|
|
111
|
+
#eventTarget;
|
|
112
|
+
#event;
|
|
113
|
+
#data;
|
|
114
|
+
#tracker;
|
|
115
|
+
constructor(eventTarget, event) {
|
|
116
|
+
this.#eventTarget = eventTarget;
|
|
117
|
+
this.#event = event;
|
|
118
|
+
this.#data = [];
|
|
119
|
+
this.#tracker = (event2) => this.#data.push(event2.detail);
|
|
120
|
+
this.#eventTarget.addEventListener(this.#event, this.#tracker);
|
|
121
|
+
}
|
|
122
|
+
get data() {
|
|
123
|
+
return this.#data;
|
|
124
|
+
}
|
|
125
|
+
clear() {
|
|
126
|
+
const result = [...this.#data];
|
|
127
|
+
this.#data.length = 0;
|
|
128
|
+
return result;
|
|
129
|
+
}
|
|
130
|
+
stop() {
|
|
131
|
+
this.#eventTarget.removeEventListener(this.#event, this.#tracker);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// src/infrastructure/console_log.ts
|
|
136
|
+
var MESSAGE_EVENT = "message";
|
|
137
|
+
|
|
138
|
+
class ConsoleLog extends EventTarget {
|
|
139
|
+
static create({ name } = {}) {
|
|
140
|
+
return new ConsoleLog(globalThis.console, name);
|
|
141
|
+
}
|
|
142
|
+
static createNull({ name } = {}) {
|
|
143
|
+
return new ConsoleLog(new ConsoleStub, name);
|
|
144
|
+
}
|
|
145
|
+
name;
|
|
146
|
+
level = "info";
|
|
147
|
+
#console;
|
|
148
|
+
constructor(console, name) {
|
|
149
|
+
super();
|
|
150
|
+
this.name = name;
|
|
151
|
+
this.#console = console;
|
|
152
|
+
}
|
|
153
|
+
log(...data) {
|
|
154
|
+
if (!this.isLoggable("log")) {
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
data = this.#applyName(data);
|
|
158
|
+
this.#console.log(...data);
|
|
159
|
+
this.dispatchEvent(new CustomEvent(MESSAGE_EVENT, {
|
|
160
|
+
detail: { level: "log", message: data }
|
|
161
|
+
}));
|
|
162
|
+
}
|
|
163
|
+
error(...data) {
|
|
164
|
+
if (!this.isLoggable("error")) {
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
data = this.#applyName(data);
|
|
168
|
+
this.#console.error(...data);
|
|
169
|
+
this.dispatchEvent(new CustomEvent(MESSAGE_EVENT, {
|
|
170
|
+
detail: { level: "error", message: data }
|
|
171
|
+
}));
|
|
172
|
+
}
|
|
173
|
+
warn(...data) {
|
|
174
|
+
if (!this.isLoggable("warn")) {
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
data = this.#applyName(data);
|
|
178
|
+
this.#console.warn(...data);
|
|
179
|
+
this.dispatchEvent(new CustomEvent(MESSAGE_EVENT, {
|
|
180
|
+
detail: { level: "warn", message: data }
|
|
181
|
+
}));
|
|
182
|
+
}
|
|
183
|
+
info(...data) {
|
|
184
|
+
if (!this.isLoggable("info")) {
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
data = this.#applyName(data);
|
|
188
|
+
this.#console.info(...data);
|
|
189
|
+
this.dispatchEvent(new CustomEvent(MESSAGE_EVENT, {
|
|
190
|
+
detail: { level: "info", message: data }
|
|
191
|
+
}));
|
|
192
|
+
}
|
|
193
|
+
debug(...data) {
|
|
194
|
+
if (!this.isLoggable("debug")) {
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
data = this.#applyName(data);
|
|
198
|
+
this.#console.debug(...data);
|
|
199
|
+
this.dispatchEvent(new CustomEvent(MESSAGE_EVENT, {
|
|
200
|
+
detail: { level: "debug", message: data }
|
|
201
|
+
}));
|
|
202
|
+
}
|
|
203
|
+
trace(...data) {
|
|
204
|
+
if (!this.isLoggable("trace")) {
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
data = this.#applyName(data);
|
|
208
|
+
this.#console.trace(...data);
|
|
209
|
+
this.dispatchEvent(new CustomEvent(MESSAGE_EVENT, {
|
|
210
|
+
detail: { level: "trace", message: data }
|
|
211
|
+
}));
|
|
212
|
+
}
|
|
213
|
+
trackMessages() {
|
|
214
|
+
return new OutputTracker(this, MESSAGE_EVENT);
|
|
215
|
+
}
|
|
216
|
+
isLoggable(level) {
|
|
217
|
+
const normalize = (level2) => level2 === "log" ? "info" : level2;
|
|
218
|
+
const levels = [
|
|
219
|
+
"off",
|
|
220
|
+
"error",
|
|
221
|
+
"warn",
|
|
222
|
+
"info",
|
|
223
|
+
"debug",
|
|
224
|
+
"trace"
|
|
225
|
+
];
|
|
226
|
+
const currentLevelIndex = levels.indexOf(normalize(this.level));
|
|
227
|
+
const messageLevelIndex = levels.indexOf(normalize(level));
|
|
228
|
+
return messageLevelIndex <= currentLevelIndex;
|
|
229
|
+
}
|
|
230
|
+
#applyName(data) {
|
|
231
|
+
if (this.name == null) {
|
|
232
|
+
return data;
|
|
233
|
+
}
|
|
234
|
+
return [`${this.name}`, ...data];
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
class ConsoleStub {
|
|
239
|
+
log(..._data) {}
|
|
240
|
+
error(..._data) {}
|
|
241
|
+
warn(..._data) {}
|
|
242
|
+
info(..._data) {}
|
|
243
|
+
debug(..._data) {}
|
|
244
|
+
trace(..._data) {}
|
|
245
|
+
}
|
|
246
|
+
// src/infrastructure/fetch_stub.ts
|
|
247
|
+
function createFetchStub(responses) {
|
|
248
|
+
const configurableResponses = ConfigurableResponses.create(responses);
|
|
249
|
+
return async function() {
|
|
250
|
+
const response = configurableResponses.next();
|
|
251
|
+
if (response instanceof Error) {
|
|
252
|
+
throw response;
|
|
253
|
+
}
|
|
254
|
+
let body = response?.body;
|
|
255
|
+
if (body != null && !(body instanceof Blob) && !(typeof body === "string")) {
|
|
256
|
+
body = JSON.stringify(body);
|
|
257
|
+
}
|
|
258
|
+
return new Response(body, {
|
|
259
|
+
status: response.status,
|
|
260
|
+
statusText: response.statusText
|
|
261
|
+
});
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
// src/infrastructure/sse_client.ts
|
|
265
|
+
class SseClient extends EventTarget {
|
|
266
|
+
static create() {
|
|
267
|
+
return new SseClient(EventSource);
|
|
268
|
+
}
|
|
269
|
+
static createNull() {
|
|
270
|
+
return new SseClient(EventSourceStub);
|
|
271
|
+
}
|
|
272
|
+
#eventSourceConstructor;
|
|
273
|
+
#eventSource;
|
|
274
|
+
constructor(eventSourceConstructor) {
|
|
275
|
+
super();
|
|
276
|
+
this.#eventSourceConstructor = eventSourceConstructor;
|
|
277
|
+
}
|
|
278
|
+
get isConnected() {
|
|
279
|
+
return this.#eventSource?.readyState === this.#eventSourceConstructor.OPEN;
|
|
280
|
+
}
|
|
281
|
+
get url() {
|
|
282
|
+
return this.#eventSource?.url;
|
|
283
|
+
}
|
|
284
|
+
async connect(url, eventName = "message", ...otherEvents) {
|
|
285
|
+
await new Promise((resolve, reject) => {
|
|
286
|
+
if (this.isConnected) {
|
|
287
|
+
reject(new Error("Already connected."));
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
try {
|
|
291
|
+
this.#eventSource = new this.#eventSourceConstructor(url);
|
|
292
|
+
this.#eventSource.addEventListener("open", (e) => {
|
|
293
|
+
this.#handleOpen(e);
|
|
294
|
+
resolve();
|
|
295
|
+
});
|
|
296
|
+
this.#eventSource.addEventListener(eventName, (e) => this.#handleMessage(e));
|
|
297
|
+
for (const otherEvent of otherEvents) {
|
|
298
|
+
this.#eventSource.addEventListener(otherEvent, (e) => this.#handleMessage(e));
|
|
299
|
+
}
|
|
300
|
+
this.#eventSource.addEventListener("error", (e) => this.#handleError(e));
|
|
301
|
+
} catch (error) {
|
|
302
|
+
reject(error);
|
|
303
|
+
}
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
async send(_message, _type) {
|
|
307
|
+
throw new Error("unsupported method");
|
|
308
|
+
}
|
|
309
|
+
async close() {
|
|
310
|
+
await new Promise((resolve, reject) => {
|
|
311
|
+
if (!this.isConnected) {
|
|
312
|
+
resolve();
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
try {
|
|
316
|
+
this.#eventSource.close();
|
|
317
|
+
resolve();
|
|
318
|
+
} catch (error) {
|
|
319
|
+
reject(error);
|
|
320
|
+
}
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
simulateMessage(message, eventName = "message", lastEventId) {
|
|
324
|
+
if (typeof message !== "string") {
|
|
325
|
+
message = JSON.stringify(message);
|
|
326
|
+
}
|
|
327
|
+
this.#handleMessage(new MessageEvent(eventName, { data: message, lastEventId }));
|
|
328
|
+
}
|
|
329
|
+
simulateError() {
|
|
330
|
+
this.#handleError(new Event("error"));
|
|
331
|
+
}
|
|
332
|
+
#handleOpen(event) {
|
|
333
|
+
this.dispatchEvent(new Event(event.type, event));
|
|
334
|
+
}
|
|
335
|
+
#handleMessage(event) {
|
|
336
|
+
this.dispatchEvent(new MessageEvent(event.type, event));
|
|
337
|
+
}
|
|
338
|
+
#handleError(event) {
|
|
339
|
+
this.dispatchEvent(new Event(event.type, event));
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
class EventSourceStub extends EventTarget {
|
|
344
|
+
static CONNECTING = 0;
|
|
345
|
+
static OPEN = 1;
|
|
346
|
+
static CLOSED = 2;
|
|
347
|
+
url;
|
|
348
|
+
readyState = EventSourceStub.CONNECTING;
|
|
349
|
+
constructor(url) {
|
|
350
|
+
super();
|
|
351
|
+
this.url = url.toString();
|
|
352
|
+
setTimeout(() => {
|
|
353
|
+
this.readyState = EventSourceStub.OPEN;
|
|
354
|
+
this.dispatchEvent(new Event("open"));
|
|
355
|
+
}, 0);
|
|
356
|
+
}
|
|
357
|
+
close() {
|
|
358
|
+
this.readyState = EventSourceStub.CLOSED;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
// src/infrastructure/web_socket_client.ts
|
|
362
|
+
var HEARTBEAT_TYPE = "heartbeat";
|
|
363
|
+
var MESSAGE_SENT_EVENT = "message-sent";
|
|
364
|
+
|
|
365
|
+
class WebSocketClient extends EventTarget {
|
|
366
|
+
static create({
|
|
367
|
+
heartbeat = 30000,
|
|
368
|
+
retry = 1000
|
|
369
|
+
} = {}) {
|
|
370
|
+
return new WebSocketClient(heartbeat, retry, WebSocket);
|
|
371
|
+
}
|
|
372
|
+
static createNull({ heartbeat = 0, retry = 0 } = {}) {
|
|
373
|
+
return new WebSocketClient(heartbeat, retry, WebSocketStub);
|
|
374
|
+
}
|
|
375
|
+
#heartbeat;
|
|
376
|
+
#retry;
|
|
377
|
+
#webSocketConstructor;
|
|
378
|
+
#webSocket;
|
|
379
|
+
#heartbeatId;
|
|
380
|
+
#retryId;
|
|
381
|
+
constructor(heartbeat, retry, webSocketConstructor) {
|
|
382
|
+
super();
|
|
383
|
+
this.#heartbeat = heartbeat;
|
|
384
|
+
this.#retry = retry;
|
|
385
|
+
this.#webSocketConstructor = webSocketConstructor;
|
|
386
|
+
}
|
|
387
|
+
get isConnected() {
|
|
388
|
+
return this.#webSocket?.readyState === WebSocket.OPEN;
|
|
389
|
+
}
|
|
390
|
+
get url() {
|
|
391
|
+
return this.#webSocket?.url;
|
|
392
|
+
}
|
|
393
|
+
async connect(url) {
|
|
394
|
+
await new Promise((resolve, reject) => {
|
|
395
|
+
this.#stopRetry();
|
|
396
|
+
if (this.isConnected) {
|
|
397
|
+
reject(new Error("Already connected."));
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
try {
|
|
401
|
+
this.#webSocket = new this.#webSocketConstructor(url);
|
|
402
|
+
this.#webSocket.addEventListener("open", (e) => {
|
|
403
|
+
this.#handleOpen(e);
|
|
404
|
+
resolve();
|
|
405
|
+
});
|
|
406
|
+
this.#webSocket.addEventListener("message", (e) => this.#handleMessage(e));
|
|
407
|
+
this.#webSocket.addEventListener("close", (e) => this.#handleClose(e));
|
|
408
|
+
this.#webSocket.addEventListener("error", (e) => this.#handleError(e));
|
|
409
|
+
} catch (error) {
|
|
410
|
+
reject(error);
|
|
411
|
+
}
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
async send(message) {
|
|
415
|
+
if (!this.isConnected) {
|
|
416
|
+
throw new Error("Not connected.");
|
|
417
|
+
}
|
|
418
|
+
this.#webSocket.send(message);
|
|
419
|
+
this.dispatchEvent(new CustomEvent(MESSAGE_SENT_EVENT, { detail: message }));
|
|
420
|
+
await Promise.resolve();
|
|
421
|
+
}
|
|
422
|
+
trackMessageSent() {
|
|
423
|
+
return OutputTracker.create(this, MESSAGE_SENT_EVENT);
|
|
424
|
+
}
|
|
425
|
+
async close(code, reason) {
|
|
426
|
+
await new Promise((resolve) => {
|
|
427
|
+
this.#stopRetry();
|
|
428
|
+
if (!this.isConnected) {
|
|
429
|
+
resolve();
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
this.#webSocket.addEventListener("close", () => resolve());
|
|
433
|
+
this.#webSocket.close(code, reason);
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
simulateMessage(message) {
|
|
437
|
+
if (typeof message !== "string" && !(message instanceof Blob) && !(message instanceof ArrayBuffer)) {
|
|
438
|
+
message = JSON.stringify(message);
|
|
439
|
+
}
|
|
440
|
+
this.#handleMessage(new MessageEvent("message", { data: message }));
|
|
441
|
+
}
|
|
442
|
+
simulateHeartbeat() {
|
|
443
|
+
this.#sendHeartbeat();
|
|
444
|
+
}
|
|
445
|
+
simulateClose(code, reason) {
|
|
446
|
+
this.#handleClose(new CloseEvent("close", { code, reason }));
|
|
447
|
+
}
|
|
448
|
+
simulateError() {
|
|
449
|
+
this.#webSocket?.close();
|
|
450
|
+
this.#handleError(new Event("error"));
|
|
451
|
+
}
|
|
452
|
+
#handleOpen(event) {
|
|
453
|
+
this.dispatchEvent(new Event(event.type, event));
|
|
454
|
+
this.#startHeartbeat();
|
|
455
|
+
}
|
|
456
|
+
#handleMessage(event) {
|
|
457
|
+
this.dispatchEvent(new MessageEvent(event.type, event));
|
|
458
|
+
}
|
|
459
|
+
#handleClose(event) {
|
|
460
|
+
this.#stopHeartbeat();
|
|
461
|
+
this.dispatchEvent(new CloseEvent(event.type, event));
|
|
462
|
+
}
|
|
463
|
+
#handleError(event) {
|
|
464
|
+
this.dispatchEvent(new Event(event.type, event));
|
|
465
|
+
this.#startRetry();
|
|
466
|
+
}
|
|
467
|
+
#startRetry() {
|
|
468
|
+
if (this.#retry <= 0) {
|
|
469
|
+
return;
|
|
470
|
+
}
|
|
471
|
+
this.#retryId = setInterval(() => this.connect(this.#webSocket.url), this.#retry);
|
|
472
|
+
}
|
|
473
|
+
#stopRetry() {
|
|
474
|
+
clearInterval(this.#retryId);
|
|
475
|
+
this.#retryId = undefined;
|
|
476
|
+
}
|
|
477
|
+
#startHeartbeat() {
|
|
478
|
+
if (this.#heartbeat <= 0) {
|
|
479
|
+
return;
|
|
480
|
+
}
|
|
481
|
+
this.#heartbeatId = setInterval(() => this.#sendHeartbeat(), this.#heartbeat);
|
|
482
|
+
}
|
|
483
|
+
#stopHeartbeat() {
|
|
484
|
+
clearInterval(this.#heartbeatId);
|
|
485
|
+
this.#heartbeatId = undefined;
|
|
486
|
+
}
|
|
487
|
+
#sendHeartbeat() {
|
|
488
|
+
if (this.#heartbeatId == null) {
|
|
489
|
+
return;
|
|
490
|
+
}
|
|
491
|
+
this.send(HEARTBEAT_TYPE);
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
class WebSocketStub extends EventTarget {
|
|
496
|
+
url;
|
|
497
|
+
readyState = WebSocket.CONNECTING;
|
|
498
|
+
constructor(url) {
|
|
499
|
+
super();
|
|
500
|
+
this.url = url.toString();
|
|
501
|
+
setTimeout(() => {
|
|
502
|
+
this.readyState = WebSocket.OPEN;
|
|
503
|
+
this.dispatchEvent(new Event("open"));
|
|
504
|
+
}, 0);
|
|
505
|
+
}
|
|
506
|
+
send() {}
|
|
507
|
+
close() {
|
|
508
|
+
this.readyState = WebSocket.CLOSED;
|
|
509
|
+
this.dispatchEvent(new Event("close"));
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
export {
|
|
513
|
+
createFetchStub,
|
|
514
|
+
WebSocketClient,
|
|
515
|
+
Success,
|
|
516
|
+
SseClient,
|
|
517
|
+
OutputTracker,
|
|
518
|
+
HEARTBEAT_TYPE,
|
|
519
|
+
Failure,
|
|
520
|
+
EventTracker,
|
|
521
|
+
ConsoleLog,
|
|
522
|
+
ConfigurableResponses,
|
|
523
|
+
Clock
|
|
524
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A clock provides access to the current timestamp.
|
|
3
|
+
*/
|
|
4
|
+
export declare class Clock {
|
|
5
|
+
#private;
|
|
6
|
+
/**
|
|
7
|
+
* Create a clock using system the clock.
|
|
8
|
+
*
|
|
9
|
+
* @return A clock that uses the system clock.
|
|
10
|
+
*/
|
|
11
|
+
static system(): Clock;
|
|
12
|
+
/**
|
|
13
|
+
* Create a clock using a fixed date.
|
|
14
|
+
*
|
|
15
|
+
* @param date The fixed date of the clock.
|
|
16
|
+
* @return A clock that always returns a fixed date.
|
|
17
|
+
*/
|
|
18
|
+
static fixed(date: Date | string | number): Clock;
|
|
19
|
+
/**
|
|
20
|
+
* Create a clock that returns a fixed offset from the given clock.
|
|
21
|
+
*
|
|
22
|
+
* @param clock The clock to offset from.
|
|
23
|
+
* @param offsetMillis The offset in milliseconds.
|
|
24
|
+
* @return A clock that returns a fixed offset from the given clock.
|
|
25
|
+
*/
|
|
26
|
+
static offset(clock: Clock, offsetMillis: number): Clock;
|
|
27
|
+
private constructor();
|
|
28
|
+
/**
|
|
29
|
+
* Return the current timestamp of the clock.
|
|
30
|
+
*
|
|
31
|
+
* @return The current timestamp.
|
|
32
|
+
*/
|
|
33
|
+
date(): Date;
|
|
34
|
+
/**
|
|
35
|
+
* Return the current timestamp of the clock in milliseconds.
|
|
36
|
+
*
|
|
37
|
+
* @return The current timestamp in milliseconds.
|
|
38
|
+
*/
|
|
39
|
+
millis(): number;
|
|
40
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Track events from an event target.
|
|
3
|
+
*
|
|
4
|
+
* Wait asynchronously for events. Useful in test code.
|
|
5
|
+
*/
|
|
6
|
+
export declare class EventTracker<T extends Event> {
|
|
7
|
+
#private;
|
|
8
|
+
/**
|
|
9
|
+
* Create a tracker for a specific event of an event target.
|
|
10
|
+
*
|
|
11
|
+
* @param eventTarget The target to track.
|
|
12
|
+
* @param event The event name to track.
|
|
13
|
+
*/
|
|
14
|
+
static create<T extends Event>(eventTarget: EventTarget, ...event: string[]): EventTracker<T>;
|
|
15
|
+
/**
|
|
16
|
+
* Create a tracker for a specific event of an event target.
|
|
17
|
+
*
|
|
18
|
+
* @param eventTarget The target to track.
|
|
19
|
+
* @param event The event name to track.
|
|
20
|
+
*/
|
|
21
|
+
constructor(eventTarget: EventTarget, event: string[]);
|
|
22
|
+
/**
|
|
23
|
+
* Return the tracked events.
|
|
24
|
+
*
|
|
25
|
+
* @return The tracked events.
|
|
26
|
+
*/
|
|
27
|
+
get events(): T[];
|
|
28
|
+
/**
|
|
29
|
+
* Clear the tracked events and return the cleared events.
|
|
30
|
+
*
|
|
31
|
+
* @return The cleared events.
|
|
32
|
+
*/
|
|
33
|
+
clear(): T[];
|
|
34
|
+
/**
|
|
35
|
+
* Stop tracking.
|
|
36
|
+
*/
|
|
37
|
+
stop(): void;
|
|
38
|
+
/**
|
|
39
|
+
* Wait asynchronously for a number of events.
|
|
40
|
+
*
|
|
41
|
+
* @param count number of events, default 1.
|
|
42
|
+
*/
|
|
43
|
+
waitFor(count?: number): Promise<T[]>;
|
|
44
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/** Defines level for setting a log level in a `Log` implementation. */
|
|
2
|
+
export type LogLevel = "log" | "error" | "warn" | "info" | "debug" | "trace" | "off";
|
|
3
|
+
/**
|
|
4
|
+
* A simple logging facade.
|
|
5
|
+
*
|
|
6
|
+
* This is a subset of the `Console` interface.
|
|
7
|
+
*
|
|
8
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/API/Console_API
|
|
9
|
+
*/
|
|
10
|
+
export interface Log {
|
|
11
|
+
log(...data: unknown[]): void;
|
|
12
|
+
error(...data: unknown[]): void;
|
|
13
|
+
warn(...data: unknown[]): void;
|
|
14
|
+
info(...data: unknown[]): void;
|
|
15
|
+
debug(...data: unknown[]): void;
|
|
16
|
+
trace(...data: unknown[]): void;
|
|
17
|
+
}
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
// Copyright (c) 2025 Falko Schumann. All rights reserved. MIT license.
|
|
2
|
-
|
|
3
1
|
/**
|
|
4
2
|
* Provides CQNS features.
|
|
5
3
|
*
|
|
@@ -22,37 +20,28 @@
|
|
|
22
20
|
* @see https://ralfw.de/command-query-notification-separation-cqns/
|
|
23
21
|
* @module
|
|
24
22
|
*/
|
|
25
|
-
|
|
26
23
|
/**
|
|
27
24
|
* The status returned by a command handler.
|
|
28
25
|
*/
|
|
29
26
|
export type CommandStatus = Success | Failure;
|
|
30
|
-
|
|
31
27
|
/**
|
|
32
28
|
* A successful status.
|
|
33
29
|
*/
|
|
34
|
-
export class Success<T = unknown> {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
constructor(result?: T) {
|
|
39
|
-
this.result = result;
|
|
40
|
-
}
|
|
30
|
+
export declare class Success<T = unknown> {
|
|
31
|
+
readonly isSuccess = true;
|
|
32
|
+
readonly result?: T;
|
|
33
|
+
constructor(result?: T);
|
|
41
34
|
}
|
|
42
|
-
|
|
43
35
|
/**
|
|
44
36
|
* A failed status.
|
|
45
37
|
*/
|
|
46
|
-
export class Failure<T = string> {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
constructor(errorMessage: T) {
|
|
56
|
-
this.errorMessage = errorMessage;
|
|
57
|
-
}
|
|
38
|
+
export declare class Failure<T = string> {
|
|
39
|
+
readonly isSuccess = false;
|
|
40
|
+
readonly errorMessage: T;
|
|
41
|
+
/**
|
|
42
|
+
* Creates a failed status.
|
|
43
|
+
*
|
|
44
|
+
* @param errorMessage
|
|
45
|
+
*/
|
|
46
|
+
constructor(errorMessage: T);
|
|
58
47
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./messages";
|