@forgehive/schema 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 ForgeHive
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,133 @@
1
+ import { z } from 'zod';
2
+ export type SchemaType = z.ZodType<string | boolean | number | Date | string[] | boolean[] | number[] | Date[] | Record<string, string | number | boolean>> | z.ZodOptional<z.ZodType<string | boolean | number | Date | string[] | boolean[] | number[] | Date[] | Record<string, string | number | boolean>>>;
3
+ type AllowedBaseTypes = 'string' | 'boolean' | 'number' | 'date' | 'stringRecord' | 'numberRecord' | 'booleanRecord' | 'mixedRecord';
4
+ type ArrayTypes = z.ZodString | z.ZodBoolean | z.ZodNumber | z.ZodDate;
5
+ type NumberValidations = {
6
+ min?: number;
7
+ max?: number;
8
+ };
9
+ type StringValidations = {
10
+ email?: boolean;
11
+ minLength?: number;
12
+ maxLength?: number;
13
+ regex?: string;
14
+ };
15
+ export type ShadowString = z.ZodString;
16
+ export type ShadowBoolean = z.ZodBoolean;
17
+ export type ShadowNumber = z.ZodNumber;
18
+ export type ShadowDate = z.ZodDate;
19
+ export type ShadowArray<T extends ArrayTypes> = z.ZodArray<T>;
20
+ export type ShadowStringRecord = z.ZodRecord<z.ZodString, z.ZodString>;
21
+ export type ShadowNumberRecord = z.ZodRecord<z.ZodString, z.ZodNumber>;
22
+ export type ShadowBooleanRecord = z.ZodRecord<z.ZodString, z.ZodBoolean>;
23
+ export type ShadowMixedRecord = z.ZodRecord<z.ZodString, z.ZodUnion<[z.ZodString, z.ZodNumber, z.ZodBoolean]>>;
24
+ export type InferShadowString = z.infer<ShadowString>;
25
+ export type InferShadowBoolean = z.infer<ShadowBoolean>;
26
+ export type InferShadowNumber = z.infer<ShadowNumber>;
27
+ export type InferShadowDate = z.infer<ShadowDate>;
28
+ export type InferShadowArray<T extends ArrayTypes> = z.infer<ShadowArray<T>>;
29
+ export type InferShadowStringRecord = z.infer<ShadowStringRecord>;
30
+ export type InferShadowNumberRecord = z.infer<ShadowNumberRecord>;
31
+ export type InferShadowBooleanRecord = z.infer<ShadowBooleanRecord>;
32
+ export type InferShadowMixedRecord = z.infer<ShadowMixedRecord>;
33
+ export type InferSchema<S extends Schema<Record<string, SchemaType>>> = z.infer<S['schema']>;
34
+ type BaseSchemaDescription = {
35
+ type: AllowedBaseTypes;
36
+ optional?: boolean;
37
+ validations?: NumberValidations | StringValidations;
38
+ };
39
+ type ArraySchemaDescription = {
40
+ type: 'array';
41
+ items: {
42
+ type: AllowedBaseTypes;
43
+ };
44
+ optional?: boolean;
45
+ };
46
+ export type SchemaDescription = Record<string, BaseSchemaDescription | ArraySchemaDescription>;
47
+ export declare class Schema<T extends Record<string, z.ZodType<string | boolean | number | Date | string[] | boolean[] | number[] | Date[] | Record<string, string | number | boolean>> | z.ZodOptional<z.ZodType<string | boolean | number | Date | string[] | boolean[] | number[] | Date[] | Record<string, string | number | boolean>>>>> {
48
+ readonly schema: z.ZodObject<T>;
49
+ constructor(fields: T);
50
+ /**
51
+ * Creates a string schema
52
+ * @returns A string schema
53
+ */
54
+ static string(): ShadowString;
55
+ /**
56
+ * Creates a boolean schema
57
+ * @returns A boolean schema
58
+ */
59
+ static boolean(): ShadowBoolean;
60
+ /**
61
+ * Creates a number schema
62
+ * @returns A number schema
63
+ */
64
+ static number(): ShadowNumber;
65
+ /**
66
+ * Creates a date schema
67
+ * @returns A date schema
68
+ */
69
+ static date(): ShadowDate;
70
+ /**
71
+ * Creates a record schema with string keys and string values
72
+ * @returns A record schema with string values
73
+ */
74
+ static stringRecord(): ShadowStringRecord;
75
+ /**
76
+ * Creates a record schema with string keys and number values
77
+ * @returns A record schema with number values
78
+ */
79
+ static numberRecord(): ShadowNumberRecord;
80
+ /**
81
+ * Creates a record schema with string keys and boolean values
82
+ * @returns A record schema with boolean values
83
+ */
84
+ static booleanRecord(): ShadowBooleanRecord;
85
+ /**
86
+ * Creates a record schema with string keys and mixed values (string, number, or boolean)
87
+ * @returns A record schema with mixed values
88
+ */
89
+ static mixedRecord(): ShadowMixedRecord;
90
+ /**
91
+ * Creates an array schema
92
+ * @param type The type of items in the array
93
+ * @returns An array schema
94
+ */
95
+ static array<T extends ArrayTypes>(type: T): ShadowArray<T>;
96
+ /**
97
+ * Infers the TypeScript type from a Schema instance
98
+ * @template S The Schema type
99
+ * @returns The inferred TypeScript type
100
+ */
101
+ static infer<S extends Schema<Record<string, z.ZodTypeAny>>>(_schema: S): z.infer<S['schema']>;
102
+ /**
103
+ * Creates a Schema instance from a description object
104
+ * @param description Object describing the schema structure with type information
105
+ * @returns A new Schema instance
106
+ */
107
+ static from(description: SchemaDescription): Schema<Record<string, z.ZodType<string | boolean | number | Date | string[] | boolean[] | number[] | Date[] | Record<string, string | number | boolean>> | z.ZodOptional<z.ZodType<string | boolean | number | Date | string[] | boolean[] | number[] | Date[] | Record<string, string | number | boolean>>>>>;
108
+ /**
109
+ * Validates the provided data against the schema
110
+ * @param data The data to validate
111
+ * @returns A boolean indicating whether the data is valid
112
+ */
113
+ validate(data: unknown): boolean;
114
+ /**
115
+ * Parses and validates the provided data against the schema
116
+ * @param data The data to parse and validate
117
+ * @returns The parsed and typed data
118
+ * @throws {z.ZodError} If the data is invalid
119
+ */
120
+ parse(data: unknown): z.infer<z.ZodObject<T>>;
121
+ /**
122
+ * Safely parses and validates the provided data against the schema
123
+ * @param data The data to parse and validate
124
+ * @returns An object containing either the successfully parsed data or error information
125
+ */
126
+ safeParse(data: unknown): z.SafeParseReturnType<z.infer<z.ZodObject<T>>, z.infer<z.ZodObject<T>>>;
127
+ /**
128
+ * Describes the schema structure and allowed types
129
+ * @returns An object describing the schema structure with type information
130
+ */
131
+ describe(): SchemaDescription;
132
+ }
133
+ export default Schema;
package/dist/index.js ADDED
@@ -0,0 +1,285 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Schema = void 0;
4
+ const zod_1 = require("zod");
5
+ // Static methods for type definitions
6
+ class Schema {
7
+ constructor(fields) {
8
+ this.schema = zod_1.z.object(fields);
9
+ }
10
+ /**
11
+ * Creates a string schema
12
+ * @returns A string schema
13
+ */
14
+ static string() {
15
+ return zod_1.z.string();
16
+ }
17
+ /**
18
+ * Creates a boolean schema
19
+ * @returns A boolean schema
20
+ */
21
+ static boolean() {
22
+ return zod_1.z.boolean();
23
+ }
24
+ /**
25
+ * Creates a number schema
26
+ * @returns A number schema
27
+ */
28
+ static number() {
29
+ return zod_1.z.number();
30
+ }
31
+ /**
32
+ * Creates a date schema
33
+ * @returns A date schema
34
+ */
35
+ static date() {
36
+ return zod_1.z.date();
37
+ }
38
+ /**
39
+ * Creates a record schema with string keys and string values
40
+ * @returns A record schema with string values
41
+ */
42
+ static stringRecord() {
43
+ return zod_1.z.record(zod_1.z.string(), zod_1.z.string());
44
+ }
45
+ /**
46
+ * Creates a record schema with string keys and number values
47
+ * @returns A record schema with number values
48
+ */
49
+ static numberRecord() {
50
+ return zod_1.z.record(zod_1.z.string(), zod_1.z.number());
51
+ }
52
+ /**
53
+ * Creates a record schema with string keys and boolean values
54
+ * @returns A record schema with boolean values
55
+ */
56
+ static booleanRecord() {
57
+ return zod_1.z.record(zod_1.z.string(), zod_1.z.boolean());
58
+ }
59
+ /**
60
+ * Creates a record schema with string keys and mixed values (string, number, or boolean)
61
+ * @returns A record schema with mixed values
62
+ */
63
+ static mixedRecord() {
64
+ return zod_1.z.record(zod_1.z.string(), zod_1.z.union([zod_1.z.string(), zod_1.z.number(), zod_1.z.boolean()]));
65
+ }
66
+ /**
67
+ * Creates an array schema
68
+ * @param type The type of items in the array
69
+ * @returns An array schema
70
+ */
71
+ static array(type) {
72
+ return zod_1.z.array(type);
73
+ }
74
+ /**
75
+ * Infers the TypeScript type from a Schema instance
76
+ * @template S The Schema type
77
+ * @returns The inferred TypeScript type
78
+ */
79
+ static infer(_schema) {
80
+ // This is a type-level utility, the implementation is not used at runtime
81
+ return {};
82
+ }
83
+ /**
84
+ * Creates a Schema instance from a description object
85
+ * @param description Object describing the schema structure with type information
86
+ * @returns A new Schema instance
87
+ */
88
+ static from(description) {
89
+ const fields = {};
90
+ for (const [key, field] of Object.entries(description)) {
91
+ const fieldType = field.type;
92
+ let fieldSchema;
93
+ switch (fieldType) {
94
+ case 'string': {
95
+ let stringSchema = Schema.string();
96
+ if (field.validations) {
97
+ const validations = field.validations;
98
+ if (validations.email) {
99
+ stringSchema = stringSchema.email();
100
+ }
101
+ if (validations.minLength !== undefined) {
102
+ stringSchema = stringSchema.min(validations.minLength);
103
+ }
104
+ if (validations.maxLength !== undefined) {
105
+ stringSchema = stringSchema.max(validations.maxLength);
106
+ }
107
+ if (validations.regex !== undefined) {
108
+ stringSchema = stringSchema.regex(new RegExp(validations.regex));
109
+ }
110
+ }
111
+ fieldSchema = stringSchema;
112
+ break;
113
+ }
114
+ case 'boolean':
115
+ fieldSchema = Schema.boolean();
116
+ break;
117
+ case 'number': {
118
+ let numberSchema = Schema.number();
119
+ if (field.validations) {
120
+ const validations = field.validations;
121
+ if (validations.min !== undefined) {
122
+ numberSchema = numberSchema.min(validations.min);
123
+ }
124
+ if (validations.max !== undefined) {
125
+ numberSchema = numberSchema.max(validations.max);
126
+ }
127
+ }
128
+ fieldSchema = numberSchema;
129
+ break;
130
+ }
131
+ case 'date':
132
+ fieldSchema = Schema.date();
133
+ break;
134
+ case 'stringRecord':
135
+ fieldSchema = Schema.stringRecord();
136
+ break;
137
+ case 'numberRecord':
138
+ fieldSchema = Schema.numberRecord();
139
+ break;
140
+ case 'booleanRecord':
141
+ fieldSchema = Schema.booleanRecord();
142
+ break;
143
+ case 'mixedRecord':
144
+ fieldSchema = Schema.mixedRecord();
145
+ break;
146
+ case 'array': {
147
+ const arrayField = field;
148
+ switch (arrayField.items.type) {
149
+ case 'string':
150
+ fieldSchema = Schema.array(Schema.string());
151
+ break;
152
+ case 'boolean':
153
+ fieldSchema = Schema.array(Schema.boolean());
154
+ break;
155
+ case 'number':
156
+ fieldSchema = Schema.array(Schema.number());
157
+ break;
158
+ case 'date':
159
+ fieldSchema = Schema.array(Schema.date());
160
+ break;
161
+ default:
162
+ throw new Error(`Unsupported array item type: ${arrayField.items.type}`);
163
+ }
164
+ break;
165
+ }
166
+ default:
167
+ throw new Error(`Unsupported type: ${fieldType}`);
168
+ }
169
+ fields[key] = field.optional ? fieldSchema.optional() : fieldSchema;
170
+ }
171
+ return new Schema(fields);
172
+ }
173
+ /**
174
+ * Validates the provided data against the schema
175
+ * @param data The data to validate
176
+ * @returns A boolean indicating whether the data is valid
177
+ */
178
+ validate(data) {
179
+ const result = this.schema.safeParse(data);
180
+ return result.success;
181
+ }
182
+ /**
183
+ * Parses and validates the provided data against the schema
184
+ * @param data The data to parse and validate
185
+ * @returns The parsed and typed data
186
+ * @throws {z.ZodError} If the data is invalid
187
+ */
188
+ parse(data) {
189
+ return this.schema.parse(data);
190
+ }
191
+ /**
192
+ * Safely parses and validates the provided data against the schema
193
+ * @param data The data to parse and validate
194
+ * @returns An object containing either the successfully parsed data or error information
195
+ */
196
+ safeParse(data) {
197
+ return this.schema.safeParse(data);
198
+ }
199
+ /**
200
+ * Describes the schema structure and allowed types
201
+ * @returns An object describing the schema structure with type information
202
+ */
203
+ describe() {
204
+ const shape = this.schema.shape;
205
+ const description = {};
206
+ for (const [key, value] of Object.entries(shape)) {
207
+ const isOptional = value instanceof zod_1.z.ZodOptional;
208
+ const baseValue = isOptional ? value.unwrap() : value;
209
+ if (baseValue instanceof zod_1.z.ZodString) {
210
+ const validations = {};
211
+ if (baseValue._def.checks) {
212
+ for (const check of baseValue._def.checks) {
213
+ if (check.kind === 'email') {
214
+ validations.email = true;
215
+ }
216
+ else if (check.kind === 'min') {
217
+ validations.minLength = check.value;
218
+ }
219
+ else if (check.kind === 'max') {
220
+ validations.maxLength = check.value;
221
+ }
222
+ else if (check.kind === 'regex') {
223
+ validations.regex = check.regex.toString().replace(/^\/|\/$/g, '').replace(/\\\//g, '/');
224
+ }
225
+ }
226
+ }
227
+ description[key] = Object.assign(Object.assign({ type: 'string' }, (isOptional && { optional: true })), (Object.keys(validations).length > 0 && { validations }));
228
+ }
229
+ else if (baseValue instanceof zod_1.z.ZodBoolean) {
230
+ description[key] = Object.assign({ type: 'boolean' }, (isOptional && { optional: true }));
231
+ }
232
+ else if (baseValue instanceof zod_1.z.ZodNumber) {
233
+ const validations = {};
234
+ if (baseValue._def.checks) {
235
+ for (const check of baseValue._def.checks) {
236
+ if (check.kind === 'min') {
237
+ validations.min = check.value;
238
+ }
239
+ else if (check.kind === 'max') {
240
+ validations.max = check.value;
241
+ }
242
+ }
243
+ }
244
+ description[key] = Object.assign(Object.assign({ type: 'number' }, (isOptional && { optional: true })), (Object.keys(validations).length > 0 && { validations }));
245
+ }
246
+ else if (baseValue instanceof zod_1.z.ZodDate) {
247
+ description[key] = Object.assign({ type: 'date' }, (isOptional && { optional: true }));
248
+ }
249
+ else if (baseValue instanceof zod_1.z.ZodArray) {
250
+ const element = baseValue.element;
251
+ if (element instanceof zod_1.z.ZodString) {
252
+ description[key] = Object.assign({ type: 'array', items: { type: 'string' } }, (isOptional && { optional: true }));
253
+ }
254
+ else if (element instanceof zod_1.z.ZodBoolean) {
255
+ description[key] = Object.assign({ type: 'array', items: { type: 'boolean' } }, (isOptional && { optional: true }));
256
+ }
257
+ else if (element instanceof zod_1.z.ZodNumber) {
258
+ description[key] = Object.assign({ type: 'array', items: { type: 'number' } }, (isOptional && { optional: true }));
259
+ }
260
+ else if (element instanceof zod_1.z.ZodDate) {
261
+ description[key] = Object.assign({ type: 'array', items: { type: 'date' } }, (isOptional && { optional: true }));
262
+ }
263
+ }
264
+ else if (baseValue instanceof zod_1.z.ZodRecord) {
265
+ // Check the value type of the record
266
+ const valueType = baseValue._def.valueType;
267
+ if (valueType instanceof zod_1.z.ZodString) {
268
+ description[key] = Object.assign({ type: 'stringRecord' }, (isOptional && { optional: true }));
269
+ }
270
+ else if (valueType instanceof zod_1.z.ZodNumber) {
271
+ description[key] = Object.assign({ type: 'numberRecord' }, (isOptional && { optional: true }));
272
+ }
273
+ else if (valueType instanceof zod_1.z.ZodBoolean) {
274
+ description[key] = Object.assign({ type: 'booleanRecord' }, (isOptional && { optional: true }));
275
+ }
276
+ else if (valueType instanceof zod_1.z.ZodUnion) {
277
+ description[key] = Object.assign({ type: 'mixedRecord' }, (isOptional && { optional: true }));
278
+ }
279
+ }
280
+ }
281
+ return description;
282
+ }
283
+ }
284
+ exports.Schema = Schema;
285
+ exports.default = Schema;
@@ -0,0 +1 @@
1
+ export {};