@vertz/schema 0.1.0 → 0.2.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.
package/README.md ADDED
@@ -0,0 +1,640 @@
1
+ # @vertz/schema
2
+
3
+ > Type-safe schema definition and validation for JavaScript/TypeScript
4
+
5
+ A powerful validation library with end-to-end type inference, inspired by Zod. Define schemas with a fluent API and get full TypeScript types automatically.
6
+
7
+ ## Prerequisites
8
+
9
+ - **Node.js** 18+ or **Bun** 1.0+
10
+ - **TypeScript** 5.0+
11
+
12
+ ## Installation
13
+
14
+ ```bash
15
+ # npm
16
+ npm install @vertz/schema
17
+
18
+ # bun
19
+ bun add @vertz/schema
20
+ ```
21
+
22
+ ## Quick Start
23
+
24
+ ```typescript
25
+ import { s } from '@vertz/schema';
26
+
27
+ // Define a schema
28
+ const userSchema = s.object({
29
+ name: s.string().min(1),
30
+ email: s.string().email(),
31
+ age: s.number().int().min(18),
32
+ });
33
+
34
+ // Parse data (throws on invalid)
35
+ const user = userSchema.parse({
36
+ name: 'Alice',
37
+ email: 'alice@example.com',
38
+ age: 25,
39
+ });
40
+
41
+ // Safe parse (returns success/error)
42
+ const result = userSchema.safeParse({
43
+ name: 'Bob',
44
+ email: 'not-an-email',
45
+ age: 17,
46
+ });
47
+
48
+ if (result.success) {
49
+ console.log('Valid user:', result.value);
50
+ } else {
51
+ console.log('Validation errors:', result.error.issues);
52
+ }
53
+
54
+ // Type inference
55
+ type User = typeof userSchema._output;
56
+ // ✅ { name: string; email: string; age: number }
57
+ ```
58
+
59
+ ## Core Concepts
60
+
61
+ ### Primitives
62
+
63
+ ```typescript
64
+ import { s } from '@vertz/schema';
65
+
66
+ s.string() // string
67
+ s.number() // number
68
+ s.boolean() // boolean
69
+ s.bigint() // bigint
70
+ s.date() // Date
71
+ s.symbol() // symbol
72
+ s.int() // number (integer)
73
+ ```
74
+
75
+ ### Special Types
76
+
77
+ ```typescript
78
+ s.any() // any
79
+ s.unknown() // unknown
80
+ s.null() // null
81
+ s.undefined() // undefined
82
+ s.void() // void
83
+ s.never() // never
84
+ ```
85
+
86
+ ### Composite Types
87
+
88
+ ```typescript
89
+ // Objects
90
+ const personSchema = s.object({
91
+ name: s.string(),
92
+ age: s.number(),
93
+ });
94
+
95
+ // Arrays
96
+ const numbersSchema = s.array(s.number());
97
+
98
+ // Tuples
99
+ const pairSchema = s.tuple([s.string(), s.number()]);
100
+
101
+ // Enums
102
+ const roleSchema = s.enum(['admin', 'user', 'guest']);
103
+
104
+ // Literals
105
+ const yesSchema = s.literal('yes');
106
+
107
+ // Unions
108
+ const statusSchema = s.union([
109
+ s.literal('pending'),
110
+ s.literal('approved'),
111
+ s.literal('rejected'),
112
+ ]);
113
+
114
+ // Records (dynamic keys)
115
+ const configSchema = s.record(s.string());
116
+
117
+ // Maps
118
+ const mapSchema = s.map(s.string(), s.number());
119
+
120
+ // Sets
121
+ const setSchema = s.set(s.string());
122
+ ```
123
+
124
+ ### String Validations
125
+
126
+ ```typescript
127
+ const schema = s.string()
128
+ .min(3) // Min length
129
+ .max(20) // Max length
130
+ .length(10) // Exact length
131
+ .regex(/^[a-z]+$/) // Pattern matching
132
+ .startsWith('hello') // Prefix check
133
+ .endsWith('world') // Suffix check
134
+ .includes('mid') // Substring check
135
+ .uppercase() // Must be all uppercase
136
+ .lowercase() // Must be all lowercase
137
+ .nonempty() // Alias for .min(1)
138
+ .trim(); // Trim whitespace (transforms)
139
+ ```
140
+
141
+ ### Number Validations
142
+
143
+ ```typescript
144
+ const schema = s.number()
145
+ .int() // Must be integer
146
+ .positive() // > 0
147
+ .negative() // < 0
148
+ .nonpositive() // <= 0
149
+ .nonnegative() // >= 0
150
+ .min(0) // Minimum value
151
+ .max(100) // Maximum value
152
+ .multipleOf(5) // Must be divisible by n
153
+ .finite(); // No Infinity or NaN
154
+ ```
155
+
156
+ ### Format Validators
157
+
158
+ Built-in validators for common formats:
159
+
160
+ ```typescript
161
+ s.email() // Email address
162
+ s.uuid() // UUID (v1-v5)
163
+ s.url() // HTTP(S) URL
164
+ s.hostname() // Valid hostname
165
+ s.ipv4() // IPv4 address
166
+ s.ipv6() // IPv6 address
167
+ s.base64() // Base64 string
168
+ s.hex() // Hexadecimal string
169
+ s.jwt() // JWT token (format only, not verified)
170
+ s.cuid() // CUID
171
+ s.ulid() // ULID
172
+ s.nanoid() // Nano ID
173
+
174
+ // ISO formats
175
+ s.iso.date() // ISO 8601 date (YYYY-MM-DD)
176
+ s.iso.time() // ISO 8601 time (HH:MM:SS)
177
+ s.iso.datetime() // ISO 8601 datetime
178
+ s.iso.duration() // ISO 8601 duration (P1Y2M3D)
179
+ ```
180
+
181
+ ### Optional and Nullable
182
+
183
+ ```typescript
184
+ const schema = s.string().optional();
185
+ // string | undefined
186
+
187
+ const schema2 = s.string().nullable();
188
+ // string | null
189
+
190
+ const schema3 = s.string().nullish();
191
+ // string | null | undefined
192
+ ```
193
+
194
+ ### Default Values
195
+
196
+ ```typescript
197
+ const schema = s.string().default('hello');
198
+
199
+ schema.parse(undefined); // 'hello'
200
+ schema.parse('world'); // 'world'
201
+ ```
202
+
203
+ ### Transformations
204
+
205
+ ```typescript
206
+ const schema = s.string().transform((val) => val.toUpperCase());
207
+
208
+ schema.parse('hello'); // 'HELLO'
209
+
210
+ // Chain transformations
211
+ const trimmed = s.string().trim().transform((s) => s.toUpperCase());
212
+ trimmed.parse(' hello '); // 'HELLO'
213
+ ```
214
+
215
+ ### Refinements (Custom Validation)
216
+
217
+ ```typescript
218
+ const schema = s.string().refine(
219
+ (val) => val.includes('@'),
220
+ { message: 'Must contain @' },
221
+ );
222
+
223
+ // Multiple refinements
224
+ const passwordSchema = s.string()
225
+ .min(8)
226
+ .refine((val) => /[A-Z]/.test(val), {
227
+ message: 'Must contain uppercase letter',
228
+ })
229
+ .refine((val) => /[0-9]/.test(val), {
230
+ message: 'Must contain number',
231
+ });
232
+ ```
233
+
234
+ ### Super Refine (Access to Context)
235
+
236
+ ```typescript
237
+ const schema = s.object({
238
+ password: s.string(),
239
+ confirmPassword: s.string(),
240
+ }).superRefine((data, ctx) => {
241
+ if (data.password !== data.confirmPassword) {
242
+ ctx.addIssue({
243
+ code: 'custom',
244
+ path: ['confirmPassword'],
245
+ message: 'Passwords must match',
246
+ });
247
+ }
248
+ });
249
+ ```
250
+
251
+ ### Branded Types
252
+
253
+ Create nominal types that are structurally identical but semantically distinct:
254
+
255
+ ```typescript
256
+ const userIdSchema = s.string().uuid().brand('UserId');
257
+ const postIdSchema = s.string().uuid().brand('PostId');
258
+
259
+ type UserId = typeof userIdSchema._output; // string & Brand<'UserId'>
260
+ type PostId = typeof postIdSchema._output; // string & Brand<'PostId'>
261
+
262
+ // Type error: UserId and PostId are not assignable to each other
263
+ function getUser(id: UserId) { /* ... */ }
264
+ function getPost(id: PostId) { /* ... */ }
265
+
266
+ const userId = userIdSchema.parse('...');
267
+ getUser(userId); // ✅ OK
268
+ getPost(userId); // ❌ Type error
269
+ ```
270
+
271
+ ### Readonly
272
+
273
+ Mark types as readonly in the type system:
274
+
275
+ ```typescript
276
+ const schema = s.object({
277
+ name: s.string(),
278
+ tags: s.array(s.string()),
279
+ }).readonly();
280
+
281
+ type Result = typeof schema._output;
282
+ // Readonly<{ name: string; tags: readonly string[] }>
283
+ ```
284
+
285
+ ### Catch (Error Recovery)
286
+
287
+ Provide fallback values on parse errors:
288
+
289
+ ```typescript
290
+ const schema = s.number().catch(0);
291
+
292
+ schema.parse(42); // 42
293
+ schema.parse('invalid'); // 0 (caught and replaced)
294
+ ```
295
+
296
+ ## Parsing
297
+
298
+ ### `.parse(data)`
299
+
300
+ Parses and returns the data. Throws `ParseError` on validation failure.
301
+
302
+ ```typescript
303
+ const schema = s.string();
304
+
305
+ try {
306
+ const value = schema.parse('hello'); // 'hello'
307
+ } catch (error) {
308
+ if (error instanceof ParseError) {
309
+ console.log(error.issues);
310
+ }
311
+ }
312
+ ```
313
+
314
+ ### `.safeParse(data)`
315
+
316
+ Returns a result object with `success` boolean. Never throws.
317
+
318
+ ```typescript
319
+ const schema = s.number();
320
+
321
+ const result = schema.safeParse('42');
322
+
323
+ if (result.success) {
324
+ console.log(result.value); // number
325
+ } else {
326
+ console.log(result.error.issues); // ValidationIssue[]
327
+ }
328
+ ```
329
+
330
+ ## Type Inference
331
+
332
+ ### Output Types (Parsed Value)
333
+
334
+ ```typescript
335
+ import type { Infer, Output } from '@vertz/schema';
336
+
337
+ const schema = s.object({
338
+ name: s.string(),
339
+ age: s.number().optional(),
340
+ });
341
+
342
+ // All equivalent:
343
+ type User1 = typeof schema._output;
344
+ type User2 = Infer<typeof schema>;
345
+ type User3 = Output<typeof schema>;
346
+
347
+ // Result: { name: string; age?: number }
348
+ ```
349
+
350
+ ### Input Types (Before Parsing)
351
+
352
+ Use `Input<T>` for the type before transformations:
353
+
354
+ ```typescript
355
+ import type { Input } from '@vertz/schema';
356
+
357
+ const schema = s.string().transform((s) => s.length);
358
+
359
+ type In = Input<typeof schema>; // string
360
+ type Out = Output<typeof schema>; // number
361
+ ```
362
+
363
+ ## Coercion
364
+
365
+ Convert values to the target type:
366
+
367
+ ```typescript
368
+ import { s } from '@vertz/schema';
369
+
370
+ const schema = s.coerce.number();
371
+
372
+ schema.parse('42'); // 42 (string → number)
373
+ schema.parse(42); // 42 (already number)
374
+
375
+ // Available coercions:
376
+ s.coerce.string() // → string
377
+ s.coerce.number() // → number
378
+ s.coerce.boolean() // → boolean
379
+ s.coerce.bigint() // → bigint
380
+ s.coerce.date() // → Date
381
+ ```
382
+
383
+ ## JSON Schema Generation
384
+
385
+ Generate JSON Schema for interoperability:
386
+
387
+ ```typescript
388
+ import { toJSONSchema } from '@vertz/schema';
389
+
390
+ const schema = s.object({
391
+ name: s.string().min(1),
392
+ age: s.number().int().min(0),
393
+ });
394
+
395
+ const jsonSchema = toJSONSchema(schema);
396
+ /*
397
+ {
398
+ type: 'object',
399
+ properties: {
400
+ name: { type: 'string', minLength: 1 },
401
+ age: { type: 'integer', minimum: 0 }
402
+ },
403
+ required: ['name', 'age']
404
+ }
405
+ */
406
+ ```
407
+
408
+ ## Schema Registry
409
+
410
+ Register and reuse schemas by name:
411
+
412
+ ```typescript
413
+ import { SchemaRegistry } from '@vertz/schema';
414
+
415
+ const registry = new SchemaRegistry();
416
+
417
+ registry.register('User', s.object({
418
+ id: s.string().uuid(),
419
+ name: s.string(),
420
+ }));
421
+
422
+ registry.register('Post', s.object({
423
+ id: s.string().uuid(),
424
+ authorId: registry.ref('User').shape.id, // Reference other schemas
425
+ title: s.string(),
426
+ }));
427
+
428
+ const userSchema = registry.get('User');
429
+ ```
430
+
431
+ ## Advanced Patterns
432
+
433
+ ### Discriminated Unions
434
+
435
+ ```typescript
436
+ const messageSchema = s.discriminatedUnion('type', [
437
+ s.object({
438
+ type: s.literal('text'),
439
+ content: s.string(),
440
+ }),
441
+ s.object({
442
+ type: s.literal('image'),
443
+ url: s.url(),
444
+ alt: s.string().optional(),
445
+ }),
446
+ ]);
447
+
448
+ type Message = typeof messageSchema._output;
449
+ // { type: 'text'; content: string } | { type: 'image'; url: string; alt?: string }
450
+ ```
451
+
452
+ ### Recursive Types (with `lazy`)
453
+
454
+ ```typescript
455
+ interface Category {
456
+ name: string;
457
+ subcategories: Category[];
458
+ }
459
+
460
+ const categorySchema: s.Schema<Category> = s.object({
461
+ name: s.string(),
462
+ subcategories: s.lazy(() => s.array(categorySchema)),
463
+ });
464
+ ```
465
+
466
+ ### Intersection
467
+
468
+ ```typescript
469
+ const baseSchema = s.object({ id: s.string() });
470
+ const namedSchema = s.object({ name: s.string() });
471
+
472
+ const userSchema = s.intersection(baseSchema, namedSchema);
473
+ // { id: string; name: string }
474
+ ```
475
+
476
+ ### Custom Validators
477
+
478
+ ```typescript
479
+ const evenSchema = s.custom<number>(
480
+ (val) => typeof val === 'number' && val % 2 === 0,
481
+ 'Must be an even number',
482
+ );
483
+
484
+ evenSchema.parse(4); // ✅ 4
485
+ evenSchema.parse(5); // ❌ throws
486
+ ```
487
+
488
+ ### File Validation
489
+
490
+ ```typescript
491
+ const imageSchema = s.file()
492
+ .maxSize(5 * 1024 * 1024) // 5MB
493
+ .mimeType(['image/png', 'image/jpeg', 'image/webp']);
494
+
495
+ imageSchema.parse(file); // File object (browser or Node.js)
496
+ ```
497
+
498
+ ## Integration with @vertz/core
499
+
500
+ Use schemas for request validation in vertz apps:
501
+
502
+ ```typescript
503
+ import { createModuleDef } from '@vertz/core';
504
+ import { s } from '@vertz/schema';
505
+
506
+ const moduleDef = createModuleDef({ name: 'users' });
507
+
508
+ const createUserSchema = s.object({
509
+ name: s.string().min(1),
510
+ email: s.string().email(),
511
+ age: s.number().int().min(18),
512
+ });
513
+
514
+ const router = moduleDef.router({ prefix: '/users' });
515
+
516
+ router.post('/', {
517
+ body: createUserSchema,
518
+ handler: (ctx) => {
519
+ // ctx.body is fully typed as { name: string; email: string; age: number }
520
+ const { name, email, age } = ctx.body;
521
+ return { created: true, user: { name, email, age } };
522
+ },
523
+ });
524
+ ```
525
+
526
+ If validation fails, a `ValidationException` is automatically thrown with details.
527
+
528
+ ## Error Handling
529
+
530
+ ```typescript
531
+ import { ParseError } from '@vertz/schema';
532
+
533
+ const result = schema.safeParse(data);
534
+
535
+ if (!result.success) {
536
+ const { error } = result;
537
+
538
+ console.log(error.issues);
539
+ /*
540
+ [
541
+ {
542
+ code: 'invalid_type',
543
+ expected: 'string',
544
+ received: 'number',
545
+ path: ['name'],
546
+ message: 'Expected string, received number'
547
+ },
548
+ ...
549
+ ]
550
+ */
551
+ }
552
+ ```
553
+
554
+ ## Comparison to Zod
555
+
556
+ `@vertz/schema` is heavily inspired by Zod with similar API design:
557
+
558
+ | Feature | @vertz/schema | Zod |
559
+ |---------|--------------|-----|
560
+ | Type inference | ✅ | ✅ |
561
+ | Primitives | ✅ | ✅ |
562
+ | Objects/Arrays | ✅ | ✅ |
563
+ | Transformations | ✅ | ✅ |
564
+ | Refinements | ✅ | ✅ |
565
+ | Branded types | ✅ | ✅ |
566
+ | JSON Schema export | ✅ | ✅ |
567
+ | Schema registry | ✅ | ❌ |
568
+ | Format validators | ✅ (built-in) | ❌ (plugin) |
569
+ | ISO format methods | ✅ (`s.iso.*`) | ❌ |
570
+
571
+ If you're familiar with Zod, you should feel right at home!
572
+
573
+ ## API Reference
574
+
575
+ ### Factory Functions
576
+
577
+ All schemas are created via the `s` object:
578
+
579
+ ```typescript
580
+ import { s } from '@vertz/schema';
581
+ ```
582
+
583
+ ### Schema Methods
584
+
585
+ All schemas inherit these methods:
586
+
587
+ - `.parse(data)` — Parse and return (throws on error)
588
+ - `.safeParse(data)` — Parse and return `{ success, value?, error? }`
589
+ - `.optional()` — Make schema optional (`T | undefined`)
590
+ - `.nullable()` — Make schema nullable (`T | null`)
591
+ - `.nullish()` — Make schema nullish (`T | null | undefined`)
592
+ - `.default(value)` — Provide default value
593
+ - `.transform(fn)` — Transform the value after validation
594
+ - `.refine(fn, opts)` — Add custom validation
595
+ - `.superRefine(fn)` — Add custom validation with context
596
+ - `.brand<Brand>()` — Create branded type
597
+ - `.readonly()` — Mark as readonly
598
+ - `.catch(value)` — Provide fallback on error
599
+ - `.optional()` — Alias for `.or(s.undefined())`
600
+
601
+ ## TypeScript Tips
602
+
603
+ ### Extracting Types
604
+
605
+ ```typescript
606
+ const schema = s.object({
607
+ name: s.string(),
608
+ age: s.number(),
609
+ });
610
+
611
+ // Extract output type
612
+ type User = typeof schema._output;
613
+
614
+ // Extract input type (before transforms)
615
+ type UserInput = typeof schema._input;
616
+ ```
617
+
618
+ ### Extending Schemas
619
+
620
+ ```typescript
621
+ const baseUserSchema = s.object({
622
+ id: s.string().uuid(),
623
+ createdAt: s.date(),
624
+ });
625
+
626
+ const userWithEmailSchema = baseUserSchema.extend({
627
+ email: s.string().email(),
628
+ });
629
+ ```
630
+
631
+ ## Performance
632
+
633
+ - Schema definitions are immutable and reusable
634
+ - No code generation — pure runtime validation
635
+ - Optimized for common cases (primitives, objects, arrays)
636
+ - JSON Schema generation is cached
637
+
638
+ ## License
639
+
640
+ MIT
package/dist/index.d.ts CHANGED
@@ -380,26 +380,26 @@ declare class StringSchema extends Schema<string> {
380
380
  private _toUpperCase;
381
381
  private _normalize;
382
382
  _parse(value: unknown, ctx: ParseContext): string;
383
- min(n: number, message?: string): StringSchema;
384
- max(n: number, message?: string): StringSchema;
385
- length(n: number, message?: string): StringSchema;
386
- regex(pattern: RegExp): StringSchema;
387
- startsWith(prefix: string): StringSchema;
388
- endsWith(suffix: string): StringSchema;
389
- includes(substring: string): StringSchema;
390
- uppercase(): StringSchema;
391
- lowercase(): StringSchema;
392
- trim(): StringSchema;
393
- toLowerCase(): StringSchema;
394
- toUpperCase(): StringSchema;
395
- normalize(): StringSchema;
383
+ min(n: number, message?: string): this;
384
+ max(n: number, message?: string): this;
385
+ length(n: number, message?: string): this;
386
+ regex(pattern: RegExp): this;
387
+ startsWith(prefix: string): this;
388
+ endsWith(suffix: string): this;
389
+ includes(substring: string): this;
390
+ uppercase(): this;
391
+ lowercase(): this;
392
+ trim(): this;
393
+ toLowerCase(): this;
394
+ toUpperCase(): this;
395
+ normalize(): this;
396
396
  _schemaType(): SchemaType;
397
397
  _toJSONSchema(_tracker: RefTracker): JSONSchemaObject;
398
- _clone(): StringSchema;
398
+ _clone(): this;
399
399
  }
