@inixiative/json-rules 2.18.4 → 2.19.1

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
@@ -494,6 +494,42 @@ Not every backend supports every rule shape.
494
494
  | Windowing (`orderBy` / `take` / `skip`) | Yes | Extremal (`take:1`, aligned) | No |
495
495
  | `path: '$.field'` current-element / same-row refs | Yes | No | Yes |
496
496
 
497
+ ### NULL Semantics
498
+
499
+ A negated operator is the complement of its positive form — the same answer
500
+ `check()` gives, where `null !== 'x'` is simply true. SQL's three-valued logic
501
+ disagrees (`col <> 'x'` is NULL, never true, for a NULL column), so the
502
+ compilers carry NULL rows explicitly:
503
+
504
+ | Rule | `check()` on `{ col: null }` | `toSql()` | `toPrisma()` (nullable column) |
505
+ | --- | --- | --- | --- |
506
+ | `notEquals 'x'` / `notContains` / `notMatches` / `notBetween` | matches | `(col <> $1 OR col IS NULL)` | `{ OR: [{ col: { not: 'x' } }, { col: { equals: null } }] }` |
507
+ | `notIn ['x']` | matches | `(col <> ALL($1) OR col IS NULL)` | `{ OR: [{ col: { notIn: ['x'] } }, { col: { equals: null } }] }` |
508
+ | `in ['x', null]` | matches | `(col = ANY($1) OR col IS NULL)` | `{ OR: [{ col: { in: ['x'] } }, { col: { equals: null } }] }` |
509
+ | `notIn ['x', null]` | no match | `(col <> ALL($1) AND col IS NOT NULL)` | `{ AND: [{ col: { notIn: ['x'] } }, { col: { not: null } }] }` |
510
+ | `equals` / `notEquals` with `path: '$.other'` | `null === null` | `IS [NOT] DISTINCT FROM` | — |
511
+ | `exists` / `notExists` | `!= null` / `== null` | `IS NOT NULL` / `IS NULL` | `{ not: null }` / `{ equals: null }` |
512
+
513
+ `toPrisma()` can only add the null arm when it knows the column is nullable —
514
+ an `equals: null` on a NOT NULL column is a Prisma validation error. Nullability
515
+ comes from the field map: `FieldMapEntry.isRequired: false` (prisma-map emits it).
516
+ Without `{ map, model }`, or on an entry that doesn't declare it, the bare
517
+ `not` / `notIn` is emitted and NULL rows fall out, as they always did.
518
+
519
+ Date rules answer the same way on both rails, from the other direction: a bare
520
+ boundary is not something a NULL column satisfies, so `check()` reports the rule's
521
+ ordinary non-match (honoring `error`) for a null or absent field, and the compilers
522
+ keep the bare `<` / `NOT BETWEEN`. To match the never-seen rows too, ask for them:
523
+ `{ any: [{ field, operator: 'notExists' }, { field, dateOperator: 'before', … }] }`.
524
+
525
+ | Rule | `check()` on `{ col: null }` | `toSql()` | `toPrisma()` |
526
+ | --- | --- | --- | --- |
527
+ | any `dateOperator` | no match | `col < $1` (NULL never satisfies) | `{ col: { lt: … } }` |
528
+ | `notExists` OR `before` | matches via the first arm | `(col IS NULL OR col < $1)` | `{ OR: [{ col: { equals: null } }, { col: { lt: … } }] }` |
529
+
530
+ `0` is an instant (1970-01-01) and compares; `''` is malformed data and raises
531
+ `"is not a valid date"`.
532
+
497
533
  ### Prisma Limitations
498
534
 
499
535
  - `matches` and `notMatches` are not supported by Prisma output