@oscarpalmer/jhunal 0.16.0 → 0.17.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.
@@ -2,10 +2,10 @@ import { Values } from "./models.mjs";
2
2
 
3
3
  //#region src/constants.d.ts
4
4
  declare const ERROR_NAME = "SchematicError";
5
- declare const EXPRESSION_PROPERTY: RegExp;
6
5
  declare const MESSAGE_CONSTRUCTOR = "Expected a constructor function";
7
6
  declare const MESSAGE_SCHEMA_INVALID_EMPTY = "Schema must have at least one property";
8
7
  declare const MESSAGE_SCHEMA_INVALID_PROPERTY_DISALLOWED = "'<key>.<property>' property is not allowed for schemas in $type";
8
+ declare const MESSAGE_SCHEMA_INVALID_PROPERTY_NULLABLE = "'<>' property must not be 'null' or 'undefined'";
9
9
  declare const MESSAGE_SCHEMA_INVALID_PROPERTY_REQUIRED = "'<>.$required' property must be a boolean";
10
10
  declare const MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE = "'<>' property must be of a valid type";
11
11
  declare const MESSAGE_SCHEMA_INVALID_TYPE = "Schema must be an object";
@@ -24,4 +24,4 @@ declare const TYPE_UNDEFINED = "undefined";
24
24
  declare const VALIDATABLE_TYPES: Set<keyof Values>;
25
25
  declare const TYPE_ALL: Set<keyof Values>;
26
26
  //#endregion
27
- export { ERROR_NAME, EXPRESSION_PROPERTY, MESSAGE_CONSTRUCTOR, MESSAGE_SCHEMA_INVALID_EMPTY, MESSAGE_SCHEMA_INVALID_PROPERTY_DISALLOWED, MESSAGE_SCHEMA_INVALID_PROPERTY_REQUIRED, MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE, MESSAGE_SCHEMA_INVALID_TYPE, MESSAGE_VALIDATOR_INVALID_KEY, MESSAGE_VALIDATOR_INVALID_TYPE, MESSAGE_VALIDATOR_INVALID_VALUE, PROPERTY_REQUIRED, PROPERTY_TYPE, PROPERTY_VALIDATORS, SCHEMATIC_NAME, TEMPLATE_PATTERN, TEMPLATE_PATTERN_KEY, TEMPLATE_PATTERN_PROPERTY, TYPE_ALL, TYPE_OBJECT, TYPE_UNDEFINED, VALIDATABLE_TYPES };
27
+ export { ERROR_NAME, MESSAGE_CONSTRUCTOR, MESSAGE_SCHEMA_INVALID_EMPTY, MESSAGE_SCHEMA_INVALID_PROPERTY_DISALLOWED, MESSAGE_SCHEMA_INVALID_PROPERTY_NULLABLE, MESSAGE_SCHEMA_INVALID_PROPERTY_REQUIRED, MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE, MESSAGE_SCHEMA_INVALID_TYPE, MESSAGE_VALIDATOR_INVALID_KEY, MESSAGE_VALIDATOR_INVALID_TYPE, MESSAGE_VALIDATOR_INVALID_VALUE, PROPERTY_REQUIRED, PROPERTY_TYPE, PROPERTY_VALIDATORS, SCHEMATIC_NAME, TEMPLATE_PATTERN, TEMPLATE_PATTERN_KEY, TEMPLATE_PATTERN_PROPERTY, TYPE_ALL, TYPE_OBJECT, TYPE_UNDEFINED, VALIDATABLE_TYPES };
@@ -1,9 +1,9 @@
1
1
  //#region src/constants.ts
2
2
  const ERROR_NAME = "SchematicError";
3
- const EXPRESSION_PROPERTY = /(^|\.)\$(required|type|validators)(\.|$)/;
4
3
  const MESSAGE_CONSTRUCTOR = "Expected a constructor function";
5
4
  const MESSAGE_SCHEMA_INVALID_EMPTY = "Schema must have at least one property";
6
5
  const MESSAGE_SCHEMA_INVALID_PROPERTY_DISALLOWED = "'<key>.<property>' property is not allowed for schemas in $type";
6
+ const MESSAGE_SCHEMA_INVALID_PROPERTY_NULLABLE = "'<>' property must not be 'null' or 'undefined'";
7
7
  const MESSAGE_SCHEMA_INVALID_PROPERTY_REQUIRED = "'<>.$required' property must be a boolean";
8
8
  const MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE = "'<>' property must be of a valid type";
9
9
  const MESSAGE_SCHEMA_INVALID_TYPE = "Schema must be an object";
@@ -36,4 +36,4 @@ const TYPE_ALL = new Set([
36
36
  TYPE_UNDEFINED
37
37
  ]);
38
38
  //#endregion
