@halleyassist/rule-templater 0.0.16 → 0.0.18
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 +16 -1
- package/dist/rule-templater.browser.js +269 -22
- package/index.d.ts +33 -2
- package/index.js +2 -0
- package/package.json +1 -1
- package/src/GeneralTemplate.js +85 -5
- package/src/HalleyFunctionBlob.js +126 -0
- package/src/RuleTemplate.js +174 -18
- package/src/RuleTemplate.production.js +174 -18
- package/src/TemplateFilters.js +88 -0
- package/src/VariableTemplate.js +74 -6
- package/src/VariableValidate.js +7 -4
package/src/TemplateFilters.js
CHANGED
|
@@ -2,6 +2,31 @@
|
|
|
2
2
|
Template filters are functions that transform variable values.
|
|
3
3
|
They are applied in the template syntax as ${variable|filter} or ${variable|filter1|filter2}
|
|
4
4
|
*/
|
|
5
|
+
const HUMANISE_TIME_UNITS = [
|
|
6
|
+
{ name: 'year', seconds: 31536000, aliases: ['year', 'years', 'yr', 'yrs', 'y'] },
|
|
7
|
+
{ name: 'month', seconds: 2592000, aliases: ['month', 'months', 'mo', 'mos'] },
|
|
8
|
+
{ name: 'week', seconds: 604800, aliases: ['week', 'weeks', 'wk', 'wks', 'w'] },
|
|
9
|
+
{ name: 'day', seconds: 86400, aliases: ['day', 'days', 'd'] },
|
|
10
|
+
{ name: 'hour', seconds: 3600, aliases: ['hour', 'hours', 'hr', 'hrs', 'h'] },
|
|
11
|
+
{ name: 'minute', seconds: 60, aliases: ['minute', 'minutes', 'min', 'mins'] },
|
|
12
|
+
{ name: 'second', seconds: 1, aliases: ['second', 'seconds', 'sec', 'secs', 's'] }
|
|
13
|
+
];
|
|
14
|
+
|
|
15
|
+
const getHumaniseTimeUnit = minUnit => {
|
|
16
|
+
if (minUnit === null || minUnit === undefined || minUnit === '') {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const normalizedMinUnit = String(minUnit).trim().toLowerCase();
|
|
21
|
+
const unit = HUMANISE_TIME_UNITS.find(candidate => candidate.aliases.includes(normalizedMinUnit));
|
|
22
|
+
|
|
23
|
+
if (!unit) {
|
|
24
|
+
throw new Error(`Unknown humanise_time min_unit \"${minUnit}\"`);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return unit;
|
|
28
|
+
};
|
|
29
|
+
|
|
5
30
|
const TemplateFilters = {
|
|
6
31
|
// Convert value to JSON string representation
|
|
7
32
|
string: varData => {
|
|
@@ -125,6 +150,69 @@ const TemplateFilters = {
|
|
|
125
150
|
|
|
126
151
|
},
|
|
127
152
|
|
|
153
|
+
humanise_list: (varData, joiner = 'and') => {
|
|
154
|
+
if (typeof varData.value === 'string') {
|
|
155
|
+
varData.type = 'string';
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (!Array.isArray(varData.value)) {
|
|
160
|
+
varData.value = String(varData.value);
|
|
161
|
+
varData.type = 'string';
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const items = varData.value.map(item => String(item));
|
|
166
|
+
|
|
167
|
+
if (items.length === 0) {
|
|
168
|
+
varData.value = '';
|
|
169
|
+
} else if (items.length === 1) {
|
|
170
|
+
[varData.value] = items;
|
|
171
|
+
} else if (items.length === 2) {
|
|
172
|
+
varData.value = `${items[0]} ${joiner} ${items[1]}`;
|
|
173
|
+
} else {
|
|
174
|
+
varData.value = `${items.slice(0, -1).join(', ')} ${joiner} ${items[items.length - 1]}`;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
varData.type = 'string';
|
|
178
|
+
|
|
179
|
+
},
|
|
180
|
+
|
|
181
|
+
humanise_time: (varData, minUnit = null) => {
|
|
182
|
+
const rawSeconds = Number(varData.value);
|
|
183
|
+
|
|
184
|
+
if (isNaN(rawSeconds)) {
|
|
185
|
+
throw new Error(`Value "${varData.value}" cannot be converted to seconds`);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const isNegative = rawSeconds < 0;
|
|
189
|
+
const absoluteSeconds = Math.abs(rawSeconds);
|
|
190
|
+
const minimumUnit = getHumaniseTimeUnit(minUnit);
|
|
191
|
+
const minimumUnitIndex = minimumUnit
|
|
192
|
+
? HUMANISE_TIME_UNITS.findIndex(unit => unit.name === minimumUnit.name)
|
|
193
|
+
: HUMANISE_TIME_UNITS.length - 1;
|
|
194
|
+
const candidateUnits = HUMANISE_TIME_UNITS.slice(0, minimumUnitIndex + 1);
|
|
195
|
+
let selectedUnit = candidateUnits.find(unit => absoluteSeconds % unit.seconds === 0);
|
|
196
|
+
let quantity;
|
|
197
|
+
|
|
198
|
+
if (selectedUnit) {
|
|
199
|
+
quantity = absoluteSeconds / selectedUnit.seconds;
|
|
200
|
+
} else if (minimumUnit) {
|
|
201
|
+
selectedUnit = minimumUnit;
|
|
202
|
+
quantity = Math.floor(absoluteSeconds / selectedUnit.seconds);
|
|
203
|
+
} else {
|
|
204
|
+
selectedUnit = HUMANISE_TIME_UNITS[HUMANISE_TIME_UNITS.length - 1];
|
|
205
|
+
quantity = absoluteSeconds;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const signedQuantity = isNegative ? -quantity : quantity;
|
|
209
|
+
const label = Math.abs(signedQuantity) === 1 ? selectedUnit.name : `${selectedUnit.name}s`;
|
|
210
|
+
|
|
211
|
+
varData.value = `${signedQuantity} ${label}`;
|
|
212
|
+
varData.type = 'string';
|
|
213
|
+
|
|
214
|
+
},
|
|
215
|
+
|
|
128
216
|
// Extract start time from time period/time period ago as time value
|
|
129
217
|
time_start: varData => {
|
|
130
218
|
if (varData.type === 'time period' || varData.type === 'time period ago') {
|
package/src/VariableTemplate.js
CHANGED
|
@@ -37,7 +37,7 @@ class VariableTemplate {
|
|
|
37
37
|
extractVariable() {
|
|
38
38
|
return {
|
|
39
39
|
name: this.variable.name,
|
|
40
|
-
filters: this.variable.filters.
|
|
40
|
+
filters: this.variable.filters.map(filter => filter.name)
|
|
41
41
|
};
|
|
42
42
|
}
|
|
43
43
|
|
|
@@ -62,12 +62,15 @@ class VariableTemplate {
|
|
|
62
62
|
|
|
63
63
|
varData = VariableTemplate._cloneVarData(varData);
|
|
64
64
|
|
|
65
|
-
for (const
|
|
66
|
-
|
|
67
|
-
|
|
65
|
+
for (const filter of this.variable.filters) {
|
|
66
|
+
const filterName = typeof filter === 'string' ? filter : filter?.name;
|
|
67
|
+
const filterArgs = typeof filter === 'string' ? [] : (Array.isArray(filter?.args) ? filter.args : []);
|
|
68
|
+
|
|
69
|
+
if (!filterName || !TemplateFilters[filterName]) {
|
|
70
|
+
throw new Error(`Unknown filter '${filterName || filter}'`);
|
|
68
71
|
}
|
|
69
72
|
|
|
70
|
-
TemplateFilters[filterName](varData);
|
|
73
|
+
TemplateFilters[filterName](varData, ...filterArgs);
|
|
71
74
|
}
|
|
72
75
|
|
|
73
76
|
return varData;
|
|
@@ -98,7 +101,11 @@ class VariableTemplate {
|
|
|
98
101
|
const filterNameNode = child.children?.find(c => c.type === 'template_filter_name');
|
|
99
102
|
const filterName = filterNameNode?.text?.trim();
|
|
100
103
|
if (filterName) {
|
|
101
|
-
|
|
104
|
+
const argsNode = child.children?.find(c => c.type === 'template_filter_args');
|
|
105
|
+
filters.push({
|
|
106
|
+
name: filterName,
|
|
107
|
+
args: VariableTemplate._extractFilterArgs(argsNode)
|
|
108
|
+
});
|
|
102
109
|
}
|
|
103
110
|
}
|
|
104
111
|
}
|
|
@@ -121,6 +128,67 @@ class VariableTemplate {
|
|
|
121
128
|
|
|
122
129
|
return cloned;
|
|
123
130
|
}
|
|
131
|
+
|
|
132
|
+
static _extractFilterArgs(node) {
|
|
133
|
+
if (!node || !Array.isArray(node.children)) {
|
|
134
|
+
return [];
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return node.children
|
|
138
|
+
.filter(child => child.type === 'template_filter_arg')
|
|
139
|
+
.map(child => VariableTemplate._extractFilterArgValue(child));
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
static _extractFilterArgValue(node) {
|
|
143
|
+
if (!node || !Array.isArray(node.children) || node.children.length === 0) {
|
|
144
|
+
return VariableTemplate._normalizeFilterArgText(node?.text?.trim() || '');
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const child = node.children[0];
|
|
148
|
+
if (!child) {
|
|
149
|
+
return VariableTemplate._normalizeFilterArgText(node.text?.trim() || '');
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (child.type === 'value' && Array.isArray(child.children) && child.children.length > 0) {
|
|
153
|
+
return VariableTemplate._extractFilterArgValue(child);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (child.type === 'string') {
|
|
157
|
+
try {
|
|
158
|
+
return JSON.parse(child.text);
|
|
159
|
+
} catch (error) {
|
|
160
|
+
return VariableTemplate._normalizeFilterArgText(child.text);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (child.type === 'number') {
|
|
165
|
+
return Number(child.text);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (child.type === 'true') {
|
|
169
|
+
return true;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (child.type === 'false') {
|
|
173
|
+
return false;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (child.type === 'null') {
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return VariableTemplate._normalizeFilterArgText(child.text?.trim() || node.text?.trim() || '');
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
static _normalizeFilterArgText(text) {
|
|
184
|
+
const normalizedText = String(text).trim();
|
|
185
|
+
|
|
186
|
+
if ((normalizedText.startsWith('"') && normalizedText.endsWith('"')) || (normalizedText.startsWith("'") && normalizedText.endsWith("'"))) {
|
|
187
|
+
return normalizedText.slice(1, -1);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return normalizedText;
|
|
191
|
+
}
|
|
124
192
|
}
|
|
125
193
|
|
|
126
194
|
module.exports = VariableTemplate;
|
package/src/VariableValidate.js
CHANGED
|
@@ -139,12 +139,15 @@ class VariableValidate {
|
|
|
139
139
|
throw new Error('Variable data filters must be an array');
|
|
140
140
|
}
|
|
141
141
|
|
|
142
|
-
for (const
|
|
143
|
-
|
|
144
|
-
|
|
142
|
+
for (const filter of normalizedVarData.filters) {
|
|
143
|
+
const filterName = typeof filter === 'string' ? filter : filter?.name;
|
|
144
|
+
const filterArgs = typeof filter === 'string' ? [] : (Array.isArray(filter?.args) ? filter.args : []);
|
|
145
|
+
|
|
146
|
+
if (!filterName || !TemplateFilters[filterName]) {
|
|
147
|
+
throw new Error(`Unknown filter '${filterName || filter}'`);
|
|
145
148
|
}
|
|
146
149
|
|
|
147
|
-
TemplateFilters[filterName](normalizedVarData);
|
|
150
|
+
TemplateFilters[filterName](normalizedVarData, ...filterArgs);
|
|
148
151
|
}
|
|
149
152
|
|
|
150
153
|
return normalizedVarData;
|