@machhub-dev/sdk-ts 1.0.17 → 1.0.18

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.
@@ -24,9 +24,7 @@ export declare class Collection {
24
24
  expand?: string | string[];
25
25
  fields?: string | string[];
26
26
  }): Promise<any[]>;
27
- count(options?: {
28
- filter?: any;
29
- }): Promise<number>;
27
+ count(): Promise<number>;
30
28
  getOne(id: string, options?: {
31
29
  expand?: string | string[];
32
30
  }): Promise<any>;
@@ -66,12 +66,9 @@ class Collection {
66
66
  throw new CollectionError('getAll', this.collectionName, error);
67
67
  }
68
68
  }
69
- async count(options) {
69
+ async count() {
70
70
  try {
71
- this.applyOptions(options);
72
- // Build query parameters for filters
73
71
  const response = await this.httpService.request.get(`${this.collectionName}/count`, this.queryParams);
74
- // Extract count from response
75
72
  return response.count;
76
73
  }
77
74
  catch (error) {
@@ -1,6 +1,13 @@
1
1
  import { HTTPService } from "../services/http.service.js";
2
2
  import { MQTTService } from "../services/mqtt.service.js";
3
3
  import { HistorizedData } from "../types/tag.models.js";
4
+ interface AggregationOption {
5
+ mean: "mean";
6
+ sum: "sum";
7
+ min: "min";
8
+ max: "max";
9
+ median: "median";
10
+ }
4
11
  export declare class Historian {
5
12
  private httpService;
6
13
  private mqttService;
@@ -10,4 +17,20 @@ export declare class Historian {
10
17
  subscribeLiveData(topic: string, callback: (data: any) => void): Promise<any>;
11
18
  getLastNValues(topic: string, n: number): Promise<HistorizedData[]>;
12
19
  query(SurrealQL: string): Promise<any>;
20
+ /**
21
+ * Export historized data for one or more topics as a gzipped CSV file (Blob).
22
+ * When multiple topics are provided, they are merged into a single CSV with columns: [Timestamp, topic1, topic2, ...].
23
+ * Supports optional time bucketing (sampleRate) and aggregation (mean, sum, min, max, median).
24
+ *
25
+ * @param topics - Array of topic strings to export
26
+ * @param startDate - Start date for the data range
27
+ * @param endDate - End date for the data range
28
+ * @param timezone - Optional Timezone string (e.g. "Asia/Kuala Lumpur")
29
+ * @param sampleRate - Optional bucket interval in underscore format (e.g. "5_second", "1_minute", "1_hour")
30
+ * @param aggregation - Optional aggregation function: "mean" | "sum" | "min" | "max" | "median" | "none"
31
+ * @param mapping - Optional key-value pairs to rename topic columns in the CSV header (e.g. { "Sensor/Temperature": "Temp °C" })
32
+ * @returns A Blob containing the gzipped CSV data
33
+ */
34
+ getHistoricalDataAsCSV(topics: string[], startDate: Date, endDate: Date, timezone?: string, sampleRate?: string, aggregation?: AggregationOption, mapping?: Record<string, string>): Promise<Blob>;
13
35
  }
36
+ export {};
@@ -41,5 +41,30 @@ class Historian {
41
41
  query: SurrealQL
42
42
  }).post("historian/query");
43
43
  }
44
+ /**
45
+ * Export historized data for one or more topics as a gzipped CSV file (Blob).
46
+ * When multiple topics are provided, they are merged into a single CSV with columns: [Timestamp, topic1, topic2, ...].
47
+ * Supports optional time bucketing (sampleRate) and aggregation (mean, sum, min, max, median).
48
+ *
49
+ * @param topics - Array of topic strings to export
50
+ * @param startDate - Start date for the data range
51
+ * @param endDate - End date for the data range
52
+ * @param timezone - Optional Timezone string (e.g. "Asia/Kuala Lumpur")
53
+ * @param sampleRate - Optional bucket interval in underscore format (e.g. "5_second", "1_minute", "1_hour")
54
+ * @param aggregation - Optional aggregation function: "mean" | "sum" | "min" | "max" | "median" | "none"
55
+ * @param mapping - Optional key-value pairs to rename topic columns in the CSV header (e.g. { "Sensor/Temperature": "Temp °C" })
56
+ * @returns A Blob containing the gzipped CSV data
57
+ */
58
+ async getHistoricalDataAsCSV(topics, startDate, endDate, timezone, sampleRate, aggregation, mapping) {
59
+ return this.httpService.request.withJSON({
60
+ topics,
61
+ startDate: startDate.toISOString(),
62
+ endDate: endDate.toISOString(),
63
+ timezone,
64
+ sampleRate: sampleRate ?? "",
65
+ aggregation: aggregation ?? "",
66
+ mapping: mapping ?? {},
67
+ }).postAsBlob("historian/export/aggregated");
68
+ }
44
69
  }
45
70
  exports.Historian = Historian;
@@ -145,7 +145,8 @@ class SDK {
145
145
  host += envCfg.pathHosted;
146
146
  }
147
147
  if (!config.httpUrl) {
148
- config.httpUrl = `${secured ? 'https' : 'http'}://${host}`;
148
+ const httpHost = envCfg.hostingMode === 'path' ? `${host}/` : host;
149
+ config.httpUrl = `${secured ? 'https' : 'http'}://${httpHost}`;
149
150
  }
150
151
  if (!config.mqttUrl) {
151
152
  config.mqttUrl = `${secured ? 'wss' : 'ws'}://${host}/mqtt`;
@@ -40,6 +40,7 @@ declare class RequestParameters {
40
40
  withJSON(body: Record<string, unknown>): RequestParameters;
41
41
  get<ReturnType>(path: string, query?: Record<string, string>): Promise<ReturnType>;
42
42
  post<ReturnType>(path: string, query?: Record<string, string>, body?: FormData | Record<string, string>): Promise<ReturnType>;
43
+ postAsBlob(path: string, query?: Record<string, string>): Promise<Blob>;
43
44
  put<ReturnType>(path: string, query?: Record<string, string>): Promise<ReturnType>;
44
45
  delete<ReturnType>(path: string, query?: Record<string, string>): Promise<ReturnType>;
45
46
  patch<ReturnType>(path: string, query?: Record<string, string>): Promise<ReturnType>;
@@ -40,7 +40,7 @@ class RequestParameters {
40
40
  this.withRuntimeID(); // Ensure withRuntimeID() is called by default
41
41
  }
42
42
  withQuery(path, query) {
43
- const newPath = [this.base.pathname, path].map(pathPart => pathPart.replace(/(^\/|\/$)/g, "")).join("/");
43
+ const newPath = "/" + [this.base.pathname, path].map(pathPart => pathPart.replace(/(^\/|\/$)/g, "")).join("/");
44
44
  const newURL = new URL(newPath, this.base);
45
45
  for (const key in query) {
46
46
  newURL.searchParams.append(key, query[key]);
@@ -183,6 +183,14 @@ class RequestParameters {
183
183
  }
184
184
  return response.json();
185
185
  }
186
+ async postAsBlob(path, query) {
187
+ const init = this.parseInit("POST") || {};
188
+ const response = await fetch(this.withQuery(path, query), init);
189
+ if (!response.ok) {
190
+ throw new HTTPException(response.status, response.statusText, await response.text());
191
+ }
192
+ return response.blob();
193
+ }
186
194
  async put(path, query) {
187
195
  const response = await fetch(this.withQuery(path, query), this.parseInit("PUT"));
188
196
  if (!response.ok) {
@@ -24,9 +24,7 @@ export declare class Collection {
24
24
  expand?: string | string[];
25
25
  fields?: string | string[];
26
26
  }): Promise<any[]>;
27
- count(options?: {
28
- filter?: any;
29
- }): Promise<number>;
27
+ count(): Promise<number>;
30
28
  getOne(id: string, options?: {
31
29
  expand?: string | string[];
32
30
  }): Promise<any>;
@@ -62,12 +62,9 @@ export class Collection {
62
62
  throw new CollectionError('getAll', this.collectionName, error);
63
63
  }
64
64
  }
65
- async count(options) {
65
+ async count() {
66
66
  try {
67
- this.applyOptions(options);
68
- // Build query parameters for filters
69
67
  const response = await this.httpService.request.get(`${this.collectionName}/count`, this.queryParams);
70
- // Extract count from response
71
68
  return response.count;
72
69
  }
73
70
  catch (error) {
@@ -1,6 +1,13 @@
1
1
  import { HTTPService } from "../services/http.service.js";
2
2
  import { MQTTService } from "../services/mqtt.service.js";
3
3
  import { HistorizedData } from "../types/tag.models.js";
4
+ interface AggregationOption {
5
+ mean: "mean";
6
+ sum: "sum";
7
+ min: "min";
8
+ max: "max";
9
+ median: "median";
10
+ }
4
11
  export declare class Historian {
5
12
  private httpService;
6
13
  private mqttService;
@@ -10,4 +17,20 @@ export declare class Historian {
10
17
  subscribeLiveData(topic: string, callback: (data: any) => void): Promise<any>;
11
18
  getLastNValues(topic: string, n: number): Promise<HistorizedData[]>;
12
19
  query(SurrealQL: string): Promise<any>;
20
+ /**
21
+ * Export historized data for one or more topics as a gzipped CSV file (Blob).
22
+ * When multiple topics are provided, they are merged into a single CSV with columns: [Timestamp, topic1, topic2, ...].
23
+ * Supports optional time bucketing (sampleRate) and aggregation (mean, sum, min, max, median).
24
+ *
25
+ * @param topics - Array of topic strings to export
26
+ * @param startDate - Start date for the data range
27
+ * @param endDate - End date for the data range
28
+ * @param timezone - Optional Timezone string (e.g. "Asia/Kuala Lumpur")
29
+ * @param sampleRate - Optional bucket interval in underscore format (e.g. "5_second", "1_minute", "1_hour")
30
+ * @param aggregation - Optional aggregation function: "mean" | "sum" | "min" | "max" | "median" | "none"
31
+ * @param mapping - Optional key-value pairs to rename topic columns in the CSV header (e.g. { "Sensor/Temperature": "Temp °C" })
32
+ * @returns A Blob containing the gzipped CSV data
33
+ */
34
+ getHistoricalDataAsCSV(topics: string[], startDate: Date, endDate: Date, timezone?: string, sampleRate?: string, aggregation?: AggregationOption, mapping?: Record<string, string>): Promise<Blob>;
13
35
  }
36
+ export {};
@@ -38,4 +38,29 @@ export class Historian {
38
38
  query: SurrealQL
39
39
  }).post("historian/query");
40
40
  }
41
+ /**
42
+ * Export historized data for one or more topics as a gzipped CSV file (Blob).
43
+ * When multiple topics are provided, they are merged into a single CSV with columns: [Timestamp, topic1, topic2, ...].
44
+ * Supports optional time bucketing (sampleRate) and aggregation (mean, sum, min, max, median).
45
+ *
46
+ * @param topics - Array of topic strings to export
47
+ * @param startDate - Start date for the data range
48
+ * @param endDate - End date for the data range
49
+ * @param timezone - Optional Timezone string (e.g. "Asia/Kuala Lumpur")
50
+ * @param sampleRate - Optional bucket interval in underscore format (e.g. "5_second", "1_minute", "1_hour")
51
+ * @param aggregation - Optional aggregation function: "mean" | "sum" | "min" | "max" | "median" | "none"
52
+ * @param mapping - Optional key-value pairs to rename topic columns in the CSV header (e.g. { "Sensor/Temperature": "Temp °C" })
53
+ * @returns A Blob containing the gzipped CSV data
54
+ */
55
+ async getHistoricalDataAsCSV(topics, startDate, endDate, timezone, sampleRate, aggregation, mapping) {
56
+ return this.httpService.request.withJSON({
57
+ topics,
58
+ startDate: startDate.toISOString(),
59
+ endDate: endDate.toISOString(),
60
+ timezone,
61
+ sampleRate: sampleRate ?? "",
62
+ aggregation: aggregation ?? "",
63
+ mapping: mapping ?? {},
64
+ }).postAsBlob("historian/export/aggregated");
65
+ }
41
66
  }
package/dist/sdk-ts.js CHANGED
@@ -142,7 +142,8 @@ export class SDK {
142
142
  host += envCfg.pathHosted;
143
143
  }
144
144
  if (!config.httpUrl) {
145
- config.httpUrl = `${secured ? 'https' : 'http'}://${host}`;
145
+ const httpHost = envCfg.hostingMode === 'path' ? `${host}/` : host;
146
+ config.httpUrl = `${secured ? 'https' : 'http'}://${httpHost}`;
146
147
  }
147
148
  if (!config.mqttUrl) {
148
149
  config.mqttUrl = `${secured ? 'wss' : 'ws'}://${host}/mqtt`;
@@ -40,6 +40,7 @@ declare class RequestParameters {
40
40
  withJSON(body: Record<string, unknown>): RequestParameters;
41
41
  get<ReturnType>(path: string, query?: Record<string, string>): Promise<ReturnType>;
42
42
  post<ReturnType>(path: string, query?: Record<string, string>, body?: FormData | Record<string, string>): Promise<ReturnType>;
43
+ postAsBlob(path: string, query?: Record<string, string>): Promise<Blob>;
43
44
  put<ReturnType>(path: string, query?: Record<string, string>): Promise<ReturnType>;
44
45
  delete<ReturnType>(path: string, query?: Record<string, string>): Promise<ReturnType>;
45
46
  patch<ReturnType>(path: string, query?: Record<string, string>): Promise<ReturnType>;
@@ -35,7 +35,7 @@ class RequestParameters {
35
35
  this.withRuntimeID(); // Ensure withRuntimeID() is called by default
36
36
  }
37
37
  withQuery(path, query) {
38
- const newPath = [this.base.pathname, path].map(pathPart => pathPart.replace(/(^\/|\/$)/g, "")).join("/");
38
+ const newPath = "/" + [this.base.pathname, path].map(pathPart => pathPart.replace(/(^\/|\/$)/g, "")).join("/");
39
39
  const newURL = new URL(newPath, this.base);
40
40
  for (const key in query) {
41
41
  newURL.searchParams.append(key, query[key]);
@@ -178,6 +178,14 @@ class RequestParameters {
178
178
  }
179
179
  return response.json();
180
180
  }
181
+ async postAsBlob(path, query) {
182
+ const init = this.parseInit("POST") || {};
183
+ const response = await fetch(this.withQuery(path, query), init);
184
+ if (!response.ok) {
185
+ throw new HTTPException(response.status, response.statusText, await response.text());
186
+ }
187
+ return response.blob();
188
+ }
181
189
  async put(path, query) {
182
190
  const response = await fetch(this.withQuery(path, query), this.parseInit("PUT"));
183
191
  if (!response.ok) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@machhub-dev/sdk-ts",
3
- "version": "1.0.17",
3
+ "version": "1.0.18",
4
4
  "description": "MACHHUB TYPESCRIPT SDK",
5
5
  "keywords": [
6
6
  "machhub",
@@ -84,14 +84,9 @@ export class Collection {
84
84
  }
85
85
  }
86
86
 
87
- async count(options?: { filter?: any }): Promise<number> {
87
+ async count(): Promise<number> {
88
88
  try {
89
- this.applyOptions(options);
90
-
91
- // Build query parameters for filters
92
89
  const response: { count: number } = await this.httpService.request.get(`${this.collectionName}/count`, this.queryParams);
93
-
94
- // Extract count from response
95
90
  return response.count;
96
91
  } catch (error) {
97
92
  throw new CollectionError('count', this.collectionName, error as Error);
@@ -2,6 +2,15 @@ import { HTTPService } from "../services/http.service.js";
2
2
  import { MQTTService } from "../services/mqtt.service.js";
3
3
  import { HistorizedData } from "../types/tag.models.js";
4
4
 
5
+ interface AggregationOption {
6
+ mean: "mean",
7
+ sum: "sum",
8
+ min: "min",
9
+ max: "max",
10
+ median: "median",
11
+ }
12
+
13
+
5
14
  export class Historian {
6
15
  private httpService: HTTPService;
7
16
  private mqttService: MQTTService | null;
@@ -36,7 +45,7 @@ export class Historian {
36
45
  throw new Error("The number of values to fetch must be greater than 0.");
37
46
  }
38
47
 
39
- if (n > 100){
48
+ if (n > 100) {
40
49
  throw new Error("The number of values to fetch must be less than 100.");
41
50
  }
42
51
 
@@ -47,9 +56,43 @@ export class Historian {
47
56
  }).patch("historian/last");
48
57
  }
49
58
 
50
- async query(SurrealQL:string): Promise<any> {
59
+ async query(SurrealQL: string): Promise<any> {
51
60
  return this.httpService.request.withJSON({
52
61
  query: SurrealQL
53
62
  }).post("historian/query");
54
63
  }
64
+
65
+ /**
66
+ * Export historized data for one or more topics as a gzipped CSV file (Blob).
67
+ * When multiple topics are provided, they are merged into a single CSV with columns: [Timestamp, topic1, topic2, ...].
68
+ * Supports optional time bucketing (sampleRate) and aggregation (mean, sum, min, max, median).
69
+ *
70
+ * @param topics - Array of topic strings to export
71
+ * @param startDate - Start date for the data range
72
+ * @param endDate - End date for the data range
73
+ * @param timezone - Optional Timezone string (e.g. "Asia/Kuala Lumpur")
74
+ * @param sampleRate - Optional bucket interval in underscore format (e.g. "5_second", "1_minute", "1_hour")
75
+ * @param aggregation - Optional aggregation function: "mean" | "sum" | "min" | "max" | "median" | "none"
76
+ * @param mapping - Optional key-value pairs to rename topic columns in the CSV header (e.g. { "Sensor/Temperature": "Temp °C" })
77
+ * @returns A Blob containing the gzipped CSV data
78
+ */
79
+ async getHistoricalDataAsCSV(
80
+ topics: string[],
81
+ startDate: Date,
82
+ endDate: Date,
83
+ timezone?: string,
84
+ sampleRate?: string,
85
+ aggregation?: AggregationOption,
86
+ mapping?: Record<string, string>,
87
+ ): Promise<Blob> {
88
+ return this.httpService.request.withJSON({
89
+ topics,
90
+ startDate: startDate.toISOString(),
91
+ endDate: endDate.toISOString(),
92
+ timezone,
93
+ sampleRate: sampleRate ?? "",
94
+ aggregation: aggregation ?? "",
95
+ mapping: mapping ?? {},
96
+ }).postAsBlob("historian/export/aggregated");
97
+ }
55
98
  }
package/src/sdk-ts.ts CHANGED
@@ -138,7 +138,6 @@ export class SDK {
138
138
  if (!config.application_id) config = { application_id: "" }
139
139
 
140
140
  const envCfg = await getEnvConfig();
141
-
142
141
  // Extract application_id from runtimeID if not provided in config
143
142
  const application_id = config.application_id ||
144
143
  this.extractApplicationIDFromRuntimeID(envCfg.runtimeID);
@@ -164,7 +163,8 @@ export class SDK {
164
163
  }
165
164
 
166
165
  if (!config.httpUrl) {
167
- config.httpUrl = `${secured ? 'https' : 'http'}://${host}`;
166
+ const httpHost = envCfg.hostingMode === 'path' ? `${host}/` : host;
167
+ config.httpUrl = `${secured ? 'https' : 'http'}://${httpHost}`;
168
168
  }
169
169
 
170
170
  if (!config.mqttUrl) {
@@ -56,7 +56,7 @@ class RequestParameters {
56
56
  }
57
57
 
58
58
  private withQuery(path: string, query?: Record<string, string>): URL {
59
- const newPath = [this.base.pathname, path].map(pathPart => pathPart.replace(/(^\/|\/$)/g, "")).join("/");
59
+ const newPath = "/" + [this.base.pathname, path].map(pathPart => pathPart.replace(/(^\/|\/$)/g, "")).join("/");
60
60
  const newURL = new URL(newPath, this.base);
61
61
 
62
62
  for (const key in query) {
@@ -231,6 +231,22 @@ class RequestParameters {
231
231
  }
232
232
 
233
233
 
234
+ public async postAsBlob(path: string, query?: Record<string, string>): Promise<Blob> {
235
+ const init: RequestInit = this.parseInit("POST") || {};
236
+
237
+ const response = await fetch(this.withQuery(path, query), init);
238
+
239
+ if (!response.ok) {
240
+ throw new HTTPException(
241
+ response.status,
242
+ response.statusText,
243
+ await response.text(),
244
+ );
245
+ }
246
+ return response.blob();
247
+ }
248
+
249
+
234
250
  public async put<ReturnType>(path: string, query?: Record<string, string>): Promise<ReturnType> {
235
251
  const response = await fetch(this.withQuery(path, query), this.parseInit("PUT"));
236
252