39
- export { ERROR_NAME, EXPRESSION_PROPERTY, MESSAGE_CONSTRUCTOR, MESSAGE_SCHEMA_INVALID_EMPTY, MESSAGE_SCHEMA_INVALID_PROPERTY_DISALLOWED, MESSAGE_SCHEMA_INVALID_PROPERTY_REQUIRED, MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE, MESSAGE_SCHEMA_INVALID_TYPE, MESSAGE_VALIDATOR_INVALID_KEY, MESSAGE_VALIDATOR_INVALID_TYPE, MESSAGE_VALIDATOR_INVALID_VALUE, PROPERTY_REQUIRED, PROPERTY_TYPE, PROPERTY_VALIDATORS, SCHEMATIC_NAME, TEMPLATE_PATTERN, TEMPLATE_PATTERN_KEY, TEMPLATE_PATTERN_PROPERTY, TYPE_ALL, TYPE_OBJECT, TYPE_UNDEFINED, VALIDATABLE_TYPES };
39
+ export { ERROR_NAME, MESSAGE_CONSTRUCTOR, MESSAGE_SCHEMA_INVALID_EMPTY, MESSAGE_SCHEMA_INVALID_PROPERTY_DISALLOWED, MESSAGE_SCHEMA_INVALID_PROPERTY_NULLABLE, MESSAGE_SCHEMA_INVALID_PROPERTY_REQUIRED, MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE, MESSAGE_SCHEMA_INVALID_TYPE, MESSAGE_VALIDATOR_INVALID_KEY, MESSAGE_VALIDATOR_INVALID_TYPE, MESSAGE_VALIDATOR_INVALID_VALUE, PROPERTY_REQUIRED, PROPERTY_TYPE, PROPERTY_VALIDATORS, SCHEMATIC_NAME, TEMPLATE_PATTERN, TEMPLATE_PATTERN_KEY, TEMPLATE_PATTERN_PROPERTY, TYPE_ALL, TYPE_OBJECT, TYPE_UNDEFINED, VALIDATABLE_TYPES };
package/dist/index.d.mts CHANGED
@@ -83,7 +83,7 @@ type InferSchemaEntry<Value> = Value extends (infer Item)[] ? InferSchemaEntryVa
83
83
  *
84
84
  * @template Value - single schema entry
85
85
  */
86
- type InferSchemaEntryValue<Value> = Value extends Constructor<infer Instance> ? Instance : Value extends Schematic<infer Model> ? Model : Value extends SchemaProperty ? InferPropertyType<Value['$type']> : Value extends NestedSchema ? Infer<Omit<Value, '$required'>> : Value extends ValueName ? Values[Value & ValueName] : Value extends Schema ? Infer<Value> : never;
86
+ type InferSchemaEntryValue<Value> = Value extends Constructor<infer Instance> ? Instance : Value extends Schematic<infer Model> ? Model : Value extends SchemaProperty ? InferPropertyType<Value['$type']> : Value extends PlainSchema ? Infer<Value & Schema> : Value extends ValueName ? Values[Value & ValueName] : Value extends Schema ? Infer<Value> : never;
87
87
  /**
88
88
  * Determines whether a schema entry is optional
89
89
  *
@@ -91,11 +91,7 @@ type InferSchemaEntryValue<Value> = Value extends Constructor<infer Instance> ?
91
91
  *
92
92
  * @template Value - Schema entry to check
93
93
  */
94
- type IsOptionalProperty<Value> = Value extends SchemaProperty ? Value['$required'] extends false ? true : false : Value extends {
95
- $required?: boolean;
96
- } ? Value extends {
97
- $required: false;
98
- } ? true : false : false;
94
+ type IsOptionalProperty<Value> = Value extends SchemaProperty ? Value['$required'] extends false ? true : false : false;
99
95
  /**
100
96
  * Extracts the last member from a union type by leveraging intersection of function return types
101
97
  *
@@ -114,24 +110,6 @@ type MapToValueTypes<Value extends unknown[]> = Value extends [infer Head, ...in
114
110
  * @template Value - Tuple of types to map
115
111
  */
116
112
  type MapToSchemaPropertyTypes<Value extends unknown[]> = Value extends [infer Head, ...infer Tail] ? [ToSchemaPropertyTypeEach<Head>, ...MapToSchemaPropertyTypes<Tail>] : [];
117
- /**
118
- * A nested schema definition that may include a `$required` flag alongside arbitrary string-keyed properties
119
- *
120
- * @example
121
- * ```ts
122
- * const address: NestedSchema = {
123
- * $required: false,
124
- * street: 'string',
125
- * city: 'string',
126
- * };
127
- * ```
128
- */
129
- type NestedSchema = {
130
- /**
131
- * Whether the nested schema is required (defaults to `true`)
132
- */
133
- $required?: boolean;
134
- } & Schema;
135
113
  /**
136
114
  * Extracts keys from an object type that are optional
137
115
  *
@@ -142,7 +120,11 @@ type OptionalKeys<Value> = { [Key in keyof Value]-?: {} extends Pick<Value, Key>
142
120
  * A generic schema allowing {@link NestedSchema}, {@link SchemaEntry}, or arrays of {@link SchemaEntry} as values
143
121
  */
