yedra 0.20.2 → 0.20.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.
@@ -1,4 +1,4 @@
1
- import { Schema } from './schema.js';
1
+ import { Schema } from "./schema.js";
2
2
  export declare class DocSchema<T> extends Schema<T> {
3
3
  private readonly _schema;
4
4
  private readonly _description;
@@ -1,4 +1,4 @@
1
- import { Schema } from './schema.js';
1
+ import { Schema } from "./schema.js";
2
2
  export class DocSchema extends Schema {
3
3
  constructor(schema, description, example) {
4
4
  super();
@@ -12,7 +12,7 @@ export class DocSchema extends Schema {
12
12
  documentation() {
13
13
  return {
14
14
  description: this._description,
15
- example: this._example,
15
+ ...(this._example !== undefined && { example: this._example }),
16
16
  ...this._schema.documentation(),
17
17
  };
18
18
  }
@@ -1,4 +1,4 @@
1
- import { ModifiableSchema } from './modifiable.js';
1
+ import { ModifiableSchema } from "./modifiable.js";
2
2
  declare class IntegerSchema extends ModifiableSchema<number> {
3
3
  private readonly minValue?;
4
4
  private readonly maxValue?;
@@ -1,5 +1,5 @@
1
- import { Issue, ValidationError } from './error.js';
2
- import { ModifiableSchema } from './modifiable.js';
1
+ import { Issue, ValidationError } from "./error.js";
2
+ import { ModifiableSchema } from "./modifiable.js";
3
3
  class IntegerSchema extends ModifiableSchema {
4
4
  constructor(min, max) {
5
5
  super();
@@ -12,7 +12,7 @@ class IntegerSchema extends ModifiableSchema {
12
12
  */
13
13
  min(value) {
14
14
  if (!Number.isInteger(value)) {
15
- throw new Error('minimum value has to be an integer');
15
+ throw new Error("minimum value has to be an integer");
16
16
  }
17
17
  return new IntegerSchema(value, this.maxValue);
18
18
  }
@@ -22,17 +22,17 @@ class IntegerSchema extends ModifiableSchema {
22
22
  */
23
23
  max(value) {
24
24
  if (!Number.isInteger(value)) {
25
- throw new Error('maximum value has to be an integer');
25
+ throw new Error("maximum value has to be an integer");
26
26
  }
27
27
  return new IntegerSchema(this.minValue, value);
28
28
  }
29
29
  parse(obj) {
30
- if (typeof obj !== 'number' && typeof obj !== 'string') {
30
+ if (typeof obj !== "number" && typeof obj !== "string") {
31
31
  throw new ValidationError([
32
32
  new Issue([], `Expected number but got ${typeof obj}`),
33
33
  ]);
34
34
  }
35
- const num = typeof obj === 'number' ? obj : Number.parseFloat(obj);
35
+ const num = typeof obj === "number" ? obj : Number.parseFloat(obj);
36
36
  if (Number.isNaN(num) || !Number.isInteger(num)) {
37
37
  throw new ValidationError([
38
38
  new Issue([], `Expected integer but got ${typeof obj}`),
@@ -52,9 +52,9 @@ class IntegerSchema extends ModifiableSchema {
52
52
  }
53
53
  documentation() {
54
54
  return {
55
- type: 'integer',
56
- minimum: this.minValue,
57
- maximum: this.maxValue,
55
+ type: "integer",
56
+ ...(this.minValue !== undefined && { minimum: this.minValue }),
57
+ ...(this.maxValue !== undefined && { maximum: this.maxValue }),
58
58
  };
59
59
  }
60
60
  }
@@ -1,11 +1,24 @@
1
- import type { Typeof } from './body.js';
2
- import { DocSchema } from './doc.js';
3
- import { Schema } from './schema.js';
1
+ import type { Typeof } from "./body.js";
2
+ import { DocSchema } from "./doc.js";
3
+ import { Schema } from "./schema.js";
4
4
  export declare abstract class ModifiableSchema<T> extends Schema<T> {
5
5
  /**
6
6
  * Mark this schema as optional.
7
7
  */
8
8
  optional(): OptionalSchema<T>;
9
+ /**
10
+ * Provide a default value. If the input is undefined or null, the default
11
+ * value is returned instead.
12
+ * @param value - The default value.
13
+ */
14
+ default(value: T): DefaultSchema<T>;
15
+ /**
16
+ * Add a custom validation predicate. The inner schema is parsed first,
17
+ * then the predicate is checked against the parsed value.
18
+ * @param fn - A predicate that returns true if the value is valid, or a
19
+ * string error message if it is not.
20
+ */
21
+ refine(fn: (value: T) => boolean | string, docs?: Record<string, unknown>): RefinedSchema<T>;
9
22
  describe(description: string, example?: T): DocSchema<T>;
10
23
  array(): ArraySchema<Schema<T>>;
11
24
  }
@@ -27,27 +40,59 @@ declare class OptionalSchema<T> extends Schema<T | undefined> {
27
40
  documentation(): object;
28
41
  isOptional(): boolean;
29
42
  }
43
+ declare class DefaultSchema<T> extends Schema<T> {
44
+ private readonly schema;
45
+ private readonly defaultValue;
46
+ constructor(schema: Schema<T>, defaultValue: T);
47
+ describe(description: string, example?: T): DocSchema<T>;
48
+ array(): ArraySchema<Schema<T>>;
49
+ parse(obj: unknown): T;
50
+ documentation(): object;
51
+ isOptional(): boolean;
52
+ }
53
+ export declare class RefinedSchema<T> extends ModifiableSchema<T> {
54
+ private readonly schema;
55
+ private readonly predicate;
56
+ private readonly docs?;
57
+ constructor(schema: Schema<T>, predicate: (value: T) => boolean | string, docs?: Record<string, unknown>);
58
+ /**
59
+ * Set the minimum length/items for strings and arrays.
60
+ * @param value - The minimum constraint.
61
+ */
62
+ min(value: number): RefinedSchema<T>;
63
+ /**
64
+ * Set the maximum length/items for strings and arrays.
65
+ * @param value - The maximum constraint.
66
+ */
67
+ max(value: number): RefinedSchema<T>;
68
+ /**
69
+ * Set the exact length/items for strings and arrays.
70
+ * @param value - The exact constraint.
71
+ */
72
+ length(value: number): RefinedSchema<T>;
73
+ parse(obj: unknown): T;
74
+ documentation(): object;
75
+ isOptional(): boolean;
76
+ }
30
77
  export declare class ArraySchema<ItemSchema extends Schema<unknown>> extends ModifiableSchema<Typeof<ItemSchema>[]> {
31
78
  private readonly itemSchema;
32
- private readonly minItems?;
33
- private readonly maxItems?;
34
- constructor(itemSchema: ItemSchema, minItems?: number, maxItems?: number);
79
+ constructor(itemSchema: ItemSchema);
35
80
  /**
36
81
  * Set the minimum number of items for arrays.
37
82
  * @param items - The minimum number of items.
38
83
  */
39
- min(items: number): ArraySchema<ItemSchema>;
84
+ min(items: number): RefinedSchema<Typeof<ItemSchema>[]>;
40
85
  /**
41
86
  * Set the maximum number of items for arrays.
42
87
  * @param items - The maximum number of items.
43
88
  */
44
- max(items: number): ArraySchema<ItemSchema>;
89
+ max(items: number): RefinedSchema<Typeof<ItemSchema>[]>;
45
90
  /**
46
91
  * Set the exact number of items for arrays.
47
92
  * This is equivalent to calling both min and max.
48
93
  * @param items - The number of items.
49
94
  */
50
- length(items: number): ArraySchema<ItemSchema>;
95
+ length(items: number): RefinedSchema<Typeof<ItemSchema>[]>;
51
96
  parse(obj: unknown): Typeof<ItemSchema>[];
52
97
  documentation(): object;
53
98
  }
@@ -1,6 +1,6 @@
1
- import { DocSchema } from './doc.js';
2
- import { Issue, ValidationError } from './error.js';
3
- import { Schema } from './schema.js';
1
+ import { DocSchema } from "./doc.js";
2
+ import { Issue, ValidationError } from "./error.js";
3
+ import { Schema } from "./schema.js";
4
4
  export class ModifiableSchema extends Schema {
5
5
  /**
6
6
  * Mark this schema as optional.
@@ -8,6 +8,23 @@ export class ModifiableSchema extends Schema {
8
8
  optional() {
9
9
  return new OptionalSchema(this);
10
10
  }
11
+ /**
12
+ * Provide a default value. If the input is undefined or null, the default
13
+ * value is returned instead.
14
+ * @param value - The default value.
15
+ */
16
+ default(value) {
17
+ return new DefaultSchema(this, value);
18
+ }
19
+ /**
20
+ * Add a custom validation predicate. The inner schema is parsed first,
21
+ * then the predicate is checked against the parsed value.
22
+ * @param fn - A predicate that returns true if the value is valid, or a
23
+ * string error message if it is not.
24
+ */
25
+ refine(fn, docs) {
26
+ return new RefinedSchema(this, fn, docs);
27
+ }
11
28
  describe(description, example) {
12
29
  return new DocSchema(this, description, example);
13
30
  }
@@ -47,26 +64,115 @@ class OptionalSchema extends Schema {
47
64
  return true;
48
65
  }
49
66
  }
67
+ class DefaultSchema extends Schema {
68
+ constructor(schema, defaultValue) {
69
+ super();
70
+ this.schema = schema;
71
+ this.defaultValue = defaultValue;
72
+ }
73
+ describe(description, example) {
74
+ return new DocSchema(this, description, example);
75
+ }
76
+ array() {
77
+ return new ArraySchema(this);
78
+ }
79
+ parse(obj) {
80
+ if (obj === undefined || obj === null) {
81
+ return this.defaultValue;
82
+ }
83
+ return this.schema.parse(obj);
84
+ }
85
+ documentation() {
86
+ return {
87
+ ...this.schema.documentation(),
88
+ default: this.defaultValue,
89
+ };
90
+ }
91
+ isOptional() {
92
+ return true;
93
+ }
94
+ }
95
+ export class RefinedSchema extends ModifiableSchema {
96
+ constructor(schema, predicate, docs) {
97
+ super();
98
+ this.schema = schema;
99
+ this.predicate = predicate;
100
+ this.docs = docs;
101
+ }
102
+ /**
103
+ * Set the minimum length/items for strings and arrays.
104
+ * @param value - The minimum constraint.
105
+ */
106
+ min(value) {
107
+ const docs = this.documentation();
108
+ const isArray = docs.type === "array";
109
+ return this.refine(((v) => {
110
+ const len = v.length;
111
+ return (len >= value ||
112
+ (isArray
113
+ ? `Must have at least ${value} items`
114
+ : `Must be at least ${value} characters`));
115
+ }), isArray ? { minItems: value } : { minLength: value });
116
+ }
117
+ /**
118
+ * Set the maximum length/items for strings and arrays.
119
+ * @param value - The maximum constraint.
120
+ */
121
+ max(value) {
122
+ const docs = this.documentation();
123
+ const isArray = docs.type === "array";
124
+ return this.refine(((v) => {
125
+ const len = v.length;
126
+ return (len <= value ||
127
+ (isArray
128
+ ? `Must have at most ${value} items`
129
+ : `Must be at most ${value} characters`));
130
+ }), isArray ? { maxItems: value } : { maxLength: value });
131
+ }
132
+ /**
133
+ * Set the exact length/items for strings and arrays.
134
+ * @param value - The exact constraint.
135
+ */
136
+ length(value) {
137
+ return this.min(value).max(value);
138
+ }
139
+ parse(obj) {
140
+ const parsed = this.schema.parse(obj);
141
+ const result = this.predicate(parsed);
142
+ if (result === true) {
143
+ return parsed;
144
+ }
145
+ const message = typeof result === "string" ? result : "Validation failed";
146
+ throw new ValidationError([new Issue([], message)]);
147
+ }
148
+ documentation() {
149
+ return {
150
+ ...this.schema.documentation(),
151
+ ...this.docs,
152
+ };
153
+ }
154
+ isOptional() {
155
+ return this.schema.isOptional();
156
+ }
157
+ }
50
158
  export class ArraySchema extends ModifiableSchema {
51
- constructor(itemSchema, minItems, maxItems) {
159
+ constructor(itemSchema) {
52
160
  super();
53
161
  this.itemSchema = itemSchema;
54
- this.minItems = minItems;
55
- this.maxItems = maxItems;
56
162
  }
57
163
  /**
58
164
  * Set the minimum number of items for arrays.
59
165
  * @param items - The minimum number of items.
60
166
  */
61
167
  min(items) {
62
- return new ArraySchema(this.itemSchema, items, this.maxItems);
168
+ return this.refine((arr) => arr.length >= items || `Must have at least ${items} items`, { minItems: items });
63
169
  }
64
170
  /**
65
171
  * Set the maximum number of items for arrays.
66
172
  * @param items - The maximum number of items.
67
173
  */
68
174
  max(items) {
69
- return new ArraySchema(this.itemSchema, this.minItems, items);
175
+ return this.refine((arr) => arr.length <= items || `Must have at most ${items} items`, { maxItems: items });
70
176
  }
71
177
  /**
72
178
  * Set the exact number of items for arrays.
@@ -74,7 +180,7 @@ export class ArraySchema extends ModifiableSchema {
74
180
  * @param items - The number of items.
75
181
  */
76
182
  length(items) {
77
- return new ArraySchema(this.itemSchema, items, items);
183
+ return this.min(items).refine((arr) => arr.length <= items || `Must have at most ${items} items`, { maxItems: items });
78
184
  }
79
185
  parse(obj) {
80
186
  if (!Array.isArray(obj)) {
@@ -82,16 +188,6 @@ export class ArraySchema extends ModifiableSchema {
82
188
  new Issue([], `Expected array but got ${typeof obj}`),
83
189
  ]);
84
190
  }
85
- if (this.minItems && obj.length < this.minItems) {
86
- throw new ValidationError([
87
- new Issue([], `Must have at least ${this.minItems} items, but has ${obj.length}`),
88
- ]);
89
- }
90
- if (this.maxItems && obj.length > this.maxItems) {
91
- throw new ValidationError([
92
- new Issue([], `Must have at most ${this.maxItems} items, but has ${obj.length}`),
93
- ]);
94
- }
95
191
  const elems = [];
96
192
  const issues = [];
97
193
  for (let i = 0; i < obj.length; ++i) {
@@ -114,10 +210,8 @@ export class ArraySchema extends ModifiableSchema {
114
210
  }
115
211
  documentation() {
116
212
  return {
117
- type: 'array',
213
+ type: "array",
118
214
  items: this.itemSchema.documentation(),
119
- minItems: this.minItems,
120
- maxItems: this.maxItems,
121
215
  };
122
216
  }
123
217
  }
@@ -1,4 +1,4 @@
1
- import { ModifiableSchema } from './modifiable.js';
1
+ import { ModifiableSchema } from "./modifiable.js";
2
2
  declare class NumberSchema extends ModifiableSchema<number> {
3
3
  private readonly minValue?;
4
4
  private readonly maxValue?;
@@ -1,5 +1,5 @@
1
- import { Issue, ValidationError } from './error.js';
2
- import { ModifiableSchema } from './modifiable.js';
1
+ import { Issue, ValidationError } from "./error.js";
2
+ import { ModifiableSchema } from "./modifiable.js";
3
3
  class NumberSchema extends ModifiableSchema {
4
4
  constructor(min, max) {
5
5
  super();
@@ -21,12 +21,12 @@ class NumberSchema extends ModifiableSchema {
21
21
  return new NumberSchema(this.minValue, value);
22
22
  }
23
23
  parse(obj) {
24
- if (typeof obj !== 'number' && typeof obj !== 'string') {
24
+ if (typeof obj !== "number" && typeof obj !== "string") {
25
25
  throw new ValidationError([
26
26
  new Issue([], `Expected number but got ${typeof obj}`),
27
27
  ]);
28
28
  }
29
- const num = typeof obj === 'number' ? obj : Number.parseFloat(obj);
29
+ const num = typeof obj === "number" ? obj : Number.parseFloat(obj);
30
30
  if (Number.isNaN(num)) {
31
31
  throw new ValidationError([
32
32
  new Issue([], `Expected number but got ${typeof obj}`),
@@ -46,9 +46,9 @@ class NumberSchema extends ModifiableSchema {
46
46
  }
47
47
  documentation() {
48
48
  return {
49
- type: 'number',
50
- minimum: this.minValue,
51
- maximum: this.maxValue,
49
+ type: "number",
50
+ ...(this.minValue !== undefined && { minimum: this.minValue }),
51
+ ...(this.maxValue !== undefined && { maximum: this.maxValue }),
52
52
  };
53
53
  }
54
54
  }
@@ -1,6 +1,6 @@
1
- import type { Typeof } from './body.js';
2
- import { ModifiableSchema } from './modifiable.js';
3
- import { Schema } from './schema.js';
1
+ import type { Typeof } from "./body.js";
2
+ import { ModifiableSchema } from "./modifiable.js";
3
+ import { Schema } from "./schema.js";
4
4
  /**
5
5
  * Make a union of all keys that are not extended by undefined
6
6
  * (i.e. have undefined as a variant) of the specified type.
@@ -15,7 +15,7 @@ type MakeFieldsOptional<T> = Pick<T, RequiredKeys<T>> & Partial<T>;
15
15
  export declare class ObjectSchema<Shape extends Record<string, Schema<unknown>>> extends ModifiableSchema<MakeFieldsOptional<{
16
16
  [K in keyof Shape]: Typeof<Shape[K]>;
17
17
  }>> {
18
- private shape;
18
+ readonly shape: Shape;
19
19
  private lax;
20
20
  constructor(shape: Shape, lax: boolean);
21
21
  parse(obj: unknown): MakeFieldsOptional<{
@@ -1,6 +1,6 @@
1
- import { Issue, ValidationError } from './error.js';
2
- import { ModifiableSchema } from './modifiable.js';
3
- import { Schema } from './schema.js';
1
+ import { Issue, ValidationError } from "./error.js";
2
+ import { ModifiableSchema } from "./modifiable.js";
3
+ import { Schema } from "./schema.js";
4
4
  export class ObjectSchema extends ModifiableSchema {
5
5
  constructor(shape, lax) {
6
6
  super();
@@ -8,14 +8,14 @@ export class ObjectSchema extends ModifiableSchema {
8
8
  this.lax = lax;
9
9
  }
10
10
  parse(obj) {
11
- if (typeof obj !== 'object') {
11
+ if (typeof obj !== "object") {
12
12
  throw new ValidationError([
13
13
  new Issue([], `Expected object but got ${typeof obj}`),
14
14
  ]);
15
15
  }
16
16
  if (obj == null) {
17
17
  throw new ValidationError([
18
- new Issue([], 'Expected object but got null'),
18
+ new Issue([], "Expected object but got null"),
19
19
  ]);
20
20
  }
21
21
  const result = {};
@@ -24,7 +24,7 @@ export class ObjectSchema extends ModifiableSchema {
24
24
  const propSchema = this.shape[prop];
25
25
  if (propSchema instanceof Schema) {
26
26
  if (!(prop in obj || propSchema.isOptional())) {
27
- issues.push(new Issue([prop], 'Required'));
27
+ issues.push(new Issue([prop], "Required"));
28
28
  continue;
29
29
  }
30
30
  try {
@@ -45,7 +45,7 @@ export class ObjectSchema extends ModifiableSchema {
45
45
  if (prop in this.shape) {
46
46
  continue;
47
47
  }
48
- issues.push(new Issue([prop], 'Unrecognized'));
48
+ issues.push(new Issue([prop], "Unrecognized"));
49
49
  }
50
50
  }
51
51
  if (issues.length > 0) {
@@ -66,10 +66,10 @@ export class ObjectSchema extends ModifiableSchema {
66
66
  }
67
67
  }
68
68
  return {
69
- type: 'object',
69
+ type: "object",
70
70
  properties,
71
- required: required.length > 0 ? required : undefined,
72
- additionalProperties: this.lax,
71
+ ...(required.length > 0 && { required }),
72
+ ...(this.lax && { additionalProperties: true }),
73
73
  };
74
74
  }
75
75
  }
@@ -1,10 +1,10 @@
1
- import type { Readable } from 'node:stream';
2
- import type { StandardSchemaV1 } from '@standard-schema/spec';
3
- import { BodyType } from './body.js';
1
+ import type { Readable } from "node:stream";
2
+ import type { StandardSchemaV1 } from "@standard-schema/spec";
3
+ import { BodyType } from "./body.js";
4
4
  /**
5
5
  * The base class for all schemas.
6
6
  */
7
- export declare abstract class Schema<T> extends BodyType<T, T> implements StandardSchemaV1<unknown, T> {
7
+ export declare abstract class Schema<T> extends BodyType<T, T> implements StandardSchemaV1<T, T> {
8
8
  deserialize(stream: Readable, contentType: string): Promise<T>;
9
9
  bodyDocs(): object;
10
10
  /**
@@ -31,5 +31,5 @@ export declare abstract class Schema<T> extends BodyType<T, T> implements Standa
31
31
  *
32
32
  * @see https://standardschema.dev/
33
33
  */
34
- get '~standard'(): StandardSchemaV1.Props<unknown, T>;
34
+ get "~standard"(): StandardSchemaV1.Props<T, T>;
35
35
  }
@@ -1,5 +1,5 @@
1
- import { BodyType } from './body.js';
2
- import { Issue, ValidationError } from './error.js';
1
+ import { BodyType } from "./body.js";
2
+ import { Issue, ValidationError } from "./error.js";
3
3
  /**
4
4
  * The base class for all schemas.
5
5
  */
@@ -8,22 +8,22 @@ export class Schema extends BodyType {
8
8
  // Lazy import to keep this module browser-safe for yedra/schema.
9
9
  // deserialize() is only called server-side, so the Node-specific
10
10
  // stream utility is never resolved when bundled for the frontend.
11
- const { readableToBuffer } = await import('../util/stream.js');
11
+ const { readableToBuffer } = await import("../util/stream.js");
12
12
  const buffer = await readableToBuffer(stream);
13
13
  if (buffer.length === 0) {
14
14
  return this.parse({});
15
15
  }
16
- if (contentType !== 'application/json') {
16
+ if (contentType !== "application/json") {
17
17
  throw new ValidationError([
18
18
  new Issue([], `Expected content type \`application/json\`, but got \`${contentType}\``),
19
19
  ]);
20
20
  }
21
- const data = JSON.parse(Buffer.from(buffer).toString('utf8'));
21
+ const data = JSON.parse(Buffer.from(buffer).toString("utf8"));
22
22
  return this.parse(data);
23
23
  }
24
24
  bodyDocs() {
25
25
  return {
26
- 'application/json': {
26
+ "application/json": {
27
27
  schema: this.documentation(),
28
28
  },
29
29
  };
@@ -44,10 +44,11 @@ export class Schema extends BodyType {
44
44
  *
45
45
  * @see https://standardschema.dev/
46
46
  */
47
- get '~standard'() {
47
+ get "~standard"() {
48
48
  return {
49
49
  version: 1,
50
- vendor: 'yedra',
50
+ vendor: "yedra",
51
+ types: {},
51
52
  // Wraps parse() to match Standard Schema's non-throwing convention:
52
53
  // returns { value } on success, { issues } on failure.
53
54
  validate: (value) => {
@@ -1,16 +1,24 @@
1
- import { ModifiableSchema } from './modifiable.js';
1
+ import { ModifiableSchema } from "./modifiable.js";
2
2
  declare class StringSchema extends ModifiableSchema<string> {
3
+ /**
4
+ * Set the minimum length the string is allowed to be.
5
+ * @param length - The minimum length.
6
+ */
7
+ min(length: number): import("./modifiable.js").RefinedSchema<string>;
8
+ /**
9
+ * Set the maximum length the string is allowed to be.
10
+ * @param length - The maximum length.
11
+ */
12
+ max(length: number): import("./modifiable.js").RefinedSchema<string>;
13
+ /**
14
+ * Require the string to be a valid email address.
15
+ */
16
+ email(): import("./modifiable.js").RefinedSchema<string>;
3
17
  /**
4
18
  * Require the string to match the specified pattern.
5
19
  * @param pattern - A regular expression.
6
20
  */
7
- pattern(pattern: RegExp): PatternStringSchema;
8
- parse(obj: unknown): string;
9
- documentation(): object;
10
- }
11
- declare class PatternStringSchema extends ModifiableSchema<string> {
12
- private readonly pattern;
13
- constructor(pattern: RegExp);
21
+ pattern(pattern: RegExp): import("./modifiable.js").RefinedSchema<string>;
14
22
  parse(obj: unknown): string;
15
23
  documentation(): object;
16
24
  }
@@ -1,49 +1,47 @@
1
- import { Issue, ValidationError } from './error.js';
2
- import { ModifiableSchema } from './modifiable.js';
1
+ import { Issue, ValidationError } from "./error.js";
2
+ import { ModifiableSchema } from "./modifiable.js";
3
+ const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
3
4
  class StringSchema extends ModifiableSchema {
4
5
  /**
5
- * Require the string to match the specified pattern.
6
- * @param pattern - A regular expression.
6
+ * Set the minimum length the string is allowed to be.
7
+ * @param length - The minimum length.
7
8
  */
8
- pattern(pattern) {
9
- return new PatternStringSchema(pattern);
9
+ min(length) {
10
+ return this.refine((s) => s.length >= length || `Must be at least ${length} characters`, { minLength: length });
10
11
  }
11
- parse(obj) {
12
- if (typeof obj !== 'string') {
13
- throw new ValidationError([
14
- new Issue([], `Expected string but got ${typeof obj}`),
15
- ]);
16
- }
17
- return obj;
12
+ /**
13
+ * Set the maximum length the string is allowed to be.
14
+ * @param length - The maximum length.
15
+ */
16
+ max(length) {
17
+ return this.refine((s) => s.length <= length || `Must be at most ${length} characters`, { maxLength: length });
18
18
  }
19
- documentation() {
20
- return {
21
- type: 'string',
22
- };
19
+ /**
20
+ * Require the string to be a valid email address.
21
+ */
22
+ email() {
23
+ return this.refine((s) => EMAIL_REGEX.test(s) || "Expected email address", {
24
+ format: "email",
25
+ });
23
26
  }
24
- }
25
- class PatternStringSchema extends ModifiableSchema {
26
- constructor(pattern) {
27
- super();
28
- this.pattern = pattern;
27
+ /**
28
+ * Require the string to match the specified pattern.
29
+ * @param pattern - A regular expression.
30
+ */
31
+ pattern(pattern) {
32
+ return this.refine((s) => pattern.test(s) || `Does not match pattern /${pattern.source}/`, { pattern: pattern.source });
29
33
  }
30
34
  parse(obj) {
31
- if (typeof obj !== 'string') {
35
+ if (typeof obj !== "string") {
32
36
  throw new ValidationError([
33
37
  new Issue([], `Expected string but got ${typeof obj}`),
34
38
  ]);
35
39
  }
36
- if (!obj.match(this.pattern)) {
37
- throw new ValidationError([
38
- new Issue([], `\`${obj}\` does not match pattern /${this.pattern.source}/`),
39
- ]);
40
- }
41
40
  return obj;
42
41
  }
43
42
  documentation() {
44
43
  return {
45
- type: 'string',
46
- pattern: this.pattern.source,
44
+ type: "string",
47
45
  };
48
46
  }
49
47
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yedra",
3
- "version": "0.20.2",
3
+ "version": "0.20.4",
4
4
  "repository": "github:0codekit/yedra",
5
5
  "main": "dist/index.js",
6
6
  "exports": {
@@ -14,9 +14,9 @@
14
14
  }
15
15
  },
16
16
  "devDependencies": {
17
- "@biomejs/biome": "^2.3.14",
18
- "@types/bun": "^1.3.9",
19
- "@types/node": "^25.2.3",
17
+ "@biomejs/biome": "^2.3.12",
18
+ "@types/bun": "^1.3.6",
19
+ "@types/node": "^25.0.10",
20
20
  "@types/uuid": "^11.0.0",
21
21
  "@types/ws": "^8.18.1",
22
22
  "typescript": "^5.9.3"