@machhub-dev/sdk-ts 0.0.1

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 (112) hide show
  1. package/LICENSE +21 -0
  2. package/dist/cjs/classes/auth.d.ts +18 -0
  3. package/dist/cjs/classes/auth.js +90 -0
  4. package/dist/cjs/classes/collection.d.ts +18 -0
  5. package/dist/cjs/classes/collection.js +43 -0
  6. package/dist/cjs/classes/flow.d.ts +6 -0
  7. package/dist/cjs/classes/flow.js +12 -0
  8. package/dist/cjs/classes/function.d.ts +10 -0
  9. package/dist/cjs/classes/function.js +29 -0
  10. package/dist/cjs/classes/historian.d.ts +12 -0
  11. package/dist/cjs/classes/historian.js +40 -0
  12. package/dist/cjs/classes/tag.d.ts +10 -0
  13. package/dist/cjs/classes/tag.js +25 -0
  14. package/dist/cjs/client.d.ts +13 -0
  15. package/dist/cjs/client.js +35 -0
  16. package/dist/cjs/config.d.ts +21 -0
  17. package/dist/cjs/config.js +37 -0
  18. package/dist/cjs/example/functions-file-config.d.ts +0 -0
  19. package/dist/cjs/example/functions-file-config.js +1 -0
  20. package/dist/cjs/example/functions.d.ts +1 -0
  21. package/dist/cjs/example/functions.js +18 -0
  22. package/dist/cjs/http-client.d.ts +10 -0
  23. package/dist/cjs/http-client.js +62 -0
  24. package/dist/cjs/index.d.ts +6 -0
  25. package/dist/cjs/index.js +7 -0
  26. package/dist/cjs/sdk-ts-clean.d.ts +108 -0
  27. package/dist/cjs/sdk-ts-clean.js +294 -0
  28. package/dist/cjs/sdk-ts.d.ts +71 -0
  29. package/dist/cjs/sdk-ts.js +227 -0
  30. package/dist/cjs/services/http.service.d.ts +41 -0
  31. package/dist/cjs/services/http.service.js +178 -0
  32. package/dist/cjs/services/mqtt.service.d.ts +15 -0
  33. package/dist/cjs/services/mqtt.service.js +103 -0
  34. package/dist/cjs/services/nats.service.d.ts +78 -0
  35. package/dist/cjs/services/nats.service.js +237 -0
  36. package/dist/cjs/types/auth.models.d.ts +52 -0
  37. package/dist/cjs/types/auth.models.js +100 -0
  38. package/dist/cjs/types/recordID.models.d.ts +7 -0
  39. package/dist/cjs/types/recordID.models.js +32 -0
  40. package/dist/cjs/types/response.models.d.ts +4 -0
  41. package/dist/cjs/types/response.models.js +2 -0
  42. package/dist/cjs/types/tag.models.d.ts +4 -0
  43. package/dist/cjs/types/tag.models.js +2 -0
  44. package/dist/cjs/utils/appConfig.d.ts +5 -0
  45. package/dist/cjs/utils/appConfig.js +62 -0
  46. package/dist/cjs/websocket-client.d.ts +15 -0
  47. package/dist/cjs/websocket-client.js +96 -0
  48. package/dist/classes/auth.d.ts +18 -0
  49. package/dist/classes/auth.js +86 -0
  50. package/dist/classes/collection.d.ts +18 -0
  51. package/dist/classes/collection.js +39 -0
  52. package/dist/classes/flow.d.ts +6 -0
  53. package/dist/classes/flow.js +8 -0
  54. package/dist/classes/function.d.ts +10 -0
  55. package/dist/classes/function.js +25 -0
  56. package/dist/classes/historian.d.ts +12 -0
  57. package/dist/classes/historian.js +36 -0
  58. package/dist/classes/tag.d.ts +10 -0
  59. package/dist/classes/tag.js +21 -0
  60. package/dist/client.d.ts +13 -0
  61. package/dist/client.js +31 -0
  62. package/dist/config.d.ts +21 -0
  63. package/dist/config.js +33 -0
  64. package/dist/example/functions-file-config.d.ts +0 -0
  65. package/dist/example/functions-file-config.js +1 -0
  66. package/dist/example/functions.d.ts +1 -0
  67. package/dist/example/functions.js +16 -0
  68. package/dist/http-client.d.ts +10 -0
  69. package/dist/http-client.js +58 -0
  70. package/dist/index.d.ts +6 -0
  71. package/dist/index.js +2 -0
  72. package/dist/sdk-ts-clean.d.ts +108 -0
  73. package/dist/sdk-ts-clean.js +290 -0
  74. package/dist/sdk-ts.d.ts +71 -0
  75. package/dist/sdk-ts.js +223 -0
  76. package/dist/services/http.service.d.ts +41 -0
  77. package/dist/services/http.service.js +173 -0
  78. package/dist/services/mqtt.service.d.ts +15 -0
  79. package/dist/services/mqtt.service.js +96 -0
  80. package/dist/services/nats.service.d.ts +78 -0
  81. package/dist/services/nats.service.js +233 -0
  82. package/dist/types/auth.models.d.ts +52 -0
  83. package/dist/types/auth.models.js +97 -0
  84. package/dist/types/recordID.models.d.ts +7 -0
  85. package/dist/types/recordID.models.js +27 -0
  86. package/dist/types/response.models.d.ts +4 -0
  87. package/dist/types/response.models.js +1 -0
  88. package/dist/types/tag.models.d.ts +4 -0
  89. package/dist/types/tag.models.js +1 -0
  90. package/dist/utils/appConfig.d.ts +5 -0
  91. package/dist/utils/appConfig.js +26 -0
  92. package/dist/websocket-client.d.ts +15 -0
  93. package/dist/websocket-client.js +92 -0
  94. package/package.json +33 -0
  95. package/src/classes/auth.ts +103 -0
  96. package/src/classes/collection.ts +55 -0
  97. package/src/classes/flow.ts +13 -0
  98. package/src/classes/function.ts +34 -0
  99. package/src/classes/historian.ts +49 -0
  100. package/src/classes/tag.ts +30 -0
  101. package/src/example/functions.ts +21 -0
  102. package/src/index.ts +8 -0
  103. package/src/sdk-ts.ts +255 -0
  104. package/src/services/http.service.ts +239 -0
  105. package/src/services/mqtt.service.ts +114 -0
  106. package/src/services/nats.service.ts +262 -0
  107. package/src/types/auth.models.ts +157 -0
  108. package/src/types/recordID.models.ts +33 -0
  109. package/src/types/response.models.ts +4 -0
  110. package/src/types/tag.models.ts +4 -0
  111. package/src/utils/appConfig.ts +30 -0
  112. package/tsconfig.json +14 -0
