@stackone/expressions 0.10.2 → 0.12.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/README.md CHANGED
@@ -154,6 +154,7 @@ This kind of expression is enclosed in double brackets `{{expression}}`. It supp
154
154
  | `<=` | Less than or equal |
155
155
  | `in` | Element of string or array |
156
156
  | `? :` | Ternary operator |
157
+ | `??` | Nullish coalescing operator |
157
158
 
158
159
  Examples:
159
160
 
@@ -165,6 +166,7 @@ Examples:
165
166
  '{{x == 10 ? "yes" : "no"}}' // Returns "yes"
166
167
  '{{x in [1, 2, 3]}}' // Returns false
167
168
  '{{x != y}}' // Returns true
169
+ '{{x ?? y}}' // Returns 10
168
170
  ```
169
171
 
170
172
  #### Identifiers
@@ -200,35 +202,13 @@ Collections, or arrays of objects, can be filtered by including a filter express
200
202
  `{{users[.age > 25].name}}` // Returns ["John"]
201
203
  ```
202
204
 
203
- ### String Interpolation
204
-
205
- To simplify strings usage, a more straightforward syntax is provided for string interpolation of variables using the `${var}` syntax.
206
-
207
- Examples:
208
-
209
- ```js
210
- // Given the context: { name: "John", age: 30 }
211
- "Hello ${name}" // Returns "Hello John"
212
- "User is ${age}" // Returns "User is 30"
213
- // You can also use JEXL inside string syntax
214
- "Status: ${age > 18 ? 'Adult' : 'Minor'}" // Returns "Status: Adult"
215
- "Age in 5 years: ${age + 5}" // Returns "Age in 5 years: 35"
216
- ```
217
-
218
- Note: If the expression is a string without any of the patterns described above, it will be returned as is.
219
-
220
- ```js
221
- // Given the context: { name: "John", age: 30 }
222
- "Hello world" // Returns "Hello world"
223
- ```
224
-
225
- ## Built-in Functions
205
+ #### Built-in Functions
226
206
 
227
207
  The expression handler provides several built-in functions you can use in your expressions:
228
208
 
229
- ### Date Functions
209
+ ##### Date Functions
230
210
 
231
- #### nextAnniversary(initialDate, format)
211
+ ###### nextAnniversary(initialDate, format)
232
212
 
233
213
  Calculates the next anniversary date for a given date.
234
214
 
@@ -246,7 +226,7 @@ Calculates the next anniversary date for a given date.
246
226
  // Returns Date object for February 15, 2026 (next year's anniversary)
247
227
  ```
248
228
 
249
- #### yearsElapsed(startDate, format, endDate?)
229
+ ###### yearsElapsed(startDate, format, endDate?)
250
230
 
251
231
  Calculates the number of complete years elapsed between two dates.
252
232
 
@@ -263,7 +243,7 @@ Calculates the number of complete years elapsed between two dates.
263
243
  "{{yearsElapsed('01/01/2015', 'dd/MM/yyyy')}}" // Returns 10
264
244
  ```
265
245
 
266
- #### hasPassed(date, format, yearsToAdd?)
246
+ ###### hasPassed(date, format, yearsToAdd?)
267
247
 
268
248
  Determines if a given date (optionally with added years) has passed.
269
249
 
@@ -284,14 +264,14 @@ Determines if a given date (optionally with added years) has passed.
284
264
  // Returns true if April 10, 2025 is after January 1, 2025
285
265
  ```
286
266
 
287
- ### Array Functions
267
+ ##### Array Functions
288
268
 
289
- #### includes(array, value)
269
+ ###### includes(array, value)
290
270
 
291
- Checks if an array includes a specific value.
271
+ Checks if an array includes a specific value or all values from another array.
292
272
 
293
273
  - `array` (array): The array to check
294
- - `value` (any): The value to search for in the array
274
+ - `value` (any | array): The value(s) to search for in the array
295
275
  - Returns: boolean
296
276
 
297
277
  ```js
@@ -304,4 +284,84 @@ Checks if an array includes a specific value.
304
284
  // Can be used with context variables
305
285
  "{{includes($.allowedRoles, $.currentUser.role)}}"
306
286
 
