@seriousme/opifex 1.1.0
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/.github/workflows/closeStale.yml +15 -0
- package/.github/workflows/codeql-analysis.yml +71 -0
- package/.github/workflows/deno-ci.yml +16 -0
- package/.github/workflows/nodejs-ci.yml +23 -0
- package/.github/workflows/publish.yml +16 -0
- package/.vscode/extensions.json +10 -0
- package/.vscode/settings.json +12 -0
- package/LICENSE.txt +21 -0
- package/README.md +34 -0
- package/bin/conformanceServer.ts +70 -0
- package/bin/demoServer.ts +64 -0
- package/bin/mqtt.ts +211 -0
- package/client/client.ts +181 -0
- package/client/context.ts +197 -0
- package/client/deps.ts +36 -0
- package/client/handlers/handleConnack.ts +21 -0
- package/client/handlers/handlePacket.ts +70 -0
- package/client/handlers/handlePuback.ts +10 -0
- package/client/handlers/handlePubcomp.ts +13 -0
- package/client/handlers/handlePublish.ts +32 -0
- package/client/handlers/handlePubrec.ts +24 -0
- package/client/handlers/handlePubrel.ts +26 -0
- package/client/handlers/handleSuback.ts +14 -0
- package/client/handlers/handleUnsuback.ts +11 -0
- package/client/mod.ts +11 -0
- package/client/store/deps.ts +22 -0
- package/client/store/memoryStore.ts +56 -0
- package/client/store/store.ts +29 -0
- package/deno/README.md +64 -0
- package/deno/client.ts +48 -0
- package/deno/server.test.ts +14 -0
- package/deno/server.ts +31 -0
- package/deno/wrapDenoConn.ts +12 -0
- package/deno.json +8 -0
- package/deno.lock +47 -0
- package/dev_utils/dummyConn.ts +95 -0
- package/dev_utils/mod.ts +1 -0
- package/mqttConn/mqttConn.test.ts +110 -0
- package/mqttConn/mqttConn.ts +134 -0
- package/mqttPacket/AuthenticationResult.ts +16 -0
- package/mqttPacket/BitMask.ts +10 -0
- package/mqttPacket/PacketType.ts +21 -0
- package/mqttPacket/connack.test.ts +129 -0
- package/mqttPacket/connack.ts +37 -0
- package/mqttPacket/connect.test.ts +297 -0
- package/mqttPacket/connect.ts +159 -0
- package/mqttPacket/decoder.test.ts +123 -0
- package/mqttPacket/decoder.ts +109 -0
- package/mqttPacket/disconnect.test.ts +50 -0
- package/mqttPacket/disconnect.ts +21 -0
- package/mqttPacket/encoder.test.ts +39 -0
- package/mqttPacket/encoder.ts +72 -0
- package/mqttPacket/length.test.ts +75 -0
- package/mqttPacket/length.ts +57 -0
- package/mqttPacket/mod.ts +151 -0
- package/mqttPacket/pingreq.test.ts +50 -0
- package/mqttPacket/pingreq.ts +22 -0
- package/mqttPacket/pingres.test.ts +50 -0
- package/mqttPacket/pingres.ts +21 -0
- package/mqttPacket/puback.test.ts +55 -0
- package/mqttPacket/puback.ts +28 -0
- package/mqttPacket/pubcomp.test.ts +55 -0
- package/mqttPacket/pubcomp.ts +28 -0
- package/mqttPacket/publish.test.ts +198 -0
- package/mqttPacket/publish.ts +73 -0
- package/mqttPacket/pubrec.test.ts +55 -0
- package/mqttPacket/pubrec.ts +28 -0
- package/mqttPacket/pubrel.test.ts +55 -0
- package/mqttPacket/pubrel.ts +28 -0
- package/mqttPacket/suback.test.ts +49 -0
- package/mqttPacket/suback.ts +41 -0
- package/mqttPacket/subscribe.test.ts +233 -0
- package/mqttPacket/subscribe.ts +69 -0
- package/mqttPacket/types.ts +27 -0
- package/mqttPacket/unsuback.test.ts +55 -0
- package/mqttPacket/unsuback.ts +28 -0
- package/mqttPacket/unsubscribe.test.ts +136 -0
- package/mqttPacket/unsubscribe.ts +48 -0
- package/mqttPacket/validators.ts +20 -0
- package/node/README.md +46 -0
- package/node/client.ts +58 -0
- package/node/server.test.ts +14 -0
- package/node/server.ts +34 -0
- package/node/wrapNodeSocket.ts +54 -0
- package/package.json +35 -0
- package/persistence/deps.ts +12 -0
- package/persistence/memory/memoryPersistence.test.ts +151 -0
- package/persistence/memory/memoryPersistence.ts +156 -0
- package/persistence/persistence.ts +24 -0
- package/persistence/store.ts +14 -0
- package/server/context.ts +157 -0
- package/server/deps.ts +37 -0
- package/server/handlers/handleConnect.ts +73 -0
- package/server/handlers/handleDisconnect.ts +6 -0
- package/server/handlers/handlePacket.ts +78 -0
- package/server/handlers/handlePingreq.ts +8 -0
- package/server/handlers/handlePuback.ts +12 -0
- package/server/handlers/handlePubcomp.ts +12 -0
- package/server/handlers/handlePublish.ts +47 -0
- package/server/handlers/handlePubrec.ts +20 -0
- package/server/handlers/handlePubrel.ts +24 -0
- package/server/handlers/handleSubscribe.ts +47 -0
- package/server/handlers/handleUnsubscribe.ts +17 -0
- package/server/mod.ts +16 -0
- package/server/server.ts +74 -0
- package/server/test/handleConnect.test.ts +126 -0
- package/server/test/test-handlers.ts +52 -0
- package/socket/socket.ts +59 -0
- package/timer/timer.test.ts +48 -0
- package/timer/timer.ts +45 -0
- package/trie/trie.test.ts +147 -0
- package/trie/trie.ts +106 -0
- package/utils/args.ts +86 -0
- package/utils/assert.ts +31 -0
- package/utils/asyncQueue.ts +63 -0
- package/utils/deferred.ts +12 -0
- package/utils/logger.ts +35 -0
- package/utils/mod.ts +7 -0
- package/utils/nextTick.ts +3 -0
- package/utils/platform.ts +4 -0
package/trie/trie.ts
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
type Parts = string[];
|
|
2
|
+
|
|
3
|
+
export class Trie<T> {
|
|
4
|
+
#value: Array<T>;
|
|
5
|
+
#children: Map<string, Trie<T>>;
|
|
6
|
+
private separator = "/";
|
|
7
|
+
private wildcardOne = "+";
|
|
8
|
+
private wildcardSubtree = "#";
|
|
9
|
+
private reservedPrefix = "$";
|
|
10
|
+
private looseCompare: boolean;
|
|
11
|
+
|
|
12
|
+
constructor(looseCompare = false) {
|
|
13
|
+
this.#value = [];
|
|
14
|
+
this.#children = new Map();
|
|
15
|
+
this.looseCompare = looseCompare;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
matchChild(child: string, parts: Parts): Array<T> {
|
|
19
|
+
const childNode = this.#children.get(child);
|
|
20
|
+
return childNode ? childNode._match(parts) : [];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
match(key: string) {
|
|
24
|
+
const parts = key.split(this.separator);
|
|
25
|
+
// toplevel wildcards are not allowed to match toplevel reserved topics
|
|
26
|
+
if (parts.length > 0 && parts[0].charAt(0) === this.reservedPrefix) {
|
|
27
|
+
return this._matchPrefix(parts);
|
|
28
|
+
}
|
|
29
|
+
return this._match(parts);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
private _matchPrefix(parts: Parts): Array<T> {
|
|
33
|
+
if (parts.length === 0) {
|
|
34
|
+
return this.#value ? this.#value : [];
|
|
35
|
+
}
|
|
36
|
+
const [first, ...rest] = parts;
|
|
37
|
+
return this.matchChild(first, rest);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
private _match(parts: Parts): Array<T> {
|
|
41
|
+
if (parts.length === 0) {
|
|
42
|
+
return this.#value ? this.#value : [];
|
|
43
|
+
}
|
|
44
|
+
const [first, ...rest] = parts;
|
|
45
|
+
const exact = this.matchChild(first, rest);
|
|
46
|
+
const single = this.matchChild(this.wildcardOne, rest);
|
|
47
|
+
const subtree = this.matchChild(this.wildcardSubtree, []);
|
|
48
|
+
const results = exact.concat(single, subtree);
|
|
49
|
+
return results;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
add(key: string, value: T) {
|
|
53
|
+
return this._add(key.split(this.separator), value);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
private _add(parts: Parts, value: T) {
|
|
57
|
+
if (parts.length === 0) {
|
|
58
|
+
this.#value = this.#value.concat(value);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
const [first, ...rest] = parts;
|
|
62
|
+
const child = this.#children.get(first);
|
|
63
|
+
if (child instanceof Trie) {
|
|
64
|
+
child._add(rest, value);
|
|
65
|
+
} else {
|
|
66
|
+
const node = new Trie<T>(this.looseCompare);
|
|
67
|
+
this.#children.set(first, node);
|
|
68
|
+
node._add(rest, value);
|
|
69
|
+
}
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
remove(key: string, value: T) {
|
|
74
|
+
return this._remove(key.split(this.separator), value);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
private _remove(parts: Parts, value: T): void {
|
|
78
|
+
if (parts.length === 0) {
|
|
79
|
+
const arr = this.#value || [];
|
|
80
|
+
this.#value = arr.filter(this.filter(value));
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
const [first, ...rest] = parts;
|
|
84
|
+
const node = this.#children.get(first);
|
|
85
|
+
if (node) {
|
|
86
|
+
node._remove(rest, value);
|
|
87
|
+
if (node.#value.length === 0 && this.#children.size === 0) {
|
|
88
|
+
this.#children.delete(first);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
private filter(value: T): (value: T, index: number, array: T[]) => boolean {
|
|
94
|
+
if (this.looseCompare && typeof value === "object") {
|
|
95
|
+
return (item) => {
|
|
96
|
+
for (const key in value) {
|
|
97
|
+
if (value[key] !== item[key]) {
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return false;
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
return (item) => item !== value;
|
|
105
|
+
}
|
|
106
|
+
}
|
package/utils/args.ts
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
// deno-lint-ignore no-explicit-any
|
|
2
|
+
export type Args = Record<string, any>;
|
|
3
|
+
export type ArgsValue = string | number | boolean;
|
|
4
|
+
|
|
5
|
+
type ArgOpts = {
|
|
6
|
+
string?: string[];
|
|
7
|
+
boolean?: string[];
|
|
8
|
+
alias?: Record<string, string>;
|
|
9
|
+
default?: Args;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
function formatValue(
|
|
13
|
+
key: string,
|
|
14
|
+
value: ArgsValue,
|
|
15
|
+
boolean: string[],
|
|
16
|
+
string: string[],
|
|
17
|
+
): string | number | boolean {
|
|
18
|
+
if (key === "__proto__" || key === "constructor") {
|
|
19
|
+
throw new Error(`Invalid argument: ${key}`);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (boolean.includes(key)) {
|
|
23
|
+
if (value !== "false") {
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
if (!string.includes(key)) {
|
|
29
|
+
const num = Number(value);
|
|
30
|
+
if (!Number.isNaN(num)) {
|
|
31
|
+
return num;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return value;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function parseKeyValue(
|
|
38
|
+
arg: string,
|
|
39
|
+
alias: Record<string, string>,
|
|
40
|
+
): [string, ArgsValue] {
|
|
41
|
+
if (arg.startsWith("--no-")) {
|
|
42
|
+
const key = arg.slice(5);
|
|
43
|
+
return [key, false];
|
|
44
|
+
}
|
|
45
|
+
if (arg.startsWith("--")) {
|
|
46
|
+
const [key, value] = arg.slice(2).split("=", 2);
|
|
47
|
+
return [key, value];
|
|
48
|
+
}
|
|
49
|
+
if (arg.startsWith("-")) {
|
|
50
|
+
const [flag, value] = arg.slice(1).split("=", 2);
|
|
51
|
+
const key = alias[flag] || flag;
|
|
52
|
+
return [key, value];
|
|
53
|
+
}
|
|
54
|
+
return ["_", arg];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function parseArgs(args: string[], opts: ArgOpts = {}): Args {
|
|
58
|
+
const { alias = {}, boolean = [], string = [], default: defaults = {} } =
|
|
59
|
+
opts;
|
|
60
|
+
const result = { ...defaults };
|
|
61
|
+
result["_"] = [];
|
|
62
|
+
|
|
63
|
+
for (let i = 0; i < args.length; i++) {
|
|
64
|
+
const [key, value] = parseKeyValue(args[i], alias);
|
|
65
|
+
if (key === "_") {
|
|
66
|
+
result["_"].push(formatValue(key, value, boolean, string));
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
if (value === undefined) {
|
|
70
|
+
const nextArg = args[i + 1];
|
|
71
|
+
if (
|
|
72
|
+
nextArg !== undefined &&
|
|
73
|
+
!nextArg.startsWith("-") &&
|
|
74
|
+
!boolean.includes(key)
|
|
75
|
+
) {
|
|
76
|
+
result[key] = formatValue(key, nextArg, boolean, string);
|
|
77
|
+
i++;
|
|
78
|
+
} else {
|
|
79
|
+
result[key] = formatValue(key, value, boolean, string);
|
|
80
|
+
}
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
result[key] = formatValue(key, value, boolean, string);
|
|
84
|
+
}
|
|
85
|
+
return result;
|
|
86
|
+
}
|
package/utils/assert.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export class AssertionError extends Error {
|
|
2
|
+
/** Constructs a new instance. */
|
|
3
|
+
constructor(message: string) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.name = "AssertionError";
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function assert(expr: unknown, msg = ""): asserts expr {
|
|
10
|
+
if (!expr) {
|
|
11
|
+
throw new AssertionError(msg);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** An array-like object (`Array`, `Uint8Array`, `NodeList`, etc.) that is not a string */
|
|
16
|
+
export type ArrayLikeArg<T> = ArrayLike<T> & object;
|
|
17
|
+
|
|
18
|
+
export function assertArrayIncludes<T>(
|
|
19
|
+
actual: ArrayLikeArg<T>,
|
|
20
|
+
expected: ArrayLikeArg<T>,
|
|
21
|
+
msg?: string,
|
|
22
|
+
) {
|
|
23
|
+
const actualArr = Array.from(actual);
|
|
24
|
+
const expectedArr = Array.from(expected);
|
|
25
|
+
for (const item of expectedArr) {
|
|
26
|
+
assert(
|
|
27
|
+
actualArr.includes(item),
|
|
28
|
+
msg + `\nExpected: ${expectedArr}\nActual: ${actualArr}`,
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { nextTick } from "./nextTick.ts";
|
|
2
|
+
|
|
3
|
+
export class AsyncQueue<T> {
|
|
4
|
+
private queue: T[] = [];
|
|
5
|
+
private maxQueueLength = Infinity;
|
|
6
|
+
private nextResolve = (_value: T) => {};
|
|
7
|
+
private nextReject = (_reason?: string) => {};
|
|
8
|
+
private done = false;
|
|
9
|
+
private hasNext = false;
|
|
10
|
+
|
|
11
|
+
constructor(maxQueueLength?: number) {
|
|
12
|
+
if (maxQueueLength) {
|
|
13
|
+
this.maxQueueLength = maxQueueLength;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async next(): Promise<T> {
|
|
18
|
+
await nextTick();
|
|
19
|
+
if (this.done && this.queue.length === 0) {
|
|
20
|
+
return Promise.reject("Closed");
|
|
21
|
+
}
|
|
22
|
+
return new Promise((resolve, reject) => {
|
|
23
|
+
if (this.queue.length > 0) {
|
|
24
|
+
const item = this.queue.shift();
|
|
25
|
+
if (item) {
|
|
26
|
+
return resolve(item);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
this.nextResolve = resolve;
|
|
30
|
+
this.nextReject = reject;
|
|
31
|
+
this.hasNext = true;
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
close(reason = "closed"): void {
|
|
36
|
+
this.done = true;
|
|
37
|
+
if (this.hasNext) {
|
|
38
|
+
this.nextReject(reason);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async *[Symbol.asyncIterator](): AsyncGenerator<Awaited<T>, void, unknown> {
|
|
43
|
+
while (true) {
|
|
44
|
+
yield this.next();
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
push(item: T) {
|
|
49
|
+
if (this.hasNext) {
|
|
50
|
+
this.nextResolve(item);
|
|
51
|
+
this.hasNext = false;
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
if (this.queue.length > this.maxQueueLength) {
|
|
55
|
+
this.queue.shift();
|
|
56
|
+
}
|
|
57
|
+
this.queue.push(item);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
get isDone(): boolean {
|
|
61
|
+
return this.done;
|
|
62
|
+
}
|
|
63
|
+
}
|
package/utils/logger.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export const LogLevel = {
|
|
2
|
+
error: 0,
|
|
3
|
+
warn: 1,
|
|
4
|
+
info: 2,
|
|
5
|
+
verbose: 3,
|
|
6
|
+
debug: 4,
|
|
7
|
+
} as const;
|
|
8
|
+
|
|
9
|
+
export type TLogLevel = typeof LogLevel[keyof typeof LogLevel];
|
|
10
|
+
|
|
11
|
+
export class Logger {
|
|
12
|
+
private defaultError = console.error;
|
|
13
|
+
private defaultWarn = console.warn;
|
|
14
|
+
private defaultInfo = console.info;
|
|
15
|
+
private defaultVerbose = console.log;
|
|
16
|
+
private defaultDebug = console.log;
|
|
17
|
+
// deno-lint-ignore no-explicit-any
|
|
18
|
+
private noop = (..._data: any[]) => {};
|
|
19
|
+
error = this.defaultError;
|
|
20
|
+
warn = this.defaultWarn;
|
|
21
|
+
info = this.defaultInfo;
|
|
22
|
+
verbose = this.noop;
|
|
23
|
+
debug = this.noop;
|
|
24
|
+
|
|
25
|
+
constructor() {}
|
|
26
|
+
|
|
27
|
+
level(logLevel: TLogLevel) {
|
|
28
|
+
this.warn = logLevel > 0 ? this.defaultWarn : this.noop;
|
|
29
|
+
this.info = logLevel > 1 ? this.defaultInfo : this.noop;
|
|
30
|
+
this.verbose = logLevel > 2 ? this.defaultVerbose : this.noop;
|
|
31
|
+
this.debug = logLevel > 3 ? this.defaultDebug : this.noop;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export const logger: Logger = new Logger();
|
package/utils/mod.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { assert } from "./assert.ts";
|
|
2
|
+
export { Deferred } from "./deferred.ts";
|
|
3
|
+
export { Logger, logger, LogLevel } from "./logger.ts";
|
|
4
|
+
export { AsyncQueue } from "./asyncQueue.ts";
|
|
5
|
+
export { nextTick } from "./nextTick.ts";
|
|
6
|
+
export { parseArgs } from "./args.ts";
|
|
7
|
+
export { getArgs } from "./platform.ts";
|