@machhub-dev/sdk-ts 0.0.6 → 0.0.8
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/dist/cjs/classes/collection.d.ts +6 -0
- package/dist/cjs/classes/collection.js +51 -6
- package/dist/cjs/sdk-ts.js +93 -14
- package/dist/cjs/services/http.service.d.ts +5 -4
- package/dist/cjs/services/http.service.js +16 -23
- package/dist/cjs/services/mqtt.service.js +2 -2
- package/dist/cjs/services/nats.service.d.ts +2 -2
- package/dist/cjs/services/nats.service.js +45 -17
- package/dist/classes/collection.d.ts +6 -0
- package/dist/classes/collection.js +49 -5
- package/dist/sdk-ts.js +93 -14
- package/dist/services/http.service.d.ts +5 -4
- package/dist/services/http.service.js +16 -23
- package/dist/services/mqtt.service.js +2 -2
- package/dist/services/nats.service.d.ts +2 -2
- package/dist/services/nats.service.js +45 -17
- package/package.json +5 -5
- package/src/classes/collection.ts +51 -5
- package/src/sdk-ts.ts +103 -14
- package/src/services/http.service.ts +19 -26
- package/src/services/mqtt.service.ts +2 -2
- package/src/services/nats.service.ts +49 -18
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { HTTPService } from "../services/http.service.js";
|
|
2
2
|
import { MQTTService } from "../services/mqtt.service.js";
|
|
3
|
+
export declare class CollectionError extends Error {
|
|
4
|
+
operation: string;
|
|
5
|
+
collectionName: string;
|
|
6
|
+
originalError: Error;
|
|
7
|
+
constructor(operation: string, collectionName: string, originalError: Error);
|
|
8
|
+
}
|
|
3
9
|
export declare class Collection {
|
|
4
10
|
protected httpService: HTTPService;
|
|
5
11
|
protected mqttService: MQTTService | null;
|
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.Collection = void 0;
|
|
3
|
+
exports.Collection = exports.CollectionError = void 0;
|
|
4
|
+
class CollectionError extends Error {
|
|
5
|
+
constructor(operation, collectionName, originalError) {
|
|
6
|
+
super(`Collection operation '${operation}' failed on '${collectionName}': ${originalError.message}`);
|
|
7
|
+
this.name = 'CollectionError';
|
|
8
|
+
this.operation = operation;
|
|
9
|
+
this.collectionName = collectionName;
|
|
10
|
+
this.originalError = originalError;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
exports.CollectionError = CollectionError;
|
|
4
14
|
class Collection {
|
|
5
15
|
constructor(httpService, mqttService, collectionName) {
|
|
6
16
|
this.queryParams = {};
|
|
@@ -25,28 +35,63 @@ class Collection {
|
|
|
25
35
|
return this;
|
|
26
36
|
}
|
|
27
37
|
async getAll() {
|
|
28
|
-
|
|
38
|
+
try {
|
|
39
|
+
return await this.httpService.request.get(this.collectionName + "/all", this.queryParams);
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
throw new CollectionError('getAll', this.collectionName, error);
|
|
43
|
+
}
|
|
29
44
|
}
|
|
30
45
|
async getOne(id) {
|
|
31
46
|
if (!id) {
|
|
32
47
|
throw new Error("ID must be provided");
|
|
33
48
|
}
|
|
34
|
-
|
|
49
|
+
try {
|
|
50
|
+
return await this.httpService.request.get(id);
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
throw new CollectionError('getOne', this.collectionName, error);
|
|
54
|
+
}
|
|
35
55
|
}
|
|
36
56
|
async create(data) {
|
|
37
|
-
|
|
57
|
+
try {
|
|
58
|
+
const formData = new FormData();
|
|
59
|
+
for (const [key, value] of Object.entries(data)) {
|
|
60
|
+
if (value instanceof File) {
|
|
61
|
+
formData.append(key, value, value.name);
|
|
62
|
+
data[key] = value.name;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
formData.append("record", JSON.stringify(data));
|
|
66
|
+
return await this.httpService.request
|
|
67
|
+
.withBody(formData)
|
|
68
|
+
.post(this.collectionName);
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
throw new CollectionError("create", this.collectionName, error);
|
|
72
|
+
}
|
|
38
73
|
}
|
|
39
74
|
async update(id, data) {
|
|
40
75
|
if (!id) {
|
|
41
76
|
throw new Error("ID must be provided");
|
|
42
77
|
}
|
|
43
|
-
|
|
78
|
+
try {
|
|
79
|
+
return await this.httpService.request.withJSON(data).put(id);
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
throw new CollectionError('update', this.collectionName, error);
|
|
83
|
+
}
|
|
44
84
|
}
|
|
45
85
|
async delete(id) {
|
|
46
86
|
if (!id) {
|
|
47
87
|
throw new Error("ID must be provided");
|
|
48
88
|
}
|
|
49
|
-
|
|
89
|
+
try {
|
|
90
|
+
return await this.httpService.request.delete(id);
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
throw new CollectionError('delete', this.collectionName, error);
|
|
94
|
+
}
|
|
50
95
|
}
|
|
51
96
|
}
|
|
52
97
|
exports.Collection = Collection;
|
package/dist/cjs/sdk-ts.js
CHANGED
|
@@ -18,8 +18,8 @@ class HTTPClient {
|
|
|
18
18
|
* @param applicationID The ID for your application (required)
|
|
19
19
|
* @param httpUrl The base URL for HTTP connection (default = http://localhost:6188)
|
|
20
20
|
*/
|
|
21
|
-
constructor(applicationID, httpUrl, developerKey) {
|
|
22
|
-
this.httpService = new http_service_js_1.HTTPService(httpUrl, MACHHUB_SDK_PATH, applicationID, developerKey);
|
|
21
|
+
constructor(applicationID, httpUrl, developerKey, runtimeID) {
|
|
22
|
+
this.httpService = new http_service_js_1.HTTPService(httpUrl, MACHHUB_SDK_PATH, applicationID, developerKey, runtimeID);
|
|
23
23
|
}
|
|
24
24
|
/**
|
|
25
25
|
* Gets server info
|
|
@@ -147,20 +147,20 @@ class SDK {
|
|
|
147
147
|
if (!config.application_id)
|
|
148
148
|
config = { application_id: "" };
|
|
149
149
|
// console.log("Using application_id:", config.application_id);
|
|
150
|
-
const
|
|
151
|
-
|
|
150
|
+
const { port, runtimeID } = await getEnvConfig();
|
|
151
|
+
console.log("Port:", port);
|
|
152
152
|
if (!config.httpUrl) {
|
|
153
|
-
config.httpUrl = "http://localhost:" +
|
|
153
|
+
config.httpUrl = "http://localhost:" + port;
|
|
154
154
|
}
|
|
155
155
|
if (!config.mqttUrl) {
|
|
156
|
-
config.mqttUrl = "ws://localhost:" +
|
|
156
|
+
config.mqttUrl = "ws://localhost:" + port + "/mqtt";
|
|
157
157
|
}
|
|
158
158
|
if (!config.natsUrl) {
|
|
159
|
-
config.natsUrl = "ws://localhost:" +
|
|
159
|
+
config.natsUrl = "ws://localhost:" + port + "/nats";
|
|
160
160
|
}
|
|
161
161
|
const { application_id, httpUrl, mqttUrl, natsUrl } = config;
|
|
162
|
-
|
|
163
|
-
this.http = new HTTPClient(application_id, httpUrl, config.developer_key);
|
|
162
|
+
console.log("SDK Config:", { application_id, httpUrl, mqttUrl, natsUrl, developer_key: config.developer_key?.split('').map((_, i) => i < config.developer_key.length - 4 ? '*' : config.developer_key[i]).join('') });
|
|
163
|
+
this.http = new HTTPClient(application_id, httpUrl, config.developer_key, runtimeID);
|
|
164
164
|
this.mqtt = await MQTTClient.getInstance(application_id, mqttUrl, config.developer_key);
|
|
165
165
|
this.nats = await NATSClient.getInstance(application_id, natsUrl);
|
|
166
166
|
this._historian = new historian_js_1.Historian(this.http["httpService"], this.mqtt["mqttService"]);
|
|
@@ -234,27 +234,106 @@ class SDK {
|
|
|
234
234
|
}
|
|
235
235
|
}
|
|
236
236
|
exports.SDK = SDK;
|
|
237
|
-
async function
|
|
237
|
+
async function getEnvConfig() {
|
|
238
238
|
try {
|
|
239
|
-
|
|
239
|
+
// Try to find the configuration endpoint by testing different base paths
|
|
240
|
+
const configUrl = await findConfigEndpoint();
|
|
241
|
+
const response = await fetchData(configUrl);
|
|
240
242
|
// console.log('Response:', response);
|
|
241
243
|
// console.log('runtimeID: ', response.runtimeID);
|
|
242
244
|
// console.log('applicationID: ', response.runtimeID.split('XmchX')[0]);
|
|
243
|
-
return response.port;
|
|
245
|
+
return { port: response.port, runtimeID: response.runtimeID };
|
|
244
246
|
}
|
|
245
247
|
catch (error) {
|
|
246
248
|
// console.log('No configured runtime ID:', error);
|
|
247
249
|
// TODO: Use DevPort from SDK Config or default to 61888
|
|
248
|
-
return "61888";
|
|
250
|
+
return { port: "61888", runtimeID: "" };
|
|
249
251
|
}
|
|
250
252
|
}
|
|
251
|
-
|
|
253
|
+
/**
|
|
254
|
+
* Attempts to find the correct configuration endpoint by trying different base paths
|
|
255
|
+
* Handles both port-based hosting (direct) and path-based hosting (reverse proxy)
|
|
256
|
+
*/
|
|
257
|
+
async function findConfigEndpoint() {
|
|
258
|
+
const origin = window.location.origin;
|
|
259
|
+
const pathname = window.location.pathname;
|
|
260
|
+
// List of potential base paths to try, ordered by likelihood
|
|
261
|
+
const basePaths = [
|
|
262
|
+
// 1. Try origin directly (for port-based hosting like localhost:6190)
|
|
263
|
+
origin,
|
|
264
|
+
// 2. Try current path segments for path-based hosting
|
|
265
|
+
...generatePathCandidates(pathname),
|
|
266
|
+
// 3. Try common root paths as fallback
|
|
267
|
+
origin,
|
|
268
|
+
];
|
|
269
|
+
// Remove duplicates while preserving order
|
|
270
|
+
const uniqueBasePaths = [...new Set(basePaths)];
|
|
271
|
+
for (const basePath of uniqueBasePaths) {
|
|
272
|
+
try {
|
|
273
|
+
const configUrl = `${basePath}/_cfg`;
|
|
274
|
+
// Test if this endpoint returns valid JSON config by making a GET request
|
|
275
|
+
const testResponse = await fetch(configUrl, {
|
|
276
|
+
method: 'GET',
|
|
277
|
+
headers: {
|
|
278
|
+
'Accept': 'application/json',
|
|
279
|
+
},
|
|
280
|
+
signal: AbortSignal.timeout(2000) // 2 second timeout
|
|
281
|
+
});
|
|
282
|
+
if (testResponse.ok) {
|
|
283
|
+
// Validate that the response is JSON and contains the expected 'port' field
|
|
284
|
+
const contentType = testResponse.headers.get('content-type');
|
|
285
|
+
if (contentType && contentType.includes('application/json')) {
|
|
286
|
+
try {
|
|
287
|
+
const testData = await testResponse.json();
|
|
288
|
+
// Check if the response has the expected structure with a 'port' field
|
|
289
|
+
if (testData && typeof testData === 'object' && 'port' in testData) {
|
|
290
|
+
// console.log(`Found config endpoint at: ${configUrl}`);
|
|
291
|
+
return configUrl;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
catch (jsonError) {
|
|
295
|
+
// Not valid JSON, continue to next candidate
|
|
296
|
+
continue;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
catch (error) {
|
|
302
|
+
// Continue to next candidate
|
|
303
|
+
continue;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
// If all attempts fail, default to origin + /_cfg
|
|
307
|
+
console.warn('Could not find config endpoint, using default origin');
|
|
308
|
+
return `${origin}/_cfg`;
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Generates potential base path candidates from the current pathname
|
|
312
|
+
* For example, /demo2/homepage/settings would generate:
|
|
313
|
+
* - http://localhost/demo2/homepage
|
|
314
|
+
* - http://localhost/demo2
|
|
315
|
+
* - http://localhost
|
|
316
|
+
*/
|
|
317
|
+
function generatePathCandidates(pathname) {
|
|
318
|
+
const origin = window.location.origin;
|
|
319
|
+
const pathSegments = pathname.split('/').filter(segment => segment.length > 0);
|
|
320
|
+
const candidates = [];
|
|
321
|
+
// Generate paths by progressively removing segments from the end
|
|
322
|
+
for (let i = pathSegments.length; i > 0; i--) {
|
|
323
|
+
const path = '/' + pathSegments.slice(0, i).join('/');
|
|
324
|
+
candidates.push(origin + path);
|
|
325
|
+
}
|
|
326
|
+
return candidates;
|
|
327
|
+
}
|
|
328
|
+
async function fetchData(url, options) {
|
|
252
329
|
try {
|
|
253
330
|
const response = await fetch(url, {
|
|
254
331
|
method: 'GET',
|
|
255
332
|
headers: {
|
|
256
333
|
'Accept': 'application/json',
|
|
257
334
|
},
|
|
335
|
+
signal: AbortSignal.timeout(5000), // 5 second timeout
|
|
336
|
+
...options
|
|
258
337
|
});
|
|
259
338
|
if (!response.ok) {
|
|
260
339
|
throw new Error(`HTTP error! status: ${response.status}`);
|
|
@@ -9,19 +9,19 @@ export declare class HTTPService {
|
|
|
9
9
|
private url;
|
|
10
10
|
private applicationID;
|
|
11
11
|
private developerKey?;
|
|
12
|
-
|
|
12
|
+
private runtimeID?;
|
|
13
|
+
constructor(url: string, prefix: string, applicationID: string, developerKey?: string, runtimeID?: string);
|
|
13
14
|
get request(): RequestParameters;
|
|
14
|
-
private addRuntimeHeaders;
|
|
15
|
-
private getCookie;
|
|
16
15
|
}
|
|
17
16
|
declare class RequestParameters {
|
|
18
17
|
private base;
|
|
19
18
|
private applicationID;
|
|
20
19
|
private developerKey?;
|
|
20
|
+
private runtimeID?;
|
|
21
21
|
query?: Record<string, string>;
|
|
22
22
|
init?: RequestInit;
|
|
23
23
|
headers?: Record<string, string>;
|
|
24
|
-
constructor(base: URL, applicationID: string, developerKey?: string, query?: Record<string, string>);
|
|
24
|
+
constructor(base: URL, applicationID: string, developerKey?: string, runtimeID?: string, query?: Record<string, string>);
|
|
25
25
|
private withQuery;
|
|
26
26
|
private parseInit;
|
|
27
27
|
withBody(body: BodyInit): RequestParameters;
|
|
@@ -33,6 +33,7 @@ declare class RequestParameters {
|
|
|
33
33
|
setHeader(key: string, value: string): RequestParameters;
|
|
34
34
|
setBearerToken(token: string): RequestParameters;
|
|
35
35
|
withAccessToken(): RequestParameters;
|
|
36
|
+
withRuntimeID(): RequestParameters;
|
|
36
37
|
withDomain(): RequestParameters;
|
|
37
38
|
withDeveloperKey(): RequestParameters;
|
|
38
39
|
withContentType(mime: string): RequestParameters;
|
|
@@ -14,47 +14,30 @@ class HTTPException extends Error {
|
|
|
14
14
|
}
|
|
15
15
|
exports.HTTPException = HTTPException;
|
|
16
16
|
class HTTPService {
|
|
17
|
-
constructor(url, prefix, applicationID, developerKey) {
|
|
17
|
+
constructor(url, prefix, applicationID, developerKey, runtimeID) {
|
|
18
18
|
if (prefix == null)
|
|
19
19
|
prefix = "";
|
|
20
20
|
this.url = new URL(prefix, url);
|
|
21
21
|
this.applicationID = "domains:" + applicationID;
|
|
22
22
|
this.developerKey = developerKey;
|
|
23
|
+
this.runtimeID = runtimeID;
|
|
23
24
|
}
|
|
24
25
|
get request() {
|
|
25
|
-
return new RequestParameters(this.url, this.applicationID, this.developerKey);
|
|
26
|
-
}
|
|
27
|
-
addRuntimeHeaders(headers = {}) {
|
|
28
|
-
// Add runtime ID from cookie if available (for hosted applications)
|
|
29
|
-
if (typeof document !== 'undefined') {
|
|
30
|
-
const runtimeID = this.getCookie('machhub_runtime_id');
|
|
31
|
-
if (runtimeID) {
|
|
32
|
-
headers['X-MachHub-Runtime-ID'] = runtimeID;
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
return headers;
|
|
36
|
-
}
|
|
37
|
-
getCookie(name) {
|
|
38
|
-
if (typeof document === 'undefined')
|
|
39
|
-
return null;
|
|
40
|
-
const value = `; ${document.cookie}`;
|
|
41
|
-
const parts = value.split(`; ${name}=`);
|
|
42
|
-
if (parts.length === 2) {
|
|
43
|
-
return parts.pop()?.split(';').shift() || null;
|
|
44
|
-
}
|
|
45
|
-
return null;
|
|
26
|
+
return new RequestParameters(this.url, this.applicationID, this.developerKey, this.runtimeID);
|
|
46
27
|
}
|
|
47
28
|
}
|
|
48
29
|
exports.HTTPService = HTTPService;
|
|
49
30
|
class RequestParameters {
|
|
50
|
-
constructor(base, applicationID, developerKey, query) {
|
|
31
|
+
constructor(base, applicationID, developerKey, runtimeID, query) {
|
|
51
32
|
this.base = base;
|
|
52
33
|
this.applicationID = applicationID;
|
|
53
34
|
this.developerKey = developerKey;
|
|
35
|
+
this.runtimeID = runtimeID;
|
|
54
36
|
this.query = query;
|
|
55
37
|
this.withDomain(); // Ensure withDomain() is called by default
|
|
56
38
|
this.withAccessToken(); // Ensure withAccessToken() is called by default
|
|
57
39
|
this.withDeveloperKey(); // Ensure withDeveloperKey() is called by default
|
|
40
|
+
this.withRuntimeID(); // Ensure withRuntimeID() is called by default
|
|
58
41
|
}
|
|
59
42
|
withQuery(path, query) {
|
|
60
43
|
const newPath = [this.base.pathname, path].map(pathPart => pathPart.replace(/(^\/|\/$)/g, "")).join("/");
|
|
@@ -138,6 +121,10 @@ class RequestParameters {
|
|
|
138
121
|
this.setHeader("Authorization", `Bearer ${tkn}`);
|
|
139
122
|
return this;
|
|
140
123
|
}
|
|
124
|
+
withRuntimeID() {
|
|
125
|
+
this.setHeader("X-Machhub-Runtime-Id", this.runtimeID ?? "");
|
|
126
|
+
return this;
|
|
127
|
+
}
|
|
141
128
|
withDomain() {
|
|
142
129
|
this.setHeader("Domain", this.applicationID);
|
|
143
130
|
return this;
|
|
@@ -168,6 +155,12 @@ class RequestParameters {
|
|
|
168
155
|
if (body) {
|
|
169
156
|
if (body instanceof FormData) {
|
|
170
157
|
init.body = body;
|
|
158
|
+
// Remove Content-Type header if it exists, let browser set it for FormData
|
|
159
|
+
if (init.headers && typeof init.headers === 'object') {
|
|
160
|
+
const headers = init.headers;
|
|
161
|
+
delete headers['Content-Type'];
|
|
162
|
+
delete headers['content-type'];
|
|
163
|
+
}
|
|
171
164
|
}
|
|
172
165
|
else {
|
|
173
166
|
init.body = JSON.stringify(body);
|
|
@@ -42,7 +42,7 @@ class MQTTService {
|
|
|
42
42
|
this.subscribedTopics.push({ topic, handler });
|
|
43
43
|
if (topic == "")
|
|
44
44
|
return;
|
|
45
|
-
console.log("New Subscription Handler:", topic);
|
|
45
|
+
// console.log("New Subscription Handler:", topic);
|
|
46
46
|
this.client.subscribe(topic, { qos: 2 }, (err) => {
|
|
47
47
|
if (err) {
|
|
48
48
|
console.error(`Failed to subscribe to topic ${topic}:`, err);
|
|
@@ -61,7 +61,7 @@ class MQTTService {
|
|
|
61
61
|
publish(topic, message) {
|
|
62
62
|
try {
|
|
63
63
|
const payload = JSON.stringify(message);
|
|
64
|
-
console.log("Publishing to", topic, "with payload:", payload);
|
|
64
|
+
// console.log("Publishing to", topic, "with payload:", payload);
|
|
65
65
|
this.client.publish(topic, payload, {
|
|
66
66
|
qos: 2,
|
|
67
67
|
retain: true,
|
|
@@ -66,9 +66,9 @@ export declare class NATSService {
|
|
|
66
66
|
*/
|
|
67
67
|
printFunctions(): void;
|
|
68
68
|
/**
|
|
69
|
-
* Parses a message buffer into a JSON object.
|
|
69
|
+
* Parses a message buffer into a JSON object or returns raw data.
|
|
70
70
|
* @param message {Uint8Array} The message buffer.
|
|
71
|
-
* @returns {unknown} The parsed message.
|
|
71
|
+
* @returns {unknown} The parsed message (JSON object) or the raw string if parsing fails.
|
|
72
72
|
*/
|
|
73
73
|
private parseMessage;
|
|
74
74
|
/**
|
|
@@ -107,20 +107,31 @@ class NATSService {
|
|
|
107
107
|
console.error("Error handling message:", err);
|
|
108
108
|
return;
|
|
109
109
|
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
msg.
|
|
114
|
-
|
|
110
|
+
try {
|
|
111
|
+
const dataStr = Buffer.from(msg.data).toString('utf-8');
|
|
112
|
+
const data = JSON.parse(dataStr);
|
|
113
|
+
const subjectParts = msg.subject.split(".");
|
|
114
|
+
if (subjectParts.length !== 4) {
|
|
115
|
+
msg.respond(JSON.stringify({ error: "Invalid subject format" }));
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
const functionName = subjectParts[3];
|
|
119
|
+
this.executeFunction(functionName, data)
|
|
120
|
+
.then((result) => {
|
|
121
|
+
msg.respond(JSON.stringify(result));
|
|
122
|
+
})
|
|
123
|
+
.catch((e) => {
|
|
124
|
+
console.log("Error executing function '" + functionName + "': ", e);
|
|
125
|
+
msg.respond(JSON.stringify({ status: "failed", error: e.message }));
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
catch (parseError) {
|
|
129
|
+
console.error("Error parsing function execution message:", parseError);
|
|
130
|
+
msg.respond(JSON.stringify({
|
|
131
|
+
status: "failed",
|
|
132
|
+
error: `Failed to parse message: ${parseError.message}`
|
|
133
|
+
}));
|
|
115
134
|
}
|
|
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
135
|
},
|
|
125
136
|
});
|
|
126
137
|
if (sub)
|
|
@@ -195,17 +206,34 @@ class NATSService {
|
|
|
195
206
|
NATSService.log("Available functions: ", Object.keys(this.functions).join(", "));
|
|
196
207
|
}
|
|
197
208
|
/**
|
|
198
|
-
* Parses a message buffer into a JSON object.
|
|
209
|
+
* Parses a message buffer into a JSON object or returns raw data.
|
|
199
210
|
* @param message {Uint8Array} The message buffer.
|
|
200
|
-
* @returns {unknown} The parsed message.
|
|
211
|
+
* @returns {unknown} The parsed message (JSON object) or the raw string if parsing fails.
|
|
201
212
|
*/
|
|
202
213
|
parseMessage(message) {
|
|
203
214
|
try {
|
|
204
|
-
|
|
215
|
+
const messageStr = Buffer.from(message).toString('utf-8');
|
|
216
|
+
// Check if the message is empty
|
|
217
|
+
if (!messageStr || messageStr.trim().length === 0) {
|
|
218
|
+
NATSService.log("Received empty message");
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
// Try to parse as JSON
|
|
222
|
+
return JSON.parse(messageStr);
|
|
205
223
|
}
|
|
206
224
|
catch (error) {
|
|
225
|
+
// If JSON parsing fails, check if it's binary data or non-JSON content
|
|
226
|
+
const messageStr = Buffer.from(message).toString('utf-8');
|
|
227
|
+
// Check if it looks like binary data (contains non-printable characters)
|
|
228
|
+
const isBinary = message.some(byte => byte < 32 && byte !== 9 && byte !== 10 && byte !== 13);
|
|
229
|
+
if (isBinary) {
|
|
230
|
+
NATSService.log("Received binary data, returning as Uint8Array");
|
|
231
|
+
return message; // Return raw binary data
|
|
232
|
+
}
|
|
233
|
+
// If it's a plain string (not JSON), return as is
|
|
234
|
+
NATSService.log("Failed to parse message as JSON, returning as string:", messageStr.substring(0, 100));
|
|
207
235
|
console.error("Error parsing message:", error);
|
|
208
|
-
return
|
|
236
|
+
return messageStr;
|
|
209
237
|
}
|
|
210
238
|
}
|
|
211
239
|
/**
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { HTTPService } from "../services/http.service.js";
|
|
2
2
|
import { MQTTService } from "../services/mqtt.service.js";
|
|
3
|
+
export declare class CollectionError extends Error {
|
|
4
|
+
operation: string;
|
|
5
|
+
collectionName: string;
|
|
6
|
+
originalError: Error;
|
|
7
|
+
constructor(operation: string, collectionName: string, originalError: Error);
|
|
8
|
+
}
|
|
3
9
|
export declare class Collection {
|
|
4
10
|
protected httpService: HTTPService;
|
|
5
11
|
protected mqttService: MQTTService | null;
|
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
export class CollectionError extends Error {
|
|
2
|
+
constructor(operation, collectionName, originalError) {
|
|
3
|
+
super(`Collection operation '${operation}' failed on '${collectionName}': ${originalError.message}`);
|
|
4
|
+
this.name = 'CollectionError';
|
|
5
|
+
this.operation = operation;
|
|
6
|
+
this.collectionName = collectionName;
|
|
7
|
+
this.originalError = originalError;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
1
10
|
export class Collection {
|
|
2
11
|
constructor(httpService, mqttService, collectionName) {
|
|
3
12
|
this.queryParams = {};
|
|
@@ -22,27 +31,62 @@ export class Collection {
|
|
|
22
31
|
return this;
|
|
23
32
|
}
|
|
24
33
|
async getAll() {
|
|
25
|
-
|
|
34
|
+
try {
|
|
35
|
+
return await this.httpService.request.get(this.collectionName + "/all", this.queryParams);
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
throw new CollectionError('getAll', this.collectionName, error);
|
|
39
|
+
}
|
|
26
40
|
}
|
|
27
41
|
async getOne(id) {
|
|
28
42
|
if (!id) {
|
|
29
43
|
throw new Error("ID must be provided");
|
|
30
44
|
}
|
|
31
|
-
|
|
45
|
+
try {
|
|
46
|
+
return await this.httpService.request.get(id);
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
throw new CollectionError('getOne', this.collectionName, error);
|
|
50
|
+
}
|
|
32
51
|
}
|
|
33
52
|
async create(data) {
|
|
34
|
-
|
|
53
|
+
try {
|
|
54
|
+
const formData = new FormData();
|
|
55
|
+
for (const [key, value] of Object.entries(data)) {
|
|
56
|
+
if (value instanceof File) {
|
|
57
|
+
formData.append(key, value, value.name);
|
|
58
|
+
data[key] = value.name;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
formData.append("record", JSON.stringify(data));
|
|
62
|
+
return await this.httpService.request
|
|
63
|
+
.withBody(formData)
|
|
64
|
+
.post(this.collectionName);
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
throw new CollectionError("create", this.collectionName, error);
|
|
68
|
+
}
|
|
35
69
|
}
|
|
36
70
|
async update(id, data) {
|
|
37
71
|
if (!id) {
|
|
38
72
|
throw new Error("ID must be provided");
|
|
39
73
|
}
|
|
40
|
-
|
|
74
|
+
try {
|
|
75
|
+
return await this.httpService.request.withJSON(data).put(id);
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
throw new CollectionError('update', this.collectionName, error);
|
|
79
|
+
}
|
|
41
80
|
}
|
|
42
81
|
async delete(id) {
|
|
43
82
|
if (!id) {
|
|
44
83
|
throw new Error("ID must be provided");
|
|
45
84
|
}
|
|
46
|
-
|
|
85
|
+
try {
|
|
86
|
+
return await this.httpService.request.delete(id);
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
throw new CollectionError('delete', this.collectionName, error);
|
|
90
|
+
}
|
|
47
91
|
}
|
|
48
92
|
}
|