400
400
  declare class CoercedStringSchema extends StringSchema {
401
401
  _parse(value: unknown, ctx: ParseContext): string;
402
- _clone(): CoercedStringSchema;
402
+ _clone(): this;
403
403
  }
404
404
  declare class CoercedNumberSchema extends NumberSchema {
405
405
  _parse(value: unknown, ctx: ParseContext): number;
@@ -465,6 +465,8 @@ declare class DiscriminatedUnionSchema<T extends DiscriminatedOptions> extends S
465
465
  declare class EnumSchema<T extends readonly [string, ...string[]]> extends Schema<T[number]> {
466
466
  private readonly _values;
467
467
  constructor(values: T);
468
+ /** Public accessor for the enum's allowed values. */
469
+ get values(): T;
468
470
  _parse(value: unknown, ctx: ParseContext): T[number];
469
471
  _schemaType(): SchemaType;
470
472
  _toJSONSchema(_tracker: RefTracker): JSONSchemaObject;
@@ -770,6 +772,11 @@ declare const s: {
770
772
  datetime: () => IsoDatetimeSchema;
771
773
  duration: () => IsoDurationSchema;
772
774
  };
775
+ fromDbEnum: <const TValues extends readonly [string, ...string[]]>(column: {
776
+ _meta: {
777
+ enumValues: TValues;
778
+ };
779
+ }) => EnumSchema<TValues>;
773
780
  coerce: {
774
781
  string: () => CoercedStringSchema;
775
782
  number: () => CoercedNumberSchema;
package/dist/index.js CHANGED
@@ -1095,7 +1095,8 @@ class StringSchema extends Schema {
1095
1095
  return schema;
1096
1096
  }
1097
1097
  _clone() {
1098
- const clone = this._cloneBase(new StringSchema);
1098
+ const Ctor = this.constructor;
1099
+ const clone = this._cloneBase(new Ctor);
1099
1100
  clone._min = this._min;
1100
1101
  clone._minMessage = this._minMessage;
1101
1102
  clone._max = this._max;
@@ -1122,7 +1123,8 @@ class CoercedStringSchema extends StringSchema {
1122
1123
  return super._parse(value == null ? "" : String(value), ctx);
1123
1124
  }
1124
1125
  _clone() {
1125
- return Object.assign(new CoercedStringSchema, super._clone());
1126
+ const Ctor = this.constructor;
1127
+ return Object.assign(new Ctor, super._clone());
1126
1128
  }
1127
1129
  }
1128
1130
 
@@ -1299,6 +1301,9 @@ class EnumSchema extends Schema {
1299
1301
  super();
1300
1302
  this._values = values;
1301
1303
  }
1304
+ get values() {
1305
+ return this._values;
1306
+ }
1302
1307
  _parse(value, ctx) {
1303
1308
  if (!this._values.includes(value)) {
1304
1309
  ctx.addIssue({
@@ -2355,6 +2360,13 @@ var s = {
2355
2360
  datetime: () => new IsoDatetimeSchema,
2356
2361
  duration: () => new IsoDurationSchema
2357
2362
  },
2363
+ fromDbEnum: (column) => {
2364
+ const values = column._meta.enumValues;
2365
+ if (!values || values.length === 0) {
2366
+ throw new Error("s.fromDbEnum(): not an enum column — _meta.enumValues is missing or empty");
2367
+ }
2368
+ return new EnumSchema(values);
2369
+ },
2358
2370
  coerce: {
2359
2371
  string: () => new CoercedStringSchema,
2360
2372
  number: () => new CoercedNumberSchema,
package/package.json CHANGED
@@ -1,8 +1,9 @@
1
1
  {
2
2
  "name": "@vertz/schema",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
+ "description": "Type-safe schema definitions for Vertz",
6
7
  "repository": {
7
8
  "type": "git",
8
9
  "url": "https://github.com/vertz-dev/vertz.git",
@@ -30,11 +31,13 @@
30
31
  "typecheck": "tsc --noEmit"
31
32
  },
32
33
  "devDependencies": {
34
+ "@vitest/coverage-v8": "^4.0.18",
33
35
  "bunup": "latest",
34
36
  "typescript": "^5.7.0",
35
- "vitest": "^3.0.0"
37
+ "vitest": "^4.0.18"
36
38
  },
37
39
  "engines": {
38
40
  "node": ">=22"
39
- }
41
+ },
42
+ "sideEffects": false
40
43
  }