valdex 1.0.2 → 1.0.3

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 CHANGED
@@ -1,6 +1,30 @@
1
1
  # valdex
2
2
 
3
- Runtime type validation with TypeScript type inference.
3
+ **Runtime type validation with TypeScript type inference**
4
+
5
+ Validate unknown data at runtime and get automatic TypeScript type narrowing—without separate schema objects or class instances.
6
+
7
+ ## Why valdex?
8
+
9
+ Runtime validation libraries typically require you to define schemas separately and instantiate them before use. This creates distance between where you validate and where you consume the data.
10
+
11
+ Valdex takes a different approach: validate inline, exactly where you need it. No jumping between schema definitions and usage points. No maintaining separate DTO classes or validator instances.
12
+
13
+ ```typescript
14
+ // Traditional approach - schema defined elsewhere
15
+ const userSchema = z.object({ name: z.string(), age: z.number() });
16
+ const user = userSchema.parse(data);
17
+
18
+ // valdex - validate at point of use
19
+ validate(data, { name: String, age: Number });
20
+ // data is now typed as { name: string, age: number }
21
+ ```
22
+
23
+ This is particularly useful when working with:
24
+ - Database query results (mysql2, pg, etc.)
25
+ - External API responses (axios, fetch)
26
+ - Message queue payloads
27
+ - Any `unknown` or `any` typed data that needs runtime verification
4
28
 
5
29
  ## Installation
6
30
 
@@ -10,11 +34,11 @@ npm install valdex
10
34
 
11
35
  ## Features
12
36
 
13
- - Runtime type validation for unknown data
14
- - Automatic TypeScript type inference from schema
15
- - Support for nested objects and arrays
16
- - Optional and nullable field modifiers
17
- - Zero dependencies
37
+ - **Zero dependencies**: No external dependencies
38
+ - **Type inference**: Automatic TypeScript type narrowing after validation
39
+ - **Inline validation**: Validate where you use, not where you define
40
+ - **Nested structures**: Full support for nested objects and arrays
41
+ - **Optional/Nullable**: Flexible handling of optional and nullable fields
18
42
 
19
43
  ## Usage
20
44
 
@@ -26,9 +50,9 @@ import { validate } from 'valdex';
26
50
  const data: unknown = await fetchData();
27
51
 
28
52
  validate(data, {
29
- name: String,
30
- age: Number,
31
- active: Boolean
53
+ name: String,
54
+ age: Number,
55
+ active: Boolean
32
56
  });
33
57
 
34
58
  // TypeScript now knows the exact type of data
@@ -41,13 +65,13 @@ data.active // boolean
41
65
 
42
66
  ```typescript
43
67
  validate(data, {
44
- user: {
45
- id: Number,
46
- profile: {
47
- name: String,
48
- email: String
49
- }
68
+ user: {
69
+ id: Number,
70
+ profile: {
71
+ name: String,
72
+ email: String
50
73
  }
74
+ }
51
75
  });
52
76
 
53
77
  data.user.profile.name // string
@@ -57,11 +81,11 @@ data.user.profile.name // string
57
81
 
58
82
  ```typescript
59
83
  validate(data, {
60
- tags: [String], // string[]
61
- items: [{ // { id: number, name: string }[]
62
- id: Number,
63
- name: String
64
- }]
84
+ tags: [String], // string[]
85
+ items: [{ // { id: number, name: string }[]
86
+ id: Number,
87
+ name: String
88
+ }]
65
89
  });
66
90
 
67
91
  data.tags[0] // string
@@ -76,12 +100,12 @@ Use `Optional()` to allow `undefined` values:
76
100
  import { validate, Optional } from 'valdex';
77
101
 
78
102
  validate(data, {
79
- required: String,
80
- optional: Optional(String), // string | undefined
81
- optionalObject: Optional({ // { id: number } | undefined
82
- id: Number
83
- }),
84
- optionalArray: Optional([Number]) // number[] | undefined
103
+ required: String,
104
+ optional: Optional(String), // string | undefined
105
+ optionalObject: Optional({ // { id: number } | undefined
106
+ id: Number
107
+ }),
108
+ optionalArray: Optional([Number]) // number[] | undefined
85
109
  });
86
110
  ```
87
111
 
@@ -93,11 +117,11 @@ Use `Nullable()` to allow `null` values:
93
117
  import { validate, Nullable } from 'valdex';
94
118
 
95
119
  validate(data, {
96
- required: String,
97
- nullable: Nullable(String), // string | null
98
- nullableObject: Nullable({ // { id: number } | null
99
- id: Number
100
- })
120
+ required: String,
121
+ nullable: Nullable(String), // string | null
122
+ nullableObject: Nullable({ // { id: number } | null
123
+ id: Number
124
+ })
101
125
  });
102
126
  ```
103
127
 