@@ -0,0 +1,41 @@
1
+ export declare class HTTPException extends Error {
2
+ status: number;
3
+ statusText: string;
4
+ body: string;
5
+ constructor(status: number, statusText: string, body: string);
6
+ get message(): string;
7
+ }
8
+ export declare class HTTPService {
9
+ private url;
10
+ private applicationID;
11
+ constructor(url: string, prefix?: string, applicationID?: string);
12
+ get request(): RequestParameters;
13
+ }
14
+ declare class RequestParameters {
15
+ private base;
16
+ private applicationID;
17
+ query?: Record<string, string>;
18
+ init?: RequestInit;
19
+ headers?: Record<string, string>;
20
+ constructor(base: URL, applicationID: string, query?: Record<string, string>);
21
+ private withQuery;
22
+ private parseInit;
23
+ withBody(body: BodyInit): RequestParameters;
24
+ includeCredentials(): RequestParameters;
25
+ includeSameOriginCredentials(): RequestParameters;
26
+ keepAlive(): RequestParameters;
27
+ followRedirect(): RequestParameters;
28
+ errorOnRedirect(): RequestParameters;
29
+ setHeader(key: string, value: string): RequestParameters;
30
+ setBearerToken(token: string): RequestParameters;
31
+ withAccessToken(): RequestParameters;
32
+ withDomain(): RequestParameters;
33
+ withContentType(mime: string): RequestParameters;
34
+ withJSON(body: Record<string, unknown>): RequestParameters;
35
+ get<ReturnType>(path: string, query?: Record<string, string>): Promise<ReturnType>;
36
+ post<ReturnType>(path: string, query?: Record<string, string>, body?: FormData | Record<string, string>): Promise<ReturnType>;
37
+ put<ReturnType>(path: string, query?: Record<string, string>): Promise<ReturnType>;
38
+ delete<ReturnType>(path: string, query?: Record<string, string>): Promise<ReturnType>;
39
+ patch<ReturnType>(path: string, query?: Record<string, string>): Promise<ReturnType>;
40
+ }
41
+ export {};
@@ -0,0 +1,178 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HTTPService = exports.HTTPException = void 0;
4
+ class HTTPException extends Error {
5
+ constructor(status, statusText, body) {
6
+ super();
7
+ this.status = status;
8
+ this.statusText = statusText;
9
+ this.body = body;
10
+ }
11
+ get message() {
12
+ return `(EXCEPTION) ${this.statusText} - ${this.body}`;
13
+ }
14
+ }
15
+ exports.HTTPException = HTTPException;
16
+ class HTTPService {
17
+ constructor(url, prefix, applicationID) {
18
+ if (prefix == null)
19
+ prefix = "";
20
+ this.url = new URL(prefix, url);
21
+ this.applicationID = "domains:" + applicationID;
22
+ }
23
+ get request() {
24
+ return new RequestParameters(this.url, this.applicationID);
25
+ }
26
+ }
27
+ exports.HTTPService = HTTPService;
28
+ class RequestParameters {
29
+ constructor(base, applicationID, query) {
30
+ this.base = base;
31
+ this.applicationID = applicationID;
32
+ this.query = query;
33
+ this.withDomain(); // Ensure withDomain() is called by default
34
+ this.withAccessToken(); // Ensure withAccessToken() is called by default
35
+ }
36
+ withQuery(path, query) {
37
+ const newPath = [this.base.pathname, path].map(pathPart => pathPart.replace(/(^\/|\/$)/g, "")).join("/");
38
+ const newURL = new URL(newPath, this.base);
39
+ for (const key in query) {
40
+ newURL.searchParams.append(key, query[key]);
41
+ }
42
+ return newURL;
43
+ }
44
+ parseInit(method) {
45
+ if (method == null && this.headers == null)
46
+ return;
47
+ let tempInit = Object.assign({}, this.init);
48
+ if (tempInit == null) {
49
+ tempInit = {};
50
+ }
51
+ if (method != null && tempInit != null)
52
+ tempInit.method = method;
53
+ if (this.headers != null && tempInit != null) {
54
+ tempInit.headers = Object.assign({}, this.headers);
55
+ }
56
+ return tempInit;
57
+ }
58
+ withBody(body) {
59
+ if (this.init == null) {
60
+ this.init = {};
61
+ }
62
+ this.init.body = body;
63
+ return this;
64
+ }
65
+ includeCredentials() {
66
+ if (this.init == null) {
67
+ this.init = {};
68
+ }
69
+ this.init.credentials = "include";
70
+ return this;
71
+ }
72
+ includeSameOriginCredentials() {
73
+ if (this.init == null) {
74
+ this.init = {};
75
+ }
76
+ this.init.credentials = "same-origin";
77
+ return this;
78
+ }
79
+ keepAlive() {
80
+ if (this.init == null) {
81
+ this.init = {};
82
+ }
83
+ this.init.keepalive = true;
84
+ return this;
85
+ }
86
+ followRedirect() {
87
+ if (this.init == null) {
88
+ this.init = {};
89
+ }
90
+ this.init.redirect = "follow";
91
+ return this;
92
+ }
93
+ errorOnRedirect() {
94
+ if (this.init == null) {
95
+ this.init = {};
96
+ }
97
+ this.init.redirect = "error";
98
+ return this;
99
+ }
100
+ setHeader(key, value) {
101
+ if (this.headers == null) {
102
+ this.headers = {
103
+ key: value
104
+ };
105
+ }
106
+ this.headers[key] = value;
107
+ return this;
108
+ }
109
+ setBearerToken(token) {
110
+ this.setHeader("Authorization", `Bearer ${token}`);
111
+ return this;
112
+ }
113
+ withAccessToken() {
114
+ const tkn = localStorage.getItem("x-machhub-auth-tkn");
115
+ this.setHeader("Authorization", `Bearer ${tkn}`);
116
+ return this;
117
+ }
118
+ withDomain() {
119
+ this.setHeader("Domain", this.applicationID);
120
+ return this;
121
+ }
122
+ withContentType(mime) {
123
+ this.setHeader("Content-Type", mime);
124
+ return this;
125
+ }
126
+ withJSON(body) {
127
+ const bd = JSON.stringify(body);
128
+ return this.withBody(bd).withContentType("application/json");
129
+ }
130
+ async get(path, query) {
131
+ const response = await fetch(this.withQuery(path, query), this.parseInit());
132
+ if (!response.ok) {
133
+ throw new HTTPException(response.status, response.statusText, await response.text());
134
+ }
135
+ return response.json();
136
+ }
137
+ async post(path, query, body) {
138
+ const init = this.parseInit("POST") || {};
139
+ if (body) {
140
+ if (body instanceof FormData) {
141
+ init.body = body;
142
+ }
143
+ else {
144
+ init.body = JSON.stringify(body);
145
+ init.headers = {
146
+ ...init.headers,
147
+ 'Content-Type': 'application/json'
148
+ };
149
+ }
150
+ }
151
+ const response = await fetch(this.withQuery(path, query), init);
152
+ if (!response.ok) {
153
+ throw new HTTPException(response.status, response.statusText, await response.text());
154
+ }
155
+ return response.json();
156
+ }
157
+ async put(path, query) {
158
+ const response = await fetch(this.withQuery(path, query), this.parseInit("PUT"));
159
+ if (!response.ok) {
160
+ throw new HTTPException(response.status, response.statusText, await response.text());
161
+ }
162
+ return response.json();
163
+ }
164
+ async delete(path, query) {
165
+ const response = await fetch(this.withQuery(path, query), this.parseInit("DELETE"));
166
+ if (!response.ok) {
167
+ throw new HTTPException(response.status, response.statusText, await response.text());
168
+ }
169
+ return response.json();
170
+ }
171
+ async patch(path, query) {
172
+ const response = await fetch(this.withQuery(path, query), this.parseInit("PATCH"));
173
+ if (!response.ok) {
174
+ throw new HTTPException(response.status, response.statusText, await response.text());
175
+ }
176
+ return response.json();
177
+ }
178
+ }
@@ -0,0 +1,15 @@
1
+ import mqtt from 'mqtt';
2
+ export declare class MQTTService {
3
+ private url;
4
+ client: mqtt.MqttClient;
5
+ private static instance;
6
+ private subscribedTopics;
7
+ constructor(url: string);
8
+ static getInstance(url?: string): Promise<MQTTService>;
9
+ static resetInstance(): void;
10
+ addTopicHandler(topic: string, handler: (message: unknown) => void): void;
11
+ clearTopics(): void;
12
+ publish(topic: string, message: unknown): boolean;
13
+ private attachMessageListener;
14
+ private parseMessage;
15
+ }
@@ -0,0 +1,103 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.MQTTService = void 0;
7
+ const mqtt_1 = __importDefault(require("mqtt"));
8
+ class MQTTService {
9
+ constructor(url) {
10
+ this.subscribedTopics = [];
11
+ this.url = url;
12
+ }
13
+ static async getInstance(url = "ws://localhost:180") {
14
+ if (!this.instance || !this.instance.client) {
15
+ this.instance = new MQTTService(url);
16
+ // Ensure the URL is set correctly
17
+ this.instance.url = url;
18
+ // Initialize the MQTT client
19
+ this.instance.client = mqtt_1.default.connect(this.instance.url, { protocolVersion: 5 });
20
+ this.instance.attachMessageListener();
21
+ // console.log("MQTT Client initialized with URL:", this.instance.url);
22
+ }
23
+ return this.instance;
24
+ }
25
+ // Method to reset the instance
26
+ static resetInstance() {
27
+ if (this.instance) {
28
+ this.instance.client.end();
29
+ this.instance = undefined;
30
+ }
31
+ }
32
+ // addTopicHandler Adds a topic and handler to the subscribed list
33
+ addTopicHandler(topic, handler) {
34
+ try {
35
+ this.subscribedTopics.push({ topic, handler });
36
+ if (topic == "")
37
+ return;
38
+ console.log("New Subscription Handler:", topic);
39
+ this.client.subscribe(topic, { qos: 2 }, (err) => {
40
+ if (err) {
41
+ console.error(`Failed to subscribe to topic ${topic}:`, err);
42
+ }
43
+ });
44
+ }
45
+ catch (e) {
46
+ console.error(`Failed to subscribe to topic ${topic}:`, e);
47
+ }
48
+ }
49
+ // clearTopics clears all the topics subscribed to
50
+ clearTopics() {
51
+ this.subscribedTopics = [];
52
+ }
53
+ // Publishes a message to a specific topic
54
+ publish(topic, message) {
55
+ try {
56
+ const payload = JSON.stringify(message);
57
+ console.log("Publishing to", topic, "with payload:", payload);
58
+ this.client.publish(topic, payload, {
59
+ qos: 2,
60
+ retain: true,
61
+ properties: {
62
+ contentType: "json",
63
+ },
64
+ }, (err) => {
65
+ if (err) {
66
+ console.error(`Failed to publish message to ${topic}:`, err);
67
+ }
68
+ });
69
+ }
70
+ catch (e) {
71
+ console.error(`Failed to publish to topic ${topic}:`, e);
72
+ throw e;
73
+ }
74
+ return true;
75
+ }
76
+ attachMessageListener() {
77
+ this.client.on('connect', () => {
78
+ console.log("MQTT connected to", this.url);
79
+ });
80
+ this.client.on('error', (error) => {
81
+ console.error("MQTT connection error:", error);
82
+ });
83
+ this.client.on('message', (topic, message) => {
84
+ for (const subscribedTopic of this.subscribedTopics) {
85
+ if (topic === subscribedTopic.topic) {
86
+ const parsedMessage = this.parseMessage(message);
87
+ subscribedTopic.handler(parsedMessage);
88
+ break;
89
+ }
90
+ }
91
+ });
92
+ }
93
+ parseMessage(message) {
94
+ try {
95
+ return JSON.parse(message.toString());
96
+ }
97
+ catch (error) {
98
+ console.error("Error parsing message:", error);
99
+ return null;
100
+ }
101
+ }
102
+ }
103
+ exports.MQTTService = MQTTService;
@@ -0,0 +1,78 @@
1
+ export declare class NATSService {
2
+ private url;
3
+ private functions;
4
+ private connection;
5
+ private static instance;
6
+ private subscribedSubjects;
7
+ private subscriptions;
8
+ private readonly EXEC_FUNCTION_SUBJECT;
9
+ constructor(url: string);
10
+ /**
11
+ * Returns a singleton instance of NATSService.
12
+ */
13
+ static getInstance(url?: string): Promise<NATSService>;
14
+ /**
15
+ * Resets the singleton instance.
16
+ */
17
+ static resetInstance(): void;
18
+ /**
19
+ * Connects to the NATS broker.
20
+ */
21
+ private connect;
22
+ /**
23
+ * Adds a subject and handler to the subscribed list.
24
+ * @param subject {string} The subject to subscribe to.
25
+ * @param handler {(message: unknown) => void} The handler function for incoming messages.
26
+ */
27
+ addSubjectHandler(subject: string, handler: (message: unknown) => void): void;
28
+ /**
29
+ * Subscribes to the EXEC_FUNCTION_SUBJECT and executes registered functions.
30
+ */
31
+ initializeFunctions(): boolean;
32
+ /**
33
+ * Adds a new function to the registry.
34
+ * @param functionName {string} The name of the function to add.
35
+ * @param func {(data: Record<string, any>) => Promise<Record<string, any>> | Record<string, any>} The function implementation.
36
+ * @throws TypeError if the provided argument is not a function.
37
+ */
38
+ addFunction(functionName: string, func: (data: Record<string, any>) => Promise<Record<string, any>> | Record<string, any>): void;
39
+ /**
40
+ * Executes a registered function by name with the provided arguments.
41
+ * @param functionName {string} The name of the function to execute.
42
+ * @param arg {Record<string, any>} The arguments to pass to the function. (JSON)
43
+ * @returns {Promise<Record<string, any>>} The result of the function execution. (JSON)
44
+ * @throws Error if the function is not found.
45
+ */
46
+ executeFunction(functionName: string, arg: Record<string, any>): Promise<Record<string, any>>;
47
+ /**
48
+ * Clears all subscribed subjects.
49
+ */
50
+ clearSubjects(): void;
51
+ /**
52
+ * Publishes a message to a specific subject.
53
+ * @param subject {string} The subject to publish to.
54
+ * @param message {unknown} The message to publish.
55
+ * @returns {boolean} True if the message was published successfully.
56
+ */
57
+ publish(subject: string, message: unknown): boolean;
58
+ /**
59
+ * Retrieves all registered functions.
60
+ * @returns {Record<string, (data: Record<string, any>) => Record<string, any>} An object containing all registered functions.
61
+ */
62
+ getAllFunctions(): Record<string, (data: Record<string, any>) => Record<string, any>>;
63
+ static log(message?: any, ...optionalParams: any[]): void;
64
+ /**
65
+ * Prints the names of all available functions to the console.
66
+ */
67
+ printFunctions(): void;
68
+ /**
69
+ * Parses a message buffer into a JSON object.
70
+ * @param message {Uint8Array} The message buffer.
71
+ * @returns {unknown} The parsed message.
72
+ */
73
+ private parseMessage;
74
+ /**
75
+ * Gracefully unsubscribes from all NATS subscriptions and closes the connection.
76
+ */
77
+ shutdown(): Promise<void>;
78
+ }
@@ -0,0 +1,237 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.NATSService = void 0;
4
+ const nats_core_1 = require("@nats-io/nats-core");
5
+ const transport_node_1 = require("@nats-io/transport-node");
6
+ const HEALTH_SUBJECT = "runtime.typescript.health";
7
+ class NATSService {
8
+ constructor(url) {
9
+ this.functions = {};
10
+ this.connection = null;
11
+ this.subscribedSubjects = [];
12
+ this.subscriptions = [];
13
+ this.EXEC_FUNCTION_SUBJECT = "runtime.exec.typescript.*";
14
+ this.url = url;
15
+ }
16
+ /**
17
+ * Returns a singleton instance of NATSService.
18
+ */
19
+ static async getInstance(url = "ws://localhost:7500") {
20
+ if (!this.instance || !this.instance.connection) {
21
+ this.instance = new NATSService(url);
22
+ await this.instance.connect();
23
+ }
24
+ return this.instance;
25
+ }
26
+ /**
27
+ * Resets the singleton instance.
28
+ */
29
+ static resetInstance() {
30
+ if (this.instance) {
31
+ this.instance.connection?.close();
32
+ this.instance = undefined;
33
+ }
34
+ }
35
+ /**
36
+ * Connects to the NATS broker.
37
+ */
38
+ async connect() {
39
+ let retries = 0;
40
+ const maxRetries = 10;
41
+ while (!this.connection && retries < maxRetries) {
42
+ try {
43
+ // NATSService.log(`Connecting to NATS server (${this.url})...`);
44
+ if (this.url.startsWith("nats")) {
45
+ this.connection = await (0, transport_node_1.connect)({ servers: this.url });
46
+ }
47
+ else if (this.url.startsWith("ws")) {
48
+ this.connection = await (0, nats_core_1.wsconnect)({ servers: this.url });
49
+ }
50
+ else {
51
+ NATSService.log("ERROR - Unsupported protocol : ", this.url.split("://")[0]);
52
+ }
53
+ this.connection?.publish(HEALTH_SUBJECT, JSON.stringify(true));
54
+ // NATSService.log(`Published message: true to ${HEALTH_SUBJECT}`);
55
+ // NATSService.log("Connected to NATS server");
56
+ }
57
+ catch (err) {
58
+ NATSService.log("Caught an error during connection attempt:", err); // Debugging log
59
+ retries++;
60
+ console.error(`Failed to connect to NATS server. Retrying (${retries}/${maxRetries})...`, err);
61
+ if (retries >= maxRetries) {
62
+ throw new Error("Max retries reached. Unable to connect to NATS server.");
63
+ }
64
+ await new Promise((resolve) => setTimeout(resolve, 5000));
65
+ }
66
+ }
67
+ if (!this.connection) {
68
+ throw new Error("Failed to establish a connection to the NATS server.");
69
+ }
70
+ }
71
+ /**
72
+ * Adds a subject and handler to the subscribed list.
73
+ * @param subject {string} The subject to subscribe to.
74
+ * @param handler {(message: unknown) => void} The handler function for incoming messages.
75
+ */
76
+ addSubjectHandler(subject, handler) {
77
+ try {
78
+ this.subscribedSubjects.push({ subject, handler });
79
+ NATSService.log("New Subscription Handler:", subject);
80
+ const sub = this.connection?.subscribe(subject, {
81
+ callback: (err, msg) => {
82
+ if (err) {
83
+ console.error(`Error handling message for subject ${subject}:`, err);
84
+ return;
85
+ }
86
+ const parsedMessage = this.parseMessage(msg.data);
87
+ handler(parsedMessage);
88
+ },
89
+ });
90
+ if (sub)
91
+ this.subscriptions.push(sub);
92
+ }
93
+ catch (e) {
94
+ console.error(`Failed to subscribe to subject ${subject}:`, e);
95
+ }
96
+ }
97
+ /**
98
+ * Subscribes to the EXEC_FUNCTION_SUBJECT and executes registered functions.
99
+ */
100
+ initializeFunctions() {
101
+ if (!this.connection) {
102
+ throw new Error("No active connection. Please connect to the NATS server first.");
103
+ }
104
+ const sub = this.connection.subscribe(this.EXEC_FUNCTION_SUBJECT, {
105
+ callback: (err, msg) => {
106
+ if (err) {
107
+ console.error("Error handling message:", err);
108
+ return;
109
+ }
110
+ const data = JSON.parse(msg.data.toString());
111
+ const subjectParts = msg.subject.split(".");
112
+ if (subjectParts.length !== 4) {
113
+ msg.respond(JSON.stringify({ error: "Invalid subject format" }));
114
+ return;
115
+ }
116
+ const functionName = subjectParts[3];
117
+ this.executeFunction(functionName, data)
118
+ .then((result) => {
119
+ msg.respond(JSON.stringify(result));
120
+ })
121
+ .catch((e) => {
122
+ msg.respond(JSON.stringify({ status: "failed", error: e.message }));
123
+ });
124
+ },
125
+ });
126
+ if (sub)
127
+ this.subscriptions.push(sub);
128
+ NATSService.log(`Subscribed to '${this.EXEC_FUNCTION_SUBJECT}'`);
129
+ return true;
130
+ }
131
+ /**
132
+ * Adds a new function to the registry.
133
+ * @param functionName {string} The name of the function to add.
134
+ * @param func {(data: Record<string, any>) => Promise<Record<string, any>> | Record<string, any>} The function implementation.
135
+ * @throws TypeError if the provided argument is not a function.
136
+ */
137
+ addFunction(functionName, func) {
138
+ if (typeof func !== "function") {
139
+ throw new TypeError(`The provided argument '${functionName}' is not a function.`);
140
+ }
141
+ this.functions[functionName] = func;
142
+ }
143
+ /**
144
+ * Executes a registered function by name with the provided arguments.
145
+ * @param functionName {string} The name of the function to execute.
146
+ * @param arg {Record<string, any>} The arguments to pass to the function. (JSON)
147
+ * @returns {Promise<Record<string, any>>} The result of the function execution. (JSON)
148
+ * @throws Error if the function is not found.
149
+ */
150
+ async executeFunction(functionName, arg) {
151
+ const func = this.functions[functionName];
152
+ if (!func) {
153
+ throw new Error(`Function '${functionName}' not found`);
154
+ }
155
+ return await func(arg);
156
+ }
157
+ /**
158
+ * Clears all subscribed subjects.
159
+ */
160
+ clearSubjects() {
161
+ this.subscribedSubjects = [];
162
+ }
163
+ /**
164
+ * Publishes a message to a specific subject.
165
+ * @param subject {string} The subject to publish to.
166
+ * @param message {unknown} The message to publish.
167
+ * @returns {boolean} True if the message was published successfully.
168
+ */
169
+ publish(subject, message) {
170
+ try {
171
+ const payload = JSON.stringify(message);
172
+ NATSService.log("Publishing to", subject, "with payload:", payload);
173
+ this.connection?.publish(subject, payload);
174
+ }
175
+ catch (e) {
176
+ console.error(`Failed to publish to subject ${subject}:`, e);
177
+ throw e;
178
+ }
179
+ return true;
180
+ }
181
+ /**
182
+ * Retrieves all registered functions.
183
+ * @returns {Record<string, (data: Record<string, any>) => Record<string, any>} An object containing all registered functions.
184
+ */
185
+ getAllFunctions() {
186
+ return this.functions;
187
+ }
188
+ static log(message, ...optionalParams) {
189
+ console.log("[TYPESCRIPT] >", message, ...optionalParams);
190
+ }
191
+ /**
192
+ * Prints the names of all available functions to the console.
193
+ */
194
+ printFunctions() {
195
+ NATSService.log("Available functions: ", Object.keys(this.functions).join(", "));
196
+ }
197
+ /**
198
+ * Parses a message buffer into a JSON object.
199
+ * @param message {Uint8Array} The message buffer.
200
+ * @returns {unknown} The parsed message.
201
+ */
202
+ parseMessage(message) {
203
+ try {
204
+ return JSON.parse(Buffer.from(message).toString());
205
+ }
206
+ catch (error) {
207
+ console.error("Error parsing message:", error);
208
+ return null;
209
+ }
210
+ }
211
+ /**
212
+ * Gracefully unsubscribes from all NATS subscriptions and closes the connection.
213
+ */
214
+ async shutdown() {
215
+ NATSService.log("Shutting down NATSService...");
216
+ for (const sub of this.subscriptions) {
217
+ try {
218
+ sub.unsubscribe();
219
+ }
220
+ catch (e) {
221
+ NATSService.log("Error unsubscribing:", e);
222
+ }
223
+ }
224
+ this.subscriptions = [];
225
+ if (this.connection) {
226
+ try {
227
+ await this.connection.drain();
228
+ await this.connection.close();
229
+ }
230
+ catch (e) {
231
+ NATSService.log("Error closing NATS connection:", e);
232
+ }
233
+ }
234
+ NATSService.log("NATSService shutdown complete.");
235
+ }
236
+ }
237
+ exports.NATSService = NATSService;