@muspellheim/shared 0.6.1 → 0.7.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.
Files changed (46) hide show
  1. package/LICENSE.txt +1 -1
  2. package/README.md +11 -13
  3. package/dist/shared.d.ts +423 -0
  4. package/dist/shared.js +535 -0
  5. package/dist/shared.umd.cjs +1 -0
  6. package/package.json +27 -23
  7. package/.prettierignore +0 -3
  8. package/.prettierrc +0 -5
  9. package/deno.json +0 -15
  10. package/deno.mk +0 -68
  11. package/eslint.config.js +0 -23
  12. package/lib/assert.js +0 -15
  13. package/lib/browser/components.js +0 -165
  14. package/lib/browser/index.js +0 -3
  15. package/lib/color.js +0 -137
  16. package/lib/configurable-responses.js +0 -69
  17. package/lib/feature-toggle.js +0 -9
  18. package/lib/health.js +0 -510
  19. package/lib/index.js +0 -23
  20. package/lib/lang.js +0 -100
  21. package/lib/logging.js +0 -599
  22. package/lib/long-polling-client.js +0 -186
  23. package/lib/message-client.js +0 -68
  24. package/lib/messages.js +0 -68
  25. package/lib/metrics.js +0 -120
  26. package/lib/node/actuator-controller.js +0 -102
  27. package/lib/node/configuration-properties.js +0 -291
  28. package/lib/node/handler.js +0 -25
  29. package/lib/node/index.js +0 -9
  30. package/lib/node/logging.js +0 -60
  31. package/lib/node/long-polling.js +0 -83
  32. package/lib/node/sse-emitter.js +0 -104
  33. package/lib/node/static-files-controller.js +0 -15
  34. package/lib/output-tracker.js +0 -89
  35. package/lib/service-locator.js +0 -44
  36. package/lib/sse-client.js +0 -163
  37. package/lib/stop-watch.js +0 -54
  38. package/lib/store.js +0 -129
  39. package/lib/time.js +0 -445
  40. package/lib/util.js +0 -380
  41. package/lib/validation.js +0 -290
  42. package/lib/vector.js +0 -194
  43. package/lib/vitest/equality-testers.js +0 -19
  44. package/lib/vitest/index.js +0 -1
  45. package/lib/web-socket-client.js +0 -262
  46. package/tsconfig.json +0 -13
