@warlock.js/seal 5.9.0 → 5.11.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.
@@ -1,12 +1,12 @@
1
1
  import { getRuleOptions } from "../standard-schema/json-schema.mjs";
2
2
  import { VALID_RULE, invalidRule } from "../helpers/validation-helpers.mjs";
3
3
  import "../helpers/index.mjs";
4
+ import { acceptedIfPresentRule, acceptedIfRequiredRule, acceptedIfRule, acceptedRule, acceptedUnlessRule, acceptedWithoutRule } from "../rules/scalar/accepted-rule.mjs";
5
+ import { declinedIfPresentRule, declinedIfRequiredRule, declinedIfRule, declinedRule, declinedUnlessRule, declinedWithoutRule } from "../rules/scalar/declined-rule.mjs";
4
6
  import { numberMutator } from "../mutators/number-mutators.mjs";
5
7
  import { stringMutator } from "../mutators/scalar-mutators.mjs";
6
8
  import "../mutators/index.mjs";
7
9
  import { PrimitiveValidator } from "./primitive-validator.mjs";
8
- import { acceptedIfPresentRule, acceptedIfRequiredRule, acceptedIfRule, acceptedRule, acceptedUnlessRule, acceptedWithoutRule } from "../rules/scalar/accepted-rule.mjs";
9
- import { declinedIfPresentRule, declinedIfRequiredRule, declinedIfRule, declinedRule, declinedUnlessRule, declinedWithoutRule } from "../rules/scalar/declined-rule.mjs";
10
10
  import "../rules/scalar/index.mjs";
11
11
 
12
12
  //#region ../seal/src/validators/scalar-validator.ts
