typebox 1.2.13 → 1.2.15

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.
@@ -3,6 +3,7 @@ export interface TSettings {
3
3
  * Determines whether types should be instantiated as immutable using `Object.freeze(...)`.
4
4
  * This helps prevent unintended schema mutation. Enabling this option introduces a slight
5
5
  * performance overhead during instantiation.
6
+ *
6
7
  * @default false
7
8
  */
8
9
  immutableTypes: boolean;
@@ -11,6 +12,7 @@ export interface TSettings {
11
12
  * performs exhaustive checks to gather diagnostics for invalid values, which can result in
12
13
  * excessive buffering for large or complex types. This setting limits the number of buffered
13
14
  * errors and acts as a safeguard against potential denial-of-service (DoS) attacks.
15
+ *
14
16
  * @default 8
15
17
  */
16
18
  maxErrors: number;
@@ -20,6 +22,7 @@ export interface TSettings {
20
22
  * generated code. If evaluation is not permitted, it falls back to dynamic checking. Setting
21
23
  * this to `false` disables evaluation entirely, which may be desirable in applications that
22
24
  * restrict runtime code evaluation, regardless of Content Security Policy (CSP).
25
+ *
23
26
  * @default true
24
27
  */
25
28
  useAcceleration: boolean;
@@ -30,11 +33,13 @@ export interface TSettings {
30
33
  * TypeScript semantics to remain consistent with the language. This option is provided to align
31
34
  * runtime check semantics with projects that configure 'exactOptionalPropertyTypes: true' in
32
35
  * tsconfig.json.
36
+ *
33
37
  * @default false
34
38
  */
35
39
  exactOptionalPropertyTypes: boolean;
36
40
  /**
37
41
  * Controls whether internal compositor properties (`~kind`, `~readonly`, `~optional`) are enumerable.
42
+ *
38
43
  * @default false
39
44
  */
40
45
  enumerableKind: boolean;
@@ -44,9 +49,19 @@ export interface TSettings {
44
49
  * the value, followed by a subsequent `Assert`. Enabling this option may incur significant performance
45
50
  * overhead for invalid values. It is recommended to keep this disabled in performance-sensitive
46
51
  * applications.
52
+ *
47
53
  * @default false
48
54
  */
49
55
  correctiveParse: boolean;
56
+ /**
57
+ * Controls whether TypeBox sorts Union variants by specificity (narrowest first) for operations
58
+ * that require deterministic value matching, such as Clean, Decode, and Encode. When enabled,
59
+ * more specific types (e.g. Literal) always take precedence over broader types (e.g. String),
60
+ * regardless of the order variants are declared.
61
+ *
62
+ * @default true
63
+ */
64
+ unionPrioritySort: boolean;
50
65
  }
51
66
  /** Resets system settings to defaults */
52
67
  export declare function Reset(): void;
@@ -6,7 +6,8 @@ const settings = {
6
6
  useAcceleration: true,
7
7
  exactOptionalPropertyTypes: false,
8
8
  enumerableKind: false,
9
- correctiveParse: false
9
+ correctiveParse: false,
10
+ unionPrioritySort: true
10
11
  };
11
12
  /** Resets system settings to defaults */
12
13
  export function Reset() {
@@ -16,6 +17,7 @@ export function Reset() {
16
17
  settings.exactOptionalPropertyTypes = false;
17
18
  settings.enumerableKind = false;
18
19
  settings.correctiveParse = false;
20
+ settings.unionPrioritySort = true;
19
21
  }
20
22
  /** Sets system settings */
21
23
  export function Set(options) {
@@ -2,7 +2,6 @@ import { type TSchema } from '../../types/schema.mjs';
2
2
  import { type TAny } from '../../types/any.mjs';
3
3
  import { type TNever } from '../../types/never.mjs';
4
4
  import { type TObject } from '../../types/object.mjs';
5
- import { type TUnion } from '../../types/union.mjs';
6
5
  import { type TCompare, ResultRightInside, ResultLeftInside, ResultEqual } from './compare.mjs';
7
6
  import { type TFlatten } from './flatten.mjs';
8
7
  import { type TEvaluateType } from './evaluate.mjs';
@@ -10,7 +9,7 @@ type TBroadFilter<Type extends TSchema, Types extends TSchema[], Result extends
10
9
  type TIsBroadestType<Type extends TSchema, Types extends TSchema[]> = (Types extends [infer Left extends TSchema, ...infer Right extends TSchema[]] ? TCompare<Type, Left> extends typeof ResultLeftInside | typeof ResultEqual ? false : TIsBroadestType<Type, Right> : true);
11
10
  type TBroadenType<Type extends TSchema, Types extends TSchema[], Evaluated extends TSchema = TEvaluateType<Type>, Result extends TSchema[] = (Evaluated extends TAny ? [Evaluated] : TIsBroadestType<Evaluated, Types> extends true ? [...TBroadFilter<Evaluated, Types>, Evaluated] : Types)> = Result;
12
11
  type TBroadenTypes<Types extends TSchema[], Result extends TSchema[] = []> = (Types extends [infer Left extends TSchema, ...infer Right extends TSchema[]] ? (Left extends TObject ? TBroadenTypes<Right, [...Result, Left]> : Left extends TNever ? TBroadenTypes<Right, Result> : TBroadenTypes<Right, TBroadenType<Left, Result>>) : Result);
13
- export type TBroaden<Types extends TSchema[], Broadened extends TSchema[] = TBroadenTypes<Types>, Flattened extends TSchema[] = TFlatten<Broadened>, Result extends TSchema = (Flattened extends [] ? TNever : Flattened extends [infer Type extends TSchema] ? Type : TUnion<Flattened>)> = Result;
12
+ export type TBroaden<Types extends TSchema[], Broadened extends TSchema[] = TBroadenTypes<Types>, Flattened extends TSchema[] = TFlatten<Broadened>> = Flattened;
14
13
  /** Broadens a set of types and returns either the most broad type, or union or disjoint types. */
15
14
  export declare function Broaden<Types extends TSchema[]>(types: [...Types]): TBroaden<Types>;
16
15
  export {};
@@ -1,9 +1,8 @@
1
1
  // deno-fmt-ignore-file
2
2
  import { Guard } from '../../../guard/index.mjs';
3
3
  import { IsAny } from '../../types/any.mjs';
4
- import { Never, IsNever } from '../../types/never.mjs';
4
+ import { IsNever } from '../../types/never.mjs';
5
5
  import { IsObject } from '../../types/object.mjs';
6
- import { Union } from '../../types/union.mjs';
7
6
  import { Compare, ResultRightInside, ResultLeftInside, ResultEqual } from './compare.mjs';
8
7
  import { Flatten } from './flatten.mjs';
9
8
  import { EvaluateType } from './evaluate.mjs';
@@ -40,8 +39,5 @@ function BroadenTypes(types) {
40
39
  export function Broaden(types) {
41
40
  const broadened = BroadenTypes(types);
42
41
  const flattened = Flatten(broadened);
43
- const result = (flattened.length === 0 ? Never() :
44
- flattened.length === 1 ? flattened[0] :
45
- Union(flattened));
46
- return result;
42
+ return flattened;
47
43
  }
@@ -14,11 +14,11 @@ export type TEvaluateDependent<If extends TSchema, Then extends TSchema, Else ex
14
14
  export declare function EvaluateDependent<If extends TSchema, Then extends TSchema, Else extends TSchema>(if_: If, then_: Then, else_: Else): TEvaluateDependent<If, Then, Else>;
15
15
  export type TEvaluateEnum<Values extends TEnumValue[], Result extends TSchema[] = []> = (Values extends [infer Left extends TEnumValue, ...infer Right extends TEnumValue[]] ? TEvaluateEnum<Right, [...Result, TLiteral<Left>]> : TEvaluateUnion<Result>);
16
16
  export declare function EvaluateEnum<Values extends TEnumValue[]>(values: [...Values]): TEvaluateEnum<Values>;
17
- export type TEvaluateIntersect<Types extends TSchema[], Distribution extends TSchema[] = TDistribute<Types>, Result extends TSchema = TBroaden<Distribution>> = Result;
17
+ export type TEvaluateIntersect<Types extends TSchema[], Distribution extends TSchema[] = TDistribute<Types>, Broadend extends TSchema[] = TBroaden<Distribution>, Result extends TSchema = TEvaluateUnionFast<Broadend>> = Result;
18
18
  export declare function EvaluateIntersect<Types extends TSchema[]>(types: [...Types]): TEvaluateIntersect<Types>;
19
19
  export type TEvaluateTemplateLiteral<Pattern extends string, Evaluated extends TSchema = TTemplateLiteralDecode<Pattern>, Result extends TSchema = TEvaluateType<Evaluated>> = Result;
20
20
  export declare function EvaluateTemplateLiteral<Pattern extends string>(pattern: Pattern): TEvaluateTemplateLiteral<Pattern>;
21
- export type TEvaluateUnion<Types extends TSchema[], Result extends TSchema = TBroaden<Types>> = Result;
21
+ export type TEvaluateUnion<Types extends TSchema[], Broadend extends TSchema[] = TBroaden<Types>, Result extends TSchema = TEvaluateUnionFast<Broadend>> = Result;
22
22
  export declare function EvaluateUnion<Types extends TSchema[]>(types: [...Types]): TEvaluateUnion<Types>;
23
23
  export type TEvaluateType<Type extends TSchema, Result extends TSchema = (Type extends TDependent<infer If extends TSchema, infer Then extends TSchema, infer Else extends TSchema> ? TEvaluateDependent<If, Then, Else> : Type extends TEnum<infer Values extends TEnumValue[]> ? TEvaluateEnum<Values> : Type extends TIntersect<infer Types extends TSchema[]> ? TEvaluateIntersect<Types> : Type extends TTemplateLiteral<infer Pattern extends string> ? TEvaluateTemplateLiteral<Pattern> : Type extends TUnion<infer Types extends TSchema[]> ? TEvaluateUnion<Types> : Type)> = Result;
24
24
  export declare function EvaluateType<Type extends TSchema>(type: Type): TEvaluateType<Type>;
@@ -23,7 +23,8 @@ export function EvaluateEnum(values) {
23
23
  }
24
24
  export function EvaluateIntersect(types) {
25
25
  const distribution = Distribute(types);
26
- const result = Broaden(distribution);
26
+ const broadend = Broaden(distribution);
27
+ const result = EvaluateUnionFast(broadend);
27
28
  return result;
28
29
  }
29
30
  export function EvaluateTemplateLiteral(pattern) {
@@ -32,7 +33,8 @@ export function EvaluateTemplateLiteral(pattern) {
32
33
  return result;
33
34
  }
34
35
  export function EvaluateUnion(types) {
35
- const result = Broaden(types);
36
+ const broadend = Broaden(types);
37
+ const result = EvaluateUnionFast(broadend);
36
38
  return result;
37
39
  }
38
40
  export function EvaluateType(type) {
@@ -22,6 +22,7 @@ export * from './parameters/index.mjs';
22
22
  export * from './patterns/index.mjs';
23
23
  export * from './partial/index.mjs';
24
24
  export * from './pick/index.mjs';
25
+ export * from './priority/index.mjs';
25
26
  export * from './readonly_object/index.mjs';
26
27
  export * from './record/index.mjs';
27
28
  export * from './ref/index.mjs';
@@ -28,6 +28,7 @@ export * from './parameters/index.mjs';
28
28
  export * from './patterns/index.mjs';
29
29
  export * from './partial/index.mjs';
30
30
  export * from './pick/index.mjs';
31
+ export * from './priority/index.mjs';
31
32
  export * from './readonly_object/index.mjs';
32
33
  export * from './record/index.mjs';
33
34
  export * from './ref/index.mjs';
@@ -0,0 +1 @@
1
+ export * from './priority.mjs';
@@ -0,0 +1 @@
1
+ export * from './priority.mjs';
@@ -0,0 +1,20 @@
1
+ import { type TSchema } from '../../types/schema.mjs';
2
+ import { type TCompare, type TCompareResult } from '../evaluate/compare.mjs';
3
+ type TComparer<Left extends TSchema, Right extends TSchema, CompareResult extends TCompareResult = TCompare<Left, Right>, Result extends 0 | 1 = (CompareResult extends 'right-inside' ? 1 : CompareResult extends 'disjoint' ? 1 : 0)> = Result;
4
+ type TInsert<Type extends TSchema, Types extends TSchema[], Result extends TSchema[] = []> = (Types extends [infer Left extends TSchema, ...infer Right extends TSchema[]] ? TComparer<Type, Left> extends 1 ? TInsert<Type, Right, [...Result, Left]> : [...Result, Type, ...Types] : [...Result, Type]);
5
+ type TSort<Types extends TSchema[], Result extends TSchema[] = []> = (Types extends [infer Left extends TSchema, ...infer Right extends TSchema[]] ? TSort<Right, TInsert<Left, Result>> : Result);
6
+ /**
7
+ * Priority sorts types in sequence of narrowest to broadest using a Drop Sort algorithm.
8
+ * This function is typically used to sequence types for union variant checks to ensure
9
+ * that values are checked against the most narrow types before the broadest, which in
10
+ * turn helps ensure order-independent checking.
11
+ */
12
+ export type TPriority<Types extends TSchema[], Result extends TSchema[] = TSort<Types>> = Result;
13
+ /**
14
+ * Priority sorts types in sequence of narrowest to broadest using a Drop Sort algorithm.
15
+ * This function is typically used to sequence types for union variant checks to ensure
16
+ * that values are checked against the most narrow types before the broadest, which in
17
+ * turn helps ensure order-independent checking.
18
+ */
19
+ export declare function Priority<Types extends TSchema[]>(types: [...Types]): TPriority<Types>;
20
+ export {};
@@ -0,0 +1,28 @@
1
+ // deno-fmt-ignore-file
2
+ import { Guard } from '../../../guard/index.mjs';
3
+ import { Compare } from '../evaluate/compare.mjs';
4
+ function Comparer(left, right) {
5
+ const compareResult = Compare(left, right);
6
+ const result = (Guard.IsEqual(compareResult, 'right-inside') ? 1 :
7
+ Guard.IsEqual(compareResult, 'disjoint') ? 1 :
8
+ 0);
9
+ return result;
10
+ }
11
+ function Insert(type, types, result = []) {
12
+ return Guard.ShiftLeft(types, (left, right) => Guard.IsEqual(Comparer(type, left), 1)
13
+ ? Insert(type, right, [...result, left])
14
+ : [...result, type, ...types], () => [...result, type]);
15
+ }
16
+ function Sort(types, result = []) {
17
+ return Guard.ShiftLeft(types, (left, right) => Sort(right, Insert(left, result)), () => result);
18
+ }
19
+ /**
20
+ * Priority sorts types in sequence of narrowest to broadest using a Drop Sort algorithm.
21
+ * This function is typically used to sequence types for union variant checks to ensure
22
+ * that values are checked against the most narrow types before the broadest, which in
23
+ * turn helps ensure order-independent checking.
24
+ */
25
+ export function Priority(types) {
26
+ const result = Sort(types);
27
+ return result;
28
+ }
@@ -6,20 +6,20 @@ export type TRefineAdd<Type extends TSchema = TSchema> = ('~refine' extends keyo
6
6
  export declare function RefineAdd<Type extends TSchema>(type: Type, refinement: TRefinement<Type>): TRefineAdd<Type>;
7
7
  /** Represents a type with embedded Refine check. */
8
8
  export type TRefine<Type extends TSchema = TSchema> = (Type & {
9
- '~refine': TRefinement<Type>[];
9
+ '~refine': TRefinement<unknown>[];
10
10
  });
11
- export type TRefineCheckCallback<Type extends TSchema = TSchema> = (value: Static<Type>) => boolean;
12
- export type TRefineErrorCallback<Type extends TSchema = TSchema> = (value: Static<Type>) => string;
13
- export interface TRefinement<Type extends TSchema = TSchema> {
14
- check: TRefineCheckCallback<Type>;
15
- error: TRefineErrorCallback<Type>;
11
+ export type TRefineCheckCallback<Value extends unknown = unknown> = (value: Value) => boolean;
12
+ export type TRefineErrorCallback<Value extends unknown = unknown> = (value: Value) => string;
13
+ export interface TRefinement<Value extends unknown = unknown> {
14
+ check: TRefineCheckCallback<Value>;
15
+ error: TRefineErrorCallback<Value>;
16
16
  }
17
17
  /** Refines a type with an explicit check */
18
- export declare function Refine<Type extends TSchema>(type: Type, check: TRefineCheckCallback<Type>, error: TRefineErrorCallback<Type>): TRefineAdd<Type>;
18
+ export declare function Refine<Type extends TSchema, Value = Static<Type>>(type: Type, check: TRefineCheckCallback<Value>, error: TRefineErrorCallback<Value>): TRefineAdd<Type>;
19
19
  /** Refines a type with an explicit check */
20
- export declare function Refine<Type extends TSchema>(type: Type, check: TRefineCheckCallback<Type>): TRefineAdd<Type>;
20
+ export declare function Refine<Type extends TSchema, Value = Static<Type>>(type: Type, check: TRefineCheckCallback<Value>): TRefineAdd<Type>;
21
21
  /** @deprecated Use the error callback signature to generate error message. This overload will be removed in the next version */
22
- export declare function Refine<Type extends TSchema>(type: Type, check: TRefineCheckCallback<Type>, message: string): TRefineAdd<Type>;
22
+ export declare function Refine<Type extends TSchema, Value = Static<Type>>(type: Type, check: TRefineCheckCallback<Value>, message: string): TRefineAdd<Type>;
23
23
  /** Returns true if the given value is a TRefinement. */
24
24
  export declare function IsRefinement(value: unknown): value is TRefinement;
25
25
  /** Returns true if the given value is a TRefine. */
@@ -30,7 +30,7 @@ export declare function RecordDeferred<Key extends TSchema, Value extends TSchem
30
30
  /** Creates a Record type. */
31
31
  export declare function Record<Key extends TSchema, Value extends TSchema>(key: Key, value: Value, options?: TObjectOptions): TRecordAction<Key, Value>;
32
32
  /** Creates a Record type from regular expression pattern. */
33
- export declare function RecordFromPattern<Pattern extends string, Value extends TSchema>(key: Pattern, value: Value): TRecord<Pattern, Value>;
33
+ export declare function RecordFromPattern<Pattern extends string, Value extends TSchema>(pattern: Pattern, value: Value): TRecord<Pattern, Value>;
34
34
  /** Transforms a Record Pattern to a Type */
35
35
  export type TRecordPatternToType<Pattern extends string, Result extends TSchema = (Pattern extends typeof StringKey ? TString : Pattern extends typeof IntegerKey ? TInteger : Pattern extends typeof NumberKey ? TNumber : TTemplateLiteralDecodeUnsafe<Pattern>)> = Result;
36
36
  /** Transforms a Record Pattern to a Type */
@@ -24,11 +24,11 @@ export function Record(key, value, options = {}) {
24
24
  return RecordAction(key, value, options);
25
25
  }
26
26
  // -------------------------------------------------------------------
27
- // FromPattern
27
+ // RecordFromPattern
28
28
  // -------------------------------------------------------------------
29
29
  /** Creates a Record type from regular expression pattern. */
30
- export function RecordFromPattern(key, value) {
31
- return CreateRecord(key, value);
30
+ export function RecordFromPattern(pattern, value) {
31
+ return CreateRecord(pattern, value);
32
32
  }
33
33
  /** Transforms a Record Pattern to a Type */
34
34
  export function RecordPatternToType(pattern) {
@@ -1,5 +1,6 @@
1
- import { Arguments } from '../../system/arguments/index.mjs';
1
+ import { Arguments, Settings } from '../../system/index.mjs';
2
2
  import { FromType } from './from_type.mjs';
3
+ import { UnionPrioritySort } from '../shared/union_priority_sort.mjs';
3
4
  /**
4
5
  * Cleans a value by removing non-evaluated properties and elements as derived from the provided type.
5
6
  * This function returns unknown so callers should Check the return value before use. This function
@@ -11,5 +12,6 @@ export function Clean(...args) {
11
12
  3: (context, type, value) => [context, type, value],
12
13
  2: (type, value) => [{}, type, value]
13
14
  });
14
- return FromType(context, type, value);
15
+ const sorted = Settings.Get().unionPrioritySort ? UnionPrioritySort(type) : type;
16
+ return FromType(context, sorted, value);
15
17
  }
@@ -1,2 +1,2 @@
1
- import type { TProperties, TUnion } from '../../type/index.mjs';
1
+ import { type TProperties, type TUnion } from '../../type/index.mjs';
2
2
  export declare function FromUnion(context: TProperties, type: TUnion, value: unknown): unknown;
@@ -2,9 +2,8 @@
2
2
  import { Check } from '../check/index.mjs';
3
3
  import { Clone } from '../clone/index.mjs';
4
4
  import { FromType } from './from_type.mjs';
5
- import { UnionPrioritySort } from '../shared/union_priority_sort.mjs';
6
5
  export function FromUnion(context, type, value) {
7
- for (const schema of UnionPrioritySort(type.anyOf)) {
6
+ for (const schema of type.anyOf) {
8
7
  const clean = FromType(context, schema, Clone(value));
9
8
  if (Check(context, schema, clean))
10
9
  return clean;
@@ -1,5 +1,5 @@
1
1
  // deno-fmt-ignore-file
2
- import { Arguments } from '../../system/arguments/index.mjs';
2
+ import { Arguments, Settings } from '../../system/index.mjs';
3
3
  import { AssertError } from '../assert/index.mjs';
4
4
  import { Check } from '../check/index.mjs';
5
5
  import { Errors } from '../errors/index.mjs';
@@ -9,6 +9,7 @@ import { Convert } from '../convert/index.mjs';
9
9
  import { Default } from '../default/index.mjs';
10
10
  import { Pipeline } from '../pipeline/index.mjs';
11
11
  import { FromType } from './from_type.mjs';
12
+ import { UnionPrioritySort } from '../shared/union_priority_sort.mjs';
12
13
  // ------------------------------------------------------------------
13
14
  // Assert
14
15
  // ------------------------------------------------------------------
@@ -27,7 +28,8 @@ function Assert(context, type, value) {
27
28
  // ------------------------------------------------------------------
28
29
  /** Executes Decode callbacks only */
29
30
  export function DecodeUnsafe(context, type, value) {
30
- return FromType('Decode', context, type, value);
31
+ const sorted = Settings.Get().unionPrioritySort ? UnionPrioritySort(type) : type;
32
+ return FromType('Decode', context, sorted, value);
31
33
  }
32
34
  // ------------------------------------------------------------------
33
35
  // Decoder
@@ -1,5 +1,5 @@
1
1
  // deno-fmt-ignore-file
2
- import { Arguments } from '../../system/arguments/index.mjs';
2
+ import { Arguments, Settings } from '../../system/index.mjs';
3
3
  import { AssertError } from '../assert/index.mjs';
4
4
  import { Check } from '../check/index.mjs';
5
5
  import { Errors } from '../errors/index.mjs';
@@ -9,6 +9,7 @@ import { Convert } from '../convert/index.mjs';
9
9
  import { Default } from '../default/index.mjs';
10
10
  import { Pipeline } from '../pipeline/index.mjs';
11
11
  import { FromType } from './from_type.mjs';
12
+ import { UnionPrioritySort } from '../shared/union_priority_sort.mjs';
12
13
  // ------------------------------------------------------------------
13
14
  // Assert
14
15
  // ------------------------------------------------------------------
@@ -27,7 +28,8 @@ function Assert(context, type, value) {
27
28
  // ------------------------------------------------------------------
28
29
  /** Executes Encode callbacks only */
29
30
  export function EncodeUnsafe(context, type, value) {
30
- return FromType('Encode', context, type, value);
31
+ const sorted = Settings.Get().unionPrioritySort ? UnionPrioritySort(type) : type;
32
+ return FromType('Encode', context, sorted, value);
31
33
  }
32
34
  // ------------------------------------------------------------------
33
35
  // Encoder
@@ -4,12 +4,11 @@ import { Callback } from './callback.mjs';
4
4
  import { FromType } from './from_type.mjs';
5
5
  import { Clone } from '../clone/index.mjs';
6
6
  import { Check } from '../check/index.mjs';
7
- import { UnionPrioritySort } from '../shared/union_priority_sort.mjs';
8
7
  // ------------------------------------------------------------------
9
8
  // Decode
10
9
  // ------------------------------------------------------------------
11
10
  function Decode(direction, context, type, value) {
12
- for (const schema of UnionPrioritySort(type.anyOf, 1)) {
11
+ for (const schema of type.anyOf) {
13
12
  if (!Check(context, schema, value))
14
13
  continue;
15
14
  const variant = FromType(direction, context, schema, value);
@@ -22,7 +21,7 @@ function Decode(direction, context, type, value) {
22
21
  // ------------------------------------------------------------------
23
22
  function Encode(direction, context, type, value) {
24
23
  const exterior = Callback(direction, context, type, value);
25
- for (const schema of UnionPrioritySort(type.anyOf, -1)) {
24
+ for (const schema of type.anyOf) {
26
25
  const variant = FromType(direction, context, schema, Clone(exterior));
27
26
  if (!Check(context, schema, variant))
28
27
  continue;
@@ -1,3 +1,7 @@
1
1
  import { type TSchema } from '../../type/index.mjs';
2
- /** Deterministically sorts schemas by structural relationship (narrow to broad) */
3
- export declare function UnionPrioritySort(types: TSchema[], order?: number): TSchema[];
2
+ /**
3
+ * (Type-Preprocessor) Recursively reorders Union variants from narrowest to broadest, ensuring
4
+ * more specific types (e.g. Literal) are evaluated before broader types (e.g. String). Used
5
+ * prior to Clean, Decode, and Encode operations.
6
+ */
7
+ export declare function UnionPrioritySort(type: TSchema): TSchema;
@@ -1,36 +1,72 @@
1
1
  // deno-fmt-ignore-file
2
- import { Guard } from '../../guard/index.mjs';
3
- import { Compare } from '../../type/index.mjs';
2
+ import Guard from '../../guard/index.mjs';
3
+ import { IsArray, Array as _Array_, ArrayOptions } from '../../type/index.mjs';
4
+ import { IsUnion, Union } from '../../type/index.mjs';
5
+ import { IsObject, Object as _Object_ } from '../../type/index.mjs';
6
+ import { IsRecord, Record, RecordKey, RecordValue } from '../../type/index.mjs';
7
+ import { IsTuple, Tuple } from '../../type/index.mjs';
8
+ import { IsIntersect, Intersect } from '../../type/index.mjs';
9
+ import { Priority } from '../../type/index.mjs';
4
10
  // ------------------------------------------------------------------
5
- // DeterministicCompare
11
+ // Modifiers (Mutable)
6
12
  //
7
- // Provides a deterministic tie-break for schemas. This is used when
8
- // schemas are structurally disjoint or mutually inclusive. While
9
- // JSON serialization incurs a performance overhead, it serves as a
10
- // reliable mechanism to ensure stable ordering and preserves the
11
- // alphabetical alignment of named constants.
13
+ // Prioritized types lose `~modifier` properties and additional constraints
14
+ // (e.g. additionalProperties) during the mapping phase and need to be
15
+ // reassigned afterward. This is a fast mutable assignment to handle that,
16
+ // but we should consider a more general solution. (review)
12
17
  //
13
18
  // ------------------------------------------------------------------
14
- function DeterministicCompare(left, right) {
15
- return JSON.stringify(left).localeCompare(JSON.stringify(right));
19
+ function Modifiers(type, next) {
20
+ for (const key of Guard.Keys(type)) {
21
+ if (Guard.HasPropertyKey(next, key))
22
+ continue;
23
+ next[key] = type[key];
24
+ }
25
+ return next;
26
+ }
27
+ // ------------------------------------------------------------------
28
+ // Properties
29
+ // ------------------------------------------------------------------
30
+ function FromProperties(properties) {
31
+ const result = {};
32
+ for (const key of Guard.Keys(properties))
33
+ result[key] = FromType(properties[key]);
34
+ return result;
35
+ }
36
+ // ------------------------------------------------------------------
37
+ // PriorityTypes
38
+ // ------------------------------------------------------------------
39
+ function FromPriorityTypes(types) {
40
+ return FromTypes(Priority(types));
41
+ }
42
+ // ------------------------------------------------------------------
43
+ // Types
44
+ // ------------------------------------------------------------------
45
+ function FromTypes(types) {
46
+ return types.map(type => FromType(type));
47
+ }
48
+ // ------------------------------------------------------------------
49
+ // Type
50
+ // ------------------------------------------------------------------
51
+ function FromType(type) {
52
+ const next = (IsArray(type) ? _Array_(FromType(type.items), ArrayOptions(type)) :
53
+ IsIntersect(type) ? Intersect(FromTypes(type.allOf)) :
54
+ IsUnion(type) ? Union(FromPriorityTypes(type.anyOf)) :
55
+ IsObject(type) ? _Object_(FromProperties(type.properties)) :
56
+ IsRecord(type) ? Record(RecordKey(type), FromType(RecordValue(type))) :
57
+ IsTuple(type) ? Tuple(FromTypes(type.items)) :
58
+ type);
59
+ return Modifiers(type, next);
16
60
  }
17
61
  // ------------------------------------------------------------------
18
62
  // UnionPrioritySort
19
- //
20
- // Performs a deterministic sort on Union members. By default, this
21
- // function ensures that narrow (more specific) types precede broader
22
- // types in the resulting array. The order can be reversed by setting
23
- // the order property to -1 which will reverse unions from broader
24
- // to more narrow.
25
- //
26
63
  // ------------------------------------------------------------------
27
- /** Deterministically sorts schemas by structural relationship (narrow to broad) */
28
- export function UnionPrioritySort(types, order = 1) {
29
- return [...types].sort((left, right) => {
30
- const result = Compare(left, right);
31
- return (Guard.IsEqual(result, 'disjoint') ? DeterministicCompare(left, right) :
32
- Guard.IsEqual(result, 'right-inside') ? 1 :
33
- Guard.IsEqual(result, 'left-inside') ? -1 :
34
- DeterministicCompare(left, right)) * order;
35
- });
64
+ /**
65
+ * (Type-Preprocessor) Recursively reorders Union variants from narrowest to broadest, ensuring
66
+ * more specific types (e.g. Literal) are evaluated before broader types (e.g. String). Used
67
+ * prior to Clean, Decode, and Encode operations.
68
+ */
69
+ export function UnionPrioritySort(type) {
70
+ const result = FromType(type);
71
+ return result;
36
72
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "typebox",
3
3
  "description": "Json Schema Type Builder with Static Type Resolution for TypeScript",
4
- "version": "1.2.13",
4
+ "version": "1.2.15",
5
5
  "keywords": [
6
6
  "typescript",
7
7
  "jsonschema"