@stackone/expressions 0.11.0 → 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
@@ -202,35 +202,13 @@ Collections, or arrays of objects, can be filtered by including a filter express
202
202
  `{{users[.age > 25].name}}` // Returns ["John"]
203
203
  ```
204
204
 
205
- ### String Interpolation
206
-
207
- To simplify strings usage, a more straightforward syntax is provided for string interpolation of variables using the `${var}` syntax.
208
-
209
- Examples:
210
-
211
- ```js
212
- // Given the context: { name: "John", age: 30 }
213
- "Hello ${name}" // Returns "Hello John"
214
- "User is ${age}" // Returns "User is 30"
215
- // You can also use JEXL inside string syntax
216
- "Status: ${age > 18 ? 'Adult' : 'Minor'}" // Returns "Status: Adult"
217
- "Age in 5 years: ${age + 5}" // Returns "Age in 5 years: 35"
218
- ```
219
-
220
- Note: If the expression is a string without any of the patterns described above, it will be returned as is.
221
-
222
- ```js
223
- // Given the context: { name: "John", age: 30 }
224
- "Hello world" // Returns "Hello world"
225
- ```
226
-
227
- ## Built-in Functions
205
+ #### Built-in Functions
228
206
 
229
207
  The expression handler provides several built-in functions you can use in your expressions:
230
208
 
231
- ### Date Functions
209
+ ##### Date Functions
232
210
 
233
- #### nextAnniversary(initialDate, format)
211
+ ###### nextAnniversary(initialDate, format)
234
212
 
235
213
  Calculates the next anniversary date for a given date.
236
214
 
@@ -248,7 +226,7 @@ Calculates the next anniversary date for a given date.
248
226
  // Returns Date object for February 15, 2026 (next year's anniversary)
249
227
  ```
250
228
 
251
- #### yearsElapsed(startDate, format, endDate?)
229
+ ###### yearsElapsed(startDate, format, endDate?)
252
230
 
253
231
  Calculates the number of complete years elapsed between two dates.
254
232
 
@@ -265,7 +243,7 @@ Calculates the number of complete years elapsed between two dates.
265
243
  "{{yearsElapsed('01/01/2015', 'dd/MM/yyyy')}}" // Returns 10
266
244
  ```
267
245
 
268
- #### hasPassed(date, format, yearsToAdd?)
246
+ ###### hasPassed(date, format, yearsToAdd?)
269
247
 
270
248
  Determines if a given date (optionally with added years) has passed.
271
249
 
@@ -286,14 +264,14 @@ Determines if a given date (optionally with added years) has passed.
286
264
  // Returns true if April 10, 2025 is after January 1, 2025
287
265
  ```
288
266
 
289
- ### Array Functions
267
+ ##### Array Functions
290
268
 
291
- #### includes(array, value)
269
+ ###### includes(array, value)
292
270
 
293
- Checks if an array includes a specific value.
271
+ Checks if an array includes a specific value or all values from another array.
294
272
 
295
273
  - `array` (array): The array to check
296
- - `value` (any): The value to search for in the array
274
+ - `value` (any | array): The value(s) to search for in the array
297
275
  - Returns: boolean
298
276
 
299
277
  ```js
@@ -306,4 +284,84 @@ Checks if an array includes a specific value.
306
284
  // Can be used with context variables
307
285
  "{{includes($.allowedRoles, $.currentUser.role)}}"
308
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
+
309
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.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=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)}`}}},p=(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.addBinaryOp("??",0,((r,t)=>e.isMissing(r)?t:e.isMissing(t)?r:r??t)),t})(),d=(e=>{if(e.startsWith("{{")&&e.endsWith("}}"))return e.slice(2,-2)})(n),p=(e=>e.replace(/\$\./g,"").trim())(d??n),v=u(p,s,c);if(v)return v;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(!v&&!d)return r;const f=o(c,p);if(!f||"."===p)throw new Error(`Invalid expression: "${n}"`);try{return f.evalSync(s)}catch{return}},v=(e,r)=>{try{return p(e,r)}catch{return null}},f=(e,r)=>{const t=e=>Array.isArray(e)?e.map(t):"object"==typeof e&&null!==e?f(e,r):"string"==typeof e?v(e,r):e;return Object.fromEntries(Object.entries(e).map((([e,r])=>[e,t(r)])))};exports.evaluate=p,exports.isValidExpression=e=>{try{return p(e),!0}catch{return!1}},exports.safeEvaluate=v,exports.safeEvaluateRecord=f;
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.11.0",
3
+ "version": "0.12.0",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.es.mjs",