@adapt-arch/utiliti-es 0.1.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/LICENSE +21 -0
- package/README.md +6 -0
- package/dist/bundle/mockServiceWorker.js +284 -0
- package/dist/bundle/utiliti-es.cjs +1 -0
- package/dist/bundle/utiliti-es.iife.js +1 -0
- package/dist/bundle/utiliti-es.js +475 -0
- package/dist/bundle/utiliti-es.umd.cjs +1 -0
- package/dist/common/contracts.d.ts +6 -0
- package/dist/common/contracts.js +1 -0
- package/dist/common/index.d.ts +1 -0
- package/dist/common/index.js +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -0
- package/dist/logger/contracts.d.ts +95 -0
- package/dist/logger/contracts.js +67 -0
- package/dist/logger/enrichers/dynamicValuesEnricher.d.ts +20 -0
- package/dist/logger/enrichers/dynamicValuesEnricher.js +31 -0
- package/dist/logger/enrichers/index.d.ts +2 -0
- package/dist/logger/enrichers/index.js +2 -0
- package/dist/logger/enrichers/valuesEnricher.d.ts +16 -0
- package/dist/logger/enrichers/valuesEnricher.js +30 -0
- package/dist/logger/index.d.ts +5 -0
- package/dist/logger/index.js +5 -0
- package/dist/logger/logger.d.ts +81 -0
- package/dist/logger/logger.js +136 -0
- package/dist/logger/loggerOptions.d.ts +28 -0
- package/dist/logger/loggerOptions.js +47 -0
- package/dist/logger/reporters/consoleReporter.d.ts +34 -0
- package/dist/logger/reporters/consoleReporter.js +58 -0
- package/dist/logger/reporters/inMemoryReporter.d.ts +17 -0
- package/dist/logger/reporters/inMemoryReporter.js +22 -0
- package/dist/logger/reporters/index.d.ts +4 -0
- package/dist/logger/reporters/index.js +4 -0
- package/dist/logger/reporters/multipleReporter.d.ts +16 -0
- package/dist/logger/reporters/multipleReporter.js +29 -0
- package/dist/logger/reporters/xhrReporter.d.ts +45 -0
- package/dist/logger/reporters/xhrReporter.js +108 -0
- package/dist/mocks/logReporterHandlers.d.ts +1 -0
- package/dist/mocks/logReporterHandlers.js +27 -0
- package/dist/pubsub/contracts.d.ts +39 -0
- package/dist/pubsub/contracts.js +1 -0
- package/dist/pubsub/index.d.ts +2 -0
- package/dist/pubsub/index.js +1 -0
- package/dist/pubsub/pubsub.d.ts +13 -0
- package/dist/pubsub/pubsub.js +51 -0
- package/dist/utils/delay.d.ts +8 -0
- package/dist/utils/delay.js +14 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +1 -0
- package/package.json +73 -0
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP Reporter options.
|
|
3
|
+
*/
|
|
4
|
+
export class XhrReporterOptions {
|
|
5
|
+
/**
|
|
6
|
+
* Endpoint that receives the logs.
|
|
7
|
+
*/
|
|
8
|
+
endpoint = "";
|
|
9
|
+
/**
|
|
10
|
+
* HTTP verb used when calling the endpoint.
|
|
11
|
+
*/
|
|
12
|
+
verb = "POST";
|
|
13
|
+
/**
|
|
14
|
+
* The number of items to send in a batch.
|
|
15
|
+
*/
|
|
16
|
+
batchSize = 20;
|
|
17
|
+
/**
|
|
18
|
+
* The maximum interval, in milliseconds, to wait for the batch size to be achieved before reporting.
|
|
19
|
+
*/
|
|
20
|
+
interval = 2_000;
|
|
21
|
+
/**
|
|
22
|
+
* A function that can be used to transform the request before sending it.
|
|
23
|
+
*/
|
|
24
|
+
requestTransform;
|
|
25
|
+
}
|
|
26
|
+
export class XhrReporter {
|
|
27
|
+
_messageQueue;
|
|
28
|
+
_options;
|
|
29
|
+
_reportActionTimeoutRef;
|
|
30
|
+
_reportActionPromise;
|
|
31
|
+
_disposed;
|
|
32
|
+
constructor(options) {
|
|
33
|
+
if (!options) {
|
|
34
|
+
throw new Error('Argument "options" is required');
|
|
35
|
+
}
|
|
36
|
+
this._messageQueue = [];
|
|
37
|
+
this._options = options;
|
|
38
|
+
this._reportActionTimeoutRef = undefined;
|
|
39
|
+
this._reportActionPromise = null;
|
|
40
|
+
this._disposed = false;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* @inheritdoc
|
|
44
|
+
*/
|
|
45
|
+
register(message) {
|
|
46
|
+
if (this._disposed) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
this._messageQueue.push(message);
|
|
50
|
+
this._scheduleNextProcessAction();
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* @inheritdoc
|
|
54
|
+
*/
|
|
55
|
+
async [Symbol.asyncDispose]() {
|
|
56
|
+
if (this._disposed) {
|
|
57
|
+
return Promise.resolve();
|
|
58
|
+
}
|
|
59
|
+
await (this._reportActionPromise ?? this._processMessages());
|
|
60
|
+
this._disposed = true;
|
|
61
|
+
}
|
|
62
|
+
_scheduleNextProcessAction() {
|
|
63
|
+
if (this._reportActionTimeoutRef) {
|
|
64
|
+
return; // Already scheduled
|
|
65
|
+
}
|
|
66
|
+
const interval = this._messageQueue.length >= this._options.batchSize ? 0 : this._options.interval;
|
|
67
|
+
this._reportActionTimeoutRef = setTimeout(() => {
|
|
68
|
+
this._reportActionPromise = this._processMessages().then(() => {
|
|
69
|
+
const prevRef = this._reportActionTimeoutRef;
|
|
70
|
+
this._reportActionTimeoutRef = undefined;
|
|
71
|
+
clearTimeout(prevRef);
|
|
72
|
+
this._reportActionPromise = null;
|
|
73
|
+
this._scheduleNextProcessAction();
|
|
74
|
+
});
|
|
75
|
+
}, interval);
|
|
76
|
+
}
|
|
77
|
+
async _processMessages() {
|
|
78
|
+
let messages;
|
|
79
|
+
let success;
|
|
80
|
+
while (this._messageQueue.length > 0) {
|
|
81
|
+
messages = this._messageQueue.splice(0, Math.min(this._messageQueue.length, this._options.batchSize));
|
|
82
|
+
success = await this._sendMessagesBatch(messages);
|
|
83
|
+
if (!success) {
|
|
84
|
+
this._messageQueue.unshift(...messages);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
_sendMessagesBatch(messages) {
|
|
90
|
+
return new Promise((resolve) => {
|
|
91
|
+
const failureHandler = () => {
|
|
92
|
+
resolve(false);
|
|
93
|
+
};
|
|
94
|
+
const request = new XMLHttpRequest();
|
|
95
|
+
request.open(this._options.verb, this._options.endpoint);
|
|
96
|
+
request.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
|
|
97
|
+
if (this._options.requestTransform) {
|
|
98
|
+
this._options.requestTransform(request);
|
|
99
|
+
}
|
|
100
|
+
request.onload = function () {
|
|
101
|
+
resolve(this.status >= 200 && this.status < 300);
|
|
102
|
+
};
|
|
103
|
+
request.onerror = failureHandler;
|
|
104
|
+
request.onabort = failureHandler;
|
|
105
|
+
request.send(JSON.stringify(messages));
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function getLogReporterHandlers(handledRequestsContainer?: Array<Request>): import("msw").HttpHandler[];
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { http, HttpResponse, delay } from "msw";
|
|
2
|
+
// https://mswjs.io/docs/basics/mocking-responses
|
|
3
|
+
export function getLogReporterHandlers(handledRequestsContainer) {
|
|
4
|
+
return [
|
|
5
|
+
http.post("/logs*", async ({ request }) => {
|
|
6
|
+
if (handledRequestsContainer) {
|
|
7
|
+
handledRequestsContainer.push(request.clone());
|
|
8
|
+
}
|
|
9
|
+
// Get the responseCode from the query string
|
|
10
|
+
const responseCode = new URL(request.url).searchParams.get("responseCode");
|
|
11
|
+
let status = responseCode ? Number.parseInt(responseCode) : 200;
|
|
12
|
+
if (Number.isNaN(status)) {
|
|
13
|
+
status = 200;
|
|
14
|
+
}
|
|
15
|
+
if (status < 200 || status < 300) {
|
|
16
|
+
await delay(60); // Simulate network latency
|
|
17
|
+
}
|
|
18
|
+
if (status === 0) {
|
|
19
|
+
// Simulate a network error
|
|
20
|
+
return HttpResponse.error();
|
|
21
|
+
}
|
|
22
|
+
return new HttpResponse(null, {
|
|
23
|
+
status: status,
|
|
24
|
+
});
|
|
25
|
+
}),
|
|
26
|
+
];
|
|
27
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { SerializableValues } from "../common";
|
|
2
|
+
/**
|
|
3
|
+
* The types of message values that can be published.
|
|
4
|
+
*/
|
|
5
|
+
export type MessageValue = SerializableValues;
|
|
6
|
+
/**
|
|
7
|
+
* The type of a message.
|
|
8
|
+
*/
|
|
9
|
+
export type MessageData = Record<string, MessageValue>;
|
|
10
|
+
/**
|
|
11
|
+
* A simple message handler.
|
|
12
|
+
*/
|
|
13
|
+
export type MessageHandler = (topic: string, message: MessageData) => void;
|
|
14
|
+
/**
|
|
15
|
+
* A simple pub/sub component.
|
|
16
|
+
*/
|
|
17
|
+
export interface IPubSubHub {
|
|
18
|
+
/**
|
|
19
|
+
* Publish a message to a topic.
|
|
20
|
+
*
|
|
21
|
+
* @param {string} topic The topic to publish to.
|
|
22
|
+
* @param {MessageData} message The message to publish.
|
|
23
|
+
**/
|
|
24
|
+
publish(topic: string, message: MessageData): void;
|
|
25
|
+
/**
|
|
26
|
+
* Subscribe to a topic.
|
|
27
|
+
*
|
|
28
|
+
* @param {string} topic The topic to subscribe to.
|
|
29
|
+
* @param {MessageHandler} handler The handler to call when a message is published.
|
|
30
|
+
* @returns {string} The id of the handler subscription if the subscription is successful.
|
|
31
|
+
**/
|
|
32
|
+
subscribe(topic: string, handler: MessageHandler): string | null;
|
|
33
|
+
/**
|
|
34
|
+
* Unsubscribe from a topic.
|
|
35
|
+
*
|
|
36
|
+
* @param {string} subscriptionId The subscription id to unsubscribe.
|
|
37
|
+
**/
|
|
38
|
+
unsubscribe(subscriptionId: string): void;
|
|
39
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { PubSubHub } from "./pubsub";
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { IPubSubHub, MessageData, MessageHandler } from "./contracts";
|
|
2
|
+
/**
|
|
3
|
+
* A PubSub implementation.
|
|
4
|
+
*/
|
|
5
|
+
export declare class PubSubHub implements IPubSubHub {
|
|
6
|
+
private _subscriptions;
|
|
7
|
+
/** @inheritdoc */
|
|
8
|
+
publish(topic: string, message: MessageData): void;
|
|
9
|
+
/** @inheritdoc */
|
|
10
|
+
subscribe(topic: string, handler: MessageHandler): string | null;
|
|
11
|
+
/** @inheritdoc */
|
|
12
|
+
unsubscribe(subscriptionId: string): void;
|
|
13
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A PubSub implementation.
|
|
3
|
+
*/
|
|
4
|
+
export class PubSubHub {
|
|
5
|
+
_subscriptions = new Map();
|
|
6
|
+
/** @inheritdoc */
|
|
7
|
+
publish(topic, message) {
|
|
8
|
+
if (!topic) {
|
|
9
|
+
throw new Error("Invalid topic.");
|
|
10
|
+
}
|
|
11
|
+
if (!message) {
|
|
12
|
+
throw new Error("Invalid message.");
|
|
13
|
+
}
|
|
14
|
+
const handlers = this._subscriptions.get(topic);
|
|
15
|
+
if (handlers) {
|
|
16
|
+
for (const handler of handlers.values()) {
|
|
17
|
+
const newTopic = structuredClone(topic);
|
|
18
|
+
const newMessage = structuredClone(message);
|
|
19
|
+
setTimeout(() => handler(newTopic, newMessage), 0);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
/** @inheritdoc */
|
|
24
|
+
subscribe(topic, handler) {
|
|
25
|
+
if (!topic) {
|
|
26
|
+
throw new Error("Invalid topic.");
|
|
27
|
+
}
|
|
28
|
+
if (!handler) {
|
|
29
|
+
throw new Error("Invalid handler.");
|
|
30
|
+
}
|
|
31
|
+
let handlers = this._subscriptions.get(topic);
|
|
32
|
+
if (!handlers) {
|
|
33
|
+
handlers = new Map();
|
|
34
|
+
this._subscriptions.set(structuredClone(topic), handlers);
|
|
35
|
+
}
|
|
36
|
+
const subscriptionId = `sub-${Date.now()}`;
|
|
37
|
+
handlers.set(subscriptionId, handler);
|
|
38
|
+
return subscriptionId;
|
|
39
|
+
}
|
|
40
|
+
/** @inheritdoc */
|
|
41
|
+
unsubscribe(subscriptionId) {
|
|
42
|
+
if (!subscriptionId) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
for (const handlers of this._subscriptions.values()) {
|
|
46
|
+
if (handlers.delete(subscriptionId)) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Get a promise that resolves after a certain duration.
|
|
3
|
+
*
|
|
4
|
+
* @param {number} duration The duration to wait before resolving the promise.
|
|
5
|
+
* @param {Error?} error An optional error to throw instead of resolving the promise.
|
|
6
|
+
* @returns {Promise<void>} A promise that resolves after a certain duration.
|
|
7
|
+
*/
|
|
8
|
+
export declare function delay(duration?: number, error?: Error): Promise<void>;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Get a promise that resolves after a certain duration.
|
|
3
|
+
*
|
|
4
|
+
* @param {number} duration The duration to wait before resolving the promise.
|
|
5
|
+
* @param {Error?} error An optional error to throw instead of resolving the promise.
|
|
6
|
+
* @returns {Promise<void>} A promise that resolves after a certain duration.
|
|
7
|
+
*/
|
|
8
|
+
export function delay(duration = 1, error) {
|
|
9
|
+
return new Promise((res, rej) => {
|
|
10
|
+
setTimeout(() => {
|
|
11
|
+
error ? rej(error) : res();
|
|
12
|
+
}, duration);
|
|
13
|
+
});
|
|
14
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { delay } from "./delay";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { delay } from "./delay";
|
package/package.json
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@adapt-arch/utiliti-es",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"description": "EcmaScript library with common utilities",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"adaptive",
|
|
8
|
+
"architecture",
|
|
9
|
+
"pubsub",
|
|
10
|
+
"logger",
|
|
11
|
+
"utilities",
|
|
12
|
+
"common"
|
|
13
|
+
],
|
|
14
|
+
"type": "module",
|
|
15
|
+
"files": [
|
|
16
|
+
"dist/**",
|
|
17
|
+
"LICENSE",
|
|
18
|
+
"README.md"
|
|
19
|
+
],
|
|
20
|
+
"main": "dist/utiliti-es.umd.cjs",
|
|
21
|
+
"module": "dist/utiliti-es.js",
|
|
22
|
+
"types": "dist/index.d.ts",
|
|
23
|
+
"exports": {
|
|
24
|
+
".": {
|
|
25
|
+
"types": "./dist/index.d.ts",
|
|
26
|
+
"import": "./dist/bundle/utiliti-es.js",
|
|
27
|
+
"require": "./dist/bundle/utiliti-es.cjs",
|
|
28
|
+
"node": {
|
|
29
|
+
"import": {
|
|
30
|
+
"production": "./dist/bundle/utiliti-es.js",
|
|
31
|
+
"development": "./dist/index.js",
|
|
32
|
+
"default": "./dist/index.js"
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
"repository": {
|
|
38
|
+
"type": "git",
|
|
39
|
+
"url": "https://github.com/adaptive-architecture/utiliti-es"
|
|
40
|
+
},
|
|
41
|
+
"bugs": {
|
|
42
|
+
"url": "https://github.com/adaptive-architecture/utiliti-es/issues"
|
|
43
|
+
},
|
|
44
|
+
"author": {
|
|
45
|
+
"name": "Adaptive Architecture"
|
|
46
|
+
},
|
|
47
|
+
"sideEffects": false,
|
|
48
|
+
"scripts": {
|
|
49
|
+
"dev": "vite",
|
|
50
|
+
"empty-output": "node ./ci-cd/empty-output.js",
|
|
51
|
+
"build": "tsc && vite build",
|
|
52
|
+
"preview": "vite preview",
|
|
53
|
+
"lint": "npx @biomejs/biome check --apply ./",
|
|
54
|
+
"test": "vitest run --coverage",
|
|
55
|
+
"docs": "typedoc"
|
|
56
|
+
},
|
|
57
|
+
"devDependencies": {
|
|
58
|
+
"@biomejs/biome": "1.7.0",
|
|
59
|
+
"@types/node": "20.12.7",
|
|
60
|
+
"@vitest/coverage-v8": "1.5.0",
|
|
61
|
+
"jsdom": "24.0.0",
|
|
62
|
+
"msw": "2.2.14",
|
|
63
|
+
"typedoc": "0.25.13",
|
|
64
|
+
"typescript": "5.4.5",
|
|
65
|
+
"vite": "5.2.9",
|
|
66
|
+
"vitest": "1.5.0"
|
|
67
|
+
},
|
|
68
|
+
"msw": {
|
|
69
|
+
"workerDirectory": [
|
|
70
|
+
"public"
|
|
71
|
+
]
|
|
72
|
+
}
|
|
73
|
+
}
|