@strictly/define 0.0.7 → 0.0.9

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.
Files changed (37) hide show
  1. package/.out/index.d.ts +2 -0
  2. package/.out/index.js +2 -0
  3. package/.out/transformers/flatteners/flatten_type_to.d.ts +1 -1
  4. package/.out/transformers/flatteners/flatten_type_to.js +1 -1
  5. package/.out/tsconfig.tsbuildinfo +1 -1
  6. package/.out/types/builders.d.ts +3 -3
  7. package/.out/types/builders.js +4 -6
  8. package/.out/validation/validator.d.ts +11 -9
  9. package/.out/validation/validator.js +6 -1
  10. package/.out/validation/validators/defined_validator.d.ts +1 -1
  11. package/.out/validation/validators/minimum_string_length_validator.d.ts +1 -1
  12. package/.out/validation/validators/minimum_string_length_validator.js +1 -1
  13. package/.out/validation/validators/optional_validator_proxy.d.ts +13 -0
  14. package/.out/validation/validators/optional_validator_proxy.js +26 -0
  15. package/.out/validation/validators/regexp_validator.d.ts +29 -0
  16. package/.out/validation/validators/regexp_validator.js +37 -0
  17. package/.out/validation/validators/specs/minimum_string_length_validator.tests.d.ts +1 -0
  18. package/.out/validation/validators/specs/minimum_string_length_validator.tests.js +69 -0
  19. package/.out/validation/validators/specs/regexp_validator.tests.d.ts +1 -0
  20. package/.out/validation/validators/specs/regexp_validator.tests.js +98 -0
  21. package/.turbo/turbo-build.log +8 -8
  22. package/.turbo/turbo-check-types.log +1 -1
  23. package/dist/index.cjs +113 -33
  24. package/dist/index.d.cts +58 -15
  25. package/dist/index.d.ts +58 -15
  26. package/dist/index.js +109 -33
  27. package/index.ts +2 -0
  28. package/package.json +1 -1
  29. package/transformers/flatteners/flatten_type_to.ts +2 -2
  30. package/types/builders.ts +11 -14
  31. package/validation/validator.ts +26 -8
  32. package/validation/validators/defined_validator.ts +1 -3
  33. package/validation/validators/minimum_string_length_validator.ts +3 -3
  34. package/validation/validators/optional_validator_proxy.ts +52 -0
  35. package/validation/validators/regexp_validator.ts +61 -0
  36. package/validation/validators/specs/minimum_string_length_validator.tests.ts +76 -0
  37. package/validation/validators/specs/regexp_validator.tests.ts +108 -0
package/dist/index.d.cts CHANGED
@@ -266,25 +266,27 @@ type InternalFlattenedTypeDefsOfUnionChildren<T extends ValidatingUnionTypeDef,
266
266
  readonly [K in keyof Unions]: InternalFlattenedTypeDefsOfChildren<Unions[K], SegmentOverride, Path, `${Qualifier}${K}:`, Depth>;
267
267
  }[keyof Unions]> : never : never;
268
268
 
269
+ type Annotations = {
270
+ readonly required: boolean;
271
+ readonly readonly: boolean;
272
+ };
269
273
  type FunctionalValidator<V = any, E = any, ValuePath extends string = any, Context = any> = (v: V, valuePath: ValuePath, context: Context) => E | null;
270
274
  type AnnotatedValidator<V = any, E = any, ValuePath extends string = any, Context = any> = {
271
275
  readonly validate: (v: V, valuePath: ValuePath, context: Context) => E | null;
272
- readonly annotations: (valuePath: ValuePath, context: Context) => {
273
- readonly required: boolean;
274
- readonly readonly: boolean;
275
- };
276
+ readonly annotations: (valuePath: ValuePath, context: Context) => Annotations;
276
277
  };
277
278
  type Validator<V = any, E = any, ValuePath extends string = any, Context = any> = FunctionalValidator<V, E, ValuePath, Context> | AnnotatedValidator<V, E, ValuePath, Context>;
278
279
  type ErrorOfValidator<V extends Validator> = V extends Validator<infer _V, infer E> ? E : never;
279
280
  type ValidationError<Type extends string, Data = {}> = Simplify<{
280
281
  type: Type;
281
282
  } & Data>;
282
- declare function isFunctionalValidator(v: Validator): v is FunctionalValidator;
283
- declare function isAnnotatedValidator(v: Validator): v is AnnotatedValidator;
283
+ declare function isFunctionalValidator<V, E, ValuePath extends string, Context>(v: Validator<V, E, ValuePath, Context>): v is FunctionalValidator<V, E, ValuePath, Context>;
284
+ declare function isAnnotatedValidator<V, E, ValuePath extends string, Context>(v: Validator<V, E, ValuePath, Context>): v is AnnotatedValidator<V, E, ValuePath, Context>;
284
285
  declare function validate<V = any, E = any, ValuePath extends string = any, Context = any>(validator: Validator<V, E, ValuePath, Context>, v: V, valuePath: ValuePath, context: Context): E | null;