144
122
  type PlainSchema = {
145
- [key: string]: NestedSchema | SchemaEntry | SchemaEntry[];
123
+ [key: string]: PlainSchema | SchemaEntry | SchemaEntry[] | undefined;
124
+ } & {
125
+ $required?: never;
126
+ $type?: never;
127
+ $validators?: never;
146
128
  };
147
129
  /**
148
130
  * A map of optional validator functions keyed by {@link ValueName}, used to add custom validation to {@link SchemaProperty} definitions
@@ -183,12 +165,12 @@ type Schema = SchemaIndex;
183
165
  *
184
166
  * Can be a {@link Constructor}, nested {@link Schema}, {@link SchemaProperty}, {@link Schematic}, {@link ValueName} string, or a custom validator function
185
167
  */
186
- type SchemaEntry = Constructor | Schema | SchemaProperty | Schematic<unknown> | ValueName | ((value: unknown) => boolean);
168
+ type SchemaEntry = Constructor | PlainSchema | SchemaProperty | Schematic<unknown> | ValueName | ((value: unknown) => boolean);
187
169
  /**
188
170
  * Index signature interface backing {@link Schema}, allowing string-keyed entries of {@link NestedSchema}, {@link SchemaEntry}, or arrays of {@link SchemaEntry}
189
171
  */
190
172
  interface SchemaIndex {
191
- [key: string]: NestedSchema | SchemaEntry | SchemaEntry[];
173
+ [key: string]: PlainSchema | SchemaEntry | SchemaEntry[];
192
174
  }
193
175
  /**
194
176
  * A property definition with explicit type(s), an optional requirement flag, and optional validators
@@ -226,12 +208,7 @@ type SchemaProperty = {
226
208
  */
227
209
  type SchemaPropertyType = Constructor | PlainSchema | Schematic<unknown> | ValueName | ((value: unknown) => boolean);
228
210
  /**
229
- * A custom error class for schema validation failures, with its `name` set to {@link ERROR_NAME}
230
- *
231
- * @example
232
- * ```ts
233
- * throw new SchematicError('Expected a string, received a number');
234
- * ```
211
+ * A custom error class for schematic validation failures
235
212
  */
236
213
  declare class SchematicError extends Error {
237
214
  constructor(message: string);
@@ -251,7 +228,7 @@ type ToSchemaPropertyType<Value> = UnwrapSingle<DeduplicateTuple<MapToSchemaProp
251
228
  *
252
229
  * @template Value - type to convert
253
230
  */
254
- type ToSchemaPropertyTypeEach<Value> = Value extends NestedSchema ? Omit<Value, '$required'> : Value extends PlainObject ? TypedSchema<Value> : ToValueType<Value>;
231
+ type ToSchemaPropertyTypeEach<Value> = Value extends PlainObject ? TypedSchema<Value> : ToValueType<Value>;
255
232
  /**
256
233
  * Converts a type into its corresponding {@link ValueName}-representation
257
234
  *
@@ -513,23 +490,23 @@ declare class Schematic<Model> {
513
490
  constructor(properties: ValidatedProperty[]);
514
491
  /**
515
492
  * Does the value match the schema?
516
- * @param value - Value to validate
493
+ * @param value Value to validate
517
494
  * @returns `true` if the value matches the schema, otherwise `false`
518
495
  */
519
496
  is(value: unknown): value is Model;
520
497
  }
521
498
  /**
522
499
  * Create a schematic from a schema
523
- * @template Model - Schema type
524
- * @param schema - Schema to create the schematic from
500
+ * @template Model Schema type
501
+ * @param schema Schema to create the schematic from
525
502
  * @throws Throws {@link SchematicError} if the schema can not be converted into a schematic
526
503
  * @returns A schematic for the given schema
527
504
  */
528
505
  declare function schematic<Model extends Schema>(schema: Model): Schematic<Infer<Model>>;
529
506
  /**
530
507
  * Create a schematic from a typed schema
531
- * @template Model - Existing type
532
- * @param schema - Typed schema to create the schematic from
508
+ * @template Model Existing type
509
+ * @param schema Typed schema to create the schematic from
533
510
  * @throws Throws {@link SchematicError} if the schema can not be converted into a schematic
534
511
  * @returns A schematic for the given typed schema
535
512
  */
package/dist/index.mjs CHANGED
@@ -2,10 +2,10 @@ import { isConstructor, isPlainObject } from "@oscarpalmer/atoms/is";
2
2
  import { join } from "@oscarpalmer/atoms/string";