287
+ // Check if an array includes all of values of the second array
288
+ "{{includes([1, 2, 3, 4], [2, 4])}}" // Returns true
289
+ ```
290
+
291
+ ###### includesSome(array, value)
292
+
293
+ Checks if an array includes at least one value from another array or a single value.
294
+
295
+ - `array` (array): The array to check
296
+ - `value` (any | array): The value(s) to search for in the array
297
+ - Returns: boolean
298
+
299
+ ```js
300
+ // Check if an array includes at least one value
301
+ "{{includesSome([1, 2, 3], 2)}}" // Returns true
302
+
303
+ // Check if an array includes at least one value from another array
304
+ "{{includesSome([1, 2, 3], [2, 5])}}" // Returns true
305
+
306
+ // Check if an array includes at least one value from another array (none match)
307
+ "{{includesSome([1, 2, 3], [4, 5])}}" // Returns false
308
+
309
+ // Can be used with context variables
310
+ "{{includesSome($.allowedRoles, $.currentUser.roles)}}"
311
+ ```
312
+
313
+ ##### Object Functions
314
+
315
+ ###### keys(object)
316
+
317
+ Returns the keys of an object as an array. If the input is not an object, null, or undefined, returns an empty array.
318
+
319
+ - `object` (object): The object to get keys from
320
+ - Returns: array of keys (string[])
321
+
322
+ ```js
323
+ // Get keys from an object
324
+ "{{keys({ a: 1, b: 2 })}}" // Returns ["a", "b"]
325
+
326
+ // Get keys from a context variable
327
+ "{{keys($.user)}}"
328
+ ```
329
+
330
+ ###### values(object)
331
+
332
+ Returns the values of an object as an array. If the input is not an object, null, or undefined, returns an empty array.
333
+
334
+ - `object` (object): The object to get values from
335
+ - Returns: array of values (any[])
336
+
337
+ ```js
338
+ // Get values from an object
339
+ "{{values({ a: 1, b: 2 })}}" // Returns [1, 2]
340
+
341
+ // Get values from a context variable
342
+ "{{values($.user)}}"
343
+ ```
344
+
307
345
  For more information on the JEXL syntax, refer to the [JEXL Syntax documentation](https://commons.apache.org/proper/commons-jexl/reference/syntax.html).
346
+
347
+ ### String Interpolation
348
+
349
+ To simplify strings usage, a more straightforward syntax is provided for string interpolation of variables using the `${var}` syntax.
350
+
351
+ Examples:
352
+
353
+ ```js
354
+ // Given the context: { name: "John", age: 30 }
355
+ "Hello ${name}" // Returns "Hello John"
356
+ "User is ${age}" // Returns "User is 30"
357
+ // You can also use JEXL inside string syntax
358
+ "Status: ${age > 18 ? 'Adult' : 'Minor'}" // Returns "Status: Adult"
359
+ "Age in 5 years: ${age + 5}" // Returns "Age in 5 years: 35"
360
+ ```
361
+
362
+ Note: If the expression is a string without any of the patterns described above, it will be returned as is.
363
+
364
+ ```js
365
+ // Given the context: { name: "John", age: 30 }
366
+ "Hello world" // Returns "Hello world"
367
+ ```
package/dist/index.es.mjs CHANGED
@@ -1 +1 @@
1
- import{calculateNextAnniversary as r,calculateYearsElapsed as t,dateHasPassed as e,isMissing as a,notMissing as n,isObject as o}from"@stackone/utils";import*as i from"jexl";import s from"jsonpath";import l from"lodash.get";const c=/\${([^}]+)}/g,u=(r,t)=>{try{return r.compile(t)}catch{return null}},d=(r,t)=>{s.parse(r);const e=s.query(t,r);if(0!==e.length)return 1===e.length?e[0]:e},y=(r,t,e)=>r.replace(t,String(e)),h=(r,t,e)=>{const a=r.match(c);if(!a)return;const o=a.length,i=new Array(o);for(let r=0;r<o;r++)i[r]={toReplace:a[r],path:a[r].slice(2,-1)};return i.reduce(((r,a)=>((r,t,e,a)=>{const o=r.path.trim();if(!o)return a;try{const i=u(t,o);if(i){const t=i.evalSync(e);if(void 0!==t)return y(a,r.toReplace,t)}const s=l(e,o);return n(s)?y(a,r.toReplace,s):a}catch{return a}})(a,e,t,r)),String(r))},p=(r,t,e,a)=>{if(Array.isArray(t)){const r=parseInt(e,10);if(!isNaN(r)&&(r<0||r>=t.length))return{value:void 0,error:`Invalid array index '${e}' at '${a}'`,availableKeys:t.map(((r,t)=>t.toString()))}}return o(t)?{value:void 0,error:`Key '${e}' not found at '${a}'`,availableKeys:Object.keys(t)}:{value:void 0,error:`${r} at '${a}'`,availableKeys:[]}},v=(r,t)=>{const e=/(\.\.)|(\[\*\])|(\[\?\()|(\[\d+:\d+\])/;try{if(e.test(r)){const e=d(r,t);return void 0===e?{value:void 0,error:`Invalid or empty JSONPath: '${r}'`}:{value:e}}const a=r.match(/\$|(?:\['([^']+)'\])|(?:\["([^"]+)"\])|\[\d+\]|[^[\].]+/g)||[];return((r,t)=>{if("$"!==r[0])return{value:void 0,error:"JSON path must start with $"};let e=t,a="$";for(let t=1;t<r.length;t++){const n=r[t],i=n.startsWith("[")?`${a}${n}`:`${a}.${n}`;if(Array.isArray(e)){const r=parseInt(n,10);if(isNaN(r)||r<0||r>=e.length)return p("Invalid array index",e,n,a);e=e[r],a=i}else{if(!o(e))return{value:void 0,error:`Cannot access '${n}' at '${a}' - parent is not an object`,availableKeys:[]};if(!Object.prototype.hasOwnProperty.call(e,n))return p("Key not found",e,n,a);e=e[n],a=i}}return{value:e}})(a.map((r=>r.replace(/^\['([^']+)'\]$/,"$1").replace(/^\["([^"]+)"\]$/,"$1").replace(/^\[(\d+)\]$/,"$1"))),t)}catch(r){return{value:void 0,error:`Something went wrong with evaluation of JSON path: ${String(r)}`}}},f=(n,s,l)=>{const c=n?.trim();if(null==c||""===c)throw new Error("Empty expression");const y=o(s)?s:{},p=((n=()=>new i.Jexl)=>{const o=n();return o.addFunction("nextAnniversary",((t,e)=>r({initialDate:t,format:e}))),o.addFunction("yearsElapsed",((r,e,a)=>t({startDate:r,endDate:a,format:e}))),o.addFunction("hasPassed",((r,t,a)=>e({date:r,yearsToAdd:a,format:t}))),o.addFunction("includes",((r,t)=>!(a(r)||!Array.isArray(r)||0===r.length||a(t))&&r.includes(t))),o})(),f=(r=>{if(r.startsWith("{{")&&r.endsWith("}}"))return r.slice(2,-2)})(c),$=(r=>r.replace(/\$\./g,"").trim())(f??c),m=h($,y,p);if(m)return m;if(!f&&c.startsWith("$"))return l?.incrementalJsonPath?((r,t)=>{const e=v(r,t);if(e.error)throw new Error(`${e.error}${e.availableKeys?.length?`. Available keys: ${e.availableKeys.join(", ")}`:""}`);return e.value})(c,y):((r,t)=>{try{return d(r,t)}catch{throw new Error(`Invalid JSON path: "${r}"`)}})(c,y);if(!m&&!f)return n;const g=u(p,$);if(!g||"."===$)throw new Error(`Invalid expression: "${c}"`);try{return g.evalSync(y)}catch{return}},$=(r,t)=>{try{return f(r,t)}catch{return null}},m=(r,t)=>{const e=r=>Array.isArray(r)?r.map(e):"object"==typeof r&&null!==r?m(r,t):"string"==typeof r?$(r,t):r;return Object.fromEntries(Object.entries(r).map((([r,t])=>[r,e(t)])))},g=r=>{try{return f(r),!0}catch{return!1}};export{f as evaluate,g as isValidExpression,$ as safeEvaluate,m as safeEvaluateRecord};
1
+ import{calculateNextAnniversary as r,calculateYearsElapsed as t,dateHasPassed as e,isMissing as a,notMissing as n,isObject as o}from"@stackone/utils";import*as i from"jexl";import s from"jsonpath";import l from"lodash.get";const c=/\${([^}]+)}/g,u=(r,t)=>{try{return r.compile(t)}catch{return null}},y=(r,t)=>{s.parse(r);const e=s.query(t,r);if(0!==e.length)return 1===e.length?e[0]:e},d=(r,t,e)=>r.replace(t,String(e)),p=(r,t,e)=>{const a=r.match(c);if(!a)return;const o=a.length,i=new Array(o);for(let r=0;r<o;r++)i[r]={toReplace:a[r],path:a[r].slice(2,-1)};return i.reduce(((r,a)=>((r,t,e,a)=>{const o=r.path.trim();if(!o)return a;try{const i=u(t,o);if(i){const t=i.evalSync(e);if(void 0!==t)return d(a,r.toReplace,t)}const s=l(e,o);return n(s)?d(a,r.toReplace,s):a}catch{return a}})(a,e,t,r)),String(r))},h=(r,t,e,a)=>{if(Array.isArray(t)){const r=parseInt(e,10);if(!isNaN(r)&&(r<0||r>=t.length))return{value:void 0,error:`Invalid array index '${e}' at '${a}'`,availableKeys:t.map(((r,t)=>t.toString()))}}return o(t)?{value:void 0,error:`Key '${e}' not found at '${a}'`,availableKeys:Object.keys(t)}:{value:void 0,error:`${r} at '${a}'`,availableKeys:[]}},v=(r,t)=>{const e=/(\.\.)|(\[\*\])|(\[\?\()|(\[\d+:\d+\])/;try{if(e.test(r)){const e=y(r,t);return void 0===e?{value:void 0,error:`Invalid or empty JSONPath: '${r}'`}:{value:e}}const a=r.match(/\$|(?:\['([^']+)'\])|(?:\["([^"]+)"\])|\[\d+\]|[^[\].]+/g)||[];return((r,t)=>{if("$"!==r[0])return{value:void 0,error:"JSON path must start with $"};let e=t,a="$";for(let t=1;t<r.length;t++){const n=r[t],i=n.startsWith("[")?`${a}${n}`:`${a}.${n}`;if(Array.isArray(e)){const r=parseInt(n,10);if(isNaN(r)||r<0||r>=e.length)return h("Invalid array index",e,n,a);e=e[r],a=i}else{if(!o(e))return{value:void 0,error:`Cannot access '${n}' at '${a}' - parent is not an object`,availableKeys:[]};if(!Object.prototype.hasOwnProperty.call(e,n))return h("Key not found",e,n,a);e=e[n],a=i}}return{value:e}})(a.map((r=>r.replace(/^\['([^']+)'\]$/,"$1").replace(/^\["([^"]+)"\]$/,"$1").replace(/^\[(\d+)\]$/,"$1"))),t)}catch(r){return{value:void 0,error:`Something went wrong with evaluation of JSON path: ${String(r)}`}}},f=(n,s,l)=>{const c=n?.trim();if(null==c||""===c)throw new Error("Empty expression");const d=o(s)?s:{},h=((n=()=>new i.Jexl)=>{const o=n();return o.addFunction("nextAnniversary",((t,e)=>r({initialDate:t,format:e}))),o.addFunction("yearsElapsed",((r,e,a)=>t({startDate:r,endDate:a,format:e}))),o.addFunction("hasPassed",((r,t,a)=>e({date:r,yearsToAdd:a,format:t}))),o.addFunction("includes",((r,t)=>!(a(r)||!Array.isArray(r)||0===r.length||a(t))&&(Array.isArray(t)?t.every((t=>r.includes(t))):r.includes(t)))),o.addFunction("includesSome",((r,t)=>!(a(r)||!Array.isArray(r)||0===r.length||a(t))&&(Array.isArray(t)?t.some((t=>r.includes(t))):r.includes(t)))),o.addFunction("keys",(r=>a(r)||"object"!=typeof r||Array.isArray(r)?[]:Object.keys(r))),o.addFunction("values",(r=>a(r)||"object"!=typeof r||Array.isArray(r)?[]:Object.values(r))),o.addBinaryOp("??",0,((r,t)=>a(r)?t:a(t)?r:r??t)),o})(),f=(r=>{if(r.startsWith("{{")&&r.endsWith("}}"))return r.slice(2,-2)})(c),$=(r=>r.replace(/\$\./g,"").trim())(f??c),m=p($,d,h);if(m)return m;if(!f&&c.startsWith("$"))return l?.incrementalJsonPath?((r,t)=>{const e=v(r,t);if(e.error)throw new Error(`${e.error}${e.availableKeys?.length?`. Available keys: ${e.availableKeys.join(", ")}`:""}`);return e.value})(c,d):((r,t)=>{try{return y(r,t)}catch{throw new Error(`Invalid JSON path: "${r}"`)}})(c,d);if(!m&&!f)return n;const A=u(h,$);if(!A||"."===$)throw new Error(`Invalid expression: "${c}"`);try{return A.evalSync(d)}catch{return}},$=(r,t)=>{try{return f(r,t)}catch{return null}},m=(r,t)=>{const e=r=>Array.isArray(r)?r.map(e):"object"==typeof r&&null!==r?m(r,t):"string"==typeof r?$(r,t):r;return Object.fromEntries(Object.entries(r).map((([r,t])=>[r,e(t)])))},A=r=>{try{return f(r),!0}catch{return!1}};export{f as evaluate,A as isValidExpression,$ as safeEvaluate,m as safeEvaluateRecord};
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- "use strict";var e=require("@stackone/utils"),r=require("jexl"),t=require("jsonpath"),a=require("lodash.get");function n(e){var r=Object.create(null);return e&&Object.keys(e).forEach((function(t){if("default"!==t){var a=Object.getOwnPropertyDescriptor(e,t);Object.defineProperty(r,t,a.get?a:{enumerable:!0,get:function(){return e[t]}})}})),r.default=e,Object.freeze(r)}var i=n(r);const s=/\${([^}]+)}/g,o=(e,r)=>{try{return e.compile(r)}catch{return null}},l=(e,r)=>{t.parse(e);const a=t.query(r,e);if(0!==a.length)return 1===a.length?a[0]:a},c=(e,r,t)=>e.replace(r,String(t)),u=(r,t,n)=>{const i=r.match(s);if(!i)return;const l=i.length,u=new Array(l);for(let e=0;e<l;e++)u[e]={toReplace:i[e],path:i[e].slice(2,-1)};return u.reduce(((r,i)=>((r,t,n,i)=>{const s=r.path.trim();if(!s)return i;try{const l=o(t,s);if(l){const e=l.evalSync(n);if(void 0!==e)return c(i,r.toReplace,e)}const u=a(n,s);return e.notMissing(u)?c(i,r.toReplace,u):i}catch{return i}})(i,n,t,r)),String(r))},d=(r,t,a,n)=>{if(Array.isArray(t)){const e=parseInt(a,10);if(!isNaN(e)&&(e<0||e>=t.length))return{value:void 0,error:`Invalid array index '${a}' at '${n}'`,availableKeys:t.map(((e,r)=>r.toString()))}}return e.isObject(t)?{value:void 0,error:`Key '${a}' not found at '${n}'`,availableKeys:Object.keys(t)}:{value:void 0,error:`${r} at '${n}'`,availableKeys:[]}},y=(r,t)=>{const a=/(\.\.)|(\[\*\])|(\[\?\()|(\[\d+:\d+\])/;try{if(a.test(r)){const e=l(r,t);return void 0===e?{value:void 0,error:`Invalid or empty JSONPath: '${r}'`}:{value:e}}const n=r.match(/\$|(?:\['([^']+)'\])|(?:\["([^"]+)"\])|\[\d+\]|[^[\].]+/g)||[];return((r,t)=>{if("$"!==r[0])return{value:void 0,error:"JSON path must start with $"};let a=t,n="$";for(let t=1;t<r.length;t++){const i=r[t],s=i.startsWith("[")?`${n}${i}`:`${n}.${i}`;if(Array.isArray(a)){const e=parseInt(i,10);if(isNaN(e)||e<0||e>=a.length)return d("Invalid array index",a,i,n);a=a[e],n=s}else{if(!e.isObject(a))return{value:void 0,error:`Cannot access '${i}' at '${n}' - parent is not an object`,availableKeys:[]};if(!Object.prototype.hasOwnProperty.call(a,i))return d("Key not found",a,i,n);a=a[i],n=s}}return{value:a}})(n.map((e=>e.replace(/^\['([^']+)'\]$/,"$1").replace(/^\["([^"]+)"\]$/,"$1").replace(/^\[(\d+)\]$/,"$1"))),t)}catch(e){return{value:void 0,error:`Something went wrong with evaluation of JSON path: ${String(e)}`}}},v=(r,t,a)=>{const n=r?.trim();if(null==n||""===n)throw new Error("Empty expression");const s=e.isObject(t)?t:{},c=((r=()=>new i.Jexl)=>{const t=r();return t.addFunction("nextAnniversary",((r,t)=>e.calculateNextAnniversary({initialDate:r,format:t}))),t.addFunction("yearsElapsed",((r,t,a)=>e.calculateYearsElapsed({startDate:r,endDate:a,format:t}))),t.addFunction("hasPassed",((r,t,a)=>e.dateHasPassed({date:r,yearsToAdd:a,format:t}))),t.addFunction("includes",((r,t)=>!(e.isMissing(r)||!Array.isArray(r)||0===r.length||e.isMissing(t))&&r.includes(t))),t})(),d=(e=>{if(e.startsWith("{{")&&e.endsWith("}}"))return e.slice(2,-2)})(n),v=(e=>e.replace(/\$\./g,"").trim())(d??n),f=u(v,s,c);if(f)return f;if(!d&&n.startsWith("$"))return a?.incrementalJsonPath?((e,r)=>{const t=y(e,r);if(t.error)throw new Error(`${t.error}${t.availableKeys?.length?`. Available keys: ${t.availableKeys.join(", ")}`:""}`);return t.value})(n,s):((e,r)=>{try{return l(e,r)}catch{throw new Error(`Invalid JSON path: "${e}"`)}})(n,s);if(!f&&!d)return r;const p=o(c,v);if(!p||"."===v)throw new Error(`Invalid expression: "${n}"`);try{return p.evalSync(s)}catch{return}},f=(e,r)=>{try{return v(e,r)}catch{return null}},p=(e,r)=>{const t=e=>Array.isArray(e)?e.map(t):"object"==typeof e&&null!==e?p(e,r):"string"==typeof e?f(e,r):e;return Object.fromEntries(Object.entries(e).map((([e,r])=>[e,t(r)])))};exports.evaluate=v,exports.isValidExpression=e=>{try{return v(e),!0}catch{return!1}},exports.safeEvaluate=f,exports.safeEvaluateRecord=p;
1
+ "use strict";var r=require("@stackone/utils"),e=require("jexl"),t=require("jsonpath"),a=require("lodash.get");function n(r){var e=Object.create(null);return r&&Object.keys(r).forEach((function(t){if("default"!==t){var a=Object.getOwnPropertyDescriptor(r,t);Object.defineProperty(e,t,a.get?a:{enumerable:!0,get:function(){return r[t]}})}})),e.default=r,Object.freeze(e)}var i=n(e);const s=/\${([^}]+)}/g,o=(r,e)=>{try{return r.compile(e)}catch{return null}},c=(r,e)=>{t.parse(r);const a=t.query(e,r);if(0!==a.length)return 1===a.length?a[0]:a},l=(r,e,t)=>r.replace(e,String(t)),u=(e,t,n)=>{const i=e.match(s);if(!i)return;const c=i.length,u=new Array(c);for(let r=0;r<c;r++)u[r]={toReplace:i[r],path:i[r].slice(2,-1)};return u.reduce(((e,i)=>((e,t,n,i)=>{const s=e.path.trim();if(!s)return i;try{const c=o(t,s);if(c){const r=c.evalSync(n);if(void 0!==r)return l(i,e.toReplace,r)}const u=a(n,s);return r.notMissing(u)?l(i,e.toReplace,u):i}catch{return i}})(i,n,t,e)),String(e))},d=(e,t,a,n)=>{if(Array.isArray(t)){const r=parseInt(a,10);if(!isNaN(r)&&(r<0||r>=t.length))return{value:void 0,error:`Invalid array index '${a}' at '${n}'`,availableKeys:t.map(((r,e)=>e.toString()))}}return r.isObject(t)?{value:void 0,error:`Key '${a}' not found at '${n}'`,availableKeys:Object.keys(t)}:{value:void 0,error:`${e} at '${n}'`,availableKeys:[]}},y=(e,t)=>{const a=/(\.\.)|(\[\*\])|(\[\?\()|(\[\d+:\d+\])/;try{if(a.test(e)){const r=c(e,t);return void 0===r?{value:void 0,error:`Invalid or empty JSONPath: '${e}'`}:{value:r}}const n=e.match(/\$|(?:\['([^']+)'\])|(?:\["([^"]+)"\])|\[\d+\]|[^[\].]+/g)||[];return((e,t)=>{if("$"!==e[0])return{value:void 0,error:"JSON path must start with $"};let a=t,n="$";for(let t=1;t<e.length;t++){const i=e[t],s=i.startsWith("[")?`${n}${i}`:`${n}.${i}`;if(Array.isArray(a)){const r=parseInt(i,10);if(isNaN(r)||r<0||r>=a.length)return d("Invalid array index",a,i,n);a=a[r],n=s}else{if(!r.isObject(a))return{value:void 0,error:`Cannot access '${i}' at '${n}' - parent is not an object`,availableKeys:[]};if(!Object.prototype.hasOwnProperty.call(a,i))return d("Key not found",a,i,n);a=a[i],n=s}}return{value:a}})(n.map((r=>r.replace(/^\['([^']+)'\]$/,"$1").replace(/^\["([^"]+)"\]$/,"$1").replace(/^\[(\d+)\]$/,"$1"))),t)}catch(r){return{value:void 0,error:`Something went wrong with evaluation of JSON path: ${String(r)}`}}},v=(e,t,a)=>{const n=e?.trim();if(null==n||""===n)throw new Error("Empty expression");const s=r.isObject(t)?t:{},l=((e=()=>new i.Jexl)=>{const t=e();return t.addFunction("nextAnniversary",((e,t)=>r.calculateNextAnniversary({initialDate:e,format:t}))),t.addFunction("yearsElapsed",((e,t,a)=>r.calculateYearsElapsed({startDate:e,endDate:a,format:t}))),t.addFunction("hasPassed",((e,t,a)=>r.dateHasPassed({date:e,yearsToAdd:a,format:t}))),t.addFunction("includes",((e,t)=>!(r.isMissing(e)||!Array.isArray(e)||0===e.length||r.isMissing(t))&&(Array.isArray(t)?t.every((r=>e.includes(r))):e.includes(t)))),t.addFunction("includesSome",((e,t)=>!(r.isMissing(e)||!Array.isArray(e)||0===e.length||r.isMissing(t))&&(Array.isArray(t)?t.some((r=>e.includes(r))):e.includes(t)))),t.addFunction("keys",(e=>r.isMissing(e)||"object"!=typeof e||Array.isArray(e)?[]:Object.keys(e))),t.addFunction("values",(e=>r.isMissing(e)||"object"!=typeof e||Array.isArray(e)?[]:Object.values(e))),t.addBinaryOp("??",0,((e,t)=>r.isMissing(e)?t:r.isMissing(t)?e:e??t)),t})(),d=(r=>{if(r.startsWith("{{")&&r.endsWith("}}"))return r.slice(2,-2)})(n),v=(r=>r.replace(/\$\./g,"").trim())(d??n),p=u(v,s,l);if(p)return p;if(!d&&n.startsWith("$"))return a?.incrementalJsonPath?((r,e)=>{const t=y(r,e);if(t.error)throw new Error(`${t.error}${t.availableKeys?.length?`. Available keys: ${t.availableKeys.join(", ")}`:""}`);return t.value})(n,s):((r,e)=>{try{return c(r,e)}catch{throw new Error(`Invalid JSON path: "${r}"`)}})(n,s);if(!p&&!d)return e;const f=o(l,v);if(!f||"."===v)throw new Error(`Invalid expression: "${n}"`);try{return f.evalSync(s)}catch{return}},p=(r,e)=>{try{return v(r,e)}catch{return null}},f=(r,e)=>{const t=r=>Array.isArray(r)?r.map(t):"object"==typeof r&&null!==r?f(r,e):"string"==typeof r?p(r,e):r;return Object.fromEntries(Object.entries(r).map((([r,e])=>[r,t(e)])))};exports.evaluate=v,exports.isValidExpression=r=>{try{return v(r),!0}catch{return!1}},exports.safeEvaluate=p,exports.safeEvaluateRecord=f;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stackone/expressions",
3
- "version": "0.10.2",
3
+ "version": "0.12.0",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.es.mjs",