package/llms-full.txt CHANGED
@@ -1553,41 +1553,68 @@ Picking guide is in [`@warlock.js/seal/pick-seal-primitive/SKILL.md`](@warlock.j
1553
1553
 
1554
1554
  ## Strict equality
1555
1555
 
1556
- | Method | Effect |
1557
- |---|---|
1558
- | `.mustBeTrue(msg?)` | strictly `=== true` (rejects `"yes"`, `1`, `"on"`) |
1559
- | `.mustBeFalse(msg?)` | strictly `=== false` |
1556
+ | Method | Effect |
1557
+ | -------------------- | -------------------------------------------------- |
1558
+ | `.mustBeTrue(msg?)` | strictly `=== true` (rejects `"yes"`, `1`, `"on"`) |
1559
+ | `.mustBeFalse(msg?)` | strictly `=== false` |
1560
1560
 
1561
1561
  Use these when the field is a real boolean checkbox value — e.g. "agree to terms" must be exactly `true`, not a truthy string.
1562
1562
 
1563
1563
  ## Form-style coercion — `accepted` / `declined`
1564
1564
 
1565
- The "accepted" rules treat `true`, `"yes"`, `"on"`, `1`, `"1"`, `"true"` as accepted. "Declined" treats their counterparts (`false`, `"no"`, `"off"`, `0`, etc.) as declined. Designed for form inputs where a checkbox/radio arrives as a string.
1566
-
1567
- | Method | Effect |
1568
- |---|---|
1565
+ Calling `.accepted()`/`.declined()` (or any conditional variant) opts `v.boolean()` into
1566
+ form-boolean parsing: a mutator runs before the type rule and converts the accepted set
1567
+ (`1`, `"1"`, `true`, `"true"`, `"yes"`, `"y"`, `"on"`, `"Yes"`, `"Y"`, `"On"`) to `true` and
1568
+ the declined set (`0`, `"0"`, `false`, `"false"`, `"no"`, `"n"`, `"off"`, `"No"`, `"N"`,
1569
+ `"Off"`) to `false`; any other value passes through unchanged and still fails the type rule.
1570
+ So `v.boolean().accepted()` given `"yes"` is valid **and its output is the real boolean
1571
+ `true`** — not the original string. Designed for form inputs where a checkbox/radio arrives
1572
+ as a string. `v.scalar().accepted()` runs the same accepted/declined rule but keeps the raw
1573
+ value untouched (no boolean type rule, no parsing mutator) — use it when you want the
1574
+ pass/fail check without normalizing the output.
1575
+
1576
+ | Method | Effect |
1577
+ | ----------------- | ---------------------- |
1569
1578
  | `.accepted(msg?)` | value must be accepted |
1570
1579
  | `.declined(msg?)` | value must be declined |
1571
1580
 
1581
+ ## Query-string coercion — `.coerce()`
1582
+
1583
+ `.coerce()` (5.10+) is the opt-in for query-string flags: `?active=true` arrives as the
1584
+ string `"true"`, and bare `v.boolean()` is strict by default — it rejects that string. Add
1585
+ `.coerce()` when a schema validates `request.query`:
1586
+
1587
+ ```ts
1588
+ v.object({ active: v.boolean().coerce() }); // "true" → true, passes
1589
+ ```
1590
+
1591
+ It converts _exactly_ `"true"` / `"1"` / `1` → `true` and `"false"` / `"0"` / `0` → `false`.
1592
+ Case-sensitive, no trimming — anything else (`"yes"`, `"on"`, `"TRUE"`, `""`, `2`) passes
1593
+ through unchanged and still fails the type rule. That's the line between this and
1594
+ `accepted()`/`declined()` below: `.coerce()` is for the narrow, exact query-string
1595
+ serialization; `accepted()`/`declined()` are for broader form-style truthy strings like
1596
+ `"yes"`/`"on"`. `v.boolean()` does not coerce by default; the output type is unaffected
1597
+ (`v.boolean().coerce()` still infers `boolean`).
1598
+
1572
1599
  ## Conditional variants — accepted
1573
1600
 
1574
- | Method | Args | Effect |
1575
- |---|---|---|
1576
- | `.acceptedIf(field, value, msg?)` | sibling field equals value | must be accepted in that case |
1601
+ | Method | Args | Effect |
1602
+ | ------------------------------------- | -------------------------- | ----------------------------------- |
1603
+ | `.acceptedIf(field, value, msg?)` | sibling field equals value | must be accepted in that case |
1577
1604
  | `.acceptedUnless(field, value, msg?)` | sibling field equals value | must be accepted unless that's true |
1578
- | `.acceptedIfRequired(field, msg?)` | sibling field is required | — |
1579
- | `.acceptedIfPresent(field, msg?)` | sibling field is present | — |
1580
- | `.acceptedWithout(field, msg?)` | sibling field is absent | — |
1605
+ | `.acceptedIfRequired(field, msg?)` | sibling field is required | — |
1606
+ | `.acceptedIfPresent(field, msg?)` | sibling field is present | — |
1607
+ | `.acceptedWithout(field, msg?)` | sibling field is absent | — |
1581
1608
 
1582
1609
  ## Conditional variants — declined
1583
1610
 
1584
- | Method | Args | Effect |
1585
- |---|---|---|
1586
- | `.declinedIf(field, value, msg?)` | sibling field equals value | must be declined in that case |
1611
+ | Method | Args | Effect |
1612
+ | ------------------------------------- | -------------------------- | ----------------------------------- |
1613
+ | `.declinedIf(field, value, msg?)` | sibling field equals value | must be declined in that case |
1587
1614
  | `.declinedUnless(field, value, msg?)` | sibling field equals value | must be declined unless that's true |
1588
- | `.declinedIfRequired(field, msg?)` | — | — |
1589
- | `.declinedIfPresent(field, msg?)` | — | — |
1590
- | `.declinedWithout(field, msg?)` | — | — |
1615
+ | `.declinedIfRequired(field, msg?)` | — | — |
1616
+ | `.declinedIfPresent(field, msg?)` | — | — |
1617
+ | `.declinedWithout(field, msg?)` | — | — |
1591
1618
 
1592
1619
  All conditional variants only run inside `v.object` — sibling resolution silently passes otherwise.
1593
1620
 
@@ -1596,27 +1623,33 @@ All conditional variants only run inside `v.object` — sibling resolution silen
1596
1623
  - `v.boolean()` → `{ type: "boolean" }`
1597
1624
  - `.mustBeTrue()` / `.mustBeFalse()` — not currently emitted (could add `const: true/false` in the future)
1598
1625
  - `.accepted()` / `.declined()` and their conditional variants — runtime coercion concerns, not representable
1626
+ - `.coerce()` — a pre-validation mutator, not representable; the schema still emits `{ type: "boolean" }`
1599
1627
 
1600
1628
  ## Common chains
1601
1629
 
1602
1630
  ```ts
1603
1631
  // Strict consent checkbox
1604
- v.boolean().mustBeTrue("You must accept the terms")
1632
+ v.boolean().mustBeTrue("You must accept the terms");
1605
1633
 
1606
1634
  // Form-style "remember me" — accepts "on" / true / 1
1607
- v.boolean().accepted().optional()
1635
+ v.boolean().accepted().optional();
1608
1636
 
1609
1637
  // Cross-field — newsletter must be accepted if subscriptionType = "premium"
1610
1638
  v.object({
1611
1639
  subscriptionType: v.string().oneOf(["free", "premium"]),
1612
1640
  newsletter: v.boolean().acceptedIf("subscriptionType", "premium"),
1613
- })
1641
+ });
1614
1642
 
1615
1643
  // Marketing opt-in — declined unless region is GDPR-exempt
1616
1644
  v.object({
1617
1645
  region: v.string(),
1618
1646
  marketingOptIn: v.boolean().declinedUnless("region", "US"),
1619
- })
1647
+ });
1648
+
1649
+ // Query-string flag — ?active=true
1650
+ v.object({
1651
+ active: v.boolean().coerce(),
1652
+ });
1620
1653
  ```
1621
1654
 
1622
1655
 
package/package.json CHANGED
@@ -22,7 +22,7 @@
22
22
  ],
