@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.
Files changed (50) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +6 -0
  3. package/dist/bundle/mockServiceWorker.js +284 -0
  4. package/dist/bundle/utiliti-es.cjs +1 -0
  5. package/dist/bundle/utiliti-es.iife.js +1 -0
  6. package/dist/bundle/utiliti-es.js +475 -0
  7. package/dist/bundle/utiliti-es.umd.cjs +1 -0
  8. package/dist/common/contracts.d.ts +6 -0
  9. package/dist/common/contracts.js +1 -0
  10. package/dist/common/index.d.ts +1 -0
  11. package/dist/common/index.js +1 -0
  12. package/dist/index.d.ts +4 -0
  13. package/dist/index.js +4 -0
  14. package/dist/logger/contracts.d.ts +95 -0
  15. package/dist/logger/contracts.js +67 -0
  16. package/dist/logger/enrichers/dynamicValuesEnricher.d.ts +20 -0
  17. package/dist/logger/enrichers/dynamicValuesEnricher.js +31 -0
  18. package/dist/logger/enrichers/index.d.ts +2 -0
  19. package/dist/logger/enrichers/index.js +2 -0
  20. package/dist/logger/enrichers/valuesEnricher.d.ts +16 -0
  21. package/dist/logger/enrichers/valuesEnricher.js +30 -0
  22. package/dist/logger/index.d.ts +5 -0
  23. package/dist/logger/index.js +5 -0
  24. package/dist/logger/logger.d.ts +81 -0
  25. package/dist/logger/logger.js +136 -0
  26. package/dist/logger/loggerOptions.d.ts +28 -0
  27. package/dist/logger/loggerOptions.js +47 -0
  28. package/dist/logger/reporters/consoleReporter.d.ts +34 -0
  29. package/dist/logger/reporters/consoleReporter.js +58 -0
  30. package/dist/logger/reporters/inMemoryReporter.d.ts +17 -0
  31. package/dist/logger/reporters/inMemoryReporter.js +22 -0
  32. package/dist/logger/reporters/index.d.ts +4 -0
  33. package/dist/logger/reporters/index.js +4 -0
  34. package/dist/logger/reporters/multipleReporter.d.ts +16 -0
  35. package/dist/logger/reporters/multipleReporter.js +29 -0
  36. package/dist/logger/reporters/xhrReporter.d.ts +45 -0
  37. package/dist/logger/reporters/xhrReporter.js +108 -0
  38. package/dist/mocks/logReporterHandlers.d.ts +1 -0
  39. package/dist/mocks/logReporterHandlers.js +27 -0
  40. package/dist/pubsub/contracts.d.ts +39 -0
  41. package/dist/pubsub/contracts.js +1 -0
  42. package/dist/pubsub/index.d.ts +2 -0
  43. package/dist/pubsub/index.js +1 -0
  44. package/dist/pubsub/pubsub.d.ts +13 -0
  45. package/dist/pubsub/pubsub.js +51 -0
  46. package/dist/utils/delay.d.ts +8 -0
  47. package/dist/utils/delay.js +14 -0
  48. package/dist/utils/index.d.ts +1 -0
  49. package/dist/utils/index.js +1 -0
  50. 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,2 @@
1
+ export type { IPubSubHub, MessageHandler, MessageData, MessageValue } from "./contracts";
2
+ export { PubSubHub } from "./pubsub";
@@ -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
+ }