3
3
  //#region src/constants.ts
4
4
  const ERROR_NAME = "SchematicError";
5
- const EXPRESSION_PROPERTY = /(^|\.)\$(required|type|validators)(\.|$)/;
6
5
  const MESSAGE_CONSTRUCTOR = "Expected a constructor function";
7
6
  const MESSAGE_SCHEMA_INVALID_EMPTY = "Schema must have at least one property";
8
7
  const MESSAGE_SCHEMA_INVALID_PROPERTY_DISALLOWED = "'<key>.<property>' property is not allowed for schemas in $type";
8
+ const MESSAGE_SCHEMA_INVALID_PROPERTY_NULLABLE = "'<>' property must not be 'null' or 'undefined'";
9
9
  const MESSAGE_SCHEMA_INVALID_PROPERTY_REQUIRED = "'<>.$required' property must be a boolean";
10
10
  const MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE = "'<>' property must be of a valid type";
11
11
  const MESSAGE_SCHEMA_INVALID_TYPE = "Schema must be an object";
@@ -61,12 +61,7 @@ function isSchematic(value) {
61
61
  //#endregion
62
62
  //#region src/models.ts
63
63
  /**
64
- * A custom error class for schema validation failures, with its `name` set to {@link ERROR_NAME}
65
- *
66
- * @example
67
- * ```ts
68
- * throw new SchematicError('Expected a string, received a number');
69
- * ```
64
+ * A custom error class for schematic validation failures
70
65
  */
71
66
  var SchematicError = class extends Error {
72
67
  constructor(message) {
@@ -92,8 +87,8 @@ function getProperties(original, prefix, fromType) {
92
87
  const properties = [];
93
88
  for (let keyIndex = 0; keyIndex < keysLength; keyIndex += 1) {
94
89
  const key = keys[keyIndex];
95
- if (EXPRESSION_PROPERTY.test(key)) continue;
96
90
  const value = original[key];
91
+ if (value == null) throw new SchematicError(MESSAGE_SCHEMA_INVALID_PROPERTY_NULLABLE.replace("<>", join([prefix, key], ".")));
97
92
  const types = [];
98
93
  let required = true;
99
94
  let validators = {};
@@ -213,7 +208,7 @@ var Schematic = class {
213
208
  }
214
209
  /**
215
210
  * Does the value match the schema?
216
- * @param value - Value to validate
211
+ * @param value Value to validate
217
212
  * @returns `true` if the value matches the schema, otherwise `false`
218
213
  */
219
214
  is(value) {
package/dist/models.d.mts CHANGED
@@ -84,7 +84,7 @@ type InferSchemaEntry<Value> = Value extends (infer Item)[] ? InferSchemaEntryVa
84
84
  *
85
85
  * @template Value - single schema entry
86
86
  */
87
- type InferSchemaEntryValue<Value> = Value extends Constructor<infer Instance> ? Instance : Value extends Schematic<infer Model> ? Model : Value extends SchemaProperty ? InferPropertyType<Value['$type']> : Value extends NestedSchema ? Infer<Omit<Value, '$required'>> : Value extends ValueName ? Values[Value & ValueName] : Value extends Schema ? Infer<Value> : never;
87
+ type InferSchemaEntryValue<Value> = Value extends Constructor<infer Instance> ? Instance : Value extends Schematic<infer Model> ? Model : Value extends SchemaProperty ? InferPropertyType<Value['$type']> : Value extends PlainSchema ? Infer<Value & Schema> : Value extends ValueName ? Values[Value & ValueName] : Value extends Schema ? Infer<Value> : never;
88
88
  /**
89
89
  * Determines whether a schema entry is optional
90
90
  *
@@ -92,11 +92,7 @@ type InferSchemaEntryValue<Value> = Value extends Constructor<infer Instance> ?
92
92
  *
93
93
  * @template Value - Schema entry to check
94
94
  */
95
- type IsOptionalProperty<Value> = Value extends SchemaProperty ? Value['$required'] extends false ? true : false : Value extends {
96
- $required?: boolean;
97
- } ? Value extends {
98
- $required: false;
99
- } ? true : false : false;
95
+ type IsOptionalProperty<Value> = Value extends SchemaProperty ? Value['$required'] extends false ? true : false : false;
100
96
  /**
101
97
  * Extracts the last member from a union type by leveraging intersection of function return types
102
98
  *
@@ -115,24 +111,6 @@ type MapToValueTypes<Value extends unknown[]> = Value extends [infer Head, ...in
115
111
  * @template Value - Tuple of types to map
116
112
  */
117
113
  type MapToSchemaPropertyTypes<Value extends unknown[]> = Value extends [infer Head, ...infer Tail] ? [ToSchemaPropertyTypeEach<Head>, ...MapToSchemaPropertyTypes<Tail>] : [];
118
- /**
119
- * A nested schema definition that may include a `$required` flag alongside arbitrary string-keyed properties
120
- *
121
- * @example
122
- * ```ts
123
- * const address: NestedSchema = {
124
- * $required: false,
125
- * street: 'string',
126
- * city: 'string',
127
- * };
128
- * ```
129
- */
130
- type NestedSchema = {
131
- /**
132
- * Whether the nested schema is required (defaults to `true`)
133
- */
134
- $required?: boolean;
135
- } & Schema;
136
114
  /**
137
115
  * Extracts keys from an object type that are optional
138
116
  *
@@ -143,7 +121,11 @@ type OptionalKeys<Value> = { [Key in keyof Value]-?: {} extends Pick<Value, Key>
143
121
  * A generic schema allowing {@link NestedSchema}, {@link SchemaEntry}, or arrays of {@link SchemaEntry} as values
144
122
  */
145
123
  type PlainSchema = {
146
- [key: string]: NestedSchema | SchemaEntry | SchemaEntry[];
124
+ [key: string]: PlainSchema | SchemaEntry | SchemaEntry[] | undefined;
125
+ } & {
126
+ $required?: never;
127
+ $type?: never;
128
+ $validators?: never;
147
129
  };
148
130
  /**
149
131
  * A map of optional validator functions keyed by {@link ValueName}, used to add custom validation to {@link SchemaProperty} definitions
@@ -184,12 +166,12 @@ type Schema = SchemaIndex;
184
166
  *
185
167
  * Can be a {@link Constructor}, nested {@link Schema}, {@link SchemaProperty}, {@link Schematic}, {@link ValueName} string, or a custom validator function
186
168
  */
187
- type SchemaEntry = Constructor | Schema | SchemaProperty | Schematic<unknown> | ValueName | ((value: unknown) => boolean);
169
+ type SchemaEntry = Constructor | PlainSchema | SchemaProperty | Schematic<unknown> | ValueName | ((value: unknown) => boolean);
188
170
  /**
189
171
  * Index signature interface backing {@link Schema}, allowing string-keyed entries of {@link NestedSchema}, {@link SchemaEntry}, or arrays of {@link SchemaEntry}
190
172
  */
191
173
  interface SchemaIndex {
192
- [key: string]: NestedSchema | SchemaEntry | SchemaEntry[];
174
+ [key: string]: PlainSchema | SchemaEntry | SchemaEntry[];
193
175
  }
194
176
  /**
195
177
  * A property definition with explicit type(s), an optional requirement flag, and optional validators
@@ -227,12 +209,7 @@ type SchemaProperty = {
227
209
  */
228
210
  type SchemaPropertyType = Constructor | PlainSchema | Schematic<unknown> | ValueName | ((value: unknown) => boolean);
229
211
  /**
230
- * A custom error class for schema validation failures, with its `name` set to {@link ERROR_NAME}
231
- *
232
- * @example
233
- * ```ts
234
- * throw new SchematicError('Expected a string, received a number');
235
- * ```
212
+ * A custom error class for schematic validation failures
236
213
  */
237
214
  declare class SchematicError extends Error {
238
215
  constructor(message: string);
@@ -252,7 +229,7 @@ type ToSchemaPropertyType<Value> = UnwrapSingle<DeduplicateTuple<MapToSchemaProp
252
229
  *
253
230
  * @template Value - type to convert
254
231
  */
255
- type ToSchemaPropertyTypeEach<Value> = Value extends NestedSchema ? Omit<Value, '$required'> : Value extends PlainObject ? TypedSchema<Value> : ToValueType<Value>;
232
+ type ToSchemaPropertyTypeEach<Value> = Value extends PlainObject ? TypedSchema<Value> : ToValueType<Value>;
256
233
  /**
257
234
  * Converts a type into its corresponding {@link ValueName}-representation
258
235
  *
@@ -504,4 +481,4 @@ type Values = {
504
481
  undefined: undefined;
505
482
  };
506
483
  //#endregion
507
- export { Infer, NestedSchema, Schema, SchemaProperty, SchematicError, TypedPropertyOptional, TypedPropertyRequired, TypedSchema, ValidatedProperty, ValidatedPropertyType, ValidatedPropertyValidators, ValueName, Values };
484
+ export { Infer, Schema, SchemaProperty, SchematicError, TypedPropertyOptional, TypedPropertyRequired, TypedSchema, ValidatedProperty, ValidatedPropertyType, ValidatedPropertyValidators, ValueName, Values };
package/dist/models.mjs CHANGED
@@ -1,12 +1,7 @@
1
1
  import { ERROR_NAME } from "./constants.mjs";
2
2
  //#region src/models.ts
3
3
  /**
4
- * A custom error class for schema validation failures, with its `name` set to {@link ERROR_NAME}
5
- *
6
- * @example
7
- * ```ts
8
- * throw new SchematicError('Expected a string, received a number');
9
- * ```
4
+ * A custom error class for schematic validation failures
10
5
  */
11
6
  var SchematicError = class extends Error {
12
7
  constructor(message) {
@@ -11,23 +11,23 @@ declare class Schematic<Model> {
11
11
  constructor(properties: ValidatedProperty[]);
12
12
  /**
13
13
  * Does the value match the schema?
14
- * @param value - Value to validate
14
+ * @param value Value to validate
15
15
  * @returns `true` if the value matches the schema, otherwise `false`
16
16
  */
17
17
  is(value: unknown): value is Model;
18
18
  }
19
19
  /**
20
20
  * Create a schematic from a schema
21
- * @template Model - Schema type
22
- * @param schema - Schema to create the schematic from
21
+ * @template Model Schema type
22
+ * @param schema Schema to create the schematic from
23
23
  * @throws Throws {@link SchematicError} if the schema can not be converted into a schematic
24
24
  * @returns A schematic for the given schema
25
25
  */
26
26
  declare function schematic<Model extends Schema>(schema: Model): Schematic<Infer<Model>>;
27
27
  /**
28
28
  * Create a schematic from a typed schema
29
- * @template Model - Existing type
30
- * @param schema - Typed schema to create the schematic from
29
+ * @template Model Existing type
30
+ * @param schema Typed schema to create the schematic from
31
31
  * @throws Throws {@link SchematicError} if the schema can not be converted into a schematic
32
32
  * @returns A schematic for the given typed schema
33
33
  */
@@ -16,7 +16,7 @@ var Schematic = class {
16
16
  }
17
17
  /**
18
18
  * Does the value match the schema?
19
- * @param value - Value to validate
19
+ * @param value Value to validate
20
20
  * @returns `true` if the value matches the schema, otherwise `false`
21
21
  */
22
22
  is(value) {
@@ -1,4 +1,4 @@
1
- import { EXPRESSION_PROPERTY, MESSAGE_SCHEMA_INVALID_EMPTY, MESSAGE_SCHEMA_INVALID_PROPERTY_DISALLOWED, MESSAGE_SCHEMA_INVALID_PROPERTY_REQUIRED, MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE, MESSAGE_VALIDATOR_INVALID_KEY, MESSAGE_VALIDATOR_INVALID_TYPE, MESSAGE_VALIDATOR_INVALID_VALUE, PROPERTY_REQUIRED, PROPERTY_TYPE, PROPERTY_VALIDATORS, TEMPLATE_PATTERN_KEY, TEMPLATE_PATTERN_PROPERTY, TYPE_ALL, TYPE_OBJECT, TYPE_UNDEFINED, VALIDATABLE_TYPES } from "../constants.mjs";
1
+ import { MESSAGE_SCHEMA_INVALID_EMPTY, MESSAGE_SCHEMA_INVALID_PROPERTY_DISALLOWED, MESSAGE_SCHEMA_INVALID_PROPERTY_NULLABLE, MESSAGE_SCHEMA_INVALID_PROPERTY_REQUIRED, MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE, MESSAGE_VALIDATOR_INVALID_KEY, MESSAGE_VALIDATOR_INVALID_TYPE, MESSAGE_VALIDATOR_INVALID_VALUE, PROPERTY_REQUIRED, PROPERTY_TYPE, PROPERTY_VALIDATORS, TEMPLATE_PATTERN_KEY, TEMPLATE_PATTERN_PROPERTY, TYPE_ALL, TYPE_OBJECT, TYPE_UNDEFINED, VALIDATABLE_TYPES } from "../constants.mjs";
2
2
  import { instanceOf, isSchematic } from "../helpers.mjs";
3
3
  import { SchematicError } from "../models.mjs";
4
4
  import { isConstructor, isPlainObject } from "@oscarpalmer/atoms/is";
@@ -20,8 +20,8 @@ function getProperties(original, prefix, fromType) {
20
20
  const properties = [];
21
21
  for (let keyIndex = 0; keyIndex < keysLength; keyIndex += 1) {
22
22
  const key = keys[keyIndex];
23
- if (EXPRESSION_PROPERTY.test(key)) continue;
24
23
  const value = original[key];
24
+ if (value == null) throw new SchematicError(MESSAGE_SCHEMA_INVALID_PROPERTY_NULLABLE.replace("<>", join([prefix, key], ".")));
25
25
  const types = [];
26
26
  let required = true;
27
27
  let validators = {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oscarpalmer/jhunal",
3
- "version": "0.16.0",
3
+ "version": "0.17.0",
4
4
  "description": "Flies free beneath the glistening moons…",
5
5
  "keywords": [
6
6
  "schema",
@@ -38,7 +38,7 @@
38
38
  "watch": "npx vite build --watch"
39
39
  },
40
40
  "dependencies": {
41
- "@oscarpalmer/atoms": "^0.166"
41
+ "@oscarpalmer/atoms": "^0.168"
42
42
  },
43
43
  "devDependencies": {
44
44
  "@types/node": "^25.5",
@@ -51,4 +51,4 @@
51
51
  "vitest": "npm:@voidzero-dev/vite-plus-test@latest"
52
52
  },
53
53
  "packageManager": "npm@11.11.1"
54
- }
54
+ }
package/src/constants.ts CHANGED
@@ -2,8 +2,6 @@ import type {ValueName} from './models';
2
2
 
3
3
  export const ERROR_NAME = 'SchematicError';
4
4
 
5
- export const EXPRESSION_PROPERTY = /(^|\.)\$(required|type|validators)(\.|$)/;
6
-
7
5
  export const MESSAGE_CONSTRUCTOR = 'Expected a constructor function';
8
6
 
9
7
  export const MESSAGE_SCHEMA_INVALID_EMPTY = 'Schema must have at least one property';
@@ -11,6 +9,9 @@ export const MESSAGE_SCHEMA_INVALID_EMPTY = 'Schema must have at least one prope
11
9
  export const MESSAGE_SCHEMA_INVALID_PROPERTY_DISALLOWED =
12
10
  "'<key>.<property>' property is not allowed for schemas in $type";
13
11
 
12
+ export const MESSAGE_SCHEMA_INVALID_PROPERTY_NULLABLE =
13
+ "'<>' property must not be 'null' or 'undefined'";
14
+
14
15
  export const MESSAGE_SCHEMA_INVALID_PROPERTY_REQUIRED = "'<>.$required' property must be a boolean";
15
16
 
16
17
  export const MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE = "'<>' property must be of a valid type";
package/src/models.ts CHANGED
@@ -135,8 +135,8 @@ type InferSchemaEntryValue<Value> =
135
135
  ? Model
136
136
  : Value extends SchemaProperty
137
137
  ? InferPropertyType<Value['$type']>
138
- : Value extends NestedSchema
139
- ? Infer<Omit<Value, '$required'>>
138
+ : Value extends PlainSchema
139
+ ? Infer<Value & Schema>
140
140
  : Value extends ValueName
141
141
  ? Values[Value & ValueName]
142
142
  : Value extends Schema
@@ -154,11 +154,7 @@ type IsOptionalProperty<Value> = Value extends SchemaProperty
154
154
  ? Value['$required'] extends false
155
155
  ? true
156
156
  : false
157
- : Value extends {$required?: boolean}
158
- ? Value extends {$required: false}
159
- ? true
160
- : false
161
- : false;
157
+ : false;
162
158
 
163
159
  /**
164
160
  * Extracts the last member from a union type by leveraging intersection of function return types
@@ -188,25 +184,6 @@ type MapToSchemaPropertyTypes<Value extends unknown[]> = Value extends [infer He
188
184
  ? [ToSchemaPropertyTypeEach<Head>, ...MapToSchemaPropertyTypes<Tail>]
189
185
  : [];
190
186
 
191
- /**
192
- * A nested schema definition that may include a `$required` flag alongside arbitrary string-keyed properties
193
- *
194
- * @example
195
- * ```ts
196
- * const address: NestedSchema = {
197
- * $required: false,
198
- * street: 'string',
199
- * city: 'string',
200
- * };
201
- * ```
202
- */
203
- export type NestedSchema = {
204
- /**
205
- * Whether the nested schema is required (defaults to `true`)
206
- */
207
- $required?: boolean;
208
- } & Schema;
209
-
210
187
  /**
211
188
  * Extracts keys from an object type that are optional
212
189
  *
@@ -220,7 +197,11 @@ type OptionalKeys<Value> = {
220
197
  * A generic schema allowing {@link NestedSchema}, {@link SchemaEntry}, or arrays of {@link SchemaEntry} as values
221
198
  */
222
199
  type PlainSchema = {
223
- [key: string]: NestedSchema | SchemaEntry | SchemaEntry[];
200
+ [key: string]: PlainSchema | SchemaEntry | SchemaEntry[] | undefined;
201
+ } & {
202
+ $required?: never;
203
+ $type?: never;
204
+ $validators?: never;
224
205
  };
225
206
 
226
207
  /**
@@ -271,7 +252,7 @@ export type Schema = SchemaIndex;
271
252
  */
272
253
  type SchemaEntry =
273
254
  | Constructor
274
- | Schema
255
+ | PlainSchema
275
256
  | SchemaProperty
276
257
  | Schematic<unknown>
277
258
  | ValueName
@@ -281,7 +262,7 @@ type SchemaEntry =
281
262
  * Index signature interface backing {@link Schema}, allowing string-keyed entries of {@link NestedSchema}, {@link SchemaEntry}, or arrays of {@link SchemaEntry}
282
263
  */
283
264
  interface SchemaIndex {
284
- [key: string]: NestedSchema | SchemaEntry | SchemaEntry[];
265
+ [key: string]: PlainSchema | SchemaEntry | SchemaEntry[];
285
266
  }
286
267
 
287
268
  /**
@@ -327,12 +308,7 @@ type SchemaPropertyType =
327
308
  | ((value: unknown) => boolean);
328
309
 
329
310
  /**
330
- * A custom error class for schema validation failures, with its `name` set to {@link ERROR_NAME}
331
- *
332
- * @example
333
- * ```ts
334
- * throw new SchematicError('Expected a string, received a number');
335
- * ```
311
+ * A custom error class for schematic validation failures
336
312
  */
337
313
  export class SchematicError extends Error {
338
314
  constructor(message: string) {
@@ -360,11 +336,9 @@ type ToSchemaPropertyType<Value> = UnwrapSingle<
360
336
  *
361
337
  * @template Value - type to convert
362
338
  */
363
- type ToSchemaPropertyTypeEach<Value> = Value extends NestedSchema
364
- ? Omit<Value, '$required'>
365
- : Value extends PlainObject
366
- ? TypedSchema<Value>
367
- : ToValueType<Value>;
339
+ type ToSchemaPropertyTypeEach<Value> = Value extends PlainObject
340
+ ? TypedSchema<Value>
341
+ : ToValueType<Value>;
368
342
 
369
343
  /**
370
344
  * Converts a type into its corresponding {@link ValueName}-representation
package/src/schematic.ts CHANGED
@@ -30,7 +30,7 @@ export class Schematic<Model> {
30
30
 
31
31
  /**
32
32
  * Does the value match the schema?
33
- * @param value - Value to validate
33
+ * @param value Value to validate
34
34
  * @returns `true` if the value matches the schema, otherwise `false`
35
35
  */
36
36
  is(value: unknown): value is Model {
@@ -40,8 +40,8 @@ export class Schematic<Model> {
40
40
 
41
41
  /**
42
42
  * Create a schematic from a schema
43
- * @template Model - Schema type
44
- * @param schema - Schema to create the schematic from
43
+ * @template Model Schema type
44
+ * @param schema Schema to create the schematic from
45
45
  * @throws Throws {@link SchematicError} if the schema can not be converted into a schematic
46
46
  * @returns A schematic for the given schema
47
47
  */
@@ -49,8 +49,8 @@ export function schematic<Model extends Schema>(schema: Model): Schematic<Infer<
49
49
 
50
50
  /**
51
51
  * Create a schematic from a typed schema
52
- * @template Model - Existing type
53
- * @param schema - Typed schema to create the schematic from
52
+ * @template Model Existing type
53
+ * @param schema Typed schema to create the schematic from
54
54
  * @throws Throws {@link SchematicError} if the schema can not be converted into a schematic
55
55
  * @returns A schematic for the given typed schema
56
56
  */
@@ -2,9 +2,9 @@ import {isConstructor, isPlainObject} from '@oscarpalmer/atoms/is';
2
2
  import type {PlainObject} from '@oscarpalmer/atoms/models';
3
3
  import {join} from '@oscarpalmer/atoms/string';
4
4
  import {
5
- EXPRESSION_PROPERTY,
6
5
  MESSAGE_SCHEMA_INVALID_EMPTY,
7
6
  MESSAGE_SCHEMA_INVALID_PROPERTY_DISALLOWED,
7
+ MESSAGE_SCHEMA_INVALID_PROPERTY_NULLABLE,
8
8
  MESSAGE_SCHEMA_INVALID_PROPERTY_REQUIRED,
9
9
  MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE,
10
10
  MESSAGE_VALIDATOR_INVALID_KEY,
@@ -74,12 +74,17 @@ export function getProperties(
74
74
  for (let keyIndex = 0; keyIndex < keysLength; keyIndex += 1) {
75
75
  const key = keys[keyIndex];
76
76
 
77
- if (EXPRESSION_PROPERTY.test(key)) {
78
- continue;
79
- }
80
-
81
77
  const value = original[key];
82
78
 
79
+ if (value == null) {
80
+ throw new SchematicError(
81
+ MESSAGE_SCHEMA_INVALID_PROPERTY_NULLABLE.replace(
82
+ TEMPLATE_PATTERN,
83
+ join([prefix, key], '.'),
84
+ ),
85
+ );
86
+ }
87
+
83
88
  const types: ValidatedPropertyType[] = [];
84
89
 
85
90
  let required = true;