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