mqtt-plus 0.9.12 → 0.9.14
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/dst-stage1/mqtt-plus-codec.d.ts +4 -2
- package/dst-stage1/mqtt-plus-codec.js +41 -6
- package/dst-stage1/mqtt-plus-event.js +6 -2
- package/dst-stage1/mqtt-plus-info.d.ts +2 -2
- package/dst-stage1/mqtt-plus-msg.d.ts +3 -3
- package/dst-stage1/mqtt-plus-resource.d.ts +5 -5
- package/dst-stage1/mqtt-plus-resource.js +22 -17
- package/dst-stage1/mqtt-plus-service.js +7 -3
- package/dst-stage1/mqtt-plus-util.d.ts +4 -4
- package/dst-stage1/mqtt-plus-util.js +22 -10
- package/dst-stage2/mqtt-plus.cjs.js +114 -50
- package/dst-stage2/mqtt-plus.esm.js +81 -34
- package/dst-stage2/mqtt-plus.umd.js +13 -19
- package/etc/eslint.mts +2 -1
- package/etc/knip.jsonc +2 -1
- package/etc/stx.conf +9 -0
- package/etc/vite.mts +12 -2
- package/package.json +7 -3
- package/src/mqtt-plus-base.ts +3 -3
- package/src/mqtt-plus-codec.ts +53 -10
- package/src/mqtt-plus-event.ts +7 -2
- package/src/mqtt-plus-info.ts +4 -4
- package/src/mqtt-plus-msg.ts +2 -2
- package/src/mqtt-plus-resource.ts +41 -37
- package/src/mqtt-plus-service.ts +11 -9
- package/src/mqtt-plus-util.ts +31 -18
- package/tst/mqtt-plus.spec.ts +3 -2
|
@@ -2,9 +2,11 @@ import { APISchema } from "./mqtt-plus-api";
|
|
|
2
2
|
import { APIOptions, OptionsTrait } from "./mqtt-plus-options";
|
|
3
3
|
export default class Codec {
|
|
4
4
|
private type;
|
|
5
|
+
private types;
|
|
6
|
+
private tags;
|
|
5
7
|
constructor(type: "cbor" | "json");
|
|
6
|
-
encode(data: unknown):
|
|
7
|
-
decode(data:
|
|
8
|
+
encode(data: unknown): Uint8Array | string;
|
|
9
|
+
decode(data: Uint8Array | string): unknown;
|
|
8
10
|
}
|
|
9
11
|
export declare class CodecTrait<T extends APISchema = APISchema> extends OptionsTrait<T> {
|
|
10
12
|
protected codec: Codec;
|
|
@@ -22,18 +22,51 @@
|
|
|
22
22
|
** SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
23
23
|
*/
|
|
24
24
|
/* external requirements */
|
|
25
|
-
import CBOR from "
|
|
25
|
+
import * as CBOR from "cbor2";
|
|
26
26
|
import { OptionsTrait } from "./mqtt-plus-options";
|
|
27
|
+
/* JSON encode/decode with Uint8Array support */
|
|
28
|
+
class JSONX {
|
|
29
|
+
static uint8ArrayToBase64(arr) {
|
|
30
|
+
return btoa(String.fromCharCode(...arr));
|
|
31
|
+
}
|
|
32
|
+
static base64ToUint8Array(base64) {
|
|
33
|
+
const binary = atob(base64);
|
|
34
|
+
const arr = new Uint8Array(binary.length);
|
|
35
|
+
for (let i = 0; i < binary.length; i++)
|
|
36
|
+
arr[i] = binary.charCodeAt(i);
|
|
37
|
+
return arr;
|
|
38
|
+
}
|
|
39
|
+
static stringify(obj) {
|
|
40
|
+
return JSON.stringify(obj, (_, value) => value instanceof Uint8Array
|
|
41
|
+
? { __Uint8Array: this.uint8ArrayToBase64(value) }
|
|
42
|
+
: value);
|
|
43
|
+
}
|
|
44
|
+
static parse(json) {
|
|
45
|
+
return JSON.parse(json, (_, value) => value?.__Uint8Array
|
|
46
|
+
? this.base64ToUint8Array(value.__Uint8Array)
|
|
47
|
+
: value);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
27
50
|
/* the encoder/decoder abstraction */
|
|
28
51
|
export default class Codec {
|
|
29
52
|
constructor(type) {
|
|
30
53
|
this.type = type;
|
|
54
|
+
this.types = new CBOR.TypeEncoderMap();
|
|
55
|
+
this.tags = new Map();
|
|
56
|
+
/* support direct encoding/decoding of Buffer */
|
|
57
|
+
const TAG_BUFFER = 64000;
|
|
58
|
+
this.types.registerEncoder(Buffer, (buffer) => {
|
|
59
|
+
return [TAG_BUFFER, new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength)];
|
|
60
|
+
});
|
|
61
|
+
this.tags.set(TAG_BUFFER, (tag) => {
|
|
62
|
+
return Buffer.from(tag.contents);
|
|
63
|
+
});
|
|
31
64
|
}
|
|
32
65
|
encode(data) {
|
|
33
66
|
let result;
|
|
34
67
|
if (this.type === "cbor") {
|
|
35
68
|
try {
|
|
36
|
-
result = CBOR.encode(data);
|
|
69
|
+
result = CBOR.encode(data, { types: this.types });
|
|
37
70
|
}
|
|
38
71
|
catch (_ex) {
|
|
39
72
|
throw new Error("failed to encode CBOR format");
|
|
@@ -41,7 +74,7 @@ export default class Codec {
|
|
|
41
74
|
}
|
|
42
75
|
else if (this.type === "json") {
|
|
43
76
|
try {
|
|
44
|
-
result =
|
|
77
|
+
result = JSONX.stringify(data);
|
|
45
78
|
}
|
|
46
79
|
catch (_ex) {
|
|
47
80
|
throw new Error("failed to encode JSON format");
|
|
@@ -53,9 +86,11 @@ export default class Codec {
|
|
|
53
86
|
}
|
|
54
87
|
decode(data) {
|
|
55
88
|
let result;
|
|
56
|
-
if (this.type === "cbor"
|
|
89
|
+
if (this.type === "cbor"
|
|
90
|
+
&& typeof data === "object"
|
|
91
|
+
&& data instanceof Uint8Array) {
|
|
57
92
|
try {
|
|
58
|
-
result = CBOR.decode(data);
|
|
93
|
+
result = CBOR.decode(data, { tags: this.tags });
|
|
59
94
|
}
|
|
60
95
|
catch (_ex) {
|
|
61
96
|
throw new Error("failed to decode CBOR format");
|
|
@@ -63,7 +98,7 @@ export default class Codec {
|
|
|
63
98
|
}
|
|
64
99
|
else if (this.type === "json" && typeof data === "string") {
|
|
65
100
|
try {
|
|
66
|
-
result =
|
|
101
|
+
result = JSONX.parse(data);
|
|
67
102
|
}
|
|
68
103
|
catch (_ex) {
|
|
69
104
|
throw new Error("failed to decode JSON format");
|
|
@@ -21,6 +21,8 @@
|
|
|
21
21
|
** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
22
22
|
** SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
23
23
|
*/
|
|
24
|
+
/* built-in requirements */
|
|
25
|
+
import { Buffer } from "node:buffer";
|
|
24
26
|
import { nanoid } from "nanoid";
|
|
25
27
|
/* internal requirements */
|
|
26
28
|
import { EventEmission } from "./mqtt-plus-msg";
|
|
@@ -98,7 +100,7 @@ export class EventTrait extends BaseTrait {
|
|
|
98
100
|
/* generate corresponding MQTT topic */
|
|
99
101
|
const topic = this.options.topicMake(event, "event-emission", receiver);
|
|
100
102
|
/* publish message to MQTT topic */
|
|
101
|
-
this.mqtt.publish(topic, message, { qos: 0, ...options });
|
|
103
|
+
this.mqtt.publish(topic, Buffer.from(message), { qos: 0, ...options });
|
|
102
104
|
}
|
|
103
105
|
/* dispatch message (Event pattern handling) */
|
|
104
106
|
_dispatchMessage(topic, parsed) {
|
|
@@ -111,7 +113,9 @@ export class EventTrait extends BaseTrait {
|
|
|
111
113
|
const name = parsed.event;
|
|
112
114
|
const handler = this.subscriptions.get(name);
|
|
113
115
|
const params = parsed.params ?? [];
|
|
114
|
-
const info = { sender: parsed.sender ?? ""
|
|
116
|
+
const info = { sender: parsed.sender ?? "" };
|
|
117
|
+
if (parsed.receiver)
|
|
118
|
+
info.receiver = parsed.receiver;
|
|
115
119
|
Promise.resolve()
|
|
116
120
|
.then(() => handler?.(...params, info))
|
|
117
121
|
.catch((err) => {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Readable } from "stream";
|
|
1
|
+
import { Readable } from "node:stream";
|
|
2
2
|
export interface InfoBase {
|
|
3
3
|
sender: string;
|
|
4
4
|
receiver?: string;
|
|
@@ -10,6 +10,6 @@ export interface InfoService extends InfoBase {
|
|
|
10
10
|
export interface InfoResource extends InfoBase {
|
|
11
11
|
meta?: Record<string, any>;
|
|
12
12
|
stream?: Readable;
|
|
13
|
-
buffer?: Promise<
|
|
13
|
+
buffer?: Promise<Uint8Array>;
|
|
14
14
|
}
|
|
15
15
|
export type WithInfo<F, I extends InfoBase> = F extends (...args: infer P) => infer R ? (...args: [...P, info: I]) => R : never;
|
|
@@ -30,18 +30,18 @@ export declare class ResourceTransferRequest extends Base {
|
|
|
30
30
|
export declare class ResourceTransferResponse extends Base {
|
|
31
31
|
resource?: string | undefined;
|
|
32
32
|
params?: any[] | undefined;
|
|
33
|
-
chunk?:
|
|
33
|
+
chunk?: Uint8Array | undefined;
|
|
34
34
|
meta?: Record<string, any> | undefined;
|
|
35
35
|
error?: string | undefined;
|
|
36
36
|
final?: boolean | undefined;
|
|
37
|
-
constructor(id: string, resource?: string | undefined, params?: any[] | undefined, chunk?:
|
|
37
|
+
constructor(id: string, resource?: string | undefined, params?: any[] | undefined, chunk?: Uint8Array | undefined, meta?: Record<string, any> | undefined, error?: string | undefined, final?: boolean | undefined, sender?: string, receiver?: string);
|
|
38
38
|
}
|
|
39
39
|
declare class Msg {
|
|
40
40
|
makeEventEmission(id: string, event: string, params?: any[], sender?: string, receiver?: string): EventEmission;
|
|
41
41
|
makeServiceCallRequest(id: string, service: string, params?: any[], sender?: string, receiver?: string): ServiceCallRequest;
|
|
42
42
|
makeServiceCallResponse(id: string, result?: any, error?: string, sender?: string, receiver?: string): ServiceCallResponse;
|
|
43
43
|
makeResourceTransferRequest(id: string, resource: string, params?: any[], sender?: string, receiver?: string): ResourceTransferRequest;
|
|
44
|
-
makeResourceTransferResponse(id: string, resource?: string, params?: any[], chunk?:
|
|
44
|
+
makeResourceTransferResponse(id: string, resource?: string, params?: any[], chunk?: Uint8Array, meta?: Record<string, any>, error?: string, final?: boolean, sender?: string, receiver?: string): ResourceTransferResponse;
|
|
45
45
|
parse(obj: any): EventEmission | ServiceCallRequest | ServiceCallResponse | ResourceTransferRequest | ResourceTransferResponse;
|
|
46
46
|
}
|
|
47
47
|
export declare class MsgTrait<T extends APISchema = APISchema> extends CodecTrait<T> {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Readable } from "stream";
|
|
1
|
+
import { Readable } from "node:stream";
|
|
2
2
|
import { IClientPublishOptions, IClientSubscribeOptions } from "mqtt";
|
|
3
3
|
import { APISchema, ResourceKeys } from "./mqtt-plus-api";
|
|
4
4
|
import type { WithInfo, InfoResource } from "./mqtt-plus-info";
|
|
@@ -13,10 +13,10 @@ export declare class ResourceTrait<T extends APISchema = APISchema> extends Serv
|
|
|
13
13
|
private pushTimers;
|
|
14
14
|
provision<K extends ResourceKeys<T> & string>(resource: K, callback: WithInfo<T[K], InfoResource>): Promise<Provisioning>;
|
|
15
15
|
provision<K extends ResourceKeys<T> & string>(resource: K, options: Partial<IClientSubscribeOptions>, callback: WithInfo<T[K], InfoResource>): Promise<Provisioning>;
|
|
16
|
-
push<K extends ResourceKeys<T> & string>(resource: K, data: Readable |
|
|
16
|
+
push<K extends ResourceKeys<T> & string>(resource: K, data: Readable | Uint8Array, ...params: Parameters<T[K]>): Promise<void>;
|
|
17
17
|
push<K extends ResourceKeys<T> & string>(config: {
|
|
18
18
|
resource: K;
|
|
19
|
-
data: Readable |
|
|
19
|
+
data: Readable | Uint8Array;
|
|
20
20
|
params: Parameters<T[K]>;
|
|
21
21
|
meta?: Record<string, any>;
|
|
22
22
|
receiver?: string;
|
|
@@ -24,7 +24,7 @@ export declare class ResourceTrait<T extends APISchema = APISchema> extends Serv
|
|
|
24
24
|
}): Promise<void>;
|
|
25
25
|
fetch<K extends ResourceKeys<T> & string>(resource: K, ...params: Parameters<T[K]>): Promise<{
|
|
26
26
|
stream: Readable;
|
|
27
|
-
buffer: Promise<
|
|
27
|
+
buffer: Promise<Uint8Array>;
|
|
28
28
|
meta: Promise<Record<string, any> | undefined>;
|
|
29
29
|
}>;
|
|
30
30
|
fetch<K extends ResourceKeys<T> & string>(config: {
|
|
@@ -34,7 +34,7 @@ export declare class ResourceTrait<T extends APISchema = APISchema> extends Serv
|
|
|
34
34
|
options?: IClientPublishOptions;
|
|
35
35
|
}): Promise<{
|
|
36
36
|
stream: Readable;
|
|
37
|
-
buffer: Promise<
|
|
37
|
+
buffer: Promise<Uint8Array>;
|
|
38
38
|
meta: Promise<Record<string, any> | undefined>;
|
|
39
39
|
}>;
|
|
40
40
|
protected _dispatchMessage(topic: string, parsed: any): void;
|
|
@@ -22,7 +22,8 @@
|
|
|
22
22
|
** SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
23
23
|
*/
|
|
24
24
|
/* built-in requirements */
|
|
25
|
-
import {
|
|
25
|
+
import { Buffer } from "node:buffer";
|
|
26
|
+
import { Readable } from "node:stream";
|
|
26
27
|
import { nanoid } from "nanoid";
|
|
27
28
|
/* internal requirements */
|
|
28
29
|
import { streamToBuffer, sendBufferAsChunks, sendStreamAsChunks } from "./mqtt-plus-util";
|
|
@@ -121,7 +122,7 @@ export class ResourceTrait extends ServiceTrait {
|
|
|
121
122
|
firstChunk = false;
|
|
122
123
|
const request = this.msg.makeResourceTransferResponse(rid, resource, params, chunk, chunkMeta, error, final, this.options.id, receiver);
|
|
123
124
|
const message = this.codec.encode(request);
|
|
124
|
-
this.mqtt.publish(topic, message, { qos: 2, ...options });
|
|
125
|
+
this.mqtt.publish(topic, Buffer.from(message), { qos: 2, ...options });
|
|
125
126
|
};
|
|
126
127
|
/* iterate over all chunks of the buffer */
|
|
127
128
|
return new Promise((resolve, reject) => {
|
|
@@ -129,7 +130,7 @@ export class ResourceTrait extends ServiceTrait {
|
|
|
129
130
|
/* attach to the readable */
|
|
130
131
|
sendStreamAsChunks(streamOrBuffer, this.options.chunkSize, sendChunk, () => resolve(), (err) => reject(err));
|
|
131
132
|
}
|
|
132
|
-
else if (streamOrBuffer instanceof
|
|
133
|
+
else if (streamOrBuffer instanceof Uint8Array) {
|
|
133
134
|
/* split buffer into chunks and send them */
|
|
134
135
|
sendBufferAsChunks(streamOrBuffer, this.options.chunkSize, sendChunk);
|
|
135
136
|
resolve();
|
|
@@ -171,13 +172,15 @@ export class ResourceTrait extends ServiceTrait {
|
|
|
171
172
|
/* define timer */
|
|
172
173
|
let timer = null;
|
|
173
174
|
/* utility function for cleanup */
|
|
174
|
-
const cleanup = () => {
|
|
175
|
+
const cleanup = (resolveMeta = false) => {
|
|
175
176
|
if (timer !== null) {
|
|
176
177
|
clearTimeout(timer);
|
|
177
178
|
timer = null;
|
|
178
179
|
}
|
|
179
180
|
this._unsubscribeTopic(responseTopic).catch(() => { });
|
|
180
181
|
this.callbacks.delete(requestId);
|
|
182
|
+
if (resolveMeta)
|
|
183
|
+
metaResolve?.(undefined);
|
|
181
184
|
};
|
|
182
185
|
/* start timeout handler */
|
|
183
186
|
timer = setTimeout(() => {
|
|
@@ -195,7 +198,7 @@ export class ResourceTrait extends ServiceTrait {
|
|
|
195
198
|
metaResolve?.(meta);
|
|
196
199
|
}
|
|
197
200
|
if (error !== undefined) {
|
|
198
|
-
cleanup();
|
|
201
|
+
cleanup(true);
|
|
199
202
|
stream.destroy(error);
|
|
200
203
|
}
|
|
201
204
|
else {
|
|
@@ -214,7 +217,7 @@ export class ResourceTrait extends ServiceTrait {
|
|
|
214
217
|
/* generate corresponding MQTT topic */
|
|
215
218
|
const topic = this.options.topicMake(resource, "resource-transfer-request", receiver);
|
|
216
219
|
/* publish message to MQTT topic */
|
|
217
|
-
this.mqtt.publish(topic, message, { qos: 2, ...options });
|
|
220
|
+
this.mqtt.publish(topic, Buffer.from(message), { qos: 2, ...options });
|
|
218
221
|
/* produce result */
|
|
219
222
|
return { stream, buffer, meta };
|
|
220
223
|
}
|
|
@@ -235,7 +238,9 @@ export class ResourceTrait extends ServiceTrait {
|
|
|
235
238
|
const params = parsed.params ?? [];
|
|
236
239
|
const sender = parsed.sender ?? "";
|
|
237
240
|
const receiver = parsed.receiver;
|
|
238
|
-
const info = { sender
|
|
241
|
+
const info = { sender };
|
|
242
|
+
if (receiver)
|
|
243
|
+
info.receiver = receiver;
|
|
239
244
|
/* generate corresponding MQTT topic */
|
|
240
245
|
const responseTopic = this.options.topicMake(resource, "resource-transfer-response", sender);
|
|
241
246
|
/* callback for creating and sending a chunk message */
|
|
@@ -244,7 +249,7 @@ export class ResourceTrait extends ServiceTrait {
|
|
|
244
249
|
const chunkMeta = firstChunk ? info.meta : undefined;
|
|
245
250
|
firstChunk = false;
|
|
246
251
|
const request = this.msg.makeResourceTransferResponse(requestId, resource, undefined, chunk, chunkMeta, error, final, this.options.id, sender);
|
|
247
|
-
const message = this.codec.encode(request);
|
|
252
|
+
const message = Buffer.from(this.codec.encode(request));
|
|
248
253
|
this.mqtt.publish(responseTopic, message, { qos: 2 });
|
|
249
254
|
};
|
|
250
255
|
/* call the handler callback */
|
|
@@ -276,8 +281,8 @@ export class ResourceTrait extends ServiceTrait {
|
|
|
276
281
|
const error = parsed.error;
|
|
277
282
|
const meta = parsed.meta;
|
|
278
283
|
const final = parsed.final;
|
|
279
|
-
const chunk = (parsed.chunk !== undefined && !
|
|
280
|
-
?
|
|
284
|
+
const chunk = (parsed.chunk !== undefined && !(parsed.chunk instanceof Uint8Array))
|
|
285
|
+
? Uint8Array.from(parsed.chunk) : parsed.chunk;
|
|
281
286
|
/* case 1: response on fetch */
|
|
282
287
|
const handler = this.callbacks.get(requestId);
|
|
283
288
|
if (handler !== undefined)
|
|
@@ -304,13 +309,13 @@ export class ResourceTrait extends ServiceTrait {
|
|
|
304
309
|
/* prepare info object */
|
|
305
310
|
const promise = streamToBuffer(readable);
|
|
306
311
|
const params = parsed.params ?? [];
|
|
307
|
-
const info = {
|
|
308
|
-
|
|
309
|
-
receiver
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
312
|
+
const info = { sender: parsed.sender ?? "" };
|
|
313
|
+
if (parsed.receiver)
|
|
314
|
+
info.receiver = parsed.receiver;
|
|
315
|
+
if (parsed.meta)
|
|
316
|
+
info.meta = meta;
|
|
317
|
+
info.stream = readable;
|
|
318
|
+
info.buffer = promise;
|
|
314
319
|
/* call handler */
|
|
315
320
|
Promise.resolve()
|
|
316
321
|
.then(() => handler(...params, info))
|
|
@@ -21,6 +21,8 @@
|
|
|
21
21
|
** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
22
22
|
** SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
23
23
|
*/
|
|
24
|
+
/* built-in requirements */
|
|
25
|
+
import { Buffer } from "node:buffer";
|
|
24
26
|
import { nanoid } from "nanoid";
|
|
25
27
|
/* internal requirements */
|
|
26
28
|
import { ServiceCallRequest, ServiceCallResponse } from "./mqtt-plus-msg";
|
|
@@ -124,7 +126,7 @@ export class ServiceTrait extends EventTrait {
|
|
|
124
126
|
/* generate corresponding MQTT topic */
|
|
125
127
|
const topic = this.options.topicMake(service, "service-call-request", receiver);
|
|
126
128
|
/* publish message to MQTT topic */
|
|
127
|
-
this.mqtt.publish(topic, message, { qos: 2, ...options }, (err) => {
|
|
129
|
+
this.mqtt.publish(topic, Buffer.from(message), { qos: 2, ...options }, (err) => {
|
|
128
130
|
/* handle request failure (only if not already handled) */
|
|
129
131
|
if (err) {
|
|
130
132
|
const pendingRequest = this.responseCallback.get(rid);
|
|
@@ -185,7 +187,9 @@ export class ServiceTrait extends EventTrait {
|
|
|
185
187
|
if (handler !== undefined) {
|
|
186
188
|
/* execute service handler */
|
|
187
189
|
const params = parsed.params ?? [];
|
|
188
|
-
const info = { sender: parsed.sender ?? ""
|
|
190
|
+
const info = { sender: parsed.sender ?? "" };
|
|
191
|
+
if (parsed.receiver)
|
|
192
|
+
info.receiver = parsed.receiver;
|
|
189
193
|
response = Promise.resolve().then(() => handler(...params, info));
|
|
190
194
|
}
|
|
191
195
|
else
|
|
@@ -212,7 +216,7 @@ export class ServiceTrait extends EventTrait {
|
|
|
212
216
|
throw new Error("invalid request: missing sender");
|
|
213
217
|
const encoded = this.codec.encode(rpcResponse);
|
|
214
218
|
const topic = this.options.topicMake(name, "service-call-response", senderPeerId);
|
|
215
|
-
this.mqtt.publish(topic, encoded, { qos: 2 });
|
|
219
|
+
this.mqtt.publish(topic, Buffer.from(encoded), { qos: 2 });
|
|
216
220
|
}).catch((err) => {
|
|
217
221
|
this.mqtt.emit("error", err);
|
|
218
222
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Readable } from "stream";
|
|
2
|
-
export declare function streamToBuffer(stream: Readable): Promise<
|
|
3
|
-
export type SendChunkCallback = (chunk:
|
|
4
|
-
export declare function sendBufferAsChunks(buffer:
|
|
1
|
+
import { Readable } from "node:stream";
|
|
2
|
+
export declare function streamToBuffer(stream: Readable): Promise<Uint8Array>;
|
|
3
|
+
export type SendChunkCallback = (chunk: Uint8Array | undefined, error: string | undefined, final: boolean) => void;
|
|
4
|
+
export declare function sendBufferAsChunks(buffer: Uint8Array, chunkSize: number, sendChunk: SendChunkCallback): void;
|
|
5
5
|
export declare function sendStreamAsChunks(readable: Readable, chunkSize: number, sendChunk: SendChunkCallback, onEnd: () => void, onError: (err: Error) => void): void;
|
|
@@ -23,35 +23,47 @@
|
|
|
23
23
|
*/
|
|
24
24
|
/* external requirements */
|
|
25
25
|
import PLazy from "p-lazy";
|
|
26
|
-
/*
|
|
26
|
+
/* concatenate elements of an Uint8Array array */
|
|
27
|
+
function uint8ArrayConcat(arrays) {
|
|
28
|
+
const totalLength = arrays.reduce((acc, value) => acc + value.length, 0);
|
|
29
|
+
const result = new Uint8Array(totalLength);
|
|
30
|
+
let offset = 0;
|
|
31
|
+
for (const array of arrays) {
|
|
32
|
+
result.set(array, offset);
|
|
33
|
+
offset += array.length;
|
|
34
|
+
}
|
|
35
|
+
return result;
|
|
36
|
+
}
|
|
37
|
+
/* utility function for collecting stream chunks into a buffer */
|
|
27
38
|
export function streamToBuffer(stream) {
|
|
28
39
|
return new PLazy((resolve, reject) => {
|
|
29
40
|
const chunks = [];
|
|
30
|
-
stream.on("data", (
|
|
41
|
+
stream.on("data", (_data) => {
|
|
42
|
+
const data = chunkToBuffer(_data);
|
|
31
43
|
chunks.push(data);
|
|
32
44
|
});
|
|
33
45
|
stream.on("end", () => {
|
|
34
|
-
resolve(
|
|
46
|
+
resolve(uint8ArrayConcat(chunks));
|
|
35
47
|
});
|
|
36
48
|
stream.on("error", (err) => {
|
|
37
49
|
reject(err);
|
|
38
50
|
});
|
|
39
51
|
});
|
|
40
52
|
}
|
|
41
|
-
/* utility function for converting a chunk to a
|
|
53
|
+
/* utility function for converting a chunk to a buffer */
|
|
42
54
|
function chunkToBuffer(chunk) {
|
|
43
55
|
let buffer;
|
|
44
|
-
if (Buffer
|
|
56
|
+
if (chunk instanceof Buffer)
|
|
57
|
+
buffer = new Uint8Array(chunk.buffer, chunk.byteOffset, chunk.length);
|
|
58
|
+
else if (chunk instanceof Uint8Array)
|
|
45
59
|
buffer = chunk;
|
|
46
60
|
else if (typeof chunk === "string")
|
|
47
|
-
buffer =
|
|
48
|
-
else if (chunk instanceof Uint8Array)
|
|
49
|
-
buffer = Buffer.from(chunk);
|
|
61
|
+
buffer = new TextEncoder().encode(chunk);
|
|
50
62
|
else
|
|
51
|
-
buffer =
|
|
63
|
+
buffer = new TextEncoder().encode(String(chunk));
|
|
52
64
|
return buffer;
|
|
53
65
|
}
|
|
54
|
-
/* utility function for sending a
|
|
66
|
+
/* utility function for sending a buffer as chunks */
|
|
55
67
|
export function sendBufferAsChunks(buffer, chunkSize, sendChunk) {
|
|
56
68
|
if (buffer.byteLength === 0) {
|
|
57
69
|
/* handle empty buffer by sending final chunk */
|