@nubase/core 0.1.0 → 0.1.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.
- package/dist/index.d.mts +658 -19
- package/dist/index.d.ts +658 -19
- package/dist/index.js +907 -67
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +872 -65
- package/dist/index.mjs.map +1 -1
- package/package.json +20 -18
- package/README.md +0 -51
- package/schema/base-schema.ts +0 -46
- package/schema/entity-schema.ts +0 -19
- package/schema/index.ts +0 -3
- package/schema/nu.test.ts +0 -325
- package/schema/nu.ts +0 -36
- package/schema/toZod.ts +0 -74
- package/schema/types.ts +0 -149
package/schema/nu.test.ts
DELETED
|
@@ -1,325 +0,0 @@
|
|
|
1
|
-
// src/tests/nu.test.ts
|
|
2
|
-
|
|
3
|
-
import { describe, it, expect, expectTypeOf } from 'vitest';
|
|
4
|
-
import { nu } from './nu';
|
|
5
|
-
import { toZod } from './toZod';
|
|
6
|
-
import { z } from 'zod';
|
|
7
|
-
|
|
8
|
-
describe('nubase Schema Library (nu)', () => {
|
|
9
|
-
|
|
10
|
-
// --- Basic Schema Creation and Metadata ---
|
|
11
|
-
it('should create a string schema with metadata', () => {
|
|
12
|
-
const stringSchema = nu.string().meta({
|
|
13
|
-
label: 'Username',
|
|
14
|
-
description: 'The user\'s login name',
|
|
15
|
-
})
|
|
16
|
-
expect(stringSchema).toBeDefined();
|
|
17
|
-
expect(stringSchema._meta.label).toBe('Username');
|
|
18
|
-
expect(stringSchema._meta.description).toBe('The user\'s login name');
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
it('should create a number schema with metadata', () => {
|
|
22
|
-
const numberSchema = nu.number().meta({
|
|
23
|
-
label: 'Age',
|
|
24
|
-
});
|
|
25
|
-
expect(numberSchema).toBeDefined();
|
|
26
|
-
expect(numberSchema._meta.label).toBe('Age');
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
it('should create an object schema with nested schemas and metadata', () => {
|
|
30
|
-
const objectSchema = nu.object({
|
|
31
|
-
id: nu.number(),
|
|
32
|
-
name: nu.string().meta({
|
|
33
|
-
label: 'Full Name',
|
|
34
|
-
}),
|
|
35
|
-
address: nu.object({
|
|
36
|
-
street: nu.string(),
|
|
37
|
-
city: nu.string(),
|
|
38
|
-
zip: nu.string(),
|
|
39
|
-
}).meta({
|
|
40
|
-
label: 'Address',
|
|
41
|
-
description: 'User\'s mailing address',
|
|
42
|
-
}),
|
|
43
|
-
}).meta({
|
|
44
|
-
description: 'User Profile',
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
expect(objectSchema).toBeDefined();
|
|
48
|
-
expect(objectSchema._shape.id).toBeDefined();
|
|
49
|
-
expect(objectSchema._shape.name).toBeDefined();
|
|
50
|
-
expect(objectSchema._shape.name._meta.label).toBe('Full Name');
|
|
51
|
-
expect(objectSchema._meta.description).toBe('User Profile');
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
it('should create an array schema with an element schema', () => {
|
|
55
|
-
const arraySchema = nu.array(nu.string().meta({ label: 'Item' })).meta({ label: 'List of Items' });
|
|
56
|
-
expect(arraySchema).toBeDefined();
|
|
57
|
-
expect(arraySchema._element).toBeDefined();
|
|
58
|
-
expect(arraySchema._element._meta.label).toBe('Item');
|
|
59
|
-
expect(arraySchema._meta.label).toBe('List of Items');
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
// --- Parse/Validation Tests ---
|
|
63
|
-
it('should parse valid string data', () => {
|
|
64
|
-
const stringSchema = nu.string();
|
|
65
|
-
expect(stringSchema.parse('hello')).toBe('hello');
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
it('should throw error for invalid string data', () => {
|
|
69
|
-
const stringSchema = nu.string();
|
|
70
|
-
expect(() => stringSchema.parse(123)).toThrow('Expected string, received number');
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
it('should parse valid number data', () => {
|
|
74
|
-
const numberSchema = nu.number();
|
|
75
|
-
expect(numberSchema.parse(123)).toBe(123);
|
|
76
|
-
expect(numberSchema.parse(123.45)).toBe(123.45);
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
it('should throw error for invalid number data', () => {
|
|
80
|
-
const numberSchema = nu.number();
|
|
81
|
-
expect(() => numberSchema.parse('abc')).toThrow('Expected number, received string');
|
|
82
|
-
expect(() => numberSchema.parse(NaN)).toThrow('Expected number, received number'); // NaN is typeof number but not finite
|
|
83
|
-
expect(() => numberSchema.parse(Infinity)).toThrow('Expected number, received number');
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
it('should parse valid object data', () => {
|
|
87
|
-
const userSchema = nu.object({
|
|
88
|
-
id: nu.number(),
|
|
89
|
-
name: nu.string(),
|
|
90
|
-
isActive: nu.boolean().meta({ label: 'Active', defaultValue: true }), // Only supported keys
|
|
91
|
-
});
|
|
92
|
-
// Note: Literal/boolean schemas aren't implemented, using parse here as a placeholder for demo.
|
|
93
|
-
// A real implementation would have nu.boolean() and potentially nu.literal()
|
|
94
|
-
|
|
95
|
-
const validData = { id: 1, name: 'Alice', isActive: true };
|
|
96
|
-
expect(userSchema.parse(validData)).toEqual(validData);
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
it('should throw error for invalid object data (wrong type)', () => {
|
|
101
|
-
const userSchema = nu.object({ id: nu.number(), name: nu.string() });
|
|
102
|
-
expect(() => userSchema.parse(123)).toThrow('Expected object, received number');
|
|
103
|
-
expect(() => userSchema.parse(null)).toThrow('Expected object, received object'); // typeof null is 'object'
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
it('should throw error for invalid object data (invalid property)', () => {
|
|
107
|
-
const userSchema = nu.object({ id: nu.number(), name: nu.string() });
|
|
108
|
-
const invalidData = { id: 'one', name: 'Alice' };
|
|
109
|
-
expect(() => userSchema.parse(invalidData)).toThrow(/Object validation failed:\nProperty "id": Expected number, received string/);
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
// Note: Current ObjectSchema parse ignores extra keys. Add a test if you change that.
|
|
113
|
-
it('should ignore extra keys in object data by default', () => {
|
|
114
|
-
const userSchema = nu.object({ id: nu.number(), name: nu.string() });
|
|
115
|
-
const dataWithExtra = { id: 1, name: 'Alice', age: 30 };
|
|
116
|
-
const parsedData = userSchema.parse(dataWithExtra);
|
|
117
|
-
expect(parsedData).toEqual({ id: 1, name: 'Alice' }); // Extra 'age' is ignored
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
it('should parse valid array data', () => {
|
|
122
|
-
const stringArraySchema = nu.array(nu.string());
|
|
123
|
-
const validData = ['a', 'b', 'c'];
|
|
124
|
-
expect(stringArraySchema.parse(validData)).toEqual(validData);
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
it('should throw error for invalid array data (wrong type)', () => {
|
|
128
|
-
const stringArraySchema = nu.array(nu.string());
|
|
129
|
-
expect(() => stringArraySchema.parse('not an array')).toThrow('Expected array, received string');
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
it('should throw error for invalid array data (invalid element)', () => {
|
|
133
|
-
const stringArraySchema = nu.array(nu.string());
|
|
134
|
-
const invalidData = ['a', 123, 'c'];
|
|
135
|
-
expect(() => stringArraySchema.parse(invalidData)).toThrow(/Array validation failed:\nElement at index 1: Expected string, received number/);
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
// --- toZod Conversion Tests ---
|
|
139
|
-
|
|
140
|
-
it('should convert a string schema to a Zod string schema', () => {
|
|
141
|
-
const nuString = nu.string();
|
|
142
|
-
const zodString = toZod(nuString);
|
|
143
|
-
expect(zodString instanceof z.ZodString).toBe(true);
|
|
144
|
-
// Test Zod validation
|
|
145
|
-
expect(zodString.parse('test')).toBe('test');
|
|
146
|
-
expect(() => zodString.parse(123)).toThrow();
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
it('should convert a number schema to a Zod number schema', () => {
|
|
150
|
-
const nuNumber = nu.number();
|
|
151
|
-
const zodNumber = toZod(nuNumber);
|
|
152
|
-
expect(zodNumber instanceof z.ZodNumber).toBe(true);
|
|
153
|
-
// Test Zod validation
|
|
154
|
-
expect(zodNumber.parse(123)).toBe(123);
|
|
155
|
-
expect(() => zodNumber.parse('test')).toThrow();
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
it('should convert an object schema to a Zod object schema', () => {
|
|
159
|
-
const nuObject = nu.object({
|
|
160
|
-
name: nu.string(),
|
|
161
|
-
age: nu.number().meta({ label: 'User Age' })
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
const zodObject = toZod(nuObject);
|
|
165
|
-
|
|
166
|
-
expect(zodObject instanceof z.ZodObject).toBe(true);
|
|
167
|
-
|
|
168
|
-
// Check the structure of the converted Zod schema
|
|
169
|
-
expect(zodObject.shape.name instanceof z.ZodString).toBe(true);
|
|
170
|
-
expect(zodObject.shape.age instanceof z.ZodNumber).toBe(true);
|
|
171
|
-
|
|
172
|
-
// Test Zod validation
|
|
173
|
-
const validData = { name: 'Bob', age: 42 };
|
|
174
|
-
expect(zodObject.parse(validData)).toEqual(validData);
|
|
175
|
-
expect(() => zodObject.parse({ name: 'Bob', age: 'old' })).toThrow();
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
it('should convert an array schema to a Zod array schema', () => {
|
|
179
|
-
const nuArray = nu.array(nu.number());
|
|
180
|
-
const zodArray = toZod(nuArray);
|
|
181
|
-
expect(zodArray instanceof z.ZodArray).toBe(true);
|
|
182
|
-
expect(zodArray.element instanceof z.ZodNumber).toBe(true);
|
|
183
|
-
|
|
184
|
-
// Test Zod validation
|
|
185
|
-
const validData = [1, 2, 3];
|
|
186
|
-
expect(zodArray.parse(validData)).toEqual(validData);
|
|
187
|
-
expect(() => zodArray.parse([1, 'two', 3])).toThrow();
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
it('should handle nested structures in toZod conversion', () => {
|
|
191
|
-
const nestedNuSchema = nu.object({
|
|
192
|
-
user: nu.object({
|
|
193
|
-
name: nu.string(),
|
|
194
|
-
address: nu.object({
|
|
195
|
-
street: nu.string(),
|
|
196
|
-
zip: nu.string()
|
|
197
|
-
})
|
|
198
|
-
}),
|
|
199
|
-
tags: nu.array(nu.string())
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
const nestedZodSchema = toZod(nestedNuSchema);
|
|
203
|
-
|
|
204
|
-
// Check types in the converted Zod schema structure
|
|
205
|
-
expect(nestedZodSchema instanceof z.ZodObject).toBe(true);
|
|
206
|
-
expect(nestedZodSchema.shape.user instanceof z.ZodObject).toBe(true);
|
|
207
|
-
expect((nestedZodSchema.shape.user as z.ZodObject<any>).shape.name instanceof z.ZodString).toBe(true);
|
|
208
|
-
expect((nestedZodSchema.shape.user as z.ZodObject<any>).shape.address instanceof z.ZodObject).toBe(true);
|
|
209
|
-
expect(((nestedZodSchema.shape.user as z.ZodObject<any>).shape.address as z.ZodObject<any>).shape.street instanceof z.ZodString).toBe(true);
|
|
210
|
-
expect(nestedZodSchema.shape.tags instanceof z.ZodArray).toBe(true);
|
|
211
|
-
expect((nestedZodSchema.shape.tags as z.ZodArray<any>).element instanceof z.ZodString).toBe(true);
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
// Test Zod validation with nested structure
|
|
215
|
-
const validData = {
|
|
216
|
-
user: {
|
|
217
|
-
name: 'Charlie',
|
|
218
|
-
address: { street: 'Main St', zip: '12345' }
|
|
219
|
-
},
|
|
220
|
-
tags: ['a', 'b']
|
|
221
|
-
};
|
|
222
|
-
expect(nestedZodSchema.parse(validData)).toEqual(validData);
|
|
223
|
-
|
|
224
|
-
const invalidData = {
|
|
225
|
-
user: {
|
|
226
|
-
name: 'Charlie',
|
|
227
|
-
address: { street: 123, zip: '12345' } // invalid street type
|
|
228
|
-
},
|
|
229
|
-
tags: ['a', 'b']
|
|
230
|
-
};
|
|
231
|
-
expect(() => nestedZodSchema.parse(invalidData)).toThrow();
|
|
232
|
-
});
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
// --- TypeScript Type Inference Tests ---
|
|
236
|
-
|
|
237
|
-
it('should infer correct type for primitive schemas', () => {
|
|
238
|
-
const s = nu.string();
|
|
239
|
-
const n = nu.number();
|
|
240
|
-
|
|
241
|
-
// Check inferred output type
|
|
242
|
-
expectTypeOf(s).toHaveProperty('_outputType').toBeString();
|
|
243
|
-
expectTypeOf(n).toHaveProperty('_outputType').toBeNumber();
|
|
244
|
-
});
|
|
245
|
-
|
|
246
|
-
it('should infer correct type for object schema', () => {
|
|
247
|
-
const userSchema = nu.object({
|
|
248
|
-
id: nu.number().meta({ label: 'User ID' }),
|
|
249
|
-
name: nu.string(),
|
|
250
|
-
settings: nu.object({
|
|
251
|
-
theme: nu.string(),
|
|
252
|
-
darkMode: nu.boolean().meta({ label: 'Switch' }) // Only supported keys
|
|
253
|
-
})
|
|
254
|
-
});
|
|
255
|
-
|
|
256
|
-
// Check inferred output type structure
|
|
257
|
-
expectTypeOf(userSchema).toHaveProperty('_outputType').toMatchTypeOf<{
|
|
258
|
-
id: number;
|
|
259
|
-
name: string;
|
|
260
|
-
settings: {
|
|
261
|
-
theme: string;
|
|
262
|
-
darkMode: any; // Placeholder output type from .parse(true)
|
|
263
|
-
}
|
|
264
|
-
}>();
|
|
265
|
-
|
|
266
|
-
// Test that assigning parsed data is type-safe
|
|
267
|
-
const userData = { id: 1, name: 'Test', settings: { theme: 'dark', darkMode: false } };
|
|
268
|
-
const parsedUser = userSchema.parse(userData);
|
|
269
|
-
expectTypeOf(parsedUser).toMatchTypeOf<{ id: number; name: string; settings: { theme: string; darkMode: any; } }>();
|
|
270
|
-
|
|
271
|
-
// This would be a TS error if types mismatched:
|
|
272
|
-
// const badParsedUser: string = userSchema.parse(userData); // TS error expected
|
|
273
|
-
});
|
|
274
|
-
|
|
275
|
-
it('should infer correct type for array schema', () => {
|
|
276
|
-
const numberArraySchema = nu.array(nu.number()); // Removed unsupported minValue metadata
|
|
277
|
-
const stringArraySchema = nu.array(nu.string().meta({ label: 'Entry' }));
|
|
278
|
-
const arrayOfObjectsSchema = nu.array(nu.object({ id: nu.number(), value: nu.string() }));
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
// Check inferred output types
|
|
282
|
-
expectTypeOf(numberArraySchema).toHaveProperty('_outputType').toBeArray();
|
|
283
|
-
expectTypeOf(stringArraySchema).toHaveProperty('_outputType').toBeArray();
|
|
284
|
-
expectTypeOf(arrayOfObjectsSchema).toHaveProperty('_outputType').toBeArray();
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
// Test that assigning parsed data is type-safe
|
|
288
|
-
const numberArrayData = [1, 2, 3];
|
|
289
|
-
const parsedNumberArray = numberArraySchema.parse(numberArrayData);
|
|
290
|
-
expectTypeOf(parsedNumberArray).toBeArray();
|
|
291
|
-
// This would be a TS error:
|
|
292
|
-
// const badParsedNumberArray: string[] = parsedNumberArray; // TS error expected
|
|
293
|
-
});
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
it('should infer correct Zod schema type after toZod conversion', () => {
|
|
297
|
-
const nuString = nu.string().meta({ label: 'ID' });
|
|
298
|
-
const zodString = toZod(nuString);
|
|
299
|
-
expectTypeOf(zodString).toMatchTypeOf<z.ZodString>(); // Should be a ZodString
|
|
300
|
-
expectTypeOf(zodString._output).toBeString(); // Should infer string output
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
const nuObject = nu.object({
|
|
304
|
-
name: nu.string(),
|
|
305
|
-
age: nu.number()
|
|
306
|
-
});
|
|
307
|
-
const zodObject = toZod(nuObject);
|
|
308
|
-
expectTypeOf(zodObject).toMatchTypeOf<z.ZodObject<any>>(); // Should be a ZodObject
|
|
309
|
-
expectTypeOf(zodObject._output).toMatchTypeOf<{ name: string, age: number }>(); // Should infer the object type
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
const nuArray = nu.array(nu.string());
|
|
313
|
-
const zodArray = toZod(nuArray);
|
|
314
|
-
expectTypeOf(zodArray).toMatchTypeOf<z.ZodArray<z.ZodString>>(); // Should be ZodArray of ZodString
|
|
315
|
-
|
|
316
|
-
const nuNested = nu.object({
|
|
317
|
-
items: nu.array(nu.object({
|
|
318
|
-
id: nu.number()
|
|
319
|
-
}))
|
|
320
|
-
});
|
|
321
|
-
const zodNested = toZod(nuNested);
|
|
322
|
-
expectTypeOf(zodNested._output).toMatchTypeOf<{ items: Array<{ id: number }> }>(); // Should infer nested type
|
|
323
|
-
});
|
|
324
|
-
|
|
325
|
-
});
|
package/schema/nu.ts
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
// src/nu.ts
|
|
2
|
-
|
|
3
|
-
import { BaseSchema } from "./base-schema";
|
|
4
|
-
import { StringSchema, NumberSchema, ObjectShape, ObjectSchema, ArraySchema, BooleanSchema } from "./types";
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* The main nubase schema instance.
|
|
9
|
-
* Provides factory methods to create different schema types.
|
|
10
|
-
*/
|
|
11
|
-
export const nu = {
|
|
12
|
-
boolean : () => new BooleanSchema(),
|
|
13
|
-
/**
|
|
14
|
-
* Creates a string schema.
|
|
15
|
-
*/
|
|
16
|
-
string: () => new StringSchema(),
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Creates a number schema.
|
|
20
|
-
*/
|
|
21
|
-
number: () => new NumberSchema(),
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Creates an object schema with a defined shape.
|
|
25
|
-
* @param shape An object mapping keys to schemas.
|
|
26
|
-
*/
|
|
27
|
-
object: <TShape extends ObjectShape>(shape: TShape) => new ObjectSchema(shape),
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Creates an array schema with a defined element schema.
|
|
31
|
-
* @param elementSchema The schema for elements within the array.
|
|
32
|
-
*/
|
|
33
|
-
array: <TElementSchema extends BaseSchema<any>>(elementSchema: TElementSchema) => new ArraySchema(elementSchema),
|
|
34
|
-
|
|
35
|
-
// Add more factory methods here (boolean, union, literal, etc.)
|
|
36
|
-
};
|
package/schema/toZod.ts
DELETED
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
// src/converters/toZod.ts
|
|
2
|
-
|
|
3
|
-
import { z } from 'zod';
|
|
4
|
-
import {
|
|
5
|
-
StringSchema,
|
|
6
|
-
NumberSchema,
|
|
7
|
-
ObjectSchema,
|
|
8
|
-
ArraySchema,
|
|
9
|
-
NuSchema, // The union type of all nu schemas
|
|
10
|
-
ObjectOutput // Type utility to get object output type
|
|
11
|
-
} from '../schema/types';
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Infers the Zod schema type corresponding to a given NuSchema type.
|
|
16
|
-
* This is a conditional type that maps NuSchema types to their Zod equivalents
|
|
17
|
-
* and preserves the output type.
|
|
18
|
-
*/
|
|
19
|
-
export type NuSchemaToZodSchema<S extends NuSchema> =
|
|
20
|
-
S extends StringSchema ? z.ZodString :
|
|
21
|
-
S extends NumberSchema ? z.ZodNumber :
|
|
22
|
-
S extends ObjectSchema<infer TShape> ? z.ZodObject<{
|
|
23
|
-
[K in keyof TShape]: NuSchemaToZodSchema<TShape[K]>;
|
|
24
|
-
}, any, z.ZodTypeAny, ObjectOutput<TShape>> : // Recursive mapping for object shape
|
|
25
|
-
S extends ArraySchema<infer TElementSchema> ? z.ZodArray<NuSchemaToZodSchema<TElementSchema>> : // Recursive mapping for array element
|
|
26
|
-
z.ZodSchema<any>; // Fallback for unknown types
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Converts a nubase schema to a Zod schema.
|
|
31
|
-
* Preserves the TypeScript output type.
|
|
32
|
-
* Metadata (label, description etc.) is NOT translated to Zod schema properties
|
|
33
|
-
* as Zod doesn't have direct equivalents in its core schema structure.
|
|
34
|
-
* It only translates the validation structure and output type.
|
|
35
|
-
*
|
|
36
|
-
* @param schema The nubase schema to convert.
|
|
37
|
-
* @returns The equivalent Zod schema.
|
|
38
|
-
*/
|
|
39
|
-
export function toZod<S extends NuSchema>(schema: S): NuSchemaToZodSchema<S> {
|
|
40
|
-
if (schema instanceof StringSchema) {
|
|
41
|
-
// Add Zod string validations based on schema._meta or specific properties if they exist
|
|
42
|
-
// e.g., if (schema._meta.minLength) zodSchema = zodSchema.min(schema._meta.minLength);
|
|
43
|
-
// For now, just the base type:
|
|
44
|
-
return z.string() as NuSchemaToZodSchema<S>;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
if (schema instanceof NumberSchema) {
|
|
48
|
-
// Add Zod number validations similarly
|
|
49
|
-
return z.number() as NuSchemaToZodSchema<S>;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
if (schema instanceof ObjectSchema) {
|
|
53
|
-
const zodShape: Record<string, z.ZodTypeAny> = {};
|
|
54
|
-
// Recursively convert each schema in the shape
|
|
55
|
-
for (const key in schema._shape) {
|
|
56
|
-
if (Object.prototype.hasOwnProperty.call(schema._shape, key)) {
|
|
57
|
-
zodShape[key] = toZod(schema._shape[key]); // Recursive call
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
// Zod object constructor needs the shape object
|
|
61
|
-
return z.object(zodShape) as NuSchemaToZodSchema<S>;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
if (schema instanceof ArraySchema) {
|
|
65
|
-
// Recursively convert the element schema
|
|
66
|
-
const zodElementSchema = toZod(schema._element);
|
|
67
|
-
// Zod array constructor needs the element schema
|
|
68
|
-
return z.array(zodElementSchema) as NuSchemaToZodSchema<S>;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// Handle other schema types here...
|
|
72
|
-
// For now, throw an error for unsupported types
|
|
73
|
-
throw new Error(`Unsupported schema type for Zod conversion: ${schema.constructor.name}`);
|
|
74
|
-
}
|
package/schema/types.ts
DELETED
|
@@ -1,149 +0,0 @@
|
|
|
1
|
-
// src/schema/types.ts
|
|
2
|
-
|
|
3
|
-
import { BaseSchema } from './base-schema';
|
|
4
|
-
|
|
5
|
-
// --- Primitive Schemas ---
|
|
6
|
-
|
|
7
|
-
export class BooleanSchema extends BaseSchema<boolean> {
|
|
8
|
-
parse(data: any): boolean {
|
|
9
|
-
if (typeof data !== 'boolean') {
|
|
10
|
-
throw new Error(`Expected boolean, received ${typeof data}`);
|
|
11
|
-
}
|
|
12
|
-
return data;
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export class StringSchema extends BaseSchema<string> {
|
|
17
|
-
parse(data: any): string {
|
|
18
|
-
if (typeof data !== 'string') {
|
|
19
|
-
throw new Error(`Expected string, received ${typeof data}`);
|
|
20
|
-
}
|
|
21
|
-
return data;
|
|
22
|
-
}
|
|
23
|
-
// Add string-specific validation methods here (e.g., minLength, pattern)
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export class NumberSchema extends BaseSchema<number> {
|
|
27
|
-
parse(data: any): number {
|
|
28
|
-
if (typeof data !== 'number' || !Number.isFinite(data)) {
|
|
29
|
-
throw new Error(`Expected number, received ${typeof data}`);
|
|
30
|
-
}
|
|
31
|
-
return data;
|
|
32
|
-
}
|
|
33
|
-
// Add number-specific validation methods here (e.g., min, max)
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// Add more primitives like BooleanSchema, NullableSchema, OptionalSchema etc.
|
|
37
|
-
|
|
38
|
-
// --- Complex Schemas ---
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Type representing the shape of an object schema (key to schema mapping).
|
|
42
|
-
*/
|
|
43
|
-
export type ObjectShape = {
|
|
44
|
-
[key: string]: BaseSchema<any>;
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Infers the TypeScript output type from an ObjectShape.
|
|
49
|
-
* Use mapped types to get the output type of each property schema.
|
|
50
|
-
*/
|
|
51
|
-
export type ObjectOutput<TShape extends ObjectShape> = {
|
|
52
|
-
[K in keyof TShape]: TShape[K]['_outputType'];
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
export class ObjectSchema<TShape extends ObjectShape> extends BaseSchema<ObjectOutput<TShape>> {
|
|
56
|
-
_shape: TShape;
|
|
57
|
-
|
|
58
|
-
constructor(shape: TShape) {
|
|
59
|
-
super();
|
|
60
|
-
this._shape = shape;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
parse(data: any): ObjectOutput<TShape> {
|
|
64
|
-
if (typeof data !== 'object' || data === null) {
|
|
65
|
-
throw new Error(`Expected object, received ${typeof data}`);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
const result: any = {};
|
|
69
|
-
const errors: string[] = [];
|
|
70
|
-
|
|
71
|
-
// Validate defined properties
|
|
72
|
-
for (const key in this._shape) {
|
|
73
|
-
if (Object.prototype.hasOwnProperty.call(this._shape, key)) {
|
|
74
|
-
const schema = this._shape[key];
|
|
75
|
-
const value = (data as any)[key]; // Get value from input data
|
|
76
|
-
try {
|
|
77
|
-
if (schema) {
|
|
78
|
-
result[key] = schema.parse(value); // Recursively parse property
|
|
79
|
-
}
|
|
80
|
-
} catch (e: any) {
|
|
81
|
-
errors.push(`Property "${key}": ${e.message}`);
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// Basic handling for extra keys (can be made configurable)
|
|
87
|
-
for (const key in data) {
|
|
88
|
-
if (Object.prototype.hasOwnProperty.call(data, key) && !Object.prototype.hasOwnProperty.call(this._shape, key)) {
|
|
89
|
-
// For now, ignore extra keys. Could throw an error or strip them.
|
|
90
|
-
// errors.push(`Unknown property "${key}"`);
|
|
91
|
-
// console.warn(`Warning: Unknown property "${key}" in object data.`);
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
if (errors.length > 0) {
|
|
97
|
-
throw new Error(`Object validation failed:\n${errors.join('\n')}`);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
return result as ObjectOutput<TShape>;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Infers the TypeScript output type from an element schema for an array.
|
|
106
|
-
*/
|
|
107
|
-
export type ArrayOutput<TElementSchema extends BaseSchema<any>> = Array<TElementSchema['_outputType']>;
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
export class ArraySchema<TElementSchema extends BaseSchema<any>> extends BaseSchema<ArrayOutput<TElementSchema>> {
|
|
111
|
-
_element: TElementSchema;
|
|
112
|
-
|
|
113
|
-
constructor(elementSchema: TElementSchema) {
|
|
114
|
-
super();
|
|
115
|
-
this._element = elementSchema;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
parse(data: any): ArrayOutput<TElementSchema> {
|
|
119
|
-
if (!Array.isArray(data)) {
|
|
120
|
-
throw new Error(`Expected array, received ${typeof data}`);
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
const result: any[] = [];
|
|
124
|
-
const errors: string[] = [];
|
|
125
|
-
|
|
126
|
-
for (let i = 0; i < data.length; i++) {
|
|
127
|
-
try {
|
|
128
|
-
result[i] = this._element.parse(data[i]); // Recursively parse each element
|
|
129
|
-
} catch (e: any) {
|
|
130
|
-
errors.push(`Element at index ${i}: ${e.message}`);
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
if (errors.length > 0) {
|
|
135
|
-
throw new Error(`Array validation failed:\n${errors.join('\n')}`);
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
return result as ArrayOutput<TElementSchema>;
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
// Refine NuSchema union after defining concrete types
|
|
143
|
-
export type NuSchema =
|
|
144
|
-
| BooleanSchema
|
|
145
|
-
| StringSchema
|
|
146
|
-
| NumberSchema
|
|
147
|
-
// Add others like BooleanSchema
|
|
148
|
-
| ObjectSchema<any>
|
|
149
|
-
| ArraySchema<any>;
|