anyvali 0.3.4 → 0.3.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/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.3.5](https://github.com/BetterCorp/AnyVali/compare/js-v0.3.4...js-v0.3.5) (2026-06-17)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * infer default coercion source ([19577b3](https://github.com/BetterCorp/AnyVali/commit/19577b3d59e00e246fe2021c0b7e30e4196a5fa3))
9
+
3
10
  ## [0.3.4](https://github.com/BetterCorp/AnyVali/compare/js-v0.3.3...js-v0.3.4) (2026-06-14)
4
11
 
5
12
 
@@ -1 +1 @@
1
- {"version":3,"file":"coerce.d.ts","sourceRoot":"","sources":["../../src/parse/coerce.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAOlD,MAAM,MAAM,cAAc,GACtB;IAAE,OAAO,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,GACjC;IAAE,OAAO,EAAE,KAAK,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAExC;;;;GAIG;AACH,wBAAgB,uBAAuB,CACrC,GAAG,EAAE,OAAO,GACX,cAAc,CA4BhB;AAED,wBAAgB,aAAa,CAC3B,KAAK,EAAE,OAAO,EACd,MAAM,EAAE,cAAc,EACtB,UAAU,EAAE,MAAM,GACjB,cAAc,CA+FhB"}
1
+ {"version":3,"file":"coerce.d.ts","sourceRoot":"","sources":["../../src/parse/coerce.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAOlD,MAAM,MAAM,cAAc,GACtB;IAAE,OAAO,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,GACjC;IAAE,OAAO,EAAE,KAAK,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAExC;;;;GAIG;AACH,wBAAgB,uBAAuB,CACrC,GAAG,EAAE,OAAO,GACX,cAAc,CA4BhB;AAED,wBAAgB,aAAa,CAC3B,KAAK,EAAE,OAAO,EACd,MAAM,EAAE,cAAc,EACtB,UAAU,EAAE,MAAM,GACjB,cAAc,CAuGhB"}
@@ -47,8 +47,15 @@ export function applyCoercion(input, config, targetType) {
47
47
  value = value.toUpperCase();
48
48
  }
49
49
  }
50
- // Type coercion from string to target
51
- if (config.from === "string" && typeof value === "string") {
50
+ // Type coercion from string to target.
51
+ // The only portable coercion source is "string" (spec 5.1), so enabling
52
+ // coercion on a non-string target (e.g. `number().coerce()`) implies a
53
+ // string source even when `from` is omitted. Without this, a bare
54
+ // `.coerce()` on a numeric/bool schema would silently no-op and the raw
55
+ // string would fail validation with invalid_type.
56
+ const isTypeTarget = targetType !== "string" && targetType !== "unknown";
57
+ const fromString = config.from === "string" || (config.from === undefined && isTypeTarget);
58
+ if (fromString && typeof value === "string") {
52
59
  switch (targetType) {
53
60
  case "int":
54
61
  case "int8":
@@ -1 +1 @@
1
- {"version":3,"file":"coerce.js","sourceRoot":"","sources":["../../src/parse/coerce.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"coerce.js","sourceRoot":"","sources":["../../src/parse/coerce.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "anyvali",
3
- "version": "0.3.4",
3
+ "version": "0.3.5",
4
4
  "description": "Native validation with portable schema interchange",
5
5
  "homepage": "https://anyvali.com",
6
6
  "repository": {
@@ -66,8 +66,16 @@ export function applyCoercion(
66
66
  }
67
67
  }
68
68
 
69
- // Type coercion from string to target
70
- if (config.from === "string" && typeof value === "string") {
69
+ // Type coercion from string to target.
70
+ // The only portable coercion source is "string" (spec 5.1), so enabling
71
+ // coercion on a non-string target (e.g. `number().coerce()`) implies a
72
+ // string source even when `from` is omitted. Without this, a bare
73
+ // `.coerce()` on a numeric/bool schema would silently no-op and the raw
74
+ // string would fail validation with invalid_type.
75
+ const isTypeTarget = targetType !== "string" && targetType !== "unknown";
76
+ const fromString =
77
+ config.from === "string" || (config.from === undefined && isTypeTarget);
78
+ if (fromString && typeof value === "string") {
71
79
  switch (targetType) {
72
80
  case "int":
73
81
  case "int8":
@@ -1,5 +1,5 @@
1
1
  import { describe, it, expect } from "vitest";
2
- import { number, float32, float64, int, int8, int16, int32, int64, uint8, uint16, uint32, uint64 } from "../../src/index.js";
2
+ import { number, float32, float64, int, int8, int16, int32, int64, uint8, uint16, uint32, uint64, object } from "../../src/index.js";
3
3
 
4
4
  describe("NumberSchema", () => {
5
5
  it("accepts valid numbers", () => {
@@ -61,6 +61,43 @@ describe("NumberSchema", () => {
61
61
  expect(result.success).toBe(false);
62
62
  if (!result.success) expect(result.issues[0].code).toBe("coercion_failed");
63
63
  });
64
+
65
+ // Bare `.coerce()` (no args) must imply string source on a numeric target.
66
+ // Regression: previously no-op'd, so a string input failed with invalid_type.
67
+ it("coerces string to number with no-arg coerce()", () => {
68
+ const s = number().coerce();
69
+ expect(s.parse("3.14")).toBe(3.14);
70
+ });
71
+
72
+ // Full string->number coercion matrix (no-arg form). ASCII decimal float
73
+ // incl. exponent, trimmed. No hex/Infinity/NaN/underscores.
74
+ describe("string->number matrix via no-arg coerce()", () => {
75
+ const s = number().coerce();
76
+
77
+ it.each([
78
+ ["3.14", 3.14],
79
+ ["-1.5e3", -1500],
80
+ [" 2 ", 2],
81
+ ["0", 0],
82
+ ])("accepts %j -> %j", (input, expected) => {
83
+ expect(s.parse(input as string)).toBe(expected);
84
+ });
85
+
86
+ it.each([
87
+ "0x10",
88
+ "Infinity",
89
+ "NaN",
90
+ "",
91
+ "1_000",
92
+ "abc",
93
+ ])("rejects %j with coercion_failed", (input) => {
94
+ const result = s.safeParse(input);
95
+ expect(result.success).toBe(false);
96
+ if (!result.success) {
97
+ expect(result.issues[0].code).toBe("coercion_failed");
98
+ }
99
+ });
100
+ });
64
101
  });
65
102
 
66
103
  describe("Float32Schema", () => {
@@ -102,6 +139,66 @@ describe("IntSchema", () => {
102
139
  const result = s.safeParse("3.14");
103
140
  expect(result.success).toBe(false);
104
141
  });
142
+
143
+ it("coerces string to int with no-arg coerce()", () => {
144
+ const s = int().coerce();
145
+ expect(s.parse("42")).toBe(42);
146
+ });
147
+
148
+ // Full string->int coercion matrix (no-arg form). ASCII `^-?\d+$`, trimmed.
149
+ describe("string->int matrix via no-arg coerce()", () => {
150
+ const s = int().coerce();
151
+
152
+ it.each([
153
+ ["42", 42],
154
+ [" 42 ", 42],
155
+ ["-7", -7],
156
+ ])("accepts %j -> %j", (input, expected) => {
157
+ expect(s.parse(input as string)).toBe(expected);
158
+ });
159
+
160
+ it.each([
161
+ "3.14",
162
+ "0x10",
163
+ "1_000",
164
+ "+5",
165
+ "Infinity",
166
+ "",
167
+ "abc",
168
+ ])("rejects %j with coercion_failed", (input) => {
169
+ const result = s.safeParse(input);
170
+ expect(result.success).toBe(false);
171
+ if (!result.success) {
172
+ expect(result.issues[0].code).toBe("coercion_failed");
173
+ }
174
+ });
175
+ });
176
+ });
177
+
178
+ // Reproduces the reported field-level coercion failure: an object whose
179
+ // numeric fields use bare `.coerce()` must coerce string inputs, not reject
180
+ // them with invalid_type.
181
+ describe("object with no-arg coerce() on numeric fields", () => {
182
+ it("coerces all string fields to numbers", () => {
183
+ const schema = object({
184
+ lumpSum: number().coerce().min(0),
185
+ monthlyContributions: number().coerce().min(0),
186
+ investmentTerm: number().coerce().min(1),
187
+ });
188
+ const result = schema.safeParse({
189
+ lumpSum: "1000000",
190
+ monthlyContributions: "1000",
191
+ investmentTerm: "20",
192
+ });
193
+ expect(result.success).toBe(true);
194
+ if (result.success) {
195
+ expect(result.data).toEqual({
196
+ lumpSum: 1000000,
197
+ monthlyContributions: 1000,
198
+ investmentTerm: 20,
199
+ });
200
+ }
201
+ });
105
202
  });
106
203
 
107
204
  describe("Int8Schema", () => {
@@ -35,6 +35,47 @@ describe("BoolSchema", () => {
35
35
  expect(result.success).toBe(false);
36
36
  if (!result.success) expect(result.issues[0].code).toBe("coercion_failed");
37
37
  });
38
+
39
+ it("coerces string to bool with no-arg coerce()", () => {
40
+ const s = bool().coerce();
41
+ expect(s.parse("true")).toBe(true);
42
+ expect(s.parse("false")).toBe(false);
43
+ });
44
+
45
+ // Full string->bool coercion matrix (no-arg form). Trim + case-insensitive.
46
+ // true <- "true"/"TRUE"/"1"; false <- "false"/"0". Nothing else.
47
+ describe("string->bool matrix via no-arg coerce()", () => {
48
+ const s = bool().coerce();
49
+
50
+ it.each([
51
+ ["true", true],
52
+ ["TRUE", true],
53
+ ["1", true],
54
+ [" true ", true],
55
+ ["false", false],
56
+ ["0", false],
57
+ [" FALSE ", false],
58
+ ])("accepts %j -> %j", (input, expected) => {
59
+ expect(s.parse(input as string)).toBe(expected);
60
+ });
61
+
62
+ it.each([
63
+ "yes",
64
+ "no",
65
+ "on",
66
+ "off",
67
+ "t",
68
+ "f",
69
+ "2",
70
+ "",
71
+ ])("rejects %j with coercion_failed", (input) => {
72
+ const result = s.safeParse(input);
73
+ expect(result.success).toBe(false);
74
+ if (!result.success) {
75
+ expect(result.issues[0].code).toBe("coercion_failed");
76
+ }
77
+ });
78
+ });
38
79
  });
39
80
 
40
81
  describe("NullSchema", () => {