@inixiative/json-rules 2.19.3 → 2.19.5

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
@@ -102,6 +102,7 @@ Supported comparison operators for aggregate rules: `equals`, `notEquals`, `less
102
102
  - `onOrBefore`
103
103
  - `onOrAfter`
104
104
  - `within`
105
+ - `notWithin`
105
106
  - `between`
106
107
  - `notBetween`
107
108
  - `dayIn`
@@ -234,7 +235,7 @@ or as `between` endpoints:
234
235
  { field: 'completedAt', dateOperator: DateOperator.before, value: { end: { last: 'month' } } }
235
236
  ```
236
237
 
237
- **Range expressions** — pair with the `within` operator:
238
+ **Range expressions** — pair with `within` / `notWithin`:
238
239
 
239
240
  ```ts
240
241
  // "this month"
@@ -245,6 +246,10 @@ or as `between` endpoints:
245
246
 
246
247
  // rolling window: "within the last 30 days" → [now - 30d, now]
247
248
  { field: 'completedAt', dateOperator: DateOperator.within, value: { ago: { days: 30 } } }
249
+
250
+ // its complement: "not in the last 30 days" — a never-set date is not in the window, so
251
+ // NULL matches (see NULL Semantics). The dormancy rule, in one leaf.
252
+ { field: 'lastLoginAt', dateOperator: DateOperator.notWithin, value: { ago: { days: 30 } } }
248
253
  ```
249
254
 
250
255
  A **bare period** with `before` / `after` resolves to the only sensible edge —
@@ -531,7 +536,7 @@ Without `{ map, model }`, or on an entry that doesn't declare it, the bare
531
536
  Date rules follow the same split. The positive operators answer non-match on both
532
537
  rails — a bare boundary is not something a NULL column satisfies, so `check()`
533
538
  reports the rule's ordinary non-match (honoring `error`) and the compilers keep the
534
- bare `<` / `BETWEEN`. The negative-flavored ones (`notBetween`, `dayNotIn`) follow
539
+ bare `<` / `BETWEEN`. The negative-flavored ones (`notWithin`, `notBetween`, `dayNotIn`) follow
535
540
  the negation ruling instead: a never-set date is not in the range, so a null column
536
541
  MATCHES, and the compilers carry the `IS NULL` arm. To match never-seen rows under a
537
542
  positive operator, ask for them:
@@ -540,6 +545,7 @@ positive operator, ask for them:
540
545
  | Rule | `check()` on `{ col: null }` | `toSql()` | `toPrisma()` |
541
546
  | --- | --- | --- | --- |
542
547
  | positive `dateOperator` (`before`, `between`, `dayIn`, …) | no match | `col < $1` (NULL never satisfies) | `{ col: { lt: … } }` |
548
+ | `notWithin { ago: { days: 30 } }` | matches | `(col NOT BETWEEN $1 AND $2 OR col IS NULL)` | `{ OR: [{ col: { NOT: { gte, lte } } }, { col: { equals: null } }] }` (nullable column) |
543
549
  | `notBetween` | matches | `(col NOT BETWEEN $1 AND $2 OR col IS NULL)` | `{ OR: [{ col: { NOT: … } }, { col: { equals: null } }] }` (nullable column, same field-map licensing as above) |
544
550
  | `dayNotIn` | matches | `(EXTRACT(DOW FROM col) <> ALL($1) OR col IS NULL)` | — (no Prisma output) |
545
551
  | `notExists` OR `before` | matches via the first arm | `(col IS NULL OR col < $1)` | `{ OR: [{ col: { equals: null } }, { col: { lt: … } }] }` |