mqtt-pattern 1.1.3 → 2.0.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/.eslintrc +30 -30
- package/LICENSE +21 -21
- package/README.md +84 -78
- package/index.d.ts +58 -0
- package/index.js +121 -97
- package/package.json +35 -33
- package/parameters.d.ts +90 -0
- package/test.js +167 -157
- package/test.ts +35 -0
package/.eslintrc
CHANGED
|
@@ -1,30 +1,30 @@
|
|
|
1
|
-
{
|
|
2
|
-
"extends": [
|
|
3
|
-
"eslint:recommended",
|
|
4
|
-
"plugin:node/recommended"
|
|
5
|
-
],
|
|
6
|
-
"env": {},
|
|
7
|
-
"globals": {
|
|
8
|
-
"window": true
|
|
9
|
-
},
|
|
10
|
-
"plugins": [
|
|
11
|
-
"node"
|
|
12
|
-
],
|
|
13
|
-
"rules": {
|
|
14
|
-
"no-undef": 2,
|
|
15
|
-
"no-unused-vars": 2,
|
|
16
|
-
"strict": [2, "global"],
|
|
17
|
-
"new-cap": 0,
|
|
18
|
-
"indent": ["error", "tab", {"MemberExpression": 1, "ArrayExpression": 1, "ObjectExpression": 1 }],
|
|
19
|
-
"no-console": 0,
|
|
20
|
-
"no-invalid-this": 0,
|
|
21
|
-
"node/no-missing-require": 1,
|
|
22
|
-
"no-trailing-spaces": 2,
|
|
23
|
-
"semi": ["error", "always"],
|
|
24
|
-
"key-spacing": ["warn", { "beforeColon": false, "afterColon": true }],
|
|
25
|
-
"space-infix-ops": ["warn"],
|
|
26
|
-
"object-curly-newline": ["warn", {"minProperties": 1}],
|
|
27
|
-
"no-shadow": ["error"],
|
|
28
|
-
"no-undef-init": ["error"]
|
|
29
|
-
}
|
|
30
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"extends": [
|
|
3
|
+
"eslint:recommended",
|
|
4
|
+
"plugin:node/recommended"
|
|
5
|
+
],
|
|
6
|
+
"env": {},
|
|
7
|
+
"globals": {
|
|
8
|
+
"window": true
|
|
9
|
+
},
|
|
10
|
+
"plugins": [
|
|
11
|
+
"node"
|
|
12
|
+
],
|
|
13
|
+
"rules": {
|
|
14
|
+
"no-undef": 2,
|
|
15
|
+
"no-unused-vars": 2,
|
|
16
|
+
"strict": [2, "global"],
|
|
17
|
+
"new-cap": 0,
|
|
18
|
+
"indent": ["error", "tab", {"MemberExpression": 1, "ArrayExpression": 1, "ObjectExpression": 1 }],
|
|
19
|
+
"no-console": 0,
|
|
20
|
+
"no-invalid-this": 0,
|
|
21
|
+
"node/no-missing-require": 1,
|
|
22
|
+
"no-trailing-spaces": 2,
|
|
23
|
+
"semi": ["error", "always"],
|
|
24
|
+
"key-spacing": ["warn", { "beforeColon": false, "afterColon": true }],
|
|
25
|
+
"space-infix-ops": ["warn"],
|
|
26
|
+
"object-curly-newline": ["warn", {"minProperties": 1}],
|
|
27
|
+
"no-shadow": ["error"],
|
|
28
|
+
"no-undef-init": ["error"]
|
|
29
|
+
}
|
|
30
|
+
}
|
package/LICENSE
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2017
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2017
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,78 +1,84 @@
|
|
|
1
|
-
# mqtt-pattern
|
|
2
|
-
Fast library for matching MQTT patterns with named wildcards to extract data from topics
|
|
3
|
-
|
|
4
|
-
Successor to [mqtt-regex](./mqtt-regex)
|
|
5
|
-
|
|
6
|
-
## Example:
|
|
7
|
-
|
|
8
|
-
``` javascript
|
|
9
|
-
var MQTTPattern = require("mqtt-pattern");
|
|
10
|
-
|
|
11
|
-
// Wildcards in patterns don't need names
|
|
12
|
-
var pattern = "device/+id/+/#data";
|
|
13
|
-
|
|
14
|
-
var topic = "device/fitbit/heartrate/rate/bpm";
|
|
15
|
-
|
|
16
|
-
var params = MQTTPattern.exec(pattern, topic);
|
|
17
|
-
|
|
18
|
-
// params will be
|
|
19
|
-
{
|
|
20
|
-
id: "fitbit",
|
|
21
|
-
data: ["rate", "bmp"]
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
var filled = MQTTPattern.fill(pattern, params);
|
|
25
|
-
// filled will be
|
|
26
|
-
"device/fitbit/undefined/rate/bmp"
|
|
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
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
1
|
+
# mqtt-pattern
|
|
2
|
+
Fast library for matching MQTT patterns with named wildcards to extract data from topics
|
|
3
|
+
|
|
4
|
+
Successor to [mqtt-regex](./mqtt-regex)
|
|
5
|
+
|
|
6
|
+
## Example:
|
|
7
|
+
|
|
8
|
+
``` javascript
|
|
9
|
+
var MQTTPattern = require("mqtt-pattern");
|
|
10
|
+
|
|
11
|
+
// Wildcards in patterns don't need names
|
|
12
|
+
var pattern = "device/+id/+/#data";
|
|
13
|
+
|
|
14
|
+
var topic = "device/fitbit/heartrate/rate/bpm";
|
|
15
|
+
|
|
16
|
+
var params = MQTTPattern.exec(pattern, topic);
|
|
17
|
+
|
|
18
|
+
// params will be
|
|
19
|
+
{
|
|
20
|
+
id: "fitbit",
|
|
21
|
+
data: ["rate", "bmp"]
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
var filled = MQTTPattern.fill(pattern, params);
|
|
25
|
+
// filled will be
|
|
26
|
+
"device/fitbit/undefined/rate/bmp"
|
|
27
|
+
|
|
28
|
+
MQTTPattern.clean("hello/+param1/world/#param2");
|
|
29
|
+
// hello/+/world/#
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Installing
|
|
34
|
+
|
|
35
|
+
With NPM:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
npm install --save mqtt-pattern
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## API
|
|
42
|
+
|
|
43
|
+
### `exec(pattern : String, topic : String) : Object | null`
|
|
44
|
+
Validates that `topic` fits the `pattern` and parses out any parameters.
|
|
45
|
+
If the topic doesn't match, it returns `null`
|
|
46
|
+
|
|
47
|
+
### `matches(pattern : String, topic : String) : Boolean`
|
|
48
|
+
Validates whether `topic` fits the `pattern`. Ignores parameters.
|
|
49
|
+
|
|
50
|
+
### `extract(pattern : String, topic : String) : Object`
|
|
51
|
+
Traverses the `pattern` and attempts to fetch parameters from the `topic`.
|
|
52
|
+
Useful if you know in advance that your `topic` will be valid and want to extract data.
|
|
53
|
+
If the `topic` doesn't match, or the `pattern` doesn't contain named wildcards, returns an empty object.
|
|
54
|
+
Do not use this for validation.
|
|
55
|
+
|
|
56
|
+
### `fill(pattern : String, params: Object) : String`
|
|
57
|
+
Reverse of `extract`, traverse the `pattern` and fill in params with keys in an object. Missing keys for `+` params are set to `undefined`. Missing keys for `#` params yeid empty strings.
|
|
58
|
+
|
|
59
|
+
### `clean(pattern : String) : String`
|
|
60
|
+
Removes the parameter names from a pattern.
|
|
61
|
+
|
|
62
|
+
## How params work
|
|
63
|
+
|
|
64
|
+
MQTT defines two types of "wildcards", one for matching a single section of the path (`+`), and one for zero or more sections of the path (`#`).
|
|
65
|
+
Note that the `#` wildcard must only be used if it's at the end of the topic.
|
|
66
|
+
This library was inspired by the syntax in the routers for web frameworks.
|
|
67
|
+
|
|
68
|
+
### Examples of topic patterns:
|
|
69
|
+
|
|
70
|
+
#### user/+id/#path
|
|
71
|
+
This would match paths that start with `user/`, and then extract the next section as the user `id`.
|
|
72
|
+
Then it would get the following paths and turn them into an array for the `path` param.
|
|
73
|
+
Here is some input/output that you can expect:
|
|
74
|
+
|
|
75
|
+
user/bob/status/mood: {id: "bob", path:["status","mood"]
|
|
76
|
+
user/bob: {id:"bob", path: []}
|
|
77
|
+
user/bob/ishungry: {id: "bob", path: ["ishungry"]
|
|
78
|
+
|
|
79
|
+
#### device/+/+/component/+type/#path
|
|
80
|
+
Not all wildcards need to be associated with a parameter, and it could be useful to use plain MQTT topics.
|
|
81
|
+
In this example you might only care about the status of some part of a device, and are willing to ignore a part of the path.
|
|
82
|
+
Here are some examples of what this might be used with:
|
|
83
|
+
|
|
84
|
+
device/deviceversion/deviceidhere/component/infrared/status/active: {type:"infrared",path: ["status","active"]}
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { MqttParameters, FillTopic, CleanTopic } from "./parameters";
|
|
2
|
+
import type { F } from "ts-toolbelt";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Extract parameters from a topic based on the pattern provided
|
|
6
|
+
* @param pattern Pattern to match against
|
|
7
|
+
* @param topic Topic to extract parameters from
|
|
8
|
+
*/
|
|
9
|
+
export declare function exec<Pattern extends string>(
|
|
10
|
+
pattern: Pattern,
|
|
11
|
+
topic: string
|
|
12
|
+
): MqttParameters<Pattern> | null;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Tests if a topic is valid for a certain pattern.
|
|
16
|
+
* @param pattern Pattern to match against
|
|
17
|
+
* @param topic Topic to test
|
|
18
|
+
*/
|
|
19
|
+
export declare function matches(pattern: string, topic: string): boolean;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Take a pattern and fill the parameters with the appropriate values
|
|
23
|
+
* @param pattern Pattern to fill
|
|
24
|
+
* @param params Parameters to insert into the pattern
|
|
25
|
+
*/
|
|
26
|
+
export declare function fill<
|
|
27
|
+
Pattern extends string,
|
|
28
|
+
Params extends MqttParameters<Pattern>
|
|
29
|
+
>(
|
|
30
|
+
pattern: F.Narrow<Pattern>,
|
|
31
|
+
params: F.Narrow<Params>
|
|
32
|
+
): FillTopic<Pattern, Params>;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Extract parameter values from a topic using a pattern.
|
|
36
|
+
*
|
|
37
|
+
* This function doesn't check if the pattern matches before attempting
|
|
38
|
+
* extraction, @see exec if you want to extract values, with assurances that the
|
|
39
|
+
* topic actually matches the pattern..
|
|
40
|
+
*
|
|
41
|
+
* @deprecated use exec for safety on topic matching the pattern
|
|
42
|
+
* @param pattern Pattern to extract using
|
|
43
|
+
* @param topic Topic to extract values from
|
|
44
|
+
*/
|
|
45
|
+
export declare function extract<Pattern extends string>(
|
|
46
|
+
pattern: Pattern,
|
|
47
|
+
topic: string
|
|
48
|
+
): MqttParameters<Pattern>;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Clean a pattern (remove any property names from wildcard segments)
|
|
52
|
+
*
|
|
53
|
+
* @param pattern Pattern to clean
|
|
54
|
+
* @returns cleaned pattern safe for usage in subscriptions etc.
|
|
55
|
+
*/
|
|
56
|
+
export declare function clean<Pattern extends string>(
|
|
57
|
+
pattern: Pattern
|
|
58
|
+
): CleanTopic<Pattern>;
|
package/index.js
CHANGED
|
@@ -1,97 +1,121 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var SEPARATOR = "/";
|
|
3
|
-
var SINGLE = "+";
|
|
4
|
-
var ALL = "#";
|
|
5
|
-
|
|
6
|
-
module.exports = {
|
|
7
|
-
matches: matches,
|
|
8
|
-
extract: extract,
|
|
9
|
-
exec: exec,
|
|
10
|
-
fill: fill,
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
var
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
var
|
|
23
|
-
var
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
var
|
|
28
|
-
var
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
var
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
var
|
|
54
|
-
var
|
|
55
|
-
var
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
break
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
var
|
|
77
|
-
var
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
var
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
var SEPARATOR = "/";
|
|
3
|
+
var SINGLE = "+";
|
|
4
|
+
var ALL = "#";
|
|
5
|
+
|
|
6
|
+
module.exports = {
|
|
7
|
+
matches: matches,
|
|
8
|
+
extract: extract,
|
|
9
|
+
exec: exec,
|
|
10
|
+
fill: fill,
|
|
11
|
+
clean: clean
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
function exec(pattern, topic) {
|
|
15
|
+
return matches(pattern, topic) ? extract(pattern, topic) : null;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function matches(pattern, topic) {
|
|
19
|
+
var patternSegments = pattern.split(SEPARATOR);
|
|
20
|
+
var topicSegments = topic.split(SEPARATOR);
|
|
21
|
+
|
|
22
|
+
var patternLength = patternSegments.length;
|
|
23
|
+
var topicLength = topicSegments.length;
|
|
24
|
+
var lastIndex = patternLength - 1;
|
|
25
|
+
|
|
26
|
+
for(var i = 0; i < patternLength; i++){
|
|
27
|
+
var currentPattern = patternSegments[i];
|
|
28
|
+
var patternChar = currentPattern[0];
|
|
29
|
+
var currentTopic = topicSegments[i];
|
|
30
|
+
|
|
31
|
+
if(!currentTopic && !currentPattern)
|
|
32
|
+
continue;
|
|
33
|
+
|
|
34
|
+
if(!currentTopic && currentPattern !== ALL) return false;
|
|
35
|
+
|
|
36
|
+
// Only allow # at end
|
|
37
|
+
if(patternChar === ALL)
|
|
38
|
+
return i === lastIndex;
|
|
39
|
+
if(patternChar !== SINGLE && currentPattern !== currentTopic)
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return patternLength === topicLength;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function fill(pattern, params){
|
|
47
|
+
var patternSegments = pattern.split(SEPARATOR);
|
|
48
|
+
var patternLength = patternSegments.length;
|
|
49
|
+
|
|
50
|
+
var result = [];
|
|
51
|
+
|
|
52
|
+
for (var i = 0; i < patternLength; i++) {
|
|
53
|
+
var currentPattern = patternSegments[i];
|
|
54
|
+
var patternChar = currentPattern[0];
|
|
55
|
+
var patternParam = currentPattern.slice(1);
|
|
56
|
+
var paramValue = params[patternParam];
|
|
57
|
+
|
|
58
|
+
if(patternChar === ALL){
|
|
59
|
+
// Check that it isn't undefined
|
|
60
|
+
if(paramValue !== void 0)
|
|
61
|
+
result.push([].concat(paramValue).join(SEPARATOR)); // Ensure it's an array
|
|
62
|
+
|
|
63
|
+
// Since # wildcards are always at the end, break out of the loop
|
|
64
|
+
break;
|
|
65
|
+
} else if (patternChar === SINGLE)
|
|
66
|
+
// Coerce param into a string, missing params will be undefined
|
|
67
|
+
result.push("" + paramValue);
|
|
68
|
+
else result.push(currentPattern);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return result.join(SEPARATOR);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
function extract(pattern, topic) {
|
|
76
|
+
var params = {};
|
|
77
|
+
var patternSegments = pattern.split(SEPARATOR);
|
|
78
|
+
var topicSegments = topic.split(SEPARATOR);
|
|
79
|
+
|
|
80
|
+
var patternLength = patternSegments.length;
|
|
81
|
+
|
|
82
|
+
for(var i = 0; i < patternLength; i++){
|
|
83
|
+
var currentPattern = patternSegments[i];
|
|
84
|
+
var patternChar = currentPattern[0];
|
|
85
|
+
|
|
86
|
+
if(currentPattern.length === 1)
|
|
87
|
+
continue;
|
|
88
|
+
|
|
89
|
+
if(patternChar === ALL){
|
|
90
|
+
params[currentPattern.slice(1)] = topicSegments.slice(i);
|
|
91
|
+
break;
|
|
92
|
+
} else if(patternChar === SINGLE){
|
|
93
|
+
params[currentPattern.slice(1)] = topicSegments[i];
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return params;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
function clean(pattern) {
|
|
102
|
+
var patternSegments = pattern.split(SEPARATOR);
|
|
103
|
+
var patternLength = patternSegments.length;
|
|
104
|
+
|
|
105
|
+
var cleanedSegments = [];
|
|
106
|
+
|
|
107
|
+
for(var i = 0; i < patternLength; i++){
|
|
108
|
+
var currentPattern = patternSegments[i];
|
|
109
|
+
var patternChar = currentPattern[0];
|
|
110
|
+
|
|
111
|
+
if(patternChar === ALL){
|
|
112
|
+
cleanedSegments.push(ALL);
|
|
113
|
+
} else if(patternChar === SINGLE){
|
|
114
|
+
cleanedSegments.push(SINGLE);
|
|
115
|
+
} else {
|
|
116
|
+
cleanedSegments.push(currentPattern);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return cleanedSegments.join('/');
|
|
121
|
+
}
|
package/package.json
CHANGED
|
@@ -1,33 +1,35 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "mqtt-pattern",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "Fast library for matching MQTT patterns with named wildcards",
|
|
5
|
-
"main": "index.js",
|
|
6
|
-
"
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
"
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
"
|
|
26
|
-
|
|
27
|
-
"eslint
|
|
28
|
-
"
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "mqtt-pattern",
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "Fast library for matching MQTT patterns with named wildcards",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"types": "index.d.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"test": "node test"
|
|
9
|
+
},
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "git+https://github.com/RangerMauve/mqtt-pattern.git"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"mqtt",
|
|
16
|
+
"pattern",
|
|
17
|
+
"match",
|
|
18
|
+
"topic"
|
|
19
|
+
],
|
|
20
|
+
"author": "rangermauve",
|
|
21
|
+
"license": "MIT",
|
|
22
|
+
"bugs": {
|
|
23
|
+
"url": "https://github.com/RangerMauve/mqtt-pattern/issues"
|
|
24
|
+
},
|
|
25
|
+
"homepage": "https://github.com/RangerMauve/mqtt-pattern#readme",
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"eslint": "^3.19.0",
|
|
28
|
+
"eslint-plugin-node": "^4.2.2",
|
|
29
|
+
"tape": "^4.6.3",
|
|
30
|
+
"ts-toolbelt": "^9.6.0"
|
|
31
|
+
},
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"mqtt-match": "^1.0.2"
|
|
34
|
+
}
|
|
35
|
+
}
|
package/parameters.d.ts
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/** This code is copied from the typings in @erichardson-lee/mqtt-router */
|
|
2
|
+
|
|
3
|
+
//#region Topic Parameter name & type inference
|
|
4
|
+
|
|
5
|
+
/** Check if a string is a parameter in a type */
|
|
6
|
+
type IsParameter<Parameter> = Parameter extends `+${infer ParamName}`
|
|
7
|
+
? ParamName
|
|
8
|
+
: never | Parameter extends `#${string}`
|
|
9
|
+
? Parameter
|
|
10
|
+
: never;
|
|
11
|
+
|
|
12
|
+
/** Type To split by / and extract parameters */
|
|
13
|
+
type FilteredTopicSplit<Topic> = Topic extends `${infer PartA}/${infer PartB}`
|
|
14
|
+
? IsParameter<PartA> | FilteredTopicSplit<PartB>
|
|
15
|
+
: IsParameter<Topic>;
|
|
16
|
+
|
|
17
|
+
/** Type to get Parameter Value */
|
|
18
|
+
type ParameterValue<Parameter> = Parameter extends `#${string}`
|
|
19
|
+
? string[]
|
|
20
|
+
: string;
|
|
21
|
+
|
|
22
|
+
/** Type to remove # prefix from parameter */
|
|
23
|
+
type StripParameterHash<Parameter> = Parameter extends `#${infer Name}`
|
|
24
|
+
? Name
|
|
25
|
+
: Parameter;
|
|
26
|
+
|
|
27
|
+
/** Parameter Type */
|
|
28
|
+
export type MqttParameters<Topic> = {
|
|
29
|
+
[key in FilteredTopicSplit<Topic> as StripParameterHash<key>]: ParameterValue<key>;
|
|
30
|
+
};
|
|
31
|
+
//#endregion
|
|
32
|
+
|
|
33
|
+
//#region Topic Cleaning
|
|
34
|
+
|
|
35
|
+
/** Clean a segment of a Topic */
|
|
36
|
+
type CleanTopicSegment<Segment> = Segment extends `+${string}`
|
|
37
|
+
? "+"
|
|
38
|
+
: Segment extends `#${string}`
|
|
39
|
+
? "#"
|
|
40
|
+
: Segment;
|
|
41
|
+
|
|
42
|
+
/** Clean a Topic (replace #value or +value with # or + respectively) */
|
|
43
|
+
export type CleanTopic<Topic> = Topic extends `${infer PartA}/${infer PartB}`
|
|
44
|
+
? `${CleanTopicSegment<PartA>}/${CleanTopic<PartB>}`
|
|
45
|
+
: CleanTopicSegment<Topic>;
|
|
46
|
+
|
|
47
|
+
//#endregion
|
|
48
|
+
|
|
49
|
+
//#region Topic Filling
|
|
50
|
+
type JoinPath<Parts extends string[]> = Parts extends [infer v, ...infer rest]
|
|
51
|
+
? rest extends []
|
|
52
|
+
? v
|
|
53
|
+
: v extends string
|
|
54
|
+
? rest extends string[]
|
|
55
|
+
? `${v}/${JoinPath<rest>}`
|
|
56
|
+
: ""
|
|
57
|
+
: ""
|
|
58
|
+
: "";
|
|
59
|
+
|
|
60
|
+
type FillParam<
|
|
61
|
+
Section extends string,
|
|
62
|
+
Parameters extends MqttParameters<FullPattern>,
|
|
63
|
+
FullPattern extends string = Section
|
|
64
|
+
> = Section extends `+${infer PName}`
|
|
65
|
+
? PName extends keyof Parameters
|
|
66
|
+
? Parameters[PName] extends string
|
|
67
|
+
? Parameters[PName]
|
|
68
|
+
: unknown
|
|
69
|
+
: unknown
|
|
70
|
+
: Section extends `#${infer PName}`
|
|
71
|
+
? PName extends keyof Parameters
|
|
72
|
+
? Parameters[PName] extends string[]
|
|
73
|
+
? JoinPath<Parameters[PName]>
|
|
74
|
+
: unknown
|
|
75
|
+
: unknown
|
|
76
|
+
: Section;
|
|
77
|
+
|
|
78
|
+
export type FillTopic<
|
|
79
|
+
Pattern extends string,
|
|
80
|
+
Parameters extends MqttParameters<FullPattern>,
|
|
81
|
+
FullPattern extends string = Pattern
|
|
82
|
+
> = Pattern extends `${infer Left}/${infer Right}`
|
|
83
|
+
? `${FillParam<Left, Parameters, FullPattern>}/${FillTopic<
|
|
84
|
+
Right,
|
|
85
|
+
Parameters,
|
|
86
|
+
FullPattern
|
|
87
|
+
>}`
|
|
88
|
+
: FillParam<Pattern, Parameters, FullPattern>;
|
|
89
|
+
|
|
90
|
+
//#endregion
|
package/test.js
CHANGED
|
@@ -1,157 +1,167 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
var test = require("tape");
|
|
4
|
-
|
|
5
|
-
var MQTTPattern = require("./");
|
|
6
|
-
|
|
7
|
-
test("matches() supports patterns with no wildcards", function (t) {
|
|
8
|
-
t.plan(1);
|
|
9
|
-
t.ok(MQTTPattern.matches("foo/bar/baz", "foo/bar/baz"), "Matched topic");
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
test("matches() doesn't match different topics", function (t) {
|
|
13
|
-
t.plan(1);
|
|
14
|
-
t.notOk(MQTTPattern.matches("foo/bar/baz", "baz/bar/foo"), "Didn't match topic");
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
test("matches() supports patterns with # at the beginning", function (t) {
|
|
18
|
-
t.plan(1);
|
|
19
|
-
t.ok(MQTTPattern.matches("#", "foo/bar/baz"), "Matched topic");
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
test("matches() supports patterns with # at the end", function (t) {
|
|
23
|
-
t.plan(1);
|
|
24
|
-
t.ok(MQTTPattern.matches("foo/#", "foo/bar/baz"), "Matched topic");
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
test("matches() supports patterns with # at the end and topic has no children", function (t) {
|
|
28
|
-
t.plan(1);
|
|
29
|
-
t.ok(MQTTPattern.matches("foo/bar/#", "foo/bar"), "Matched childless topic");
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
test("matches() doesn't support # wildcards with more after them", function (t) {
|
|
33
|
-
t.plan(1);
|
|
34
|
-
t.notOk(MQTTPattern.matches("#/bar/baz", "foo/bar/baz"), "Didn't match topic");
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
test("matches() supports patterns with + at the beginning", function (t) {
|
|
38
|
-
t.plan(1);
|
|
39
|
-
t.ok(MQTTPattern.matches("+/bar/baz", "foo/bar/baz"), "Matched topic");
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
test("matches() supports patterns with + at the end", function (t) {
|
|
43
|
-
t.plan(1);
|
|
44
|
-
t.ok(MQTTPattern.matches("foo/bar/+", "foo/bar/baz"), "Matched topic");
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
test("matches() supports patterns with + in the middle", function (t) {
|
|
48
|
-
t.plan(1);
|
|
49
|
-
t.ok(MQTTPattern.matches("foo/+/baz", "foo/bar/baz"), "Matched topic");
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
test("matches() supports patterns multiple wildcards", function (t) {
|
|
53
|
-
t.plan(1);
|
|
54
|
-
t.ok(MQTTPattern.matches("foo/+/#", "foo/bar/baz"), "Matched topic");
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
test("matches() supports named wildcards", function (t) {
|
|
58
|
-
t.plan(1);
|
|
59
|
-
t.ok(MQTTPattern.matches("foo/+something/#else", "foo/bar/baz"), "Matched topic");
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
test("matches() supports leading slashes", function (t){
|
|
63
|
-
t.plan(2);
|
|
64
|
-
t.ok(MQTTPattern.matches("/foo/bar", "/foo/bar"), "Matched topic");
|
|
65
|
-
t.notok(MQTTPattern.matches("/foo/bar", "/bar/foo"), "Didn't match invalid topic");
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
test("extract() returns empty object of there's nothing to extract", function (t) {
|
|
69
|
-
t.plan(1);
|
|
70
|
-
t.deepEqual(MQTTPattern.extract("foo/bar/baz", "foo/bar/baz"), {}, "Extracted empty object");
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
test("extract() returns empty object if wildcards don't have label", function (t) {
|
|
74
|
-
t.plan(1);
|
|
75
|
-
t.deepEqual(MQTTPattern.extract("foo/+/#", "foo/bar/baz"), {}, "Extracted empty object");
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
test("extract() returns object with an array for # wildcard", function (t) {
|
|
79
|
-
t.plan(1);
|
|
80
|
-
t.deepEqual(MQTTPattern.extract("foo/#something", "foo/bar/baz"), {
|
|
81
|
-
something: ["bar", "baz"]
|
|
82
|
-
}, "Extracted param");
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
test("extract() returns object with a string for + wildcard", function (t) {
|
|
86
|
-
t.plan(1);
|
|
87
|
-
t.deepEqual(MQTTPattern.extract("foo/+hello/+world", "foo/bar/baz"), {
|
|
88
|
-
hello: "bar",
|
|
89
|
-
world: "baz"
|
|
90
|
-
}, "Extracted params");
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
test("extract() parses params from all wildcards", function (t) {
|
|
94
|
-
t.plan(1);
|
|
95
|
-
t.deepEqual(MQTTPattern.extract("+hello/+world/#wow", "foo/bar/baz/fizz"), {
|
|
96
|
-
hello: "foo",
|
|
97
|
-
world: "bar",
|
|
98
|
-
wow: ["baz", "fizz"]
|
|
99
|
-
}, "Extracted params");
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
test("exec() returns null if it doesn't match", function (t) {
|
|
103
|
-
t.plan(1);
|
|
104
|
-
t.equal(MQTTPattern.exec("hello/world", "foo/bar/baz"), null, "Got null");
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
test("exec() returns params if they can be parsed", function(t){
|
|
108
|
-
t.plan(1);
|
|
109
|
-
t.deepEqual(MQTTPattern.exec("foo/+hello/#world", "foo/bar/baz"), {
|
|
110
|
-
hello: "bar",
|
|
111
|
-
world: ["baz"]
|
|
112
|
-
}, "Extracted params");
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
test("fill() fills in pattern with both types of wildcards", function(t){
|
|
116
|
-
t.plan(1);
|
|
117
|
-
t.deepEqual(MQTTPattern.fill("foo/+hello/#world", {
|
|
118
|
-
hello: "Hello",
|
|
119
|
-
world: ["the", "world", "wow"],
|
|
120
|
-
}), "foo/Hello/the/world/wow", "Filled in params");
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
test("fill() fills missing + params with undefined", function(t){
|
|
124
|
-
t.plan(1);
|
|
125
|
-
t.deepEqual(
|
|
126
|
-
MQTTPattern.fill("foo/+hello", {}),
|
|
127
|
-
"foo/undefined",
|
|
128
|
-
"Filled in params"
|
|
129
|
-
);
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
test("fill() ignores empty # params", function(t){
|
|
133
|
-
t.plan(1);
|
|
134
|
-
t.deepEqual(
|
|
135
|
-
MQTTPattern.fill("foo/#hello", {}),
|
|
136
|
-
"foo",
|
|
137
|
-
"Filled in params"
|
|
138
|
-
);
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
test("fill() ignores non-named # params", function (t) {
|
|
142
|
-
t.plan(1);
|
|
143
|
-
t.deepEqual(
|
|
144
|
-
MQTTPattern.fill("foo/#", {}),
|
|
145
|
-
"foo",
|
|
146
|
-
"Filled in params"
|
|
147
|
-
);
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
test("fill() uses `undefined` for non-named + params", function(t){
|
|
151
|
-
t.plan(1);
|
|
152
|
-
t.deepEqual(
|
|
153
|
-
MQTTPattern.fill("foo/+", {}),
|
|
154
|
-
"foo/undefined",
|
|
155
|
-
"Filled in params"
|
|
156
|
-
);
|
|
157
|
-
});
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var test = require("tape");
|
|
4
|
+
|
|
5
|
+
var MQTTPattern = require("./");
|
|
6
|
+
|
|
7
|
+
test("matches() supports patterns with no wildcards", function (t) {
|
|
8
|
+
t.plan(1);
|
|
9
|
+
t.ok(MQTTPattern.matches("foo/bar/baz", "foo/bar/baz"), "Matched topic");
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
test("matches() doesn't match different topics", function (t) {
|
|
13
|
+
t.plan(1);
|
|
14
|
+
t.notOk(MQTTPattern.matches("foo/bar/baz", "baz/bar/foo"), "Didn't match topic");
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
test("matches() supports patterns with # at the beginning", function (t) {
|
|
18
|
+
t.plan(1);
|
|
19
|
+
t.ok(MQTTPattern.matches("#", "foo/bar/baz"), "Matched topic");
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
test("matches() supports patterns with # at the end", function (t) {
|
|
23
|
+
t.plan(1);
|
|
24
|
+
t.ok(MQTTPattern.matches("foo/#", "foo/bar/baz"), "Matched topic");
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test("matches() supports patterns with # at the end and topic has no children", function (t) {
|
|
28
|
+
t.plan(1);
|
|
29
|
+
t.ok(MQTTPattern.matches("foo/bar/#", "foo/bar"), "Matched childless topic");
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test("matches() doesn't support # wildcards with more after them", function (t) {
|
|
33
|
+
t.plan(1);
|
|
34
|
+
t.notOk(MQTTPattern.matches("#/bar/baz", "foo/bar/baz"), "Didn't match topic");
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test("matches() supports patterns with + at the beginning", function (t) {
|
|
38
|
+
t.plan(1);
|
|
39
|
+
t.ok(MQTTPattern.matches("+/bar/baz", "foo/bar/baz"), "Matched topic");
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test("matches() supports patterns with + at the end", function (t) {
|
|
43
|
+
t.plan(1);
|
|
44
|
+
t.ok(MQTTPattern.matches("foo/bar/+", "foo/bar/baz"), "Matched topic");
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test("matches() supports patterns with + in the middle", function (t) {
|
|
48
|
+
t.plan(1);
|
|
49
|
+
t.ok(MQTTPattern.matches("foo/+/baz", "foo/bar/baz"), "Matched topic");
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test("matches() supports patterns multiple wildcards", function (t) {
|
|
53
|
+
t.plan(1);
|
|
54
|
+
t.ok(MQTTPattern.matches("foo/+/#", "foo/bar/baz"), "Matched topic");
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test("matches() supports named wildcards", function (t) {
|
|
58
|
+
t.plan(1);
|
|
59
|
+
t.ok(MQTTPattern.matches("foo/+something/#else", "foo/bar/baz"), "Matched topic");
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test("matches() supports leading slashes", function (t){
|
|
63
|
+
t.plan(2);
|
|
64
|
+
t.ok(MQTTPattern.matches("/foo/bar", "/foo/bar"), "Matched topic");
|
|
65
|
+
t.notok(MQTTPattern.matches("/foo/bar", "/bar/foo"), "Didn't match invalid topic");
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test("extract() returns empty object of there's nothing to extract", function (t) {
|
|
69
|
+
t.plan(1);
|
|
70
|
+
t.deepEqual(MQTTPattern.extract("foo/bar/baz", "foo/bar/baz"), {}, "Extracted empty object");
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test("extract() returns empty object if wildcards don't have label", function (t) {
|
|
74
|
+
t.plan(1);
|
|
75
|
+
t.deepEqual(MQTTPattern.extract("foo/+/#", "foo/bar/baz"), {}, "Extracted empty object");
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test("extract() returns object with an array for # wildcard", function (t) {
|
|
79
|
+
t.plan(1);
|
|
80
|
+
t.deepEqual(MQTTPattern.extract("foo/#something", "foo/bar/baz"), {
|
|
81
|
+
something: ["bar", "baz"]
|
|
82
|
+
}, "Extracted param");
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test("extract() returns object with a string for + wildcard", function (t) {
|
|
86
|
+
t.plan(1);
|
|
87
|
+
t.deepEqual(MQTTPattern.extract("foo/+hello/+world", "foo/bar/baz"), {
|
|
88
|
+
hello: "bar",
|
|
89
|
+
world: "baz"
|
|
90
|
+
}, "Extracted params");
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test("extract() parses params from all wildcards", function (t) {
|
|
94
|
+
t.plan(1);
|
|
95
|
+
t.deepEqual(MQTTPattern.extract("+hello/+world/#wow", "foo/bar/baz/fizz"), {
|
|
96
|
+
hello: "foo",
|
|
97
|
+
world: "bar",
|
|
98
|
+
wow: ["baz", "fizz"]
|
|
99
|
+
}, "Extracted params");
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test("exec() returns null if it doesn't match", function (t) {
|
|
103
|
+
t.plan(1);
|
|
104
|
+
t.equal(MQTTPattern.exec("hello/world", "foo/bar/baz"), null, "Got null");
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
test("exec() returns params if they can be parsed", function(t){
|
|
108
|
+
t.plan(1);
|
|
109
|
+
t.deepEqual(MQTTPattern.exec("foo/+hello/#world", "foo/bar/baz"), {
|
|
110
|
+
hello: "bar",
|
|
111
|
+
world: ["baz"]
|
|
112
|
+
}, "Extracted params");
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
test("fill() fills in pattern with both types of wildcards", function(t){
|
|
116
|
+
t.plan(1);
|
|
117
|
+
t.deepEqual(MQTTPattern.fill("foo/+hello/#world", {
|
|
118
|
+
hello: "Hello",
|
|
119
|
+
world: ["the", "world", "wow"],
|
|
120
|
+
}), "foo/Hello/the/world/wow", "Filled in params");
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
test("fill() fills missing + params with undefined", function(t){
|
|
124
|
+
t.plan(1);
|
|
125
|
+
t.deepEqual(
|
|
126
|
+
MQTTPattern.fill("foo/+hello", {}),
|
|
127
|
+
"foo/undefined",
|
|
128
|
+
"Filled in params"
|
|
129
|
+
);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
test("fill() ignores empty # params", function(t){
|
|
133
|
+
t.plan(1);
|
|
134
|
+
t.deepEqual(
|
|
135
|
+
MQTTPattern.fill("foo/#hello", {}),
|
|
136
|
+
"foo",
|
|
137
|
+
"Filled in params"
|
|
138
|
+
);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
test("fill() ignores non-named # params", function (t) {
|
|
142
|
+
t.plan(1);
|
|
143
|
+
t.deepEqual(
|
|
144
|
+
MQTTPattern.fill("foo/#", {}),
|
|
145
|
+
"foo",
|
|
146
|
+
"Filled in params"
|
|
147
|
+
);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
test("fill() uses `undefined` for non-named + params", function(t){
|
|
151
|
+
t.plan(1);
|
|
152
|
+
t.deepEqual(
|
|
153
|
+
MQTTPattern.fill("foo/+", {}),
|
|
154
|
+
"foo/undefined",
|
|
155
|
+
"Filled in params"
|
|
156
|
+
);
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
test("clean() removes parameter names", function(t){
|
|
160
|
+
t.plan(1);
|
|
161
|
+
t.equal(MQTTPattern.clean("hello/+param1/world/#param2"), "hello/+/world/#", "Got hello/+/world/#");
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
test("clean() works when there aren't any parameter names", function(t){
|
|
165
|
+
t.plan(1);
|
|
166
|
+
t.equal(MQTTPattern.clean("hello/+/world/#"), "hello/+/world/#", "Got hello/+/world/#");
|
|
167
|
+
});
|
package/test.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { clean, exec, extract, fill, matches } from "./index";
|
|
2
|
+
import { Test } from "ts-toolbelt";
|
|
3
|
+
import test from "node:test";
|
|
4
|
+
// Type Testing
|
|
5
|
+
|
|
6
|
+
const { check, checks } = Test;
|
|
7
|
+
|
|
8
|
+
// Exec
|
|
9
|
+
const execv = exec("foo/bar/+baz", "foo/bar/test");
|
|
10
|
+
checks([
|
|
11
|
+
check<typeof execv, { baz: string } | null, Test.Pass>(),
|
|
12
|
+
check<typeof execv, { baz: string[] } | null, Test.Fail>(),
|
|
13
|
+
]);
|
|
14
|
+
|
|
15
|
+
// Matches
|
|
16
|
+
const matchv = matches("foo/bar/+baz", "foo/bar/test");
|
|
17
|
+
check<typeof matchv, boolean, Test.Pass>();
|
|
18
|
+
|
|
19
|
+
// Fill
|
|
20
|
+
const fillv = fill("foo/bar/+baz/#test", {
|
|
21
|
+
baz: "test",
|
|
22
|
+
test: ["v1", "v2"],
|
|
23
|
+
});
|
|
24
|
+
check<typeof fillv, "foo/bar/test/v1/v2", Test.Pass>();
|
|
25
|
+
|
|
26
|
+
// Extract
|
|
27
|
+
const extractv = extract("foo/bar/+baz", "foo/bar/test");
|
|
28
|
+
checks([
|
|
29
|
+
check<typeof extractv, { baz: string }, Test.Pass>(),
|
|
30
|
+
check<typeof extractv, { baz: string[] }, Test.Fail>(),
|
|
31
|
+
]);
|
|
32
|
+
|
|
33
|
+
// Clean
|
|
34
|
+
const cleanv = clean("foo/bar/+baz");
|
|
35
|
+
check<typeof cleanv, "foo/bar/+", Test.Pass>();
|