anyvali 0.3.1 → 0.3.4

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.
Files changed (40) hide show
  1. package/CHANGELOG.md +67 -44
  2. package/README.md +370 -370
  3. package/dist/parse/coerce.d.ts.map +1 -1
  4. package/dist/parse/coerce.js +14 -0
  5. package/dist/parse/coerce.js.map +1 -1
  6. package/dist/schemas/number.d.ts.map +1 -1
  7. package/dist/schemas/number.js +15 -0
  8. package/dist/schemas/number.js.map +1 -1
  9. package/dist/schemas/optional.d.ts.map +1 -1
  10. package/dist/schemas/optional.js +4 -3
  11. package/dist/schemas/optional.js.map +1 -1
  12. package/package.json +40 -40
  13. package/sdk/js/CHANGELOG.md +13 -13
  14. package/src/format/validators.ts +71 -71
  15. package/src/index.ts +285 -285
  16. package/src/infer.ts +12 -12
  17. package/src/interchange/importer.ts +285 -285
  18. package/src/issue-codes.ts +19 -19
  19. package/src/parse/coerce.ts +15 -0
  20. package/src/schemas/base.ts +322 -322
  21. package/src/schemas/intersection.ts +81 -81
  22. package/src/schemas/number.ts +17 -0
  23. package/src/schemas/object.ts +203 -203
  24. package/src/schemas/optional.ts +4 -3
  25. package/src/schemas/record.ts +55 -55
  26. package/src/schemas/string.ts +192 -192
  27. package/src/schemas/union.ts +53 -53
  28. package/src/types.ts +239 -239
  29. package/tests/unit/collections.test.ts +99 -99
  30. package/tests/unit/date-format.test.ts +18 -18
  31. package/tests/unit/default-mutation.test.ts +32 -32
  32. package/tests/unit/defaults.test.ts +70 -1
  33. package/tests/unit/inference.test.ts +306 -306
  34. package/tests/unit/interchange.test.ts +191 -191
  35. package/tests/unit/object.test.ts +208 -208
  36. package/tests/unit/security-recursion.test.ts +105 -105
  37. package/tests/unit/security.test.ts +1067 -945
  38. package/tests/unit/shared-ref-falsepos.test.ts +33 -33
  39. package/tests/unit/string-pattern-redos.test.ts +46 -46
  40. package/tests/unit/string.test.ts +147 -147
@@ -1,18 +1,18 @@
1
- import { describe, it, expect } from "vitest";
2
- import { string } from "../../src/index.js";
3
-
4
- const date = () => string().format("date");
5
-
6
- describe("date format validation", () => {
7
- it("accepts valid ISO dates including early years (0001-0099)", () => {
8
- for (const d of ["0001-01-01", "0050-01-01", "0099-12-31", "2021-06-15", "9999-12-31"]) {
9
- expect(date().safeParse(d).success).toBe(true);
10
- }
11
- });
12
-
13
- it("rejects impossible calendar dates", () => {
14
- for (const d of ["2021-02-30", "2021-04-31", "2021-13-01", "2021-00-10"]) {
15
- expect(date().safeParse(d).success).toBe(false);
16
- }
17
- });
18
- });
1
+ import { describe, it, expect } from "vitest";
2
+ import { string } from "../../src/index.js";
3
+
4
+ const date = () => string().format("date");
5
+
6
+ describe("date format validation", () => {
7
+ it("accepts valid ISO dates including early years (0001-0099)", () => {
8
+ for (const d of ["0001-01-01", "0050-01-01", "0099-12-31", "2021-06-15", "9999-12-31"]) {
9
+ expect(date().safeParse(d).success).toBe(true);
10
+ }
11
+ });
12
+
13
+ it("rejects impossible calendar dates", () => {
14
+ for (const d of ["2021-02-30", "2021-04-31", "2021-13-01", "2021-00-10"]) {
15
+ expect(date().safeParse(d).success).toBe(false);
16
+ }
17
+ });
18
+ });
@@ -1,32 +1,32 @@
1
- import { describe, it, expect } from "vitest";
2
- import { any, unknown, object } from "../../src/index.js";
3
-
4
- // Repro for AVV-007: mutable default values shared between validations.
5
- // Pass-through schemas (any/unknown) return the default value by reference,
6
- // so mutating one parse result corrupts the default for the next parse.
7
- describe("Mutable default isolation (AVV-007)", () => {
8
- it("any() array default is not shared between parses", () => {
9
- const s = any().default([] as unknown[]);
10
- const a = s.parse(undefined) as unknown[];
11
- a.push("mutated");
12
- const b = s.parse(undefined) as unknown[];
13
- expect(b).toEqual([]);
14
- });
15
-
16
- it("unknown() object default is not shared between parses", () => {
17
- const s = unknown().default({ tags: [] as string[] });
18
- const a = s.parse(undefined) as { tags: string[] };
19
- a.tags.push("x");
20
- const b = s.parse(undefined) as { tags: string[] };
21
- expect(b.tags).toEqual([]);
22
- });
23
-
24
- it("object field with any() default is not shared", () => {
25
- const s = object({ meta: any().default({ count: 0, items: [] as number[] }) });
26
- const a = s.parse({}) as { meta: { count: number; items: number[] } };
27
- a.meta.items.push(1);
28
- a.meta.count = 99;
29
- const b = s.parse({}) as { meta: { count: number; items: number[] } };
30
- expect(b.meta).toEqual({ count: 0, items: [] });
31
- });
32
- });
1
+ import { describe, it, expect } from "vitest";
2
+ import { any, unknown, object } from "../../src/index.js";
3
+
4
+ // Repro for AVV-007: mutable default values shared between validations.
5
+ // Pass-through schemas (any/unknown) return the default value by reference,
6
+ // so mutating one parse result corrupts the default for the next parse.
7
+ describe("Mutable default isolation (AVV-007)", () => {
8
+ it("any() array default is not shared between parses", () => {
9
+ const s = any().default([] as unknown[]);
10
+ const a = s.parse(undefined) as unknown[];
11
+ a.push("mutated");
12
+ const b = s.parse(undefined) as unknown[];
13
+ expect(b).toEqual([]);
14
+ });
15
+
16
+ it("unknown() object default is not shared between parses", () => {
17
+ const s = unknown().default({ tags: [] as string[] });
18
+ const a = s.parse(undefined) as { tags: string[] };
19
+ a.tags.push("x");
20
+ const b = s.parse(undefined) as { tags: string[] };
21
+ expect(b.tags).toEqual([]);
22
+ });
23
+
24
+ it("object field with any() default is not shared", () => {
25
+ const s = object({ meta: any().default({ count: 0, items: [] as number[] }) });
26
+ const a = s.parse({}) as { meta: { count: number; items: number[] } };
27
+ a.meta.items.push(1);
28
+ a.meta.count = 99;
29
+ const b = s.parse({}) as { meta: { count: number; items: number[] } };
30
+ expect(b.meta).toEqual({ count: 0, items: [] });
31
+ });
32
+ });
@@ -1,5 +1,5 @@
1
1
  import { describe, it, expect } from "vitest";
