@agentuity/schema 0.0.69 → 0.0.71

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.
@@ -13,6 +13,8 @@ export declare class StringSchema implements Schema<string, string> {
13
13
  description?: string;
14
14
  private _min?;
15
15
  private _max?;
16
+ private _email?;
17
+ private _url?;
16
18
  readonly '~standard': {
17
19
  version: 1;
18
20
  vendor: string;
@@ -45,6 +47,28 @@ export declare class StringSchema implements Schema<string, string> {
45
47
  * ```
46
48
  */
47
49
  max(length: number): StringSchema;
50
+ /**
51
+ * Validate email format.
52
+ *
53
+ * @example
54
+ * ```typescript
55
+ * const schema = s.string().email();
56
+ * schema.parse('user@example.com'); // "user@example.com"
57
+ * schema.parse('invalid'); // throws ValidationError
58
+ * ```
59
+ */
60
+ email(): StringSchema;
61
+ /**
62
+ * Validate URL format.
63
+ *
64
+ * @example
65
+ * ```typescript
66
+ * const schema = s.string().url();
67
+ * schema.parse('https://example.com'); // "https://example.com"
68
+ * schema.parse('invalid'); // throws ValidationError
69
+ * ```
70
+ */
71
+ url(): StringSchema;
48
72
  optional(): import("..").OptionalSchema<this>;
49
73
  nullable(): import("..").NullableSchema<this>;
50
74
  private _clone;
@@ -1 +1 @@
1
- {"version":3,"file":"string.d.ts","sourceRoot":"","sources":["../../src/primitives/string.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAOtC;;;;;;;;;GASG;AACH,qBAAa,YAAa,YAAW,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IAC1D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,IAAI,CAAC,CAAS;IACtB,OAAO,CAAC,IAAI,CAAC,CAAS;IAEtB,QAAQ,CAAC,WAAW;;;0BAGD,OAAO;eAgBM;YAAE,KAAK,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,MAAM,CAAA;SAAE;MAC/D;IAEF,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI;IAKnC;;;;;;;;;OASG;IACH,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY;IAMjC;;;;;;;;;OASG;IACH,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY;IAMjC,QAAQ;IAIR,QAAQ;IAIR,OAAO,CAAC,MAAM;IAQd,KAAK,wDAAsB;IAC3B,SAAS,sFAA0B;CACnC;AAED;;;;;;;;GAQG;AACH,wBAAgB,MAAM,IAAI,YAAY,CAErC"}
1
+ {"version":3,"file":"string.d.ts","sourceRoot":"","sources":["../../src/primitives/string.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAOtC;;;;;;;;;GASG;AACH,qBAAa,YAAa,YAAW,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IAC1D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,IAAI,CAAC,CAAS;IACtB,OAAO,CAAC,IAAI,CAAC,CAAS;IACtB,OAAO,CAAC,MAAM,CAAC,CAAU;IACzB,OAAO,CAAC,IAAI,CAAC,CAAU;IAEvB,QAAQ,CAAC,WAAW;;;0BAGD,OAAO;eA8BM;YAAE,KAAK,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,MAAM,CAAA;SAAE;MAC/D;IAEF,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI;IAKnC;;;;;;;;;OASG;IACH,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY;IAMjC;;;;;;;;;OASG;IACH,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY;IAMjC;;;;;;;;;OASG;IACH,KAAK,IAAI,YAAY;IAMrB;;;;;;;;;OASG;IACH,GAAG,IAAI,YAAY;IAMnB,QAAQ;IAIR,QAAQ;IAIR,OAAO,CAAC,MAAM;IAUd,KAAK,wDAAsB;IAC3B,SAAS,sFAA0B;CACnC;AAED;;;;;;;;GAQG;AACH,wBAAgB,MAAM,IAAI,YAAY,CAErC"}
@@ -16,6 +16,8 @@ export class StringSchema {
16
16
  description;
17
17
  _min;
18
18
  _max;
19
+ _email;
20
+ _url;
19
21
  '~standard' = {
20
22
  version: 1,
21
23
  vendor: 'agentuity',
@@ -33,6 +35,21 @@ export class StringSchema {
33
35
  createIssue(`String must be at most ${this._max} characters, got ${value.length}`),
34
36
  ]);
35
37
  }
38
+ if (this._email) {
39
+ // Basic email regex - matches most valid emails
40
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
41
+ if (!emailRegex.test(value)) {
42
+ return failure([createIssue(`Invalid email format`)]);
43
+ }
44
+ }
45
+ if (this._url) {
46
+ try {
47
+ new URL(value);
48
+ }
49
+ catch {
50
+ return failure([createIssue(`Invalid URL format`)]);
51
+ }
52
+ }
36
53
  return success(value);
37
54
  },
38
55
  types: undefined,
@@ -71,6 +88,36 @@ export class StringSchema {
71
88
  clone._max = length;
72
89
  return clone;
73
90
  }
91
+ /**
92
+ * Validate email format.
93
+ *
94
+ * @example
95
+ * ```typescript
96
+ * const schema = s.string().email();
97
+ * schema.parse('user@example.com'); // "user@example.com"
98
+ * schema.parse('invalid'); // throws ValidationError
99
+ * ```
100
+ */
101
+ email() {
102
+ const clone = this._clone();
103
+ clone._email = true;
104
+ return clone;
105
+ }
106
+ /**
107
+ * Validate URL format.
108
+ *
109
+ * @example
110
+ * ```typescript
111
+ * const schema = s.string().url();
112
+ * schema.parse('https://example.com'); // "https://example.com"
113
+ * schema.parse('invalid'); // throws ValidationError
114
+ * ```
115
+ */
116
+ url() {
117
+ const clone = this._clone();
118
+ clone._url = true;
119
+ return clone;
120
+ }
74
121
  optional() {
75
122
  return optional(this);
76
123
  }
@@ -82,6 +129,8 @@ export class StringSchema {
82
129
  clone.description = this.description;
83
130
  clone._min = this._min;
84
131
  clone._max = this._max;
132
+ clone._email = this._email;
133
+ clone._url = this._url;
85
134
  return clone;
86
135
  }
87
136
  parse = parseMethods.parse;
@@ -1 +1 @@
1
- {"version":3,"file":"string.js","sourceRoot":"","sources":["../../src/primitives/string.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,OAAO,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAC5E,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAE7C,MAAM,YAAY,GAAG,kBAAkB,EAAU,CAAC;AAElD;;;;;;;;;GASG;AACH,MAAM,OAAO,YAAY;IACxB,WAAW,CAAU;IACb,IAAI,CAAU;IACd,IAAI,CAAU;IAEb,WAAW,GAAG;QACtB,OAAO,EAAE,CAAU;QACnB,MAAM,EAAE,WAAW;QACnB,QAAQ,EAAE,CAAC,KAAc,EAAE,EAAE;YAC5B,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;gBAC/B,OAAO,OAAO,CAAC,CAAC,WAAW,CAAC,wBAAwB,OAAO,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC;YACvE,CAAC;YACD,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,IAAI,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;gBACzD,OAAO,OAAO,CAAC;oBACd,WAAW,CAAC,2BAA2B,IAAI,CAAC,IAAI,oBAAoB,KAAK,CAAC,MAAM,EAAE,CAAC;iBACnF,CAAC,CAAC;YACJ,CAAC;YACD,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,IAAI,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;gBACzD,OAAO,OAAO,CAAC;oBACd,WAAW,CAAC,0BAA0B,IAAI,CAAC,IAAI,oBAAoB,KAAK,CAAC,MAAM,EAAE,CAAC;iBAClF,CAAC,CAAC;YACJ,CAAC;YACD,OAAO,OAAO,CAAC,KAAK,CAAC,CAAC;QACvB,CAAC;QACD,KAAK,EAAE,SAAyD;KAChE,CAAC;IAEF,QAAQ,CAAC,WAAmB;QAC3B,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;QAC/B,OAAO,IAAI,CAAC;IACb,CAAC;IAED;;;;;;;;;OASG;IACH,GAAG,CAAC,MAAc;QACjB,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;QAC5B,KAAK,CAAC,IAAI,GAAG,MAAM,CAAC;QACpB,OAAO,KAAK,CAAC;IACd,CAAC;IAED;;;;;;;;;OASG;IACH,GAAG,CAAC,MAAc;QACjB,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;QAC5B,KAAK,CAAC,IAAI,GAAG,MAAM,CAAC;QACpB,OAAO,KAAK,CAAC;IACd,CAAC;IAED,QAAQ;QACP,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC;IACvB,CAAC;IAED,QAAQ;QACP,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC;IACvB,CAAC;IAEO,MAAM;QACb,MAAM,KAAK,GAAG,IAAI,YAAY,EAAE,CAAC;QACjC,KAAK,CAAC,WAAW,GAAG,IAAI,CAAC,WAAW,CAAC;QACrC,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;QACvB,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;QACvB,OAAO,KAAK,CAAC;IACd,CAAC;IAED,KAAK,GAAG,YAAY,CAAC,KAAK,CAAC;IAC3B,SAAS,GAAG,YAAY,CAAC,SAAS,CAAC;CACnC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,MAAM;IACrB,OAAO,IAAI,YAAY,EAAE,CAAC;AAC3B,CAAC"}
1
+ {"version":3,"file":"string.js","sourceRoot":"","sources":["../../src/primitives/string.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentuity/schema",
3
- "version": "0.0.69",
3
+ "version": "0.0.71",
4
4
  "license": "Apache-2.0",
5
5
  "author": "Agentuity employees and contributors",
6
6
  "type": "module",
@@ -26,7 +26,7 @@
26
26
  "prepublishOnly": "bun run clean && bun run build"
27
27
  },
28
28
  "dependencies": {
29
- "@agentuity/core": "0.0.69"
29
+ "@agentuity/core": "0.0.71"
30
30
  },
31
31
  "devDependencies": {
32
32
  "@types/bun": "latest",
@@ -19,6 +19,8 @@ export class StringSchema implements Schema<string, string> {
19
19
  description?: string;
20
20
  private _min?: number;
21
21
  private _max?: number;
22
+ private _email?: boolean;
23
+ private _url?: boolean;
22
24
 
23
25
  readonly '~standard' = {
24
26
  version: 1 as const,
@@ -37,6 +39,20 @@ export class StringSchema implements Schema<string, string> {
37
39
  createIssue(`String must be at most ${this._max} characters, got ${value.length}`),
38
40
  ]);
39
41
  }
42
+ if (this._email) {
43
+ // Basic email regex - matches most valid emails
44
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
45
+ if (!emailRegex.test(value)) {
46
+ return failure([createIssue(`Invalid email format`)]);
47
+ }
48
+ }
49
+ if (this._url) {
50
+ try {
51
+ new URL(value);
52
+ } catch {
53
+ return failure([createIssue(`Invalid URL format`)]);
54
+ }
55
+ }
40
56
  return success(value);
41
57
  },
42
58
  types: undefined as unknown as { input: string; output: string },
@@ -79,6 +95,38 @@ export class StringSchema implements Schema<string, string> {
79
95
  return clone;
80
96
  }
81
97
 
98
+ /**
99
+ * Validate email format.
100
+ *
101
+ * @example
102
+ * ```typescript
103
+ * const schema = s.string().email();
104
+ * schema.parse('user@example.com'); // "user@example.com"
105
+ * schema.parse('invalid'); // throws ValidationError
106
+ * ```
107
+ */
108
+ email(): StringSchema {
109
+ const clone = this._clone();
110
+ clone._email = true;
111
+ return clone;
112
+ }
113
+
114
+ /**
115
+ * Validate URL format.
116
+ *
117
+ * @example
118
+ * ```typescript
119
+ * const schema = s.string().url();
120
+ * schema.parse('https://example.com'); // "https://example.com"
121
+ * schema.parse('invalid'); // throws ValidationError
122
+ * ```
123
+ */
124
+ url(): StringSchema {
125
+ const clone = this._clone();
126
+ clone._url = true;
127
+ return clone;
128
+ }
129
+
82
130
  optional() {
83
131
  return optional(this);
84
132
  }
@@ -92,6 +140,8 @@ export class StringSchema implements Schema<string, string> {
92
140
  clone.description = this.description;
93
141
  clone._min = this._min;
94
142
  clone._max = this._max;
143
+ clone._email = this._email;
144
+ clone._url = this._url;
95
145
  return clone;
96
146
  }
97
147
 
@@ -1,88 +0,0 @@
1
- import { describe, test, expect } from 'bun:test';
2
- import { s, ValidationError } from '../index.js';
3
-
4
- describe('Coercion Schemas', () => {
5
- describe('coerce.string', () => {
6
- const schema = s.coerce.string();
7
-
8
- test('should coerce numbers to strings', () => {
9
- expect(schema.parse(123)).toBe('123');
10
- });
11
-
12
- test('should coerce booleans to strings', () => {
13
- expect(schema.parse(true)).toBe('true');
14
- expect(schema.parse(false)).toBe('false');
15
- });
16
-
17
- test('should keep strings as-is', () => {
18
- expect(schema.parse('hello')).toBe('hello');
19
- });
20
- });
21
-
22
- describe('coerce.number', () => {
23
- const schema = s.coerce.number();
24
-
25
- test('should coerce string numbers', () => {
26
- expect(schema.parse('123')).toBe(123);
27
- expect(schema.parse('45.67')).toBe(45.67);
28
- });
29
-
30
- test('should coerce booleans', () => {
31
- expect(schema.parse(true)).toBe(1);
32
- expect(schema.parse(false)).toBe(0);
33
- });
34
-
35
- test('should keep numbers as-is', () => {
36
- expect(schema.parse(42)).toBe(42);
37
- });
38
-
39
- test('should reject invalid coercions', () => {
40
- expect(() => schema.parse('not-a-number')).toThrow(ValidationError);
41
- expect(() => schema.parse(NaN)).toThrow(ValidationError);
42
- });
43
- });
44
-
45
- describe('coerce.boolean', () => {
46
- const schema = s.coerce.boolean();
47
-
48
- test('should coerce truthy values', () => {
49
- expect(schema.parse(1)).toBe(true);
50
- expect(schema.parse('hello')).toBe(true);
51
- expect(schema.parse([])).toBe(true);
52
- });
53
-
54
- test('should coerce falsy values', () => {
55
- expect(schema.parse(0)).toBe(false);
56
- expect(schema.parse('')).toBe(false);
57
- expect(schema.parse(null)).toBe(false);
58
- expect(schema.parse(undefined)).toBe(false);
59
- });
60
- });
61
-
62
- describe('coerce.date', () => {
63
- const schema = s.coerce.date();
64
-
65
- test('should coerce ISO strings', () => {
66
- const result = schema.parse('2025-01-01');
67
- expect(result).toBeInstanceOf(Date);
68
- expect(result.getFullYear()).toBe(2025);
69
- });
70
-
71
- test('should coerce timestamps', () => {
72
- const timestamp = Date.now();
73
- const result = schema.parse(timestamp);
74
- expect(result).toBeInstanceOf(Date);
75
- expect(result.getTime()).toBe(timestamp);
76
- });
77
-
78
- test('should keep dates as-is', () => {
79
- const date = new Date('2025-01-01');
80
- const result = schema.parse(date);
81
- expect(result).toBe(date);
82
- });
83
-
84
- test('should reject invalid dates', () => {
85
- expect(() => schema.parse('invalid-date')).toThrow(ValidationError);
86
- });
87
- });
88
- });
@@ -1,124 +0,0 @@
1
- import { describe, test, expect } from 'bun:test';
2
- import { s, ValidationError } from '../index.js';
3
-
4
- describe('Complex Schemas', () => {
5
- describe('object', () => {
6
- const schema = s.object({
7
- name: s.string(),
8
- age: s.number(),
9
- active: s.boolean(),
10
- });
11
-
12
- test('should validate objects', () => {
13
- const result = schema.parse({
14
- name: 'John',
15
- age: 30,
16
- active: true,
17
- });
18
- expect(result.name).toBe('John');
19
- expect(result.age).toBe(30);
20
- expect(result.active).toBe(true);
21
- });
22
-
23
- test('should reject invalid objects', () => {
24
- expect(() =>
25
- schema.parse({
26
- name: 'John',
27
- age: 'thirty',
28
- active: true,
29
- })
30
- ).toThrow(ValidationError);
31
- });
32
-
33
- test('should report field path in errors', () => {
34
- try {
35
- schema.parse({
36
- name: 'John',
37
- age: 'invalid',
38
- active: true,
39
- });
40
- throw new Error('Expected parse to throw');
41
- } catch (error) {
42
- expect(error).toBeInstanceOf(ValidationError);
43
- if (error instanceof ValidationError) {
44
- expect(error.issues[0].path).toEqual(['age']);
45
- } else {
46
- throw error;
47
- }
48
- }
49
- });
50
-
51
- test('should work with nested objects', () => {
52
- const nested = s.object({
53
- user: s.object({
54
- name: s.string(),
55
- email: s.string(),
56
- }),
57
- });
58
-
59
- const result = nested.parse({
60
- user: {
61
- name: 'Alice',
62
- email: 'alice@example.com',
63
- },
64
- });
65
- expect(result.user.name).toBe('Alice');
66
- });
67
- });
68
-
69
- describe('array', () => {
70
- const schema = s.array(s.string());
71
-
72
- test('should validate arrays', () => {
73
- const result = schema.parse(['a', 'b', 'c']);
74
- expect(result).toEqual(['a', 'b', 'c']);
75
- });
76
-
77
- test('should reject non-arrays', () => {
78
- expect(() => schema.parse('not-an-array')).toThrow(ValidationError);
79
- });
80
-
81
- test('should validate array items', () => {
82
- expect(() => schema.parse(['a', 123, 'c'])).toThrow(ValidationError);
83
- });
84
-
85
- test('should report array index in errors', () => {
86
- try {
87
- schema.parse(['a', 123, 'c']);
88
- throw new Error('Expected parse to throw');
89
- } catch (error) {
90
- expect(error).toBeInstanceOf(ValidationError);
91
- if (error instanceof ValidationError) {
92
- expect(error.issues[0].path).toContain(1);
93
- } else {
94
- throw error;
95
- }
96
- }
97
- });
98
-
99
- test('should work with array of objects', () => {
100
- const objArray = s.array(
101
- s.object({
102
- id: s.number(),
103
- name: s.string(),
104
- })
105
- );
106
-
107
- const result = objArray.parse([
108
- { id: 1, name: 'First' },
109
- { id: 2, name: 'Second' },
110
- ]);
111
- expect(result.length).toBe(2);
112
- expect(result[0].id).toBe(1);
113
- });
114
- });
115
- });
116
- describe('record', () => {
117
- const schema = s.record(s.string(), s.number());
118
- test('should validate records', () => {
119
- expect(schema.parse({ a: 1, b: 2 })).toEqual({ a: 1, b: 2 });
120
- });
121
- test('should reject non-objects', () => {
122
- expect(() => schema.parse([])).toThrow(ValidationError);
123
- });
124
- });
@@ -1,129 +0,0 @@
1
- import { describe, test, expect } from 'bun:test';
2
- import { s, ValidationError } from '../index.js';
3
-
4
- describe('Error Handling', () => {
5
- describe('ValidationError', () => {
6
- test('should contain issues array', () => {
7
- const schema = s.string();
8
-
9
- try {
10
- schema.parse(123);
11
- throw new Error('Expected parse to throw');
12
- } catch (error) {
13
- expect(error).toBeInstanceOf(ValidationError);
14
- if (error instanceof ValidationError) {
15
- expect(Array.isArray(error.issues)).toBe(true);
16
- expect(error.issues.length).toBeGreaterThan(0);
17
- } else {
18
- throw error;
19
- }
20
- }
21
- });
22
-
23
- test('should include field paths', () => {
24
- const schema = s.object({
25
- user: s.object({
26
- name: s.string(),
27
- }),
28
- });
29
-
30
- try {
31
- schema.parse({
32
- user: {
33
- name: 123,
34
- },
35
- });
36
- throw new Error('Expected parse to throw');
37
- } catch (error) {
38
- expect(error).toBeInstanceOf(ValidationError);
39
- if (error instanceof ValidationError) {
40
- expect(error.issues[0].path).toEqual(['user', 'name']);
41
- } else {
42
- throw error;
43
- }
44
- }
45
- });
46
-
47
- test('should have readable error message', () => {
48
- const schema = s.object({
49
- age: s.number(),
50
- });
51
-
52
- try {
53
- schema.parse({ age: 'invalid' });
54
- throw new Error('Expected parse to throw');
55
- } catch (error) {
56
- expect(error).toBeInstanceOf(ValidationError);
57
- if (error instanceof ValidationError) {
58
- expect(error.message).toContain('age');
59
- expect(error.message).toContain('number');
60
- } else {
61
- throw error;
62
- }
63
- }
64
- });
65
- });
66
-
67
- describe('safeParse', () => {
68
- test('should not throw on invalid data', () => {
69
- const schema = s.string();
70
- const result = schema.safeParse(123);
71
-
72
- expect(result.success).toBe(false);
73
- if (!result.success) {
74
- expect(result.error).toBeInstanceOf(ValidationError);
75
- }
76
- });
77
-
78
- test('should return data on valid input', () => {
79
- const schema = s.string();
80
- const result = schema.safeParse('hello');
81
-
82
- expect(result.success).toBe(true);
83
- if (result.success) {
84
- expect(result.data).toBe('hello');
85
- }
86
- });
87
- });
88
-
89
- describe('parse', () => {
90
- test('should throw ValidationError on invalid data', () => {
91
- const schema = s.number();
92
-
93
- expect(() => schema.parse('not-a-number')).toThrow(ValidationError);
94
- });
95
-
96
- test('should return typed data on valid input', () => {
97
- const schema = s.number();
98
- const result = schema.parse(42);
99
-
100
- expect(result).toBe(42);
101
- });
102
- });
103
-
104
- describe('multiple errors', () => {
105
- test('should collect all validation errors', () => {
106
- const schema = s.object({
107
- name: s.string(),
108
- age: s.number(),
109
- email: s.string(),
110
- });
111
-
112
- try {
113
- schema.parse({
114
- name: 123,
115
- age: 'invalid',
116
- email: false,
117
- });
118
- throw new Error('Expected parse to throw');
119
- } catch (error) {
120
- expect(error).toBeInstanceOf(ValidationError);
121
- if (error instanceof ValidationError) {
122
- expect(error.issues.length).toBe(3);
123
- } else {
124
- throw error;
125
- }
126
- }
127
- });
128
- });
129
- });
@@ -1,138 +0,0 @@
1
- import { describe, test, expect } from 'bun:test';
2
- import { s } from '../index.js';
3
-
4
- describe('JSON Schema Conversion', () => {
5
- describe('toJSONSchema', () => {
6
- test('should convert primitive types', () => {
7
- const stringSchema = s.toJSONSchema(s.string());
8
- expect(stringSchema.type).toBe('string');
9
-
10
- const numberSchema = s.toJSONSchema(s.number());
11
- expect(numberSchema.type).toBe('number');
12
-
13
- const booleanSchema = s.toJSONSchema(s.boolean());
14
- expect(booleanSchema.type).toBe('boolean');
15
- });
16
-
17
- test('should convert object schemas', () => {
18
- const schema = s.object({
19
- name: s.string(),
20
- age: s.number(),
21
- });
22
-
23
- const jsonSchema = s.toJSONSchema(schema);
24
- expect(jsonSchema.type).toBe('object');
25
- expect(jsonSchema.properties).toHaveProperty('name');
26
- expect(jsonSchema.properties).toHaveProperty('age');
27
- expect(jsonSchema.required).toContain('name');
28
- expect(jsonSchema.required).toContain('age');
29
- });
30
-
31
- test('should convert array schemas', () => {
32
- const schema = s.array(s.string());
33
- const jsonSchema = s.toJSONSchema(schema);
34
-
35
- expect(jsonSchema.type).toBe('array');
36
- expect(jsonSchema.items).toHaveProperty('type', 'string');
37
- });
38
-
39
- test('should preserve descriptions', () => {
40
- const schema = s.string().describe('A test string');
41
- const jsonSchema = s.toJSONSchema(schema);
42
-
43
- expect(jsonSchema.description).toBe('A test string');
44
- });
45
-
46
- test('should handle optional fields', () => {
47
- const schema = s.object({
48
- required: s.string(),
49
- optional: s.optional(s.string()),
50
- });
51
-
52
- const jsonSchema = s.toJSONSchema(schema);
53
- expect(jsonSchema.required).toContain('required');
54
- expect(jsonSchema.required).not.toContain('optional');
55
- });
56
-
57
- test('should handle nullable fields', () => {
58
- const schema = s.nullable(s.string());
59
- const jsonSchema = s.toJSONSchema(schema);
60
-
61
- expect(jsonSchema.anyOf).toHaveLength(2);
62
- });
63
- });
64
-
65
- describe('fromJSONSchema', () => {
66
- test('should convert primitive types', () => {
67
- const stringSchema = s.fromJSONSchema({ type: 'string' });
68
- expect(stringSchema.parse('hello')).toBe('hello');
69
-
70
- const numberSchema = s.fromJSONSchema({ type: 'number' });
71
- expect(numberSchema.parse(123)).toBe(123);
72
- });
73
-
74
- test('should convert object schemas', () => {
75
- const jsonSchema = {
76
- type: 'object' as const,
77
- properties: {
78
- name: { type: 'string' as const },
79
- age: { type: 'number' as const },
80
- },
81
- required: ['name', 'age'],
82
- };
83
-
84
- const schema = s.fromJSONSchema(jsonSchema);
85
- const result = schema.parse({
86
- name: 'John',
87
- age: 30,
88
- });
89
-
90
- expect(result).toEqual({ name: 'John', age: 30 });
91
- });
92
-
93
- test('should convert array schemas', () => {
94
- const jsonSchema = {
95
- type: 'array' as const,
96
- items: { type: 'string' as const },
97
- };
98
-
99
- const schema = s.fromJSONSchema(jsonSchema);
100
- const result = schema.parse(['a', 'b', 'c']);
101
-
102
- expect(result).toEqual(['a', 'b', 'c']);
103
- });
104
-
105
- test('should handle enum values', () => {
106
- const jsonSchema = {
107
- enum: ['red', 'green', 'blue'],
108
- };
109
-
110
- const schema = s.fromJSONSchema(jsonSchema);
111
- expect(schema.parse('red')).toBe('red');
112
- });
113
- });
114
-
115
- describe('round-trip conversion', () => {
116
- test('should preserve schema through round-trip', () => {
117
- const original = s.object({
118
- name: s.string(),
119
- age: s.number(),
120
- tags: s.array(s.string()),
121
- });
122
-
123
- const jsonSchema = s.toJSONSchema(original);
124
- const reconstructed = s.fromJSONSchema(jsonSchema);
125
-
126
- const testData = {
127
- name: 'Test',
128
- age: 25,
129
- tags: ['tag1', 'tag2'],
130
- };
131
-
132
- const originalResult = original.parse(testData);
133
- const reconstructedResult = reconstructed.parse(testData);
134
-
135
- expect(reconstructedResult).toEqual(originalResult);
136
- });
137
- });
138
- });
@@ -1,184 +0,0 @@
1
- import { describe, test, expect } from 'bun:test';
2
- import { s, ValidationError } from '../index.js';
3
-
4
- describe('Primitive Schemas', () => {
5
- describe('string', () => {
6
- const schema = s.string();
7
-
8
- test('should validate strings', () => {
9
- expect(schema.parse('hello')).toBe('hello');
10
- expect(schema.parse('')).toBe('');
11
- });
12
-
13
- test('should reject non-strings', () => {
14
- expect(() => schema.parse(123)).toThrow(ValidationError);
15
- expect(() => schema.parse(true)).toThrow(ValidationError);
16
- expect(() => schema.parse(null)).toThrow(ValidationError);
17
- });
18
-
19
- test('should work with safeParse', () => {
20
- const result = schema.safeParse('test');
21
- expect(result.success).toBe(true);
22
- if (result.success) {
23
- expect(result.data).toBe('test');
24
- }
25
-
26
- const badResult = schema.safeParse(123);
27
- expect(badResult.success).toBe(false);
28
- });
29
-
30
- test('min() should enforce minimum length', () => {
31
- const minSchema = s.string().min(3);
32
- expect(minSchema.parse('hello')).toBe('hello');
33
- expect(minSchema.parse('abc')).toBe('abc');
34
- expect(() => minSchema.parse('ab')).toThrow(ValidationError);
35
- });
36
-
37
- test('max() should enforce maximum length', () => {
38
- const maxSchema = s.string().max(5);
39
- expect(maxSchema.parse('hello')).toBe('hello');
40
- expect(maxSchema.parse('hi')).toBe('hi');
41
- expect(() => maxSchema.parse('toolong')).toThrow(ValidationError);
42
- });
43
-
44
- test('should chain min and max', () => {
45
- const rangeSchema = s.string().min(3).max(10);
46
- expect(rangeSchema.parse('hello')).toBe('hello');
47
- expect(() => rangeSchema.parse('ab')).toThrow(ValidationError);
48
- expect(() => rangeSchema.parse('this is too long')).toThrow(ValidationError);
49
- });
50
- });
51
-
52
- describe('number', () => {
53
- const schema = s.number();
54
-
55
- test('should validate numbers', () => {
56
- expect(schema.parse(123)).toBe(123);
57
- expect(schema.parse(0)).toBe(0);
58
- expect(schema.parse(-45.67)).toBe(-45.67);
59
- expect(schema.parse(Infinity)).toBe(Infinity);
60
- expect(schema.parse(-Infinity)).toBe(-Infinity);
61
- });
62
-
63
- test('should reject non-numbers', () => {
64
- expect(() => schema.parse('123')).toThrow(ValidationError);
65
- expect(() => schema.parse(true)).toThrow(ValidationError);
66
- expect(() => schema.parse(NaN)).toThrow(ValidationError);
67
- });
68
-
69
- test('finite() should reject Infinity', () => {
70
- const finiteSchema = s.number().finite();
71
- expect(finiteSchema.parse(123)).toBe(123);
72
- expect(finiteSchema.parse(0)).toBe(0);
73
- expect(finiteSchema.parse(-45.67)).toBe(-45.67);
74
- expect(() => finiteSchema.parse(Infinity)).toThrow(ValidationError);
75
- expect(() => finiteSchema.parse(-Infinity)).toThrow(ValidationError);
76
- });
77
-
78
- test('min() should enforce minimum', () => {
79
- const minSchema = s.number().min(0);
80
- expect(minSchema.parse(0)).toBe(0);
81
- expect(minSchema.parse(10)).toBe(10);
82
- expect(() => minSchema.parse(-1)).toThrow(ValidationError);
83
- });
84
-
85
- test('max() should enforce maximum', () => {
86
- const maxSchema = s.number().max(100);
87
- expect(maxSchema.parse(100)).toBe(100);
88
- expect(maxSchema.parse(50)).toBe(50);
89
- expect(() => maxSchema.parse(101)).toThrow(ValidationError);
90
- });
91
-
92
- test('should chain min and max', () => {
93
- const rangeSchema = s.number().min(0).max(100);
94
- expect(rangeSchema.parse(50)).toBe(50);
95
- expect(() => rangeSchema.parse(-1)).toThrow(ValidationError);
96
- expect(() => rangeSchema.parse(101)).toThrow(ValidationError);
97
- });
98
- });
99
-
100
- describe('boolean', () => {
101
- const schema = s.boolean();
102
-
103
- test('should validate booleans', () => {
104
- expect(schema.parse(true)).toBe(true);
105
- expect(schema.parse(false)).toBe(false);
106
- });
107
-
108
- test('should reject non-booleans', () => {
109
- expect(() => schema.parse('true')).toThrow(ValidationError);
110
- expect(() => schema.parse(1)).toThrow(ValidationError);
111
- expect(() => schema.parse(0)).toThrow(ValidationError);
112
- });
113
- });
114
-
115
- describe('null', () => {
116
- const schema = s.null();
117
-
118
- test('should validate null', () => {
119
- expect(schema.parse(null)).toBe(null);
120
- });
121
-
122
- test('should reject non-null', () => {
123
- expect(() => schema.parse(undefined)).toThrow(ValidationError);
124
- expect(() => schema.parse(0)).toThrow(ValidationError);
125
- });
126
- });
127
-
128
- describe('undefined', () => {
129
- const schema = s.undefined();
130
-
131
- test('should validate undefined', () => {
132
- expect(schema.parse(undefined)).toBe(undefined);
133
- });
134
-
135
- test('should reject non-undefined', () => {
136
- expect(() => schema.parse(null)).toThrow(ValidationError);
137
- expect(() => schema.parse(0)).toThrow(ValidationError);
138
- });
139
- });
140
-
141
- describe('unknown', () => {
142
- const schema = s.unknown();
143
-
144
- test('should accept any value', () => {
145
- expect(schema.parse(123)).toBe(123);
146
- expect(schema.parse('hello')).toBe('hello');
147
- expect(schema.parse(true)).toBe(true);
148
- expect(schema.parse(null)).toBe(null);
149
- expect(schema.parse(undefined)).toBe(undefined);
150
- expect(schema.parse({ foo: 'bar' })).toEqual({ foo: 'bar' });
151
- expect(schema.parse([1, 2, 3])).toEqual([1, 2, 3]);
152
- });
153
-
154
- test('should work with safeParse', () => {
155
- const result = schema.safeParse('anything');
156
- expect(result.success).toBe(true);
157
- if (result.success) {
158
- expect(result.data).toBe('anything');
159
- }
160
- });
161
- });
162
-
163
- describe('any', () => {
164
- const schema = s.any();
165
-
166
- test('should accept any value', () => {
167
- expect(schema.parse(123)).toBe(123);
168
- expect(schema.parse('hello')).toBe('hello');
169
- expect(schema.parse(true)).toBe(true);
170
- expect(schema.parse(null)).toBe(null);
171
- expect(schema.parse(undefined)).toBe(undefined);
172
- expect(schema.parse({ foo: 'bar' })).toEqual({ foo: 'bar' });
173
- expect(schema.parse([1, 2, 3])).toEqual([1, 2, 3]);
174
- });
175
-
176
- test('should work with safeParse', () => {
177
- const result = schema.safeParse('anything');
178
- expect(result.success).toBe(true);
179
- if (result.success) {
180
- expect(result.data).toBe('anything');
181
- }
182
- });
183
- });
184
- });
@@ -1,68 +0,0 @@
1
- import { describe, test, expect } from 'bun:test';
2
- import { s } from '../index.js';
3
-
4
- describe('Type Inference', () => {
5
- test('should infer object types', () => {
6
- const schema = s.object({
7
- name: s.string(),
8
- age: s.number(),
9
- });
10
-
11
- type User = s.infer<typeof schema>;
12
-
13
- const user: User = schema.parse({
14
- name: 'John',
15
- age: 30,
16
- });
17
-
18
- expect(user.name).toBe('John');
19
- expect(user.age).toBe(30);
20
- });
21
-
22
- test('should infer array types', () => {
23
- const schema = s.array(s.string());
24
- type StringArray = s.infer<typeof schema>;
25
-
26
- const arr: StringArray = schema.parse(['a', 'b', 'c']);
27
- expect(arr).toEqual(['a', 'b', 'c']);
28
- });
29
-
30
- test('should infer union types', () => {
31
- const schema = s.union(s.literal('admin'), s.literal('user'));
32
-
33
- type Role = s.infer<typeof schema>;
34
-
35
- const role: Role = schema.parse('admin');
36
- expect(role).toBe('admin');
37
- });
38
-
39
- test('should infer optional types', () => {
40
- const schema = s.object({
41
- required: s.string(),
42
- optional: s.optional(s.number()),
43
- });
44
-
45
- type Data = s.infer<typeof schema>;
46
-
47
- const data: Data = schema.parse({
48
- required: 'test',
49
- });
50
-
51
- expect(data.required).toBe('test');
52
- expect(data.optional).toBe(undefined);
53
- });
54
-
55
- test('should infer nullable types', () => {
56
- const schema = s.object({
57
- value: s.nullable(s.string()),
58
- });
59
-
60
- type Data = s.infer<typeof schema>;
61
-
62
- const data: Data = schema.parse({
63
- value: null,
64
- });
65
-
66
- expect(data.value).toBe(null);
67
- });
68
- });
@@ -1,100 +0,0 @@
1
- import { describe, test, expect } from 'bun:test';
2
- import { s, ValidationError } from '../index.js';
3
-
4
- describe('Utility Schemas', () => {
5
- describe('literal', () => {
6
- test('should validate exact string values', () => {
7
- const schema = s.literal('admin');
8
- expect(schema.parse('admin')).toBe('admin');
9
- expect(() => schema.parse('user')).toThrow(ValidationError);
10
- });
11
-
12
- test('should validate exact number values', () => {
13
- const schema = s.literal(42);
14
- expect(schema.parse(42)).toBe(42);
15
- expect(() => schema.parse(41)).toThrow(ValidationError);
16
- });
17
-
18
- test('should validate exact boolean values', () => {
19
- const schema = s.literal(true);
20
- expect(schema.parse(true)).toBe(true);
21
- expect(() => schema.parse(false)).toThrow(ValidationError);
22
- });
23
- });
24
-
25
- describe('enum', () => {
26
- test('should validate enum values', () => {
27
- const schema = s.enum(['red', 'green', 'blue']);
28
- expect(schema.parse('red')).toBe('red');
29
- expect(schema.parse('green')).toBe('green');
30
- expect(() => schema.parse('yellow')).toThrow(ValidationError);
31
- });
32
-
33
- test('should work with numbers', () => {
34
- const schema = s.enum([1, 2, 3]);
35
- expect(schema.parse(2)).toBe(2);
36
- expect(() => schema.parse(4)).toThrow(ValidationError);
37
- });
38
-
39
- test('should work with mixed types', () => {
40
- const schema = s.enum(['active', 'inactive', 1, 2]);
41
- expect(schema.parse('active')).toBe('active');
42
- expect(schema.parse(1)).toBe(1);
43
- expect(() => schema.parse('pending')).toThrow(ValidationError);
44
- });
45
- });
46
-
47
- describe('optional', () => {
48
- const schema = s.optional(s.string());
49
-
50
- test('should validate undefined', () => {
51
- expect(schema.parse(undefined)).toBe(undefined);
52
- });
53
-
54
- test('should validate the wrapped type', () => {
55
- expect(schema.parse('hello')).toBe('hello');
56
- });
57
-
58
- test('should reject invalid values', () => {
59
- expect(() => schema.parse(123)).toThrow(ValidationError);
60
- });
61
- });
62
-
63
- describe('nullable', () => {
64
- const schema = s.nullable(s.string());
65
-
66
- test('should validate null', () => {
67
- expect(schema.parse(null)).toBe(null);
68
- });
69
-
70
- test('should validate the wrapped type', () => {
71
- expect(schema.parse('hello')).toBe('hello');
72
- });
73
-
74
- test('should reject invalid values', () => {
75
- expect(() => schema.parse(123)).toThrow(ValidationError);
76
- });
77
- });
78
-
79
- describe('union', () => {
80
- const schema = s.union(s.string(), s.number(), s.boolean());
81
-
82
- test('should validate any union member', () => {
83
- expect(schema.parse('hello')).toBe('hello');
84
- expect(schema.parse(123)).toBe(123);
85
- expect(schema.parse(true)).toBe(true);
86
- });
87
-
88
- test('should reject non-members', () => {
89
- expect(() => schema.parse(null)).toThrow(ValidationError);
90
- expect(() => schema.parse(undefined)).toThrow(ValidationError);
91
- });
92
-
93
- test('should work with literal union (enum-like)', () => {
94
- const roleSchema = s.union(s.literal('admin'), s.literal('user'), s.literal('guest'));
95
-
96
- expect(roleSchema.parse('admin')).toBe('admin');
97
- expect(() => roleSchema.parse('superuser')).toThrow(ValidationError);
98
- });
99
- });
100
- });