23
23
  "author": "hassanzohdy",
24
24
  "license": "MIT",
25
- "version": "5.9.0",
25
+ "version": "5.11.0",
26
26
  "main": "./cjs/index.cjs",
27
27
  "module": "./esm/index.mjs",
28
28
  "types": "./esm/index.d.mts",
@@ -4,41 +4,68 @@ Picking guide is in [`@warlock.js/seal/pick-seal-primitive/SKILL.md`](@warlock.j
4
4
 
5
5
  ## Strict equality
6
6
 
7
- | Method | Effect |
8
- |---|---|
9
- | `.mustBeTrue(msg?)` | strictly `=== true` (rejects `"yes"`, `1`, `"on"`) |
10
- | `.mustBeFalse(msg?)` | strictly `=== false` |
7
+ | Method | Effect |
8
+ | -------------------- | -------------------------------------------------- |
9
+ | `.mustBeTrue(msg?)` | strictly `=== true` (rejects `"yes"`, `1`, `"on"`) |
10
+ | `.mustBeFalse(msg?)` | strictly `=== false` |
11
11
 
12
12
  Use these when the field is a real boolean checkbox value — e.g. "agree to terms" must be exactly `true`, not a truthy string.
13
13
 
14
14
  ## Form-style coercion — `accepted` / `declined`
15
15
 
16
- The "accepted" rules treat `true`, `"yes"`, `"on"`, `1`, `"1"`, `"true"` as accepted. "Declined" treats their counterparts (`false`, `"no"`, `"off"`, `0`, etc.) as declined. Designed for form inputs where a checkbox/radio arrives as a string.
17
-
18
- | Method | Effect |
19
- |---|---|
16
+ Calling `.accepted()`/`.declined()` (or any conditional variant) opts `v.boolean()` into
17
+ form-boolean parsing: a mutator runs before the type rule and converts the accepted set
18
+ (`1`, `"1"`, `true`, `"true"`, `"yes"`, `"y"`, `"on"`, `"Yes"`, `"Y"`, `"On"`) to `true` and
19
+ the declined set (`0`, `"0"`, `false`, `"false"`, `"no"`, `"n"`, `"off"`, `"No"`, `"N"`,
20
+ `"Off"`) to `false`; any other value passes through unchanged and still fails the type rule.
21
+ So `v.boolean().accepted()` given `"yes"` is valid **and its output is the real boolean
22
+ `true`** — not the original string. Designed for form inputs where a checkbox/radio arrives
23
+ as a string. `v.scalar().accepted()` runs the same accepted/declined rule but keeps the raw
24
+ value untouched (no boolean type rule, no parsing mutator) — use it when you want the
25
+ pass/fail check without normalizing the output.
26
+
27
+ | Method | Effect |
28
+ | ----------------- | ---------------------- |
20
29
  | `.accepted(msg?)` | value must be accepted |
21
30
  | `.declined(msg?)` | value must be declined |
22
31
 
32
+ ## Query-string coercion — `.coerce()`
33
+
34
+ `.coerce()` (5.10+) is the opt-in for query-string flags: `?active=true` arrives as the
35
+ string `"true"`, and bare `v.boolean()` is strict by default — it rejects that string. Add
36
+ `.coerce()` when a schema validates `request.query`:
37
+
38
+ ```ts
39
+ v.object({ active: v.boolean().coerce() }); // "true" → true, passes
40
+ ```
41
+
42
+ It converts _exactly_ `"true"` / `"1"` / `1` → `true` and `"false"` / `"0"` / `0` → `false`.
43
+ Case-sensitive, no trimming — anything else (`"yes"`, `"on"`, `"TRUE"`, `""`, `2`) passes
44
+ through unchanged and still fails the type rule. That's the line between this and
45
+ `accepted()`/`declined()` below: `.coerce()` is for the narrow, exact query-string
46
+ serialization; `accepted()`/`declined()` are for broader form-style truthy strings like
47
+ `"yes"`/`"on"`. `v.boolean()` does not coerce by default; the output type is unaffected
48
+ (`v.boolean().coerce()` still infers `boolean`).
49
+
23
50
  ## Conditional variants — accepted
24
51
 
25
- | Method | Args | Effect |
26
- |---|---|---|
27
- | `.acceptedIf(field, value, msg?)` | sibling field equals value | must be accepted in that case |
52
+ | Method | Args | Effect |
53
+ | ------------------------------------- | -------------------------- | ----------------------------------- |
54
+ | `.acceptedIf(field, value, msg?)` | sibling field equals value | must be accepted in that case |
28
55
  | `.acceptedUnless(field, value, msg?)` | sibling field equals value | must be accepted unless that's true |
29
- | `.acceptedIfRequired(field, msg?)` | sibling field is required | — |
30
- | `.acceptedIfPresent(field, msg?)` | sibling field is present | — |
31
- | `.acceptedWithout(field, msg?)` | sibling field is absent | — |
56
+ | `.acceptedIfRequired(field, msg?)` | sibling field is required | — |
57
+ | `.acceptedIfPresent(field, msg?)` | sibling field is present | — |
58
+ | `.acceptedWithout(field, msg?)` | sibling field is absent | — |
32
59
 
33
60
  ## Conditional variants — declined
34
61
 
35
- | Method | Args | Effect |
36
- |---|---|---|
37
- | `.declinedIf(field, value, msg?)` | sibling field equals value | must be declined in that case |
62
+ | Method | Args | Effect |
63
+ | ------------------------------------- | -------------------------- | ----------------------------------- |
64
+ | `.declinedIf(field, value, msg?)` | sibling field equals value | must be declined in that case |
38
65
  | `.declinedUnless(field, value, msg?)` | sibling field equals value | must be declined unless that's true |
39
- | `.declinedIfRequired(field, msg?)` | — | — |
40
- | `.declinedIfPresent(field, msg?)` | — | — |
41
- | `.declinedWithout(field, msg?)` | — | — |
66
+ | `.declinedIfRequired(field, msg?)` | — | — |
67
+ | `.declinedIfPresent(field, msg?)` | — | — |
68
+ | `.declinedWithout(field, msg?)` | — | — |
42
69
 
43
70
  All conditional variants only run inside `v.object` — sibling resolution silently passes otherwise.
44
71
 
@@ -47,25 +74,31 @@ All conditional variants only run inside `v.object` — sibling resolution silen
47
74
  - `v.boolean()` → `{ type: "boolean" }`
48
75
  - `.mustBeTrue()` / `.mustBeFalse()` — not currently emitted (could add `const: true/false` in the future)
49
76
  - `.accepted()` / `.declined()` and their conditional variants — runtime coercion concerns, not representable
77
+ - `.coerce()` — a pre-validation mutator, not representable; the schema still emits `{ type: "boolean" }`
50
78
 
51
79
  ## Common chains
52
80
 
53
81
  ```ts
54
82
  // Strict consent checkbox
55
- v.boolean().mustBeTrue("You must accept the terms")
83
+ v.boolean().mustBeTrue("You must accept the terms");
56
84
 
57
85
  // Form-style "remember me" — accepts "on" / true / 1
58
- v.boolean().accepted().optional()
86
+ v.boolean().accepted().optional();
59
87
 
60
88
  // Cross-field — newsletter must be accepted if subscriptionType = "premium"
61
89
  v.object({
62
90
  subscriptionType: v.string().oneOf(["free", "premium"]),
63
91
  newsletter: v.boolean().acceptedIf("subscriptionType", "premium"),
64
- })
92
+ });
65
93
 
66
94
  // Marketing opt-in — declined unless region is GDPR-exempt
67
95
  v.object({
68
96
  region: v.string(),
69
97
  marketingOptIn: v.boolean().declinedUnless("region", "US"),
70
- })
98
+ });
99
+
100
+ // Query-string flag — ?active=true
101
+ v.object({
102
+ active: v.boolean().coerce(),
103
+ });
71
104
  ```