285
- declare function annotations<V = any, E = any, ValuePath extends string = any, Context = any>(validator: Validator<V, E, ValuePath, Context>, valuePath: ValuePath, context: Context): {
286
- readonly required: boolean;
287
- readonly readonly: boolean;
286
+ declare function annotations<V = any, E = any, ValuePath extends string = any, Context = any>(validator: Validator<V, E, ValuePath, Context>, valuePath: ValuePath, context: Context): Annotations;
287
+ declare function mergeAnnotations(a1: Annotations, a2: Annotations): {
288
+ readonly: boolean;
289
+ required: boolean;
288
290
  };
289
291
 
290
292
  type ValidatorOfValidatingType<T extends ValidatingTypeDef, ValuePath extends string, Context> = T extends ValidatingTypeDef<infer E> ? Validator<ValueOfTypeDef<ReadonlyOfTypeDef<T>>, E, ValuePath, Context> : never;
@@ -402,9 +404,8 @@ type ValidatingUnionTypeDefWithError<T extends ValidatingUnionTypeDef, E2> = T e
402
404
  declare class TypeDefBuilder<T extends ValidatingTypeDef> implements ValidatingType<T> {
403
405
  readonly definition: T;
404
406
  constructor(definition: T);
405
- enforce<E2>(rule: Rule<E2, ValueOfType<Type<T>>>): TypeDefBuilder<ValidatingTypeDefWithError<T, E2>>;
406
- required(): TypeDefBuilder<ValidatingTypeDefWithError<T, never>>;
407
- required<RequiredError>(rule: Rule<RequiredError, ValueOfType<typeof this.narrow>>): TypeDefBuilder<ValidatingTypeDefWithError<T, RequiredError>>;
407
+ enforce<E2>(rule: Rule<E2, ValueOfType<Type<T>>> | Validator<ValueOfType<Type<T>>, E2, never, never>): TypeDefBuilder<ValidatingTypeDefWithError<T, E2>>;
408
+ required(): TypeDefBuilder<T>;
408
409
  readonly(): TypeDefBuilder<T>;
409
410
  get _type(): TypeOfType<Type<T>>;
410
411
  get narrow(): ValidatingType<T>;
@@ -519,7 +520,7 @@ type ValueTypesOfDiscriminatedUnion<U extends UnionTypeDef> = U extends UnionTyp
519
520
  };
520
521
  } : never : never;
521
522
 
