@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.
@@ -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') {
@@ -37,7 +37,7 @@ class VariableTemplate {
37
37
  extractVariable() {
38
38
  return {
39
39
  name: this.variable.name,
40
- filters: this.variable.filters.slice()
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 filterName of this.variable.filters) {
66
- if (!TemplateFilters[filterName]) {
67
- throw new Error(`Unknown filter '${filterName}'`);
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
- filters.push(filterName);
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;
@@ -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 filterName of normalizedVarData.filters) {
143
- if (!TemplateFilters[filterName]) {
144
- throw new Error(`Unknown filter '${filterName}'`);
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;