@inixiative/json-rules 1.0.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/LICENSE +21 -0
- package/README.md +340 -0
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +83 -0
- package/dist/index.d.ts +83 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/package.json +59 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 inixiative
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
# @inixiative/json-rules
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@inixiative/json-rules)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
|
|
6
|
+
A powerful, type-safe JSON-based rules engine for TypeScript/JavaScript applications. Define complex validation and business logic rules using simple JSON structures.
|
|
7
|
+
|
|
8
|
+
## Installation
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm install @inixiative/json-rules
|
|
12
|
+
# or
|
|
13
|
+
yarn add @inixiative/json-rules
|
|
14
|
+
# or
|
|
15
|
+
bun add @inixiative/json-rules
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Features
|
|
19
|
+
|
|
20
|
+
- 🎯 **Type-safe**: Full TypeScript support with strict type checking
|
|
21
|
+
- 🔧 **Flexible**: 22 standard operators, 8 array operators, and 8 date operators
|
|
22
|
+
- 🌳 **Composable**: Nest rules with logical operators (all/any) and conditional logic (if-then-else)
|
|
23
|
+
- 📊 **Array validation**: Rich array validation with element-wise conditions
|
|
24
|
+
- 📅 **Date handling**: Comprehensive date comparison with timezone support
|
|
25
|
+
- 🔍 **Path-based access**: Reference values from anywhere in your data structure
|
|
26
|
+
- 💬 **Custom errors**: Every rule supports custom error messages
|
|
27
|
+
|
|
28
|
+
## Installation
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npm install json-rules
|
|
32
|
+
# or
|
|
33
|
+
yarn add json-rules
|
|
34
|
+
# or
|
|
35
|
+
bun add json-rules
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Quick Start
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
import { check, Operator } from 'json-rules';
|
|
42
|
+
|
|
43
|
+
// Simple rule
|
|
44
|
+
const rule = {
|
|
45
|
+
field: 'age',
|
|
46
|
+
operator: Operator.greaterThanEqual,
|
|
47
|
+
value: 18,
|
|
48
|
+
error: 'Must be 18 or older'
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const result = check(rule, { age: 21 }); // returns true
|
|
52
|
+
const result2 = check(rule, { age: 16 }); // returns "Must be 18 or older"
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Operators
|
|
56
|
+
|
|
57
|
+
### Standard Operators (22)
|
|
58
|
+
|
|
59
|
+
#### Comparison
|
|
60
|
+
- `equal` - Exact equality check
|
|
61
|
+
- `notEqual` - Not equal check
|
|
62
|
+
- `lessThan` - Less than comparison
|
|
63
|
+
- `lessThanEqual` - Less than or equal
|
|
64
|
+
- `greaterThan` - Greater than comparison
|
|
65
|
+
- `greaterThanEqual` - Greater than or equal
|
|
66
|
+
|
|
67
|
+
#### Range
|
|
68
|
+
- `between` - Value within range (inclusive)
|
|
69
|
+
- `notBetween` - Value outside range
|
|
70
|
+
|
|
71
|
+
#### Membership
|
|
72
|
+
- `in` - Value in array
|
|
73
|
+
- `notIn` - Value not in array
|
|
74
|
+
- `contains` - Array/string contains value
|
|
75
|
+
- `notContains` - Array/string doesn't contain value
|
|
76
|
+
|
|
77
|
+
#### String
|
|
78
|
+
- `startsWith` - String starts with value
|
|
79
|
+
- `endsWith` - String ends with value
|
|
80
|
+
|
|
81
|
+
#### Pattern
|
|
82
|
+
- `match` - Regex pattern match
|
|
83
|
+
- `notMatch` - Regex pattern doesn't match
|
|
84
|
+
|
|
85
|
+
#### Existence
|
|
86
|
+
- `isEmpty` - Check if value is empty (null, undefined, "", [], {})
|
|
87
|
+
- `notEmpty` - Check if value is not empty
|
|
88
|
+
- `exists` - Field exists (not undefined)
|
|
89
|
+
- `notExists` - Field doesn't exist (undefined)
|
|
90
|
+
|
|
91
|
+
### Array Operators (8)
|
|
92
|
+
|
|
93
|
+
- `all` - All elements match condition
|
|
94
|
+
- `any` - At least one element matches
|
|
95
|
+
- `none` - No elements match
|
|
96
|
+
- `atLeast` - At least X elements match
|
|
97
|
+
- `atMost` - At most X elements match
|
|
98
|
+
- `exactly` - Exactly X elements match
|
|
99
|
+
- `empty` - Array is empty
|
|
100
|
+
- `notEmpty` - Array has elements
|
|
101
|
+
|
|
102
|
+
### Date Operators (8)
|
|
103
|
+
|
|
104
|
+
- `before` - Date is before comparison date
|
|
105
|
+
- `after` - Date is after comparison date
|
|
106
|
+
- `onOrBefore` - Date is on or before
|
|
107
|
+
- `onOrAfter` - Date is on or after
|
|
108
|
+
- `between` - Date is between two dates
|
|
109
|
+
- `notBetween` - Date is outside range
|
|
110
|
+
- `dayIn` - Day of week is in list
|
|
111
|
+
- `dayNotIn` - Day of week is not in list
|
|
112
|
+
|
|
113
|
+
#### Timezone Handling
|
|
114
|
+
|
|
115
|
+
Date comparisons are timezone-aware:
|
|
116
|
+
|
|
117
|
+
1. **When condition value has no timezone** (e.g., `'2025-01-20'`), it's interpreted in the field's timezone:
|
|
118
|
+
```typescript
|
|
119
|
+
// Field: Jan 20 10:00 AM Sydney time
|
|
120
|
+
{ eventDate: '2025-01-20T10:00:00+11:00' }
|
|
121
|
+
// Condition: on or after Jan 20 (interpreted as Jan 20 in Sydney)
|
|
122
|
+
{ dateOperator: 'onOrAfter', value: '2025-01-20' } // ✓ passes
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
2. **When condition value has timezone** (e.g., `'2025-01-20T00:00:00Z'`), it's used as-is:
|
|
126
|
+
```typescript
|
|
127
|
+
// Condition: after midnight UTC specifically
|
|
128
|
+
{ dateOperator: 'after', value: '2025-01-20T00:00:00Z' }
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
3. **Fields without timezone** are treated as local time (UTC offset 0)
|
|
132
|
+
|
|
133
|
+
## Rule Types
|
|
134
|
+
|
|
135
|
+
### Basic Rule
|
|
136
|
+
|
|
137
|
+
```typescript
|
|
138
|
+
{
|
|
139
|
+
field: 'status',
|
|
140
|
+
operator: Operator.equal,
|
|
141
|
+
value: 'active'
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Logical Operators
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
// All conditions must pass (AND)
|
|
149
|
+
{
|
|
150
|
+
all: [
|
|
151
|
+
{ field: 'age', operator: Operator.greaterThanEqual, value: 18 },
|
|
152
|
+
{ field: 'hasLicense', operator: Operator.equal, value: true }
|
|
153
|
+
]
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// At least one must pass (OR)
|
|
157
|
+
{
|
|
158
|
+
any: [
|
|
159
|
+
{ field: 'role', operator: Operator.equal, value: 'admin' },
|
|
160
|
+
{ field: 'isOwner', operator: Operator.equal, value: true }
|
|
161
|
+
]
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Conditional Logic (If-Then-Else)
|
|
166
|
+
|
|
167
|
+
```typescript
|
|
168
|
+
{
|
|
169
|
+
if: { field: 'type', operator: Operator.equal, value: 'premium' },
|
|
170
|
+
then: { field: 'discount', operator: Operator.greaterThan, value: 0 },
|
|
171
|
+
else: { field: 'discount', operator: Operator.equal, value: 0 }
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### Array Validation
|
|
176
|
+
|
|
177
|
+
```typescript
|
|
178
|
+
{
|
|
179
|
+
field: 'orders',
|
|
180
|
+
arrayOperator: ArrayOperator.all,
|
|
181
|
+
condition: {
|
|
182
|
+
field: 'total',
|
|
183
|
+
operator: Operator.lessThan,
|
|
184
|
+
value: 1000
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### Date Validation
|
|
190
|
+
|
|
191
|
+
```typescript
|
|
192
|
+
{
|
|
193
|
+
field: 'expiryDate',
|
|
194
|
+
dateOperator: DateOperator.after,
|
|
195
|
+
value: '2024-12-31'
|
|
196
|
+
}
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## Advanced Features
|
|
200
|
+
|
|
201
|
+
### Path-Based Value Resolution
|
|
202
|
+
|
|
203
|
+
Compare fields against each other using paths:
|
|
204
|
+
|
|
205
|
+
```typescript
|
|
206
|
+
{
|
|
207
|
+
field: 'confirmPassword',
|
|
208
|
+
operator: Operator.equal,
|
|
209
|
+
path: 'password' // Compare against another field
|
|
210
|
+
}
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### Array Element Context
|
|
214
|
+
|
|
215
|
+
Use `$.` prefix to reference the current array element:
|
|
216
|
+
|
|
217
|
+
```typescript
|
|
218
|
+
{
|
|
219
|
+
field: 'items',
|
|
220
|
+
arrayOperator: ArrayOperator.all,
|
|
221
|
+
condition: {
|
|
222
|
+
field: 'price',
|
|
223
|
+
operator: Operator.lessThan,
|
|
224
|
+
path: '$.maxPrice' // Reference field on current array element
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
### Custom Error Messages
|
|
230
|
+
|
|
231
|
+
Every rule supports custom error messages:
|
|
232
|
+
|
|
233
|
+
```typescript
|
|
234
|
+
{
|
|
235
|
+
field: 'email',
|
|
236
|
+
operator: Operator.match,
|
|
237
|
+
value: /^[^@]+@[^@]+\.[^@]+$/,
|
|
238
|
+
error: 'Please enter a valid email address'
|
|
239
|
+
}
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
## Complex Example
|
|
243
|
+
|
|
244
|
+
```typescript
|
|
245
|
+
const rule = {
|
|
246
|
+
all: [
|
|
247
|
+
// User must be active
|
|
248
|
+
{ field: 'status', operator: Operator.equal, value: 'active' },
|
|
249
|
+
|
|
250
|
+
// Age requirement
|
|
251
|
+
{ field: 'age', operator: Operator.between, value: [18, 65] },
|
|
252
|
+
|
|
253
|
+
// Must have at least one verified email
|
|
254
|
+
{
|
|
255
|
+
field: 'emails',
|
|
256
|
+
arrayOperator: ArrayOperator.any,
|
|
257
|
+
condition: { field: 'verified', operator: Operator.equal, value: true }
|
|
258
|
+
},
|
|
259
|
+
|
|
260
|
+
// Conditional premium features
|
|
261
|
+
{
|
|
262
|
+
if: { field: 'subscription', operator: Operator.equal, value: 'premium' },
|
|
263
|
+
then: {
|
|
264
|
+
field: 'features',
|
|
265
|
+
arrayOperator: ArrayOperator.all,
|
|
266
|
+
condition: { field: 'enabled', operator: Operator.equal, value: true }
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
]
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
const userData = {
|
|
273
|
+
status: 'active',
|
|
274
|
+
age: 25,
|
|
275
|
+
emails: [
|
|
276
|
+
{ address: 'user@example.com', verified: true },
|
|
277
|
+
{ address: 'alt@example.com', verified: false }
|
|
278
|
+
],
|
|
279
|
+
subscription: 'premium',
|
|
280
|
+
features: [
|
|
281
|
+
{ name: 'advanced', enabled: true },
|
|
282
|
+
{ name: 'analytics', enabled: true }
|
|
283
|
+
]
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
const result = check(rule, userData); // returns true
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
## API Reference
|
|
290
|
+
|
|
291
|
+
### `check(condition: Condition, data: any, context?: any): boolean | string`
|
|
292
|
+
|
|
293
|
+
The main validation function.
|
|
294
|
+
|
|
295
|
+
- **condition**: The rule to evaluate
|
|
296
|
+
- **data**: The data to validate against
|
|
297
|
+
- **context**: Optional context (defaults to data)
|
|
298
|
+
- **Returns**: `true` if validation passes, error string if it fails
|
|
299
|
+
|
|
300
|
+
### Types
|
|
301
|
+
|
|
302
|
+
```typescript
|
|
303
|
+
type Condition = Rule | ArrayRule | DateRule | All | Any | IfThenElse | boolean;
|
|
304
|
+
|
|
305
|
+
type Rule = {
|
|
306
|
+
field: string;
|
|
307
|
+
operator: Operator;
|
|
308
|
+
value?: any;
|
|
309
|
+
path?: string;
|
|
310
|
+
error?: string;
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
type ArrayRule = {
|
|
314
|
+
field: string;
|
|
315
|
+
arrayOperator: ArrayOperator;
|
|
316
|
+
condition?: Condition;
|
|
317
|
+
count?: number;
|
|
318
|
+
error?: string;
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
type DateRule = {
|
|
322
|
+
field: string;
|
|
323
|
+
dateOperator: DateOperator;
|
|
324
|
+
value?: any;
|
|
325
|
+
path?: string;
|
|
326
|
+
error?: string;
|
|
327
|
+
};
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
## Error Handling
|
|
331
|
+
|
|
332
|
+
The engine throws errors for:
|
|
333
|
+
- Invalid array fields when using array operators
|
|
334
|
+
- Missing required parameters (e.g., count for atLeast)
|
|
335
|
+
- Invalid dates in date comparisons
|
|
336
|
+
- Primitive arrays with array operators (use `contains` or `in` instead)
|
|
337
|
+
|
|
338
|
+
## License
|
|
339
|
+
|
|
340
|
+
MIT
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
'use strict';var lodash=require('lodash'),m=require('dayjs'),A=require('dayjs/plugin/utc'),B=require('dayjs/plugin/timezone'),D=require('dayjs/plugin/isSameOrBefore'),I=require('dayjs/plugin/isSameOrAfter');function _interopDefault(e){return e&&e.__esModule?e:{default:e}}var m__default=/*#__PURE__*/_interopDefault(m);var A__default=/*#__PURE__*/_interopDefault(A);var B__default=/*#__PURE__*/_interopDefault(B);var D__default=/*#__PURE__*/_interopDefault(D);var I__default=/*#__PURE__*/_interopDefault(I);var b=(f=>(f.equal="equal",f.notEqual="notEqual",f.lessThan="lessThan",f.lessThanEqual="lessThanEqual",f.greaterThan="greaterThan",f.greaterThanEqual="greaterThanEqual",f.contains="contains",f.notContains="notContains",f.in="in",f.notIn="notIn",f.match="match",f.notMatch="notMatch",f.between="between",f.notBetween="notBetween",f.isEmpty="isEmpty",f.notEmpty="notEmpty",f.exists="exists",f.notExists="notExists",f.startsWith="startsWith",f.endsWith="endsWith",f))(b||{}),E=(n=>(n.all="all",n.any="any",n.none="none",n.atLeast="atLeast",n.atMost="atMost",n.exactly="exactly",n.empty="empty",n.notEmpty="notEmpty",n))(E||{}),$=(n=>(n.before="before",n.after="after",n.onOrBefore="onOrBefore",n.onOrAfter="onOrAfter",n.between="between",n.notBetween="notBetween",n.dayIn="dayIn",n.dayNotIn="dayNotIn",n))($||{});m__default.default.extend(A__default.default);m__default.default.extend(B__default.default);m__default.default.extend(D__default.default);m__default.default.extend(I__default.default);var q=(e,o,u)=>{let a=lodash.get(o,e.field);if(!a)throw new Error(`${e.field} is null or undefined`);let r=m__default.default(a);if(!r.isValid())throw new Error(`${e.field} is not a valid date: ${a}`);let l=i=>e.error||`${e.field} ${i}`,s=j(e,o,u,r,a),t=s[0],n=s[1];switch(e.dateOperator){case "before":return r.isBefore(t)||l(`must be before ${t.format()}`);case "after":return r.isAfter(t)||l(`must be after ${t.format()}`);case "onOrBefore":return r.isSameOrBefore(t)||l(`must be on or before ${t.format()}`);case "onOrAfter":return r.isSameOrAfter(t)||l(`must be on or after ${t.format()}`);case "between":return r.isSameOrAfter(t)&&r.isSameOrBefore(n)||l(`must be between ${t.format()} and ${n.format()}`);case "notBetween":return r.isBefore(t)||r.isAfter(n)||l(`must not be between ${t.format()} and ${n.format()}`);case "dayIn":if(!Array.isArray(e.value))throw new Error("dayIn operator requires an array of day names");let i=r.format("dddd").toLowerCase(),h=e.value.map(c=>c.toLowerCase());return h.includes(i)||l(`must be on ${h.join(" or ")}`);case "dayNotIn":if(!Array.isArray(e.value))throw new Error("dayNotIn operator requires an array of day names");let x=r.format("dddd").toLowerCase(),g=e.value.map(c=>c.toLowerCase());return !g.includes(x)||l(`must not be on ${g.join(" or ")}`);default:throw new Error("Unknown date operator")}},j=(e,o,u,a,r)=>{if(["between","notBetween"].includes(e.dateOperator)){if(!Array.isArray(e.value)||e.value.length!==2)throw new Error(`${e.dateOperator} operator requires an array of two dates`);let t=p(e.value[0],r),n=p(e.value[1],r);if(!t.isValid())throw new Error(`Invalid start date: ${e.value[0]}`);if(!n.isValid())throw new Error(`Invalid end date: ${e.value[1]}`);return [t,n]}if(["before","after","onOrBefore","onOrAfter"].includes(e.dateOperator)){let t;if(e.value!==void 0)t=e.value;else if(e.path)e.path.startsWith("$.")?t=lodash.get(o,e.path.substring(2)):t=lodash.get(u,e.path);else throw new Error("No value or path specified for date comparison");let n=p(t,r);if(!n.isValid())throw new Error(`Invalid comparison date: ${t}`);return [n,void 0]}return [m__default.default(),void 0]},p=(e,o)=>{let u=String(e);if(u.includes("Z")||u.includes("T")&&(u.includes("+")||u.match(/T.*-\d{2}:/)))return m__default.default(e);let r=String(o),l=0;if(r.includes("+")||r.includes("T")&&r.match(/T.*-\d{2}:/)){let t=r.match(/([+-])(\d{2}):(\d{2})/);t&&(l=(t[1]==="+"?1:-1)*(parseInt(t[2])*60+parseInt(t[3])));}else r.includes("Z")||(l=0);return u.match(/^\d{4}-\d{2}-\d{2}$/)?m__default.default(e+"T00:00:00").subtract(l,"minute"):m__default.default(e).subtract(l,"minute")};var T=(e,o,u)=>{let a=lodash.get(o,e.field),l=!["isEmpty","notEmpty","exists","notExists"].includes(e.operator),s=l?C(e,o,u):void 0,t=n=>e.error||`${e.field} ${n}${l?" "+JSON.stringify(s):""}`;switch(e.operator){case "equal":return a===s||t("must equal");case "notEqual":return a!==s||t("must not equal");case "lessThan":return a<s||t("must be less than");case "lessThanEqual":return a<=s||t("must be less than or equal to");case "greaterThan":return a>s||t("must be greater than");case "greaterThanEqual":return a>=s||t("must be greater than or equal to");case "in":return s?.includes(a)||t("must be one of");case "notIn":return !s?.includes(a)||t("must not be one of");case "contains":return a?.includes(s)||t("must contain");case "notContains":return !a?.includes(s)||t("must not contain");case "match":return !!a?.match(s)||t("must match pattern");case "notMatch":return !a?.match(s)||t("must not match pattern");case "between":if(!Array.isArray(s)||s.length!==2)throw new Error("between operator requires an array of two values");return a>=s[0]&&a<=s[1]||t("must be between");case "notBetween":if(!Array.isArray(s)||s.length!==2)throw new Error("notBetween operator requires an array of two values");return a<s[0]||a>s[1]||t("must not be between");case "isEmpty":return lodash.isEmpty(a)||t("must be empty");case "notEmpty":return !lodash.isEmpty(a)||t("must not be empty");case "exists":return a!==void 0||t("must exist");case "notExists":return a===void 0||t("must not exist");case "startsWith":return a?.startsWith?.(s)||t("must start with");case "endsWith":return a?.endsWith?.(s)||t("must end with");default:throw new Error("Unknown operator")}},C=(e,o,u)=>{if(e.value!==void 0)return e.value;if(e.path)return e.path.startsWith("$.")?lodash.get(o,e.path.substring(2)):lodash.get(u,e.path);throw new Error("No value or path specified")};var y=(e,o,u=o)=>typeof e=="boolean"?e:"all"in e?W(e.all,o,u,e.error):"any"in e?M(e.any,o,u,e.error):"arrayOperator"in e?N(e,o,u):"dateOperator"in e?q(e,o,u):"field"in e?T(e,o,u):"if"in e?L(e,o,u):false,W=(e,o,u,a)=>{let r=[];for(let l of e){let s=y(l,o,u);s!==true&&(typeof s=="string"?r.push(s):r.push("false"));}return r.length?a||(r.length===1?r[0]:`All conditions must pass: ${r.join(" AND ")}`):true},M=(e,o,u,a)=>{let r=[];for(let l of e){let s=y(l,o,u);if(typeof s!="string")return true;r.push(s);}return a||(r.length===1?r[0]:`At least one condition must pass: ${r.join(" OR ")}`)},L=(e,o,u)=>y(e.if,o,u)===true?y(e.then,o,u):e.else?y(e.else,o,u):true,N=(e,o,u)=>{let a=lodash.get(u,e.field);if(!Array.isArray(a))throw new Error(`${e.field} must be an array`);let r=i=>e.error||`${e.field} ${i}`,l=["all","any","none","atLeast","atMost","exactly"],s=["atLeast","atMost","exactly"];if(l.includes(e.arrayOperator)&&!e.condition)throw new Error(`${e.arrayOperator} requires a condition to check against array elements`);if(s.includes(e.arrayOperator)&&e.count===void 0)throw new Error(`${e.arrayOperator} requires a count`);let t=0,n=0;if(l.includes(e.arrayOperator)){if(!lodash.some(a,lodash.isObject))throw new Error(`${e.field} contains only primitive values. Use 'in' or 'contains' operators instead of array operators for primitive arrays`);let i=a.map(h=>y(e.condition,h,u));t=i.filter(h=>h===true).length,n=i.filter(h=>typeof h=="string").length;}switch(e.arrayOperator){case "empty":return !a.length||r("must be empty");case "notEmpty":return !!a.length||r("must not be empty");case "all":return t===a.length||r(`all elements must match (${n} failed)`);case "any":return !!t||r("at least one element must match");case "none":return !t||r(`no elements should match (${t} matched)`);case "atLeast":return t>=e.count||r(`at least ${e.count} elements must match (${t} matched)`);case "atMost":return t<=e.count||r(`at most ${e.count} elements must match (${t} matched)`);case "exactly":return t===e.count||r(`exactly ${e.count} elements must match (${t} matched)`);default:throw new Error("Unknown array operator")}};exports.ArrayOperator=E;exports.DateOperator=$;exports.Operator=b;exports.check=y;//# sourceMappingURL=index.cjs.map
|
|
2
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/operator.ts","../src/date.ts","../src/field.ts","../src/check.ts"],"names":["Operator","ArrayOperator","DateOperator","dayjs","utc","timezone","isSameOrBefore","isSameOrAfter","checkDate","condition","data","context","fieldValue","get","fieldDate","getError","op","dates","parseCompareDates","compareDate","endDate","dayName","allowedDays","d","day","excludedDays","startDate","parseDateWithTimezone","value","date","valueStr","fieldStr","offset","match","checkField","needsValue","getValue","isEmpty","check","conditions","all","any","checkArray","checkIfThenElse","error","errors","result","arrayValue","defaultMsg","requiresCondition","requiresCount","matches","failures","some","isObject","results","item","r"],"mappings":"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","file":"index.cjs","sourcesContent":["export enum Operator {\n equal = 'equal',\n notEqual = 'notEqual',\n lessThan = 'lessThan',\n lessThanEqual = 'lessThanEqual',\n greaterThan = 'greaterThan',\n greaterThanEqual = 'greaterThanEqual',\n contains = 'contains',\n notContains = 'notContains',\n in = 'in',\n notIn = 'notIn',\n match = 'match',\n notMatch = 'notMatch',\n between = 'between',\n notBetween = 'notBetween',\n isEmpty = 'isEmpty',\n notEmpty = 'notEmpty',\n exists = 'exists',\n notExists = 'notExists',\n startsWith = 'startsWith',\n endsWith = 'endsWith',\n}\n\nexport enum ArrayOperator {\n all = 'all',\n any = 'any',\n none = 'none',\n atLeast = 'atLeast',\n atMost = 'atMost',\n exactly = 'exactly',\n empty = 'empty',\n notEmpty = 'notEmpty',\n}\n\nexport enum DateOperator {\n before = 'before',\n after = 'after',\n onOrBefore = 'onOrBefore',\n onOrAfter = 'onOrAfter',\n between = 'between',\n notBetween = 'notBetween',\n dayIn = 'dayIn', // e.g., ['monday', 'tuesday', 'friday']\n dayNotIn = 'dayNotIn',\n}\n","import { get } from 'lodash';\nimport dayjs from 'dayjs';\nimport utc from 'dayjs/plugin/utc';\nimport timezone from 'dayjs/plugin/timezone';\nimport isSameOrBefore from 'dayjs/plugin/isSameOrBefore';\nimport isSameOrAfter from 'dayjs/plugin/isSameOrAfter';\nimport type { DateRule } from './types';\nimport { DateOperator } from './operator';\n\ndayjs.extend(utc);\ndayjs.extend(timezone);\ndayjs.extend(isSameOrBefore);\ndayjs.extend(isSameOrAfter);\n\nexport const checkDate = (condition: DateRule, data: any, context: any): boolean | string => {\n const fieldValue = get(data, condition.field);\n \n if (!fieldValue) throw new Error(`${condition.field} is null or undefined`);\n \n const fieldDate = dayjs(fieldValue);\n \n if (!fieldDate.isValid()) throw new Error(`${condition.field} is not a valid date: ${fieldValue}`);\n \n const getError = (op: string) => condition.error || `${condition.field} ${op}`;\n \n // Parse comparison dates with timezone context - pass the original string to preserve offset info\n const dates = parseCompareDates(condition, data, context, fieldDate, fieldValue);\n const compareDate = dates[0];\n const endDate = dates[1];\n \n switch (condition.dateOperator) {\n case DateOperator.before:\n return fieldDate.isBefore(compareDate) || getError(`must be before ${compareDate.format()}`);\n \n case DateOperator.after:\n return fieldDate.isAfter(compareDate) || getError(`must be after ${compareDate.format()}`);\n \n case DateOperator.onOrBefore:\n return fieldDate.isSameOrBefore(compareDate) || getError(`must be on or before ${compareDate.format()}`);\n \n case DateOperator.onOrAfter:\n return fieldDate.isSameOrAfter(compareDate) || getError(`must be on or after ${compareDate.format()}`);\n \n case DateOperator.between:\n return (fieldDate.isSameOrAfter(compareDate) && fieldDate.isSameOrBefore(endDate!)) || \n getError(`must be between ${compareDate.format()} and ${endDate!.format()}`);\n \n case DateOperator.notBetween:\n return (fieldDate.isBefore(compareDate) || fieldDate.isAfter(endDate!)) || \n getError(`must not be between ${compareDate.format()} and ${endDate!.format()}`);\n \n case DateOperator.dayIn:\n if (!Array.isArray(condition.value)) throw new Error('dayIn operator requires an array of day names');\n const dayName = fieldDate.format('dddd').toLowerCase();\n const allowedDays = condition.value.map(d => d.toLowerCase());\n return allowedDays.includes(dayName) || getError(`must be on ${allowedDays.join(' or ')}`);\n \n case DateOperator.dayNotIn:\n if (!Array.isArray(condition.value)) throw new Error('dayNotIn operator requires an array of day names');\n const day = fieldDate.format('dddd').toLowerCase();\n const excludedDays = condition.value.map(d => d.toLowerCase());\n return !excludedDays.includes(day) || getError(`must not be on ${excludedDays.join(' or ')}`);\n \n default:\n throw new Error('Unknown date operator');\n }\n}\n\nconst parseCompareDates = (condition: DateRule, data: any, context: any, fieldDate: dayjs.Dayjs, fieldValue: string): [dayjs.Dayjs, dayjs.Dayjs | undefined] => {\n const requiresTwoDates = [DateOperator.between, DateOperator.notBetween];\n \n if (requiresTwoDates.includes(condition.dateOperator)) {\n if (!Array.isArray(condition.value) || condition.value.length !== 2) \n throw new Error(`${condition.dateOperator} operator requires an array of two dates`);\n const startDate = parseDateWithTimezone(condition.value[0], fieldValue);\n const endDate = parseDateWithTimezone(condition.value[1], fieldValue);\n if (!startDate.isValid()) throw new Error(`Invalid start date: ${condition.value[0]}`);\n if (!endDate.isValid()) throw new Error(`Invalid end date: ${condition.value[1]}`);\n return [startDate, endDate];\n }\n \n const requiresOneDate = [\n DateOperator.before,\n DateOperator.after,\n DateOperator.onOrBefore,\n DateOperator.onOrAfter\n ];\n \n if (requiresOneDate.includes(condition.dateOperator)) {\n let value;\n if (condition.value !== undefined) {\n value = condition.value;\n } else if (condition.path) {\n // Support $.path for current element\n if (condition.path.startsWith('$.')) {\n value = get(data, condition.path.substring(2));\n } else {\n value = get(context, condition.path);\n }\n } else {\n throw new Error('No value or path specified for date comparison');\n }\n const date = parseDateWithTimezone(value, fieldValue);\n if (!date.isValid()) throw new Error(`Invalid comparison date: ${value}`);\n return [date, undefined];\n }\n \n return [dayjs(), undefined]; // Won't be used for dayIn/dayNotIn\n}\n\nconst parseDateWithTimezone = (value: any, fieldValue: string): dayjs.Dayjs => {\n const valueStr = String(value);\n \n // Check if value has explicit timezone information\n const hasTimezone = valueStr.includes('Z') || \n (valueStr.includes('T') && (valueStr.includes('+') || valueStr.match(/T.*-\\d{2}:/)));\n \n if (hasTimezone) return dayjs(value);\n \n // No timezone info in value - interpret in field's timezone\n // Extract offset from field value\n const fieldStr = String(fieldValue);\n let offset = 0;\n \n if (fieldStr.includes('+') || (fieldStr.includes('T') && fieldStr.match(/T.*-\\d{2}:/))) {\n // Field has explicit offset like +11:00 or -08:00\n const match = fieldStr.match(/([+-])(\\d{2}):(\\d{2})/);\n if (match) {\n const sign = match[1] === '+' ? 1 : -1;\n offset = sign * (parseInt(match[2]) * 60 + parseInt(match[3]));\n }\n } else if (!fieldStr.includes('Z')) {\n // Field has no timezone, assume local time (offset 0)\n offset = 0;\n }\n // If field has Z, it's UTC (offset 0)\n \n // Create a date representing the same local time as the field's timezone\n if (valueStr.match(/^\\d{4}-\\d{2}-\\d{2}$/)) {\n // For date-only, we want midnight in the field's timezone\n const localMidnight = dayjs(value + 'T00:00:00');\n return localMidnight.subtract(offset, 'minute');\n }\n \n // For datetime without timezone, interpret as local time in field's timezone\n const localTime = dayjs(value);\n return localTime.subtract(offset, 'minute');\n}","import { get, isEmpty } from 'lodash';\nimport type { Rule } from './types';\nimport { Operator } from './operator';\n\nexport const checkField = (condition: Rule, data: any, context: any): boolean | string => {\n // Use data for field access (current element) but context remains available for path references\n const fieldValue = get(data, condition.field);\n \n // Operators that don't need a value\n const noValueOps = [Operator.isEmpty, Operator.notEmpty, Operator.exists, Operator.notExists];\n const needsValue = !noValueOps.includes(condition.operator);\n const value = needsValue ? getValue(condition, data, context) : undefined;\n \n const getError = (op: string) => condition.error || `${condition.field} ${op}${needsValue ? ' ' + JSON.stringify(value) : ''}`;\n\n switch (condition.operator) {\n case Operator.equal:\n return fieldValue === value || getError(`must equal`);\n case Operator.notEqual:\n return fieldValue !== value || getError(`must not equal`);\n case Operator.lessThan:\n return fieldValue < value || getError(`must be less than`);\n case Operator.lessThanEqual:\n return fieldValue <= value || getError(`must be less than or equal to`);\n case Operator.greaterThan:\n return fieldValue > value || getError(`must be greater than`);\n case Operator.greaterThanEqual:\n return fieldValue >= value || getError(`must be greater than or equal to`);\n case Operator.in:\n return value?.includes(fieldValue) || getError(`must be one of`);\n case Operator.notIn:\n return !value?.includes(fieldValue) || getError(`must not be one of`);\n case Operator.contains:\n return fieldValue?.includes(value) || getError(`must contain`);\n case Operator.notContains:\n return !fieldValue?.includes(value) || getError(`must not contain`);\n case Operator.match:\n return !!fieldValue?.match(value) || getError(`must match pattern`);\n case Operator.notMatch:\n return !fieldValue?.match(value) || getError(`must not match pattern`);\n case Operator.between:\n if (!Array.isArray(value) || value.length !== 2) \n throw new Error('between operator requires an array of two values');\n return (fieldValue >= value[0] && fieldValue <= value[1]) || getError(`must be between`);\n case Operator.notBetween:\n if (!Array.isArray(value) || value.length !== 2) \n throw new Error('notBetween operator requires an array of two values');\n return (fieldValue < value[0] || fieldValue > value[1]) || getError(`must not be between`);\n case Operator.isEmpty:\n return isEmpty(fieldValue) || getError(`must be empty`);\n case Operator.notEmpty:\n return !isEmpty(fieldValue) || getError(`must not be empty`);\n case Operator.exists:\n return fieldValue !== undefined || getError(`must exist`);\n case Operator.notExists:\n return fieldValue === undefined || getError(`must not exist`);\n case Operator.startsWith:\n return fieldValue?.startsWith?.(value) || getError(`must start with`);\n case Operator.endsWith:\n return fieldValue?.endsWith?.(value) || getError(`must end with`);\n default:\n throw new Error('Unknown operator');\n }\n};\n\nconst getValue = (condition: Rule, data: any, context: any): any => {\n if (condition.value !== undefined) return condition.value;\n if (condition.path) {\n // Special case: if path starts with \"$.\" use data (current element)\n if (condition.path.startsWith('$.')) {\n return get(data, condition.path.substring(2));\n }\n // Otherwise use context (root data)\n return get(context, condition.path);\n }\n throw new Error('No value or path specified');\n};","import { get, some, isObject } from 'lodash';\nimport type { Condition, ArrayRule } from './types';\nimport { ArrayOperator } from './operator';\nimport { checkDate } from './date';\nimport { checkField } from './field';\n\nexport const check = (conditions: Condition, data: any, context: any = data): boolean | string => {\n if (typeof conditions === 'boolean') return conditions;\n if ('all' in conditions) return all(conditions.all, data, context, conditions.error);\n if ('any' in conditions) return any(conditions.any, data, context, conditions.error);\n if ('arrayOperator' in conditions) return checkArray(conditions, data, context);\n if ('dateOperator' in conditions) return checkDate(conditions, data, context);\n if ('field' in conditions) return checkField(conditions, data, context);\n if ('if' in conditions) return checkIfThenElse(conditions, data, context);\n\n return false;\n}\n\nconst all = (conditions: Condition[], data: any, context: any, error?: string): boolean | string => {\n const errors: string[] = [];\n \n for (const condition of conditions) {\n const result = check(condition, data, context);\n if (result !== true) {\n // Handle both string errors and false boolean results\n if (typeof result === 'string') {\n errors.push(result);\n } else {\n // For boolean false, include it in the error message\n errors.push('false');\n }\n }\n }\n\n if (!errors.length) return true;\n if (error) return error;\n if (errors.length === 1) return errors[0];\n return `All conditions must pass: ${errors.join(' AND ')}`;\n}\n\nconst any = (conditions: Condition[], data: any, context: any, error?: string): boolean | string => {\n const errors: string[] = [];\n\n for (const condition of conditions) {\n const result = check(condition, data, context);\n if (typeof result !== 'string') return true;\n errors.push(result);\n }\n\n if (error) return error;\n if (errors.length === 1) return errors[0];\n return `At least one condition must pass: ${errors.join(' OR ')}`;\n}\n\nconst checkIfThenElse = (condition: any, data: any, context: any): boolean | string => {\n const ifResult = check(condition.if, data, context);\n \n if (ifResult === true) return check(condition.then, data, context);\n return condition.else ? check(condition.else, data, context) : true;\n}\n\nconst checkArray = (condition: ArrayRule, data: any, context: any): boolean | string => {\n const arrayValue = get(context, condition.field);\n \n if (!Array.isArray(arrayValue)) throw new Error(`${condition.field} must be an array`);\n \n const getError = (defaultMsg: string) => condition.error || `${condition.field} ${defaultMsg}`;\n \n // Operators that require a condition\n const requiresCondition = [\n ArrayOperator.all, \n ArrayOperator.any, \n ArrayOperator.none,\n ArrayOperator.atLeast,\n ArrayOperator.atMost,\n ArrayOperator.exactly\n ];\n \n // Operators that require a count\n const requiresCount = [\n ArrayOperator.atLeast,\n ArrayOperator.atMost,\n ArrayOperator.exactly\n ];\n \n if (requiresCondition.includes(condition.arrayOperator) && !condition.condition) \n throw new Error(`${condition.arrayOperator} requires a condition to check against array elements`);\n \n if (requiresCount.includes(condition.arrayOperator) && condition.count === undefined) \n throw new Error(`${condition.arrayOperator} requires a count`);\n \n // For operators that check elements, compute matches\n let matches = 0;\n let failures = 0;\n \n if (requiresCondition.includes(condition.arrayOperator)) {\n // Check if array contains any objects\n if (!some(arrayValue, isObject)) \n throw new Error(`${condition.field} contains only primitive values. Use 'in' or 'contains' operators instead of array operators for primitive arrays`);\n \n // Pass item as data (for relative field access) but keep original context (for path access)\n const results = arrayValue.map(item => check(condition.condition!, item, context));\n matches = results.filter(r => r === true).length;\n failures = results.filter(r => typeof r === 'string').length;\n }\n \n switch (condition.arrayOperator) {\n case ArrayOperator.empty:\n return !arrayValue.length || getError('must be empty');\n \n case ArrayOperator.notEmpty:\n return !!arrayValue.length || getError('must not be empty');\n \n case ArrayOperator.all:\n return matches === arrayValue.length || getError(`all elements must match (${failures} failed)`);\n \n case ArrayOperator.any:\n return !!matches || getError('at least one element must match');\n \n case ArrayOperator.none:\n return !matches || getError(`no elements should match (${matches} matched)`);\n \n case ArrayOperator.atLeast:\n return matches >= condition.count! || getError(`at least ${condition.count} elements must match (${matches} matched)`);\n \n case ArrayOperator.atMost:\n return matches <= condition.count! || getError(`at most ${condition.count} elements must match (${matches} matched)`);\n \n case ArrayOperator.exactly:\n return matches === condition.count! || getError(`exactly ${condition.count} elements must match (${matches} matched)`);\n \n default:\n throw new Error('Unknown array operator');\n }\n}"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
declare enum Operator {
|
|
2
|
+
equal = "equal",
|
|
3
|
+
notEqual = "notEqual",
|
|
4
|
+
lessThan = "lessThan",
|
|
5
|
+
lessThanEqual = "lessThanEqual",
|
|
6
|
+
greaterThan = "greaterThan",
|
|
7
|
+
greaterThanEqual = "greaterThanEqual",
|
|
8
|
+
contains = "contains",
|
|
9
|
+
notContains = "notContains",
|
|
10
|
+
in = "in",
|
|
11
|
+
notIn = "notIn",
|
|
12
|
+
match = "match",
|
|
13
|
+
notMatch = "notMatch",
|
|
14
|
+
between = "between",
|
|
15
|
+
notBetween = "notBetween",
|
|
16
|
+
isEmpty = "isEmpty",
|
|
17
|
+
notEmpty = "notEmpty",
|
|
18
|
+
exists = "exists",
|
|
19
|
+
notExists = "notExists",
|
|
20
|
+
startsWith = "startsWith",
|
|
21
|
+
endsWith = "endsWith"
|
|
22
|
+
}
|
|
23
|
+
declare enum ArrayOperator {
|
|
24
|
+
all = "all",
|
|
25
|
+
any = "any",
|
|
26
|
+
none = "none",
|
|
27
|
+
atLeast = "atLeast",
|
|
28
|
+
atMost = "atMost",
|
|
29
|
+
exactly = "exactly",
|
|
30
|
+
empty = "empty",
|
|
31
|
+
notEmpty = "notEmpty"
|
|
32
|
+
}
|
|
33
|
+
declare enum DateOperator {
|
|
34
|
+
before = "before",
|
|
35
|
+
after = "after",
|
|
36
|
+
onOrBefore = "onOrBefore",
|
|
37
|
+
onOrAfter = "onOrAfter",
|
|
38
|
+
between = "between",
|
|
39
|
+
notBetween = "notBetween",
|
|
40
|
+
dayIn = "dayIn",// e.g., ['monday', 'tuesday', 'friday']
|
|
41
|
+
dayNotIn = "dayNotIn"
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
type Rule = {
|
|
45
|
+
field: string;
|
|
46
|
+
operator: Operator;
|
|
47
|
+
value?: any;
|
|
48
|
+
path?: string;
|
|
49
|
+
error?: string;
|
|
50
|
+
};
|
|
51
|
+
type ArrayRule = {
|
|
52
|
+
field: string;
|
|
53
|
+
arrayOperator: ArrayOperator;
|
|
54
|
+
condition?: Condition;
|
|
55
|
+
count?: number;
|
|
56
|
+
error?: string;
|
|
57
|
+
};
|
|
58
|
+
type DateRule = {
|
|
59
|
+
field: string;
|
|
60
|
+
dateOperator: DateOperator;
|
|
61
|
+
value?: any;
|
|
62
|
+
path?: string;
|
|
63
|
+
error?: string;
|
|
64
|
+
};
|
|
65
|
+
type All = {
|
|
66
|
+
all: Condition[];
|
|
67
|
+
error?: string;
|
|
68
|
+
};
|
|
69
|
+
type Any = {
|
|
70
|
+
any: Condition[];
|
|
71
|
+
error?: string;
|
|
72
|
+
};
|
|
73
|
+
type IfThenElse = {
|
|
74
|
+
if: Condition;
|
|
75
|
+
then: Condition;
|
|
76
|
+
else?: Condition;
|
|
77
|
+
error?: string;
|
|
78
|
+
};
|
|
79
|
+
type Condition = Rule | ArrayRule | DateRule | All | Any | IfThenElse | boolean;
|
|
80
|
+
|
|
81
|
+
declare const check: (conditions: Condition, data: any, context?: any) => boolean | string;
|
|
82
|
+
|
|
83
|
+
export { type All, type Any, ArrayOperator, type ArrayRule, type Condition, DateOperator, type DateRule, type IfThenElse, Operator, type Rule, check };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
declare enum Operator {
|
|
2
|
+
equal = "equal",
|
|
3
|
+
notEqual = "notEqual",
|
|
4
|
+
lessThan = "lessThan",
|
|
5
|
+
lessThanEqual = "lessThanEqual",
|
|
6
|
+
greaterThan = "greaterThan",
|
|
7
|
+
greaterThanEqual = "greaterThanEqual",
|
|
8
|
+
contains = "contains",
|
|
9
|
+
notContains = "notContains",
|
|
10
|
+
in = "in",
|
|
11
|
+
notIn = "notIn",
|
|
12
|
+
match = "match",
|
|
13
|
+
notMatch = "notMatch",
|
|
14
|
+
between = "between",
|
|
15
|
+
notBetween = "notBetween",
|
|
16
|
+
isEmpty = "isEmpty",
|
|
17
|
+
notEmpty = "notEmpty",
|
|
18
|
+
exists = "exists",
|
|
19
|
+
notExists = "notExists",
|
|
20
|
+
startsWith = "startsWith",
|
|
21
|
+
endsWith = "endsWith"
|
|
22
|
+
}
|
|
23
|
+
declare enum ArrayOperator {
|
|
24
|
+
all = "all",
|
|
25
|
+
any = "any",
|
|
26
|
+
none = "none",
|
|
27
|
+
atLeast = "atLeast",
|
|
28
|
+
atMost = "atMost",
|
|
29
|
+
exactly = "exactly",
|
|
30
|
+
empty = "empty",
|
|
31
|
+
notEmpty = "notEmpty"
|
|
32
|
+
}
|
|
33
|
+
declare enum DateOperator {
|
|
34
|
+
before = "before",
|
|
35
|
+
after = "after",
|
|
36
|
+
onOrBefore = "onOrBefore",
|
|
37
|
+
onOrAfter = "onOrAfter",
|
|
38
|
+
between = "between",
|
|
39
|
+
notBetween = "notBetween",
|
|
40
|
+
dayIn = "dayIn",// e.g., ['monday', 'tuesday', 'friday']
|
|
41
|
+
dayNotIn = "dayNotIn"
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
type Rule = {
|
|
45
|
+
field: string;
|
|
46
|
+
operator: Operator;
|
|
47
|
+
value?: any;
|
|
48
|
+
path?: string;
|
|
49
|
+
error?: string;
|
|
50
|
+
};
|
|
51
|
+
type ArrayRule = {
|
|
52
|
+
field: string;
|
|
53
|
+
arrayOperator: ArrayOperator;
|
|
54
|
+
condition?: Condition;
|
|
55
|
+
count?: number;
|
|
56
|
+
error?: string;
|
|
57
|
+
};
|
|
58
|
+
type DateRule = {
|
|
59
|
+
field: string;
|
|
60
|
+
dateOperator: DateOperator;
|
|
61
|
+
value?: any;
|
|
62
|
+
path?: string;
|
|
63
|
+
error?: string;
|
|
64
|
+
};
|
|
65
|
+
type All = {
|
|
66
|
+
all: Condition[];
|
|
67
|
+
error?: string;
|
|
68
|
+
};
|
|
69
|
+
type Any = {
|
|
70
|
+
any: Condition[];
|
|
71
|
+
error?: string;
|
|
72
|
+
};
|
|
73
|
+
type IfThenElse = {
|
|
74
|
+
if: Condition;
|
|
75
|
+
then: Condition;
|
|
76
|
+
else?: Condition;
|
|
77
|
+
error?: string;
|
|
78
|
+
};
|
|
79
|
+
type Condition = Rule | ArrayRule | DateRule | All | Any | IfThenElse | boolean;
|
|
80
|
+
|
|
81
|
+
declare const check: (conditions: Condition, data: any, context?: any) => boolean | string;
|
|
82
|
+
|
|
83
|
+
export { type All, type Any, ArrayOperator, type ArrayRule, type Condition, DateOperator, type DateRule, type IfThenElse, Operator, type Rule, check };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import {get,isEmpty,some,isObject}from'lodash';import m from'dayjs';import A from'dayjs/plugin/utc';import B from'dayjs/plugin/timezone';import D from'dayjs/plugin/isSameOrBefore';import I from'dayjs/plugin/isSameOrAfter';var b=(f=>(f.equal="equal",f.notEqual="notEqual",f.lessThan="lessThan",f.lessThanEqual="lessThanEqual",f.greaterThan="greaterThan",f.greaterThanEqual="greaterThanEqual",f.contains="contains",f.notContains="notContains",f.in="in",f.notIn="notIn",f.match="match",f.notMatch="notMatch",f.between="between",f.notBetween="notBetween",f.isEmpty="isEmpty",f.notEmpty="notEmpty",f.exists="exists",f.notExists="notExists",f.startsWith="startsWith",f.endsWith="endsWith",f))(b||{}),E=(n=>(n.all="all",n.any="any",n.none="none",n.atLeast="atLeast",n.atMost="atMost",n.exactly="exactly",n.empty="empty",n.notEmpty="notEmpty",n))(E||{}),$=(n=>(n.before="before",n.after="after",n.onOrBefore="onOrBefore",n.onOrAfter="onOrAfter",n.between="between",n.notBetween="notBetween",n.dayIn="dayIn",n.dayNotIn="dayNotIn",n))($||{});m.extend(A);m.extend(B);m.extend(D);m.extend(I);var q=(e,o,u)=>{let a=get(o,e.field);if(!a)throw new Error(`${e.field} is null or undefined`);let r=m(a);if(!r.isValid())throw new Error(`${e.field} is not a valid date: ${a}`);let l=i=>e.error||`${e.field} ${i}`,s=j(e,o,u,r,a),t=s[0],n=s[1];switch(e.dateOperator){case "before":return r.isBefore(t)||l(`must be before ${t.format()}`);case "after":return r.isAfter(t)||l(`must be after ${t.format()}`);case "onOrBefore":return r.isSameOrBefore(t)||l(`must be on or before ${t.format()}`);case "onOrAfter":return r.isSameOrAfter(t)||l(`must be on or after ${t.format()}`);case "between":return r.isSameOrAfter(t)&&r.isSameOrBefore(n)||l(`must be between ${t.format()} and ${n.format()}`);case "notBetween":return r.isBefore(t)||r.isAfter(n)||l(`must not be between ${t.format()} and ${n.format()}`);case "dayIn":if(!Array.isArray(e.value))throw new Error("dayIn operator requires an array of day names");let i=r.format("dddd").toLowerCase(),h=e.value.map(c=>c.toLowerCase());return h.includes(i)||l(`must be on ${h.join(" or ")}`);case "dayNotIn":if(!Array.isArray(e.value))throw new Error("dayNotIn operator requires an array of day names");let x=r.format("dddd").toLowerCase(),g=e.value.map(c=>c.toLowerCase());return !g.includes(x)||l(`must not be on ${g.join(" or ")}`);default:throw new Error("Unknown date operator")}},j=(e,o,u,a,r)=>{if(["between","notBetween"].includes(e.dateOperator)){if(!Array.isArray(e.value)||e.value.length!==2)throw new Error(`${e.dateOperator} operator requires an array of two dates`);let t=p(e.value[0],r),n=p(e.value[1],r);if(!t.isValid())throw new Error(`Invalid start date: ${e.value[0]}`);if(!n.isValid())throw new Error(`Invalid end date: ${e.value[1]}`);return [t,n]}if(["before","after","onOrBefore","onOrAfter"].includes(e.dateOperator)){let t;if(e.value!==void 0)t=e.value;else if(e.path)e.path.startsWith("$.")?t=get(o,e.path.substring(2)):t=get(u,e.path);else throw new Error("No value or path specified for date comparison");let n=p(t,r);if(!n.isValid())throw new Error(`Invalid comparison date: ${t}`);return [n,void 0]}return [m(),void 0]},p=(e,o)=>{let u=String(e);if(u.includes("Z")||u.includes("T")&&(u.includes("+")||u.match(/T.*-\d{2}:/)))return m(e);let r=String(o),l=0;if(r.includes("+")||r.includes("T")&&r.match(/T.*-\d{2}:/)){let t=r.match(/([+-])(\d{2}):(\d{2})/);t&&(l=(t[1]==="+"?1:-1)*(parseInt(t[2])*60+parseInt(t[3])));}else r.includes("Z")||(l=0);return u.match(/^\d{4}-\d{2}-\d{2}$/)?m(e+"T00:00:00").subtract(l,"minute"):m(e).subtract(l,"minute")};var T=(e,o,u)=>{let a=get(o,e.field),l=!["isEmpty","notEmpty","exists","notExists"].includes(e.operator),s=l?C(e,o,u):void 0,t=n=>e.error||`${e.field} ${n}${l?" "+JSON.stringify(s):""}`;switch(e.operator){case "equal":return a===s||t("must equal");case "notEqual":return a!==s||t("must not equal");case "lessThan":return a<s||t("must be less than");case "lessThanEqual":return a<=s||t("must be less than or equal to");case "greaterThan":return a>s||t("must be greater than");case "greaterThanEqual":return a>=s||t("must be greater than or equal to");case "in":return s?.includes(a)||t("must be one of");case "notIn":return !s?.includes(a)||t("must not be one of");case "contains":return a?.includes(s)||t("must contain");case "notContains":return !a?.includes(s)||t("must not contain");case "match":return !!a?.match(s)||t("must match pattern");case "notMatch":return !a?.match(s)||t("must not match pattern");case "between":if(!Array.isArray(s)||s.length!==2)throw new Error("between operator requires an array of two values");return a>=s[0]&&a<=s[1]||t("must be between");case "notBetween":if(!Array.isArray(s)||s.length!==2)throw new Error("notBetween operator requires an array of two values");return a<s[0]||a>s[1]||t("must not be between");case "isEmpty":return isEmpty(a)||t("must be empty");case "notEmpty":return !isEmpty(a)||t("must not be empty");case "exists":return a!==void 0||t("must exist");case "notExists":return a===void 0||t("must not exist");case "startsWith":return a?.startsWith?.(s)||t("must start with");case "endsWith":return a?.endsWith?.(s)||t("must end with");default:throw new Error("Unknown operator")}},C=(e,o,u)=>{if(e.value!==void 0)return e.value;if(e.path)return e.path.startsWith("$.")?get(o,e.path.substring(2)):get(u,e.path);throw new Error("No value or path specified")};var y=(e,o,u=o)=>typeof e=="boolean"?e:"all"in e?W(e.all,o,u,e.error):"any"in e?M(e.any,o,u,e.error):"arrayOperator"in e?N(e,o,u):"dateOperator"in e?q(e,o,u):"field"in e?T(e,o,u):"if"in e?L(e,o,u):false,W=(e,o,u,a)=>{let r=[];for(let l of e){let s=y(l,o,u);s!==true&&(typeof s=="string"?r.push(s):r.push("false"));}return r.length?a||(r.length===1?r[0]:`All conditions must pass: ${r.join(" AND ")}`):true},M=(e,o,u,a)=>{let r=[];for(let l of e){let s=y(l,o,u);if(typeof s!="string")return true;r.push(s);}return a||(r.length===1?r[0]:`At least one condition must pass: ${r.join(" OR ")}`)},L=(e,o,u)=>y(e.if,o,u)===true?y(e.then,o,u):e.else?y(e.else,o,u):true,N=(e,o,u)=>{let a=get(u,e.field);if(!Array.isArray(a))throw new Error(`${e.field} must be an array`);let r=i=>e.error||`${e.field} ${i}`,l=["all","any","none","atLeast","atMost","exactly"],s=["atLeast","atMost","exactly"];if(l.includes(e.arrayOperator)&&!e.condition)throw new Error(`${e.arrayOperator} requires a condition to check against array elements`);if(s.includes(e.arrayOperator)&&e.count===void 0)throw new Error(`${e.arrayOperator} requires a count`);let t=0,n=0;if(l.includes(e.arrayOperator)){if(!some(a,isObject))throw new Error(`${e.field} contains only primitive values. Use 'in' or 'contains' operators instead of array operators for primitive arrays`);let i=a.map(h=>y(e.condition,h,u));t=i.filter(h=>h===true).length,n=i.filter(h=>typeof h=="string").length;}switch(e.arrayOperator){case "empty":return !a.length||r("must be empty");case "notEmpty":return !!a.length||r("must not be empty");case "all":return t===a.length||r(`all elements must match (${n} failed)`);case "any":return !!t||r("at least one element must match");case "none":return !t||r(`no elements should match (${t} matched)`);case "atLeast":return t>=e.count||r(`at least ${e.count} elements must match (${t} matched)`);case "atMost":return t<=e.count||r(`at most ${e.count} elements must match (${t} matched)`);case "exactly":return t===e.count||r(`exactly ${e.count} elements must match (${t} matched)`);default:throw new Error("Unknown array operator")}};export{E as ArrayOperator,$ as DateOperator,b as Operator,y as check};//# sourceMappingURL=index.js.map
|
|
2
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/operator.ts","../src/date.ts","../src/field.ts","../src/check.ts"],"names":["Operator","ArrayOperator","DateOperator","dayjs","utc","timezone","isSameOrBefore","isSameOrAfter","checkDate","condition","data","context","fieldValue","get","fieldDate","getError","op","dates","parseCompareDates","compareDate","endDate","dayName","allowedDays","d","day","excludedDays","startDate","parseDateWithTimezone","value","date","valueStr","fieldStr","offset","match","checkField","needsValue","getValue","isEmpty","check","conditions","all","any","checkArray","checkIfThenElse","error","errors","result","arrayValue","defaultMsg","requiresCondition","requiresCount","matches","failures","some","isObject","results","item","r"],"mappings":"8NAAO,IAAKA,OACVA,CAAAA,CAAA,KAAA,CAAQ,QACRA,CAAAA,CAAA,QAAA,CAAW,WACXA,CAAAA,CAAA,QAAA,CAAW,UAAA,CACXA,CAAAA,CAAA,cAAgB,eAAA,CAChBA,CAAAA,CAAA,YAAc,aAAA,CACdA,CAAAA,CAAA,iBAAmB,kBAAA,CACnBA,CAAAA,CAAA,QAAA,CAAW,UAAA,CACXA,EAAA,WAAA,CAAc,aAAA,CACdA,EAAA,EAAA,CAAK,IAAA,CACLA,EAAA,KAAA,CAAQ,OAAA,CACRA,CAAAA,CAAA,KAAA,CAAQ,QACRA,CAAAA,CAAA,QAAA,CAAW,WACXA,CAAAA,CAAA,OAAA,CAAU,UACVA,CAAAA,CAAA,UAAA,CAAa,YAAA,CACbA,CAAAA,CAAA,QAAU,SAAA,CACVA,CAAAA,CAAA,SAAW,UAAA,CACXA,CAAAA,CAAA,OAAS,QAAA,CACTA,CAAAA,CAAA,SAAA,CAAY,WAAA,CACZA,EAAA,UAAA,CAAa,YAAA,CACbA,EAAA,QAAA,CAAW,UAAA,CApBDA,OAAA,EAAA,CAAA,CAuBAC,CAAAA,CAAAA,CAAAA,CAAAA,GACVA,CAAAA,CAAA,GAAA,CAAM,MACNA,CAAAA,CAAA,GAAA,CAAM,MACNA,CAAAA,CAAA,IAAA,CAAO,OACPA,CAAAA,CAAA,OAAA,CAAU,UACVA,CAAAA,CAAA,MAAA,CAAS,SACTA,CAAAA,CAAA,OAAA,CAAU,UACVA,CAAAA,CAAA,KAAA,CAAQ,QACRA,CAAAA,CAAA,QAAA,CAAW,UAAA,CARDA,CAAAA,CAAAA,EAAAA,CAAAA,EAAA,IAWAC,CAAAA,CAAAA,CAAAA,CAAAA,GACVA,CAAAA,CAAA,OAAS,QAAA,CACTA,CAAAA,CAAA,MAAQ,OAAA,CACRA,CAAAA,CAAA,UAAA,CAAa,YAAA,CACbA,EAAA,SAAA,CAAY,WAAA,CACZA,EAAA,OAAA,CAAU,SAAA,CACVA,EAAA,UAAA,CAAa,YAAA,CACbA,CAAAA,CAAA,KAAA,CAAQ,QACRA,CAAAA,CAAA,QAAA,CAAW,WARDA,CAAAA,CAAAA,EAAAA,CAAAA,EAAA,EAAA,ECzBZC,EAAM,MAAA,CAAOC,CAAG,EAChBD,CAAAA,CAAM,MAAA,CAAOE,CAAQ,CAAA,CACrBF,CAAAA,CAAM,MAAA,CAAOG,CAAc,EAC3BH,CAAAA,CAAM,MAAA,CAAOI,CAAa,CAAA,CAEnB,IAAMC,EAAY,CAACC,CAAAA,CAAqBC,EAAWC,CAAAA,GAAmC,CAC3F,IAAMC,CAAAA,CAAaC,GAAAA,CAAIH,EAAMD,CAAAA,CAAU,KAAK,EAE5C,GAAI,CAACG,CAAAA,CAAY,MAAM,IAAI,KAAA,CAAM,CAAA,EAAGH,EAAU,KAAK,CAAA,qBAAA,CAAuB,EAE1E,IAAMK,CAAAA,CAAYX,CAAAA,CAAMS,CAAU,EAElC,GAAI,CAACE,EAAU,OAAA,EAAQ,CAAG,MAAM,IAAI,KAAA,CAAM,CAAA,EAAGL,CAAAA,CAAU,KAAK,CAAA,sBAAA,EAAyBG,CAAU,EAAE,CAAA,CAEjG,IAAMG,EAAYC,CAAAA,EAAeP,CAAAA,CAAU,KAAA,EAAS,CAAA,EAAGA,EAAU,KAAK,CAAA,CAAA,EAAIO,CAAE,CAAA,CAAA,CAGtEC,CAAAA,CAAQC,EAAkBT,CAAAA,CAAWC,CAAAA,CAAMC,CAAAA,CAASG,CAAAA,CAAWF,CAAU,CAAA,CACzEO,CAAAA,CAAcF,EAAM,CAAC,CAAA,CACrBG,EAAUH,CAAAA,CAAM,CAAC,CAAA,CAEvB,OAAQR,EAAU,YAAA,EAChB,cACE,OAAOK,CAAAA,CAAU,SAASK,CAAW,CAAA,EAAKJ,CAAAA,CAAS,CAAA,eAAA,EAAkBI,EAAY,MAAA,EAAQ,EAAE,CAAA,CAE7F,KAAA,OAAA,CACE,OAAOL,CAAAA,CAAU,OAAA,CAAQK,CAAW,CAAA,EAAKJ,CAAAA,CAAS,iBAAiBI,CAAAA,CAAY,MAAA,EAAQ,CAAA,CAAE,CAAA,CAE3F,kBACE,OAAOL,CAAAA,CAAU,cAAA,CAAeK,CAAW,GAAKJ,CAAAA,CAAS,CAAA,qBAAA,EAAwBI,EAAY,MAAA,EAAQ,EAAE,CAAA,CAEzG,KAAA,WAAA,CACE,OAAOL,CAAAA,CAAU,cAAcK,CAAW,CAAA,EAAKJ,EAAS,CAAA,oBAAA,EAAuBI,CAAAA,CAAY,QAAQ,CAAA,CAAE,CAAA,CAEvG,KAAA,SAAA,CACE,OAAQL,CAAAA,CAAU,aAAA,CAAcK,CAAW,CAAA,EAAKL,CAAAA,CAAU,eAAeM,CAAQ,CAAA,EAC/EL,CAAAA,CAAS,CAAA,gBAAA,EAAmBI,EAAY,MAAA,EAAQ,QAAQC,CAAAA,CAAS,MAAA,EAAQ,CAAA,CAAE,CAAA,CAE/E,KAAA,YAAA,CACE,OAAQN,EAAU,QAAA,CAASK,CAAW,GAAKL,CAAAA,CAAU,OAAA,CAAQM,CAAQ,CAAA,EACnEL,CAAAA,CAAS,CAAA,oBAAA,EAAuBI,CAAAA,CAAY,QAAQ,CAAA,KAAA,EAAQC,EAAS,MAAA,EAAQ,EAAE,CAAA,CAEnF,KAAA,OAAA,CACE,GAAI,CAAC,MAAM,OAAA,CAAQX,CAAAA,CAAU,KAAK,CAAA,CAAG,MAAM,IAAI,KAAA,CAAM,+CAA+C,EACpG,IAAMY,CAAAA,CAAUP,EAAU,MAAA,CAAO,MAAM,EAAE,WAAA,EAAY,CAC/CQ,EAAcb,CAAAA,CAAU,KAAA,CAAM,GAAA,CAAIc,CAAAA,EAAKA,EAAE,WAAA,EAAa,EAC5D,OAAOD,CAAAA,CAAY,SAASD,CAAO,CAAA,EAAKN,CAAAA,CAAS,CAAA,WAAA,EAAcO,EAAY,IAAA,CAAK,MAAM,CAAC,CAAA,CAAE,CAAA,CAE3F,gBACE,GAAI,CAAC,KAAA,CAAM,OAAA,CAAQb,EAAU,KAAK,CAAA,CAAG,MAAM,IAAI,KAAA,CAAM,kDAAkD,CAAA,CACvG,IAAMe,CAAAA,CAAMV,CAAAA,CAAU,OAAO,MAAM,CAAA,CAAE,aAAY,CAC3CW,CAAAA,CAAehB,EAAU,KAAA,CAAM,GAAA,CAAIc,CAAAA,EAAKA,CAAAA,CAAE,aAAa,CAAA,CAC7D,OAAO,CAACE,CAAAA,CAAa,SAASD,CAAG,CAAA,EAAKT,CAAAA,CAAS,CAAA,eAAA,EAAkBU,EAAa,IAAA,CAAK,MAAM,CAAC,CAAA,CAAE,CAAA,CAE9F,QACE,MAAM,IAAI,KAAA,CAAM,uBAAuB,CAC3C,CACF,CAAA,CAEMP,EAAoB,CAACT,CAAAA,CAAqBC,EAAWC,CAAAA,CAAcG,CAAAA,CAAwBF,IAA+D,CAG9J,GAFyB,uBAA8C,CAAA,CAElD,QAAA,CAASH,EAAU,YAAY,CAAA,CAAG,CACrD,GAAI,CAAC,KAAA,CAAM,OAAA,CAAQA,EAAU,KAAK,CAAA,EAAKA,EAAU,KAAA,CAAM,MAAA,GAAW,EAChE,MAAM,IAAI,KAAA,CAAM,CAAA,EAAGA,EAAU,YAAY,CAAA,wCAAA,CAA0C,EACrF,IAAMiB,CAAAA,CAAYC,EAAsBlB,CAAAA,CAAU,KAAA,CAAM,CAAC,CAAA,CAAGG,CAAU,CAAA,CAChEQ,CAAAA,CAAUO,EAAsBlB,CAAAA,CAAU,KAAA,CAAM,CAAC,CAAA,CAAGG,CAAU,CAAA,CACpE,GAAI,CAACc,CAAAA,CAAU,OAAA,GAAW,MAAM,IAAI,MAAM,CAAA,oBAAA,EAAuBjB,CAAAA,CAAU,KAAA,CAAM,CAAC,CAAC,CAAA,CAAE,CAAA,CACrF,GAAI,CAACW,CAAAA,CAAQ,SAAQ,CAAG,MAAM,IAAI,KAAA,CAAM,qBAAqBX,CAAAA,CAAU,KAAA,CAAM,CAAC,CAAC,CAAA,CAAE,EACjF,OAAO,CAACiB,CAAAA,CAAWN,CAAO,CAC5B,CASA,GAPwB,0CAKxB,CAAA,CAEoB,QAAA,CAASX,EAAU,YAAY,CAAA,CAAG,CACpD,IAAImB,CAAAA,CACJ,GAAInB,CAAAA,CAAU,KAAA,GAAU,OACtBmB,CAAAA,CAAQnB,CAAAA,CAAU,cACTA,CAAAA,CAAU,IAAA,CAEfA,CAAAA,CAAU,IAAA,CAAK,WAAW,IAAI,CAAA,CAChCmB,EAAQf,GAAAA,CAAIH,CAAAA,CAAMD,EAAU,IAAA,CAAK,SAAA,CAAU,CAAC,CAAC,EAE7CmB,CAAAA,CAAQf,GAAAA,CAAIF,EAASF,CAAAA,CAAU,IAAI,OAGrC,MAAM,IAAI,KAAA,CAAM,gDAAgD,EAElE,IAAMoB,CAAAA,CAAOF,EAAsBC,CAAAA,CAAOhB,CAAU,EACpD,GAAI,CAACiB,CAAAA,CAAK,OAAA,GAAW,MAAM,IAAI,MAAM,CAAA,yBAAA,EAA4BD,CAAK,EAAE,CAAA,CACxE,OAAO,CAACC,CAAAA,CAAM,MAAS,CACzB,CAEA,OAAO,CAAC1B,CAAAA,GAAS,MAAS,CAC5B,CAAA,CAEMwB,CAAAA,CAAwB,CAACC,CAAAA,CAAYhB,CAAAA,GAAoC,CAC7E,IAAMkB,CAAAA,CAAW,OAAOF,CAAK,CAAA,CAM7B,GAHoBE,CAAAA,CAAS,SAAS,GAAG,CAAA,EACtCA,EAAS,QAAA,CAAS,GAAG,IAAMA,CAAAA,CAAS,QAAA,CAAS,GAAG,CAAA,EAAKA,CAAAA,CAAS,MAAM,YAAY,CAAA,CAAA,CAElE,OAAO3B,CAAAA,CAAMyB,CAAK,EAInC,IAAMG,CAAAA,CAAW,MAAA,CAAOnB,CAAU,EAC9BoB,CAAAA,CAAS,CAAA,CAEb,GAAID,CAAAA,CAAS,QAAA,CAAS,GAAG,CAAA,EAAMA,CAAAA,CAAS,QAAA,CAAS,GAAG,GAAKA,CAAAA,CAAS,KAAA,CAAM,YAAY,CAAA,CAAI,CAEtF,IAAME,CAAAA,CAAQF,CAAAA,CAAS,KAAA,CAAM,uBAAuB,EAChDE,CAAAA,GAEFD,CAAAA,CAAAA,CADaC,EAAM,CAAC,CAAA,GAAM,IAAM,CAAA,CAAI,EAAA,GACnB,QAAA,CAASA,CAAAA,CAAM,CAAC,CAAC,CAAA,CAAI,GAAK,QAAA,CAASA,CAAAA,CAAM,CAAC,CAAC,CAAA,CAAA,EAEhE,CAAA,KAAYF,CAAAA,CAAS,SAAS,GAAG,CAAA,GAE/BC,EAAS,CAAA,CAAA,CAKX,OAAIF,EAAS,KAAA,CAAM,qBAAqB,CAAA,CAEhB3B,CAAAA,CAAMyB,EAAQ,WAAW,CAAA,CAC1B,SAASI,CAAAA,CAAQ,QAAQ,EAI9B7B,CAAAA,CAAMyB,CAAK,CAAA,CACZ,QAAA,CAASI,EAAQ,QAAQ,CAC5C,EC/IO,IAAME,CAAAA,CAAa,CAACzB,CAAAA,CAAiBC,CAAAA,CAAWC,IAAmC,CAExF,IAAMC,EAAaC,GAAAA,CAAIH,CAAAA,CAAMD,CAAAA,CAAU,KAAK,EAItC0B,CAAAA,CAAa,CADA,0CAAyE,CAAA,CAC7D,QAAA,CAAS1B,EAAU,QAAQ,CAAA,CACpDmB,CAAAA,CAAQO,CAAAA,CAAaC,EAAS3B,CAAAA,CAAWC,CAAAA,CAAMC,CAAO,CAAA,CAAI,MAAA,CAE1DI,EAAYC,CAAAA,EAAeP,CAAAA,CAAU,KAAA,EAAS,CAAA,EAAGA,EAAU,KAAK,CAAA,CAAA,EAAIO,CAAE,CAAA,EAAGmB,CAAAA,CAAa,IAAM,IAAA,CAAK,SAAA,CAAUP,CAAK,CAAA,CAAI,EAAE,CAAA,CAAA,CAE5H,OAAQnB,EAAU,QAAA,EAChB,aACE,OAAOG,CAAAA,GAAegB,CAAAA,EAASb,CAAAA,CAAS,YAAY,CAAA,CACtD,KAAA,UAAA,CACE,OAAOH,CAAAA,GAAegB,CAAAA,EAASb,EAAS,gBAAgB,CAAA,CAC1D,KAAA,UAAA,CACE,OAAOH,EAAagB,CAAAA,EAASb,CAAAA,CAAS,mBAAmB,CAAA,CAC3D,KAAA,eAAA,CACE,OAAOH,CAAAA,EAAcgB,CAAAA,EAASb,CAAAA,CAAS,+BAA+B,EACxE,KAAA,aAAA,CACE,OAAOH,EAAagB,CAAAA,EAASb,CAAAA,CAAS,sBAAsB,CAAA,CAC9D,KAAA,kBAAA,CACE,OAAOH,CAAAA,EAAcgB,CAAAA,EAASb,EAAS,kCAAkC,CAAA,CAC3E,UACE,OAAOa,CAAAA,EAAO,SAAShB,CAAU,CAAA,EAAKG,CAAAA,CAAS,gBAAgB,EACjE,KAAA,OAAA,CACE,OAAO,CAACa,CAAAA,EAAO,QAAA,CAAShB,CAAU,CAAA,EAAKG,CAAAA,CAAS,oBAAoB,CAAA,CACtE,gBACE,OAAOH,CAAAA,EAAY,SAASgB,CAAK,CAAA,EAAKb,EAAS,cAAc,CAAA,CAC/D,KAAA,aAAA,CACE,OAAO,CAACH,CAAAA,EAAY,QAAA,CAASgB,CAAK,CAAA,EAAKb,CAAAA,CAAS,kBAAkB,CAAA,CACpE,KAAA,OAAA,CACE,OAAO,CAAC,CAACH,CAAAA,EAAY,KAAA,CAAMgB,CAAK,CAAA,EAAKb,CAAAA,CAAS,oBAAoB,CAAA,CACpE,KAAA,UAAA,CACE,OAAO,CAACH,GAAY,KAAA,CAAMgB,CAAK,GAAKb,CAAAA,CAAS,wBAAwB,EACvE,KAAA,SAAA,CACE,GAAI,CAAC,KAAA,CAAM,QAAQa,CAAK,CAAA,EAAKA,EAAM,MAAA,GAAW,CAAA,CAC5C,MAAM,IAAI,KAAA,CAAM,kDAAkD,CAAA,CACpE,OAAQhB,CAAAA,EAAcgB,CAAAA,CAAM,CAAC,CAAA,EAAKhB,CAAAA,EAAcgB,EAAM,CAAC,CAAA,EAAMb,EAAS,iBAAiB,CAAA,CACzF,kBACE,GAAI,CAAC,MAAM,OAAA,CAAQa,CAAK,GAAKA,CAAAA,CAAM,MAAA,GAAW,CAAA,CAC5C,MAAM,IAAI,KAAA,CAAM,qDAAqD,EACvE,OAAQhB,CAAAA,CAAagB,EAAM,CAAC,CAAA,EAAKhB,CAAAA,CAAagB,CAAAA,CAAM,CAAC,CAAA,EAAMb,CAAAA,CAAS,qBAAqB,CAAA,CAC3F,KAAA,SAAA,CACE,OAAOsB,OAAAA,CAAQzB,CAAU,CAAA,EAAKG,CAAAA,CAAS,eAAe,CAAA,CACxD,KAAA,UAAA,CACE,OAAO,CAACsB,OAAAA,CAAQzB,CAAU,CAAA,EAAKG,CAAAA,CAAS,mBAAmB,CAAA,CAC7D,cACE,OAAOH,CAAAA,GAAe,QAAaG,CAAAA,CAAS,YAAY,EAC1D,KAAA,WAAA,CACE,OAAOH,CAAAA,GAAe,MAAA,EAAaG,EAAS,gBAAgB,CAAA,CAC9D,kBACE,OAAOH,CAAAA,EAAY,aAAagB,CAAK,CAAA,EAAKb,CAAAA,CAAS,iBAAiB,EACtE,KAAA,UAAA,CACE,OAAOH,GAAY,QAAA,GAAWgB,CAAK,GAAKb,CAAAA,CAAS,eAAe,CAAA,CAClE,QACE,MAAM,IAAI,KAAA,CAAM,kBAAkB,CACtC,CACF,EAEMqB,CAAAA,CAAW,CAAC3B,EAAiBC,CAAAA,CAAWC,CAAAA,GAAsB,CAClE,GAAIF,CAAAA,CAAU,QAAU,MAAA,CAAW,OAAOA,EAAU,KAAA,CACpD,GAAIA,CAAAA,CAAU,IAAA,CAEZ,OAAIA,CAAAA,CAAU,IAAA,CAAK,WAAW,IAAI,CAAA,CACzBI,IAAIH,CAAAA,CAAMD,CAAAA,CAAU,IAAA,CAAK,SAAA,CAAU,CAAC,CAAC,CAAA,CAGvCI,IAAIF,CAAAA,CAASF,CAAAA,CAAU,IAAI,CAAA,CAEpC,MAAM,IAAI,KAAA,CAAM,4BAA4B,CAC9C,CAAA,KCtEa6B,CAAAA,CAAQ,CAACC,EAAuB7B,CAAAA,CAAWC,CAAAA,CAAeD,CAAAA,GACjE,OAAO6B,GAAe,SAAA,CAAkBA,CAAAA,CACxC,QAASA,CAAAA,CAAmBC,CAAAA,CAAID,EAAW,GAAA,CAAK7B,CAAAA,CAAMC,CAAAA,CAAS4B,CAAAA,CAAW,KAAK,CAAA,CAC/E,KAAA,GAASA,EAAmBE,CAAAA,CAAIF,CAAAA,CAAW,IAAK7B,CAAAA,CAAMC,CAAAA,CAAS4B,CAAAA,CAAW,KAAK,EAC/E,eAAA,GAAmBA,CAAAA,CAAmBG,EAAWH,CAAAA,CAAY7B,CAAAA,CAAMC,CAAO,CAAA,CAC1E,cAAA,GAAkB4B,CAAAA,CAAmB/B,CAAAA,CAAU+B,EAAY7B,CAAAA,CAAMC,CAAO,EACxE,OAAA,GAAW4B,CAAAA,CAAmBL,EAAWK,CAAAA,CAAY7B,CAAAA,CAAMC,CAAO,CAAA,CAClE,IAAA,GAAQ4B,EAAmBI,CAAAA,CAAgBJ,CAAAA,CAAY7B,EAAMC,CAAO,CAAA,CAEjE,MAGH6B,CAAAA,CAAM,CAACD,CAAAA,CAAyB7B,CAAAA,CAAWC,EAAciC,CAAAA,GAAqC,CAClG,IAAMC,CAAAA,CAAmB,GAEzB,IAAA,IAAWpC,CAAAA,IAAa8B,CAAAA,CAAY,CAClC,IAAMO,CAAAA,CAASR,CAAAA,CAAM7B,EAAWC,CAAAA,CAAMC,CAAO,EACzCmC,CAAAA,GAAW,IAAA,GAET,OAAOA,CAAAA,EAAW,SACpBD,CAAAA,CAAO,IAAA,CAAKC,CAAM,CAAA,CAGlBD,CAAAA,CAAO,KAAK,OAAO,CAAA,EAGzB,CAEA,OAAKA,EAAO,MAAA,CACRD,CAAAA,GACAC,EAAO,MAAA,GAAW,CAAA,CAAUA,EAAO,CAAC,CAAA,CACjC,CAAA,0BAAA,EAA6BA,CAAAA,CAAO,KAAK,OAAO,CAAC,IAH7B,IAI7B,CAAA,CAEMJ,EAAM,CAACF,CAAAA,CAAyB7B,CAAAA,CAAWC,CAAAA,CAAciC,IAAqC,CAClG,IAAMC,EAAmB,EAAC,CAE1B,QAAWpC,CAAAA,IAAa8B,CAAAA,CAAY,CAClC,IAAMO,EAASR,CAAAA,CAAM7B,CAAAA,CAAWC,EAAMC,CAAO,CAAA,CAC7C,GAAI,OAAOmC,CAAAA,EAAW,SAAU,OAAO,KAAA,CACvCD,EAAO,IAAA,CAAKC,CAAM,EACpB,CAEA,OAAIF,IACAC,CAAAA,CAAO,MAAA,GAAW,CAAA,CAAUA,CAAAA,CAAO,CAAC,CAAA,CACjC,CAAA,kCAAA,EAAqCA,EAAO,IAAA,CAAK,MAAM,CAAC,CAAA,CAAA,CACjE,CAAA,CAEMF,CAAAA,CAAkB,CAAClC,EAAgBC,CAAAA,CAAWC,CAAAA,GACjC2B,EAAM7B,CAAAA,CAAU,EAAA,CAAIC,EAAMC,CAAO,CAAA,GAEjC,IAAA,CAAa2B,CAAAA,CAAM7B,EAAU,IAAA,CAAMC,CAAAA,CAAMC,CAAO,CAAA,CAC1DF,CAAAA,CAAU,KAAO6B,CAAAA,CAAM7B,CAAAA,CAAU,IAAA,CAAMC,CAAAA,CAAMC,CAAO,CAAA,CAAI,IAAA,CAG3D+B,EAAa,CAACjC,CAAAA,CAAsBC,EAAWC,CAAAA,GAAmC,CACtF,IAAMoC,CAAAA,CAAalC,IAAIF,CAAAA,CAASF,CAAAA,CAAU,KAAK,CAAA,CAE/C,GAAI,CAAC,KAAA,CAAM,OAAA,CAAQsC,CAAU,CAAA,CAAG,MAAM,IAAI,KAAA,CAAM,GAAGtC,CAAAA,CAAU,KAAK,mBAAmB,CAAA,CAErF,IAAMM,CAAAA,CAAYiC,CAAAA,EAAuBvC,EAAU,KAAA,EAAS,CAAA,EAAGA,EAAU,KAAK,CAAA,CAAA,EAAIuC,CAAU,CAAA,CAAA,CAGtFC,CAAAA,CAAoB,gDAO1B,CAAA,CAGMC,CAAAA,CAAgB,6BAItB,CAAA,CAEA,GAAID,EAAkB,QAAA,CAASxC,CAAAA,CAAU,aAAa,CAAA,EAAK,CAACA,CAAAA,CAAU,SAAA,CACpE,MAAM,IAAI,KAAA,CAAM,GAAGA,CAAAA,CAAU,aAAa,uDAAuD,CAAA,CAEnG,GAAIyC,CAAAA,CAAc,QAAA,CAASzC,EAAU,aAAa,CAAA,EAAKA,EAAU,KAAA,GAAU,MAAA,CACzE,MAAM,IAAI,KAAA,CAAM,CAAA,EAAGA,CAAAA,CAAU,aAAa,CAAA,iBAAA,CAAmB,CAAA,CAG/D,IAAI0C,CAAAA,CAAU,CAAA,CACVC,EAAW,CAAA,CAEf,GAAIH,CAAAA,CAAkB,QAAA,CAASxC,EAAU,aAAa,CAAA,CAAG,CAEvD,GAAI,CAAC4C,KAAKN,CAAAA,CAAYO,QAAQ,CAAA,CAC5B,MAAM,IAAI,KAAA,CAAM,CAAA,EAAG7C,EAAU,KAAK,CAAA,iHAAA,CAAmH,EAGvJ,IAAM8C,CAAAA,CAAUR,CAAAA,CAAW,GAAA,CAAIS,GAAQlB,CAAAA,CAAM7B,CAAAA,CAAU,UAAY+C,CAAAA,CAAM7C,CAAO,CAAC,CAAA,CACjFwC,CAAAA,CAAUI,CAAAA,CAAQ,MAAA,CAAOE,GAAKA,CAAAA,GAAM,IAAI,EAAE,MAAA,CAC1CL,CAAAA,CAAWG,EAAQ,MAAA,CAAOE,CAAAA,EAAK,OAAOA,CAAAA,EAAM,QAAQ,EAAE,OACxD,CAEA,OAAQhD,CAAAA,CAAU,aAAA,EAChB,KAAA,OAAA,CACE,OAAO,CAACsC,CAAAA,CAAW,QAAUhC,CAAAA,CAAS,eAAe,EAEvD,KAAA,UAAA,CACE,OAAO,CAAC,CAACgC,CAAAA,CAAW,MAAA,EAAUhC,CAAAA,CAAS,mBAAmB,CAAA,CAE5D,KAAA,KAAA,CACE,OAAOoC,CAAAA,GAAYJ,CAAAA,CAAW,QAAUhC,CAAAA,CAAS,CAAA,yBAAA,EAA4BqC,CAAQ,CAAA,QAAA,CAAU,EAEjG,KAAA,KAAA,CACE,OAAO,CAAC,CAACD,CAAAA,EAAWpC,EAAS,iCAAiC,CAAA,CAEhE,KAAA,MAAA,CACE,OAAO,CAACoC,CAAAA,EAAWpC,CAAAA,CAAS,6BAA6BoC,CAAO,CAAA,SAAA,CAAW,EAE7E,KAAA,SAAA,CACE,OAAOA,CAAAA,EAAW1C,CAAAA,CAAU,OAAUM,CAAAA,CAAS,CAAA,SAAA,EAAYN,EAAU,KAAK,CAAA,sBAAA,EAAyB0C,CAAO,CAAA,SAAA,CAAW,CAAA,CAEvH,KAAA,QAAA,CACE,OAAOA,GAAW1C,CAAAA,CAAU,KAAA,EAAUM,EAAS,CAAA,QAAA,EAAWN,CAAAA,CAAU,KAAK,CAAA,sBAAA,EAAyB0C,CAAO,CAAA,SAAA,CAAW,CAAA,CAEtH,eACE,OAAOA,CAAAA,GAAY1C,EAAU,KAAA,EAAUM,CAAAA,CAAS,WAAWN,CAAAA,CAAU,KAAK,yBAAyB0C,CAAO,CAAA,SAAA,CAAW,EAEvH,QACE,MAAM,IAAI,KAAA,CAAM,wBAAwB,CAC5C,CACF","file":"index.js","sourcesContent":["export enum Operator {\n equal = 'equal',\n notEqual = 'notEqual',\n lessThan = 'lessThan',\n lessThanEqual = 'lessThanEqual',\n greaterThan = 'greaterThan',\n greaterThanEqual = 'greaterThanEqual',\n contains = 'contains',\n notContains = 'notContains',\n in = 'in',\n notIn = 'notIn',\n match = 'match',\n notMatch = 'notMatch',\n between = 'between',\n notBetween = 'notBetween',\n isEmpty = 'isEmpty',\n notEmpty = 'notEmpty',\n exists = 'exists',\n notExists = 'notExists',\n startsWith = 'startsWith',\n endsWith = 'endsWith',\n}\n\nexport enum ArrayOperator {\n all = 'all',\n any = 'any',\n none = 'none',\n atLeast = 'atLeast',\n atMost = 'atMost',\n exactly = 'exactly',\n empty = 'empty',\n notEmpty = 'notEmpty',\n}\n\nexport enum DateOperator {\n before = 'before',\n after = 'after',\n onOrBefore = 'onOrBefore',\n onOrAfter = 'onOrAfter',\n between = 'between',\n notBetween = 'notBetween',\n dayIn = 'dayIn', // e.g., ['monday', 'tuesday', 'friday']\n dayNotIn = 'dayNotIn',\n}\n","import { get } from 'lodash';\nimport dayjs from 'dayjs';\nimport utc from 'dayjs/plugin/utc';\nimport timezone from 'dayjs/plugin/timezone';\nimport isSameOrBefore from 'dayjs/plugin/isSameOrBefore';\nimport isSameOrAfter from 'dayjs/plugin/isSameOrAfter';\nimport type { DateRule } from './types';\nimport { DateOperator } from './operator';\n\ndayjs.extend(utc);\ndayjs.extend(timezone);\ndayjs.extend(isSameOrBefore);\ndayjs.extend(isSameOrAfter);\n\nexport const checkDate = (condition: DateRule, data: any, context: any): boolean | string => {\n const fieldValue = get(data, condition.field);\n \n if (!fieldValue) throw new Error(`${condition.field} is null or undefined`);\n \n const fieldDate = dayjs(fieldValue);\n \n if (!fieldDate.isValid()) throw new Error(`${condition.field} is not a valid date: ${fieldValue}`);\n \n const getError = (op: string) => condition.error || `${condition.field} ${op}`;\n \n // Parse comparison dates with timezone context - pass the original string to preserve offset info\n const dates = parseCompareDates(condition, data, context, fieldDate, fieldValue);\n const compareDate = dates[0];\n const endDate = dates[1];\n \n switch (condition.dateOperator) {\n case DateOperator.before:\n return fieldDate.isBefore(compareDate) || getError(`must be before ${compareDate.format()}`);\n \n case DateOperator.after:\n return fieldDate.isAfter(compareDate) || getError(`must be after ${compareDate.format()}`);\n \n case DateOperator.onOrBefore:\n return fieldDate.isSameOrBefore(compareDate) || getError(`must be on or before ${compareDate.format()}`);\n \n case DateOperator.onOrAfter:\n return fieldDate.isSameOrAfter(compareDate) || getError(`must be on or after ${compareDate.format()}`);\n \n case DateOperator.between:\n return (fieldDate.isSameOrAfter(compareDate) && fieldDate.isSameOrBefore(endDate!)) || \n getError(`must be between ${compareDate.format()} and ${endDate!.format()}`);\n \n case DateOperator.notBetween:\n return (fieldDate.isBefore(compareDate) || fieldDate.isAfter(endDate!)) || \n getError(`must not be between ${compareDate.format()} and ${endDate!.format()}`);\n \n case DateOperator.dayIn:\n if (!Array.isArray(condition.value)) throw new Error('dayIn operator requires an array of day names');\n const dayName = fieldDate.format('dddd').toLowerCase();\n const allowedDays = condition.value.map(d => d.toLowerCase());\n return allowedDays.includes(dayName) || getError(`must be on ${allowedDays.join(' or ')}`);\n \n case DateOperator.dayNotIn:\n if (!Array.isArray(condition.value)) throw new Error('dayNotIn operator requires an array of day names');\n const day = fieldDate.format('dddd').toLowerCase();\n const excludedDays = condition.value.map(d => d.toLowerCase());\n return !excludedDays.includes(day) || getError(`must not be on ${excludedDays.join(' or ')}`);\n \n default:\n throw new Error('Unknown date operator');\n }\n}\n\nconst parseCompareDates = (condition: DateRule, data: any, context: any, fieldDate: dayjs.Dayjs, fieldValue: string): [dayjs.Dayjs, dayjs.Dayjs | undefined] => {\n const requiresTwoDates = [DateOperator.between, DateOperator.notBetween];\n \n if (requiresTwoDates.includes(condition.dateOperator)) {\n if (!Array.isArray(condition.value) || condition.value.length !== 2) \n throw new Error(`${condition.dateOperator} operator requires an array of two dates`);\n const startDate = parseDateWithTimezone(condition.value[0], fieldValue);\n const endDate = parseDateWithTimezone(condition.value[1], fieldValue);\n if (!startDate.isValid()) throw new Error(`Invalid start date: ${condition.value[0]}`);\n if (!endDate.isValid()) throw new Error(`Invalid end date: ${condition.value[1]}`);\n return [startDate, endDate];\n }\n \n const requiresOneDate = [\n DateOperator.before,\n DateOperator.after,\n DateOperator.onOrBefore,\n DateOperator.onOrAfter\n ];\n \n if (requiresOneDate.includes(condition.dateOperator)) {\n let value;\n if (condition.value !== undefined) {\n value = condition.value;\n } else if (condition.path) {\n // Support $.path for current element\n if (condition.path.startsWith('$.')) {\n value = get(data, condition.path.substring(2));\n } else {\n value = get(context, condition.path);\n }\n } else {\n throw new Error('No value or path specified for date comparison');\n }\n const date = parseDateWithTimezone(value, fieldValue);\n if (!date.isValid()) throw new Error(`Invalid comparison date: ${value}`);\n return [date, undefined];\n }\n \n return [dayjs(), undefined]; // Won't be used for dayIn/dayNotIn\n}\n\nconst parseDateWithTimezone = (value: any, fieldValue: string): dayjs.Dayjs => {\n const valueStr = String(value);\n \n // Check if value has explicit timezone information\n const hasTimezone = valueStr.includes('Z') || \n (valueStr.includes('T') && (valueStr.includes('+') || valueStr.match(/T.*-\\d{2}:/)));\n \n if (hasTimezone) return dayjs(value);\n \n // No timezone info in value - interpret in field's timezone\n // Extract offset from field value\n const fieldStr = String(fieldValue);\n let offset = 0;\n \n if (fieldStr.includes('+') || (fieldStr.includes('T') && fieldStr.match(/T.*-\\d{2}:/))) {\n // Field has explicit offset like +11:00 or -08:00\n const match = fieldStr.match(/([+-])(\\d{2}):(\\d{2})/);\n if (match) {\n const sign = match[1] === '+' ? 1 : -1;\n offset = sign * (parseInt(match[2]) * 60 + parseInt(match[3]));\n }\n } else if (!fieldStr.includes('Z')) {\n // Field has no timezone, assume local time (offset 0)\n offset = 0;\n }\n // If field has Z, it's UTC (offset 0)\n \n // Create a date representing the same local time as the field's timezone\n if (valueStr.match(/^\\d{4}-\\d{2}-\\d{2}$/)) {\n // For date-only, we want midnight in the field's timezone\n const localMidnight = dayjs(value + 'T00:00:00');\n return localMidnight.subtract(offset, 'minute');\n }\n \n // For datetime without timezone, interpret as local time in field's timezone\n const localTime = dayjs(value);\n return localTime.subtract(offset, 'minute');\n}","import { get, isEmpty } from 'lodash';\nimport type { Rule } from './types';\nimport { Operator } from './operator';\n\nexport const checkField = (condition: Rule, data: any, context: any): boolean | string => {\n // Use data for field access (current element) but context remains available for path references\n const fieldValue = get(data, condition.field);\n \n // Operators that don't need a value\n const noValueOps = [Operator.isEmpty, Operator.notEmpty, Operator.exists, Operator.notExists];\n const needsValue = !noValueOps.includes(condition.operator);\n const value = needsValue ? getValue(condition, data, context) : undefined;\n \n const getError = (op: string) => condition.error || `${condition.field} ${op}${needsValue ? ' ' + JSON.stringify(value) : ''}`;\n\n switch (condition.operator) {\n case Operator.equal:\n return fieldValue === value || getError(`must equal`);\n case Operator.notEqual:\n return fieldValue !== value || getError(`must not equal`);\n case Operator.lessThan:\n return fieldValue < value || getError(`must be less than`);\n case Operator.lessThanEqual:\n return fieldValue <= value || getError(`must be less than or equal to`);\n case Operator.greaterThan:\n return fieldValue > value || getError(`must be greater than`);\n case Operator.greaterThanEqual:\n return fieldValue >= value || getError(`must be greater than or equal to`);\n case Operator.in:\n return value?.includes(fieldValue) || getError(`must be one of`);\n case Operator.notIn:\n return !value?.includes(fieldValue) || getError(`must not be one of`);\n case Operator.contains:\n return fieldValue?.includes(value) || getError(`must contain`);\n case Operator.notContains:\n return !fieldValue?.includes(value) || getError(`must not contain`);\n case Operator.match:\n return !!fieldValue?.match(value) || getError(`must match pattern`);\n case Operator.notMatch:\n return !fieldValue?.match(value) || getError(`must not match pattern`);\n case Operator.between:\n if (!Array.isArray(value) || value.length !== 2) \n throw new Error('between operator requires an array of two values');\n return (fieldValue >= value[0] && fieldValue <= value[1]) || getError(`must be between`);\n case Operator.notBetween:\n if (!Array.isArray(value) || value.length !== 2) \n throw new Error('notBetween operator requires an array of two values');\n return (fieldValue < value[0] || fieldValue > value[1]) || getError(`must not be between`);\n case Operator.isEmpty:\n return isEmpty(fieldValue) || getError(`must be empty`);\n case Operator.notEmpty:\n return !isEmpty(fieldValue) || getError(`must not be empty`);\n case Operator.exists:\n return fieldValue !== undefined || getError(`must exist`);\n case Operator.notExists:\n return fieldValue === undefined || getError(`must not exist`);\n case Operator.startsWith:\n return fieldValue?.startsWith?.(value) || getError(`must start with`);\n case Operator.endsWith:\n return fieldValue?.endsWith?.(value) || getError(`must end with`);\n default:\n throw new Error('Unknown operator');\n }\n};\n\nconst getValue = (condition: Rule, data: any, context: any): any => {\n if (condition.value !== undefined) return condition.value;\n if (condition.path) {\n // Special case: if path starts with \"$.\" use data (current element)\n if (condition.path.startsWith('$.')) {\n return get(data, condition.path.substring(2));\n }\n // Otherwise use context (root data)\n return get(context, condition.path);\n }\n throw new Error('No value or path specified');\n};","import { get, some, isObject } from 'lodash';\nimport type { Condition, ArrayRule } from './types';\nimport { ArrayOperator } from './operator';\nimport { checkDate } from './date';\nimport { checkField } from './field';\n\nexport const check = (conditions: Condition, data: any, context: any = data): boolean | string => {\n if (typeof conditions === 'boolean') return conditions;\n if ('all' in conditions) return all(conditions.all, data, context, conditions.error);\n if ('any' in conditions) return any(conditions.any, data, context, conditions.error);\n if ('arrayOperator' in conditions) return checkArray(conditions, data, context);\n if ('dateOperator' in conditions) return checkDate(conditions, data, context);\n if ('field' in conditions) return checkField(conditions, data, context);\n if ('if' in conditions) return checkIfThenElse(conditions, data, context);\n\n return false;\n}\n\nconst all = (conditions: Condition[], data: any, context: any, error?: string): boolean | string => {\n const errors: string[] = [];\n \n for (const condition of conditions) {\n const result = check(condition, data, context);\n if (result !== true) {\n // Handle both string errors and false boolean results\n if (typeof result === 'string') {\n errors.push(result);\n } else {\n // For boolean false, include it in the error message\n errors.push('false');\n }\n }\n }\n\n if (!errors.length) return true;\n if (error) return error;\n if (errors.length === 1) return errors[0];\n return `All conditions must pass: ${errors.join(' AND ')}`;\n}\n\nconst any = (conditions: Condition[], data: any, context: any, error?: string): boolean | string => {\n const errors: string[] = [];\n\n for (const condition of conditions) {\n const result = check(condition, data, context);\n if (typeof result !== 'string') return true;\n errors.push(result);\n }\n\n if (error) return error;\n if (errors.length === 1) return errors[0];\n return `At least one condition must pass: ${errors.join(' OR ')}`;\n}\n\nconst checkIfThenElse = (condition: any, data: any, context: any): boolean | string => {\n const ifResult = check(condition.if, data, context);\n \n if (ifResult === true) return check(condition.then, data, context);\n return condition.else ? check(condition.else, data, context) : true;\n}\n\nconst checkArray = (condition: ArrayRule, data: any, context: any): boolean | string => {\n const arrayValue = get(context, condition.field);\n \n if (!Array.isArray(arrayValue)) throw new Error(`${condition.field} must be an array`);\n \n const getError = (defaultMsg: string) => condition.error || `${condition.field} ${defaultMsg}`;\n \n // Operators that require a condition\n const requiresCondition = [\n ArrayOperator.all, \n ArrayOperator.any, \n ArrayOperator.none,\n ArrayOperator.atLeast,\n ArrayOperator.atMost,\n ArrayOperator.exactly\n ];\n \n // Operators that require a count\n const requiresCount = [\n ArrayOperator.atLeast,\n ArrayOperator.atMost,\n ArrayOperator.exactly\n ];\n \n if (requiresCondition.includes(condition.arrayOperator) && !condition.condition) \n throw new Error(`${condition.arrayOperator} requires a condition to check against array elements`);\n \n if (requiresCount.includes(condition.arrayOperator) && condition.count === undefined) \n throw new Error(`${condition.arrayOperator} requires a count`);\n \n // For operators that check elements, compute matches\n let matches = 0;\n let failures = 0;\n \n if (requiresCondition.includes(condition.arrayOperator)) {\n // Check if array contains any objects\n if (!some(arrayValue, isObject)) \n throw new Error(`${condition.field} contains only primitive values. Use 'in' or 'contains' operators instead of array operators for primitive arrays`);\n \n // Pass item as data (for relative field access) but keep original context (for path access)\n const results = arrayValue.map(item => check(condition.condition!, item, context));\n matches = results.filter(r => r === true).length;\n failures = results.filter(r => typeof r === 'string').length;\n }\n \n switch (condition.arrayOperator) {\n case ArrayOperator.empty:\n return !arrayValue.length || getError('must be empty');\n \n case ArrayOperator.notEmpty:\n return !!arrayValue.length || getError('must not be empty');\n \n case ArrayOperator.all:\n return matches === arrayValue.length || getError(`all elements must match (${failures} failed)`);\n \n case ArrayOperator.any:\n return !!matches || getError('at least one element must match');\n \n case ArrayOperator.none:\n return !matches || getError(`no elements should match (${matches} matched)`);\n \n case ArrayOperator.atLeast:\n return matches >= condition.count! || getError(`at least ${condition.count} elements must match (${matches} matched)`);\n \n case ArrayOperator.atMost:\n return matches <= condition.count! || getError(`at most ${condition.count} elements must match (${matches} matched)`);\n \n case ArrayOperator.exactly:\n return matches === condition.count! || getError(`exactly ${condition.count} elements must match (${matches} matched)`);\n \n default:\n throw new Error('Unknown array operator');\n }\n}"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@inixiative/json-rules",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "TypeScript-first JSON rules engine with intuitive syntax and detailed error messages",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/index.mjs",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"type": "module",
|
|
9
|
+
"files": [
|
|
10
|
+
"dist",
|
|
11
|
+
"README.md",
|
|
12
|
+
"LICENSE"
|
|
13
|
+
],
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsup",
|
|
16
|
+
"test": "bun test",
|
|
17
|
+
"prepublishOnly": "bun run build"
|
|
18
|
+
},
|
|
19
|
+
"keywords": [
|
|
20
|
+
"json",
|
|
21
|
+
"rules",
|
|
22
|
+
"engine",
|
|
23
|
+
"validation",
|
|
24
|
+
"typescript",
|
|
25
|
+
"rules-engine",
|
|
26
|
+
"business-rules"
|
|
27
|
+
],
|
|
28
|
+
"author": "Aron Greenspan",
|
|
29
|
+
"license": "MIT",
|
|
30
|
+
"repository": {
|
|
31
|
+
"type": "git",
|
|
32
|
+
"url": "https://github.com/inixiative/json-rules"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@types/bun": "latest",
|
|
36
|
+
"@types/lodash": "^4.17.20",
|
|
37
|
+
"tsup": "^8.5.0",
|
|
38
|
+
"typescript": "^5.0.0"
|
|
39
|
+
},
|
|
40
|
+
"peerDependencies": {
|
|
41
|
+
"typescript": "^5.0.0"
|
|
42
|
+
},
|
|
43
|
+
"peerDependenciesMeta": {
|
|
44
|
+
"typescript": {
|
|
45
|
+
"optional": true
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
"dependencies": {
|
|
49
|
+
"dayjs": "^1.11.13",
|
|
50
|
+
"lodash": "^4.17.21"
|
|
51
|
+
},
|
|
52
|
+
"exports": {
|
|
53
|
+
".": {
|
|
54
|
+
"types": "./dist/index.d.ts",
|
|
55
|
+
"import": "./dist/index.mjs",
|
|
56
|
+
"require": "./dist/index.js"
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|