@@ -107,7 +131,7 @@ validate(data, {
107
131
  import { validate, Optional, Nullable } from 'valdex';
108
132
 
109
133
  validate(data, {
110
- field: Optional(Nullable(String)) // string | undefined | null
134
+ field: Optional(Nullable(String)) // string | undefined | null
111
135
  });
112
136
  ```
113
137
 
@@ -130,12 +154,12 @@ When validation fails, a `RuntimeTypeError` is thrown:
130
154
  import { validate, RuntimeTypeError } from 'valdex';
131
155
 
132
156
  try {
133
- validate(data, { count: Number });
157
+ validate(data, { count: Number });
134
158
  } catch (error) {
135
- if (error instanceof RuntimeTypeError) {
136
- console.error(error.message);
137
- // "count must be Number, but got String. Actual value: hello"
138
- }
159
+ if (error instanceof RuntimeTypeError) {
160
+ console.error(error.message);
161
+ // "count must be Number, but got String. Actual value: hello"
162
+ }
139
163
  }
140
164
  ```
141
165
 
package/dist/index.d.mts CHANGED
@@ -4,19 +4,20 @@ type OptionalMarker = {
4
4
  type NullableMarker = {
5
5
  __nullable: true;
6
6
  };
7
+ type MarkerKeys = '__optional' | '__nullable' | '__type';
7
8
  type ArraySchema = [PrimitiveType | Expression];
8
9
  type Expression = {
9
- [key: string | number]: PrimitiveType | (PrimitiveType & OptionalMarker) | (PrimitiveType & NullableMarker) | (PrimitiveType & OptionalMarker & NullableMarker) | Expression | (Expression & OptionalMarker) | (Expression & NullableMarker) | (Expression & OptionalMarker & NullableMarker) | ArraySchema | (ArraySchema & OptionalMarker) | (ArraySchema & NullableMarker) | (ArraySchema & OptionalMarker & NullableMarker);
10
+ [key: string | number]: PrimitiveType | Expression | ArraySchema | (PrimitiveType & OptionalMarker) | (PrimitiveType & NullableMarker) | (PrimitiveType & OptionalMarker & NullableMarker) | (Expression & OptionalMarker) | (Expression & NullableMarker) | (Expression & OptionalMarker & NullableMarker) | (ArraySchema & OptionalMarker) | (ArraySchema & NullableMarker) | (ArraySchema & OptionalMarker & NullableMarker) | true;
10
11
  };
11
12
  type PrimitiveType = NumberConstructor | StringConstructor | BooleanConstructor | ArrayConstructor | ObjectConstructor | DateConstructor;
12
13
  type InferPrimitiveType<T> = T extends NumberConstructor ? number : T extends StringConstructor ? string : T extends BooleanConstructor ? boolean : T extends ArrayConstructor ? any[] : T extends ObjectConstructor ? object : T extends DateConstructor ? Date : never;
13
- type InferType<T> = T extends PrimitiveType & OptionalMarker & NullableMarker ? InferPrimitiveType<T> | undefined | null : T extends PrimitiveType & OptionalMarker ? InferPrimitiveType<T> | undefined : T extends PrimitiveType & NullableMarker ? InferPrimitiveType<T> | null : T extends PrimitiveType ? InferPrimitiveType<T> : T extends Expression & OptionalMarker & NullableMarker ? {
14
- [K in keyof T]: InferType<T[K]>;
15
- } | undefined | null : T extends Expression & OptionalMarker ? {
16
- [K in keyof T]: InferType<T[K]>;
17
- } | undefined : T extends Expression & NullableMarker ? {
18
- [K in keyof T]: InferType<T[K]>;
19
- } | null : T extends ArraySchema & OptionalMarker & NullableMarker ? (T extends [infer U] ? (U extends Expression ? InferType<U>[] : U extends PrimitiveType ? InferPrimitiveType<U>[] : never) : never) | undefined | null : T extends ArraySchema & OptionalMarker ? (T extends [infer U] ? (U extends Expression ? InferType<U>[] : U extends PrimitiveType ? InferPrimitiveType<U>[] : never) : never) | undefined : T extends ArraySchema & NullableMarker ? (T extends [infer U] ? (U extends Expression ? InferType<U>[] : U extends PrimitiveType ? InferPrimitiveType<U>[] : never) : never) | null : T extends [infer U] ? U extends Expression ? InferType<U>[] : U extends PrimitiveType ? InferPrimitiveType<U>[] : never : T extends Expression ? {
14
+ type InferType<T> = T extends PrimitiveType & OptionalMarker & NullableMarker ? InferPrimitiveType<T> | undefined | null : T extends PrimitiveType & OptionalMarker ? InferPrimitiveType<T> | undefined : T extends PrimitiveType & NullableMarker ? InferPrimitiveType<T> | null : T extends PrimitiveType ? InferPrimitiveType<T> : T extends ArraySchema & OptionalMarker & NullableMarker ? (T extends [infer U] ? (U extends Expression ? InferType<U>[] : U extends PrimitiveType ? InferPrimitiveType<U>[] : never) : never) | undefined | null : T extends ArraySchema & OptionalMarker ? (T extends [infer U] ? (U extends Expression ? InferType<U>[] : U extends PrimitiveType ? InferPrimitiveType<U>[] : never) : never) | undefined : T extends ArraySchema & NullableMarker ? (T extends [infer U] ? (U extends Expression ? InferType<U>[] : U extends PrimitiveType ? InferPrimitiveType<U>[] : never) : never) | null : T extends [infer U] ? U extends Expression ? InferType<U>[] : U extends PrimitiveType ? InferPrimitiveType<U>[] : never : T extends OptionalMarker & NullableMarker ? {
15
+ [K in keyof T as K extends MarkerKeys ? never : K]: InferType<T[K]>;
16
+ } | undefined | null : T extends OptionalMarker ? {
17
+ [K in keyof T as K extends MarkerKeys ? never : K]: InferType<T[K]>;
18
+ } | undefined : T extends NullableMarker ? {
19
+ [K in keyof T as K extends MarkerKeys ? never : K]: InferType<T[K]>;
20
+ } | null : T extends Expression ? {
20
21
  [K in keyof T]: InferType<T[K]>;
21
22
  } : never;
22
23
  /**
package/dist/index.d.ts CHANGED
@@ -4,19 +4,20 @@ type OptionalMarker = {
4
4
  type NullableMarker = {
5
5
  __nullable: true;
6
6
  };
7
+ type MarkerKeys = '__optional' | '__nullable' | '__type';
7
8
  type ArraySchema = [PrimitiveType | Expression];
8
9
  type Expression = {
9
- [key: string | number]: PrimitiveType | (PrimitiveType & OptionalMarker) | (PrimitiveType & NullableMarker) | (PrimitiveType & OptionalMarker & NullableMarker) | Expression | (Expression & OptionalMarker) | (Expression & NullableMarker) | (Expression & OptionalMarker & NullableMarker) | ArraySchema | (ArraySchema & OptionalMarker) | (ArraySchema & NullableMarker) | (ArraySchema & OptionalMarker & NullableMarker);
10
+ [key: string | number]: PrimitiveType | Expression | ArraySchema | (PrimitiveType & OptionalMarker) | (PrimitiveType & NullableMarker) | (PrimitiveType & OptionalMarker & NullableMarker) | (Expression & OptionalMarker) | (Expression & NullableMarker) | (Expression & OptionalMarker & NullableMarker) | (ArraySchema & OptionalMarker) | (ArraySchema & NullableMarker) | (ArraySchema & OptionalMarker & NullableMarker) | true;
10
11
  };
11
12
  type PrimitiveType = NumberConstructor | StringConstructor | BooleanConstructor | ArrayConstructor | ObjectConstructor | DateConstructor;
12
13
  type InferPrimitiveType<T> = T extends NumberConstructor ? number : T extends StringConstructor ? string : T extends BooleanConstructor ? boolean : T extends ArrayConstructor ? any[] : T extends ObjectConstructor ? object : T extends DateConstructor ? Date : never;
13
- type InferType<T> = T extends PrimitiveType & OptionalMarker & NullableMarker ? InferPrimitiveType<T> | undefined | null : T extends PrimitiveType & OptionalMarker ? InferPrimitiveType<T> | undefined : T extends PrimitiveType & NullableMarker ? InferPrimitiveType<T> | null : T extends PrimitiveType ? InferPrimitiveType<T> : T extends Expression & OptionalMarker & NullableMarker ? {
14
- [K in keyof T]: InferType<T[K]>;
15
- } | undefined | null : T extends Expression & OptionalMarker ? {
16
- [K in keyof T]: InferType<T[K]>;
17
- } | undefined : T extends Expression & NullableMarker ? {
18
- [K in keyof T]: InferType<T[K]>;
19
- } | null : T extends ArraySchema & OptionalMarker & NullableMarker ? (T extends [infer U] ? (U extends Expression ? InferType<U>[] : U extends PrimitiveType ? InferPrimitiveType<U>[] : never) : never) | undefined | null : T extends ArraySchema & OptionalMarker ? (T extends [infer U] ? (U extends Expression ? InferType<U>[] : U extends PrimitiveType ? InferPrimitiveType<U>[] : never) : never) | undefined : T extends ArraySchema & NullableMarker ? (T extends [infer U] ? (U extends Expression ? InferType<U>[] : U extends PrimitiveType ? InferPrimitiveType<U>[] : never) : never) | null : T extends [infer U] ? U extends Expression ? InferType<U>[] : U extends PrimitiveType ? InferPrimitiveType<U>[] : never : T extends Expression ? {
14
+ type InferType<T> = T extends PrimitiveType & OptionalMarker & NullableMarker ? InferPrimitiveType<T> | undefined | null : T extends PrimitiveType & OptionalMarker ? InferPrimitiveType<T> | undefined : T extends PrimitiveType & NullableMarker ? InferPrimitiveType<T> | null : T extends PrimitiveType ? InferPrimitiveType<T> : T extends ArraySchema & OptionalMarker & NullableMarker ? (T extends [infer U] ? (U extends Expression ? InferType<U>[] : U extends PrimitiveType ? InferPrimitiveType<U>[] : never) : never) | undefined | null : T extends ArraySchema & OptionalMarker ? (T extends [infer U] ? (U extends Expression ? InferType<U>[] : U extends PrimitiveType ? InferPrimitiveType<U>[] : never) : never) | undefined : T extends ArraySchema & NullableMarker ? (T extends [infer U] ? (U extends Expression ? InferType<U>[] : U extends PrimitiveType ? InferPrimitiveType<U>[] : never) : never) | null : T extends [infer U] ? U extends Expression ? InferType<U>[] : U extends PrimitiveType ? InferPrimitiveType<U>[] : never : T extends OptionalMarker & NullableMarker ? {
15
+ [K in keyof T as K extends MarkerKeys ? never : K]: InferType<T[K]>;
16
+ } | undefined | null : T extends OptionalMarker ? {
17
+ [K in keyof T as K extends MarkerKeys ? never : K]: InferType<T[K]>;
18
+ } | undefined : T extends NullableMarker ? {
19
+ [K in keyof T as K extends MarkerKeys ? never : K]: InferType<T[K]>;
20
+ } | null : T extends Expression ? {
20
21
  [K in keyof T]: InferType<T[K]>;
21
22
  } : never;
22
23
  /**
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["type OptionalMarker = { __optional: true };\ntype NullableMarker = { __nullable: true };\n\ntype ArraySchema = [PrimitiveType | Expression];\n\ntype Expression = {\n [key: string | number]: PrimitiveType | (PrimitiveType & OptionalMarker) | (PrimitiveType & NullableMarker) | (PrimitiveType & OptionalMarker & NullableMarker) | Expression | (Expression & OptionalMarker) | (Expression & NullableMarker) | (Expression & OptionalMarker & NullableMarker) | ArraySchema | (ArraySchema & OptionalMarker) | (ArraySchema & NullableMarker) | (ArraySchema & OptionalMarker & NullableMarker)\n}\n\ntype PrimitiveType = NumberConstructor | StringConstructor | BooleanConstructor | ArrayConstructor | ObjectConstructor | DateConstructor;\n\ntype InferPrimitiveType<T> =\n T extends NumberConstructor ? number :\n T extends StringConstructor ? string :\n T extends BooleanConstructor ? boolean :\n T extends ArrayConstructor ? any[] :\n T extends ObjectConstructor ? object :\n T extends DateConstructor ? Date :\n never;\n\ntype InferType<T> =\n T extends PrimitiveType & OptionalMarker & NullableMarker\n ? InferPrimitiveType<T> | undefined | null\n : T extends PrimitiveType & OptionalMarker\n ? InferPrimitiveType<T> | undefined\n : T extends PrimitiveType & NullableMarker\n ? InferPrimitiveType<T> | null\n : T extends PrimitiveType\n ? InferPrimitiveType<T>\n : T extends Expression & OptionalMarker & NullableMarker\n ? { [K in keyof T]: InferType<T[K]> } | undefined | null\n : T extends Expression & OptionalMarker\n ? { [K in keyof T]: InferType<T[K]> } | undefined\n : T extends Expression & NullableMarker\n ? { [K in keyof T]: InferType<T[K]> } | null\n : T extends ArraySchema & OptionalMarker & NullableMarker\n ? (T extends [infer U] ? (U extends Expression ? InferType<U>[] : U extends PrimitiveType ? InferPrimitiveType<U>[] : never) : never) | undefined | null\n : T extends ArraySchema & OptionalMarker\n ? (T extends [infer U] ? (U extends Expression ? InferType<U>[] : U extends PrimitiveType ? InferPrimitiveType<U>[] : never) : never) | undefined\n : T extends ArraySchema & NullableMarker\n ? (T extends [infer U] ? (U extends Expression ? InferType<U>[] : U extends PrimitiveType ? InferPrimitiveType<U>[] : never) : never) | null\n : T extends [infer U]\n ? U extends Expression\n ? InferType<U>[]\n : U extends PrimitiveType\n ? InferPrimitiveType<U>[]\n : never\n : T extends Expression\n ? { [K in keyof T]: InferType<T[K]> }\n : never;\n\n/**\n * Marks a field as optional.\n * Optional fields pass validation even if they are undefined.\n * The type is inferred as T | undefined.\n * @example\n * ```ts\n * validate(data, {\n * required_field: String, // string (required)\n * optional_field: Optional(String), // string | undefined\n * optional_object: Optional({ // { id: string } | undefined\n * id: String\n * }),\n * optional_array: Optional([String]) // string[] | undefined\n * });\n * ```\n */\nexport function Optional<T extends PrimitiveType | Expression | ArraySchema>(ctor: T): T & OptionalMarker {\n return {\n ...ctor as any,\n __optional: true,\n __type: ctor\n } as T & OptionalMarker;\n}\n\n/**\n * Marks a field as nullable.\n * Nullable fields pass validation even if they are null.\n * The type is inferred as T | null.\n * @example\n * ```ts\n * validate(data, {\n * required_field: String, // string (required)\n * nullable_field: Nullable(String), // string | null\n * nullable_object: Nullable({ // { id: string } | null\n * id: String\n * }),\n * nullable_array: Nullable([String]) // string[] | null\n * });\n * ```\n */\nexport function Nullable<T extends PrimitiveType | Expression | ArraySchema>(ctor: T): T & NullableMarker {\n return {\n ...ctor as any,\n __nullable: true,\n __type: ctor\n } as T & NullableMarker;\n}\n\nexport class RuntimeTypeError extends Error {\n constructor(\n key: string,\n requiredType: string,\n actualType: string,\n actualValue: any,\n message?: string\n ) {\n if (message) message += \": \"\n super(`${message || \"\"}${key} must be ${requiredType}, but got ${actualType}. Actual value: ${actualValue}`);\n }\n}\n\nfunction getTypeName(value: any): string {\n if (value === null) return 'null';\n if (value === undefined) return 'undefined';\n if (Array.isArray(value)) return 'Array';\n if (typeof value === 'number' && Number.isNaN(value)) return 'NaN';\n if (typeof value === 'object') return 'Object';\n\n const type = typeof value;\n return type.charAt(0).toUpperCase() + type.slice(1);\n}\n\nconst TYPE_VALIDATORS = new Map<Function, (value: any) => boolean>([\n [Number, (value) => typeof value === 'number' && !Number.isNaN(value)],\n [String, (value) => typeof value === 'string'],\n [Boolean, (value) => typeof value === 'boolean'],\n [Array, (value) => Array.isArray(value)],\n [Object, (value) => typeof value === 'object' && value !== null && !Array.isArray(value) && !(value instanceof Date)],\n [Date, (value) => value instanceof Date && !Number.isNaN(value.getTime())]\n]);\n\nfunction isValidateExpression(ctor: any): ctor is Expression {\n return typeof ctor === 'object' && !Array.isArray(ctor);\n}\n\nfunction isArraySchema(ctor: any): ctor is [PrimitiveType | Expression] {\n return Array.isArray(ctor) && ctor.length === 1;\n}\n\nfunction isOptional(ctor: any): boolean {\n return ctor.__optional === true;\n}\n\nfunction isNullable(ctor: any): boolean {\n return ctor.__nullable === true;\n}\n\nfunction isBothOptionalAndNullable(ctor: any): boolean {\n return ctor.__optional === true && ctor.__nullable === true;\n}\n\nfunction unwrapOptionalNullable(ctor: any): any {\n while (ctor && typeof ctor === 'object' && !Array.isArray(ctor) && ctor.__type) {\n ctor = ctor.__type;\n }\n return ctor;\n}\n\nfunction assertType(key: string, expectedType: string, value: any, isValid: boolean): void {\n if (!isValid) {\n throw new RuntimeTypeError(key, expectedType, getTypeName(value), value);\n }\n}\n\nfunction validateArrayElement(item: any, schema: any, path: string): void {\n if (isArraySchema(schema)) {\n assertType(path, \"Array\", item, Array.isArray(item));\n const innerSchema = schema[0];\n const arr = item as any[];\n for (let i = 0; i < arr.length; i++) {\n validateArrayElement(arr[i], innerSchema, `${path}[${i}]`);\n }\n } else if (isValidateExpression(schema)) {\n const objectValidator = TYPE_VALIDATORS.get(Object);\n assertType(path, \"Object\", item, objectValidator?.(item) ?? false);\n validate(item, schema, path);\n } else if (typeof schema === 'function') {\n const validator = TYPE_VALIDATORS.get(schema);\n if (!validator) {\n throw new Error(\"Invalid array element expression.\");\n }\n assertType(path, schema.name, item, validator(item));\n } else {\n throw new Error(\"Invalid array element expression.\");\n }\n}\n\n/**\n * Asserts the type of target according to the expression schema.\n * The expression should be represented using primitive type constructors.\n * Supports nested types and array element type validation.\n *\n * Fields not declared in the expression but present in target are ignored.\n *\n * Fields declared in the expression do not allow undefined or null by default.\n * Wrap with Optional() to allow undefined.\n * Wrap with Nullable() to allow null.\n *\n * @param target - The value to validate\n * @param expression - The schema expression to validate against\n * @throws {RuntimeTypeError} When type mismatch occurs\n * @throws {RuntimeTypeError} When value is undefined or null (unless optional/nullable)\n * @example\n * ```ts\n * const data: unknown = getData();\n *\n * validate(data, {\n * \"a\": Boolean,\n * \"b\": {\n * \"c\": Number,\n * \"d\": Optional(String), // string | undefined\n * \"e\": Nullable(String) // string | null\n * },\n * \"items\": [{\n * \"name\": String,\n * \"value\": Number\n * }]\n * });\n *\n * // From this point, data is type-asserted\n * data.a // boolean\n * data.b.c // number\n * data.b.d // string | undefined\n * data.b.e // string | null\n * data.items[0].name // string\n * ```\n */\nexport function validate<T extends Expression>(\n target: any,\n expression: T,\n path: string = \"\"\n): asserts target is InferType<T> {\n for (const key in expression) {\n if (!Object.prototype.hasOwnProperty.call(expression, key)) continue;\n\n const ctor = expression[key];\n const value = target[key];\n const currentPath = path ? `${path}.${key}` : key;\n\n if (isBothOptionalAndNullable(ctor)) {\n if (value === undefined || value === null) {\n continue\n }\n } else if (value === undefined && isOptional(ctor)) {\n continue;\n } else if (value === null && isNullable(ctor)) {\n continue;\n } else {\n assertType(currentPath, \"not undefined or null\", value, value !== undefined && value !== null);\n }\n\n const unwrappedCtor = unwrapOptionalNullable(ctor);\n\n if (isArraySchema(unwrappedCtor)) {\n assertType(currentPath, \"Array\", value, Array.isArray(value));\n const elementSchema = unwrappedCtor[0];\n const arr = value as any[];\n\n for (let i = 0; i < arr.length; i++) {\n validateArrayElement(arr[i], elementSchema, `${currentPath}[${i}]`);\n }\n continue;\n }\n\n if (typeof unwrappedCtor === 'function') {\n const validator = TYPE_VALIDATORS.get(unwrappedCtor);\n if (!validator) {\n throw new Error(\"Invalid expression. Use 'Number' or 'String' or 'Boolean' or 'Array' or 'Object'.\");\n }\n assertType(currentPath, unwrappedCtor.name, value, validator(value));\n } else if (isValidateExpression(unwrappedCtor)) {\n const objectValidator = TYPE_VALIDATORS.get(Object)!;\n assertType(currentPath, \"Object\", value, objectValidator(value));\n validate(value, unwrappedCtor, currentPath);\n } else {\n throw new Error(\"Invalid expression. Use 'Number' or 'String' or 'Boolean' or 'Array' or 'Object'.\");\n }\n }\n}"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["type OptionalMarker = { __optional: true };\ntype NullableMarker = { __nullable: true };\ntype MarkerKeys = '__optional' | '__nullable' | '__type';\n\ntype ArraySchema = [PrimitiveType | Expression];\n\ntype Expression = {\n [key: string | number]: PrimitiveType | Expression | ArraySchema | (PrimitiveType & OptionalMarker) | (PrimitiveType & NullableMarker) | (PrimitiveType & OptionalMarker & NullableMarker) | (Expression & OptionalMarker) | (Expression & NullableMarker) | (Expression & OptionalMarker & NullableMarker) | (ArraySchema & OptionalMarker) | (ArraySchema & NullableMarker) | (ArraySchema & OptionalMarker & NullableMarker) | true\n}\n\ntype PrimitiveType = NumberConstructor | StringConstructor | BooleanConstructor | ArrayConstructor | ObjectConstructor | DateConstructor;\n\ntype InferPrimitiveType<T> =\n T extends NumberConstructor ? number :\n T extends StringConstructor ? string :\n T extends BooleanConstructor ? boolean :\n T extends ArrayConstructor ? any[] :\n T extends ObjectConstructor ? object :\n T extends DateConstructor ? Date :\n never;\n\ntype InferType<T> =\n T extends PrimitiveType & OptionalMarker & NullableMarker\n ? InferPrimitiveType<T> | undefined | null\n : T extends PrimitiveType & OptionalMarker\n ? InferPrimitiveType<T> | undefined\n : T extends PrimitiveType & NullableMarker\n ? InferPrimitiveType<T> | null\n : T extends PrimitiveType\n ? InferPrimitiveType<T>\n : T extends ArraySchema & OptionalMarker & NullableMarker\n ? (T extends [infer U] ? (U extends Expression ? InferType<U>[] : U extends PrimitiveType ? InferPrimitiveType<U>[] : never) : never) | undefined | null\n : T extends ArraySchema & OptionalMarker\n ? (T extends [infer U] ? (U extends Expression ? InferType<U>[] : U extends PrimitiveType ? InferPrimitiveType<U>[] : never) : never) | undefined\n : T extends ArraySchema & NullableMarker\n ? (T extends [infer U] ? (U extends Expression ? InferType<U>[] : U extends PrimitiveType ? InferPrimitiveType<U>[] : never) : never) | null\n : T extends [infer U]\n ? U extends Expression\n ? InferType<U>[]\n : U extends PrimitiveType\n ? InferPrimitiveType<U>[]\n : never\n : T extends OptionalMarker & NullableMarker\n ? { [K in keyof T as K extends MarkerKeys ? never : K]: InferType<T[K]> } | undefined | null\n : T extends OptionalMarker\n ? { [K in keyof T as K extends MarkerKeys ? never : K]: InferType<T[K]> } | undefined\n : T extends NullableMarker\n ? { [K in keyof T as K extends MarkerKeys ? never : K]: InferType<T[K]> } | null\n : T extends Expression\n ? { [K in keyof T]: InferType<T[K]> }\n : never;\n\n/**\n * Marks a field as optional.\n * Optional fields pass validation even if they are undefined.\n * The type is inferred as T | undefined.\n * @example\n * ```ts\n * validate(data, {\n * required_field: String, // string (required)\n * optional_field: Optional(String), // string | undefined\n * optional_object: Optional({ // { id: string } | undefined\n * id: String\n * }),\n * optional_array: Optional([String]) // string[] | undefined\n * });\n * ```\n */\nexport function Optional<T extends PrimitiveType | Expression | ArraySchema>(ctor: T): T & OptionalMarker {\n return {\n ...ctor as any,\n __optional: true,\n __type: ctor\n } as T & OptionalMarker;\n}\n\n/**\n * Marks a field as nullable.\n * Nullable fields pass validation even if they are null.\n * The type is inferred as T | null.\n * @example\n * ```ts\n * validate(data, {\n * required_field: String, // string (required)\n * nullable_field: Nullable(String), // string | null\n * nullable_object: Nullable({ // { id: string } | null\n * id: String\n * }),\n * nullable_array: Nullable([String]) // string[] | null\n * });\n * ```\n */\nexport function Nullable<T extends PrimitiveType | Expression | ArraySchema>(ctor: T): T & NullableMarker {\n return {\n ...ctor as any,\n __nullable: true,\n __type: ctor\n } as T & NullableMarker;\n}\n\nexport class RuntimeTypeError extends Error {\n constructor(\n key: string,\n requiredType: string,\n actualType: string,\n actualValue: any,\n message?: string\n ) {\n if (message) message += \": \"\n super(`${message || \"\"}${key} must be ${requiredType}, but got ${actualType}. Actual value: ${actualValue}`);\n }\n}\n\nfunction getTypeName(value: any): string {\n if (value === null) return 'null';\n if (value === undefined) return 'undefined';\n if (Array.isArray(value)) return 'Array';\n if (typeof value === 'number' && Number.isNaN(value)) return 'NaN';\n if (typeof value === 'object') return 'Object';\n\n const type = typeof value;\n return type.charAt(0).toUpperCase() + type.slice(1);\n}\n\nconst TYPE_VALIDATORS = new Map<Function, (value: any) => boolean>([\n [Number, (value) => typeof value === 'number' && !Number.isNaN(value)],\n [String, (value) => typeof value === 'string'],\n [Boolean, (value) => typeof value === 'boolean'],\n [Array, (value) => Array.isArray(value)],\n [Object, (value) => typeof value === 'object' && value !== null && !Array.isArray(value) && !(value instanceof Date)],\n [Date, (value) => value instanceof Date && !Number.isNaN(value.getTime())]\n]);\n\nfunction isValidateExpression(ctor: any): ctor is Expression {\n return typeof ctor === 'object' && !Array.isArray(ctor);\n}\n\nfunction isArraySchema(ctor: any): ctor is [PrimitiveType | Expression] {\n return Array.isArray(ctor) && ctor.length === 1;\n}\n\nfunction isOptional(ctor: any): boolean {\n return ctor.__optional === true;\n}\n\nfunction isNullable(ctor: any): boolean {\n return ctor.__nullable === true;\n}\n\nfunction isBothOptionalAndNullable(ctor: any): boolean {\n return ctor.__optional === true && ctor.__nullable === true;\n}\n\nfunction unwrapOptionalNullable(ctor: any): any {\n while (ctor && typeof ctor === 'object' && !Array.isArray(ctor) && ctor.__type) {\n ctor = ctor.__type;\n }\n return ctor;\n}\n\nfunction assertType(key: string, expectedType: string, value: any, isValid: boolean): void {\n if (!isValid) {\n throw new RuntimeTypeError(key, expectedType, getTypeName(value), value);\n }\n}\n\nfunction validateArrayElement(item: any, schema: any, path: string): void {\n if (isArraySchema(schema)) {\n assertType(path, \"Array\", item, Array.isArray(item));\n const innerSchema = schema[0];\n const arr = item as any[];\n for (let i = 0; i < arr.length; i++) {\n validateArrayElement(arr[i], innerSchema, `${path}[${i}]`);\n }\n } else if (isValidateExpression(schema)) {\n const objectValidator = TYPE_VALIDATORS.get(Object);\n assertType(path, \"Object\", item, objectValidator?.(item) ?? false);\n validate(item, schema, path);\n } else if (typeof schema === 'function') {\n const validator = TYPE_VALIDATORS.get(schema);\n if (!validator) {\n throw new Error(\"Invalid array element expression.\");\n }\n assertType(path, schema.name, item, validator(item));\n } else {\n throw new Error(\"Invalid array element expression.\");\n }\n}\n\n/**\n * Asserts the type of target according to the expression schema.\n * The expression should be represented using primitive type constructors.\n * Supports nested types and array element type validation.\n *\n * Fields not declared in the expression but present in target are ignored.\n *\n * Fields declared in the expression do not allow undefined or null by default.\n * Wrap with Optional() to allow undefined.\n * Wrap with Nullable() to allow null.\n *\n * @param target - The value to validate\n * @param expression - The schema expression to validate against\n * @throws {RuntimeTypeError} When type mismatch occurs\n * @throws {RuntimeTypeError} When value is undefined or null (unless optional/nullable)\n * @example\n * ```ts\n * const data: unknown = getData();\n *\n * validate(data, {\n * \"a\": Boolean,\n * \"b\": {\n * \"c\": Number,\n * \"d\": Optional(String), // string | undefined\n * \"e\": Nullable(String) // string | null\n * },\n * \"items\": [{\n * \"name\": String,\n * \"value\": Number\n * }]\n * });\n *\n * // From this point, data is type-asserted\n * data.a // boolean\n * data.b.c // number\n * data.b.d // string | undefined\n * data.b.e // string | null\n * data.items[0].name // string\n * ```\n */\nexport function validate<T extends Expression>(\n target: any,\n expression: T,\n path: string = \"\"\n): asserts target is InferType<T> {\n for (const key in expression) {\n if (!Object.prototype.hasOwnProperty.call(expression, key)) continue;\n\n const ctor = expression[key];\n const value = target[key];\n const currentPath = path ? `${path}.${key}` : key;\n\n if (isBothOptionalAndNullable(ctor)) {\n if (value === undefined || value === null) {\n continue\n }\n } else if (value === undefined && isOptional(ctor)) {\n continue;\n } else if (value === null && isNullable(ctor)) {\n continue;\n } else {\n assertType(currentPath, \"not undefined or null\", value, value !== undefined && value !== null);\n }\n\n const unwrappedCtor = unwrapOptionalNullable(ctor);\n\n if (isArraySchema(unwrappedCtor)) {\n assertType(currentPath, \"Array\", value, Array.isArray(value));\n const elementSchema = unwrappedCtor[0];\n const arr = value as any[];\n\n for (let i = 0; i < arr.length; i++) {\n validateArrayElement(arr[i], elementSchema, `${currentPath}[${i}]`);\n }\n continue;\n }\n\n if (typeof unwrappedCtor === 'function') {\n const validator = TYPE_VALIDATORS.get(unwrappedCtor);\n if (!validator) {\n throw new Error(\"Invalid expression. Use 'Number' or 'String' or 'Boolean' or 'Array' or 'Object'.\");\n }\n assertType(currentPath, unwrappedCtor.name, value, validator(value));\n } else if (isValidateExpression(unwrappedCtor)) {\n const objectValidator = TYPE_VALIDATORS.get(Object)!;\n assertType(currentPath, \"Object\", value, objectValidator(value));\n validate(value, unwrappedCtor, currentPath);\n } else {\n throw new Error(\"Invalid expression. Use 'Number' or 'String' or 'Boolean' or 'Array' or 'Object'.\");\n }\n }\n}"],"mappings":"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","names":[]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["type OptionalMarker = { __optional: true };\ntype NullableMarker = { __nullable: true };\n\ntype ArraySchema = [PrimitiveType | Expression];\n\ntype Expression = {\n [key: string | number]: PrimitiveType | (PrimitiveType & OptionalMarker) | (PrimitiveType & NullableMarker) | (PrimitiveType & OptionalMarker & NullableMarker) | Expression | (Expression & OptionalMarker) | (Expression & NullableMarker) | (Expression & OptionalMarker & NullableMarker) | ArraySchema | (ArraySchema & OptionalMarker) | (ArraySchema & NullableMarker) | (ArraySchema & OptionalMarker & NullableMarker)\n}\n\ntype PrimitiveType = NumberConstructor | StringConstructor | BooleanConstructor | ArrayConstructor | ObjectConstructor | DateConstructor;\n\ntype InferPrimitiveType<T> =\n T extends NumberConstructor ? number :\n T extends StringConstructor ? string :\n T extends BooleanConstructor ? boolean :\n T extends ArrayConstructor ? any[] :\n T extends ObjectConstructor ? object :\n T extends DateConstructor ? Date :\n never;\n\ntype InferType<T> =\n T extends PrimitiveType & OptionalMarker & NullableMarker\n ? InferPrimitiveType<T> | undefined | null\n : T extends PrimitiveType & OptionalMarker\n ? InferPrimitiveType<T> | undefined\n : T extends PrimitiveType & NullableMarker\n ? InferPrimitiveType<T> | null\n : T extends PrimitiveType\n ? InferPrimitiveType<T>\n : T extends Expression & OptionalMarker & NullableMarker\n ? { [K in keyof T]: InferType<T[K]> } | undefined | null\n : T extends Expression & OptionalMarker\n ? { [K in keyof T]: InferType<T[K]> } | undefined\n : T extends Expression & NullableMarker\n ? { [K in keyof T]: InferType<T[K]> } | null\n : T extends ArraySchema & OptionalMarker & NullableMarker\n ? (T extends [infer U] ? (U extends Expression ? InferType<U>[] : U extends PrimitiveType ? InferPrimitiveType<U>[] : never) : never) | undefined | null\n : T extends ArraySchema & OptionalMarker\n ? (T extends [infer U] ? (U extends Expression ? InferType<U>[] : U extends PrimitiveType ? InferPrimitiveType<U>[] : never) : never) | undefined\n : T extends ArraySchema & NullableMarker\n ? (T extends [infer U] ? (U extends Expression ? InferType<U>[] : U extends PrimitiveType ? InferPrimitiveType<U>[] : never) : never) | null\n : T extends [infer U]\n ? U extends Expression\n ? InferType<U>[]\n : U extends PrimitiveType\n ? InferPrimitiveType<U>[]\n : never\n : T extends Expression\n ? { [K in keyof T]: InferType<T[K]> }\n : never;\n\n/**\n * Marks a field as optional.\n * Optional fields pass validation even if they are undefined.\n * The type is inferred as T | undefined.\n * @example\n * ```ts\n * validate(data, {\n * required_field: String, // string (required)\n * optional_field: Optional(String), // string | undefined\n * optional_object: Optional({ // { id: string } | undefined\n * id: String\n * }),\n * optional_array: Optional([String]) // string[] | undefined\n * });\n * ```\n */\nexport function Optional<T extends PrimitiveType | Expression | ArraySchema>(ctor: T): T & OptionalMarker {\n return {\n ...ctor as any,\n __optional: true,\n __type: ctor\n } as T & OptionalMarker;\n}\n\n/**\n * Marks a field as nullable.\n * Nullable fields pass validation even if they are null.\n * The type is inferred as T | null.\n * @example\n * ```ts\n * validate(data, {\n * required_field: String, // string (required)\n * nullable_field: Nullable(String), // string | null\n * nullable_object: Nullable({ // { id: string } | null\n * id: String\n * }),\n * nullable_array: Nullable([String]) // string[] | null\n * });\n * ```\n */\nexport function Nullable<T extends PrimitiveType | Expression | ArraySchema>(ctor: T): T & NullableMarker {\n return {\n ...ctor as any,\n __nullable: true,\n __type: ctor\n } as T & NullableMarker;\n}\n\nexport class RuntimeTypeError extends Error {\n constructor(\n key: string,\n requiredType: string,\n actualType: string,\n actualValue: any,\n message?: string\n ) {\n if (message) message += \": \"\n super(`${message || \"\"}${key} must be ${requiredType}, but got ${actualType}. Actual value: ${actualValue}`);\n }\n}\n\nfunction getTypeName(value: any): string {\n if (value === null) return 'null';\n if (value === undefined) return 'undefined';\n if (Array.isArray(value)) return 'Array';\n if (typeof value === 'number' && Number.isNaN(value)) return 'NaN';\n if (typeof value === 'object') return 'Object';\n\n const type = typeof value;\n return type.charAt(0).toUpperCase() + type.slice(1);\n}\n\nconst TYPE_VALIDATORS = new Map<Function, (value: any) => boolean>([\n [Number, (value) => typeof value === 'number' && !Number.isNaN(value)],\n [String, (value) => typeof value === 'string'],\n [Boolean, (value) => typeof value === 'boolean'],\n [Array, (value) => Array.isArray(value)],\n [Object, (value) => typeof value === 'object' && value !== null && !Array.isArray(value) && !(value instanceof Date)],\n [Date, (value) => value instanceof Date && !Number.isNaN(value.getTime())]\n]);\n\nfunction isValidateExpression(ctor: any): ctor is Expression {\n return typeof ctor === 'object' && !Array.isArray(ctor);\n}\n\nfunction isArraySchema(ctor: any): ctor is [PrimitiveType | Expression] {\n return Array.isArray(ctor) && ctor.length === 1;\n}\n\nfunction isOptional(ctor: any): boolean {\n return ctor.__optional === true;\n}\n\nfunction isNullable(ctor: any): boolean {\n return ctor.__nullable === true;\n}\n\nfunction isBothOptionalAndNullable(ctor: any): boolean {\n return ctor.__optional === true && ctor.__nullable === true;\n}\n\nfunction unwrapOptionalNullable(ctor: any): any {\n while (ctor && typeof ctor === 'object' && !Array.isArray(ctor) && ctor.__type) {\n ctor = ctor.__type;\n }\n return ctor;\n}\n\nfunction assertType(key: string, expectedType: string, value: any, isValid: boolean): void {\n if (!isValid) {\n throw new RuntimeTypeError(key, expectedType, getTypeName(value), value);\n }\n}\n\nfunction validateArrayElement(item: any, schema: any, path: string): void {\n if (isArraySchema(schema)) {\n assertType(path, \"Array\", item, Array.isArray(item));\n const innerSchema = schema[0];\n const arr = item as any[];\n for (let i = 0; i < arr.length; i++) {\n validateArrayElement(arr[i], innerSchema, `${path}[${i}]`);\n }\n } else if (isValidateExpression(schema)) {\n const objectValidator = TYPE_VALIDATORS.get(Object);\n assertType(path, \"Object\", item, objectValidator?.(item) ?? false);\n validate(item, schema, path);\n } else if (typeof schema === 'function') {\n const validator = TYPE_VALIDATORS.get(schema);\n if (!validator) {\n throw new Error(\"Invalid array element expression.\");\n }\n assertType(path, schema.name, item, validator(item));\n } else {\n throw new Error(\"Invalid array element expression.\");\n }\n}\n\n/**\n * Asserts the type of target according to the expression schema.\n * The expression should be represented using primitive type constructors.\n * Supports nested types and array element type validation.\n *\n * Fields not declared in the expression but present in target are ignored.\n *\n * Fields declared in the expression do not allow undefined or null by default.\n * Wrap with Optional() to allow undefined.\n * Wrap with Nullable() to allow null.\n *\n * @param target - The value to validate\n * @param expression - The schema expression to validate against\n * @throws {RuntimeTypeError} When type mismatch occurs\n * @throws {RuntimeTypeError} When value is undefined or null (unless optional/nullable)\n * @example\n * ```ts\n * const data: unknown = getData();\n *\n * validate(data, {\n * \"a\": Boolean,\n * \"b\": {\n * \"c\": Number,\n * \"d\": Optional(String), // string | undefined\n * \"e\": Nullable(String) // string | null\n * },\n * \"items\": [{\n * \"name\": String,\n * \"value\": Number\n * }]\n * });\n *\n * // From this point, data is type-asserted\n * data.a // boolean\n * data.b.c // number\n * data.b.d // string | undefined\n * data.b.e // string | null\n * data.items[0].name // string\n * ```\n */\nexport function validate<T extends Expression>(\n target: any,\n expression: T,\n path: string = \"\"\n): asserts target is InferType<T> {\n for (const key in expression) {\n if (!Object.prototype.hasOwnProperty.call(expression, key)) continue;\n\n const ctor = expression[key];\n const value = target[key];\n const currentPath = path ? `${path}.${key}` : key;\n\n if (isBothOptionalAndNullable(ctor)) {\n if (value === undefined || value === null) {\n continue\n }\n } else if (value === undefined && isOptional(ctor)) {\n continue;\n } else if (value === null && isNullable(ctor)) {\n continue;\n } else {\n assertType(currentPath, \"not undefined or null\", value, value !== undefined && value !== null);\n }\n\n const unwrappedCtor = unwrapOptionalNullable(ctor);\n\n if (isArraySchema(unwrappedCtor)) {\n assertType(currentPath, \"Array\", value, Array.isArray(value));\n const elementSchema = unwrappedCtor[0];\n const arr = value as any[];\n\n for (let i = 0; i < arr.length; i++) {\n validateArrayElement(arr[i], elementSchema, `${currentPath}[${i}]`);\n }\n continue;\n }\n\n if (typeof unwrappedCtor === 'function') {\n const validator = TYPE_VALIDATORS.get(unwrappedCtor);\n if (!validator) {\n throw new Error(\"Invalid expression. Use 'Number' or 'String' or 'Boolean' or 'Array' or 'Object'.\");\n }\n assertType(currentPath, unwrappedCtor.name, value, validator(value));\n } else if (isValidateExpression(unwrappedCtor)) {\n const objectValidator = TYPE_VALIDATORS.get(Object)!;\n assertType(currentPath, \"Object\", value, objectValidator(value));\n validate(value, unwrappedCtor, currentPath);\n } else {\n throw new Error(\"Invalid expression. Use 'Number' or 'String' or 'Boolean' or 'Array' or 'Object'.\");\n }\n }\n}"],"mappings":";AAmEO,SAAS,SAA6D,MAA6B;AACtG,SAAO;AAAA,IACH,GAAG;AAAA,IACH,YAAY;AAAA,IACZ,QAAQ;AAAA,EACZ;AACJ;AAkBO,SAAS,SAA6D,MAA6B;AACtG,SAAO;AAAA,IACH,GAAG;AAAA,IACH,YAAY;AAAA,IACZ,QAAQ;AAAA,EACZ;AACJ;AAEO,IAAM,mBAAN,cAA+B,MAAM;AAAA,EACxC,YACI,KACA,cACA,YACA,aACA,SACF;AACE,QAAI,QAAS,YAAW;AACxB,UAAM,GAAG,WAAW,EAAE,GAAG,GAAG,YAAY,YAAY,aAAa,UAAU,mBAAmB,WAAW,EAAE;AAAA,EAC/G;AACJ;AAEA,SAAS,YAAY,OAAoB;AACrC,MAAI,UAAU,KAAM,QAAO;AAC3B,MAAI,UAAU,OAAW,QAAO;AAChC,MAAI,MAAM,QAAQ,KAAK,EAAG,QAAO;AACjC,MAAI,OAAO,UAAU,YAAY,OAAO,MAAM,KAAK,EAAG,QAAO;AAC7D,MAAI,OAAO,UAAU,SAAU,QAAO;AAEtC,QAAM,OAAO,OAAO;AACpB,SAAO,KAAK,OAAO,CAAC,EAAE,YAAY,IAAI,KAAK,MAAM,CAAC;AACtD;AAEA,IAAM,kBAAkB,oBAAI,IAAuC;AAAA,EAC/D,CAAC,QAAQ,CAAC,UAAU,OAAO,UAAU,YAAY,CAAC,OAAO,MAAM,KAAK,CAAC;AAAA,EACrE,CAAC,QAAQ,CAAC,UAAU,OAAO,UAAU,QAAQ;AAAA,EAC7C,CAAC,SAAS,CAAC,UAAU,OAAO,UAAU,SAAS;AAAA,EAC/C,CAAC,OAAO,CAAC,UAAU,MAAM,QAAQ,KAAK,CAAC;AAAA,EACvC,CAAC,QAAQ,CAAC,UAAU,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK,KAAK,EAAE,iBAAiB,KAAK;AAAA,EACpH,CAAC,MAAM,CAAC,UAAU,iBAAiB,QAAQ,CAAC,OAAO,MAAM,MAAM,QAAQ,CAAC,CAAC;AAC7E,CAAC;AAED,SAAS,qBAAqB,MAA+B;AACzD,SAAO,OAAO,SAAS,YAAY,CAAC,MAAM,QAAQ,IAAI;AAC1D;AAEA,SAAS,cAAc,MAAiD;AACpE,SAAO,MAAM,QAAQ,IAAI,KAAK,KAAK,WAAW;AAClD;AAEA,SAAS,WAAW,MAAoB;AACpC,SAAO,KAAK,eAAe;AAC/B;AAEA,SAAS,WAAW,MAAoB;AACpC,SAAO,KAAK,eAAe;AAC/B;AAEA,SAAS,0BAA0B,MAAoB;AACnD,SAAO,KAAK,eAAe,QAAQ,KAAK,eAAe;AAC3D;AAEA,SAAS,uBAAuB,MAAgB;AAC5C,SAAO,QAAQ,OAAO,SAAS,YAAY,CAAC,MAAM,QAAQ,IAAI,KAAK,KAAK,QAAQ;AAC5E,WAAO,KAAK;AAAA,EAChB;AACA,SAAO;AACX;AAEA,SAAS,WAAW,KAAa,cAAsB,OAAY,SAAwB;AACvF,MAAI,CAAC,SAAS;AACV,UAAM,IAAI,iBAAiB,KAAK,cAAc,YAAY,KAAK,GAAG,KAAK;AAAA,EAC3E;AACJ;AAEA,SAAS,qBAAqB,MAAW,QAAa,MAAoB;AACtE,MAAI,cAAc,MAAM,GAAG;AACvB,eAAW,MAAM,SAAS,MAAM,MAAM,QAAQ,IAAI,CAAC;AACnD,UAAM,cAAc,OAAO,CAAC;AAC5B,UAAM,MAAM;AACZ,aAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK;AACjC,2BAAqB,IAAI,CAAC,GAAG,aAAa,GAAG,IAAI,IAAI,CAAC,GAAG;AAAA,IAC7D;AAAA,EACJ,WAAW,qBAAqB,MAAM,GAAG;AACrC,UAAM,kBAAkB,gBAAgB,IAAI,MAAM;AAClD,eAAW,MAAM,UAAU,MAAM,kBAAkB,IAAI,KAAK,KAAK;AACjE,aAAS,MAAM,QAAQ,IAAI;AAAA,EAC/B,WAAW,OAAO,WAAW,YAAY;AACrC,UAAM,YAAY,gBAAgB,IAAI,MAAM;AAC5C,QAAI,CAAC,WAAW;AACZ,YAAM,IAAI,MAAM,mCAAmC;AAAA,IACvD;AACA,eAAW,MAAM,OAAO,MAAM,MAAM,UAAU,IAAI,CAAC;AAAA,EACvD,OAAO;AACH,UAAM,IAAI,MAAM,mCAAmC;AAAA,EACvD;AACJ;AA0CO,SAAS,SACZ,QACA,YACA,OAAe,IACe;AAC9B,aAAW,OAAO,YAAY;AAC1B,QAAI,CAAC,OAAO,UAAU,eAAe,KAAK,YAAY,GAAG,EAAG;AAE5D,UAAM,OAAO,WAAW,GAAG;AAC3B,UAAM,QAAQ,OAAO,GAAG;AACxB,UAAM,cAAc,OAAO,GAAG,IAAI,IAAI,GAAG,KAAK;AAE9C,QAAI,0BAA0B,IAAI,GAAG;AACjC,UAAI,UAAU,UAAa,UAAU,MAAM;AACvC;AAAA,MACJ;AAAA,IACJ,WAAW,UAAU,UAAa,WAAW,IAAI,GAAG;AAChD;AAAA,IACJ,WAAW,UAAU,QAAQ,WAAW,IAAI,GAAG;AAC3C;AAAA,IACJ,OAAO;AACH,iBAAW,aAAa,yBAAyB,OAAO,UAAU,UAAa,UAAU,IAAI;AAAA,IACjG;AAEA,UAAM,gBAAgB,uBAAuB,IAAI;AAEjD,QAAI,cAAc,aAAa,GAAG;AAC9B,iBAAW,aAAa,SAAS,OAAO,MAAM,QAAQ,KAAK,CAAC;AAC5D,YAAM,gBAAgB,cAAc,CAAC;AACrC,YAAM,MAAM;AAEZ,eAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK;AACjC,6BAAqB,IAAI,CAAC,GAAG,eAAe,GAAG,WAAW,IAAI,CAAC,GAAG;AAAA,MACtE;AACA;AAAA,IACJ;AAEA,QAAI,OAAO,kBAAkB,YAAY;AACrC,YAAM,YAAY,gBAAgB,IAAI,aAAa;AACnD,UAAI,CAAC,WAAW;AACZ,cAAM,IAAI,MAAM,mFAAmF;AAAA,MACvG;AACA,iBAAW,aAAa,cAAc,MAAM,OAAO,UAAU,KAAK,CAAC;AAAA,IACvE,WAAW,qBAAqB,aAAa,GAAG;AAC5C,YAAM,kBAAkB,gBAAgB,IAAI,MAAM;AAClD,iBAAW,aAAa,UAAU,OAAO,gBAAgB,KAAK,CAAC;AAC/D,eAAS,OAAO,eAAe,WAAW;AAAA,IAC9C,OAAO;AACH,YAAM,IAAI,MAAM,mFAAmF;AAAA,IACvG;AAAA,EACJ;AACJ;","names":[]}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["type OptionalMarker = { __optional: true };\ntype NullableMarker = { __nullable: true };\ntype MarkerKeys = '__optional' | '__nullable' | '__type';\n\ntype ArraySchema = [PrimitiveType | Expression];\n\ntype Expression = {\n [key: string | number]: PrimitiveType | Expression | ArraySchema | (PrimitiveType & OptionalMarker) | (PrimitiveType & NullableMarker) | (PrimitiveType & OptionalMarker & NullableMarker) | (Expression & OptionalMarker) | (Expression & NullableMarker) | (Expression & OptionalMarker & NullableMarker) | (ArraySchema & OptionalMarker) | (ArraySchema & NullableMarker) | (ArraySchema & OptionalMarker & NullableMarker) | true\n}\n\ntype PrimitiveType = NumberConstructor | StringConstructor | BooleanConstructor | ArrayConstructor | ObjectConstructor | DateConstructor;\n\ntype InferPrimitiveType<T> =\n T extends NumberConstructor ? number :\n T extends StringConstructor ? string :\n T extends BooleanConstructor ? boolean :\n T extends ArrayConstructor ? any[] :\n T extends ObjectConstructor ? object :\n T extends DateConstructor ? Date :\n never;\n\ntype InferType<T> =\n T extends PrimitiveType & OptionalMarker & NullableMarker\n ? InferPrimitiveType<T> | undefined | null\n : T extends PrimitiveType & OptionalMarker\n ? InferPrimitiveType<T> | undefined\n : T extends PrimitiveType & NullableMarker\n ? InferPrimitiveType<T> | null\n : T extends PrimitiveType\n ? InferPrimitiveType<T>\n : T extends ArraySchema & OptionalMarker & NullableMarker\n ? (T extends [infer U] ? (U extends Expression ? InferType<U>[] : U extends PrimitiveType ? InferPrimitiveType<U>[] : never) : never) | undefined | null\n : T extends ArraySchema & OptionalMarker\n ? (T extends [infer U] ? (U extends Expression ? InferType<U>[] : U extends PrimitiveType ? InferPrimitiveType<U>[] : never) : never) | undefined\n : T extends ArraySchema & NullableMarker\n ? (T extends [infer U] ? (U extends Expression ? InferType<U>[] : U extends PrimitiveType ? InferPrimitiveType<U>[] : never) : never) | null\n : T extends [infer U]\n ? U extends Expression\n ? InferType<U>[]\n : U extends PrimitiveType\n ? InferPrimitiveType<U>[]\n : never\n : T extends OptionalMarker & NullableMarker\n ? { [K in keyof T as K extends MarkerKeys ? never : K]: InferType<T[K]> } | undefined | null\n : T extends OptionalMarker\n ? { [K in keyof T as K extends MarkerKeys ? never : K]: InferType<T[K]> } | undefined\n : T extends NullableMarker\n ? { [K in keyof T as K extends MarkerKeys ? never : K]: InferType<T[K]> } | null\n : T extends Expression\n ? { [K in keyof T]: InferType<T[K]> }\n : never;\n\n/**\n * Marks a field as optional.\n * Optional fields pass validation even if they are undefined.\n * The type is inferred as T | undefined.\n * @example\n * ```ts\n * validate(data, {\n * required_field: String, // string (required)\n * optional_field: Optional(String), // string | undefined\n * optional_object: Optional({ // { id: string } | undefined\n * id: String\n * }),\n * optional_array: Optional([String]) // string[] | undefined\n * });\n * ```\n */\nexport function Optional<T extends PrimitiveType | Expression | ArraySchema>(ctor: T): T & OptionalMarker {\n return {\n ...ctor as any,\n __optional: true,\n __type: ctor\n } as T & OptionalMarker;\n}\n\n/**\n * Marks a field as nullable.\n * Nullable fields pass validation even if they are null.\n * The type is inferred as T | null.\n * @example\n * ```ts\n * validate(data, {\n * required_field: String, // string (required)\n * nullable_field: Nullable(String), // string | null\n * nullable_object: Nullable({ // { id: string } | null\n * id: String\n * }),\n * nullable_array: Nullable([String]) // string[] | null\n * });\n * ```\n */\nexport function Nullable<T extends PrimitiveType | Expression | ArraySchema>(ctor: T): T & NullableMarker {\n return {\n ...ctor as any,\n __nullable: true,\n __type: ctor\n } as T & NullableMarker;\n}\n\nexport class RuntimeTypeError extends Error {\n constructor(\n key: string,\n requiredType: string,\n actualType: string,\n actualValue: any,\n message?: string\n ) {\n if (message) message += \": \"\n super(`${message || \"\"}${key} must be ${requiredType}, but got ${actualType}. Actual value: ${actualValue}`);\n }\n}\n\nfunction getTypeName(value: any): string {\n if (value === null) return 'null';\n if (value === undefined) return 'undefined';\n if (Array.isArray(value)) return 'Array';\n if (typeof value === 'number' && Number.isNaN(value)) return 'NaN';\n if (typeof value === 'object') return 'Object';\n\n const type = typeof value;\n return type.charAt(0).toUpperCase() + type.slice(1);\n}\n\nconst TYPE_VALIDATORS = new Map<Function, (value: any) => boolean>([\n [Number, (value) => typeof value === 'number' && !Number.isNaN(value)],\n [String, (value) => typeof value === 'string'],\n [Boolean, (value) => typeof value === 'boolean'],\n [Array, (value) => Array.isArray(value)],\n [Object, (value) => typeof value === 'object' && value !== null && !Array.isArray(value) && !(value instanceof Date)],\n [Date, (value) => value instanceof Date && !Number.isNaN(value.getTime())]\n]);\n\nfunction isValidateExpression(ctor: any): ctor is Expression {\n return typeof ctor === 'object' && !Array.isArray(ctor);\n}\n\nfunction isArraySchema(ctor: any): ctor is [PrimitiveType | Expression] {\n return Array.isArray(ctor) && ctor.length === 1;\n}\n\nfunction isOptional(ctor: any): boolean {\n return ctor.__optional === true;\n}\n\nfunction isNullable(ctor: any): boolean {\n return ctor.__nullable === true;\n}\n\nfunction isBothOptionalAndNullable(ctor: any): boolean {\n return ctor.__optional === true && ctor.__nullable === true;\n}\n\nfunction unwrapOptionalNullable(ctor: any): any {\n while (ctor && typeof ctor === 'object' && !Array.isArray(ctor) && ctor.__type) {\n ctor = ctor.__type;\n }\n return ctor;\n}\n\nfunction assertType(key: string, expectedType: string, value: any, isValid: boolean): void {\n if (!isValid) {\n throw new RuntimeTypeError(key, expectedType, getTypeName(value), value);\n }\n}\n\nfunction validateArrayElement(item: any, schema: any, path: string): void {\n if (isArraySchema(schema)) {\n assertType(path, \"Array\", item, Array.isArray(item));\n const innerSchema = schema[0];\n const arr = item as any[];\n for (let i = 0; i < arr.length; i++) {\n validateArrayElement(arr[i], innerSchema, `${path}[${i}]`);\n }\n } else if (isValidateExpression(schema)) {\n const objectValidator = TYPE_VALIDATORS.get(Object);\n assertType(path, \"Object\", item, objectValidator?.(item) ?? false);\n validate(item, schema, path);\n } else if (typeof schema === 'function') {\n const validator = TYPE_VALIDATORS.get(schema);\n if (!validator) {\n throw new Error(\"Invalid array element expression.\");\n }\n assertType(path, schema.name, item, validator(item));\n } else {\n throw new Error(\"Invalid array element expression.\");\n }\n}\n\n/**\n * Asserts the type of target according to the expression schema.\n * The expression should be represented using primitive type constructors.\n * Supports nested types and array element type validation.\n *\n * Fields not declared in the expression but present in target are ignored.\n *\n * Fields declared in the expression do not allow undefined or null by default.\n * Wrap with Optional() to allow undefined.\n * Wrap with Nullable() to allow null.\n *\n * @param target - The value to validate\n * @param expression - The schema expression to validate against\n * @throws {RuntimeTypeError} When type mismatch occurs\n * @throws {RuntimeTypeError} When value is undefined or null (unless optional/nullable)\n * @example\n * ```ts\n * const data: unknown = getData();\n *\n * validate(data, {\n * \"a\": Boolean,\n * \"b\": {\n * \"c\": Number,\n * \"d\": Optional(String), // string | undefined\n * \"e\": Nullable(String) // string | null\n * },\n * \"items\": [{\n * \"name\": String,\n * \"value\": Number\n * }]\n * });\n *\n * // From this point, data is type-asserted\n * data.a // boolean\n * data.b.c // number\n * data.b.d // string | undefined\n * data.b.e // string | null\n * data.items[0].name // string\n * ```\n */\nexport function validate<T extends Expression>(\n target: any,\n expression: T,\n path: string = \"\"\n): asserts target is InferType<T> {\n for (const key in expression) {\n if (!Object.prototype.hasOwnProperty.call(expression, key)) continue;\n\n const ctor = expression[key];\n const value = target[key];\n const currentPath = path ? `${path}.${key}` : key;\n\n if (isBothOptionalAndNullable(ctor)) {\n if (value === undefined || value === null) {\n continue\n }\n } else if (value === undefined && isOptional(ctor)) {\n continue;\n } else if (value === null && isNullable(ctor)) {\n continue;\n } else {\n assertType(currentPath, \"not undefined or null\", value, value !== undefined && value !== null);\n }\n\n const unwrappedCtor = unwrapOptionalNullable(ctor);\n\n if (isArraySchema(unwrappedCtor)) {\n assertType(currentPath, \"Array\", value, Array.isArray(value));\n const elementSchema = unwrappedCtor[0];\n const arr = value as any[];\n\n for (let i = 0; i < arr.length; i++) {\n validateArrayElement(arr[i], elementSchema, `${currentPath}[${i}]`);\n }\n continue;\n }\n\n if (typeof unwrappedCtor === 'function') {\n const validator = TYPE_VALIDATORS.get(unwrappedCtor);\n if (!validator) {\n throw new Error(\"Invalid expression. Use 'Number' or 'String' or 'Boolean' or 'Array' or 'Object'.\");\n }\n assertType(currentPath, unwrappedCtor.name, value, validator(value));\n } else if (isValidateExpression(unwrappedCtor)) {\n const objectValidator = TYPE_VALIDATORS.get(Object)!;\n assertType(currentPath, \"Object\", value, objectValidator(value));\n validate(value, unwrappedCtor, currentPath);\n } else {\n throw new Error(\"Invalid expression. Use 'Number' or 'String' or 'Boolean' or 'Array' or 'Object'.\");\n }\n }\n}"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "valdex",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
4
4
  "description": "Runtime type validation with TypeScript type inference",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",