package/lib/util.js DELETED
@@ -1,380 +0,0 @@
1
- // Copyright (c) 2023-2024 Falko Schumann. All rights reserved. MIT license.
2
-
3
- /**
4
- * Contains several miscellaneous utility classes.
5
- *
6
- * Ported from
7
- * [Java Util](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/package-summary.html).
8
- *
9
- * @module
10
- */
11
-
12
- import { Clock } from './time.js';
13
-
14
- // TODO deep equals
15
-
16
- /**
17
- * Creates a deep copy of a value.
18
- *
19
- * @param {*} value The value to copy.
20
- * @returns {*} The deep copy of the value.
21
- */
22
- export function deepCopy(value) {
23
- return JSON.parse(JSON.stringify(value));
24
- }
25
-
26
- /**
27
- * Merges two objects deeply.
28
- *
29
- * @param {*} target The object to merge into.
30
- * @param {*} source The object to merge.
31
- * @returns {*} The merged object.
32
- */
33
- export function deepMerge(target, source) {
34
- if (source === undefined) {
35
- return target;
36
- }
37
-
38
- if (typeof source !== 'object' || source === null) {
39
- return source;
40
- }
41
-
42
- if (Array.isArray(target) && Array.isArray(source)) {
43
- for (const item of source) {
44
- const element = deepMerge(undefined, item);
45
- target.push(element);
46
- }
47
- return target;
48
- }
49
-
50
- for (const key in source) {
51
- if (typeof target !== 'object' || target === null) {
52
- target = {};
53
- }
54
-
55
- target[key] = deepMerge(target[key], source[key]);
56
- }
57
-
58
- return target;
59
- }
60
-
61
- /**
62
- * An instance of `Random` is used to generate random numbers.
63
- */
64
- export class Random {
65
- static create() {
66
- return new Random();
67
- }
68
-
69
- /** @hideconstructor */
70
- constructor() {}
71
-
72
- /**
73
- * Returns a random boolean value.
74
- *
75
- * @param {number} [probabilityOfUndefined=0.0] The probability of returning
76
- * `undefined`.
77
- * @return {boolean|undefined} A random boolean between `origin` (inclusive)
78
- * and `bound` (exclusive) or undefined.
79
- */
80
- nextBoolean(probabilityOfUndefined = 0.0) {
81
- return this.#randomOptional(
82
- () => Math.random() < 0.5,
83
- probabilityOfUndefined,
84
- );
85
- }
86
-
87
- /**
88
- * Returns a random integer between `origin` (inclusive) and `bound`
89
- * (exclusive).
90
- *
91
- * @param {number} [origin=0] The least value that can be returned.
92
- * @param {number} [bound=1] The upper bound (exclusive) for the returned
93
- * value.
94
- * @param {number} [probabilityOfUndefined=0.0] The probability of returning
95
- * `undefined`.
96
- * @return {number|undefined} A random integer between `origin` (inclusive)
97
- * and `bound` (exclusive) or undefined.
98
- */
99
- nextInt(origin = 0, bound = 1, probabilityOfUndefined = 0.0) {
100
- return this.#randomOptional(
101
- () => Math.floor(this.nextFloat(origin, bound)),
102
- probabilityOfUndefined,
103
- );
104
- }
105
-
106
- /**
107
- * Returns a random float between `origin` (inclusive) and `bound`
108
- * (exclusive).
109
- *
110
- * @param {number} [origin=0.0] The least value that can be returned.
111
- * @param {number} [bound=1.0] The upper bound (exclusive) for the returned
112
- * value.
113
- * @param {number} [probabilityOfUndefined=0.0] The probability of returning
114
- * `undefined`.
115
- * @return {number|undefined} A random float between `origin` (inclusive) and
116
- * `bound` (exclusive) or undefined.
117
- */
118
- nextFloat(origin = 0.0, bound = 1.0, probabilityOfUndefined = 0.0) {
119
- return this.#randomOptional(
120
- () => Math.random() * (bound - origin) + origin,
121
- probabilityOfUndefined,
122
- );
123
- }
124
-
125
- /**
126
- * Returns a random timestamp with optional random offset.
127
- *
128
- * @param {number} [maxMillis=0] The maximum offset in milliseconds.
129
- * @param {number} [probabilityOfUndefined=0.0] The probability of returning
130
- * `undefined`.
131
- * @return {Date|undefined} A random timestamp or `undefined`.
132
- */
133
- nextDate(maxMillis = 0, probabilityOfUndefined = 0.0) {
134
- return this.#randomOptional(() => {
135
- const now = new Date();
136
- let t = now.getTime();
137
- const r = Math.random();
138
- t += r * maxMillis;
139
- return new Date(t);
140
- }, probabilityOfUndefined);
141
- }
142
-
143
- /**
144
- * Returns a random value from an array.
145
- *
146
- * @param {Array} [values=[]] The array of values.
147
- * @param {number} [probabilityOfUndefined=0.0] The probability of returning
148
- * `undefined`.
149
- * @return {*|undefined} A random value from the array or `undefined`.
150
- */
151
- nextValue(values = [], probabilityOfUndefined = 0.0) {
152
- return this.#randomOptional(() => {
153
- const index = new Random().nextInt(0, values.length - 1);
154
- return values[index];
155
- }, probabilityOfUndefined);
156
- }
157
-
158
- #randomOptional(randomFactory, probabilityOfUndefined) {
159
- const r = Math.random();
160
- return r < probabilityOfUndefined ? undefined : randomFactory();
161
- }
162
- }
163
-
164
- const TASK_CREATED = 'created';
165
- const TASK_SCHEDULED = 'scheduled';
166
- const TASK_EXECUTED = 'executed';
167
- const TASK_CANCELLED = 'cancelled';
168
-
169
- /**
170
- * A task that can be scheduled by a {@link Timer}.
171
- */
172
- export class TimerTask {
173
- _state = TASK_CREATED;
174
- _nextExecutionTime = 0;
175
- _period = 0;
176
-
177
- /**
178
- * Runs the task.
179
- *
180
- * @abstract
181
- */
182
- run() {
183
- throw new Error('Method not implemented.');
184
- }
185
-
186
- /**
187
- * Cancels the task.
188
- *
189
- * @return {boolean} `true` if this task was scheduled for one-time execution
190
- * and has not yet run, or this task was scheduled for repeated execution.
191
- * Return `false` if the task was scheduled for one-time execution and has
192
- * already run, or if the task was never scheduled, or if the task was
193
- * already cancelled.
194
- */
195
- cancel() {
196
- const result = this._state === TASK_SCHEDULED;
197
- this._state = TASK_CANCELLED;
198
- return result;
199
- }
200
-
201
- /**
202
- * Returns scheduled execution time of the most recent actual execution of
203
- * this task.
204
- *
205
- * Example usage:
206
- *
207
- * ```javascript
208
- * run() {
209
- * if (Date.now() - scheduledExecutionTime() >= MAX_TARDINESS) {
210
- * return; // Too late; skip this execution.
211
- * }
212
- * // Perform the task
213
- * }
214
- *
215
- * ```
216
- *
217
- * @return {number} The time in milliseconds since the epoch, undefined if
218
- * the task has not yet run for the first time.
219
- */
220
- scheduledExecutionTime() {
221
- return this._period < 0
222
- ? this._nextExecutionTime + this._period
223
- : this._nextExecutionTime - this._period;
224
- }
225
- }
226
-
227
- /**
228
- * A timer that schedules and cancels tasks.
229
- *
230
- * Tasks may be scheduled for one-time execution or for repeated execution at
231
- * regular intervals.
232
- */
233
- export class Timer extends EventTarget {
234
- /**
235
- * Returns a new `Timer`.
236
- */
237
- static create() {
238
- return new Timer(Clock.system(), globalThis);
239
- }
240
-
241
- /**
242
- * Returns a new `Timer` for testing without side effects.
243
- */
244
- static createNull({ clock = Clock.fixed() } = {}) {
245
- return new Timer(clock, new TimeoutStub());
246
- }
247
-
248
- /** @type {Clock} */
249
- #clock;
250
-
251
- /** @type {globalThis} */
252
- #global;
253
-
254
- /** @type {TimerTask[]} */
255
- _queue;
256
-
257
- /**
258
- * Returns a new `Timer`.
259
- *
260
- * @param {Clock=} clock The clock to use.
261
- * @param {globalThis} global The global object.
262
- */
263
- constructor(clock = Clock.system(), global = globalThis) {
264
- super();
265
- this.#clock = clock;
266
- this.#global = global;
267
- this._queue = [];
268
- }
269
-
270
- /**
271
- * Schedules a task for repeated execution at regular intervals.
272
- *
273
- * @param {TimerTask} task The task to execute.
274
- * @param {number|Date} delayOrTime The delay before the first execution, in
275
- * milliseconds or the time of the first execution.
276
- * @param {number} [period=0] The interval between executions, in
277
- * milliseconds; 0 means single execution.
278
- */
279
- schedule(task, delayOrTime, period = 0) {
280
- this.#doSchedule(task, delayOrTime, -period);
281
- }
282
-
283
- /**
284
- * Schedule a task for repeated fixed-rate execution.
285
- *
286
- * @param {TimerTask} task The task to execute.
287
- * @param {number|Date} delayOrTime The delay before the first execution, in
288
- * milliseconds or the time of the first.
289
- * @param {number} period The interval between executions, in milliseconds.
290
- */
291
- scheduleAtFixedRate(task, delayOrTime, period) {
292
- this.#doSchedule(task, delayOrTime, period);
293
- }
294
-
295
- /**
296
- * Cancels all scheduled tasks.
297
- */
298
- cancel() {
299
- for (const task of this._queue) {
300
- task.cancel();
301
- }
302
- this._queue = [];
303
- }
304
-
305
- /**
306
- * Removes all cancelled tasks from the task queue.
307
- *
308
- * @return {number} The number of tasks removed from the task queue.
309
- */
310
- purge() {
311
- let result = 0;
312
- for (let i = 0; i < this._queue.length; i++) {
313
- if (this._queue[i]._state === TASK_CANCELLED) {
314
- this._queue.splice(i, 1);
315
- i--;
316
- result++;
317
- }
318
- }
319
- return result;
320
- }
321
-
322
- /**
323
- * Simulates the execution of a task.
324
- *
325
- * @param {object} options The simulation options.
326
- * @param {number} [options.ticks=1000] The number of milliseconds to advance
327
- * the clock.
328
- */
329
- simulateTaskExecution({ ticks = 1000 } = {}) {
330
- this.#clock.add(ticks);
331
- this.#runMainLoop();
332
- }
333
-
334
- #doSchedule(task, delayOrTime, period) {
335
- if (delayOrTime instanceof Date) {
336
- task._nextExecutionTime = delayOrTime.getTime();
337
- } else {
338
- task._nextExecutionTime = this.#clock.millis() + delayOrTime;
339
- }
340
- task._period = period;
341
- task._state = TASK_SCHEDULED;
342
- this._queue.push(task);
343
- this._queue.sort((a, b) => b._nextExecutionTime - a._nextExecutionTime);
344
- if (this._queue[0] === task) {
345
- this.#runMainLoop();
346
- }
347
- }
348
-
349
- #runMainLoop() {
350
- if (this._queue.length === 0) {
351
- return;
352
- }
353
-
354
- const task = this._queue[0];
355
- if (task._state === TASK_CANCELLED) {
356
- this._queue.shift();
357
- return this.#runMainLoop();
358
- }
359
-
360
- const now = this.#clock.millis();
361
- const executionTime = task._nextExecutionTime;
362
- const taskFired = executionTime <= now;
363
- if (taskFired) {
364
- if (task._period === 0) {
365
- this._queue.shift();
366
- task._state = TASK_EXECUTED;
367
- } else {
368
- task._nextExecutionTime =
369
- task._period < 0 ? now - task._period : executionTime + task._period;
370
- }
371
- task.run();
372
- } else {
373
- this.#global.setTimeout(() => this.#runMainLoop(), executionTime - now);
374
- }
375
- }
376
- }
377
-
378
- class TimeoutStub {
379
- setTimeout() {}
380
- }
package/lib/validation.js DELETED
@@ -1,290 +0,0 @@
1
- // Copyright (c) 2023-2024 Falko Schumann. All rights reserved. MIT license.
2
-
3
- // TODO Use JSON schema to validate like Java Bean Validation?
4
-
5
- export class ValidationError extends Error {
6
- constructor(message) {
7
- super(message);
8
- this.name = 'ValidationError';
9
- }
10
- }
11
-
12
- /** @return {never} */
13
- export function ensureUnreachable(message = 'Unreachable code executed.') {
14
- throw new Error(message);
15
- }
16
-
17
- export function ensureThat(
18
- value,
19
- predicate,
20
- message = 'Expected predicate is not true.',
21
- ) {
22
- const condition = predicate(value);
23
- if (!condition) {
24
- throw new ValidationError(message);
25
- }
26
-
27
- return value;
28
- }
29
-
30
- export function ensureAnything(value, { name = 'value' } = {}) {
31
- if (value == null) {
32
- throw new ValidationError(`The ${name} is required, but it was ${value}.`);
33
- }
34
-
35
- return value;
36
- }
37
-
38
- export function ensureNonEmpty(value, { name = 'value' } = {}) {
39
- const valueType = getType(value);
40
- if (
41
- (valueType === String && value.length === 0) ||
42
- (valueType === Array && value.length === 0) ||
43
- (valueType === Object && Object.keys(value).length === 0)
44
- ) {
45
- throw new ValidationError(
46
- `The ${name} must not be empty, but it was ${JSON.stringify(value)}.`,
47
- );
48
- }
49
-
50
- return value;
51
- }
52
-
53
- /*
54
- * type: undefined | null | Boolean | Number | BigInt | String | Symbol | Function | Object | Array | Enum | constructor | Record<string, type>
55
- * expectedType: type | [ type ]
56
- */
57
-
58
- export function ensureType(value, expectedType, { name = 'value' } = {}) {
59
- const result = checkType(value, expectedType, { name });
60
- if (result.error) {
61
- throw new ValidationError(result.error);
62
- }
63
- return result.value;
64
- }
65
-
66
- export function ensureItemType(array, expectedType, { name = 'value' } = {}) {
67
- const result = checkType(array, Array, { name });
68
- if (result.error) {
69
- throw new ValidationError(result.error);
70
- }
71
-
72
- array.forEach((item, index) => {
73
- const result = checkType(item, expectedType, {
74
- name: `${name}.${index}`,
75
- });
76
- if (result.error) {
77
- throw new ValidationError(result.error);
78
- }
79
- array[index] = result.value;
80
- });
81
- return array;
82
- }
83
-
84
- export function ensureArguments(args, expectedTypes = [], names = []) {
85
- ensureThat(
86
- expectedTypes,
87
- Array.isArray,
88
- 'The expectedTypes must be an array.',
89
- );
90
- ensureThat(names, Array.isArray, 'The names must be an array.');
91
- if (args.length > expectedTypes.length) {
92
- throw new ValidationError(
93
- `Too many arguments: expected ${expectedTypes.length}, but got ${args.length}.`,
94
- );
95
- }
96
- expectedTypes.forEach((expectedType, index) => {
97
- const name = names[index] ? names[index] : `argument #${index + 1}`;
98
- ensureType(args[index], expectedType, { name });
99
- });
100
- }
101
-
102
- function checkType(value, expectedType, { name = 'value' } = {}) {
103
- const valueType = getType(value);
104
-
105
- // Check built-in types
106
- if (
107
- expectedType === undefined ||
108
- expectedType === null ||
109
- expectedType === Boolean ||
110
- expectedType === Number ||
111
- expectedType === BigInt ||
112
- expectedType === String ||
113
- expectedType === Symbol ||
114
- expectedType === Function ||
115
- expectedType === Object ||
116
- expectedType === Array
117
- ) {
118
- if (valueType === expectedType) {
119
- return { value };
120
- }
121
-
122
- return {
123
- error: `The ${name} must be ${describe(expectedType, {
124
- articles: true,
125
- })}, but it was ${describe(valueType, { articles: true })}.`,
126
- };
127
- }
128
-
129
- // Check enum types
130
- if (Object.getPrototypeOf(expectedType).name === 'Enum') {
131
- try {
132
- return { value: expectedType.valueOf(String(value).toUpperCase()) };
133
- } catch {
134
- return {
135
- error: `The ${name} must be ${describe(expectedType, {
136
- articles: true,
137
- })}, but it was ${describe(valueType, { articles: true })}.`,
138
- };
139
- }
140
- }
141
-
142
- // Check constructor types
143
- if (typeof expectedType === 'function') {
144
- if (value instanceof expectedType) {
145
- return { value };
146
- } else {
147
- const convertedValue = new expectedType(value);
148
- if (String(convertedValue).toLowerCase().startsWith('invalid')) {
149
- let error = `The ${name} must be a valid ${describe(
150
- expectedType,
151
- )}, but it was ${describe(valueType, { articles: true })}`;
152
- if (valueType != null) {
153
- error += `: ${JSON.stringify(value)}`;
154
- }
155
- error += '.';
156
- return { error };
157
- }
158
-
159
- return { value: convertedValue };
160
- }
161
- }
162
-
163
- // Check one of multiple types
164
- if (Array.isArray(expectedType)) {
165
- for (const type of expectedType) {
166
- const result = checkType(value, type, { name });
167
- if (!result.error) {
168
- return { value };
169
- }
170
- }
171
-
172
- return {
173
- error: `The ${name} must be ${describe(expectedType, {
174
- articles: true,
175
- })}, but it was ${describe(valueType, { articles: true })}.`,
176
- };
177
- }
178
-
179
- if (typeof expectedType === 'object') {
180
- // Check struct types
181
- const result = checkType(value, Object, { name });
182
- if (result.error) {
183
- return result;
184
- }
185
-
186
- for (const key in expectedType) {
187
- const result = checkType(value[key], expectedType[key], {
188
- name: `${name}.${key}`,
189
- });
190
- if (result.error) {
191
- return result;
192
- }
193
- value[key] = result.value;
194
- }
195
-
196
- return { value };
197
- }
198
-
199
- ensureUnreachable();
200
- }
201
-
202
- function getType(value) {
203
- if (value === null) {
204
- return null;
205
- }
206
- if (Array.isArray(value)) {
207
- return Array;
208
- }
209
- if (Number.isNaN(value)) {
210
- return NaN;
211
- }
212
-
213
- switch (typeof value) {
214
- case 'undefined':
215
- return undefined;
216
- case 'boolean':
217
- return Boolean;
218
- case 'number':
219
- return Number;
220
- case 'bigint':
221
- return BigInt;
222
- case 'string':
223
- return String;
224
- case 'symbol':
225
- return Symbol;
226
- case 'function':
227
- return Function;
228
- case 'object':
229
- return Object;
230
- default:
231
- ensureUnreachable(`Unknown typeof value: ${typeof value}.`);
232
- }
233
- }
234
-
235
- function describe(type, { articles = false } = {}) {
236
- if (Array.isArray(type)) {
237
- const types = type.map((t) => describe(t, { articles }));
238
- if (types.length <= 2) {
239
- return types.join(' or ');
240
- } else {
241
- const allButLast = types.slice(0, -1);
242
- const last = types.at(-1);
243
- return allButLast.join(', ') + ', or ' + last;
244
- }
245
- }
246
-
247
- if (Number.isNaN(type)) {
248
- return 'NaN';
249
- }
250
-
251
- let name;
252
- switch (type) {
253
- case null:
254
- return 'null';
255
- case undefined:
256
- return 'undefined';
257
- case Array:
258
- name = 'array';
259
- break;
260
- case Boolean:
261
- name = 'boolean';
262
- break;
263
- case Number:
264
- name = 'number';
265
- break;
266
- case BigInt:
267
- name = 'bigint';
268
- break;
269
- case String:
270
- name = 'string';
271
- break;
272
- case Symbol:
273
- name = 'symbol';
274
- break;
275
- case Function:
276
- name = 'function';
277
- break;
278
- case Object:
279
- name = 'object';
280
- break;
281
- default:
282
- name = type.name;
283
- break;
284
- }
285
-
286
- if (articles) {
287
- name = 'aeiou'.includes(name[0].toLowerCase()) ? `an ${name}` : `a ${name}`;
288
- }
289
- return name;
290
- }