@halleyassist/rule-templater 0.0.10 → 0.0.11

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 CHANGED
@@ -121,6 +121,8 @@ const prepared = parsed.prepare({
121
121
  - **round**: Round number to nearest integer
122
122
  - **floor**: Round number down
123
123
  - **ceil**: Round number up
124
+ - **time_start**: Extract `from` from `time period` / `time period ago` and convert to `time value`
125
+ - **time_end**: Extract `to` from `time period` / `time period ago` and convert to `time value`
124
126
 
125
127
  #### Filter Examples
126
128
 
@@ -137,6 +139,9 @@ const prepared = parsed.prepare({
137
139
 
138
140
  // Chaining filters
139
141
  '${text|trim|upper}' with text=' hello ' → HELLO
142
+
143
+ // Time period conversion
144
+ '${window|time_start}' with window={from:'08:00',to:'12:00'} → 08:00
140
145
  ```
141
146
 
142
147
  ## API
@@ -222,13 +227,16 @@ Helper method to validate that an AST node matches the expected variable type.
222
227
 
223
228
  Access to the filter functions used by the template engine. Can be extended with custom filters.
224
229
 
230
+ Custom filters receive a cloned `varData` object (`{ value, type }`) used for template rendering. They can mutate both fields without mutating the original input variables.
231
+
225
232
  **Example:**
226
233
  ```javascript
227
234
  const RuleTemplate = require('@halleyassist/rule-templater');
228
235
 
229
236
  // Add a custom filter
230
- RuleTemplate.TemplateFilters.reverse = (value) => {
231
- return String(value).split('').reverse().join('');
237
+ RuleTemplate.TemplateFilters.reverse = (varData) => {
238
+ varData.value = String(varData.value).split('').reverse().join('');
239
+ return varData;
232
240
  };
233
241
 
234
242
  // Use the custom filter
@@ -3663,6 +3663,7 @@ const VariableTypes = [
3663
3663
  'boolean',
3664
3664
  'object',
3665
3665
  'time period',
3666
+ 'time period ago',
3666
3667
  'time value',
3667
3668
  'string array',
3668
3669
  'number array',
@@ -3675,6 +3676,7 @@ const AllowedTypeMapping = {
3675
3676
  'number': ['number_atom', 'math_expr'],
3676
3677
  'boolean': ['boolean_atom', 'boolean_expr'],
3677
3678
  'time period': ['time_period_atom'],
3679
+ 'time period ago': ['time_period_atom'],
3678
3680
  'time value': ['time_value_atom', 'tod_atom'],
3679
3681
  'string array': ['string_array'],
3680
3682
  'number array': ['number_array'],
@@ -3985,12 +3987,12 @@ class RuleTemplate {
3985
3987
  throw new Error(`Variable '${varName}' not provided in variables object`);
3986
3988
  }
3987
3989
 
3988
- const varData = variables[varName];
3990
+ let varData = variables[varName];
3989
3991
  if (typeof varData !== 'object' || !varData.hasOwnProperty('value')) {
3990
3992
  throw new Error(`Variable '${varName}' must be an object with 'value' property`);
3991
3993
  }
3992
-
3993
- let { value, type } = varData;
3994
+
3995
+ varData = Object.assign({}, varData);
3994
3996
 
3995
3997
  // Require type property for all variables
3996
3998
  if (!varData.hasOwnProperty('type')) {
@@ -3998,8 +4000,8 @@ class RuleTemplate {
3998
4000
  }
3999
4001
 
4000
4002
  // Validate type
4001
- if (!VariableTypes.includes(type)) {
4002
- throw new Error(`Invalid variable type '${type}' for variable '${varName}'`);
4003
+ if (!VariableTypes.includes(varData.type)) {
4004
+ throw new Error(`Invalid variable type '${varData.type}' for variable '${varName}'`);
4003
4005
  }
4004
4006
 
4005
4007
  // Apply filters if present
@@ -4008,27 +4010,42 @@ class RuleTemplate {
4008
4010
  if (!TemplateFilters[filterName]) {
4009
4011
  throw new Error(`Unknown filter '${filterName}'`);
4010
4012
  }
4011
- value = TemplateFilters[filterName](value);
4013
+
4014
+ TemplateFilters[filterName](varData);
4012
4015
  }
4013
- // After applying filters, the result is already a string representation
4014
- return String(value);
4015
4016
  }
4016
-
4017
- // Convert value to string representation based on type
4017
+
4018
+ return this._serializeVarData(varData, varName);
4019
+ }
4020
+
4021
+ _serializeVarData(varData, varName) {
4022
+ const { value, type } = varData;
4023
+
4024
+ if (!VariableTypes.includes(type)) {
4025
+ throw new Error(`Invalid variable type '${type}' for variable '${varName}'`);
4026
+ }
4027
+
4018
4028
  if (type === 'string') {
4019
- // Escape backslashes first, then quotes in string values.
4020
- // Order is critical: escaping backslashes first prevents double-escaping.
4021
- // E.g., "test\" becomes "test\\" then "test\\\"" (correct)
4022
- // If reversed, "test\" would become "test\\"" then "test\\\\"" (incorrect)
4023
4029
  return `"${String(value).replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`;
4024
- } else if (type === 'number') {
4030
+ }
4031
+
4032
+ if (type === 'number') {
4025
4033
  return String(value);
4026
- } else if (type === 'boolean') {
4034
+ }
4035
+
4036
+ if (type === 'boolean') {
4027
4037
  return value ? 'true' : 'false';
4028
- } else {
4029
- // Default behavior - just insert the value as-is
4030
- return String(value);
4031
4038
  }
4039
+
4040
+ if (type === 'time period' || type === 'time period ago') {
4041
+ let ret = `${value.from} TO ${value.to}`;
4042
+ if(value.ago) {
4043
+ ret += ` AGO ${value.ago[0]} ${value.ago[1]}`;
4044
+ }
4045
+ return ret;
4046
+ }
4047
+
4048
+ return String(value);
4032
4049
  }
4033
4050
 
4034
4051
  /**
@@ -4063,59 +4080,153 @@ They are applied in the template syntax as ${variable|filter} or ${variable|filt
4063
4080
  */
4064
4081
  const TemplateFilters = {
4065
4082
  // Convert value to JSON string representation
4066
- string: value => JSON.stringify(String(value)),
4067
-
4083
+ string: varData => {
4084
+ varData.value = String(varData.value);
4085
+ varData.type = 'string';
4086
+
4087
+ },
4088
+
4068
4089
  // Convert to uppercase
4069
- upper: value => String(value).toUpperCase(),
4070
-
4090
+ upper: varData => {
4091
+ varData.value = String(varData.value).toUpperCase();
4092
+ varData.type = 'string';
4093
+
4094
+ },
4095
+
4071
4096
  // Convert to lowercase
4072
- lower: value => String(value).toLowerCase(),
4073
-
4097
+ lower: varData => {
4098
+ varData.value = String(varData.value).toLowerCase();
4099
+ varData.type = 'string';
4100
+
4101
+ },
4102
+
4074
4103
  // Capitalize first letter
4075
- capitalize: value => {
4076
- const str = String(value);
4077
- return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
4104
+ capitalize: varData => {
4105
+ const str = String(varData.value);
4106
+ varData.value = str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
4107
+ varData.type = 'string';
4108
+
4078
4109
  },
4079
-
4110
+
4080
4111
  // Convert to title case
4081
- title: value => {
4082
- return String(value).split(' ')
4112
+ title: varData => {
4113
+ varData.value = String(varData.value).split(' ')
4083
4114
  .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
4084
4115
  .join(' ');
4116
+ varData.type = 'string';
4117
+
4085
4118
  },
4086
-
4119
+
4087
4120
  // Trim whitespace
4088
- trim: value => String(value).trim(),
4089
-
4121
+ trim: varData => {
4122
+ varData.value = String(varData.value).trim();
4123
+ varData.type = 'string';
4124
+
4125
+ },
4126
+
4090
4127
  // Convert to number
4091
- number: value => Number(value),
4092
-
4128
+ number: varData => {
4129
+ varData.value = Number(varData.value);
4130
+ varData.type = 'number';
4131
+
4132
+ },
4133
+
4093
4134
  // Convert to boolean
4094
- boolean: value => {
4095
- if (typeof value === 'boolean') return value;
4135
+ boolean: varData => {
4136
+ const value = varData.value;
4137
+ if (typeof value === 'boolean') {
4138
+ varData.type = 'boolean';
4139
+
4140
+ }
4141
+
4096
4142
  if (typeof value === 'string') {
4097
4143
  const lower = value.toLowerCase();
4098
- if (lower === 'true' || lower === '1' || lower === 'yes') return true;
4099
- if (lower === 'false' || lower === '0' || lower === 'no') return false;
4144
+ if (lower === 'true' || lower === '1' || lower === 'yes') {
4145
+ varData.value = true;
4146
+ varData.type = 'boolean';
4147
+
4148
+ }
4149
+ if (lower === 'false' || lower === '0' || lower === 'no') {
4150
+ varData.value = false;
4151
+ varData.type = 'boolean';
4152
+
4153
+ }
4100
4154
  }
4101
- return Boolean(value);
4155
+
4156
+ varData.value = Boolean(value);
4157
+ varData.type = 'boolean';
4158
+
4102
4159
  },
4103
-
4160
+
4104
4161
  // Convert to absolute value (for numbers)
4105
- abs: value => Math.abs(Number(value)),
4106
-
4162
+ abs: varData => {
4163
+ varData.value = Math.abs(Number(varData.value));
4164
+ varData.type = 'number';
4165
+
4166
+ },
4167
+
4107
4168
  // Round number
4108
- round: value => Math.round(Number(value)),
4109
-
4169
+ round: varData => {
4170
+ varData.value = Math.round(Number(varData.value));
4171
+ varData.type = 'number';
4172
+
4173
+ },
4174
+
4110
4175
  // Floor number
4111
- floor: value => Math.floor(Number(value)),
4112
-
4176
+ floor: varData => {
4177
+ varData.value = Math.floor(Number(varData.value));
4178
+ varData.type = 'number';
4179
+
4180
+ },
4181
+
4113
4182
  // Ceil number
4114
- ceil: value => Math.ceil(Number(value)),
4115
-
4183
+ ceil: varData => {
4184
+ varData.value = Math.ceil(Number(varData.value));
4185
+ varData.type = 'number';
4186
+
4187
+ },
4188
+
4116
4189
  // Default value if empty/null/undefined
4117
- default: (value, defaultValue = '') => {
4118
- return (value === null || value === undefined || value === '') ? defaultValue : value;
4190
+ default: (varData, defaultValue = '') => {
4191
+ varData.value = (varData.value === null || varData.value === undefined || varData.value === '') ? defaultValue : varData.value;
4192
+ if (typeof varData.value === 'string') {
4193
+ varData.type = 'string';
4194
+ } else if (typeof varData.value === 'number') {
4195
+ varData.type = 'number';
4196
+ } else if (typeof varData.value === 'boolean') {
4197
+ varData.type = 'boolean';
4198
+ }
4199
+
4200
+ },
4201
+
4202
+ // Extract start time from time period/time period ago as time value
4203
+ time_start: varData => {
4204
+ if (varData.type === 'time period' || varData.type === 'time period ago') {
4205
+ if (!varData.value || typeof varData.value !== 'object' || !Object.prototype.hasOwnProperty.call(varData.value, 'from')) {
4206
+ throw new Error('time_start filter requires value.from for time period types');
4207
+ }
4208
+
4209
+ varData.value = varData.value.from;
4210
+ varData.type = 'time value';
4211
+ return;
4212
+ }
4213
+
4214
+ throw new Error('time_start filter requires variable type to be \"time period\" or \"time period ago\"');
4215
+ },
4216
+
4217
+ // Extract end time from time period/time period ago as time value
4218
+ time_end: varData => {
4219
+ if (varData.type === 'time period' || varData.type === 'time period ago') {
4220
+ if (!varData.value || typeof varData.value !== 'object' || !Object.prototype.hasOwnProperty.call(varData.value, 'to')) {
4221
+ throw new Error('time_end filter requires value.from for time period types');
4222
+ }
4223
+
4224
+ varData.value = varData.value.to;
4225
+ varData.type = 'time value';
4226
+ return;
4227
+ }
4228
+
4229
+ throw new Error('time_end filter requires variable type to be \"time period\" or \"time period ago\"');
4119
4230
  }
4120
4231
  }
4121
4232
 
package/index.d.ts CHANGED
@@ -30,7 +30,7 @@ export interface ASTNode {
30
30
  [key: string]: any;
31
31
  }
32
32
 
33
- export type FilterFunction = (value: any) => any;
33
+ export type FilterFunction = (varData: VariableValue, ...args: any[]) => VariableValue | any;
34
34
 
35
35
  export interface TemplateFiltersType {
36
36
  string: FilterFunction;
@@ -46,6 +46,7 @@ export interface TemplateFiltersType {
46
46
  floor: FilterFunction;
47
47
  ceil: FilterFunction;
48
48
  default: FilterFunction;
49
+ time_start: FilterFunction;
49
50
  [key: string]: FilterFunction;
50
51
  }
51
52
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@halleyassist/rule-templater",
3
- "version": "0.0.10",
3
+ "version": "0.0.11",
4
4
  "description": "The grammar for HalleyAssist rules",
5
5
  "main": "src/RuleTemplater.production.js",
6
6
  "browser": "./dist/rule-templater.browser.js",
@@ -13,6 +13,7 @@ const VariableTypes = [
13
13
  'boolean',
14
14
  'object',
15
15
  'time period',
16
+ 'time period ago',
16
17
  'time value',
17
18
  'string array',
18
19
  'number array',
@@ -25,6 +26,7 @@ const AllowedTypeMapping = {
25
26
  'number': ['number_atom', 'math_expr'],
26
27
  'boolean': ['boolean_atom', 'boolean_expr'],
27
28
  'time period': ['time_period_atom'],
29
+ 'time period ago': ['time_period_atom'],
28
30
  'time value': ['time_value_atom', 'tod_atom'],
29
31
  'string array': ['string_array'],
30
32
  'number array': ['number_array'],
@@ -335,12 +337,12 @@ class RuleTemplate {
335
337
  throw new Error(`Variable '${varName}' not provided in variables object`);
336
338
  }
337
339
 
338
- const varData = variables[varName];
340
+ let varData = variables[varName];
339
341
  if (typeof varData !== 'object' || !varData.hasOwnProperty('value')) {
340
342
  throw new Error(`Variable '${varName}' must be an object with 'value' property`);
341
343
  }
342
-
343
- let { value, type } = varData;
344
+
345
+ varData = Object.assign({}, varData);
344
346
 
345
347
  // Require type property for all variables
346
348
  if (!varData.hasOwnProperty('type')) {
@@ -348,8 +350,8 @@ class RuleTemplate {
348
350
  }
349
351
 
350
352
  // Validate type
351
- if (!VariableTypes.includes(type)) {
352
- throw new Error(`Invalid variable type '${type}' for variable '${varName}'`);
353
+ if (!VariableTypes.includes(varData.type)) {
354
+ throw new Error(`Invalid variable type '${varData.type}' for variable '${varName}'`);
353
355
  }
354
356
 
355
357
  // Apply filters if present
@@ -358,27 +360,42 @@ class RuleTemplate {
358
360
  if (!TemplateFilters[filterName]) {
359
361
  throw new Error(`Unknown filter '${filterName}'`);
360
362
  }
361
- value = TemplateFilters[filterName](value);
363
+
364
+ TemplateFilters[filterName](varData);
362
365
  }
363
- // After applying filters, the result is already a string representation
364
- return String(value);
365
366
  }
366
-
367
- // Convert value to string representation based on type
367
+
368
+ return this._serializeVarData(varData, varName);
369
+ }
370
+
371
+ _serializeVarData(varData, varName) {
372
+ const { value, type } = varData;
373
+
374
+ if (!VariableTypes.includes(type)) {
375
+ throw new Error(`Invalid variable type '${type}' for variable '${varName}'`);
376
+ }
377
+
368
378
  if (type === 'string') {
369
- // Escape backslashes first, then quotes in string values.
370
- // Order is critical: escaping backslashes first prevents double-escaping.
371
- // E.g., "test\" becomes "test\\" then "test\\\"" (correct)
372
- // If reversed, "test\" would become "test\\"" then "test\\\\"" (incorrect)
373
379
  return `"${String(value).replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`;
374
- } else if (type === 'number') {
380
+ }
381
+
382
+ if (type === 'number') {
375
383
  return String(value);
376
- } else if (type === 'boolean') {
384
+ }
385
+
386
+ if (type === 'boolean') {
377
387
  return value ? 'true' : 'false';
378
- } else {
379
- // Default behavior - just insert the value as-is
380
- return String(value);
381
388
  }
389
+
390
+ if (type === 'time period' || type === 'time period ago') {
391
+ let ret = `${value.from} TO ${value.to}`;
392
+ if(value.ago) {
393
+ ret += ` AGO ${value.ago[0]} ${value.ago[1]}`;
394
+ }
395
+ return ret;
396
+ }
397
+
398
+ return String(value);
382
399
  }
383
400
 
384
401
  /**
@@ -13,6 +13,7 @@ const VariableTypes = [
13
13
  'boolean',
14
14
  'object',
15
15
  'time period',
16
+ 'time period ago',
16
17
  'time value',
17
18
  'string array',
18
19
  'number array',
@@ -25,6 +26,7 @@ const AllowedTypeMapping = {
25
26
  'number': ['number_atom', 'math_expr'],
26
27
  'boolean': ['boolean_atom', 'boolean_expr'],
27
28
  'time period': ['time_period_atom'],
29
+ 'time period ago': ['time_period_atom'],
28
30
  'time value': ['time_value_atom', 'tod_atom'],
29
31
  'string array': ['string_array'],
30
32
  'number array': ['number_array'],
@@ -335,12 +337,12 @@ class RuleTemplate {
335
337
  throw new Error(`Variable '${varName}' not provided in variables object`);
336
338
  }
337
339
 
338
- const varData = variables[varName];
340
+ let varData = variables[varName];
339
341
  if (typeof varData !== 'object' || !varData.hasOwnProperty('value')) {
340
342
  throw new Error(`Variable '${varName}' must be an object with 'value' property`);
341
343
  }
342
-
343
- let { value, type } = varData;
344
+
345
+ varData = Object.assign({}, varData);
344
346
 
345
347
  // Require type property for all variables
346
348
  if (!varData.hasOwnProperty('type')) {
@@ -348,8 +350,8 @@ class RuleTemplate {
348
350
  }
349
351
 
350
352
  // Validate type
351
- if (!VariableTypes.includes(type)) {
352
- throw new Error(`Invalid variable type '${type}' for variable '${varName}'`);
353
+ if (!VariableTypes.includes(varData.type)) {
354
+ throw new Error(`Invalid variable type '${varData.type}' for variable '${varName}'`);
353
355
  }
354
356
 
355
357
  // Apply filters if present
@@ -358,27 +360,42 @@ class RuleTemplate {
358
360
  if (!TemplateFilters[filterName]) {
359
361
  throw new Error(`Unknown filter '${filterName}'`);
360
362
  }
361
- value = TemplateFilters[filterName](value);
363
+
364
+ TemplateFilters[filterName](varData);
362
365
  }
363
- // After applying filters, the result is already a string representation
364
- return String(value);
365
366
  }
366
-
367
- // Convert value to string representation based on type
367
+
368
+ return this._serializeVarData(varData, varName);
369
+ }
370
+
371
+ _serializeVarData(varData, varName) {
372
+ const { value, type } = varData;
373
+
374
+ if (!VariableTypes.includes(type)) {
375
+ throw new Error(`Invalid variable type '${type}' for variable '${varName}'`);
376
+ }
377
+
368
378
  if (type === 'string') {
369
- // Escape backslashes first, then quotes in string values.
370
- // Order is critical: escaping backslashes first prevents double-escaping.
371
- // E.g., "test\" becomes "test\\" then "test\\\"" (correct)
372
- // If reversed, "test\" would become "test\\"" then "test\\\\"" (incorrect)
373
379
  return `"${String(value).replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`;
374
- } else if (type === 'number') {
380
+ }
381
+
382
+ if (type === 'number') {
375
383
  return String(value);
376
- } else if (type === 'boolean') {
384
+ }
385
+
386
+ if (type === 'boolean') {
377
387
  return value ? 'true' : 'false';
378
- } else {
379
- // Default behavior - just insert the value as-is
380
- return String(value);
381
388
  }
389
+
390
+ if (type === 'time period' || type === 'time period ago') {
391
+ let ret = `${value.from} TO ${value.to}`;
392
+ if(value.ago) {
393
+ ret += ` AGO ${value.ago[0]} ${value.ago[1]}`;
394
+ }
395
+ return ret;
396
+ }
397
+
398
+ return String(value);
382
399
  }
383
400
 
384
401
  /**
@@ -4,59 +4,153 @@ They are applied in the template syntax as ${variable|filter} or ${variable|filt
4
4
  */
5
5
  const TemplateFilters = {
6
6
  // Convert value to JSON string representation
7
- string: value => JSON.stringify(String(value)),
8
-
7
+ string: varData => {
8
+ varData.value = String(varData.value);
9
+ varData.type = 'string';
10
+
11
+ },
12
+
9
13
  // Convert to uppercase
10
- upper: value => String(value).toUpperCase(),
11
-
14
+ upper: varData => {
15
+ varData.value = String(varData.value).toUpperCase();
16
+ varData.type = 'string';
17
+
18
+ },
19
+
12
20
  // Convert to lowercase
13
- lower: value => String(value).toLowerCase(),
14
-
21
+ lower: varData => {
22
+ varData.value = String(varData.value).toLowerCase();
23
+ varData.type = 'string';
24
+
25
+ },
26
+
15
27
  // Capitalize first letter
16
- capitalize: value => {
17
- const str = String(value);
18
- return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
28
+ capitalize: varData => {
29
+ const str = String(varData.value);
30
+ varData.value = str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
31
+ varData.type = 'string';
32
+
19
33
  },
20
-
34
+
21
35
  // Convert to title case
22
- title: value => {
23
- return String(value).split(' ')
36
+ title: varData => {
37
+ varData.value = String(varData.value).split(' ')
24
38
  .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
25
39
  .join(' ');
40
+ varData.type = 'string';
41
+
26
42
  },
27
-
43
+
28
44
  // Trim whitespace
29
- trim: value => String(value).trim(),
30
-
45
+ trim: varData => {
46
+ varData.value = String(varData.value).trim();
47
+ varData.type = 'string';
48
+
49
+ },
50
+
31
51
  // Convert to number
32
- number: value => Number(value),
33
-
52
+ number: varData => {
53
+ varData.value = Number(varData.value);
54
+ varData.type = 'number';
55
+
56
+ },
57
+
34
58
  // Convert to boolean
35
- boolean: value => {
36
- if (typeof value === 'boolean') return value;
59
+ boolean: varData => {
60
+ const value = varData.value;
61
+ if (typeof value === 'boolean') {
62
+ varData.type = 'boolean';
63
+
64
+ }
65
+
37
66
  if (typeof value === 'string') {
38
67
  const lower = value.toLowerCase();
39
- if (lower === 'true' || lower === '1' || lower === 'yes') return true;
40
- if (lower === 'false' || lower === '0' || lower === 'no') return false;
68
+ if (lower === 'true' || lower === '1' || lower === 'yes') {
69
+ varData.value = true;
70
+ varData.type = 'boolean';
71
+
72
+ }
73
+ if (lower === 'false' || lower === '0' || lower === 'no') {
74
+ varData.value = false;
75
+ varData.type = 'boolean';
76
+
77
+ }
41
78
  }
42
- return Boolean(value);
79
+
80
+ varData.value = Boolean(value);
81
+ varData.type = 'boolean';
82
+
43
83
  },
44
-
84
+
45
85
  // Convert to absolute value (for numbers)
46
- abs: value => Math.abs(Number(value)),
47
-
86
+ abs: varData => {
87
+ varData.value = Math.abs(Number(varData.value));
88
+ varData.type = 'number';
89
+
90
+ },
91
+
48
92
  // Round number
49
- round: value => Math.round(Number(value)),
50
-
93
+ round: varData => {
94
+ varData.value = Math.round(Number(varData.value));
95
+ varData.type = 'number';
96
+
97
+ },
98
+
51
99
  // Floor number
52
- floor: value => Math.floor(Number(value)),
53
-
100
+ floor: varData => {
101
+ varData.value = Math.floor(Number(varData.value));
102
+ varData.type = 'number';
103
+
104
+ },
105
+
54
106
  // Ceil number
55
- ceil: value => Math.ceil(Number(value)),
56
-
107
+ ceil: varData => {
108
+ varData.value = Math.ceil(Number(varData.value));
109
+ varData.type = 'number';
110
+
111
+ },
112
+
57
113
  // Default value if empty/null/undefined
58
- default: (value, defaultValue = '') => {
59
- return (value === null || value === undefined || value === '') ? defaultValue : value;
114
+ default: (varData, defaultValue = '') => {
115
+ varData.value = (varData.value === null || varData.value === undefined || varData.value === '') ? defaultValue : varData.value;
116
+ if (typeof varData.value === 'string') {
117
+ varData.type = 'string';
118
+ } else if (typeof varData.value === 'number') {
119
+ varData.type = 'number';
120
+ } else if (typeof varData.value === 'boolean') {
121
+ varData.type = 'boolean';
122
+ }
123
+
124
+ },
125
+
126
+ // Extract start time from time period/time period ago as time value
127
+ time_start: varData => {
128
+ if (varData.type === 'time period' || varData.type === 'time period ago') {
129
+ if (!varData.value || typeof varData.value !== 'object' || !Object.prototype.hasOwnProperty.call(varData.value, 'from')) {
130
+ throw new Error('time_start filter requires value.from for time period types');
131
+ }
132
+
133
+ varData.value = varData.value.from;
134
+ varData.type = 'time value';
135
+ return;
136
+ }
137
+
138
+ throw new Error('time_start filter requires variable type to be \"time period\" or \"time period ago\"');
139
+ },
140
+
141
+ // Extract end time from time period/time period ago as time value
142
+ time_end: varData => {
143
+ if (varData.type === 'time period' || varData.type === 'time period ago') {
144
+ if (!varData.value || typeof varData.value !== 'object' || !Object.prototype.hasOwnProperty.call(varData.value, 'to')) {
145
+ throw new Error('time_end filter requires value.from for time period types');
146
+ }
147
+
148
+ varData.value = varData.value.to;
149
+ varData.type = 'time value';
150
+ return;
151
+ }
152
+
153
+ throw new Error('time_end filter requires variable type to be \"time period\" or \"time period ago\"');
60
154
  }
61
155
  }
62
156