@machhub-dev/sdk-ts 0.0.6 → 0.0.7
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 +41 -6
- package/dist/cjs/sdk-ts.js +83 -4
- package/dist/cjs/services/mqtt.service.js +2 -2
- package/dist/classes/collection.d.ts +6 -0
- package/dist/classes/collection.js +39 -5
- package/dist/sdk-ts.js +83 -4
- package/dist/services/mqtt.service.js +2 -2
- package/package.json +1 -1
- package/src/classes/collection.ts +39 -5
- package/src/sdk-ts.ts +93 -4
- package/src/services/mqtt.service.ts +2 -2
|
@@ -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,53 @@ 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
|
+
return await this.httpService.request.withJSON(data).post(this.collectionName);
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
throw new CollectionError('create', this.collectionName, error);
|
|
62
|
+
}
|
|
38
63
|
}
|
|
39
64
|
async update(id, data) {
|
|
40
65
|
if (!id) {
|
|
41
66
|
throw new Error("ID must be provided");
|
|
42
67
|
}
|
|
43
|
-
|
|
68
|
+
try {
|
|
69
|
+
return await this.httpService.request.withJSON(data).put(id);
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
throw new CollectionError('update', this.collectionName, error);
|
|
73
|
+
}
|
|
44
74
|
}
|
|
45
75
|
async delete(id) {
|
|
46
76
|
if (!id) {
|
|
47
77
|
throw new Error("ID must be provided");
|
|
48
78
|
}
|
|
49
|
-
|
|
79
|
+
try {
|
|
80
|
+
return await this.httpService.request.delete(id);
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
throw new CollectionError('delete', this.collectionName, error);
|
|
84
|
+
}
|
|
50
85
|
}
|
|
51
86
|
}
|
|
52
87
|
exports.Collection = Collection;
|
package/dist/cjs/sdk-ts.js
CHANGED
|
@@ -148,7 +148,7 @@ class SDK {
|
|
|
148
148
|
config = { application_id: "" };
|
|
149
149
|
// console.log("Using application_id:", config.application_id);
|
|
150
150
|
const PORT = await getEnvPort();
|
|
151
|
-
|
|
151
|
+
console.log("Port:", PORT);
|
|
152
152
|
if (!config.httpUrl) {
|
|
153
153
|
config.httpUrl = "http://localhost:" + PORT;
|
|
154
154
|
}
|
|
@@ -159,7 +159,7 @@ class SDK {
|
|
|
159
159
|
config.natsUrl = "ws://localhost:" + PORT + "/nats";
|
|
160
160
|
}
|
|
161
161
|
const { application_id, httpUrl, mqttUrl, natsUrl } = config;
|
|
162
|
-
|
|
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
163
|
this.http = new HTTPClient(application_id, httpUrl, config.developer_key);
|
|
164
164
|
this.mqtt = await MQTTClient.getInstance(application_id, mqttUrl, config.developer_key);
|
|
165
165
|
this.nats = await NATSClient.getInstance(application_id, natsUrl);
|
|
@@ -236,7 +236,9 @@ class SDK {
|
|
|
236
236
|
exports.SDK = SDK;
|
|
237
237
|
async function getEnvPort() {
|
|
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]);
|
|
@@ -248,13 +250,90 @@ async function getEnvPort() {
|
|
|
248
250
|
return "61888";
|
|
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}`);
|
|
@@ -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,
|
|
@@ -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,52 @@ 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
|
+
return await this.httpService.request.withJSON(data).post(this.collectionName);
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
throw new CollectionError('create', this.collectionName, error);
|
|
58
|
+
}
|
|
35
59
|
}
|
|
36
60
|
async update(id, data) {
|
|
37
61
|
if (!id) {
|
|
38
62
|
throw new Error("ID must be provided");
|
|
39
63
|
}
|
|
40
|
-
|
|
64
|
+
try {
|
|
65
|
+
return await this.httpService.request.withJSON(data).put(id);
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
throw new CollectionError('update', this.collectionName, error);
|
|
69
|
+
}
|
|
41
70
|
}
|
|
42
71
|
async delete(id) {
|
|
43
72
|
if (!id) {
|
|
44
73
|
throw new Error("ID must be provided");
|
|
45
74
|
}
|
|
46
|
-
|
|
75
|
+
try {
|
|
76
|
+
return await this.httpService.request.delete(id);
|
|
77
|
+
}
|
|
78
|
+
catch (error) {
|
|
79
|
+
throw new CollectionError('delete', this.collectionName, error);
|
|
80
|
+
}
|
|
47
81
|
}
|
|
48
82
|
}
|
package/dist/sdk-ts.js
CHANGED
|
@@ -145,7 +145,7 @@ export class SDK {
|
|
|
145
145
|
config = { application_id: "" };
|
|
146
146
|
// console.log("Using application_id:", config.application_id);
|
|
147
147
|
const PORT = await getEnvPort();
|
|
148
|
-
|
|
148
|
+
console.log("Port:", PORT);
|
|
149
149
|
if (!config.httpUrl) {
|
|
150
150
|
config.httpUrl = "http://localhost:" + PORT;
|
|
151
151
|
}
|
|
@@ -156,7 +156,7 @@ export class SDK {
|
|
|
156
156
|
config.natsUrl = "ws://localhost:" + PORT + "/nats";
|
|
157
157
|
}
|
|
158
158
|
const { application_id, httpUrl, mqttUrl, natsUrl } = config;
|
|
159
|
-
|
|
159
|
+
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('') });
|
|
160
160
|
this.http = new HTTPClient(application_id, httpUrl, config.developer_key);
|
|
161
161
|
this.mqtt = await MQTTClient.getInstance(application_id, mqttUrl, config.developer_key);
|
|
162
162
|
this.nats = await NATSClient.getInstance(application_id, natsUrl);
|
|
@@ -232,7 +232,9 @@ export class SDK {
|
|
|
232
232
|
}
|
|
233
233
|
async function getEnvPort() {
|
|
234
234
|
try {
|
|
235
|
-
|
|
235
|
+
// Try to find the configuration endpoint by testing different base paths
|
|
236
|
+
const configUrl = await findConfigEndpoint();
|
|
237
|
+
const response = await fetchData(configUrl);
|
|
236
238
|
// console.log('Response:', response);
|
|
237
239
|
// console.log('runtimeID: ', response.runtimeID);
|
|
238
240
|
// console.log('applicationID: ', response.runtimeID.split('XmchX')[0]);
|
|
@@ -244,13 +246,90 @@ async function getEnvPort() {
|
|
|
244
246
|
return "61888";
|
|
245
247
|
}
|
|
246
248
|
}
|
|
247
|
-
|
|
249
|
+
/**
|
|
250
|
+
* Attempts to find the correct configuration endpoint by trying different base paths
|
|
251
|
+
* Handles both port-based hosting (direct) and path-based hosting (reverse proxy)
|
|
252
|
+
*/
|
|
253
|
+
async function findConfigEndpoint() {
|
|
254
|
+
const origin = window.location.origin;
|
|
255
|
+
const pathname = window.location.pathname;
|
|
256
|
+
// List of potential base paths to try, ordered by likelihood
|
|
257
|
+
const basePaths = [
|
|
258
|
+
// 1. Try origin directly (for port-based hosting like localhost:6190)
|
|
259
|
+
origin,
|
|
260
|
+
// 2. Try current path segments for path-based hosting
|
|
261
|
+
...generatePathCandidates(pathname),
|
|
262
|
+
// 3. Try common root paths as fallback
|
|
263
|
+
origin,
|
|
264
|
+
];
|
|
265
|
+
// Remove duplicates while preserving order
|
|
266
|
+
const uniqueBasePaths = [...new Set(basePaths)];
|
|
267
|
+
for (const basePath of uniqueBasePaths) {
|
|
268
|
+
try {
|
|
269
|
+
const configUrl = `${basePath}/_cfg`;
|
|
270
|
+
// Test if this endpoint returns valid JSON config by making a GET request
|
|
271
|
+
const testResponse = await fetch(configUrl, {
|
|
272
|
+
method: 'GET',
|
|
273
|
+
headers: {
|
|
274
|
+
'Accept': 'application/json',
|
|
275
|
+
},
|
|
276
|
+
signal: AbortSignal.timeout(2000) // 2 second timeout
|
|
277
|
+
});
|
|
278
|
+
if (testResponse.ok) {
|
|
279
|
+
// Validate that the response is JSON and contains the expected 'port' field
|
|
280
|
+
const contentType = testResponse.headers.get('content-type');
|
|
281
|
+
if (contentType && contentType.includes('application/json')) {
|
|
282
|
+
try {
|
|
283
|
+
const testData = await testResponse.json();
|
|
284
|
+
// Check if the response has the expected structure with a 'port' field
|
|
285
|
+
if (testData && typeof testData === 'object' && 'port' in testData) {
|
|
286
|
+
// console.log(`Found config endpoint at: ${configUrl}`);
|
|
287
|
+
return configUrl;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
catch (jsonError) {
|
|
291
|
+
// Not valid JSON, continue to next candidate
|
|
292
|
+
continue;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
catch (error) {
|
|
298
|
+
// Continue to next candidate
|
|
299
|
+
continue;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
// If all attempts fail, default to origin + /_cfg
|
|
303
|
+
console.warn('Could not find config endpoint, using default origin');
|
|
304
|
+
return `${origin}/_cfg`;
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* Generates potential base path candidates from the current pathname
|
|
308
|
+
* For example, /demo2/homepage/settings would generate:
|
|
309
|
+
* - http://localhost/demo2/homepage
|
|
310
|
+
* - http://localhost/demo2
|
|
311
|
+
* - http://localhost
|
|
312
|
+
*/
|
|
313
|
+
function generatePathCandidates(pathname) {
|
|
314
|
+
const origin = window.location.origin;
|
|
315
|
+
const pathSegments = pathname.split('/').filter(segment => segment.length > 0);
|
|
316
|
+
const candidates = [];
|
|
317
|
+
// Generate paths by progressively removing segments from the end
|
|
318
|
+
for (let i = pathSegments.length; i > 0; i--) {
|
|
319
|
+
const path = '/' + pathSegments.slice(0, i).join('/');
|
|
320
|
+
candidates.push(origin + path);
|
|
321
|
+
}
|
|
322
|
+
return candidates;
|
|
323
|
+
}
|
|
324
|
+
async function fetchData(url, options) {
|
|
248
325
|
try {
|
|
249
326
|
const response = await fetch(url, {
|
|
250
327
|
method: 'GET',
|
|
251
328
|
headers: {
|
|
252
329
|
'Accept': 'application/json',
|
|
253
330
|
},
|
|
331
|
+
signal: AbortSignal.timeout(5000), // 5 second timeout
|
|
332
|
+
...options
|
|
254
333
|
});
|
|
255
334
|
if (!response.ok) {
|
|
256
335
|
throw new Error(`HTTP error! status: ${response.status}`);
|
|
@@ -36,7 +36,7 @@ export class MQTTService {
|
|
|
36
36
|
this.subscribedTopics.push({ topic, handler });
|
|
37
37
|
if (topic == "")
|
|
38
38
|
return;
|
|
39
|
-
console.log("New Subscription Handler:", topic);
|
|
39
|
+
// console.log("New Subscription Handler:", topic);
|
|
40
40
|
this.client.subscribe(topic, { qos: 2 }, (err) => {
|
|
41
41
|
if (err) {
|
|
42
42
|
console.error(`Failed to subscribe to topic ${topic}:`, err);
|
|
@@ -55,7 +55,7 @@ export class MQTTService {
|
|
|
55
55
|
publish(topic, message) {
|
|
56
56
|
try {
|
|
57
57
|
const payload = JSON.stringify(message);
|
|
58
|
-
console.log("Publishing to", topic, "with payload:", payload);
|
|
58
|
+
// console.log("Publishing to", topic, "with payload:", payload);
|
|
59
59
|
this.client.publish(topic, payload, {
|
|
60
60
|
qos: 2,
|
|
61
61
|
retain: true,
|
package/package.json
CHANGED
|
@@ -1,6 +1,20 @@
|
|
|
1
1
|
import { HTTPService } from "../services/http.service.js";
|
|
2
2
|
import { MQTTService } from "../services/mqtt.service.js";
|
|
3
3
|
|
|
4
|
+
export class CollectionError extends Error {
|
|
5
|
+
public operation: string;
|
|
6
|
+
public collectionName: string;
|
|
7
|
+
public originalError: Error;
|
|
8
|
+
|
|
9
|
+
constructor(operation: string, collectionName: string, originalError: Error) {
|
|
10
|
+
super(`Collection operation '${operation}' failed on '${collectionName}': ${originalError.message}`);
|
|
11
|
+
this.name = 'CollectionError';
|
|
12
|
+
this.operation = operation;
|
|
13
|
+
this.collectionName = collectionName;
|
|
14
|
+
this.originalError = originalError;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
4
18
|
export class Collection {
|
|
5
19
|
protected httpService: HTTPService;
|
|
6
20
|
protected mqttService: MQTTService | null;
|
|
@@ -34,31 +48,51 @@ export class Collection {
|
|
|
34
48
|
}
|
|
35
49
|
|
|
36
50
|
async getAll(): Promise<any[]> {
|
|
37
|
-
|
|
51
|
+
try {
|
|
52
|
+
return await this.httpService.request.get(this.collectionName + "/all", this.queryParams);
|
|
53
|
+
} catch (error) {
|
|
54
|
+
throw new CollectionError('getAll', this.collectionName, error as Error);
|
|
55
|
+
}
|
|
38
56
|
}
|
|
39
57
|
|
|
40
58
|
async getOne(id: string): Promise<any> {
|
|
41
59
|
if (!id) {
|
|
42
60
|
throw new Error("ID must be provided");
|
|
43
61
|
}
|
|
44
|
-
|
|
62
|
+
try {
|
|
63
|
+
return await this.httpService.request.get(id);
|
|
64
|
+
} catch (error) {
|
|
65
|
+
throw new CollectionError('getOne', this.collectionName, error as Error);
|
|
66
|
+
}
|
|
45
67
|
}
|
|
46
68
|
|
|
47
69
|
async create(data: Record<string, any>): Promise<any> {
|
|
48
|
-
|
|
70
|
+
try {
|
|
71
|
+
return await this.httpService.request.withJSON(data).post(this.collectionName);
|
|
72
|
+
} catch (error) {
|
|
73
|
+
throw new CollectionError('create', this.collectionName, error as Error);
|
|
74
|
+
}
|
|
49
75
|
}
|
|
50
76
|
|
|
51
77
|
async update(id: string, data: Record<string, any>): Promise<any> {
|
|
52
78
|
if (!id) {
|
|
53
79
|
throw new Error("ID must be provided");
|
|
54
80
|
}
|
|
55
|
-
|
|
81
|
+
try {
|
|
82
|
+
return await this.httpService.request.withJSON(data).put(id);
|
|
83
|
+
} catch (error) {
|
|
84
|
+
throw new CollectionError('update', this.collectionName, error as Error);
|
|
85
|
+
}
|
|
56
86
|
}
|
|
57
87
|
|
|
58
88
|
async delete(id: string): Promise<any> {
|
|
59
89
|
if (!id) {
|
|
60
90
|
throw new Error("ID must be provided");
|
|
61
91
|
}
|
|
62
|
-
|
|
92
|
+
try {
|
|
93
|
+
return await this.httpService.request.delete(id);
|
|
94
|
+
} catch (error) {
|
|
95
|
+
throw new CollectionError('delete', this.collectionName, error as Error);
|
|
96
|
+
}
|
|
63
97
|
}
|
|
64
98
|
}
|
package/src/sdk-ts.ts
CHANGED
|
@@ -174,7 +174,7 @@ export class SDK {
|
|
|
174
174
|
|
|
175
175
|
|
|
176
176
|
const PORT = await getEnvPort();
|
|
177
|
-
|
|
177
|
+
console.log("Port:", PORT);
|
|
178
178
|
|
|
179
179
|
if (!config.httpUrl) {
|
|
180
180
|
config.httpUrl = "http://localhost:" + PORT;
|
|
@@ -190,7 +190,7 @@ export class SDK {
|
|
|
190
190
|
|
|
191
191
|
const { application_id, httpUrl, mqttUrl, natsUrl } = config;
|
|
192
192
|
|
|
193
|
-
|
|
193
|
+
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('') });
|
|
194
194
|
|
|
195
195
|
this.http = new HTTPClient(application_id, httpUrl, config.developer_key);
|
|
196
196
|
this.mqtt = await MQTTClient.getInstance(application_id, mqttUrl, config.developer_key);
|
|
@@ -275,7 +275,9 @@ export class SDK {
|
|
|
275
275
|
|
|
276
276
|
async function getEnvPort(): Promise<string> {
|
|
277
277
|
try {
|
|
278
|
-
|
|
278
|
+
// Try to find the configuration endpoint by testing different base paths
|
|
279
|
+
const configUrl = await findConfigEndpoint();
|
|
280
|
+
const response = await fetchData<{runtimeID:string, port:string}>(configUrl);
|
|
279
281
|
// console.log('Response:', response);
|
|
280
282
|
// console.log('runtimeID: ', response.runtimeID);
|
|
281
283
|
// console.log('applicationID: ', response.runtimeID.split('XmchX')[0]);
|
|
@@ -287,14 +289,101 @@ async function getEnvPort(): Promise<string> {
|
|
|
287
289
|
}
|
|
288
290
|
}
|
|
289
291
|
|
|
292
|
+
/**
|
|
293
|
+
* Attempts to find the correct configuration endpoint by trying different base paths
|
|
294
|
+
* Handles both port-based hosting (direct) and path-based hosting (reverse proxy)
|
|
295
|
+
*/
|
|
296
|
+
async function findConfigEndpoint(): Promise<string> {
|
|
297
|
+
const origin = window.location.origin;
|
|
298
|
+
const pathname = window.location.pathname;
|
|
299
|
+
|
|
300
|
+
// List of potential base paths to try, ordered by likelihood
|
|
301
|
+
const basePaths = [
|
|
302
|
+
// 1. Try origin directly (for port-based hosting like localhost:6190)
|
|
303
|
+
origin,
|
|
304
|
+
|
|
305
|
+
// 2. Try current path segments for path-based hosting
|
|
306
|
+
...generatePathCandidates(pathname),
|
|
307
|
+
|
|
308
|
+
// 3. Try common root paths as fallback
|
|
309
|
+
origin,
|
|
310
|
+
];
|
|
311
|
+
|
|
312
|
+
// Remove duplicates while preserving order
|
|
313
|
+
const uniqueBasePaths = [...new Set(basePaths)];
|
|
314
|
+
|
|
315
|
+
for (const basePath of uniqueBasePaths) {
|
|
316
|
+
try {
|
|
317
|
+
const configUrl = `${basePath}/_cfg`;
|
|
318
|
+
|
|
319
|
+
// Test if this endpoint returns valid JSON config by making a GET request
|
|
320
|
+
const testResponse = await fetch(configUrl, {
|
|
321
|
+
method: 'GET',
|
|
322
|
+
headers: {
|
|
323
|
+
'Accept': 'application/json',
|
|
324
|
+
},
|
|
325
|
+
signal: AbortSignal.timeout(2000) // 2 second timeout
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
if (testResponse.ok) {
|
|
329
|
+
// Validate that the response is JSON and contains the expected 'port' field
|
|
330
|
+
const contentType = testResponse.headers.get('content-type');
|
|
331
|
+
if (contentType && contentType.includes('application/json')) {
|
|
332
|
+
try {
|
|
333
|
+
const testData = await testResponse.json();
|
|
334
|
+
// Check if the response has the expected structure with a 'port' field
|
|
335
|
+
if (testData && typeof testData === 'object' && 'port' in testData) {
|
|
336
|
+
// console.log(`Found config endpoint at: ${configUrl}`);
|
|
337
|
+
return configUrl;
|
|
338
|
+
}
|
|
339
|
+
} catch (jsonError) {
|
|
340
|
+
// Not valid JSON, continue to next candidate
|
|
341
|
+
continue;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
} catch (error) {
|
|
346
|
+
// Continue to next candidate
|
|
347
|
+
continue;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// If all attempts fail, default to origin + /_cfg
|
|
352
|
+
console.warn('Could not find config endpoint, using default origin');
|
|
353
|
+
return `${origin}/_cfg`;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Generates potential base path candidates from the current pathname
|
|
358
|
+
* For example, /demo2/homepage/settings would generate:
|
|
359
|
+
* - http://localhost/demo2/homepage
|
|
360
|
+
* - http://localhost/demo2
|
|
361
|
+
* - http://localhost
|
|
362
|
+
*/
|
|
363
|
+
function generatePathCandidates(pathname: string): string[] {
|
|
364
|
+
const origin = window.location.origin;
|
|
365
|
+
const pathSegments = pathname.split('/').filter(segment => segment.length > 0);
|
|
366
|
+
const candidates: string[] = [];
|
|
367
|
+
|
|
368
|
+
// Generate paths by progressively removing segments from the end
|
|
369
|
+
for (let i = pathSegments.length; i > 0; i--) {
|
|
370
|
+
const path = '/' + pathSegments.slice(0, i).join('/');
|
|
371
|
+
candidates.push(origin + path);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
return candidates;
|
|
375
|
+
}
|
|
376
|
+
|
|
290
377
|
|
|
291
|
-
async function fetchData<T>(url: string): Promise<T> {
|
|
378
|
+
async function fetchData<T>(url: string, options?: RequestInit): Promise<T> {
|
|
292
379
|
try {
|
|
293
380
|
const response = await fetch(url, {
|
|
294
381
|
method: 'GET',
|
|
295
382
|
headers: {
|
|
296
383
|
'Accept': 'application/json',
|
|
297
384
|
},
|
|
385
|
+
signal: AbortSignal.timeout(5000), // 5 second timeout
|
|
386
|
+
...options
|
|
298
387
|
});
|
|
299
388
|
|
|
300
389
|
if (!response.ok) {
|
|
@@ -50,7 +50,7 @@ export class MQTTService {
|
|
|
50
50
|
try {
|
|
51
51
|
this.subscribedTopics.push({ topic, handler });
|
|
52
52
|
if (topic == "") return;
|
|
53
|
-
console.log("New Subscription Handler:", topic);
|
|
53
|
+
// console.log("New Subscription Handler:", topic);
|
|
54
54
|
this.client.subscribe(topic, { qos: 2 }, (err?: unknown) => {
|
|
55
55
|
if (err) {
|
|
56
56
|
console.error(`Failed to subscribe to topic ${topic}:`, err);
|
|
@@ -70,7 +70,7 @@ export class MQTTService {
|
|
|
70
70
|
public publish(topic: string, message: unknown): boolean {
|
|
71
71
|
try {
|
|
72
72
|
const payload = JSON.stringify(message);
|
|
73
|
-
console.log("Publishing to", topic, "with payload:", payload);
|
|
73
|
+
// console.log("Publishing to", topic, "with payload:", payload);
|
|
74
74
|
|
|
75
75
|
this.client.publish(topic, payload, {
|
|
76
76
|
qos: 2,
|