@stackone/expressions 0.11.0 → 0.13.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,25 @@ 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
+ ###### now()
290
268
 
291
- #### includes(array, value)
269
+ Returns the current date and time.
292
270
 
293
- Checks if an array includes a specific value.
271
+ - Returns: string (ISO 8601 format)
272
+
273
+ ```js
274
+ // Get the current date and time
275
+ "{{now()}}" // Returns "2025-04-10T12:00:00.000Z" (example)
276
+ ```
277
+
278
+ ##### Array Functions
279
+
280
+ ###### includes(array, value)
281
+
282
+ Checks if an array includes a specific value or all values from another array.
294
283
 
295
284
  - `array` (array): The array to check
296
- - `value` (any): The value to search for in the array
285
+ - `value` (any | array): The value(s) to search for in the array
297
286
  - Returns: boolean
298
287
 
299
288
  ```js
@@ -306,4 +295,118 @@ Checks if an array includes a specific value.
306
295
  // Can be used with context variables
307
296
  "{{includes($.allowedRoles, $.currentUser.role)}}"
308
297
 
298
+ // Check if an array includes all of values of the second array
299
+ "{{includes([1, 2, 3, 4], [2, 4])}}" // Returns true
300
+ ```
301
+
302
+ ###### includesSome(array, value)
303
+
304
+ Checks if an array includes at least one value from another array or a single value.
305
+
306
+ - `array` (array): The array to check
307
+ - `value` (any | array): The value(s) to search for in the array
308
+ - Returns: boolean
309
+
310
+ ```js
311
+ // Check if an array includes at least one value
312
+ "{{includesSome([1, 2, 3], 2)}}" // Returns true
313
+
314
+ // Check if an array includes at least one value from another array
315
+ "{{includesSome([1, 2, 3], [2, 5])}}" // Returns true
316
+
317
+ // Check if an array includes at least one value from another array (none match)
318
+ "{{includesSome([1, 2, 3], [4, 5])}}" // Returns false
319
+
320
+ // Can be used with context variables
321
+ "{{includesSome($.allowedRoles, $.currentUser.roles)}}"
322
+ ```
323
+
324
+ ##### Object Functions
325
+
326
+ ###### present(object)
327
+
328
+ Checks if an object is present (not null or undefined).
329
+
330
+ - `object` (object): The object to check
331
+ - Returns: boolean
332
+
333
+ ```js
334
+ // Check if an object is present
335
+ "{{present({})}}" // Returns true
336
+ "{{present(null)}}" // Returns false
337
+ "{{present(undefined)}}" // Returns false
338
+ "{{present('string')}}" // Returns true
339
+ "{{present(0)}}" // Returns true
340
+ "{{present([])}}" // Returns true
341
+ ```
342
+
343
+ ###### missing(object)
344
+
345
+ Checks if an object is missing (null or undefined).
346
+
347
+ - `object` (object): The object to check
348
+ - Returns: boolean
349
+
350
+ ```js
351
+ // Check if an object is not present
352
+ "{{missing({})}}" // Returns false
353
+ "{{missing(null)}}" // Returns true
354
+ "{{missing(undefined)}}" // Returns true
355
+ "{{missing('string')}}" // Returns false
356
+ "{{missing(0)}}" // Returns false
357
+ "{{missing([])}}" // Returns false
358
+ ```
359
+
360
+ ###### keys(object)
361
+
362
+ Returns the keys of an object as an array. If the input is not an object, null, or undefined, returns an empty array.
363
+
364
+ - `object` (object): The object to get keys from
365
+ - Returns: array of keys (string[])
366
+
367
+ ```js
368
+ // Get keys from an object
369
+ "{{keys({ a: 1, b: 2 })}}" // Returns ["a", "b"]
370
+
371
+ // Get keys from a context variable
372
+ "{{keys($.user)}}"
373
+ ```
374
+
375
+ ###### values(object)
376
+
377
+ Returns the values of an object as an array. If the input is not an object, null, or undefined, returns an empty array.
378
+
379
+ - `object` (object): The object to get values from
380
+ - Returns: array of values (any[])
381
+
382
+ ```js
383
+ // Get values from an object
384
+ "{{values({ a: 1, b: 2 })}}" // Returns [1, 2]
385
+
386
+ // Get values from a context variable
387
+ "{{values($.user)}}"
388
+ ```
389
+
309
390
  For more information on the JEXL syntax, refer to the [JEXL Syntax documentation](https://commons.apache.org/proper/commons-jexl/reference/syntax.html).
391
+
392
+ ### String Interpolation
393
+
394
+ To simplify strings usage, a more straightforward syntax is provided for string interpolation of variables using the `${var}` syntax.
395
+
396
+ Examples:
397
+
398
+ ```js
399
+ // Given the context: { name: "John", age: 30 }
400
+ "Hello ${name}" // Returns "Hello John"
401
+ "User is ${age}" // Returns "User is 30"
402
+ // You can also use JEXL inside string syntax
403
+ "Status: ${age > 18 ? 'Adult' : 'Minor'}" // Returns "Status: Adult"
404
+ "Age in 5 years: ${age + 5}" // Returns "Age in 5 years: 35"
405
+ ```
406
+
407
+ Note: If the expression is a string without any of the patterns described above, it will be returned as is.
408
+
409
+ ```js
410
+ // Given the context: { name: "John", age: 30 }
411
+ "Hello world" // Returns "Hello world"
412
+ ```
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}},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)),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 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))},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=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 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=(s,l,c)=>{const y=s?.trim();if(null==y||""===y)throw new Error("Empty expression");const h=o(l)?l:{},f=((o=()=>new i.Jexl)=>{const s=o();return s.addFunction("nextAnniversary",((t,e)=>r({initialDate:t,format:e}))),s.addFunction("yearsElapsed",((r,e,a)=>t({startDate:r,endDate:a,format:e}))),s.addFunction("hasPassed",((r,t,a)=>e({date:r,yearsToAdd:a,format:t}))),s.addFunction("now",(()=>(new Date).toISOString())),s.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)))),s.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)))),s.addFunction("present",(r=>n(r))),s.addFunction("missing",(r=>a(r))),s.addFunction("keys",(r=>a(r)||"object"!=typeof r||Array.isArray(r)?[]:Object.keys(r))),s.addFunction("values",(r=>a(r)||"object"!=typeof r||Array.isArray(r)?[]:Object.values(r))),s.addBinaryOp("??",0,((r,t)=>a(r)?t:a(t)?r:r??t)),s})(),$=(r=>{if(r.startsWith("{{")&&r.endsWith("}}"))return r.slice(2,-2)})(y),m=(r=>r.replace(/\$\./g,"").trim())($??y),g=p(m,h,f);if(g)return g;if(!$&&y.startsWith("$"))return c?.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})(y,h):((r,t)=>{try{return d(r,t)}catch{throw new Error(`Invalid JSON path: "${r}"`)}})(y,h);if(!g&&!$)return s;const A=u(f,m);if(!A||"."===m)throw new Error(`Invalid expression: "${y}"`);try{return A.evalSync(h)}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};
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 e=require("@stackone/utils"),r=require("jexl"),t=require("jsonpath"),n=require("lodash.get");function a(e){var r=Object.create(null);return e&&Object.keys(e).forEach((function(t){if("default"!==t){var n=Object.getOwnPropertyDescriptor(e,t);Object.defineProperty(r,t,n.get?n:{enumerable:!0,get:function(){return e[t]}})}})),r.default=e,Object.freeze(r)}var i=a(r);const s=/\${([^}]+)}/g,o=(e,r)=>{try{return e.compile(r)}catch{return null}},c=(e,r)=>{t.parse(e);const n=t.query(r,e);if(0!==n.length)return 1===n.length?n[0]:n},l=(e,r,t)=>e.replace(r,String(t)),u=(r,t,a)=>{const i=r.match(s);if(!i)return;const c=i.length,u=new Array(c);for(let e=0;e<c;e++)u[e]={toReplace:i[e],path:i[e].slice(2,-1)};return u.reduce(((r,i)=>((r,t,a,i)=>{const s=r.path.trim();if(!s)return i;try{const c=o(t,s);if(c){const e=c.evalSync(a);if(void 0!==e)return l(i,r.toReplace,e)}const u=n(a,s);return e.notMissing(u)?l(i,r.toReplace,u):i}catch{return i}})(i,a,t,r)),String(r))},d=(r,t,n,a)=>{if(Array.isArray(t)){const e=parseInt(n,10);if(!isNaN(e)&&(e<0||e>=t.length))return{value:void 0,error:`Invalid array index '${n}' at '${a}'`,availableKeys:t.map(((e,r)=>r.toString()))}}return e.isObject(t)?{value:void 0,error:`Key '${n}' not found at '${a}'`,availableKeys:Object.keys(t)}:{value:void 0,error:`${r} at '${a}'`,availableKeys:[]}},y=(r,t)=>{const n=/(\.\.)|(\[\*\])|(\[\?\()|(\[\d+:\d+\])/;try{if(n.test(r)){const e=c(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 n=t,a="$";for(let t=1;t<r.length;t++){const i=r[t],s=i.startsWith("[")?`${a}${i}`:`${a}.${i}`;if(Array.isArray(n)){const e=parseInt(i,10);if(isNaN(e)||e<0||e>=n.length)return d("Invalid array index",n,i,a);n=n[e],a=s}else{if(!e.isObject(n))return{value:void 0,error:`Cannot access '${i}' at '${a}' - parent is not an object`,availableKeys:[]};if(!Object.prototype.hasOwnProperty.call(n,i))return d("Key not found",n,i,a);n=n[i],a=s}}return{value:n}})(a.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,n)=>{const a=r?.trim();if(null==a||""===a)throw new Error("Empty expression");const s=e.isObject(t)?t:{},l=((r=()=>new i.Jexl)=>{const t=r();return t.addFunction("nextAnniversary",((r,t)=>e.calculateNextAnniversary({initialDate:r,format:t}))),t.addFunction("yearsElapsed",((r,t,n)=>e.calculateYearsElapsed({startDate:r,endDate:n,format:t}))),t.addFunction("hasPassed",((r,t,n)=>e.dateHasPassed({date:r,yearsToAdd:n,format:t}))),t.addFunction("now",(()=>(new Date).toISOString())),t.addFunction("includes",((r,t)=>!(e.isMissing(r)||!Array.isArray(r)||0===r.length||e.isMissing(t))&&(Array.isArray(t)?t.every((e=>r.includes(e))):r.includes(t)))),t.addFunction("includesSome",((r,t)=>!(e.isMissing(r)||!Array.isArray(r)||0===r.length||e.isMissing(t))&&(Array.isArray(t)?t.some((e=>r.includes(e))):r.includes(t)))),t.addFunction("present",(r=>e.notMissing(r))),t.addFunction("missing",(r=>e.isMissing(r))),t.addFunction("keys",(r=>e.isMissing(r)||"object"!=typeof r||Array.isArray(r)?[]:Object.keys(r))),t.addFunction("values",(r=>e.isMissing(r)||"object"!=typeof r||Array.isArray(r)?[]:Object.values(r))),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)})(a),p=(e=>e.replace(/\$\./g,"").trim())(d??a),v=u(p,s,l);if(v)return v;if(!d&&a.startsWith("$"))return n?.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})(a,s):((e,r)=>{try{return c(e,r)}catch{throw new Error(`Invalid JSON path: "${e}"`)}})(a,s);if(!v&&!d)return r;const f=o(l,p);if(!f||"."===p)throw new Error(`Invalid expression: "${a}"`);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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stackone/expressions",
3
- "version": "0.11.0",
3
+ "version": "0.13.0",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.es.mjs",