mqtt-plus 1.4.13 → 1.4.15
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/AGENTS.md +0 -1
- package/CHANGELOG.md +18 -0
- package/dst-stage1/mqtt-plus-api.js +1 -0
- package/dst-stage1/mqtt-plus-api.js.map +1 -0
- package/dst-stage1/mqtt-plus-auth.js +1 -0
- package/dst-stage1/mqtt-plus-auth.js.map +1 -0
- package/dst-stage1/mqtt-plus-base.js +1 -0
- package/dst-stage1/mqtt-plus-base.js.map +1 -0
- package/dst-stage1/mqtt-plus-codec.js +1 -0
- package/dst-stage1/mqtt-plus-codec.js.map +1 -0
- package/dst-stage1/mqtt-plus-encode.js +1 -0
- package/dst-stage1/mqtt-plus-encode.js.map +1 -0
- package/dst-stage1/mqtt-plus-error.js +1 -0
- package/dst-stage1/mqtt-plus-error.js.map +1 -0
- package/dst-stage1/mqtt-plus-event.js +1 -0
- package/dst-stage1/mqtt-plus-event.js.map +1 -0
- package/dst-stage1/mqtt-plus-info.js +1 -0
- package/dst-stage1/mqtt-plus-info.js.map +1 -0
- package/dst-stage1/mqtt-plus-meta.js +1 -0
- package/dst-stage1/mqtt-plus-meta.js.map +1 -0
- package/dst-stage1/mqtt-plus-msg.js +1 -0
- package/dst-stage1/mqtt-plus-msg.js.map +1 -0
- package/dst-stage1/mqtt-plus-options.js +1 -0
- package/dst-stage1/mqtt-plus-options.js.map +1 -0
- package/dst-stage1/mqtt-plus-service.js +1 -0
- package/dst-stage1/mqtt-plus-service.js.map +1 -0
- package/dst-stage1/mqtt-plus-sink.js +14 -2
- package/dst-stage1/mqtt-plus-sink.js.map +1 -0
- package/dst-stage1/mqtt-plus-source.js +27 -16
- package/dst-stage1/mqtt-plus-source.js.map +1 -0
- package/dst-stage1/mqtt-plus-subscription.js +1 -0
- package/dst-stage1/mqtt-plus-subscription.js.map +1 -0
- package/dst-stage1/mqtt-plus-timer.js +1 -0
- package/dst-stage1/mqtt-plus-timer.js.map +1 -0
- package/dst-stage1/mqtt-plus-trace.js +1 -0
- package/dst-stage1/mqtt-plus-trace.js.map +1 -0
- package/dst-stage1/mqtt-plus-util.js +1 -0
- package/dst-stage1/mqtt-plus-util.js.map +1 -0
- package/dst-stage1/mqtt-plus-version.d.ts +1 -1
- package/dst-stage1/mqtt-plus-version.js +3 -1
- package/dst-stage1/mqtt-plus-version.js.map +1 -0
- package/dst-stage1/mqtt-plus.js +1 -0
- package/dst-stage1/mqtt-plus.js.map +1 -0
- package/dst-stage2/mqtt-plus.cjs.js +1972 -2161
- package/dst-stage2/mqtt-plus.esm.js +1934 -2131
- package/dst-stage2/mqtt-plus.umd.js +13 -14
- package/etc/c8.json +16 -0
- package/etc/knip.jsonc +7 -1
- package/etc/stx.conf +36 -4
- package/etc/tsc.json +1 -1
- package/etc/vite.mts +11 -5
- package/package.d/vite+8.0.0.patch +12 -0
- package/package.json +12 -3
- package/src/mqtt-plus-sink.ts +14 -2
- package/src/mqtt-plus-source.ts +27 -16
- package/src/mqtt-plus-version.ts +2 -3
- package/tst/.c8/base.css +224 -0
- package/tst/.c8/block-navigation.js +87 -0
- package/tst/.c8/favicon.png +0 -0
- package/tst/.c8/index.html +371 -0
- package/tst/.c8/mqtt-plus-auth.ts.html +538 -0
- package/tst/.c8/mqtt-plus-base.ts.html +826 -0
- package/tst/.c8/mqtt-plus-codec.ts.html +457 -0
- package/tst/.c8/mqtt-plus-encode.ts.html +310 -0
- package/tst/.c8/mqtt-plus-error.ts.html +1186 -0
- package/tst/.c8/mqtt-plus-event.ts.html +733 -0
- package/tst/.c8/mqtt-plus-meta.ts.html +271 -0
- package/tst/.c8/mqtt-plus-msg.ts.html +1693 -0
- package/tst/.c8/mqtt-plus-options.ts.html +319 -0
- package/tst/.c8/mqtt-plus-service.ts.html +865 -0
- package/tst/.c8/mqtt-plus-sink.ts.html +1645 -0
- package/tst/.c8/mqtt-plus-source.ts.html +1585 -0
- package/tst/.c8/mqtt-plus-subscription.ts.html +706 -0
- package/tst/.c8/mqtt-plus-timer.ts.html +286 -0
- package/tst/.c8/mqtt-plus-trace.ts.html +463 -0
- package/tst/.c8/mqtt-plus-util.ts.html +823 -0
- package/tst/.c8/mqtt-plus-version.ts.html +205 -0
- package/tst/.c8/mqtt-plus.ts.html +193 -0
- package/tst/.c8/prettify.css +1 -0
- package/tst/.c8/prettify.js +2 -0
- package/tst/.c8/sort-arrow-sprite.png +0 -0
- package/tst/.c8/sorter.js +210 -0
- package/tst/.c8/tmp/coverage-6577-1773528098323-2.json +1 -0
- package/tst/.c8/tmp/coverage-6577-1773528098331-1.json +1 -0
- package/tst/.c8/tmp/coverage-6577-1773528098353-0.json +1 -0
- package/tst/.c8/tmp/coverage-6578-1773528089194-0.json +1 -0
- package/tst/mqtt-plus-2-event.spec.ts +29 -0
- package/tst/mqtt-plus-6-misc.spec.ts +79 -2
- package/tst/mqtt-plus-7-spool.spec.ts +101 -0
- package/tst/mqtt-plus-8-run.spec.ts +115 -0
- package/tst/{tsc.json → tsc.cov.json} +4 -3
- package/tst/tsc.std.json +31 -0
|
@@ -9,2168 +9,1971 @@ import * as sha256 from "@stablelib/sha256";
|
|
|
9
9
|
import { EventEmitter } from "node:events";
|
|
10
10
|
import * as v from "valibot";
|
|
11
11
|
import * as CBOR from "cbor2";
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
this.remaining += amount;
|
|
54
|
-
while (this.waiters.length > 0 && this.remaining > 0)
|
|
55
|
-
this.waiters.shift()(false);
|
|
56
|
-
}
|
|
57
|
-
/* release any waiting producer (for cleanup on error/abort) */
|
|
58
|
-
abort() {
|
|
59
|
-
this.aborted = true;
|
|
60
|
-
while (this.waiters.length > 0)
|
|
61
|
-
this.waiters.shift()(true);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
const textEncoder$1 = new TextEncoder();
|
|
12
|
+
//#region dst-stage1/mqtt-plus-util.js
|
|
13
|
+
var PLazy = PLazyAPI.default ?? PLazyAPI;
|
|
14
|
+
var CreditGate = class {
|
|
15
|
+
constructor(initialCredit) {
|
|
16
|
+
this.waiters = [];
|
|
17
|
+
this.aborted = false;
|
|
18
|
+
this.remaining = initialCredit;
|
|
19
|
+
}
|
|
20
|
+
async acquire(abortSignal) {
|
|
21
|
+
if (this.aborted) throw new Error("credit gate aborted");
|
|
22
|
+
if (abortSignal?.aborted) throw abortSignal.reason ?? /* @__PURE__ */ new Error("aborted");
|
|
23
|
+
if (this.remaining > 0) this.remaining--;
|
|
24
|
+
else await new Promise((resolve, reject) => {
|
|
25
|
+
const onAbort = () => {
|
|
26
|
+
const idx = this.waiters.indexOf(waiter);
|
|
27
|
+
if (idx !== -1) this.waiters.splice(idx, 1);
|
|
28
|
+
reject(abortSignal?.reason ?? /* @__PURE__ */ new Error("aborted"));
|
|
29
|
+
};
|
|
30
|
+
if (abortSignal) abortSignal.addEventListener("abort", onAbort, { once: true });
|
|
31
|
+
const waiter = (aborted) => {
|
|
32
|
+
if (abortSignal) abortSignal.removeEventListener("abort", onAbort);
|
|
33
|
+
if (aborted) {
|
|
34
|
+
reject(/* @__PURE__ */ new Error("credit gate aborted"));
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
this.remaining--;
|
|
38
|
+
resolve();
|
|
39
|
+
};
|
|
40
|
+
this.waiters.push(waiter);
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
replenish(amount) {
|
|
44
|
+
this.remaining += amount;
|
|
45
|
+
while (this.waiters.length > 0 && this.remaining > 0) this.waiters.shift()(false);
|
|
46
|
+
}
|
|
47
|
+
abort() {
|
|
48
|
+
this.aborted = true;
|
|
49
|
+
while (this.waiters.length > 0) this.waiters.shift()(true);
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
var textEncoder$1 = new TextEncoder();
|
|
65
53
|
function uint8ArrayConcat(arrays) {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
54
|
+
const totalLength = arrays.reduce((acc, value) => acc + value.byteLength, 0);
|
|
55
|
+
const result = new Uint8Array(totalLength);
|
|
56
|
+
let offset = 0;
|
|
57
|
+
for (const a of arrays) {
|
|
58
|
+
result.set(a, offset);
|
|
59
|
+
offset += a.byteLength;
|
|
60
|
+
}
|
|
61
|
+
return result;
|
|
74
62
|
}
|
|
75
63
|
function chunkToBuffer(chunk) {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
buffer = textEncoder$1.encode(chunk);
|
|
83
|
-
else
|
|
84
|
-
throw new Error("invalid chunk type: expected Buffer, Uint8Array, or string");
|
|
85
|
-
return buffer;
|
|
64
|
+
let buffer;
|
|
65
|
+
if (chunk instanceof Buffer$1) buffer = new Uint8Array(chunk.buffer, chunk.byteOffset, chunk.byteLength);
|
|
66
|
+
else if (chunk instanceof Uint8Array) buffer = chunk;
|
|
67
|
+
else if (typeof chunk === "string") buffer = textEncoder$1.encode(chunk);
|
|
68
|
+
else throw new Error("invalid chunk type: expected Buffer, Uint8Array, or string");
|
|
69
|
+
return buffer;
|
|
86
70
|
}
|
|
87
71
|
function streamToBuffer(stream) {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
72
|
+
return new PLazy((resolve, reject) => {
|
|
73
|
+
const chunks = [];
|
|
74
|
+
stream.on("data", (raw) => {
|
|
75
|
+
const data = chunkToBuffer(raw);
|
|
76
|
+
chunks.push(data);
|
|
77
|
+
});
|
|
78
|
+
stream.on("end", () => {
|
|
79
|
+
resolve(uint8ArrayConcat(chunks));
|
|
80
|
+
});
|
|
81
|
+
stream.on("error", (err) => {
|
|
82
|
+
reject(err);
|
|
83
|
+
});
|
|
84
|
+
});
|
|
101
85
|
}
|
|
102
86
|
async function sendBufferAsChunks(buffer, chunkSize, sendChunk, creditGate, abortSignal) {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
if (creditGate)
|
|
113
|
-
await creditGate.acquire(abortSignal);
|
|
114
|
-
await sendChunk(chunk, void 0, final);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
87
|
+
if (buffer.byteLength === 0) await sendChunk(void 0, void 0, true);
|
|
88
|
+
else for (let i = 0; i < buffer.byteLength; i += chunkSize) {
|
|
89
|
+
if (abortSignal?.aborted) throw abortSignal.reason ?? /* @__PURE__ */ new Error("aborted");
|
|
90
|
+
const size = Math.min(buffer.byteLength - i, chunkSize);
|
|
91
|
+
const chunk = buffer.subarray(i, i + size);
|
|
92
|
+
const final = i + size >= buffer.byteLength;
|
|
93
|
+
if (creditGate) await creditGate.acquire(abortSignal);
|
|
94
|
+
await sendChunk(chunk, void 0, final);
|
|
95
|
+
}
|
|
117
96
|
}
|
|
118
97
|
async function sendStreamAsChunks(readable, chunkSize, sendChunk, creditGate, abortSignal) {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
throw abortSignal.reason ?? new Error("aborted");
|
|
141
|
-
if (pending !== void 0) {
|
|
142
|
-
if (creditGate)
|
|
143
|
-
await creditGate.acquire(abortSignal);
|
|
144
|
-
await sendChunk(pending, void 0, true);
|
|
145
|
-
} else
|
|
146
|
-
await sendChunk(void 0, void 0, true);
|
|
98
|
+
let pending;
|
|
99
|
+
for await (const raw of readable) {
|
|
100
|
+
if (abortSignal?.aborted) throw abortSignal.reason ?? /* @__PURE__ */ new Error("aborted");
|
|
101
|
+
const buffer = chunkToBuffer(raw);
|
|
102
|
+
if (buffer.byteLength === 0) continue;
|
|
103
|
+
for (let i = 0; i < buffer.byteLength; i += chunkSize) {
|
|
104
|
+
if (abortSignal?.aborted) throw abortSignal.reason ?? /* @__PURE__ */ new Error("aborted");
|
|
105
|
+
const size = Math.min(buffer.byteLength - i, chunkSize);
|
|
106
|
+
const chunk = buffer.subarray(i, i + size);
|
|
107
|
+
if (pending !== void 0) {
|
|
108
|
+
if (creditGate) await creditGate.acquire(abortSignal);
|
|
109
|
+
await sendChunk(pending, void 0, false);
|
|
110
|
+
}
|
|
111
|
+
pending = chunk;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
if (abortSignal?.aborted) throw abortSignal.reason ?? /* @__PURE__ */ new Error("aborted");
|
|
115
|
+
if (pending !== void 0) {
|
|
116
|
+
if (creditGate) await creditGate.acquire(abortSignal);
|
|
117
|
+
await sendChunk(pending, void 0, true);
|
|
118
|
+
} else await sendChunk(void 0, void 0, true);
|
|
147
119
|
}
|
|
148
120
|
function makeMutuallyExclusiveFields(obj, f1Name, f2Name) {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
enumerable: true,
|
|
172
|
-
configurable: true
|
|
173
|
-
});
|
|
174
|
-
}
|
|
175
|
-
class Spool {
|
|
176
|
-
constructor() {
|
|
177
|
-
this.resources = [];
|
|
178
|
-
}
|
|
179
|
-
roll(...args) {
|
|
180
|
-
let resource;
|
|
181
|
-
let cleanup;
|
|
182
|
-
if (args.length === 1) {
|
|
183
|
-
resource = void 0;
|
|
184
|
-
cleanup = args[0];
|
|
185
|
-
} else if (args.length === 2) {
|
|
186
|
-
resource = args[0];
|
|
187
|
-
cleanup = args[1];
|
|
188
|
-
} else
|
|
189
|
-
throw new Error("invalid number of arguments");
|
|
190
|
-
this.resources.push({ resource, cleanup });
|
|
191
|
-
}
|
|
192
|
-
/* roll a sub-spool onto spool */
|
|
193
|
-
sub() {
|
|
194
|
-
const spool = new Spool();
|
|
195
|
-
this.roll(spool, () => {
|
|
196
|
-
});
|
|
197
|
-
return spool;
|
|
198
|
-
}
|
|
199
|
-
/* unroll all cleanup procedures from spool */
|
|
200
|
-
unroll(suppress = true) {
|
|
201
|
-
try {
|
|
202
|
-
let promise;
|
|
203
|
-
while (this.resources.length > 0) {
|
|
204
|
-
const entry = this.resources.pop();
|
|
205
|
-
const resource = entry.resource;
|
|
206
|
-
const cleanup = entry.cleanup;
|
|
207
|
-
if (promise) {
|
|
208
|
-
if (resource instanceof Spool)
|
|
209
|
-
promise = promise.then(
|
|
210
|
-
() => resource.unroll()
|
|
211
|
-
/* RECURSION */
|
|
212
|
-
);
|
|
213
|
-
else
|
|
214
|
-
promise = promise.then(() => cleanup(resource));
|
|
215
|
-
} else {
|
|
216
|
-
let result;
|
|
217
|
-
if (resource instanceof Spool)
|
|
218
|
-
result = resource.unroll();
|
|
219
|
-
else
|
|
220
|
-
result = cleanup(resource);
|
|
221
|
-
if (result instanceof Promise)
|
|
222
|
-
promise = result;
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
if (promise)
|
|
226
|
-
return suppress ? promise.catch(() => {
|
|
227
|
-
}) : promise;
|
|
228
|
-
else
|
|
229
|
-
return;
|
|
230
|
-
} catch (error) {
|
|
231
|
-
if (suppress)
|
|
232
|
-
return;
|
|
233
|
-
throw error;
|
|
234
|
-
}
|
|
235
|
-
}
|
|
121
|
+
if (!(typeof obj === "object" && obj !== null)) throw new Error("invalid object");
|
|
122
|
+
let consumed;
|
|
123
|
+
const f1Value = obj[f1Name];
|
|
124
|
+
const f2Value = obj[f2Name];
|
|
125
|
+
Object.defineProperty(obj, f1Name, {
|
|
126
|
+
get: () => {
|
|
127
|
+
if (consumed === "f2") throw new Error(`field "${f1Name}" is mutually exclusive with field "${f2Name}" and "${f2Name}" was already consumed`);
|
|
128
|
+
consumed = "f1";
|
|
129
|
+
return f1Value;
|
|
130
|
+
},
|
|
131
|
+
enumerable: true,
|
|
132
|
+
configurable: true
|
|
133
|
+
});
|
|
134
|
+
Object.defineProperty(obj, f2Name, {
|
|
135
|
+
get: () => {
|
|
136
|
+
if (consumed === "f1") throw new Error(`field "${f2Name}" is mutually exclusive with field "${f1Name}" and "${f1Name}" was already consumed`);
|
|
137
|
+
consumed = "f2";
|
|
138
|
+
return f2Value;
|
|
139
|
+
},
|
|
140
|
+
enumerable: true,
|
|
141
|
+
configurable: true
|
|
142
|
+
});
|
|
236
143
|
}
|
|
144
|
+
//#endregion
|
|
145
|
+
//#region dst-stage1/mqtt-plus-error.js
|
|
146
|
+
var Spool = class Spool {
|
|
147
|
+
constructor() {
|
|
148
|
+
this.resources = [];
|
|
149
|
+
}
|
|
150
|
+
roll(...args) {
|
|
151
|
+
let resource;
|
|
152
|
+
let cleanup;
|
|
153
|
+
if (args.length === 1) {
|
|
154
|
+
resource = void 0;
|
|
155
|
+
cleanup = args[0];
|
|
156
|
+
} else if (args.length === 2) {
|
|
157
|
+
resource = args[0];
|
|
158
|
+
cleanup = args[1];
|
|
159
|
+
} else throw new Error("invalid number of arguments");
|
|
160
|
+
this.resources.push({
|
|
161
|
+
resource,
|
|
162
|
+
cleanup
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
sub() {
|
|
166
|
+
const spool = new Spool();
|
|
167
|
+
this.roll(spool, () => {});
|
|
168
|
+
return spool;
|
|
169
|
+
}
|
|
170
|
+
unroll(suppress = true) {
|
|
171
|
+
try {
|
|
172
|
+
let promise;
|
|
173
|
+
while (this.resources.length > 0) {
|
|
174
|
+
const entry = this.resources.pop();
|
|
175
|
+
const resource = entry.resource;
|
|
176
|
+
const cleanup = entry.cleanup;
|
|
177
|
+
if (promise) if (resource instanceof Spool) promise = promise.then(() => resource.unroll());
|
|
178
|
+
else promise = promise.then(() => cleanup(resource));
|
|
179
|
+
else {
|
|
180
|
+
let result;
|
|
181
|
+
if (resource instanceof Spool) result = resource.unroll();
|
|
182
|
+
else result = cleanup(resource);
|
|
183
|
+
if (result instanceof Promise) promise = result;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
if (promise) return suppress ? promise.catch(() => {}) : promise;
|
|
187
|
+
else return;
|
|
188
|
+
} catch (error) {
|
|
189
|
+
if (suppress) return;
|
|
190
|
+
throw error;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
};
|
|
237
194
|
function ensureError(error, prefix, debug = false) {
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
const err = new Error(msg, { cause: error });
|
|
248
|
-
err.stack = error.stack;
|
|
249
|
-
return err;
|
|
250
|
-
} else
|
|
251
|
-
return new Error(msg);
|
|
195
|
+
if (error instanceof Error && prefix === void 0 && debug === false) return error;
|
|
196
|
+
let msg = error instanceof Error ? error.message : String(error);
|
|
197
|
+
if (prefix) msg = `${prefix}: ${msg}`;
|
|
198
|
+
if (debug && error instanceof Error) msg = `${msg}\n${error.stack}`;
|
|
199
|
+
if (error instanceof Error) {
|
|
200
|
+
const err = new Error(msg, { cause: error });
|
|
201
|
+
err.stack = error.stack;
|
|
202
|
+
return err;
|
|
203
|
+
} else return new Error(msg);
|
|
252
204
|
}
|
|
253
205
|
function runFinally(isAsync, onfinally) {
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
else
|
|
267
|
-
throw error;
|
|
268
|
-
}
|
|
269
|
-
if (!isAsync && result instanceof Promise)
|
|
270
|
-
throw new Error("onfinally callback returned Promise in non-async context");
|
|
271
|
-
if (isAsync && !(result instanceof Promise))
|
|
272
|
-
result = Promise.resolve(result);
|
|
273
|
-
return result;
|
|
206
|
+
if (!onfinally) if (isAsync) return Promise.resolve();
|
|
207
|
+
else return;
|
|
208
|
+
let result;
|
|
209
|
+
try {
|
|
210
|
+
result = onfinally();
|
|
211
|
+
} catch (error) {
|
|
212
|
+
if (isAsync) return Promise.reject(error);
|
|
213
|
+
else throw error;
|
|
214
|
+
}
|
|
215
|
+
if (!isAsync && result instanceof Promise) throw new Error("onfinally callback returned Promise in non-async context");
|
|
216
|
+
if (isAsync && !(result instanceof Promise)) result = Promise.resolve(result);
|
|
217
|
+
return result;
|
|
274
218
|
}
|
|
275
219
|
function runUnroll(isAsync, spool) {
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
let result = spool.unroll();
|
|
283
|
-
if (!isAsync && result instanceof Promise)
|
|
284
|
-
throw new Error("spool unroll returned Promise in non-async context");
|
|
285
|
-
if (isAsync && !(result instanceof Promise))
|
|
286
|
-
result = Promise.resolve(result);
|
|
287
|
-
return result;
|
|
220
|
+
if (!spool) if (isAsync) return Promise.resolve();
|
|
221
|
+
else return;
|
|
222
|
+
let result = spool.unroll();
|
|
223
|
+
if (!isAsync && result instanceof Promise) throw new Error("spool unroll returned Promise in non-async context");
|
|
224
|
+
if (isAsync && !(result instanceof Promise)) result = Promise.resolve(result);
|
|
225
|
+
return result;
|
|
288
226
|
}
|
|
289
227
|
function run(...args) {
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
throw error;
|
|
377
|
-
});
|
|
378
|
-
} else {
|
|
379
|
-
runFinally(false, onfinally);
|
|
380
|
-
if (spool && oncleanup)
|
|
381
|
-
spool.roll(result, oncleanup);
|
|
382
|
-
return result;
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
class OptionsTrait {
|
|
386
|
-
/* construct API class */
|
|
387
|
-
constructor(options = {}) {
|
|
388
|
-
this.options = {
|
|
389
|
-
id: nanoid(),
|
|
390
|
-
codec: "cbor",
|
|
391
|
-
timeout: 10 * 1e3,
|
|
392
|
-
share: "",
|
|
393
|
-
chunkSize: 16 * 1024,
|
|
394
|
-
chunkCredit: 4,
|
|
395
|
-
topicMake: (name, operation, peerId) => {
|
|
396
|
-
return `${name}/${operation}/${peerId ?? "any"}`;
|
|
397
|
-
},
|
|
398
|
-
topicMatch: (topic) => {
|
|
399
|
-
const m = topic.match(/^(.+)\/([^/]+)\/([^/]+)$/);
|
|
400
|
-
return m ? {
|
|
401
|
-
name: m[1],
|
|
402
|
-
operation: m[2],
|
|
403
|
-
peerId: m[3] === "any" ? void 0 : m[3]
|
|
404
|
-
} : null;
|
|
405
|
-
},
|
|
406
|
-
...options
|
|
407
|
-
};
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
class JSONX {
|
|
411
|
-
static uint8ArrayToBase64(arr) {
|
|
412
|
-
return Buffer$1.from(arr.buffer, arr.byteOffset, arr.byteLength).toString("base64");
|
|
413
|
-
}
|
|
414
|
-
static base64ToUint8Array(base64) {
|
|
415
|
-
return new Uint8Array(Buffer$1.from(base64, "base64"));
|
|
416
|
-
}
|
|
417
|
-
static stringify(obj) {
|
|
418
|
-
return JSON.stringify(obj, (_, value) => value instanceof Uint8Array ? { __Uint8Array: this.uint8ArrayToBase64(value) } : value);
|
|
419
|
-
}
|
|
420
|
-
static parse(json) {
|
|
421
|
-
return JSON.parse(json, (_, value) => typeof value?.__Uint8Array === "string" ? this.base64ToUint8Array(value.__Uint8Array) : value);
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
class Codec {
|
|
425
|
-
constructor(format) {
|
|
426
|
-
this.format = format;
|
|
427
|
-
this.types = new CBOR.TypeEncoderMap();
|
|
428
|
-
this.tags = /* @__PURE__ */ new Map();
|
|
429
|
-
const TAG_BUFFER = 64e3;
|
|
430
|
-
this.types.registerEncoder(Buffer$1, (buffer) => {
|
|
431
|
-
return [TAG_BUFFER, new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength)];
|
|
432
|
-
});
|
|
433
|
-
this.tags.set(TAG_BUFFER, (tag) => {
|
|
434
|
-
return Buffer$1.from(tag.contents);
|
|
435
|
-
});
|
|
436
|
-
}
|
|
437
|
-
encode(data) {
|
|
438
|
-
let result;
|
|
439
|
-
if (this.format === "cbor") {
|
|
440
|
-
try {
|
|
441
|
-
result = CBOR.encode(data, { types: this.types });
|
|
442
|
-
} catch (err) {
|
|
443
|
-
throw new Error("failed to encode CBOR format", { cause: err });
|
|
444
|
-
}
|
|
445
|
-
} else if (this.format === "json") {
|
|
446
|
-
try {
|
|
447
|
-
result = JSONX.stringify(data);
|
|
448
|
-
} catch (err) {
|
|
449
|
-
throw new Error("failed to encode JSON format", { cause: err });
|
|
450
|
-
}
|
|
451
|
-
} else
|
|
452
|
-
throw new Error(`invalid format "${this.format}"`);
|
|
453
|
-
return result;
|
|
454
|
-
}
|
|
455
|
-
decode(data) {
|
|
456
|
-
let result;
|
|
457
|
-
if (this.format === "cbor") {
|
|
458
|
-
if (!(data instanceof Uint8Array))
|
|
459
|
-
throw new Error("failed to decode CBOR format (data type is not Uint8Array)");
|
|
460
|
-
if (data.byteLength === 0)
|
|
461
|
-
throw new Error("failed to decode CBOR format (data is empty)");
|
|
462
|
-
try {
|
|
463
|
-
result = CBOR.decode(data, { tags: this.tags });
|
|
464
|
-
} catch (err) {
|
|
465
|
-
throw new Error("failed to decode CBOR format", { cause: err });
|
|
466
|
-
}
|
|
467
|
-
} else if (this.format === "json") {
|
|
468
|
-
if (typeof data !== "string")
|
|
469
|
-
throw new Error("failed to decode JSON format (data type is not string)");
|
|
470
|
-
if (data.length === 0)
|
|
471
|
-
throw new Error("failed to decode JSON format (data is empty)");
|
|
472
|
-
try {
|
|
473
|
-
result = JSONX.parse(data);
|
|
474
|
-
} catch (err) {
|
|
475
|
-
throw new Error("failed to decode JSON format", { cause: err });
|
|
476
|
-
}
|
|
477
|
-
} else
|
|
478
|
-
throw new Error(`invalid format "${this.format}"`);
|
|
479
|
-
return result;
|
|
480
|
-
}
|
|
481
|
-
}
|
|
482
|
-
class CodecTrait extends OptionsTrait {
|
|
483
|
-
/* construct API class */
|
|
484
|
-
constructor(options = {}) {
|
|
485
|
-
super(options);
|
|
486
|
-
this.codec = new Codec(this.options.codec);
|
|
487
|
-
}
|
|
488
|
-
}
|
|
489
|
-
class EncodeTrait extends CodecTrait {
|
|
490
|
-
static {
|
|
491
|
-
this.encoder = new TextEncoder();
|
|
492
|
-
}
|
|
493
|
-
static {
|
|
494
|
-
this.decoder = new TextDecoder();
|
|
495
|
-
}
|
|
496
|
-
/* convert character string to buffer */
|
|
497
|
-
str2buf(data) {
|
|
498
|
-
return EncodeTrait.encoder.encode(data);
|
|
499
|
-
}
|
|
500
|
-
/* convert buffer to character string */
|
|
501
|
-
buf2str(data) {
|
|
502
|
-
return EncodeTrait.decoder.decode(data);
|
|
503
|
-
}
|
|
504
|
-
/* convert byte-based typed array to buffer */
|
|
505
|
-
arr2buf(data) {
|
|
506
|
-
let buffer;
|
|
507
|
-
if (data instanceof Uint8Array)
|
|
508
|
-
buffer = data;
|
|
509
|
-
else
|
|
510
|
-
buffer = new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
|
|
511
|
-
return buffer;
|
|
512
|
-
}
|
|
513
|
-
buf2arr(data, cons) {
|
|
514
|
-
let arr;
|
|
515
|
-
const ctor = cons;
|
|
516
|
-
if (ctor === Buffer$1)
|
|
517
|
-
arr = Buffer$1.from(data.buffer, data.byteOffset, data.byteLength);
|
|
518
|
-
else if (ctor === Uint8Array)
|
|
519
|
-
arr = data;
|
|
520
|
-
else if (ctor === Int8Array)
|
|
521
|
-
arr = new Int8Array(data.buffer, data.byteOffset, data.byteLength);
|
|
522
|
-
else
|
|
523
|
-
throw new Error("invalid data type");
|
|
524
|
-
return arr;
|
|
525
|
-
}
|
|
228
|
+
let description;
|
|
229
|
+
let spool;
|
|
230
|
+
let action;
|
|
231
|
+
let oncatch;
|
|
232
|
+
let onfinally;
|
|
233
|
+
let oncleanup;
|
|
234
|
+
if (args.length === 1 && typeof args[0] === "object" && args[0] !== null) {
|
|
235
|
+
description = args[0].description;
|
|
236
|
+
spool = args[0].spool;
|
|
237
|
+
action = args[0].action;
|
|
238
|
+
oncatch = args[0].oncatch;
|
|
239
|
+
onfinally = args[0].onfinally;
|
|
240
|
+
oncleanup = args[0].oncleanup;
|
|
241
|
+
} else if (typeof args[0] === "string") {
|
|
242
|
+
description = args[0];
|
|
243
|
+
if (args[1] instanceof Spool || args[1] === void 0 && typeof args[2] === "function") {
|
|
244
|
+
spool = args[1];
|
|
245
|
+
action = args[2];
|
|
246
|
+
oncatch = args[3];
|
|
247
|
+
onfinally = args[4];
|
|
248
|
+
oncleanup = args[5];
|
|
249
|
+
} else {
|
|
250
|
+
action = args[1];
|
|
251
|
+
oncatch = args[2];
|
|
252
|
+
onfinally = args[3];
|
|
253
|
+
oncleanup = args[4];
|
|
254
|
+
}
|
|
255
|
+
} else if (args[0] instanceof Spool || args[0] === void 0 && typeof args[1] === "function") {
|
|
256
|
+
spool = args[0];
|
|
257
|
+
action = args[1];
|
|
258
|
+
oncatch = args[2];
|
|
259
|
+
onfinally = args[3];
|
|
260
|
+
oncleanup = args[4];
|
|
261
|
+
} else {
|
|
262
|
+
action = args[0];
|
|
263
|
+
oncatch = args[1];
|
|
264
|
+
onfinally = args[2];
|
|
265
|
+
oncleanup = args[3];
|
|
266
|
+
}
|
|
267
|
+
if (oncleanup && !spool) throw new Error("oncleanup requires a spool");
|
|
268
|
+
let result;
|
|
269
|
+
try {
|
|
270
|
+
result = action();
|
|
271
|
+
} catch (arg) {
|
|
272
|
+
let error = ensureError(arg, description);
|
|
273
|
+
if (oncatch) {
|
|
274
|
+
try {
|
|
275
|
+
result = oncatch(error);
|
|
276
|
+
} catch (arg) {
|
|
277
|
+
error = ensureError(arg, description);
|
|
278
|
+
runFinally(false, onfinally);
|
|
279
|
+
runUnroll(false, spool);
|
|
280
|
+
throw error;
|
|
281
|
+
}
|
|
282
|
+
runFinally(false, onfinally);
|
|
283
|
+
return result;
|
|
284
|
+
}
|
|
285
|
+
runFinally(false, onfinally);
|
|
286
|
+
runUnroll(false, spool);
|
|
287
|
+
throw error;
|
|
288
|
+
}
|
|
289
|
+
if (result instanceof Promise) return result.then(async (result) => {
|
|
290
|
+
await runFinally(true, onfinally);
|
|
291
|
+
if (spool && oncleanup) spool.roll(result, oncleanup);
|
|
292
|
+
return result;
|
|
293
|
+
}, async (arg) => {
|
|
294
|
+
let error = ensureError(arg, description);
|
|
295
|
+
if (oncatch) try {
|
|
296
|
+
const result = oncatch(error);
|
|
297
|
+
await runFinally(true, onfinally);
|
|
298
|
+
return result;
|
|
299
|
+
} catch (arg) {
|
|
300
|
+
error = ensureError(arg, description);
|
|
301
|
+
await runFinally(true, onfinally);
|
|
302
|
+
await runUnroll(true, spool);
|
|
303
|
+
throw error;
|
|
304
|
+
}
|
|
305
|
+
await runFinally(true, onfinally);
|
|
306
|
+
await runUnroll(true, spool);
|
|
307
|
+
throw error;
|
|
308
|
+
});
|
|
309
|
+
else {
|
|
310
|
+
runFinally(false, onfinally);
|
|
311
|
+
if (spool && oncleanup) spool.roll(result, oncleanup);
|
|
312
|
+
return result;
|
|
313
|
+
}
|
|
526
314
|
}
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
315
|
+
//#endregion
|
|
316
|
+
//#region dst-stage1/mqtt-plus-options.js
|
|
317
|
+
var OptionsTrait = class {
|
|
318
|
+
constructor(options = {}) {
|
|
319
|
+
this.options = {
|
|
320
|
+
id: nanoid(),
|
|
321
|
+
codec: "cbor",
|
|
322
|
+
timeout: 10 * 1e3,
|
|
323
|
+
share: "",
|
|
324
|
+
chunkSize: 16 * 1024,
|
|
325
|
+
chunkCredit: 4,
|
|
326
|
+
topicMake: (name, operation, peerId) => {
|
|
327
|
+
return `${name}/${operation}/${peerId ?? "any"}`;
|
|
328
|
+
},
|
|
329
|
+
topicMatch: (topic) => {
|
|
330
|
+
const m = topic.match(/^(.+)\/([^/]+)\/([^/]+)$/);
|
|
331
|
+
return m ? {
|
|
332
|
+
name: m[1],
|
|
333
|
+
operation: m[2],
|
|
334
|
+
peerId: m[3] === "any" ? void 0 : m[3]
|
|
335
|
+
} : null;
|
|
336
|
+
},
|
|
337
|
+
...options
|
|
338
|
+
};
|
|
339
|
+
}
|
|
535
340
|
};
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
type: v.string(),
|
|
552
|
-
id: v.string(),
|
|
553
|
-
sender: v.optional(v.string()),
|
|
554
|
-
receiver: v.optional(v.string())
|
|
341
|
+
//#endregion
|
|
342
|
+
//#region dst-stage1/mqtt-plus-codec.js
|
|
343
|
+
var JSONX = class {
|
|
344
|
+
static uint8ArrayToBase64(arr) {
|
|
345
|
+
return Buffer$1.from(arr.buffer, arr.byteOffset, arr.byteLength).toString("base64");
|
|
346
|
+
}
|
|
347
|
+
static base64ToUint8Array(base64) {
|
|
348
|
+
return new Uint8Array(Buffer$1.from(base64, "base64"));
|
|
349
|
+
}
|
|
350
|
+
static stringify(obj) {
|
|
351
|
+
return JSON.stringify(obj, (_, value) => value instanceof Uint8Array ? { __Uint8Array: this.uint8ArrayToBase64(value) } : value);
|
|
352
|
+
}
|
|
353
|
+
static parse(json) {
|
|
354
|
+
return JSON.parse(json, (_, value) => typeof value?.__Uint8Array === "string" ? this.base64ToUint8Array(value.__Uint8Array) : value);
|
|
355
|
+
}
|
|
555
356
|
};
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
}
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
357
|
+
var Codec = class {
|
|
358
|
+
constructor(format) {
|
|
359
|
+
this.format = format;
|
|
360
|
+
this.types = new CBOR.TypeEncoderMap();
|
|
361
|
+
this.tags = /* @__PURE__ */ new Map();
|
|
362
|
+
const TAG_BUFFER = 64e3;
|
|
363
|
+
this.types.registerEncoder(Buffer$1, (buffer) => {
|
|
364
|
+
return [TAG_BUFFER, new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength)];
|
|
365
|
+
});
|
|
366
|
+
this.tags.set(TAG_BUFFER, (tag) => {
|
|
367
|
+
return Buffer$1.from(tag.contents);
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
encode(data) {
|
|
371
|
+
let result;
|
|
372
|
+
if (this.format === "cbor") try {
|
|
373
|
+
result = CBOR.encode(data, { types: this.types });
|
|
374
|
+
} catch (err) {
|
|
375
|
+
throw new Error("failed to encode CBOR format", { cause: err });
|
|
376
|
+
}
|
|
377
|
+
else if (this.format === "json") try {
|
|
378
|
+
result = JSONX.stringify(data);
|
|
379
|
+
} catch (err) {
|
|
380
|
+
throw new Error("failed to encode JSON format", { cause: err });
|
|
381
|
+
}
|
|
382
|
+
else throw new Error(`invalid format "${this.format}"`);
|
|
383
|
+
return result;
|
|
384
|
+
}
|
|
385
|
+
decode(data) {
|
|
386
|
+
let result;
|
|
387
|
+
if (this.format === "cbor") {
|
|
388
|
+
if (!(data instanceof Uint8Array)) throw new Error("failed to decode CBOR format (data type is not Uint8Array)");
|
|
389
|
+
if (data.byteLength === 0) throw new Error("failed to decode CBOR format (data is empty)");
|
|
390
|
+
try {
|
|
391
|
+
result = CBOR.decode(data, { tags: this.tags });
|
|
392
|
+
} catch (err) {
|
|
393
|
+
throw new Error("failed to decode CBOR format", { cause: err });
|
|
394
|
+
}
|
|
395
|
+
} else if (this.format === "json") {
|
|
396
|
+
if (typeof data !== "string") throw new Error("failed to decode JSON format (data type is not string)");
|
|
397
|
+
if (data.length === 0) throw new Error("failed to decode JSON format (data is empty)");
|
|
398
|
+
try {
|
|
399
|
+
result = JSONX.parse(data);
|
|
400
|
+
} catch (err) {
|
|
401
|
+
throw new Error("failed to decode JSON format", { cause: err });
|
|
402
|
+
}
|
|
403
|
+
} else throw new Error(`invalid format "${this.format}"`);
|
|
404
|
+
return result;
|
|
405
|
+
}
|
|
406
|
+
};
|
|
407
|
+
var CodecTrait = class extends OptionsTrait {
|
|
408
|
+
constructor(options = {}) {
|
|
409
|
+
super(options);
|
|
410
|
+
this.codec = new Codec(this.options.codec);
|
|
411
|
+
}
|
|
412
|
+
};
|
|
413
|
+
//#endregion
|
|
414
|
+
//#region dst-stage1/mqtt-plus-encode.js
|
|
415
|
+
var EncodeTrait = class EncodeTrait extends CodecTrait {
|
|
416
|
+
static {
|
|
417
|
+
this.encoder = new TextEncoder();
|
|
418
|
+
}
|
|
419
|
+
static {
|
|
420
|
+
this.decoder = new TextDecoder();
|
|
421
|
+
}
|
|
422
|
+
str2buf(data) {
|
|
423
|
+
return EncodeTrait.encoder.encode(data);
|
|
424
|
+
}
|
|
425
|
+
buf2str(data) {
|
|
426
|
+
return EncodeTrait.decoder.decode(data);
|
|
427
|
+
}
|
|
428
|
+
arr2buf(data) {
|
|
429
|
+
let buffer;
|
|
430
|
+
if (data instanceof Uint8Array) buffer = data;
|
|
431
|
+
else buffer = new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
|
|
432
|
+
return buffer;
|
|
433
|
+
}
|
|
434
|
+
buf2arr(data, cons) {
|
|
435
|
+
let arr;
|
|
436
|
+
const ctor = cons;
|
|
437
|
+
if (ctor === Buffer$1) arr = Buffer$1.from(data.buffer, data.byteOffset, data.byteLength);
|
|
438
|
+
else if (ctor === Uint8Array) arr = data;
|
|
439
|
+
else if (ctor === Int8Array) arr = new Int8Array(data.buffer, data.byteOffset, data.byteLength);
|
|
440
|
+
else throw new Error("invalid data type");
|
|
441
|
+
return arr;
|
|
442
|
+
}
|
|
443
|
+
};
|
|
444
|
+
//#endregion
|
|
445
|
+
//#region dst-stage1/mqtt-plus-version.js
|
|
446
|
+
var versionToNum = (str) => {
|
|
447
|
+
const m = str.match(/^(\d+)\.(\d+)$/);
|
|
448
|
+
if (m === null) throw new Error("invalid version string");
|
|
449
|
+
const minor = parseInt(m[2], 10);
|
|
450
|
+
if (minor > 99) throw new Error("invalid version string: minor version exceeds 99");
|
|
451
|
+
return parseInt(m[1], 10) * 100 + minor;
|
|
452
|
+
};
|
|
453
|
+
var version = versionToNum("1.4");
|
|
454
|
+
//#endregion
|
|
455
|
+
//#region dst-stage1/mqtt-plus-msg.js
|
|
456
|
+
var MetaSchema = v.pipe(v.record(v.string(), v.unknown()), v.check((data) => !Array.isArray(data)));
|
|
457
|
+
var AuthSchema = v.pipe(v.array(v.pipe(v.string(), v.maxLength(8192))), v.maxLength(8));
|
|
458
|
+
var Base = class {
|
|
459
|
+
constructor(type, id, sender, receiver) {
|
|
460
|
+
this.type = type;
|
|
461
|
+
this.id = id;
|
|
462
|
+
this.sender = sender;
|
|
463
|
+
this.receiver = receiver;
|
|
464
|
+
this.version = `MQTT+/1.4`;
|
|
465
|
+
}
|
|
466
|
+
};
|
|
467
|
+
var BaseSchema = {
|
|
468
|
+
version: v.pipe(v.string(), v.regex(/^MQTT\+\/\d+\.\d+$/)),
|
|
469
|
+
type: v.string(),
|
|
470
|
+
id: v.string(),
|
|
471
|
+
sender: v.optional(v.string()),
|
|
472
|
+
receiver: v.optional(v.string())
|
|
473
|
+
};
|
|
474
|
+
var EventEmission = class extends Base {
|
|
475
|
+
constructor(id, name, params, sender, receiver, auth, meta) {
|
|
476
|
+
super("event-emission", id, sender, receiver);
|
|
477
|
+
this.name = name;
|
|
478
|
+
this.params = params;
|
|
479
|
+
this.auth = auth;
|
|
480
|
+
this.meta = meta;
|
|
481
|
+
}
|
|
482
|
+
};
|
|
483
|
+
var EventEmissionSchema = v.strictObject({
|
|
484
|
+
...BaseSchema,
|
|
485
|
+
type: v.literal("event-emission"),
|
|
486
|
+
name: v.string(),
|
|
487
|
+
params: v.optional(v.pipe(v.array(v.unknown()), v.maxLength(64))),
|
|
488
|
+
auth: v.optional(AuthSchema),
|
|
489
|
+
meta: v.optional(MetaSchema)
|
|
572
490
|
});
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
}
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
491
|
+
var ServiceCallRequest = class extends Base {
|
|
492
|
+
constructor(id, name, params, sender, receiver, auth, meta) {
|
|
493
|
+
super("service-call-request", id, sender, receiver);
|
|
494
|
+
this.name = name;
|
|
495
|
+
this.params = params;
|
|
496
|
+
this.auth = auth;
|
|
497
|
+
this.meta = meta;
|
|
498
|
+
}
|
|
499
|
+
};
|
|
500
|
+
var ServiceCallRequestSchema = v.strictObject({
|
|
501
|
+
...BaseSchema,
|
|
502
|
+
type: v.literal("service-call-request"),
|
|
503
|
+
name: v.string(),
|
|
504
|
+
params: v.optional(v.pipe(v.array(v.unknown()), v.maxLength(64))),
|
|
505
|
+
auth: v.optional(AuthSchema),
|
|
506
|
+
meta: v.optional(MetaSchema)
|
|
589
507
|
});
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
}
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
508
|
+
var ServiceCallResponse = class extends Base {
|
|
509
|
+
constructor(id, result, error, sender, receiver) {
|
|
510
|
+
super("service-call-response", id, sender, receiver);
|
|
511
|
+
this.result = result;
|
|
512
|
+
this.error = error;
|
|
513
|
+
}
|
|
514
|
+
};
|
|
515
|
+
var ServiceCallResponseSchema = v.strictObject({
|
|
516
|
+
...BaseSchema,
|
|
517
|
+
type: v.literal("service-call-response"),
|
|
518
|
+
result: v.optional(v.unknown()),
|
|
519
|
+
error: v.optional(v.string())
|
|
602
520
|
});
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
}
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
521
|
+
var SinkPushRequest = class extends Base {
|
|
522
|
+
constructor(id, name, params, sender, receiver, auth, meta) {
|
|
523
|
+
super("sink-push-request", id, sender, receiver);
|
|
524
|
+
this.name = name;
|
|
525
|
+
this.params = params;
|
|
526
|
+
this.auth = auth;
|
|
527
|
+
this.meta = meta;
|
|
528
|
+
}
|
|
529
|
+
};
|
|
530
|
+
var SinkPushRequestSchema = v.strictObject({
|
|
531
|
+
...BaseSchema,
|
|
532
|
+
type: v.literal("sink-push-request"),
|
|
533
|
+
name: v.string(),
|
|
534
|
+
params: v.optional(v.pipe(v.array(v.unknown()), v.maxLength(64))),
|
|
535
|
+
auth: v.optional(AuthSchema),
|
|
536
|
+
meta: v.optional(MetaSchema)
|
|
619
537
|
});
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
}
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
538
|
+
var SinkPushResponse = class extends Base {
|
|
539
|
+
constructor(id, name, error, sender, receiver, credit) {
|
|
540
|
+
super("sink-push-response", id, sender, receiver);
|
|
541
|
+
this.name = name;
|
|
542
|
+
this.error = error;
|
|
543
|
+
this.credit = credit;
|
|
544
|
+
}
|
|
545
|
+
};
|
|
546
|
+
var SinkPushResponseSchema = v.strictObject({
|
|
547
|
+
...BaseSchema,
|
|
548
|
+
type: v.literal("sink-push-response"),
|
|
549
|
+
name: v.string(),
|
|
550
|
+
error: v.optional(v.string()),
|
|
551
|
+
credit: v.optional(v.pipe(v.number(), v.integer(), v.minValue(1)))
|
|
634
552
|
});
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
}
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
553
|
+
var SinkPushChunk = class extends Base {
|
|
554
|
+
constructor(id, name, chunk, error, final, sender, receiver) {
|
|
555
|
+
super("sink-push-chunk", id, sender, receiver);
|
|
556
|
+
this.name = name;
|
|
557
|
+
this.chunk = chunk;
|
|
558
|
+
this.error = error;
|
|
559
|
+
this.final = final;
|
|
560
|
+
}
|
|
561
|
+
};
|
|
562
|
+
var SinkPushChunkSchema = v.strictObject({
|
|
563
|
+
...BaseSchema,
|
|
564
|
+
type: v.literal("sink-push-chunk"),
|
|
565
|
+
name: v.string(),
|
|
566
|
+
chunk: v.optional(v.instance(Uint8Array)),
|
|
567
|
+
error: v.optional(v.string()),
|
|
568
|
+
final: v.optional(v.boolean())
|
|
651
569
|
});
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
}
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
570
|
+
var SinkPushCredit = class extends Base {
|
|
571
|
+
constructor(id, name, credit, sender, receiver) {
|
|
572
|
+
super("sink-push-credit", id, sender, receiver);
|
|
573
|
+
this.name = name;
|
|
574
|
+
this.credit = credit;
|
|
575
|
+
}
|
|
576
|
+
};
|
|
577
|
+
var SinkPushCreditSchema = v.strictObject({
|
|
578
|
+
...BaseSchema,
|
|
579
|
+
type: v.literal("sink-push-credit"),
|
|
580
|
+
name: v.string(),
|
|
581
|
+
credit: v.pipe(v.number(), v.integer(), v.minValue(1))
|
|
664
582
|
});
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
}
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
583
|
+
var SourceFetchRequest = class extends Base {
|
|
584
|
+
constructor(id, name, params, sender, receiver, auth, meta, credit) {
|
|
585
|
+
super("source-fetch-request", id, sender, receiver);
|
|
586
|
+
this.name = name;
|
|
587
|
+
this.params = params;
|
|
588
|
+
this.auth = auth;
|
|
589
|
+
this.meta = meta;
|
|
590
|
+
this.credit = credit;
|
|
591
|
+
}
|
|
592
|
+
};
|
|
593
|
+
var SourceFetchRequestSchema = v.strictObject({
|
|
594
|
+
...BaseSchema,
|
|
595
|
+
type: v.literal("source-fetch-request"),
|
|
596
|
+
name: v.string(),
|
|
597
|
+
params: v.optional(v.pipe(v.array(v.unknown()), v.maxLength(64))),
|
|
598
|
+
auth: v.optional(AuthSchema),
|
|
599
|
+
meta: v.optional(MetaSchema),
|
|
600
|
+
credit: v.optional(v.pipe(v.number(), v.integer(), v.minValue(1)))
|
|
683
601
|
});
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
}
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
602
|
+
var SourceFetchResponse = class extends Base {
|
|
603
|
+
constructor(id, name, error, sender, receiver, meta) {
|
|
604
|
+
super("source-fetch-response", id, sender, receiver);
|
|
605
|
+
this.name = name;
|
|
606
|
+
this.error = error;
|
|
607
|
+
this.meta = meta;
|
|
608
|
+
}
|
|
609
|
+
};
|
|
610
|
+
var SourceFetchResponseSchema = v.strictObject({
|
|
611
|
+
...BaseSchema,
|
|
612
|
+
type: v.literal("source-fetch-response"),
|
|
613
|
+
name: v.string(),
|
|
614
|
+
error: v.optional(v.string()),
|
|
615
|
+
meta: v.optional(MetaSchema)
|
|
698
616
|
});
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
}
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
617
|
+
var SourceFetchChunk = class extends Base {
|
|
618
|
+
constructor(id, name, chunk, error, final, sender, receiver) {
|
|
619
|
+
super("source-fetch-chunk", id, sender, receiver);
|
|
620
|
+
this.name = name;
|
|
621
|
+
this.chunk = chunk;
|
|
622
|
+
this.error = error;
|
|
623
|
+
this.final = final;
|
|
624
|
+
}
|
|
625
|
+
};
|
|
626
|
+
var SourceFetchChunkSchema = v.strictObject({
|
|
627
|
+
...BaseSchema,
|
|
628
|
+
type: v.literal("source-fetch-chunk"),
|
|
629
|
+
name: v.string(),
|
|
630
|
+
chunk: v.optional(v.instance(Uint8Array)),
|
|
631
|
+
error: v.optional(v.string()),
|
|
632
|
+
final: v.optional(v.boolean())
|
|
715
633
|
});
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
}
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
634
|
+
var SourceFetchCredit = class extends Base {
|
|
635
|
+
constructor(id, name, credit, sender, receiver) {
|
|
636
|
+
super("source-fetch-credit", id, sender, receiver);
|
|
637
|
+
this.name = name;
|
|
638
|
+
this.credit = credit;
|
|
639
|
+
}
|
|
640
|
+
};
|
|
641
|
+
var SourceFetchCreditSchema = v.strictObject({
|
|
642
|
+
...BaseSchema,
|
|
643
|
+
type: v.literal("source-fetch-credit"),
|
|
644
|
+
name: v.string(),
|
|
645
|
+
credit: v.pipe(v.number(), v.integer(), v.minValue(0))
|
|
728
646
|
});
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
const error = new Error(`push to sink "${name}" timed out`);
|
|
2057
|
-
abortController.abort(error);
|
|
2058
|
-
spool.unroll();
|
|
2059
|
-
});
|
|
2060
|
-
spool.roll(() => {
|
|
2061
|
-
this.timerClear(pushTimerId);
|
|
2062
|
-
});
|
|
2063
|
-
refreshTimeout();
|
|
2064
|
-
let initialCredit;
|
|
2065
|
-
let creditGate;
|
|
2066
|
-
let remoteError = false;
|
|
2067
|
-
let pushAcked = false;
|
|
2068
|
-
let pushFinalized = false;
|
|
2069
|
-
let pushFinalizeResolve;
|
|
2070
|
-
let pushFinalizeReject;
|
|
2071
|
-
const pushFinalize = new Promise((resolve, reject) => {
|
|
2072
|
-
pushFinalizeResolve = resolve;
|
|
2073
|
-
pushFinalizeReject = reject;
|
|
2074
|
-
});
|
|
2075
|
-
pushFinalize.catch(() => {
|
|
2076
|
-
});
|
|
2077
|
-
try {
|
|
2078
|
-
await new Promise((resolve, reject) => {
|
|
2079
|
-
const onAbort = () => {
|
|
2080
|
-
reject(abortSignal.reason);
|
|
2081
|
-
};
|
|
2082
|
-
abortSignal.addEventListener("abort", onAbort, { once: true });
|
|
2083
|
-
spool.roll(() => {
|
|
2084
|
-
abortSignal.removeEventListener("abort", onAbort);
|
|
2085
|
-
});
|
|
2086
|
-
this.onResponse.set(`sink-push-response:${requestId}`, (response) => {
|
|
2087
|
-
if (response.error)
|
|
2088
|
-
reject(new Error(response.error));
|
|
2089
|
-
else {
|
|
2090
|
-
if (response.sender)
|
|
2091
|
-
receiver = response.sender;
|
|
2092
|
-
initialCredit = response.credit;
|
|
2093
|
-
pushAcked = true;
|
|
2094
|
-
resolve();
|
|
2095
|
-
}
|
|
2096
|
-
});
|
|
2097
|
-
spool.roll(() => {
|
|
2098
|
-
this.onResponse.delete(`sink-push-response:${requestId}`);
|
|
2099
|
-
});
|
|
2100
|
-
const auth = this.authenticate();
|
|
2101
|
-
const metaStore = this.metaStore(meta);
|
|
2102
|
-
const request = this.msg.makeSinkPushRequest(requestId, name, params, this.options.id, receiver, auth, metaStore);
|
|
2103
|
-
const message = this.codec.encode(request);
|
|
2104
|
-
const requestTopic = this.options.topicMake(name, "sink-push-request", receiver);
|
|
2105
|
-
run(`publish push request as MQTT message to topic "${requestTopic}"`, spool, () => this.publishToTopic(requestTopic, message, { qos: 2, ...options })).catch((err) => {
|
|
2106
|
-
reject(err);
|
|
2107
|
-
});
|
|
2108
|
-
});
|
|
2109
|
-
this.onResponse.set(`sink-push-response:${requestId}`, (response) => {
|
|
2110
|
-
if (response.error) {
|
|
2111
|
-
remoteError = true;
|
|
2112
|
-
pushFinalizeReject(new Error(response.error));
|
|
2113
|
-
abortController.abort(new Error(response.error));
|
|
2114
|
-
} else if (pushAcked && !pushFinalized) {
|
|
2115
|
-
pushFinalized = true;
|
|
2116
|
-
pushFinalizeResolve();
|
|
2117
|
-
}
|
|
2118
|
-
});
|
|
2119
|
-
if (initialCredit !== void 0 && initialCredit > 0)
|
|
2120
|
-
creditGate = new CreditGate(initialCredit);
|
|
2121
|
-
if (creditGate) {
|
|
2122
|
-
const gate = creditGate;
|
|
2123
|
-
spool.roll(() => {
|
|
2124
|
-
gate.abort();
|
|
2125
|
-
});
|
|
2126
|
-
this.onResponse.set(`sink-push-credit:${requestId}`, (response) => {
|
|
2127
|
-
gate.replenish(response.credit);
|
|
2128
|
-
refreshTimeout();
|
|
2129
|
-
});
|
|
2130
|
-
spool.roll(() => {
|
|
2131
|
-
this.onResponse.delete(`sink-push-credit:${requestId}`);
|
|
2132
|
-
});
|
|
2133
|
-
}
|
|
2134
|
-
const chunkTopic = this.options.topicMake(name, "sink-push-request", receiver);
|
|
2135
|
-
const sendChunk = async (chunk, error, final) => {
|
|
2136
|
-
refreshTimeout();
|
|
2137
|
-
const chunkMsg = this.msg.makeSinkPushChunk(requestId, name, chunk, error, final, this.options.id, receiver);
|
|
2138
|
-
const message = this.codec.encode(chunkMsg);
|
|
2139
|
-
await this.publishToTopic(chunkTopic, message, { qos: 2, ...options });
|
|
2140
|
-
};
|
|
2141
|
-
if (data instanceof Readable)
|
|
2142
|
-
await sendStreamAsChunks(data, this.options.chunkSize, sendChunk, creditGate, abortSignal);
|
|
2143
|
-
else if (data instanceof Uint8Array)
|
|
2144
|
-
await sendBufferAsChunks(data, this.options.chunkSize, sendChunk, creditGate, abortSignal);
|
|
2145
|
-
if (!pushFinalized) {
|
|
2146
|
-
await new Promise((resolve, reject) => {
|
|
2147
|
-
const onAbort = () => {
|
|
2148
|
-
reject(abortSignal.reason);
|
|
2149
|
-
};
|
|
2150
|
-
abortSignal.addEventListener("abort", onAbort, { once: true });
|
|
2151
|
-
pushFinalize.then(resolve, reject).finally(() => {
|
|
2152
|
-
abortSignal.removeEventListener("abort", onAbort);
|
|
2153
|
-
});
|
|
2154
|
-
});
|
|
2155
|
-
}
|
|
2156
|
-
} catch (err) {
|
|
2157
|
-
const error = ensureError(err);
|
|
2158
|
-
abortController.abort(error);
|
|
2159
|
-
if (pushAcked && !remoteError) {
|
|
2160
|
-
const chunkTopic = this.options.topicMake(name, "sink-push-request", receiver);
|
|
2161
|
-
const chunkMsg = this.msg.makeSinkPushChunk(requestId, name, void 0, error.message, true, this.options.id, receiver);
|
|
2162
|
-
const message = this.codec.encode(chunkMsg);
|
|
2163
|
-
await this.publishToTopic(chunkTopic, message, { qos: 2, ...options }).catch(() => {
|
|
2164
|
-
});
|
|
2165
|
-
}
|
|
2166
|
-
throw err;
|
|
2167
|
-
} finally {
|
|
2168
|
-
await spool.unroll();
|
|
2169
|
-
}
|
|
2170
|
-
}
|
|
2171
|
-
}
|
|
2172
|
-
class MQTTp extends SinkTrait {
|
|
2173
|
-
}
|
|
2174
|
-
export {
|
|
2175
|
-
MQTTp as default
|
|
647
|
+
var Msg = class {
|
|
648
|
+
makeEventEmission(id, name, params, sender, receiver, auth, meta) {
|
|
649
|
+
return new EventEmission(id, name, params, sender, receiver, auth, meta);
|
|
650
|
+
}
|
|
651
|
+
makeServiceCallRequest(id, name, params, sender, receiver, auth, meta) {
|
|
652
|
+
return new ServiceCallRequest(id, name, params, sender, receiver, auth, meta);
|
|
653
|
+
}
|
|
654
|
+
makeServiceCallResponse(id, result, error, sender, receiver) {
|
|
655
|
+
return new ServiceCallResponse(id, result, error, sender, receiver);
|
|
656
|
+
}
|
|
657
|
+
makeSinkPushRequest(id, name, params, sender, receiver, auth, meta) {
|
|
658
|
+
return new SinkPushRequest(id, name, params, sender, receiver, auth, meta);
|
|
659
|
+
}
|
|
660
|
+
makeSinkPushResponse(id, name, error, sender, receiver, credit) {
|
|
661
|
+
return new SinkPushResponse(id, name, error, sender, receiver, credit);
|
|
662
|
+
}
|
|
663
|
+
makeSinkPushChunk(id, name, chunk, error, final, sender, receiver) {
|
|
664
|
+
return new SinkPushChunk(id, name, chunk, error, final, sender, receiver);
|
|
665
|
+
}
|
|
666
|
+
makeSinkPushCredit(id, name, credit, sender, receiver) {
|
|
667
|
+
return new SinkPushCredit(id, name, credit, sender, receiver);
|
|
668
|
+
}
|
|
669
|
+
makeSourceFetchRequest(id, name, params, sender, receiver, auth, meta, credit) {
|
|
670
|
+
return new SourceFetchRequest(id, name, params, sender, receiver, auth, meta, credit);
|
|
671
|
+
}
|
|
672
|
+
makeSourceFetchResponse(id, name, error, sender, receiver, meta) {
|
|
673
|
+
return new SourceFetchResponse(id, name, error, sender, receiver, meta);
|
|
674
|
+
}
|
|
675
|
+
makeSourceFetchChunk(id, name, chunk, error, final, sender, receiver) {
|
|
676
|
+
return new SourceFetchChunk(id, name, chunk, error, final, sender, receiver);
|
|
677
|
+
}
|
|
678
|
+
makeSourceFetchCredit(id, name, credit, sender, receiver) {
|
|
679
|
+
return new SourceFetchCredit(id, name, credit, sender, receiver);
|
|
680
|
+
}
|
|
681
|
+
parse(obj) {
|
|
682
|
+
if (typeof obj !== "object" || obj === null) throw new Error("invalid argument: not an object");
|
|
683
|
+
if (typeof obj.version !== "string") throw new Error("invalid object: missing or invalid \"version\" field");
|
|
684
|
+
const match = obj.version.match(/^MQTT\+\/(\d+\.\d+)$/);
|
|
685
|
+
if ((match !== null ? versionToNum(match[1]) : 0) !== version) throw new Error(`protocol version mismatch (expected version 1.4, got version ${obj.version})`);
|
|
686
|
+
const parseObject = (obj, name, schema) => {
|
|
687
|
+
const res = v.safeParse(schema, obj);
|
|
688
|
+
if (!res.success) {
|
|
689
|
+
const issues = res.issues.map((issue) => issue.message).join("; ");
|
|
690
|
+
throw new Error(`invalid ${name} object: ${issues}`);
|
|
691
|
+
}
|
|
692
|
+
return res.output;
|
|
693
|
+
};
|
|
694
|
+
if (typeof obj.type !== "string") throw new Error("invalid object: missing or invalid \"type\" field");
|
|
695
|
+
if (obj.type === "event-emission") {
|
|
696
|
+
const out = parseObject(obj, "EventEmission", EventEmissionSchema);
|
|
697
|
+
return this.makeEventEmission(out.id, out.name, out.params, out.sender, out.receiver, out.auth, out.meta);
|
|
698
|
+
} else if (obj.type === "service-call-request") {
|
|
699
|
+
const out = parseObject(obj, "ServiceCallRequest", ServiceCallRequestSchema);
|
|
700
|
+
return this.makeServiceCallRequest(out.id, out.name, out.params, out.sender, out.receiver, out.auth, out.meta);
|
|
701
|
+
} else if (obj.type === "service-call-response") {
|
|
702
|
+
const out = parseObject(obj, "ServiceCallResponse", ServiceCallResponseSchema);
|
|
703
|
+
return this.makeServiceCallResponse(out.id, out.result, out.error, out.sender, out.receiver);
|
|
704
|
+
} else if (obj.type === "sink-push-request") {
|
|
705
|
+
const out = parseObject(obj, "SinkPushRequest", SinkPushRequestSchema);
|
|
706
|
+
return this.makeSinkPushRequest(out.id, out.name, out.params, out.sender, out.receiver, out.auth, out.meta);
|
|
707
|
+
} else if (obj.type === "sink-push-response") {
|
|
708
|
+
const out = parseObject(obj, "SinkPushResponse", SinkPushResponseSchema);
|
|
709
|
+
return this.makeSinkPushResponse(out.id, out.name, out.error, out.sender, out.receiver, out.credit);
|
|
710
|
+
} else if (obj.type === "sink-push-chunk") {
|
|
711
|
+
const out = parseObject(obj, "SinkPushChunk", SinkPushChunkSchema);
|
|
712
|
+
return this.makeSinkPushChunk(out.id, out.name, out.chunk, out.error, out.final, out.sender, out.receiver);
|
|
713
|
+
} else if (obj.type === "sink-push-credit") {
|
|
714
|
+
const out = parseObject(obj, "SinkPushCredit", SinkPushCreditSchema);
|
|
715
|
+
return this.makeSinkPushCredit(out.id, out.name, out.credit, out.sender, out.receiver);
|
|
716
|
+
} else if (obj.type === "source-fetch-request") {
|
|
717
|
+
const out = parseObject(obj, "SourceFetchRequest", SourceFetchRequestSchema);
|
|
718
|
+
return this.makeSourceFetchRequest(out.id, out.name, out.params, out.sender, out.receiver, out.auth, out.meta, out.credit);
|
|
719
|
+
} else if (obj.type === "source-fetch-response") {
|
|
720
|
+
const out = parseObject(obj, "SourceFetchResponse", SourceFetchResponseSchema);
|
|
721
|
+
return this.makeSourceFetchResponse(out.id, out.name, out.error, out.sender, out.receiver, out.meta);
|
|
722
|
+
} else if (obj.type === "source-fetch-chunk") {
|
|
723
|
+
const out = parseObject(obj, "SourceFetchChunk", SourceFetchChunkSchema);
|
|
724
|
+
return this.makeSourceFetchChunk(out.id, out.name, out.chunk, out.error, out.final, out.sender, out.receiver);
|
|
725
|
+
} else if (obj.type === "source-fetch-credit") {
|
|
726
|
+
const out = parseObject(obj, "SourceFetchCredit", SourceFetchCreditSchema);
|
|
727
|
+
return this.makeSourceFetchCredit(out.id, out.name, out.credit, out.sender, out.receiver);
|
|
728
|
+
} else throw new Error("invalid object: not of any known type");
|
|
729
|
+
}
|
|
730
|
+
isRequest(msg) {
|
|
731
|
+
return msg instanceof EventEmission || msg instanceof ServiceCallRequest || msg instanceof SourceFetchRequest || msg instanceof SinkPushRequest;
|
|
732
|
+
}
|
|
733
|
+
isResponse(msg) {
|
|
734
|
+
return msg instanceof ServiceCallResponse || msg instanceof SinkPushResponse || msg instanceof SinkPushChunk || msg instanceof SinkPushCredit || msg instanceof SourceFetchResponse || msg instanceof SourceFetchChunk || msg instanceof SourceFetchCredit;
|
|
735
|
+
}
|
|
736
|
+
};
|
|
737
|
+
var MsgTrait = class extends EncodeTrait {
|
|
738
|
+
constructor() {
|
|
739
|
+
super(...arguments);
|
|
740
|
+
this.msg = new Msg();
|
|
741
|
+
}
|
|
742
|
+
};
|
|
743
|
+
//#endregion
|
|
744
|
+
//#region dst-stage1/mqtt-plus-trace.js
|
|
745
|
+
var LogEvent = class {
|
|
746
|
+
constructor(timestamp, level, msg, data) {
|
|
747
|
+
this.timestamp = timestamp;
|
|
748
|
+
this.level = level;
|
|
749
|
+
this.msg = msg;
|
|
750
|
+
this.data = data;
|
|
751
|
+
}
|
|
752
|
+
async resolve() {
|
|
753
|
+
if (this.msg instanceof Promise) this.msg = await this.msg.catch(() => "<resolve-failed>");
|
|
754
|
+
if (this.data) {
|
|
755
|
+
for (const k of Object.keys(this.data)) if (this.data[k] instanceof Promise) this.data[k] = await this.data[k].catch(() => "<resolve-failed>");
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
toString() {
|
|
759
|
+
const timestamp = new Date(this.timestamp);
|
|
760
|
+
const time = `${timestamp.getFullYear()}-${(timestamp.getMonth() + 1).toString().padStart(2, "0")}-${timestamp.getDate().toString().padStart(2, "0")} ${timestamp.getHours().toString().padStart(2, "0")}:${timestamp.getMinutes().toString().padStart(2, "0")}:${timestamp.getSeconds().toString().padStart(2, "0")}.${timestamp.getMilliseconds().toString().padStart(3, "0")}`;
|
|
761
|
+
const msg = this.msg instanceof Promise ? "<unresolved>" : this.msg;
|
|
762
|
+
let extra = "";
|
|
763
|
+
if (this.data !== void 0) {
|
|
764
|
+
const data = this.data;
|
|
765
|
+
extra = ` (${Object.keys(data).map((key) => {
|
|
766
|
+
const value = data[key] instanceof Promise ? "<unresolved>" : data[key];
|
|
767
|
+
return `${key}: ${JSONX.stringify(value)}`;
|
|
768
|
+
}).join(", ")})`;
|
|
769
|
+
}
|
|
770
|
+
return `[${time}] ${this.level}: ${msg}${extra}`;
|
|
771
|
+
}
|
|
772
|
+
};
|
|
773
|
+
var TraceTrait = class extends MsgTrait {
|
|
774
|
+
constructor() {
|
|
775
|
+
super(...arguments);
|
|
776
|
+
this._events = new EventEmitter();
|
|
777
|
+
}
|
|
778
|
+
on(...args) {
|
|
779
|
+
this._events.on(...args);
|
|
780
|
+
}
|
|
781
|
+
off(...args) {
|
|
782
|
+
this._events.off(...args);
|
|
783
|
+
}
|
|
784
|
+
emitEvent(...args) {
|
|
785
|
+
if (this._events.listenerCount(args[0]) === 0) return false;
|
|
786
|
+
return this._events.emit(...args);
|
|
787
|
+
}
|
|
788
|
+
log(level, msg, data) {
|
|
789
|
+
const event = new LogEvent(Date.now(), level, msg, data);
|
|
790
|
+
this.emitEvent("log", event);
|
|
791
|
+
}
|
|
792
|
+
error(error, msg) {
|
|
793
|
+
let err = error;
|
|
794
|
+
if (msg !== void 0) err = new Error(`${msg}: ${error.message}`, { cause: error });
|
|
795
|
+
this.emitEvent("error", err);
|
|
796
|
+
this.log("error", err.message);
|
|
797
|
+
}
|
|
798
|
+
};
|
|
799
|
+
//#endregion
|
|
800
|
+
//#region dst-stage1/mqtt-plus-base.js
|
|
801
|
+
var BaseTrait = class extends TraceTrait {
|
|
802
|
+
constructor(mqtt, options = {}) {
|
|
803
|
+
super(options);
|
|
804
|
+
this.onRequest = /* @__PURE__ */ new Map();
|
|
805
|
+
this.onResponse = /* @__PURE__ */ new Map();
|
|
806
|
+
if (mqtt === null) {
|
|
807
|
+
this.log("info", "establishing proxy MQTT client");
|
|
808
|
+
mqtt = new Proxy({}, { get(_target, prop, _receiver) {
|
|
809
|
+
if (prop === "isFakeProxy") return true;
|
|
810
|
+
else if (typeof prop === "string" && [
|
|
811
|
+
"on",
|
|
812
|
+
"off",
|
|
813
|
+
"once"
|
|
814
|
+
].includes(prop)) return () => {};
|
|
815
|
+
else return () => {
|
|
816
|
+
throw new Error(`Underlying MQTT operation "${String(prop)}" called on a null MQTT client -- only MQTT+ "emit({ ..., dry: true })" is supported in this special operation mode`);
|
|
817
|
+
};
|
|
818
|
+
} });
|
|
819
|
+
}
|
|
820
|
+
this.mqtt = mqtt;
|
|
821
|
+
this.log("info", "hooking into MQTT client");
|
|
822
|
+
this.messageHandler = (topic, message, packet) => {
|
|
823
|
+
let input;
|
|
824
|
+
if (this.options.codec === "json") input = message.toString();
|
|
825
|
+
else if (this.options.codec === "cbor") input = Buffer.isBuffer(message) ? new Uint8Array(message.buffer, message.byteOffset, message.byteLength) : message;
|
|
826
|
+
else throw new Error("invalid codec configured");
|
|
827
|
+
this._onMessage(topic, input, packet);
|
|
828
|
+
};
|
|
829
|
+
this.mqtt.on("message", this.messageHandler);
|
|
830
|
+
}
|
|
831
|
+
async destroy() {
|
|
832
|
+
this.log("info", "un-hooking from MQTT client");
|
|
833
|
+
this.mqtt.off("message", this.messageHandler);
|
|
834
|
+
this.onRequest.clear();
|
|
835
|
+
this.onResponse.clear();
|
|
836
|
+
}
|
|
837
|
+
makeRegistration(spool, kind, name, key) {
|
|
838
|
+
return { destroy: async () => {
|
|
839
|
+
if (!this.onRequest.has(key)) throw new Error(`destroy: ${kind} "${name}" not registered`);
|
|
840
|
+
await spool.unroll(false)?.catch((err) => {
|
|
841
|
+
const error = ensureError(err, `destroy: ${kind} "${name}" failed to cleanup`);
|
|
842
|
+
this.error(error);
|
|
843
|
+
throw error;
|
|
844
|
+
});
|
|
845
|
+
} };
|
|
846
|
+
}
|
|
847
|
+
async subscribeTopic(topic, options = {}) {
|
|
848
|
+
this.log("info", `subscribing to MQTT topic "${topic}"`);
|
|
849
|
+
return new Promise((resolve, reject) => {
|
|
850
|
+
this.mqtt.subscribe(topic, {
|
|
851
|
+
qos: 2,
|
|
852
|
+
...options
|
|
853
|
+
}, (err, _granted) => {
|
|
854
|
+
if (err) {
|
|
855
|
+
this.error(err, `subscribing to MQTT topic "${topic}" failed`);
|
|
856
|
+
reject(err);
|
|
857
|
+
} else resolve();
|
|
858
|
+
});
|
|
859
|
+
});
|
|
860
|
+
}
|
|
861
|
+
async unsubscribeTopic(topic) {
|
|
862
|
+
this.log("info", `unsubscribing from MQTT topic "${topic}"`);
|
|
863
|
+
return new Promise((resolve, reject) => {
|
|
864
|
+
this.mqtt.unsubscribe(topic, (err, _packet) => {
|
|
865
|
+
if (err) {
|
|
866
|
+
this.error(err, `unsubscribing from MQTT topic "${topic}" failed`);
|
|
867
|
+
reject(err);
|
|
868
|
+
} else resolve();
|
|
869
|
+
});
|
|
870
|
+
});
|
|
871
|
+
}
|
|
872
|
+
async publishToTopic(topic, message, options = {}) {
|
|
873
|
+
if (typeof message === "string") this.log("info", `publishing to MQTT topic "${topic}" (type: string, length: ${message.length} chars)`);
|
|
874
|
+
else this.log("info", `publishing to MQTT topic "${topic}" (type: buffer, length: ${message.byteLength} bytes)`);
|
|
875
|
+
const messageOnDemand = new PLazy((resolve, reject) => {
|
|
876
|
+
let parsed;
|
|
877
|
+
try {
|
|
878
|
+
const payload = this.codec.decode(message);
|
|
879
|
+
parsed = this.msg.parse(payload);
|
|
880
|
+
} catch (err) {
|
|
881
|
+
return reject(err);
|
|
882
|
+
}
|
|
883
|
+
resolve(parsed);
|
|
884
|
+
});
|
|
885
|
+
this.log("debug", `publishing to MQTT topic "${topic}"`, { message: messageOnDemand });
|
|
886
|
+
return new Promise((resolve, reject) => {
|
|
887
|
+
const messageData = typeof message === "string" ? message : Buffer.from(message.buffer, message.byteOffset, message.byteLength);
|
|
888
|
+
this.mqtt.publish(topic, messageData, options, (err) => {
|
|
889
|
+
if (err) {
|
|
890
|
+
this.error(err, `publishing to MQTT topic "${topic}" failed`);
|
|
891
|
+
reject(err);
|
|
892
|
+
} else resolve();
|
|
893
|
+
});
|
|
894
|
+
});
|
|
895
|
+
}
|
|
896
|
+
_onMessage(topic, data, _packet) {
|
|
897
|
+
const topicMatch = this.options.topicMatch(topic);
|
|
898
|
+
if (topicMatch === null) return;
|
|
899
|
+
if (typeof data === "string") this.log("info", `received from MQTT topic "${topic}" (type: string, length: ${data.length} chars)`);
|
|
900
|
+
else this.log("info", `received from MQTT topic "${topic}" (type: buffer, length: ${data.byteLength} bytes)`);
|
|
901
|
+
let payload;
|
|
902
|
+
try {
|
|
903
|
+
payload = this.codec.decode(data);
|
|
904
|
+
} catch (err) {
|
|
905
|
+
this.error(ensureError(err, "failed to parse message into object"));
|
|
906
|
+
return;
|
|
907
|
+
}
|
|
908
|
+
let message;
|
|
909
|
+
try {
|
|
910
|
+
message = this.msg.parse(payload);
|
|
911
|
+
} catch (err) {
|
|
912
|
+
this.error(ensureError(err, "failed to parse object into typed message object"));
|
|
913
|
+
return;
|
|
914
|
+
}
|
|
915
|
+
this.log("debug", `received from MQTT topic "${topic}"`, { message });
|
|
916
|
+
if (this.msg.isRequest(message)) {
|
|
917
|
+
const handler = this.onRequest.get(`${message.type}:${message.name}`);
|
|
918
|
+
if (handler !== void 0) Promise.resolve().then(() => handler(message, topicMatch.name)).catch((err) => {
|
|
919
|
+
this.error(ensureError(err, `dispatching request message from MQTT topic "${topic}" failed`));
|
|
920
|
+
});
|
|
921
|
+
} else if (this.msg.isResponse(message)) {
|
|
922
|
+
const handler = this.onResponse.get(`${message.type}:${message.id}`);
|
|
923
|
+
if (handler !== void 0) Promise.resolve().then(() => handler(message, topicMatch.name)).catch((err) => {
|
|
924
|
+
this.error(ensureError(err, `dispatching response message from MQTT topic "${topic}" failed`));
|
|
925
|
+
});
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
};
|
|
929
|
+
//#endregion
|
|
930
|
+
//#region dst-stage1/mqtt-plus-subscription.js
|
|
931
|
+
var RefCountedSubscription = class {
|
|
932
|
+
constructor(subscribeFn, unsubscribeFn, lingerMs = 30 * 1e3) {
|
|
933
|
+
this.subscribeFn = subscribeFn;
|
|
934
|
+
this.unsubscribeFn = unsubscribeFn;
|
|
935
|
+
this.lingerMs = lingerMs;
|
|
936
|
+
this.counts = /* @__PURE__ */ new Map();
|
|
937
|
+
this.pending = /* @__PURE__ */ new Map();
|
|
938
|
+
this.lingers = /* @__PURE__ */ new Map();
|
|
939
|
+
this.unsubbing = /* @__PURE__ */ new Map();
|
|
940
|
+
}
|
|
941
|
+
incrementCount(topic) {
|
|
942
|
+
const count = this.counts.get(topic) ?? 0;
|
|
943
|
+
this.counts.set(topic, count + 1);
|
|
944
|
+
return count;
|
|
945
|
+
}
|
|
946
|
+
decrementCount(topic) {
|
|
947
|
+
const count = this.counts.get(topic);
|
|
948
|
+
if (count !== void 0) if (count <= 1) {
|
|
949
|
+
this.counts.delete(topic);
|
|
950
|
+
return 0;
|
|
951
|
+
} else {
|
|
952
|
+
this.counts.set(topic, count - 1);
|
|
953
|
+
return count - 1;
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
async subscribe(topic, options = { qos: 2 }) {
|
|
957
|
+
const count = this.incrementCount(topic);
|
|
958
|
+
const linger = this.lingers.get(topic);
|
|
959
|
+
if (linger) {
|
|
960
|
+
clearTimeout(linger);
|
|
961
|
+
this.lingers.delete(topic);
|
|
962
|
+
return;
|
|
963
|
+
}
|
|
964
|
+
if (count === 0) {
|
|
965
|
+
let resolve;
|
|
966
|
+
let reject;
|
|
967
|
+
const deferred = new Promise((res, rej) => {
|
|
968
|
+
resolve = res;
|
|
969
|
+
reject = rej;
|
|
970
|
+
});
|
|
971
|
+
deferred.catch(() => {});
|
|
972
|
+
this.pending.set(topic, deferred);
|
|
973
|
+
const inflight = this.unsubbing.get(topic);
|
|
974
|
+
if (inflight) await inflight;
|
|
975
|
+
return this.subscribeFn(topic, options).then(() => {
|
|
976
|
+
this.pending.delete(topic);
|
|
977
|
+
resolve();
|
|
978
|
+
}).catch((err) => {
|
|
979
|
+
this.pending.delete(topic);
|
|
980
|
+
this.decrementCount(topic);
|
|
981
|
+
reject(err);
|
|
982
|
+
throw err;
|
|
983
|
+
});
|
|
984
|
+
} else {
|
|
985
|
+
const pending = this.pending.get(topic);
|
|
986
|
+
if (pending) return pending.catch((err) => {
|
|
987
|
+
this.decrementCount(topic);
|
|
988
|
+
throw err;
|
|
989
|
+
});
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
async unsubscribe(topic) {
|
|
993
|
+
if (this.decrementCount(topic) === 0) if (this.lingerMs > 0) {
|
|
994
|
+
const timer = setTimeout(() => {
|
|
995
|
+
this.lingers.delete(topic);
|
|
996
|
+
const promise = this.unsubscribeFn(topic).catch(() => {}).finally(() => {
|
|
997
|
+
this.unsubbing.delete(topic);
|
|
998
|
+
});
|
|
999
|
+
this.unsubbing.set(topic, promise);
|
|
1000
|
+
}, this.lingerMs);
|
|
1001
|
+
this.lingers.set(topic, timer);
|
|
1002
|
+
} else {
|
|
1003
|
+
const promise = this.unsubscribeFn(topic).catch(() => {}).finally(() => {
|
|
1004
|
+
this.unsubbing.delete(topic);
|
|
1005
|
+
});
|
|
1006
|
+
this.unsubbing.set(topic, promise);
|
|
1007
|
+
await promise;
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
async flush() {
|
|
1011
|
+
const topics = new Set([
|
|
1012
|
+
...this.counts.keys(),
|
|
1013
|
+
...this.lingers.keys(),
|
|
1014
|
+
...this.pending.keys(),
|
|
1015
|
+
...this.unsubbing.keys()
|
|
1016
|
+
]);
|
|
1017
|
+
for (const timer of this.lingers.values()) clearTimeout(timer);
|
|
1018
|
+
this.lingers.clear();
|
|
1019
|
+
this.counts.clear();
|
|
1020
|
+
await Promise.allSettled([...this.pending.values(), ...this.unsubbing.values()]);
|
|
1021
|
+
await Promise.allSettled([...topics].map((topic) => this.unsubscribeFn(topic).catch(() => {})));
|
|
1022
|
+
this.pending.clear();
|
|
1023
|
+
this.unsubbing.clear();
|
|
1024
|
+
}
|
|
1025
|
+
};
|
|
1026
|
+
var SubscriptionTrait = class extends BaseTrait {
|
|
1027
|
+
constructor() {
|
|
1028
|
+
super(...arguments);
|
|
1029
|
+
this.subscriptions = new RefCountedSubscription((topic, options) => this.subscribeTopic(topic, options), (topic) => this.unsubscribeTopic(topic));
|
|
1030
|
+
}
|
|
1031
|
+
async subscribeTopicAndSpool(spool, topic, options = {}) {
|
|
1032
|
+
await run(`subscribe to MQTT topic "${topic}"`, spool, () => this.subscriptions.subscribe(topic, {
|
|
1033
|
+
qos: 2,
|
|
1034
|
+
...options
|
|
1035
|
+
}));
|
|
1036
|
+
spool.roll(() => this.subscriptions.unsubscribe(topic));
|
|
1037
|
+
}
|
|
1038
|
+
async destroy() {
|
|
1039
|
+
await this.subscriptions.flush();
|
|
1040
|
+
await super.destroy();
|
|
1041
|
+
}
|
|
1042
|
+
};
|
|
1043
|
+
//#endregion
|
|
1044
|
+
//#region dst-stage1/mqtt-plus-timer.js
|
|
1045
|
+
var TimerTrait = class extends SubscriptionTrait {
|
|
1046
|
+
constructor() {
|
|
1047
|
+
super(...arguments);
|
|
1048
|
+
this.timers = /* @__PURE__ */ new Map();
|
|
1049
|
+
}
|
|
1050
|
+
async destroy() {
|
|
1051
|
+
for (const timer of this.timers.values()) clearTimeout(timer);
|
|
1052
|
+
this.timers.clear();
|
|
1053
|
+
await super.destroy();
|
|
1054
|
+
}
|
|
1055
|
+
timerRefresh(id, onTimeout) {
|
|
1056
|
+
const timer = this.timers.get(id);
|
|
1057
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
1058
|
+
this.timers.set(id, setTimeout(async () => {
|
|
1059
|
+
this.timers.delete(id);
|
|
1060
|
+
try {
|
|
1061
|
+
await onTimeout();
|
|
1062
|
+
} catch (err) {
|
|
1063
|
+
this.error(ensureError(err), `timer "${id}" failed`);
|
|
1064
|
+
}
|
|
1065
|
+
}, this.options.timeout));
|
|
1066
|
+
}
|
|
1067
|
+
timerClear(id) {
|
|
1068
|
+
const timer = this.timers.get(id);
|
|
1069
|
+
if (timer !== void 0) {
|
|
1070
|
+
clearTimeout(timer);
|
|
1071
|
+
this.timers.delete(id);
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
};
|
|
1075
|
+
//#endregion
|
|
1076
|
+
//#region dst-stage1/mqtt-plus-meta.js
|
|
1077
|
+
var MetaTrait = class extends TimerTrait {
|
|
1078
|
+
constructor() {
|
|
1079
|
+
super(...arguments);
|
|
1080
|
+
this._meta = /* @__PURE__ */ new Map();
|
|
1081
|
+
}
|
|
1082
|
+
meta(...args) {
|
|
1083
|
+
const [key, value] = args;
|
|
1084
|
+
if (args.length === 0) return Object.fromEntries(this._meta);
|
|
1085
|
+
else if (args.length === 1) return this._meta.get(key);
|
|
1086
|
+
else if (value === void 0 || value === null) this._meta.delete(key);
|
|
1087
|
+
else this._meta.set(key, value);
|
|
1088
|
+
}
|
|
1089
|
+
metaStore(extra) {
|
|
1090
|
+
const extraEmpty = extra === void 0 || Object.keys(extra).length === 0;
|
|
1091
|
+
if (this._meta.size === 0 && extraEmpty) return void 0;
|
|
1092
|
+
else if (this._meta.size > 0 && extraEmpty) return Object.fromEntries(this._meta);
|
|
1093
|
+
else if (this._meta.size === 0 && !extraEmpty) return extra;
|
|
1094
|
+
else return {
|
|
1095
|
+
...Object.fromEntries(this._meta),
|
|
1096
|
+
...extra
|
|
1097
|
+
};
|
|
1098
|
+
}
|
|
1099
|
+
};
|
|
1100
|
+
//#endregion
|
|
1101
|
+
//#region dst-stage1/mqtt-plus-auth.js
|
|
1102
|
+
var textEncoder = new TextEncoder();
|
|
1103
|
+
var AuthTrait = class extends MetaTrait {
|
|
1104
|
+
constructor() {
|
|
1105
|
+
super(...arguments);
|
|
1106
|
+
this._credential = null;
|
|
1107
|
+
this._tokens = /* @__PURE__ */ new Set();
|
|
1108
|
+
}
|
|
1109
|
+
credential(credential) {
|
|
1110
|
+
if (credential.length === 0) throw new Error("credential must not be empty");
|
|
1111
|
+
const pass = textEncoder.encode(credential);
|
|
1112
|
+
const salt = textEncoder.encode("mqtt-plus");
|
|
1113
|
+
this._credential = pbkdf2.deriveKey(sha256.SHA256, pass, salt, 6e5, 32);
|
|
1114
|
+
}
|
|
1115
|
+
async issue(payload) {
|
|
1116
|
+
if (this._credential === null) throw new Error("credential has to be provided before issuing tokens");
|
|
1117
|
+
if (payload.roles.length === 0) throw new Error("payload.roles must be a non-empty array");
|
|
1118
|
+
if (payload.roles.length > 64) throw new Error("payload.roles must not exceed 64 roles");
|
|
1119
|
+
const jwt = new SignJWT(payload);
|
|
1120
|
+
jwt.setProtectedHeader({
|
|
1121
|
+
alg: "HS256",
|
|
1122
|
+
typ: "JWT"
|
|
1123
|
+
});
|
|
1124
|
+
return await jwt.sign(this._credential);
|
|
1125
|
+
}
|
|
1126
|
+
authenticate(token, remove) {
|
|
1127
|
+
if (token === void 0) {
|
|
1128
|
+
const tokens = Array.from(this._tokens).filter((token) => token.length <= 8192).slice(0, 8);
|
|
1129
|
+
return tokens.length > 0 ? tokens : void 0;
|
|
1130
|
+
} else if (remove === true) this._tokens.delete(token);
|
|
1131
|
+
else {
|
|
1132
|
+
if (token.length > 8192) throw new Error("token must not exceed 8192 characters");
|
|
1133
|
+
if (!this._tokens.has(token) && this._tokens.size >= 8) throw new Error("at most 8 tokens can be authenticated at once");
|
|
1134
|
+
this._tokens.add(token);
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1137
|
+
async validateToken(token) {
|
|
1138
|
+
if (this._credential === null) throw new Error("credential has to be provided before validating tokens");
|
|
1139
|
+
return (await jwtVerify(token, this._credential).catch(() => null))?.payload ?? null;
|
|
1140
|
+
}
|
|
1141
|
+
async authenticated(clientId, tokens, option) {
|
|
1142
|
+
let authenticated = false;
|
|
1143
|
+
let mode;
|
|
1144
|
+
let roles;
|
|
1145
|
+
if (typeof option === "string") {
|
|
1146
|
+
mode = "require";
|
|
1147
|
+
roles = [option];
|
|
1148
|
+
} else {
|
|
1149
|
+
mode = option.mode;
|
|
1150
|
+
roles = option.roles;
|
|
1151
|
+
}
|
|
1152
|
+
if (tokens !== void 0) for (const token of tokens.slice(0, 8)) {
|
|
1153
|
+
if (token.length > 8192) continue;
|
|
1154
|
+
const payload = await this.validateToken(token);
|
|
1155
|
+
if (payload === null) continue;
|
|
1156
|
+
if (payload.id && payload.id !== clientId) continue;
|
|
1157
|
+
if (!Array.isArray(payload.roles)) continue;
|
|
1158
|
+
if (payload.roles.length > 64) continue;
|
|
1159
|
+
for (const role of roles) if (payload.roles.includes(role)) {
|
|
1160
|
+
authenticated = true;
|
|
1161
|
+
break;
|
|
1162
|
+
}
|
|
1163
|
+
if (authenticated) break;
|
|
1164
|
+
}
|
|
1165
|
+
if (!authenticated && mode === "optional") authenticated = true;
|
|
1166
|
+
return authenticated;
|
|
1167
|
+
}
|
|
1168
|
+
};
|
|
1169
|
+
//#endregion
|
|
1170
|
+
//#region dst-stage1/mqtt-plus-event.js
|
|
1171
|
+
var EventTrait = class extends AuthTrait {
|
|
1172
|
+
async event(nameOrConfig, ...args) {
|
|
1173
|
+
let name;
|
|
1174
|
+
let callback;
|
|
1175
|
+
let options = {};
|
|
1176
|
+
let share = this.options.share;
|
|
1177
|
+
let auth;
|
|
1178
|
+
if (typeof nameOrConfig === "object" && nameOrConfig !== null) {
|
|
1179
|
+
name = nameOrConfig.name;
|
|
1180
|
+
callback = nameOrConfig.callback;
|
|
1181
|
+
options = nameOrConfig.options ?? {};
|
|
1182
|
+
share = nameOrConfig.share ?? this.options.share;
|
|
1183
|
+
auth = nameOrConfig.auth;
|
|
1184
|
+
} else {
|
|
1185
|
+
name = nameOrConfig;
|
|
1186
|
+
callback = args[0];
|
|
1187
|
+
}
|
|
1188
|
+
const spool = new Spool();
|
|
1189
|
+
if (this.onRequest.has(`event-emission:${name}`)) throw new Error(`event: event "${name}" already registered`);
|
|
1190
|
+
const topicS = share !== "" ? `$share/${share}/${name}` : name;
|
|
1191
|
+
const topicB = this.options.topicMake(topicS, "event-emission");
|
|
1192
|
+
const topicD = this.options.topicMake(name, "event-emission", this.options.id);
|
|
1193
|
+
this.onRequest.set(`event-emission:${name}`, async (request, topicName) => {
|
|
1194
|
+
const senderId = request.sender;
|
|
1195
|
+
if (senderId === void 0 || senderId === "") throw new Error("invalid request: missing sender");
|
|
1196
|
+
const params = request.params ?? [];
|
|
1197
|
+
const info = { sender: senderId };
|
|
1198
|
+
if (request.receiver) info.receiver = request.receiver;
|
|
1199
|
+
if (request.meta) info.meta = request.meta;
|
|
1200
|
+
try {
|
|
1201
|
+
if (topicName !== request.name) throw new Error(`event name mismatch (topic: "${topicName}", payload: "${request.name}")`);
|
|
1202
|
+
if (auth) info.authenticated = await this.authenticated(senderId, request.auth, auth);
|
|
1203
|
+
if (info.authenticated !== void 0 && !info.authenticated) throw new Error(`event "${name}" failed authentication`);
|
|
1204
|
+
await callback(...params, info);
|
|
1205
|
+
} catch (result) {
|
|
1206
|
+
const error = ensureError(result);
|
|
1207
|
+
this.error(error, `handler for event "${name}" failed`);
|
|
1208
|
+
}
|
|
1209
|
+
});
|
|
1210
|
+
spool.roll(() => {
|
|
1211
|
+
this.onRequest.delete(`event-emission:${name}`);
|
|
1212
|
+
});
|
|
1213
|
+
await this.subscribeTopicAndSpool(spool, topicB, options);
|
|
1214
|
+
await this.subscribeTopicAndSpool(spool, topicD, options);
|
|
1215
|
+
return this.makeRegistration(spool, "event", name, `event-emission:${name}`);
|
|
1216
|
+
}
|
|
1217
|
+
emit(nameOrConfig, ...args) {
|
|
1218
|
+
let name;
|
|
1219
|
+
let params;
|
|
1220
|
+
let receiver;
|
|
1221
|
+
let options = {};
|
|
1222
|
+
let meta;
|
|
1223
|
+
let dry;
|
|
1224
|
+
if (typeof nameOrConfig === "object" && nameOrConfig !== null) {
|
|
1225
|
+
name = nameOrConfig.name;
|
|
1226
|
+
params = nameOrConfig.params;
|
|
1227
|
+
receiver = nameOrConfig.receiver;
|
|
1228
|
+
options = nameOrConfig.options ?? {};
|
|
1229
|
+
meta = nameOrConfig.meta;
|
|
1230
|
+
dry = nameOrConfig.dry;
|
|
1231
|
+
} else {
|
|
1232
|
+
name = nameOrConfig;
|
|
1233
|
+
params = args;
|
|
1234
|
+
}
|
|
1235
|
+
const requestId = nanoid();
|
|
1236
|
+
const auth = this.authenticate();
|
|
1237
|
+
const metaStore = this.metaStore(meta);
|
|
1238
|
+
const request = this.msg.makeEventEmission(requestId, name, params, this.options.id, receiver, auth, metaStore);
|
|
1239
|
+
const message = this.codec.encode(request);
|
|
1240
|
+
const topic = this.options.topicMake(name, "event-emission", receiver);
|
|
1241
|
+
if (dry) return {
|
|
1242
|
+
topic,
|
|
1243
|
+
payload: message,
|
|
1244
|
+
options: {
|
|
1245
|
+
qos: 2,
|
|
1246
|
+
...options
|
|
1247
|
+
}
|
|
1248
|
+
};
|
|
1249
|
+
else this.publishToTopic(topic, message, {
|
|
1250
|
+
qos: 2,
|
|
1251
|
+
...options
|
|
1252
|
+
}).catch((err) => {
|
|
1253
|
+
this.error(err, `emitting event "${name}" failed`);
|
|
1254
|
+
});
|
|
1255
|
+
}
|
|
1256
|
+
};
|
|
1257
|
+
//#endregion
|
|
1258
|
+
//#region dst-stage1/mqtt-plus-service.js
|
|
1259
|
+
var ServiceTrait = class extends EventTrait {
|
|
1260
|
+
async service(nameOrConfig, ...args) {
|
|
1261
|
+
let name;
|
|
1262
|
+
let callback;
|
|
1263
|
+
let options = {};
|
|
1264
|
+
let share = this.options.share;
|
|
1265
|
+
let auth;
|
|
1266
|
+
if (typeof nameOrConfig === "object" && nameOrConfig !== null) {
|
|
1267
|
+
name = nameOrConfig.name;
|
|
1268
|
+
callback = nameOrConfig.callback;
|
|
1269
|
+
options = nameOrConfig.options ?? {};
|
|
1270
|
+
share = nameOrConfig.share ?? this.options.share;
|
|
1271
|
+
auth = nameOrConfig.auth;
|
|
1272
|
+
} else {
|
|
1273
|
+
name = nameOrConfig;
|
|
1274
|
+
callback = args[0];
|
|
1275
|
+
}
|
|
1276
|
+
const spool = new Spool();
|
|
1277
|
+
if (this.onRequest.has(`service-call-request:${name}`)) throw new Error(`service: service "${name}" already registered`);
|
|
1278
|
+
const topicS = share !== "" ? `$share/${share}/${name}` : name;
|
|
1279
|
+
const topicB = this.options.topicMake(topicS, "service-call-request");
|
|
1280
|
+
const topicD = this.options.topicMake(name, "service-call-request", this.options.id);
|
|
1281
|
+
this.onRequest.set(`service-call-request:${name}`, async (request, topicName) => {
|
|
1282
|
+
const requestId = request.id;
|
|
1283
|
+
const senderId = request.sender;
|
|
1284
|
+
if (senderId === void 0 || senderId === "") throw new Error("invalid request: missing sender");
|
|
1285
|
+
const params = request.params ?? [];
|
|
1286
|
+
const info = { sender: senderId };
|
|
1287
|
+
if (request.receiver) info.receiver = request.receiver;
|
|
1288
|
+
if (request.meta) info.meta = request.meta;
|
|
1289
|
+
try {
|
|
1290
|
+
if (topicName !== request.name) throw new Error(`service name mismatch (topic: "${topicName}", payload: "${request.name}")`);
|
|
1291
|
+
if (auth) info.authenticated = await this.authenticated(senderId, request.auth, auth);
|
|
1292
|
+
if (info.authenticated !== void 0 && !info.authenticated) throw new Error(`service "${name}" failed authentication`);
|
|
1293
|
+
const result = await callback(...params, info);
|
|
1294
|
+
const rpcResponse = this.msg.makeServiceCallResponse(requestId, result, void 0, this.options.id, senderId);
|
|
1295
|
+
const encoded = this.codec.encode(rpcResponse);
|
|
1296
|
+
const topic = this.options.topicMake(name, "service-call-response", senderId);
|
|
1297
|
+
await this.publishToTopic(topic, encoded, { qos: options.qos ?? 2 });
|
|
1298
|
+
} catch (err) {
|
|
1299
|
+
const error = ensureError(err);
|
|
1300
|
+
this.error(error, `handler for service "${name}" failed`);
|
|
1301
|
+
const rpcResponse = this.msg.makeServiceCallResponse(requestId, void 0, error.message, this.options.id, senderId);
|
|
1302
|
+
try {
|
|
1303
|
+
const encoded = this.codec.encode(rpcResponse);
|
|
1304
|
+
const topic = this.options.topicMake(name, "service-call-response", senderId);
|
|
1305
|
+
await this.publishToTopic(topic, encoded, { qos: options.qos ?? 2 });
|
|
1306
|
+
} catch (err2) {
|
|
1307
|
+
this.error(ensureError(err2), `sending error response for service "${name}" failed`);
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1310
|
+
});
|
|
1311
|
+
spool.roll(() => {
|
|
1312
|
+
this.onRequest.delete(`service-call-request:${name}`);
|
|
1313
|
+
});
|
|
1314
|
+
await this.subscribeTopicAndSpool(spool, topicB, options);
|
|
1315
|
+
await this.subscribeTopicAndSpool(spool, topicD, options);
|
|
1316
|
+
return this.makeRegistration(spool, "service", name, `service-call-request:${name}`);
|
|
1317
|
+
}
|
|
1318
|
+
async call(nameOrConfig, ...args) {
|
|
1319
|
+
let name;
|
|
1320
|
+
let params;
|
|
1321
|
+
let receiver;
|
|
1322
|
+
let options = {};
|
|
1323
|
+
let meta;
|
|
1324
|
+
if (typeof nameOrConfig === "object" && nameOrConfig !== null) {
|
|
1325
|
+
name = nameOrConfig.name;
|
|
1326
|
+
params = nameOrConfig.params;
|
|
1327
|
+
receiver = nameOrConfig.receiver;
|
|
1328
|
+
options = nameOrConfig.options ?? {};
|
|
1329
|
+
meta = nameOrConfig.meta;
|
|
1330
|
+
} else {
|
|
1331
|
+
name = nameOrConfig;
|
|
1332
|
+
params = args;
|
|
1333
|
+
}
|
|
1334
|
+
const spool = new Spool();
|
|
1335
|
+
let requestId = nanoid();
|
|
1336
|
+
while (this.onResponse.has(`service-call-response:${requestId}`)) requestId = nanoid();
|
|
1337
|
+
const responseTopic = this.options.topicMake(name, "service-call-response", this.options.id);
|
|
1338
|
+
await this.subscribeTopicAndSpool(spool, responseTopic, { qos: options.qos ?? 2 });
|
|
1339
|
+
const timerId = `service-call:${requestId}`;
|
|
1340
|
+
let rejectPromise;
|
|
1341
|
+
const promise = new Promise((resolve, reject) => {
|
|
1342
|
+
rejectPromise = reject;
|
|
1343
|
+
this.timerRefresh(timerId, async () => {
|
|
1344
|
+
await spool.unroll();
|
|
1345
|
+
reject(/* @__PURE__ */ new Error("communication timeout"));
|
|
1346
|
+
});
|
|
1347
|
+
spool.roll(() => {
|
|
1348
|
+
this.timerClear(timerId);
|
|
1349
|
+
});
|
|
1350
|
+
this.onResponse.set(`service-call-response:${requestId}`, async (response) => {
|
|
1351
|
+
await spool.unroll();
|
|
1352
|
+
if (response.error !== void 0) reject(new Error(response.error));
|
|
1353
|
+
else resolve(response.result);
|
|
1354
|
+
});
|
|
1355
|
+
spool.roll(() => {
|
|
1356
|
+
this.onResponse.delete(`service-call-response:${requestId}`);
|
|
1357
|
+
});
|
|
1358
|
+
});
|
|
1359
|
+
const auth = this.authenticate();
|
|
1360
|
+
const metaStore = this.metaStore(meta);
|
|
1361
|
+
const request = this.msg.makeServiceCallRequest(requestId, name, params, this.options.id, receiver, auth, metaStore);
|
|
1362
|
+
const message = this.codec.encode(request);
|
|
1363
|
+
const topic = this.options.topicMake(name, "service-call-request", receiver);
|
|
1364
|
+
try {
|
|
1365
|
+
await run(`publish service request as MQTT message to topic "${topic}"`, spool, () => this.publishToTopic(topic, message, {
|
|
1366
|
+
qos: 2,
|
|
1367
|
+
...options
|
|
1368
|
+
}));
|
|
1369
|
+
} catch (err) {
|
|
1370
|
+
rejectPromise(err);
|
|
1371
|
+
return promise;
|
|
1372
|
+
}
|
|
1373
|
+
return promise;
|
|
1374
|
+
}
|
|
1375
|
+
};
|
|
1376
|
+
//#endregion
|
|
1377
|
+
//#region dst-stage1/mqtt-plus-source.js
|
|
1378
|
+
var SourceTrait = class extends ServiceTrait {
|
|
1379
|
+
constructor() {
|
|
1380
|
+
super(...arguments);
|
|
1381
|
+
this.sourceCreditGates = /* @__PURE__ */ new Map();
|
|
1382
|
+
this.sourceControllers = /* @__PURE__ */ new Map();
|
|
1383
|
+
}
|
|
1384
|
+
async destroy() {
|
|
1385
|
+
for (const controller of this.sourceControllers.values()) controller.abort(/* @__PURE__ */ new Error("source destroyed"));
|
|
1386
|
+
this.sourceControllers.clear();
|
|
1387
|
+
for (const gate of this.sourceCreditGates.values()) gate.abort();
|
|
1388
|
+
this.sourceCreditGates.clear();
|
|
1389
|
+
await super.destroy();
|
|
1390
|
+
}
|
|
1391
|
+
async source(nameOrConfig, ...args) {
|
|
1392
|
+
let name;
|
|
1393
|
+
let callback;
|
|
1394
|
+
let options = {};
|
|
1395
|
+
let share = this.options.share;
|
|
1396
|
+
let auth;
|
|
1397
|
+
if (typeof nameOrConfig === "object" && nameOrConfig !== null) {
|
|
1398
|
+
name = nameOrConfig.name;
|
|
1399
|
+
callback = nameOrConfig.callback;
|
|
1400
|
+
options = nameOrConfig.options ?? {};
|
|
1401
|
+
share = nameOrConfig.share ?? this.options.share;
|
|
1402
|
+
auth = nameOrConfig.auth;
|
|
1403
|
+
} else {
|
|
1404
|
+
name = nameOrConfig;
|
|
1405
|
+
callback = args[0];
|
|
1406
|
+
}
|
|
1407
|
+
const spool = new Spool();
|
|
1408
|
+
if (this.onRequest.has(`source-fetch-request:${name}`)) throw new Error(`source: source "${name}" already registered`);
|
|
1409
|
+
const topicS = share !== "" ? `$share/${share}/${name}` : name;
|
|
1410
|
+
const topicReqB = this.options.topicMake(topicS, "source-fetch-request");
|
|
1411
|
+
const topicReqD = this.options.topicMake(name, "source-fetch-request", this.options.id);
|
|
1412
|
+
this.onRequest.set(`source-fetch-request:${name}`, async (request, topicName) => {
|
|
1413
|
+
const requestId = request.id;
|
|
1414
|
+
const params = request.params ?? [];
|
|
1415
|
+
const sender = request.sender;
|
|
1416
|
+
if (sender === void 0 || sender === "") throw new Error("invalid request: missing sender");
|
|
1417
|
+
const receiver = request.receiver;
|
|
1418
|
+
const info = { sender };
|
|
1419
|
+
if (receiver) info.receiver = receiver;
|
|
1420
|
+
if (request.meta) info.meta = request.meta;
|
|
1421
|
+
const responseTopic = this.options.topicMake(name, "source-fetch-response", sender);
|
|
1422
|
+
const sendResponse = async (error) => {
|
|
1423
|
+
const metaStore = this.metaStore(info.meta);
|
|
1424
|
+
const response = this.msg.makeSourceFetchResponse(requestId, name, error, this.options.id, sender, metaStore);
|
|
1425
|
+
const message = this.codec.encode(response);
|
|
1426
|
+
await this.publishToTopic(responseTopic, message, { qos: options.qos ?? 2 });
|
|
1427
|
+
};
|
|
1428
|
+
const reqSpool = new Spool();
|
|
1429
|
+
reqSpool.roll(() => {
|
|
1430
|
+
this.onResponse.delete(`source-fetch-credit:${requestId}`);
|
|
1431
|
+
this.sourceControllers.delete(requestId);
|
|
1432
|
+
});
|
|
1433
|
+
const abortController = new AbortController();
|
|
1434
|
+
this.sourceControllers.set(requestId, abortController);
|
|
1435
|
+
const abortSignal = abortController.signal;
|
|
1436
|
+
abortSignal.addEventListener("abort", () => {
|
|
1437
|
+
if (info.stream instanceof Readable && !info.stream.destroyed) info.stream.destroy(ensureError(abortSignal.reason));
|
|
1438
|
+
}, { once: true });
|
|
1439
|
+
const sourceTimerId = `source-fetch-send:${requestId}`;
|
|
1440
|
+
const refreshSourceTimeout = () => this.timerRefresh(sourceTimerId, () => {
|
|
1441
|
+
const error = /* @__PURE__ */ new Error(`source fetch "${name}" timed out`);
|
|
1442
|
+
abortController.abort(error);
|
|
1443
|
+
const gate = this.sourceCreditGates.get(requestId);
|
|
1444
|
+
if (gate !== void 0) {
|
|
1445
|
+
gate.abort();
|
|
1446
|
+
this.sourceCreditGates.delete(requestId);
|
|
1447
|
+
}
|
|
1448
|
+
reqSpool.unroll();
|
|
1449
|
+
});
|
|
1450
|
+
const clearSourceTimeout = () => this.timerClear(sourceTimerId);
|
|
1451
|
+
refreshSourceTimeout();
|
|
1452
|
+
reqSpool.roll(() => {
|
|
1453
|
+
clearSourceTimeout();
|
|
1454
|
+
});
|
|
1455
|
+
const sendChunk = async (chunk, error, final) => {
|
|
1456
|
+
refreshSourceTimeout();
|
|
1457
|
+
const chunkMsg = this.msg.makeSourceFetchChunk(requestId, name, chunk, error, final, this.options.id, sender);
|
|
1458
|
+
const message = this.codec.encode(chunkMsg);
|
|
1459
|
+
await this.publishToTopic(responseTopic, message, { qos: options.qos ?? 2 });
|
|
1460
|
+
};
|
|
1461
|
+
let ackSent = false;
|
|
1462
|
+
let creditGate;
|
|
1463
|
+
let cancelledByFetcher = false;
|
|
1464
|
+
try {
|
|
1465
|
+
if (topicName !== request.name) throw new Error(`source name mismatch (topic: "${topicName}", payload: "${request.name}")`);
|
|
1466
|
+
if (auth) info.authenticated = await this.authenticated(sender, request.auth, auth);
|
|
1467
|
+
if (info.authenticated !== void 0 && !info.authenticated) throw new Error(`source "${name}" failed authentication`);
|
|
1468
|
+
const initialCredit = request.credit;
|
|
1469
|
+
creditGate = initialCredit !== void 0 && initialCredit > 0 ? new CreditGate(initialCredit) : void 0;
|
|
1470
|
+
if (creditGate) {
|
|
1471
|
+
this.sourceCreditGates.set(requestId, creditGate);
|
|
1472
|
+
reqSpool.roll(() => {
|
|
1473
|
+
creditGate.abort();
|
|
1474
|
+
this.sourceCreditGates.delete(requestId);
|
|
1475
|
+
});
|
|
1476
|
+
}
|
|
1477
|
+
this.onResponse.set(`source-fetch-credit:${requestId}`, (creditParsed) => {
|
|
1478
|
+
if (creditParsed.credit === 0) {
|
|
1479
|
+
cancelledByFetcher = true;
|
|
1480
|
+
abortController.abort(/* @__PURE__ */ new Error(`source fetch "${name}" cancelled by fetcher`));
|
|
1481
|
+
return;
|
|
1482
|
+
}
|
|
1483
|
+
if (creditGate) {
|
|
1484
|
+
creditGate.replenish(creditParsed.credit);
|
|
1485
|
+
refreshSourceTimeout();
|
|
1486
|
+
}
|
|
1487
|
+
});
|
|
1488
|
+
await callback(...params, info);
|
|
1489
|
+
if (!(info.stream instanceof Readable) && !(info.buffer instanceof Promise)) throw new Error("handler did not provide data via info.stream or info.buffer fields");
|
|
1490
|
+
if (info.stream instanceof Readable && info.buffer instanceof Promise) throw new Error("handler has set both info.stream and info.buffer fields");
|
|
1491
|
+
await sendResponse();
|
|
1492
|
+
ackSent = true;
|
|
1493
|
+
if (info.stream instanceof Readable) await sendStreamAsChunks(info.stream, this.options.chunkSize, sendChunk, creditGate, abortSignal);
|
|
1494
|
+
else if (info.buffer instanceof Promise) await sendBufferAsChunks(await info.buffer, this.options.chunkSize, sendChunk, creditGate, abortSignal);
|
|
1495
|
+
} catch (err) {
|
|
1496
|
+
const error = ensureError(err, `handler for source "${name}" failed`);
|
|
1497
|
+
abortController.abort(error);
|
|
1498
|
+
if (!cancelledByFetcher) {
|
|
1499
|
+
this.error(error);
|
|
1500
|
+
if (ackSent) await sendChunk(void 0, error.message, true).catch(() => {});
|
|
1501
|
+
else await sendResponse(error.message).catch(() => {});
|
|
1502
|
+
}
|
|
1503
|
+
} finally {
|
|
1504
|
+
await reqSpool.unroll();
|
|
1505
|
+
}
|
|
1506
|
+
});
|
|
1507
|
+
spool.roll(() => {
|
|
1508
|
+
this.onRequest.delete(`source-fetch-request:${name}`);
|
|
1509
|
+
});
|
|
1510
|
+
await this.subscribeTopicAndSpool(spool, topicReqB, options);
|
|
1511
|
+
await this.subscribeTopicAndSpool(spool, topicReqD, options);
|
|
1512
|
+
return this.makeRegistration(spool, "source", name, `source-fetch-request:${name}`);
|
|
1513
|
+
}
|
|
1514
|
+
async fetch(nameOrConfig, ...args) {
|
|
1515
|
+
let name;
|
|
1516
|
+
let params;
|
|
1517
|
+
let receiver;
|
|
1518
|
+
let options = {};
|
|
1519
|
+
let meta;
|
|
1520
|
+
if (typeof nameOrConfig === "object" && nameOrConfig !== null) {
|
|
1521
|
+
name = nameOrConfig.name;
|
|
1522
|
+
params = nameOrConfig.params;
|
|
1523
|
+
receiver = nameOrConfig.receiver;
|
|
1524
|
+
options = nameOrConfig.options ?? {};
|
|
1525
|
+
meta = nameOrConfig.meta;
|
|
1526
|
+
} else {
|
|
1527
|
+
name = nameOrConfig;
|
|
1528
|
+
params = args;
|
|
1529
|
+
}
|
|
1530
|
+
const spool = new Spool();
|
|
1531
|
+
let requestId = nanoid();
|
|
1532
|
+
while (this.onResponse.has(`source-fetch-response:${requestId}`) || this.onResponse.has(`source-fetch-chunk:${requestId}`)) requestId = nanoid();
|
|
1533
|
+
const responseTopic = this.options.topicMake(name, "source-fetch-response", this.options.id);
|
|
1534
|
+
await this.subscribeTopicAndSpool(spool, responseTopic, { qos: options.qos ?? 2 });
|
|
1535
|
+
const chunkCredit = this.options.chunkCredit;
|
|
1536
|
+
let chunksReceived = 0;
|
|
1537
|
+
let creditGranted = chunkCredit;
|
|
1538
|
+
let serverId;
|
|
1539
|
+
let streamEnded = false;
|
|
1540
|
+
const timerId = `source-fetch:${requestId}`;
|
|
1541
|
+
let stream = void 0;
|
|
1542
|
+
const refreshTimeout = () => {
|
|
1543
|
+
this.timerRefresh(timerId, () => {
|
|
1544
|
+
stream?.destroy(/* @__PURE__ */ new Error("communication timeout"));
|
|
1545
|
+
spool.unroll();
|
|
1546
|
+
});
|
|
1547
|
+
};
|
|
1548
|
+
spool.roll(() => {
|
|
1549
|
+
this.timerClear(timerId);
|
|
1550
|
+
});
|
|
1551
|
+
stream = new Readable({
|
|
1552
|
+
highWaterMark: chunkCredit > 0 ? chunkCredit * this.options.chunkSize : 16 * 1024,
|
|
1553
|
+
read: (_size) => {
|
|
1554
|
+
if (chunkCredit <= 0 || !this.onResponse.has(`source-fetch-response:${requestId}`)) return;
|
|
1555
|
+
const targetId = serverId ?? receiver;
|
|
1556
|
+
if (!targetId) return;
|
|
1557
|
+
const creditToGrant = Math.max(0, chunksReceived + chunkCredit - creditGranted);
|
|
1558
|
+
if (creditToGrant > 0) {
|
|
1559
|
+
creditGranted += creditToGrant;
|
|
1560
|
+
const creditMsg = this.msg.makeSourceFetchCredit(requestId, name, creditToGrant, this.options.id, targetId);
|
|
1561
|
+
const encoded = this.codec.encode(creditMsg);
|
|
1562
|
+
const creditTopic = this.options.topicMake(name, "source-fetch-request", targetId);
|
|
1563
|
+
this.publishToTopic(creditTopic, encoded, { qos: options.qos ?? 2 }).catch((err) => {
|
|
1564
|
+
this.error(err, `sending credit for fetch "${name}" failed`);
|
|
1565
|
+
});
|
|
1566
|
+
refreshTimeout();
|
|
1567
|
+
}
|
|
1568
|
+
}
|
|
1569
|
+
});
|
|
1570
|
+
const buffer = streamToBuffer(stream);
|
|
1571
|
+
let metaResolve;
|
|
1572
|
+
const metaP = new Promise((resolve) => {
|
|
1573
|
+
metaResolve = resolve;
|
|
1574
|
+
});
|
|
1575
|
+
spool.roll(() => {
|
|
1576
|
+
metaResolve(void 0);
|
|
1577
|
+
});
|
|
1578
|
+
refreshTimeout();
|
|
1579
|
+
let cancelled = false;
|
|
1580
|
+
const cancelAndUnroll = () => {
|
|
1581
|
+
if (!cancelled && !streamEnded) {
|
|
1582
|
+
cancelled = true;
|
|
1583
|
+
const targetId = serverId ?? receiver;
|
|
1584
|
+
if (targetId) {
|
|
1585
|
+
const cancelMsg = this.msg.makeSourceFetchCredit(requestId, name, 0, this.options.id, targetId);
|
|
1586
|
+
const encoded = this.codec.encode(cancelMsg);
|
|
1587
|
+
const cancelTopic = this.options.topicMake(name, "source-fetch-request", targetId);
|
|
1588
|
+
this.publishToTopic(cancelTopic, encoded, { qos: options.qos ?? 2 }).catch(() => {});
|
|
1589
|
+
}
|
|
1590
|
+
}
|
|
1591
|
+
spool.unroll();
|
|
1592
|
+
};
|
|
1593
|
+
stream.once("close", cancelAndUnroll);
|
|
1594
|
+
stream.once("error", cancelAndUnroll);
|
|
1595
|
+
this.onResponse.set(`source-fetch-response:${requestId}`, (response) => {
|
|
1596
|
+
if (streamEnded) return;
|
|
1597
|
+
if (response.name !== name) {
|
|
1598
|
+
streamEnded = true;
|
|
1599
|
+
stream.destroy(/* @__PURE__ */ new Error(`source name mismatch (expected "${name}", got "${response.name}")`));
|
|
1600
|
+
spool.unroll();
|
|
1601
|
+
return;
|
|
1602
|
+
}
|
|
1603
|
+
if (response.sender) serverId = response.sender;
|
|
1604
|
+
if (response.error) {
|
|
1605
|
+
streamEnded = true;
|
|
1606
|
+
stream.destroy(new Error(response.error));
|
|
1607
|
+
spool.unroll();
|
|
1608
|
+
} else {
|
|
1609
|
+
metaResolve(response.meta);
|
|
1610
|
+
refreshTimeout();
|
|
1611
|
+
}
|
|
1612
|
+
});
|
|
1613
|
+
this.onResponse.set(`source-fetch-chunk:${requestId}`, (response) => {
|
|
1614
|
+
if (streamEnded) return;
|
|
1615
|
+
if (response.error) {
|
|
1616
|
+
streamEnded = true;
|
|
1617
|
+
stream.destroy(new Error(response.error));
|
|
1618
|
+
spool.unroll();
|
|
1619
|
+
} else {
|
|
1620
|
+
refreshTimeout();
|
|
1621
|
+
if (response.chunk !== void 0) {
|
|
1622
|
+
chunksReceived++;
|
|
1623
|
+
stream.push(response.chunk);
|
|
1624
|
+
}
|
|
1625
|
+
if (response.final) {
|
|
1626
|
+
streamEnded = true;
|
|
1627
|
+
stream.push(null);
|
|
1628
|
+
spool.unroll();
|
|
1629
|
+
}
|
|
1630
|
+
}
|
|
1631
|
+
});
|
|
1632
|
+
spool.roll(() => {
|
|
1633
|
+
this.onResponse.delete(`source-fetch-response:${requestId}`);
|
|
1634
|
+
this.onResponse.delete(`source-fetch-chunk:${requestId}`);
|
|
1635
|
+
});
|
|
1636
|
+
const auth = this.authenticate();
|
|
1637
|
+
const metaStore = this.metaStore(meta);
|
|
1638
|
+
const credit = chunkCredit > 0 ? chunkCredit : void 0;
|
|
1639
|
+
const request = this.msg.makeSourceFetchRequest(requestId, name, params, this.options.id, receiver, auth, metaStore, credit);
|
|
1640
|
+
const message = this.codec.encode(request);
|
|
1641
|
+
const topic = this.options.topicMake(name, "source-fetch-request", receiver);
|
|
1642
|
+
run(`publish fetch request as MQTT message to topic "${topic}"`, () => this.publishToTopic(topic, message, {
|
|
1643
|
+
qos: 2,
|
|
1644
|
+
...options
|
|
1645
|
+
})).catch((err) => {
|
|
1646
|
+
stream.destroy(ensureError(err));
|
|
1647
|
+
});
|
|
1648
|
+
const result = {
|
|
1649
|
+
stream,
|
|
1650
|
+
buffer,
|
|
1651
|
+
meta: metaP
|
|
1652
|
+
};
|
|
1653
|
+
makeMutuallyExclusiveFields(result, "stream", "buffer");
|
|
1654
|
+
return result;
|
|
1655
|
+
}
|
|
1656
|
+
};
|
|
1657
|
+
//#endregion
|
|
1658
|
+
//#region dst-stage1/mqtt-plus-sink.js
|
|
1659
|
+
var SinkTrait = class extends SourceTrait {
|
|
1660
|
+
constructor() {
|
|
1661
|
+
super(...arguments);
|
|
1662
|
+
this.pushStreams = /* @__PURE__ */ new Map();
|
|
1663
|
+
this.pushSpools = /* @__PURE__ */ new Map();
|
|
1664
|
+
}
|
|
1665
|
+
async destroy() {
|
|
1666
|
+
for (const stream of this.pushStreams.values()) stream.destroy(/* @__PURE__ */ new Error("sink destroyed"));
|
|
1667
|
+
this.pushStreams.clear();
|
|
1668
|
+
for (const spool of this.pushSpools.values()) await spool.unroll();
|
|
1669
|
+
this.pushSpools.clear();
|
|
1670
|
+
await super.destroy();
|
|
1671
|
+
}
|
|
1672
|
+
async sink(nameOrConfig, ...args) {
|
|
1673
|
+
let name;
|
|
1674
|
+
let callback;
|
|
1675
|
+
let options = {};
|
|
1676
|
+
let share = this.options.share;
|
|
1677
|
+
let auth;
|
|
1678
|
+
if (typeof nameOrConfig === "object" && nameOrConfig !== null) {
|
|
1679
|
+
name = nameOrConfig.name;
|
|
1680
|
+
callback = nameOrConfig.callback;
|
|
1681
|
+
options = nameOrConfig.options ?? {};
|
|
1682
|
+
share = nameOrConfig.share ?? this.options.share;
|
|
1683
|
+
auth = nameOrConfig.auth;
|
|
1684
|
+
} else {
|
|
1685
|
+
name = nameOrConfig;
|
|
1686
|
+
callback = args[0];
|
|
1687
|
+
}
|
|
1688
|
+
const spool = new Spool();
|
|
1689
|
+
if (this.onRequest.has(`sink-push-request:${name}`)) throw new Error(`sink: sink "${name}" already registered`);
|
|
1690
|
+
const topicS = share !== "" ? `$share/${share}/${name}` : name;
|
|
1691
|
+
const topicReqB = this.options.topicMake(topicS, "sink-push-request");
|
|
1692
|
+
const topicReqD = this.options.topicMake(name, "sink-push-request", this.options.id);
|
|
1693
|
+
this.onRequest.set(`sink-push-request:${name}`, async (request, topicName) => {
|
|
1694
|
+
const requestId = request.id;
|
|
1695
|
+
const params = request.params ?? [];
|
|
1696
|
+
const sender = request.sender;
|
|
1697
|
+
if (sender === void 0 || sender === "") throw new Error("invalid request: missing sender");
|
|
1698
|
+
const receiver = request.receiver;
|
|
1699
|
+
const responseTopic = this.options.topicMake(name, "sink-push-response", sender);
|
|
1700
|
+
const chunkCredit = this.options.chunkCredit;
|
|
1701
|
+
const sendResponse = async (error, withCredit = false) => {
|
|
1702
|
+
const credit = error === void 0 && withCredit && chunkCredit > 0 ? chunkCredit : void 0;
|
|
1703
|
+
const response = this.msg.makeSinkPushResponse(requestId, name, error, this.options.id, sender, credit);
|
|
1704
|
+
const message = this.codec.encode(response);
|
|
1705
|
+
await this.publishToTopic(responseTopic, message, { qos: options.qos ?? 2 });
|
|
1706
|
+
};
|
|
1707
|
+
const reqSpool = new Spool();
|
|
1708
|
+
this.pushSpools.set(requestId, reqSpool);
|
|
1709
|
+
reqSpool.roll(() => {
|
|
1710
|
+
this.pushSpools.delete(requestId);
|
|
1711
|
+
});
|
|
1712
|
+
try {
|
|
1713
|
+
if (topicName !== request.name) throw new Error(`sink name mismatch (topic: "${topicName}", payload: "${request.name}")`);
|
|
1714
|
+
let authenticated = void 0;
|
|
1715
|
+
if (auth) authenticated = await this.authenticated(sender, request.auth, auth);
|
|
1716
|
+
if (authenticated !== void 0 && !authenticated) throw new Error(`sink "${name}" failed authentication`);
|
|
1717
|
+
const creditState = chunkCredit > 0 ? {
|
|
1718
|
+
chunksReceived: 0,
|
|
1719
|
+
creditGranted: chunkCredit
|
|
1720
|
+
} : void 0;
|
|
1721
|
+
const pushTimerId = `sink-push-recv:${requestId}`;
|
|
1722
|
+
const refreshPushTimeout = () => this.timerRefresh(pushTimerId, () => {
|
|
1723
|
+
if (streamEnded) return;
|
|
1724
|
+
const stream = this.pushStreams.get(requestId);
|
|
1725
|
+
if (stream !== void 0) stream.destroy(/* @__PURE__ */ new Error("push stream timeout"));
|
|
1726
|
+
this.pushSpools.get(requestId)?.unroll();
|
|
1727
|
+
});
|
|
1728
|
+
const clearPushTimeout = () => this.timerClear(pushTimerId);
|
|
1729
|
+
const readable = new Readable({
|
|
1730
|
+
highWaterMark: chunkCredit > 0 ? chunkCredit * this.options.chunkSize : 16 * 1024,
|
|
1731
|
+
read: (_size) => {
|
|
1732
|
+
if (!creditState || !this.pushSpools.has(requestId)) return;
|
|
1733
|
+
const creditToGrant = Math.max(0, creditState.chunksReceived + chunkCredit - creditState.creditGranted);
|
|
1734
|
+
if (creditToGrant > 0) {
|
|
1735
|
+
creditState.creditGranted += creditToGrant;
|
|
1736
|
+
const creditMsg = this.msg.makeSinkPushCredit(requestId, name, creditToGrant, this.options.id, sender);
|
|
1737
|
+
const encoded = this.codec.encode(creditMsg);
|
|
1738
|
+
this.publishToTopic(responseTopic, encoded, { qos: options.qos ?? 2 }).catch((err) => {
|
|
1739
|
+
this.error(err, `sending credit for push "${name}" failed`);
|
|
1740
|
+
});
|
|
1741
|
+
refreshPushTimeout();
|
|
1742
|
+
}
|
|
1743
|
+
}
|
|
1744
|
+
});
|
|
1745
|
+
this.pushStreams.set(requestId, readable);
|
|
1746
|
+
reqSpool.roll(() => {
|
|
1747
|
+
this.pushStreams.delete(requestId);
|
|
1748
|
+
});
|
|
1749
|
+
readable.once("error", () => {});
|
|
1750
|
+
let streamEnded = false;
|
|
1751
|
+
this.onResponse.set(`sink-push-chunk:${requestId}`, async (chunkParsed) => {
|
|
1752
|
+
if (streamEnded) return;
|
|
1753
|
+
if (chunkParsed.error !== void 0) {
|
|
1754
|
+
streamEnded = true;
|
|
1755
|
+
clearPushTimeout();
|
|
1756
|
+
readable.destroy(new Error(chunkParsed.error));
|
|
1757
|
+
} else {
|
|
1758
|
+
refreshPushTimeout();
|
|
1759
|
+
if (chunkParsed.chunk !== void 0) {
|
|
1760
|
+
if (creditState) creditState.chunksReceived++;
|
|
1761
|
+
readable.push(chunkParsed.chunk);
|
|
1762
|
+
}
|
|
1763
|
+
if (chunkParsed.final) {
|
|
1764
|
+
streamEnded = true;
|
|
1765
|
+
clearPushTimeout();
|
|
1766
|
+
readable.push(null);
|
|
1767
|
+
}
|
|
1768
|
+
}
|
|
1769
|
+
});
|
|
1770
|
+
reqSpool.roll(() => {
|
|
1771
|
+
this.onResponse.delete(`sink-push-chunk:${requestId}`);
|
|
1772
|
+
});
|
|
1773
|
+
refreshPushTimeout();
|
|
1774
|
+
reqSpool.roll(() => {
|
|
1775
|
+
clearPushTimeout();
|
|
1776
|
+
});
|
|
1777
|
+
const promise = streamToBuffer(readable);
|
|
1778
|
+
const streamDone = new Promise((resolve, reject) => {
|
|
1779
|
+
readable.once("end", () => {
|
|
1780
|
+
resolve();
|
|
1781
|
+
});
|
|
1782
|
+
readable.once("error", (err) => {
|
|
1783
|
+
reject(err);
|
|
1784
|
+
});
|
|
1785
|
+
});
|
|
1786
|
+
streamDone.catch(() => {});
|
|
1787
|
+
const info = {
|
|
1788
|
+
sender,
|
|
1789
|
+
stream: readable,
|
|
1790
|
+
buffer: promise
|
|
1791
|
+
};
|
|
1792
|
+
if (receiver) info.receiver = receiver;
|
|
1793
|
+
if (authenticated !== void 0) info.authenticated = authenticated;
|
|
1794
|
+
if (request.meta) info.meta = request.meta;
|
|
1795
|
+
makeMutuallyExclusiveFields(info, "stream", "buffer");
|
|
1796
|
+
await sendResponse(void 0, true);
|
|
1797
|
+
await callback(...params, info);
|
|
1798
|
+
if (readable.readableFlowing !== true && !readable.destroyed) readable.resume();
|
|
1799
|
+
await streamDone;
|
|
1800
|
+
try {
|
|
1801
|
+
await sendResponse();
|
|
1802
|
+
} catch (err2) {
|
|
1803
|
+
this.error(ensureError(err2), `sending terminal response for sink "${name}" failed`);
|
|
1804
|
+
}
|
|
1805
|
+
} catch (err) {
|
|
1806
|
+
const error = ensureError(err, `handler for sink "${name}" failed`);
|
|
1807
|
+
this.error(error);
|
|
1808
|
+
await sendResponse(error.message).catch(() => {});
|
|
1809
|
+
} finally {
|
|
1810
|
+
const stream = this.pushStreams.get(requestId);
|
|
1811
|
+
if (stream !== void 0 && !stream.destroyed) stream.destroy();
|
|
1812
|
+
await reqSpool.unroll();
|
|
1813
|
+
}
|
|
1814
|
+
});
|
|
1815
|
+
spool.roll(() => {
|
|
1816
|
+
this.onRequest.delete(`sink-push-request:${name}`);
|
|
1817
|
+
});
|
|
1818
|
+
await this.subscribeTopicAndSpool(spool, topicReqB, options);
|
|
1819
|
+
await this.subscribeTopicAndSpool(spool, topicReqD, options);
|
|
1820
|
+
return this.makeRegistration(spool, "sink", name, `sink-push-request:${name}`);
|
|
1821
|
+
}
|
|
1822
|
+
async push(nameOrConfig, ...args) {
|
|
1823
|
+
let name;
|
|
1824
|
+
let data;
|
|
1825
|
+
let params;
|
|
1826
|
+
let receiver;
|
|
1827
|
+
let options = {};
|
|
1828
|
+
let meta;
|
|
1829
|
+
if (typeof nameOrConfig === "object" && nameOrConfig !== null) {
|
|
1830
|
+
name = nameOrConfig.name;
|
|
1831
|
+
data = nameOrConfig.data;
|
|
1832
|
+
params = nameOrConfig.params;
|
|
1833
|
+
receiver = nameOrConfig.receiver;
|
|
1834
|
+
options = nameOrConfig.options ?? {};
|
|
1835
|
+
meta = nameOrConfig.meta;
|
|
1836
|
+
} else {
|
|
1837
|
+
name = nameOrConfig;
|
|
1838
|
+
data = args[0];
|
|
1839
|
+
params = args.slice(1);
|
|
1840
|
+
}
|
|
1841
|
+
if (!(data instanceof Readable) && !(data instanceof Uint8Array)) throw new Error("invalid data type: expected Readable or Uint8Array");
|
|
1842
|
+
const spool = new Spool();
|
|
1843
|
+
let requestId = nanoid();
|
|
1844
|
+
while (this.onResponse.has(`sink-push-response:${requestId}`) || this.onResponse.has(`sink-push-credit:${requestId}`)) requestId = nanoid();
|
|
1845
|
+
const responseTopic = this.options.topicMake(name, "sink-push-response", this.options.id);
|
|
1846
|
+
await this.subscribeTopicAndSpool(spool, responseTopic, { qos: options.qos ?? 2 });
|
|
1847
|
+
const abortController = new AbortController();
|
|
1848
|
+
const abortSignal = abortController.signal;
|
|
1849
|
+
if (data instanceof Readable) {
|
|
1850
|
+
const stream = data;
|
|
1851
|
+
abortSignal.addEventListener("abort", () => {
|
|
1852
|
+
if (!stream.destroyed) stream.destroy(ensureError(abortSignal.reason));
|
|
1853
|
+
}, { once: true });
|
|
1854
|
+
}
|
|
1855
|
+
const pushTimerId = `sink-push-send:${requestId}`;
|
|
1856
|
+
const refreshTimeout = () => this.timerRefresh(pushTimerId, () => {
|
|
1857
|
+
const error = /* @__PURE__ */ new Error(`push to sink "${name}" timed out`);
|
|
1858
|
+
abortController.abort(error);
|
|
1859
|
+
spool.unroll();
|
|
1860
|
+
});
|
|
1861
|
+
spool.roll(() => {
|
|
1862
|
+
this.timerClear(pushTimerId);
|
|
1863
|
+
});
|
|
1864
|
+
refreshTimeout();
|
|
1865
|
+
let initialCredit;
|
|
1866
|
+
let creditGate;
|
|
1867
|
+
let remoteError = false;
|
|
1868
|
+
let pushAcked = false;
|
|
1869
|
+
let pushFinalized = false;
|
|
1870
|
+
let pushDataFinalSent = false;
|
|
1871
|
+
let pushFinalizeResolve;
|
|
1872
|
+
let pushFinalizeReject;
|
|
1873
|
+
const pushFinalize = new Promise((resolve, reject) => {
|
|
1874
|
+
pushFinalizeResolve = resolve;
|
|
1875
|
+
pushFinalizeReject = reject;
|
|
1876
|
+
});
|
|
1877
|
+
pushFinalize.catch(() => {});
|
|
1878
|
+
try {
|
|
1879
|
+
await new Promise((resolve, reject) => {
|
|
1880
|
+
const onAbort = () => {
|
|
1881
|
+
reject(abortSignal.reason);
|
|
1882
|
+
};
|
|
1883
|
+
abortSignal.addEventListener("abort", onAbort, { once: true });
|
|
1884
|
+
spool.roll(() => {
|
|
1885
|
+
abortSignal.removeEventListener("abort", onAbort);
|
|
1886
|
+
});
|
|
1887
|
+
this.onResponse.set(`sink-push-response:${requestId}`, (response) => {
|
|
1888
|
+
if (response.error) reject(new Error(response.error));
|
|
1889
|
+
else {
|
|
1890
|
+
if (response.sender) receiver = response.sender;
|
|
1891
|
+
initialCredit = response.credit;
|
|
1892
|
+
pushAcked = true;
|
|
1893
|
+
resolve();
|
|
1894
|
+
}
|
|
1895
|
+
});
|
|
1896
|
+
spool.roll(() => {
|
|
1897
|
+
this.onResponse.delete(`sink-push-response:${requestId}`);
|
|
1898
|
+
});
|
|
1899
|
+
const auth = this.authenticate();
|
|
1900
|
+
const metaStore = this.metaStore(meta);
|
|
1901
|
+
const request = this.msg.makeSinkPushRequest(requestId, name, params, this.options.id, receiver, auth, metaStore);
|
|
1902
|
+
const message = this.codec.encode(request);
|
|
1903
|
+
const requestTopic = this.options.topicMake(name, "sink-push-request", receiver);
|
|
1904
|
+
run(`publish push request as MQTT message to topic "${requestTopic}"`, spool, () => this.publishToTopic(requestTopic, message, {
|
|
1905
|
+
qos: 2,
|
|
1906
|
+
...options
|
|
1907
|
+
})).catch((err) => {
|
|
1908
|
+
reject(err);
|
|
1909
|
+
});
|
|
1910
|
+
});
|
|
1911
|
+
this.onResponse.set(`sink-push-response:${requestId}`, (response) => {
|
|
1912
|
+
if (response.error) {
|
|
1913
|
+
remoteError = true;
|
|
1914
|
+
pushFinalizeReject(new Error(response.error));
|
|
1915
|
+
abortController.abort(new Error(response.error));
|
|
1916
|
+
} else if (pushAcked && !pushFinalized) {
|
|
1917
|
+
pushFinalized = true;
|
|
1918
|
+
pushFinalizeResolve();
|
|
1919
|
+
}
|
|
1920
|
+
});
|
|
1921
|
+
if (initialCredit !== void 0 && initialCredit > 0) creditGate = new CreditGate(initialCredit);
|
|
1922
|
+
if (creditGate) {
|
|
1923
|
+
const gate = creditGate;
|
|
1924
|
+
spool.roll(() => {
|
|
1925
|
+
gate.abort();
|
|
1926
|
+
});
|
|
1927
|
+
this.onResponse.set(`sink-push-credit:${requestId}`, (response) => {
|
|
1928
|
+
gate.replenish(response.credit);
|
|
1929
|
+
refreshTimeout();
|
|
1930
|
+
});
|
|
1931
|
+
spool.roll(() => {
|
|
1932
|
+
this.onResponse.delete(`sink-push-credit:${requestId}`);
|
|
1933
|
+
});
|
|
1934
|
+
}
|
|
1935
|
+
const chunkTopic = this.options.topicMake(name, "sink-push-request", receiver);
|
|
1936
|
+
const sendChunk = async (chunk, error, final) => {
|
|
1937
|
+
refreshTimeout();
|
|
1938
|
+
const chunkMsg = this.msg.makeSinkPushChunk(requestId, name, chunk, error, final, this.options.id, receiver);
|
|
1939
|
+
const message = this.codec.encode(chunkMsg);
|
|
1940
|
+
await this.publishToTopic(chunkTopic, message, {
|
|
1941
|
+
qos: 2,
|
|
1942
|
+
...options
|
|
1943
|
+
});
|
|
1944
|
+
if (error === void 0 && final) pushDataFinalSent = true;
|
|
1945
|
+
};
|
|
1946
|
+
if (data instanceof Readable) await sendStreamAsChunks(data, this.options.chunkSize, sendChunk, creditGate, abortSignal);
|
|
1947
|
+
else if (data instanceof Uint8Array) await sendBufferAsChunks(data, this.options.chunkSize, sendChunk, creditGate, abortSignal);
|
|
1948
|
+
if (!pushFinalized) await new Promise((resolve, reject) => {
|
|
1949
|
+
const onAbort = () => {
|
|
1950
|
+
reject(abortSignal.reason);
|
|
1951
|
+
};
|
|
1952
|
+
abortSignal.addEventListener("abort", onAbort, { once: true });
|
|
1953
|
+
pushFinalize.then(resolve, reject).finally(() => {
|
|
1954
|
+
abortSignal.removeEventListener("abort", onAbort);
|
|
1955
|
+
});
|
|
1956
|
+
});
|
|
1957
|
+
} catch (err) {
|
|
1958
|
+
const error = ensureError(err);
|
|
1959
|
+
abortController.abort(error);
|
|
1960
|
+
if (pushAcked && !remoteError && !pushDataFinalSent) {
|
|
1961
|
+
const chunkTopic = this.options.topicMake(name, "sink-push-request", receiver);
|
|
1962
|
+
const chunkMsg = this.msg.makeSinkPushChunk(requestId, name, void 0, error.message, true, this.options.id, receiver);
|
|
1963
|
+
const message = this.codec.encode(chunkMsg);
|
|
1964
|
+
await this.publishToTopic(chunkTopic, message, {
|
|
1965
|
+
qos: 2,
|
|
1966
|
+
...options
|
|
1967
|
+
}).catch(() => {});
|
|
1968
|
+
}
|
|
1969
|
+
throw err;
|
|
1970
|
+
} finally {
|
|
1971
|
+
await spool.unroll();
|
|
1972
|
+
}
|
|
1973
|
+
}
|
|
2176
1974
|
};
|
|
1975
|
+
//#endregion
|
|
1976
|
+
//#region dst-stage1/mqtt-plus.js
|
|
1977
|
+
var MQTTp = class extends SinkTrait {};
|
|
1978
|
+
//#endregion
|
|
1979
|
+
export { MQTTp as default };
|