2
- import { string, int, object, optional } from "../../src/index.js";
2
+ import { any, string, int, object, optional, nullable, bool, exportSchema } from "../../src/index.js";
3
3
 
4
4
  describe("Defaults", () => {
5
5
  it("applies default when value is absent", () => {
@@ -46,4 +46,73 @@ describe("Defaults", () => {
46
46
  const result = s.parse({});
47
47
  expect(result).toEqual({ count: 0 });
48
48
  });
49
+
50
+ it("applies default on optional wrapper field", () => {
51
+ const s = object({
52
+ host: optional(string()).default("localhost"),
53
+ });
54
+ expect(s.parse({})).toEqual({ host: "localhost" });
55
+ });
56
+
57
+ it("does not override present optional wrapper field with default", () => {
58
+ const s = object({
59
+ host: optional(string()).default("localhost"),
60
+ });
61
+ expect(s.parse({ host: "example.com" })).toEqual({ host: "example.com" });
62
+ });
63
+
64
+ it("validates default on optional wrapper field", () => {
65
+ const s = object({
66
+ host: optional(string().minLength(5)).default("hi"),
67
+ });
68
+ const result = s.safeParse({});
69
+ expect(result.success).toBe(false);
70
+ if (!result.success) {
71
+ expect(result.issues[0].code).toBe("default_invalid");
72
+ expect(result.issues[0].path).toEqual(["host"]);
73
+ }
74
+ });
75
+
76
+ it("exports default on optional wrapper", () => {
77
+ const doc = exportSchema(optional(string()).default("localhost"));
78
+ expect(doc.root.kind).toBe("optional");
79
+ expect(doc.root.default).toBe("localhost");
80
+ });
81
+
82
+ it("clones mutable default on optional wrapper field", () => {
83
+ const s = object({
84
+ config: optional(any()).default({ tags: [] as string[] }),
85
+ });
86
+ const first = s.parse({}) as any;
87
+ first.config.tags.push("mutated");
88
+ expect(s.parse({})).toEqual({ config: { tags: [] } });
89
+ });
90
+
91
+ it("does not apply default when nullable field is null", () => {
92
+ const s = object({
93
+ value: nullable(string()).default("fallback"),
94
+ });
95
+ expect(s.parse({ value: null })).toEqual({ value: null });
96
+ });
97
+
98
+ it("applies falsy defaults", () => {
99
+ const s = object({
100
+ count: int().default(0),
101
+ name: string().default(""),
102
+ active: bool().default(false),
103
+ });
104
+ expect(s.parse({})).toEqual({ count: 0, name: "", active: false });
105
+ });
106
+
107
+ it("applies nested defaults", () => {
108
+ const s = object({
109
+ user: object({
110
+ name: string(),
111
+ role: string().default("guest"),
112
+ }),
113
+ });
114
+ expect(s.parse({ user: { name: "Bob" } })).toEqual({
115
+ user: { name: "Bob", role: "guest" },
116
+ });
117
+ });
49
118
  });