@halleyassist/rule-templater 0.0.4 → 0.0.6
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/README.md +32 -5
- package/dist/rule-templater.browser.js +64 -6
- package/index.d.ts +33 -0
- package/package.json +1 -1
- package/src/RuleTemplater.js +64 -6
- package/src/RuleTemplater.production.js +64 -6
package/README.md
CHANGED
|
@@ -27,10 +27,15 @@ const parsed = RuleTemplate.parse(template);
|
|
|
27
27
|
const variables = parsed.extractVariables();
|
|
28
28
|
console.log(variables);
|
|
29
29
|
// [
|
|
30
|
-
// { name: 'EVENT_TYPE', filters: [] },
|
|
31
|
-
// { name: 'THRESHOLD', filters: [] }
|
|
30
|
+
// { name: 'EVENT_TYPE', filters: [], positions: [{ start: 8, end: 21 }] },
|
|
31
|
+
// { name: 'THRESHOLD', filters: [], positions: [{ start: 36, end: 48 }] }
|
|
32
32
|
// ]
|
|
33
33
|
|
|
34
|
+
// Extract function calls from the template
|
|
35
|
+
const functions = parsed.extractFunctions();
|
|
36
|
+
console.log(functions);
|
|
37
|
+
// ['EventIs', 'Value']
|
|
38
|
+
|
|
34
39
|
// Validate that variables are provided correctly
|
|
35
40
|
const validation = parsed.validate({
|
|
36
41
|
EVENT_TYPE: { value: 'sensor-update', type: 'string' },
|
|
@@ -57,8 +62,8 @@ const parsed = RuleTemplate.parse(template);
|
|
|
57
62
|
// Extract variables
|
|
58
63
|
const variables = parsed.extractVariables();
|
|
59
64
|
// [
|
|
60
|
-
// { name: 'ACTION', filters: [] },
|
|
61
|
-
// { name: 'TIME', filters: [] }
|
|
65
|
+
// { name: 'ACTION', filters: [], positions: [{ start: 48, end: 57 }] },
|
|
66
|
+
// { name: 'TIME', filters: [], positions: [{ start: 142, end: 149 }] }
|
|
62
67
|
// ]
|
|
63
68
|
|
|
64
69
|
// Prepare with values
|
|
@@ -79,7 +84,7 @@ const template = 'EventIs(${EVENT_TYPE|upper})';
|
|
|
79
84
|
|
|
80
85
|
const parsed = RuleTemplate.parse(template);
|
|
81
86
|
const variables = parsed.extractVariables();
|
|
82
|
-
// [{ name: 'EVENT_TYPE', filters: ['upper'] }]
|
|
87
|
+
// [{ name: 'EVENT_TYPE', filters: ['upper'], positions: [{ start: 8, end: 27 }] }]
|
|
83
88
|
|
|
84
89
|
// Prepare with filters applied
|
|
85
90
|
const prepared = parsed.prepare({
|
|
@@ -154,6 +159,28 @@ Extracts all variables from the template using the AST.
|
|
|
154
159
|
**Returns:** Array of objects with:
|
|
155
160
|
- `name` (string): The variable name
|
|
156
161
|
- `filters` (array): Array of filter names applied to the variable
|
|
162
|
+
- `positions` (array): Array of position objects, each with:
|
|
163
|
+
- `start` (number): Zero-based start index of the variable in the template string
|
|
164
|
+
- `end` (number): Zero-based end index of the variable in the template string
|
|
165
|
+
|
|
166
|
+
Note: If a variable appears multiple times in the template, all occurrences will be recorded in the `positions` array.
|
|
167
|
+
|
|
168
|
+
### `ruleTemplate.extractFunctions()`
|
|
169
|
+
|
|
170
|
+
Extracts all function calls from the template using the AST.
|
|
171
|
+
|
|
172
|
+
**Returns:** Array of unique function names (sorted alphabetically) used in the template.
|
|
173
|
+
|
|
174
|
+
**Example:**
|
|
175
|
+
```javascript
|
|
176
|
+
const template = 'EventIs("test") && Value() > 10 && TimeLastTrueCheck("last_check") < 60';
|
|
177
|
+
const parsed = RuleTemplate.parse(template);
|
|
178
|
+
const functions = parsed.extractFunctions();
|
|
179
|
+
console.log(functions);
|
|
180
|
+
// ['EventIs', 'TimeLastTrueCheck', 'Value']
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
This is useful for comparing against a hub's list of available functions to ensure all functions used in the template are supported.
|
|
157
184
|
|
|
158
185
|
### `ruleTemplate.validate(variables)`
|
|
159
186
|
|
|
@@ -2113,11 +2113,11 @@ class RuleTemplate {
|
|
|
2113
2113
|
|
|
2114
2114
|
/**
|
|
2115
2115
|
* Extract variables from the template using the AST
|
|
2116
|
-
* @returns {Array} Array of {name, filters: []} objects
|
|
2116
|
+
* @returns {Array} Array of {name, filters: [], positions: [{start, end}]} objects
|
|
2117
2117
|
*/
|
|
2118
2118
|
extractVariables(){
|
|
2119
2119
|
const variables = [];
|
|
2120
|
-
const
|
|
2120
|
+
const variableMap = new Map();
|
|
2121
2121
|
|
|
2122
2122
|
const traverse = (node) => {
|
|
2123
2123
|
if (!node) return;
|
|
@@ -2126,9 +2126,24 @@ class RuleTemplate {
|
|
|
2126
2126
|
if (node.type === 'template_value') {
|
|
2127
2127
|
// Extract the variable information
|
|
2128
2128
|
const varInfo = this._extractVariableFromNode(node);
|
|
2129
|
-
if (varInfo
|
|
2130
|
-
|
|
2131
|
-
|
|
2129
|
+
if (varInfo) {
|
|
2130
|
+
// Add position to existing variable or create new entry
|
|
2131
|
+
if (variableMap.has(varInfo.name)) {
|
|
2132
|
+
const existing = variableMap.get(varInfo.name);
|
|
2133
|
+
existing.positions.push({
|
|
2134
|
+
start: varInfo.start,
|
|
2135
|
+
end: varInfo.end
|
|
2136
|
+
});
|
|
2137
|
+
} else {
|
|
2138
|
+
variableMap.set(varInfo.name, {
|
|
2139
|
+
name: varInfo.name,
|
|
2140
|
+
filters: varInfo.filters,
|
|
2141
|
+
positions: [{
|
|
2142
|
+
start: varInfo.start,
|
|
2143
|
+
end: varInfo.end
|
|
2144
|
+
}]
|
|
2145
|
+
});
|
|
2146
|
+
}
|
|
2132
2147
|
}
|
|
2133
2148
|
}
|
|
2134
2149
|
|
|
@@ -2141,9 +2156,48 @@ class RuleTemplate {
|
|
|
2141
2156
|
};
|
|
2142
2157
|
|
|
2143
2158
|
traverse(this.ast);
|
|
2159
|
+
|
|
2160
|
+
// Convert map to array
|
|
2161
|
+
for (const variable of variableMap.values()) {
|
|
2162
|
+
variables.push(variable);
|
|
2163
|
+
}
|
|
2164
|
+
|
|
2144
2165
|
return variables;
|
|
2145
2166
|
}
|
|
2146
2167
|
|
|
2168
|
+
/**
|
|
2169
|
+
* Extract function calls from the template using the AST
|
|
2170
|
+
* @returns {Array} Array of unique function names used in the template
|
|
2171
|
+
*/
|
|
2172
|
+
extractFunctions(){
|
|
2173
|
+
const functions = new Set();
|
|
2174
|
+
|
|
2175
|
+
const traverse = (node) => {
|
|
2176
|
+
if (!node) return;
|
|
2177
|
+
|
|
2178
|
+
// Check if this is a function call node
|
|
2179
|
+
if (node.type === 'fcall') {
|
|
2180
|
+
// Find the function name in children
|
|
2181
|
+
const fnameNode = node.children?.find(c => c.type === 'fname');
|
|
2182
|
+
if (fnameNode && fnameNode.text) {
|
|
2183
|
+
functions.add(fnameNode.text.trim());
|
|
2184
|
+
}
|
|
2185
|
+
}
|
|
2186
|
+
|
|
2187
|
+
// Traverse children
|
|
2188
|
+
if (node.children) {
|
|
2189
|
+
for (const child of node.children) {
|
|
2190
|
+
traverse(child);
|
|
2191
|
+
}
|
|
2192
|
+
}
|
|
2193
|
+
};
|
|
2194
|
+
|
|
2195
|
+
traverse(this.ast);
|
|
2196
|
+
|
|
2197
|
+
// Convert set to sorted array for consistent output
|
|
2198
|
+
return Array.from(functions).sort();
|
|
2199
|
+
}
|
|
2200
|
+
|
|
2147
2201
|
/**
|
|
2148
2202
|
* Extract variable name and filters from a template_value AST node
|
|
2149
2203
|
* @private
|
|
@@ -2172,7 +2226,11 @@ class RuleTemplate {
|
|
|
2172
2226
|
}
|
|
2173
2227
|
}
|
|
2174
2228
|
|
|
2175
|
-
|
|
2229
|
+
// Extract position information from the node
|
|
2230
|
+
const start = node.start;
|
|
2231
|
+
const end = node.end;
|
|
2232
|
+
|
|
2233
|
+
return { name, filters, start, end };
|
|
2176
2234
|
}
|
|
2177
2235
|
|
|
2178
2236
|
/**
|
package/index.d.ts
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
|
+
export interface VariablePosition {
|
|
2
|
+
start: number;
|
|
3
|
+
end: number;
|
|
4
|
+
}
|
|
5
|
+
|
|
1
6
|
export interface VariableInfo {
|
|
2
7
|
name: string;
|
|
3
8
|
filters: string[];
|
|
9
|
+
positions: VariablePosition[];
|
|
4
10
|
}
|
|
5
11
|
|
|
6
12
|
export interface VariableValue {
|
|
@@ -62,6 +68,12 @@ export default class RuleTemplate {
|
|
|
62
68
|
*/
|
|
63
69
|
extractVariables(): VariableInfo[];
|
|
64
70
|
|
|
71
|
+
/**
|
|
72
|
+
* Extract function calls from the template using the AST
|
|
73
|
+
* @returns Array of unique function names used in the template
|
|
74
|
+
*/
|
|
75
|
+
extractFunctions(): string[];
|
|
76
|
+
|
|
65
77
|
/**
|
|
66
78
|
* Validate variable types against the AST
|
|
67
79
|
* @param variables Object mapping variable names to {value, type} objects
|
|
@@ -89,3 +101,24 @@ export default class RuleTemplate {
|
|
|
89
101
|
export const ParserRules: any[];
|
|
90
102
|
export const VariableTypes: string[];
|
|
91
103
|
export const TemplateFilters: TemplateFiltersType;
|
|
104
|
+
|
|
105
|
+
export interface IParsingErrorPosition {
|
|
106
|
+
offset: number;
|
|
107
|
+
line: number;
|
|
108
|
+
column: number;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export interface IFailureTreeNode {
|
|
112
|
+
name: string;
|
|
113
|
+
expected?: string | RegExp;
|
|
114
|
+
children?: IFailureTreeNode[];
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export class ParsingError extends Error {
|
|
118
|
+
readonly position: IParsingErrorPosition;
|
|
119
|
+
readonly expected: string[];
|
|
120
|
+
readonly found: string;
|
|
121
|
+
readonly failureTree?: IFailureTreeNode[];
|
|
122
|
+
constructor(message: string, position: IParsingErrorPosition, expected: string[], found: string, failureTree?: IFailureTreeNode[]);
|
|
123
|
+
toString(): string;
|
|
124
|
+
}
|
package/package.json
CHANGED
package/src/RuleTemplater.js
CHANGED
|
@@ -75,11 +75,11 @@ class RuleTemplate {
|
|
|
75
75
|
|
|
76
76
|
/**
|
|
77
77
|
* Extract variables from the template using the AST
|
|
78
|
-
* @returns {Array} Array of {name, filters: []} objects
|
|
78
|
+
* @returns {Array} Array of {name, filters: [], positions: [{start, end}]} objects
|
|
79
79
|
*/
|
|
80
80
|
extractVariables(){
|
|
81
81
|
const variables = [];
|
|
82
|
-
const
|
|
82
|
+
const variableMap = new Map();
|
|
83
83
|
|
|
84
84
|
const traverse = (node) => {
|
|
85
85
|
if (!node) return;
|
|
@@ -88,9 +88,24 @@ class RuleTemplate {
|
|
|
88
88
|
if (node.type === 'template_value') {
|
|
89
89
|
// Extract the variable information
|
|
90
90
|
const varInfo = this._extractVariableFromNode(node);
|
|
91
|
-
if (varInfo
|
|
92
|
-
|
|
93
|
-
|
|
91
|
+
if (varInfo) {
|
|
92
|
+
// Add position to existing variable or create new entry
|
|
93
|
+
if (variableMap.has(varInfo.name)) {
|
|
94
|
+
const existing = variableMap.get(varInfo.name);
|
|
95
|
+
existing.positions.push({
|
|
96
|
+
start: varInfo.start,
|
|
97
|
+
end: varInfo.end
|
|
98
|
+
});
|
|
99
|
+
} else {
|
|
100
|
+
variableMap.set(varInfo.name, {
|
|
101
|
+
name: varInfo.name,
|
|
102
|
+
filters: varInfo.filters,
|
|
103
|
+
positions: [{
|
|
104
|
+
start: varInfo.start,
|
|
105
|
+
end: varInfo.end
|
|
106
|
+
}]
|
|
107
|
+
});
|
|
108
|
+
}
|
|
94
109
|
}
|
|
95
110
|
}
|
|
96
111
|
|
|
@@ -103,9 +118,48 @@ class RuleTemplate {
|
|
|
103
118
|
};
|
|
104
119
|
|
|
105
120
|
traverse(this.ast);
|
|
121
|
+
|
|
122
|
+
// Convert map to array
|
|
123
|
+
for (const variable of variableMap.values()) {
|
|
124
|
+
variables.push(variable);
|
|
125
|
+
}
|
|
126
|
+
|
|
106
127
|
return variables;
|
|
107
128
|
}
|
|
108
129
|
|
|
130
|
+
/**
|
|
131
|
+
* Extract function calls from the template using the AST
|
|
132
|
+
* @returns {Array} Array of unique function names used in the template
|
|
133
|
+
*/
|
|
134
|
+
extractFunctions(){
|
|
135
|
+
const functions = new Set();
|
|
136
|
+
|
|
137
|
+
const traverse = (node) => {
|
|
138
|
+
if (!node) return;
|
|
139
|
+
|
|
140
|
+
// Check if this is a function call node
|
|
141
|
+
if (node.type === 'fcall') {
|
|
142
|
+
// Find the function name in children
|
|
143
|
+
const fnameNode = node.children?.find(c => c.type === 'fname');
|
|
144
|
+
if (fnameNode && fnameNode.text) {
|
|
145
|
+
functions.add(fnameNode.text.trim());
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Traverse children
|
|
150
|
+
if (node.children) {
|
|
151
|
+
for (const child of node.children) {
|
|
152
|
+
traverse(child);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
traverse(this.ast);
|
|
158
|
+
|
|
159
|
+
// Convert set to sorted array for consistent output
|
|
160
|
+
return Array.from(functions).sort();
|
|
161
|
+
}
|
|
162
|
+
|
|
109
163
|
/**
|
|
110
164
|
* Extract variable name and filters from a template_value AST node
|
|
111
165
|
* @private
|
|
@@ -134,7 +188,11 @@ class RuleTemplate {
|
|
|
134
188
|
}
|
|
135
189
|
}
|
|
136
190
|
|
|
137
|
-
|
|
191
|
+
// Extract position information from the node
|
|
192
|
+
const start = node.start;
|
|
193
|
+
const end = node.end;
|
|
194
|
+
|
|
195
|
+
return { name, filters, start, end };
|
|
138
196
|
}
|
|
139
197
|
|
|
140
198
|
/**
|
|
@@ -75,11 +75,11 @@ class RuleTemplate {
|
|
|
75
75
|
|
|
76
76
|
/**
|
|
77
77
|
* Extract variables from the template using the AST
|
|
78
|
-
* @returns {Array} Array of {name, filters: []} objects
|
|
78
|
+
* @returns {Array} Array of {name, filters: [], positions: [{start, end}]} objects
|
|
79
79
|
*/
|
|
80
80
|
extractVariables(){
|
|
81
81
|
const variables = [];
|
|
82
|
-
const
|
|
82
|
+
const variableMap = new Map();
|
|
83
83
|
|
|
84
84
|
const traverse = (node) => {
|
|
85
85
|
if (!node) return;
|
|
@@ -88,9 +88,24 @@ class RuleTemplate {
|
|
|
88
88
|
if (node.type === 'template_value') {
|
|
89
89
|
// Extract the variable information
|
|
90
90
|
const varInfo = this._extractVariableFromNode(node);
|
|
91
|
-
if (varInfo
|
|
92
|
-
|
|
93
|
-
|
|
91
|
+
if (varInfo) {
|
|
92
|
+
// Add position to existing variable or create new entry
|
|
93
|
+
if (variableMap.has(varInfo.name)) {
|
|
94
|
+
const existing = variableMap.get(varInfo.name);
|
|
95
|
+
existing.positions.push({
|
|
96
|
+
start: varInfo.start,
|
|
97
|
+
end: varInfo.end
|
|
98
|
+
});
|
|
99
|
+
} else {
|
|
100
|
+
variableMap.set(varInfo.name, {
|
|
101
|
+
name: varInfo.name,
|
|
102
|
+
filters: varInfo.filters,
|
|
103
|
+
positions: [{
|
|
104
|
+
start: varInfo.start,
|
|
105
|
+
end: varInfo.end
|
|
106
|
+
}]
|
|
107
|
+
});
|
|
108
|
+
}
|
|
94
109
|
}
|
|
95
110
|
}
|
|
96
111
|
|
|
@@ -103,9 +118,48 @@ class RuleTemplate {
|
|
|
103
118
|
};
|
|
104
119
|
|
|
105
120
|
traverse(this.ast);
|
|
121
|
+
|
|
122
|
+
// Convert map to array
|
|
123
|
+
for (const variable of variableMap.values()) {
|
|
124
|
+
variables.push(variable);
|
|
125
|
+
}
|
|
126
|
+
|
|
106
127
|
return variables;
|
|
107
128
|
}
|
|
108
129
|
|
|
130
|
+
/**
|
|
131
|
+
* Extract function calls from the template using the AST
|
|
132
|
+
* @returns {Array} Array of unique function names used in the template
|
|
133
|
+
*/
|
|
134
|
+
extractFunctions(){
|
|
135
|
+
const functions = new Set();
|
|
136
|
+
|
|
137
|
+
const traverse = (node) => {
|
|
138
|
+
if (!node) return;
|
|
139
|
+
|
|
140
|
+
// Check if this is a function call node
|
|
141
|
+
if (node.type === 'fcall') {
|
|
142
|
+
// Find the function name in children
|
|
143
|
+
const fnameNode = node.children?.find(c => c.type === 'fname');
|
|
144
|
+
if (fnameNode && fnameNode.text) {
|
|
145
|
+
functions.add(fnameNode.text.trim());
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Traverse children
|
|
150
|
+
if (node.children) {
|
|
151
|
+
for (const child of node.children) {
|
|
152
|
+
traverse(child);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
traverse(this.ast);
|
|
158
|
+
|
|
159
|
+
// Convert set to sorted array for consistent output
|
|
160
|
+
return Array.from(functions).sort();
|
|
161
|
+
}
|
|
162
|
+
|
|
109
163
|
/**
|
|
110
164
|
* Extract variable name and filters from a template_value AST node
|
|
111
165
|
* @private
|
|
@@ -134,7 +188,11 @@ class RuleTemplate {
|
|
|
134
188
|
}
|
|
135
189
|
}
|
|
136
190
|
|
|
137
|
-
|
|
191
|
+
// Extract position information from the node
|
|
192
|
+
const start = node.start;
|
|
193
|
+
const end = node.end;
|
|
194
|
+
|
|
195
|
+
return { name, filters, start, end };
|
|
138
196
|
}
|
|
139
197
|
|
|
140
198
|
/**
|