522
- declare class DefinedValidator<V, E, ValuePath extends string, Context> implements AnnotatedValidator<V | null | undefined, E, ValuePath, Context> {
523
+ declare class DefinedValidator<V, E> implements AnnotatedValidator<V | null | undefined, E, never, never> {
523
524
  private readonly error;
524
525
  constructor(error: E);
525
526
  validate(v: V | null | undefined): E | null;
@@ -535,7 +536,7 @@ type MinimumStringLengthValidationError = {
535
536
  receivedLength: number;
536
537
  minimumLength: number;
537
538
  };
538
- declare class MinimumStringLengthValidator<ValuePath extends string, Context> implements AnnotatedValidator<string, MinimumStringLengthValidationError, ValuePath, Context> {
539
+ declare class MinimumStringLengthValidator implements AnnotatedValidator<string, MinimumStringLengthValidationError, never, never> {
539
540
  private readonly minimumLength;
540
541
  constructor(minimumLength: number);
541
542
  validate(value: string): MinimumStringLengthValidationError | null;
@@ -545,8 +546,50 @@ declare class MinimumStringLengthValidator<ValuePath extends string, Context> im
545
546
  };
546
547
  }
547
548
 
549
+ declare class OptionalValidatorProxy<V, V1 extends V, E, ValuePath extends string, Context> implements AnnotatedValidator<V, E, ValuePath, Context> {
550
+ private readonly proxied;
551
+ private readonly isRequired;
552
+ static createNullable<V, E, ValuePath extends string, Context>(proxied: AnnotatedValidator<NonNullable<V>, E, ValuePath, Context>): OptionalValidatorProxy<V, NonNullable<V>, E, ValuePath, Context>;
553
+ static createNullableOrEmptyString<V extends string | null | undefined, E, ValuePath extends string, Context>(proxied: AnnotatedValidator<NonNullable<V>, E, ValuePath, Context>): OptionalValidatorProxy<V, NonNullable<V>, E, ValuePath, Context>;
554
+ constructor(proxied: AnnotatedValidator<V1, E, ValuePath, Context>, isRequired: (v: V) => v is V1);
555
+ validate(v: V, valuePath: ValuePath, context: Context): E | null;
556
+ annotations(valuePath: ValuePath, context: Context): {
557
+ required: boolean;
558
+ readonly: boolean;
559
+ };
560
+ }
561
+
562
+ declare const RegexpValidationErrorType = "regexp";
563
+ type RegexpValidationError<Intent extends string> = {
564
+ type: typeof RegexpValidationErrorType;
565
+ intent: Intent;
566
+ };
567
+ declare class RegexpValidator<Intent extends string> implements AnnotatedValidator<string, RegexpValidationError<Intent>, never, never> {
568
+ private readonly regexp;
569
+ private readonly intent;
570
+ /**
571
+ * Extremely permissive email validator
572
+ */
573
+ static readonly email: RegexpValidator<"email">;
574
+ /**
575
+ * Extremely permissive phone number validator
576
+ */
577
+ static readonly phone: RegexpValidator<"phone">;
578
+ private readonly negate;
579
+ private readonly required;
580
+ constructor(regexp: RegExp, intent: Intent, { negate, required, }?: {
581
+ negate?: boolean;
582
+ required?: boolean;
583
+ });
584
+ validate(value: string): RegexpValidationError<Intent> | null;
585
+ annotations(): {
586
+ required: boolean;
587
+ readonly: boolean;
588
+ };
589
+ }
590
+
548
591
  type ValidatorsOfValues<FlattenedValues extends Readonly<Record<string, any>>, TypePathsToValuePaths extends Readonly<Record<keyof FlattenedValues, string>> = Readonly<Record<keyof FlattenedValues, any>>, Context = any> = {
549
592
  readonly [K in keyof FlattenedValues]: Validator<FlattenedValues[K], any, TypePathsToValuePaths[K], Context>;
550
593
  };
551
594
 
552
- export { type Accessor, type AnnotatedValidator, type AnyValueType, DefinedValidator, type ErrorOfValidator, type FlattenedAccessorsOfType, type FlattenedTypesOfType, type FlattenedValuesOfType, type FunctionalValidator, type InternalJsonPathsOf, type IsStrictUnion, type ListTypeDef, type LiteralTypeDef, type Mapper, type MinimumStringLengthValidationError, MinimumStringLengthValidationErrorType, MinimumStringLengthValidator, type MobxObservable, type MobxValueOfType, type NonMobxObservable, type ObjectFieldKey, type ObjectTypeDef, type ObjectTypeDefFields, type PathsOfType, type ReadonlyOfTypeDef, type ReadonlyTypeOfType, type RecordKeyType, type RecordTypeDef, type Setter, type StrictListTypeDef, type StrictLiteralTypeDef, type StrictObjectTypeDef, type StrictObjectTypeDefFields, type StrictRecordTypeDef, type StrictType, type StrictTypeDef, type StrictUnionTypeDef, type Type, type TypeDef, TypeDefType, type UnionKey, type UnionTypeDef, type ValidationError, type Validator, type ValidatorsOfValues, type ValueOfType, type ValueOfTypeDef, type ValueToTypePathsOfType, type ValueTypesOfDiscriminatedUnion, annotations, booleanType, copy, flattenAccessorsOfType, flattenJsonValueToTypePathsOf, flattenTypesOfType, flattenValidatorsOfValidatingType, flattenValueTo, flattenValuesOfType, getUnionTypeDef, isAnnotatedValidator, isFunctionalValidator, jsonPath, jsonPathPop, list, literal, mobxCopy, nullType, nullable, numberType, object, record, stringType, union, validate, valuePathToTypePath };
595
+ export { type Accessor, type AnnotatedValidator, type Annotations, type AnyValueType, DefinedValidator, type ErrorOfValidator, type FlattenedAccessorsOfType, type FlattenedTypesOfType, type FlattenedValuesOfType, type FunctionalValidator, type InternalJsonPathsOf, type IsStrictUnion, type ListTypeDef, type LiteralTypeDef, type Mapper, type MinimumStringLengthValidationError, MinimumStringLengthValidationErrorType, MinimumStringLengthValidator, type MobxObservable, type MobxValueOfType, type NonMobxObservable, type ObjectFieldKey, type ObjectTypeDef, type ObjectTypeDefFields, OptionalValidatorProxy, type PathsOfType, type ReadonlyOfTypeDef, type ReadonlyTypeOfType, type RecordKeyType, type RecordTypeDef, type RegexpValidationError, RegexpValidationErrorType, RegexpValidator, type Setter, type StrictListTypeDef, type StrictLiteralTypeDef, type StrictObjectTypeDef, type StrictObjectTypeDefFields, type StrictRecordTypeDef, type StrictType, type StrictTypeDef, type StrictUnionTypeDef, type Type, type TypeDef, TypeDefType, type UnionKey, type UnionTypeDef, type ValidationError, type Validator, type ValidatorsOfValues, type ValueOfType, type ValueOfTypeDef, type ValueToTypePathsOfType, type ValueTypesOfDiscriminatedUnion, annotations, booleanType, copy, flattenAccessorsOfType, flattenJsonValueToTypePathsOf, flattenTypesOfType, flattenValidatorsOfValidatingType, flattenValueTo, flattenValuesOfType, getUnionTypeDef, isAnnotatedValidator, isFunctionalValidator, jsonPath, jsonPathPop, list, literal, mergeAnnotations, mobxCopy, nullType, nullable, numberType, object, record, stringType, union, validate, valuePathToTypePath };
package/dist/index.d.ts CHANGED
@@ -266,25 +266,27 @@ type InternalFlattenedTypeDefsOfUnionChildren<T extends ValidatingUnionTypeDef,
266
266
  readonly [K in keyof Unions]: InternalFlattenedTypeDefsOfChildren<Unions[K], SegmentOverride, Path, `${Qualifier}${K}:`, Depth>;
267
267
  }[keyof Unions]> : never : never;
268
268
 
269
+ type Annotations = {
270
+ readonly required: boolean;
271
+ readonly readonly: boolean;
272
+ };
269
273
  type FunctionalValidator<V = any, E = any, ValuePath extends string = any, Context = any> = (v: V, valuePath: ValuePath, context: Context) => E | null;
270
274
  type AnnotatedValidator<V = any, E = any, ValuePath extends string = any, Context = any> = {
271
275
  readonly validate: (v: V, valuePath: ValuePath, context: Context) => E | null;
272
- readonly annotations: (valuePath: ValuePath, context: Context) => {
273
- readonly required: boolean;
274
- readonly readonly: boolean;
275
- };
276
+ readonly annotations: (valuePath: ValuePath, context: Context) => Annotations;
276
277
  };
277
278
  type Validator<V = any, E = any, ValuePath extends string = any, Context = any> = FunctionalValidator<V, E, ValuePath, Context> | AnnotatedValidator<V, E, ValuePath, Context>;
278
279
  type ErrorOfValidator<V extends Validator> = V extends Validator<infer _V, infer E> ? E : never;
279
280
  type ValidationError<Type extends string, Data = {}> = Simplify<{
280
281
  type: Type;
281
282
  } & Data>;
282
- declare function isFunctionalValidator(v: Validator): v is FunctionalValidator;
283
- declare function isAnnotatedValidator(v: Validator): v is AnnotatedValidator;
283
+ declare function isFunctionalValidator<V, E, ValuePath extends string, Context>(v: Validator<V, E, ValuePath, Context>): v is FunctionalValidator<V, E, ValuePath, Context>;
284
+ declare function isAnnotatedValidator<V, E, ValuePath extends string, Context>(v: Validator<V, E, ValuePath, Context>): v is AnnotatedValidator<V, E, ValuePath, Context>;
284
285
  declare function validate<V = any, E = any, ValuePath extends string = any, Context = any>(validator: Validator<V, E, ValuePath, Context>, v: V, valuePath: ValuePath, context: Context): E | null;
285
- declare function annotations<V = any, E = any, ValuePath extends string = any, Context = any>(validator: Validator<V, E, ValuePath, Context>, valuePath: ValuePath, context: Context): {
286
- readonly required: boolean;
287
- readonly readonly: boolean;
286
+ declare function annotations<V = any, E = any, ValuePath extends string = any, Context = any>(validator: Validator<V, E, ValuePath, Context>, valuePath: ValuePath, context: Context): Annotations;
287
+ declare function mergeAnnotations(a1: Annotations, a2: Annotations): {
288
+ readonly: boolean;
289
+ required: boolean;
288
290
  };
289
291
 
290
292
  type ValidatorOfValidatingType<T extends ValidatingTypeDef, ValuePath extends string, Context> = T extends ValidatingTypeDef<infer E> ? Validator<ValueOfTypeDef<ReadonlyOfTypeDef<T>>, E, ValuePath, Context> : never;
@@ -402,9 +404,8 @@ type ValidatingUnionTypeDefWithError<T extends ValidatingUnionTypeDef, E2> = T e
402
404
  declare class TypeDefBuilder<T extends ValidatingTypeDef> implements ValidatingType<T> {
403
405
  readonly definition: T;
404
406
  constructor(definition: T);
405
- enforce<E2>(rule: Rule<E2, ValueOfType<Type<T>>>): TypeDefBuilder<ValidatingTypeDefWithError<T, E2>>;
406
- required(): TypeDefBuilder<ValidatingTypeDefWithError<T, never>>;
407
- required<RequiredError>(rule: Rule<RequiredError, ValueOfType<typeof this.narrow>>): TypeDefBuilder<ValidatingTypeDefWithError<T, RequiredError>>;
407
+ enforce<E2>(rule: Rule<E2, ValueOfType<Type<T>>> | Validator<ValueOfType<Type<T>>, E2, never, never>): TypeDefBuilder<ValidatingTypeDefWithError<T, E2>>;
408
+ required(): TypeDefBuilder<T>;
408
409
  readonly(): TypeDefBuilder<T>;
409
410
  get _type(): TypeOfType<Type<T>>;
410
411
  get narrow(): ValidatingType<T>;
@@ -519,7 +520,7 @@ type ValueTypesOfDiscriminatedUnion<U extends UnionTypeDef> = U extends UnionTyp
519
520
  };
520
521
  } : never : never;
521
522
 
522
- declare class DefinedValidator<V, E, ValuePath extends string, Context> implements AnnotatedValidator<V | null | undefined, E, ValuePath, Context> {
523
+ declare class DefinedValidator<V, E> implements AnnotatedValidator<V | null | undefined, E, never, never> {
523
524
  private readonly error;
524
525
  constructor(error: E);
525
526
  validate(v: V | null | undefined): E | null;
@@ -535,7 +536,7 @@ type MinimumStringLengthValidationError = {
535
536
  receivedLength: number;
536
537
  minimumLength: number;
537
538
  };
538
- declare class MinimumStringLengthValidator<ValuePath extends string, Context> implements AnnotatedValidator<string, MinimumStringLengthValidationError, ValuePath, Context> {
539
+ declare class MinimumStringLengthValidator implements AnnotatedValidator<string, MinimumStringLengthValidationError, never, never> {
539
540
  private readonly minimumLength;
540
541
  constructor(minimumLength: number);
541
542
  validate(value: string): MinimumStringLengthValidationError | null;
@@ -545,8 +546,50 @@ declare class MinimumStringLengthValidator<ValuePath extends string, Context> im
545
546
  };
546
547
  }
547
548
 
549
+ declare class OptionalValidatorProxy<V, V1 extends V, E, ValuePath extends string, Context> implements AnnotatedValidator<V, E, ValuePath, Context> {
550
+ private readonly proxied;
551
+ private readonly isRequired;
552
+ static createNullable<V, E, ValuePath extends string, Context>(proxied: AnnotatedValidator<NonNullable<V>, E, ValuePath, Context>): OptionalValidatorProxy<V, NonNullable<V>, E, ValuePath, Context>;
553
+ static createNullableOrEmptyString<V extends string | null | undefined, E, ValuePath extends string, Context>(proxied: AnnotatedValidator<NonNullable<V>, E, ValuePath, Context>): OptionalValidatorProxy<V, NonNullable<V>, E, ValuePath, Context>;
554
+ constructor(proxied: AnnotatedValidator<V1, E, ValuePath, Context>, isRequired: (v: V) => v is V1);
555
+ validate(v: V, valuePath: ValuePath, context: Context): E | null;
556
+ annotations(valuePath: ValuePath, context: Context): {
557
+ required: boolean;
558
+ readonly: boolean;
559
+ };
560
+ }
561
+
562
+ declare const RegexpValidationErrorType = "regexp";
563
+ type RegexpValidationError<Intent extends string> = {
564
+ type: typeof RegexpValidationErrorType;
565
+ intent: Intent;
566
+ };
567
+ declare class RegexpValidator<Intent extends string> implements AnnotatedValidator<string, RegexpValidationError<Intent>, never, never> {
568
+ private readonly regexp;
569
+ private readonly intent;
570
+ /**
571
+ * Extremely permissive email validator
572
+ */
573
+ static readonly email: RegexpValidator<"email">;
574
+ /**
575
+ * Extremely permissive phone number validator
576
+ */
577
+ static readonly phone: RegexpValidator<"phone">;
578
+ private readonly negate;
579
+ private readonly required;
580
+ constructor(regexp: RegExp, intent: Intent, { negate, required, }?: {
581
+ negate?: boolean;
582
+ required?: boolean;
583
+ });
584
+ validate(value: string): RegexpValidationError<Intent> | null;
585
+ annotations(): {
586
+ required: boolean;
587
+ readonly: boolean;
588
+ };
589
+ }
590
+
548
591
  type ValidatorsOfValues<FlattenedValues extends Readonly<Record<string, any>>, TypePathsToValuePaths extends Readonly<Record<keyof FlattenedValues, string>> = Readonly<Record<keyof FlattenedValues, any>>, Context = any> = {
549
592
  readonly [K in keyof FlattenedValues]: Validator<FlattenedValues[K], any, TypePathsToValuePaths[K], Context>;
550
593
  };
551
594
 
552
- export { type Accessor, type AnnotatedValidator, type AnyValueType, DefinedValidator, type ErrorOfValidator, type FlattenedAccessorsOfType, type FlattenedTypesOfType, type FlattenedValuesOfType, type FunctionalValidator, type InternalJsonPathsOf, type IsStrictUnion, type ListTypeDef, type LiteralTypeDef, type Mapper, type MinimumStringLengthValidationError, MinimumStringLengthValidationErrorType, MinimumStringLengthValidator, type MobxObservable, type MobxValueOfType, type NonMobxObservable, type ObjectFieldKey, type ObjectTypeDef, type ObjectTypeDefFields, type PathsOfType, type ReadonlyOfTypeDef, type ReadonlyTypeOfType, type RecordKeyType, type RecordTypeDef, type Setter, type StrictListTypeDef, type StrictLiteralTypeDef, type StrictObjectTypeDef, type StrictObjectTypeDefFields, type StrictRecordTypeDef, type StrictType, type StrictTypeDef, type StrictUnionTypeDef, type Type, type TypeDef, TypeDefType, type UnionKey, type UnionTypeDef, type ValidationError, type Validator, type ValidatorsOfValues, type ValueOfType, type ValueOfTypeDef, type ValueToTypePathsOfType, type ValueTypesOfDiscriminatedUnion, annotations, booleanType, copy, flattenAccessorsOfType, flattenJsonValueToTypePathsOf, flattenTypesOfType, flattenValidatorsOfValidatingType, flattenValueTo, flattenValuesOfType, getUnionTypeDef, isAnnotatedValidator, isFunctionalValidator, jsonPath, jsonPathPop, list, literal, mobxCopy, nullType, nullable, numberType, object, record, stringType, union, validate, valuePathToTypePath };
595
+ export { type Accessor, type AnnotatedValidator, type Annotations, type AnyValueType, DefinedValidator, type ErrorOfValidator, type FlattenedAccessorsOfType, type FlattenedTypesOfType, type FlattenedValuesOfType, type FunctionalValidator, type InternalJsonPathsOf, type IsStrictUnion, type ListTypeDef, type LiteralTypeDef, type Mapper, type MinimumStringLengthValidationError, MinimumStringLengthValidationErrorType, MinimumStringLengthValidator, type MobxObservable, type MobxValueOfType, type NonMobxObservable, type ObjectFieldKey, type ObjectTypeDef, type ObjectTypeDefFields, OptionalValidatorProxy, type PathsOfType, type ReadonlyOfTypeDef, type ReadonlyTypeOfType, type RecordKeyType, type RecordTypeDef, type RegexpValidationError, RegexpValidationErrorType, RegexpValidator, type Setter, type StrictListTypeDef, type StrictLiteralTypeDef, type StrictObjectTypeDef, type StrictObjectTypeDefFields, type StrictRecordTypeDef, type StrictType, type StrictTypeDef, type StrictUnionTypeDef, type Type, type TypeDef, TypeDefType, type UnionKey, type UnionTypeDef, type ValidationError, type Validator, type ValidatorsOfValues, type ValueOfType, type ValueOfTypeDef, type ValueToTypePathsOfType, type ValueTypesOfDiscriminatedUnion, annotations, booleanType, copy, flattenAccessorsOfType, flattenJsonValueToTypePathsOf, flattenTypesOfType, flattenValidatorsOfValidatingType, flattenValueTo, flattenValuesOfType, getUnionTypeDef, isAnnotatedValidator, isFunctionalValidator, jsonPath, jsonPathPop, list, literal, mergeAnnotations, mobxCopy, nullType, nullable, numberType, object, record, stringType, union, validate, valuePathToTypePath };
package/dist/index.js CHANGED
@@ -392,7 +392,7 @@ function flattenTypeTo({ definition }, mapper2) {
392
392
  return reduce4(
393
393
  typeDefs,
394
394
  function(acc, key, typeDef) {
395
- acc[key] = mapper2(typeDef);
395
+ acc[key] = mapper2(typeDef, key);
396
396
  return acc;
397
397
  },
398
398
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
@@ -640,6 +640,37 @@ function internalJsonValuePathToTypePath(typeDef, valueSteps, allowMissingPaths,
640
640
  }
641
641
  }
642
642
 
643
+ // validation/validator.ts
644
+ function isFunctionalValidator(v) {
645
+ return typeof v === "function";
646
+ }
647
+ function isAnnotatedValidator(v) {
648
+ return typeof v !== "function";
649
+ }
650
+ function validate(validator, v, valuePath, context) {
651
+ if (isAnnotatedValidator(validator)) {
652
+ return validator.validate(v, valuePath, context);
653
+ } else {
654
+ return validator(v, valuePath, context);
655
+ }
656
+ }
657
+ function annotations(validator, valuePath, context) {
658
+ if (isAnnotatedValidator(validator)) {
659
+ return validator.annotations(valuePath, context);
660
+ } else {
661
+ return {
662
+ required: false,
663
+ readonly: false
664
+ };
665
+ }
666
+ }
667
+ function mergeAnnotations(a1, a2) {
668
+ return {
669
+ readonly: a1.readonly || a2.readonly,
670
+ required: a1.required || a2.required
671
+ };
672
+ }
673
+
643
674
  // types/type_of_type.ts
644
675
  import {
645
676
  map as map2,
@@ -733,23 +764,20 @@ var TypeDefBuilder = class _TypeDefBuilder {
733
764
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
734
765
  {
735
766
  ...this.definition,
767
+ ...isAnnotatedValidator(rule) ? mergeAnnotations(rule.annotations(null, null), this.definition) : {},
736
768
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
737
769
  rule: (value) => {
738
- return this.definition.rule(value) || rule(value);
770
+ return this.definition.rule(value) ?? validate(rule, value, null, null);
739
771
  }
740
772
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
741
773
  }
742
774
  );
743
775
  }
744
- required(rule) {
776
+ required() {
745
777
  return new _TypeDefBuilder(
746
778
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
747
779
  {
748
780
  ...this.definition,
749
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
750
- rule: (v) => {
751
- return this.definition.rule(v) || rule?.(v);
752
- },
753
781
  required: true
754
782
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
755
783
  }
@@ -954,31 +982,6 @@ function union(discriminator) {
954
982
  );
955
983
  }
956
984
 
957
- // validation/validator.ts
958
- function isFunctionalValidator(v) {
959
- return typeof v === "function";
960
- }
961
- function isAnnotatedValidator(v) {
962
- return typeof v !== "function";
963
- }
964
- function validate(validator, v, valuePath, context) {
965
- if (isAnnotatedValidator(validator)) {
966
- return validator.validate(v, valuePath, context);
967
- } else {
968
- return validator(v, valuePath, context);
969
- }
970
- }
971
- function annotations(validator, valuePath, context) {
972
- if (isAnnotatedValidator(validator)) {
973
- return validator.annotations(valuePath, context);
974
- } else {
975
- return {
976
- required: false,
977
- readonly: false
978
- };
979
- }
980
- }
981
-
982
985
  // validation/validators/defined_validator.ts
983
986
  var DefinedValidator = class {
984
987
  constructor(error) {
@@ -1016,7 +1019,76 @@ var MinimumStringLengthValidator = class {
1016
1019
  }
1017
1020
  annotations() {
1018
1021
  return {
1019
- required: true,
1022
+ required: this.minimumLength > 0,
1023
+ readonly: false
1024
+ };
1025
+ }
1026
+ };
1027
+
1028
+ // validation/validators/optional_validator_proxy.ts
1029
+ var OptionalValidatorProxy = class _OptionalValidatorProxy {
1030
+ constructor(proxied, isRequired) {
1031
+ this.proxied = proxied;
1032
+ this.isRequired = isRequired;
1033
+ }
1034
+ static createNullable(proxied) {
1035
+ return new _OptionalValidatorProxy(proxied, (v) => v != null);
1036
+ }
1037
+ static createNullableOrEmptyString(proxied) {
1038
+ return new _OptionalValidatorProxy(proxied, (v) => v != null && v !== "");
1039
+ }
1040
+ validate(v, valuePath, context) {
1041
+ if (this.isRequired(v)) {
1042
+ return this.proxied.validate(v, valuePath, context);
1043
+ }
1044
+ return null;
1045
+ }
1046
+ annotations(valuePath, context) {
1047
+ return {
1048
+ ...this.proxied.annotations(valuePath, context),
1049
+ required: false
1050
+ };
1051
+ }
1052
+ };
1053
+
1054
+ // validation/validators/regexp_validator.ts
1055
+ var RegexpValidationErrorType = "regexp";
1056
+ var RegexpValidator = class _RegexpValidator {
1057
+ constructor(regexp, intent, {
1058
+ negate = false,
1059
+ required = false
1060
+ } = {}) {
1061
+ this.regexp = regexp;
1062
+ this.intent = intent;
1063
+ this.negate = negate;
1064
+ this.required = required;
1065
+ }
1066
+ /**
1067
+ * Extremely permissive email validator
1068
+ */
1069
+ static email = new _RegexpValidator(/^[^\s@]+@[^\s@]+\.[^\s@]+$/, "email");
1070
+ /**
1071
+ * Extremely permissive phone number validator
1072
+ */
1073
+ static phone = new _RegexpValidator(
1074
+ /^(\+\d{1,4}[\s]*)?(((\([\d\s-]{1,6}\))|\d)[\s-]*){3,14}(\d|(\(\d+\)))$/,
1075
+ "phone"
1076
+ );
1077
+ negate;
1078
+ required;
1079
+ validate(value) {
1080
+ const passes = this.regexp.test(value);
1081
+ if (!passes && !this.negate || passes && this.negate) {
1082
+ return {
1083
+ type: RegexpValidationErrorType,
1084
+ intent: this.intent
1085
+ };
1086
+ }
1087
+ return null;
1088
+ }
1089
+ annotations() {
1090
+ return {
1091
+ required: this.required,
1020
1092
  readonly: false
1021
1093
  };
1022
1094
  }
@@ -1025,6 +1097,9 @@ export {
1025
1097
  DefinedValidator,
1026
1098
  MinimumStringLengthValidationErrorType,
1027
1099
  MinimumStringLengthValidator,
1100
+ OptionalValidatorProxy,
1101
+ RegexpValidationErrorType,
1102
+ RegexpValidator,
1028
1103
  TypeDefType,
1029
1104
  annotations,
1030
1105
  booleanType,
@@ -1042,6 +1117,7 @@ export {
1042
1117
  jsonPathPop,
1043
1118
  list,
1044
1119
  literal,
1120
+ mergeAnnotations,
1045
1121
  mobxCopy,
1046
1122
  nullType,
1047
1123
  nullable,
package/index.ts CHANGED
@@ -23,4 +23,6 @@ export * from './types/value_types_of_discriminated_union'
23
23
  export * from './validation/validator'
24
24
  export * from './validation/validators/defined_validator'
25
25
  export * from './validation/validators/minimum_string_length_validator'
26
+ export * from './validation/validators/optional_validator_proxy'
27
+ export * from './validation/validators/regexp_validator'
26
28
  export * from './validation/validators_of_values'
package/package.json CHANGED
@@ -35,7 +35,7 @@
35
35
  "test:watch": "vitest"
36
36
  },
37
37
  "type": "module",
38
- "version": "0.0.7",
38
+ "version": "0.0.9",
39
39
  "exports": {
40
40
  ".": {
41
41
  "import": {
@@ -18,7 +18,7 @@ import { jsonPath } from './json_path'
18
18
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
19
19
  export type AnyValueType = any
20
20
 
21
- export type Mapper<R> = (t: StrictTypeDef) => R
21
+ export type Mapper<R> = (t: StrictTypeDef, key: string) => R
22
22
 
23
23
  export function flattenTypeTo<M, R extends Readonly<Record<string, M>>>(
24
24
  { definition }: StrictType,
@@ -29,7 +29,7 @@ export function flattenTypeTo<M, R extends Readonly<Record<string, M>>>(
29
29
  return reduce(
30
30
  typeDefs,
31
31
  function (acc, key, typeDef) {
32
- acc[key] = mapper(typeDef)
32
+ acc[key] = mapper(typeDef, key)
33
33
  return acc
34
34
  },
35
35
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
package/types/builders.ts CHANGED
@@ -1,6 +1,12 @@
1
1
  import {
2
2
  type IsFieldReadonly,
3
3
  } from '@strictly/base'
4
+ import {
5
+ isAnnotatedValidator,
6
+ mergeAnnotations,
7
+ validate,
8
+ type Validator,
9
+ } from 'validation/validator'
4
10
  import {
5
11
  type ObjectFieldKey,
6
12
  type RecordKeyType,
@@ -37,35 +43,26 @@ class TypeDefBuilder<T extends ValidatingTypeDef> implements ValidatingType<T> {
37
43
  constructor(readonly definition: T) {
38
44
  }
39
45
 
40
- enforce<E2>(rule: Rule<E2, ValueOfType<Type<T>>>) {
46
+ enforce<E2>(rule: Rule<E2, ValueOfType<Type<T>>> | Validator<ValueOfType<Type<T>>, E2, never, never>) {
41
47
  return new TypeDefBuilder<ValidatingTypeDefWithError<T, E2>>(
42
48
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
43
49
  {
44
50
  ...this.definition,
51
+ ...(isAnnotatedValidator(rule) ? mergeAnnotations(rule.annotations(null!, null!), this.definition) : {}),
45
52
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
46
53
  rule: (value: any) => {
47
- return this.definition.rule(value) || rule(value)
54
+ return this.definition.rule(value) ?? validate(rule, value, null!, null!)
48
55
  },
49
56
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
50
57
  } as any,
51
58
  )
52
59
  }
53
60
 
54
- required(): TypeDefBuilder<ValidatingTypeDefWithError<T, never>>
55
- required<
56
- RequiredError,
57
- >(rule: Rule<RequiredError, ValueOfType<typeof this.narrow>>): TypeDefBuilder<
58
- ValidatingTypeDefWithError<T, RequiredError>
59
- >
60
- required<RequiredError = never>(rule?: Rule<RequiredError, ValueOfType<typeof this.narrow>>) {
61
- return new TypeDefBuilder<ValidatingTypeDefWithError<T, RequiredError>>(
61
+ required(): TypeDefBuilder<T> {
62
+ return new TypeDefBuilder<T>(
62
63
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
63
64
  {
64
65
  ...this.definition,
65
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
66
- rule: (v: any) => {
67
- return this.definition.rule(v) || rule?.(v)
68
- },
69
66
  required: true,
70
67
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
71
68
  } as any,
@@ -1,5 +1,10 @@
1
1
  import { type Simplify } from 'type-fest'
2
2
 
3
+ export type Annotations = {
4
+ readonly required: boolean,
5
+ readonly readonly: boolean,
6
+ }
7
+
3
8
  export type FunctionalValidator<
4
9
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
5
10
  V = any,
@@ -22,10 +27,7 @@ export type AnnotatedValidator<
22
27
  Context = any,
23
28
  > = {
24
29
  readonly validate: (v: V, valuePath: ValuePath, context: Context) => E | null,
25
- readonly annotations: (valuePath: ValuePath, context: Context) => {
26
- readonly required: boolean,
27
- readonly readonly: boolean,
28
- },
30
+ readonly annotations: (valuePath: ValuePath, context: Context) => Annotations,
29
31
  }
30
32
 
31
33
  export type Validator<
@@ -47,11 +49,21 @@ export type ValidationError<Type extends string, Data = {}> = Simplify<
47
49
  } & Data
48
50
  >
49
51
 
50
- export function isFunctionalValidator(v: Validator): v is FunctionalValidator {
52
+ export function isFunctionalValidator<
53
+ V,
54
+ E,
55
+ ValuePath extends string,
56
+ Context,
57
+ >(v: Validator<V, E, ValuePath, Context>): v is FunctionalValidator<V, E, ValuePath, Context> {
51
58
  return typeof v === 'function'
52
59
  }
53
60
 
54
- export function isAnnotatedValidator(v: Validator): v is AnnotatedValidator {
61
+ export function isAnnotatedValidator<
62
+ V,
63
+ E,
64
+ ValuePath extends string,
65
+ Context,
66
+ >(v: Validator<V, E, ValuePath, Context>): v is AnnotatedValidator<V, E, ValuePath, Context> {
55
67
  return typeof v !== 'function'
56
68
  }
57
69
 
@@ -73,8 +85,7 @@ export function validate<
73
85
  if (isAnnotatedValidator(validator)) {
74
86
  return validator.validate(v, valuePath, context)
75
87
  } else {
76
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
77
- return (validator as FunctionalValidator<V, E, ValuePath, Context>)(v, valuePath, context)
88
+ return validator(v, valuePath, context)
78
89
  }
79
90
  }
80
91
 
@@ -101,3 +112,10 @@ export function annotations<
101
112
  }
102
113
  }
103
114
  }
115
+
116
+ export function mergeAnnotations(a1: Annotations, a2: Annotations) {
117
+ return {
118
+ readonly: a1.readonly || a2.readonly,
119
+ required: a1.required || a2.required,
120
+ }
121
+ }
@@ -1,8 +1,6 @@
1
1
  import { type AnnotatedValidator } from 'validation/validator'
2
2
 
3
- export class DefinedValidator<V, E, ValuePath extends string, Context>
4
- implements AnnotatedValidator<V | null | undefined, E, ValuePath, Context>
5
- {
3
+ export class DefinedValidator<V, E> implements AnnotatedValidator<V | null | undefined, E, never, never> {
6
4
  constructor(private readonly error: E) {
7
5
  }
8
6
 
@@ -10,8 +10,8 @@ export type MinimumStringLengthValidationError = {
10
10
  minimumLength: number,
11
11
  }
12
12
 
13
- export class MinimumStringLengthValidator<ValuePath extends string, Context>
14
- implements AnnotatedValidator<string, MinimumStringLengthValidationError, ValuePath, Context>
13
+ export class MinimumStringLengthValidator
14
+ implements AnnotatedValidator<string, MinimumStringLengthValidationError, never, never>
15
15
  {
16
16
  constructor(private readonly minimumLength: number) {
17
17
  }
@@ -29,7 +29,7 @@ export class MinimumStringLengthValidator<ValuePath extends string, Context>
29
29
 
30
30
  annotations() {
31
31
  return {
32
- required: true,
32
+ required: this.minimumLength > 0,
33
33
  readonly: false,
34
34
  }
35
35
  }