typia 3.4.20 → 3.4.21

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.
@@ -1,6 +1,7 @@
1
1
  import { CommentFactory } from "../../factories/CommentFactory";
2
2
 
3
3
  import { IJsDocTagInfo } from "../../metadata/IJsDocTagInfo";
4
+ import { Metadata } from "../../metadata/Metadata";
4
5
  import { MetadataObject } from "../../metadata/MetadataObject";
5
6
  import { IJsonComponents } from "../../schemas/IJsonComponents";
6
7
 
@@ -24,8 +25,10 @@ export const application_object =
24
25
 
25
26
  // ITERATE PROPERTIES
26
27
  const properties: Record<string, any> = {};
27
- const patternProperties: Record<string, any> = {};
28
- const additionalProperties: IJsonSchema[] = [];
28
+ const extraMeta: ISuperfluous = {
29
+ patternProperties: {},
30
+ additionalProperties: undefined,
31
+ };
29
32
  const required: string[] = [];
30
33
 
31
34
  for (const property of obj.properties) {
@@ -38,7 +41,7 @@ export const application_object =
38
41
  continue;
39
42
 
40
43
  const key: string | null = property.key.getSoleLiteral();
41
- const value: IJsonSchema | null = application_schema(options)(
44
+ const schema: IJsonSchema | null = application_schema(options)(
42
45
  components,
43
46
  )(true)(property.value, {
44
47
  deprecated:
@@ -62,21 +65,35 @@ export const application_object =
62
65
  "x-typia-required": property.value.required,
63
66
  });
64
67
 
65
- if (value === null) continue;
68
+ if (schema === null) continue;
66
69
  else if (key !== null) {
67
- properties[key] = value;
70
+ properties[key] = schema;
68
71
  if (property.value.required === true) required.push(key);
69
72
  } else {
70
73
  const pattern: string = metadata_to_pattern(true)(property.key);
71
- if (
72
- options.purpose === "swagger" ||
73
- pattern === PatternUtil.STRING
74
- )
75
- additionalProperties.push(value);
76
- else patternProperties[pattern] = value;
74
+ if (pattern === PatternUtil.STRING)
75
+ extraMeta.additionalProperties = [property.value, schema];
76
+ else
77
+ extraMeta.patternProperties[pattern] = [
78
+ property.value,
79
+ schema,
80
+ ];
77
81
  }
78
82
  }
79
83
 
84
+ const extraProps = {
85
+ additionalProperties: extraMeta.additionalProperties?.[1],
86
+ patternProperties: (() => {
87
+ if (Object.keys(extraMeta.patternProperties).length === 0)
88
+ return undefined;
89
+ const output: Record<string, IJsonSchema> = {};
90
+ for (const [key, value] of Object.entries(
91
+ extraMeta.patternProperties,
92
+ ))
93
+ output[key] = value[1];
94
+ return output;
95
+ })(),
96
+ };
80
97
  const schema: IJsonComponents.IObject = {
81
98
  $id:
82
99
  options.purpose === "ajv"
@@ -86,18 +103,51 @@ export const application_object =
86
103
  (options.purpose === "ajv" && obj.recursive) || undefined,
87
104
  type: "object",
88
105
  properties,
89
- patternProperties: Object.keys(patternProperties).length
90
- ? patternProperties
91
- : undefined,
92
- additionalProperties: additionalProperties.length
93
- ? additionalProperties.length === 1
94
- ? additionalProperties[0]
95
- : { oneOf: additionalProperties }
96
- : undefined,
97
106
  nullable,
98
107
  required: required.length ? required : undefined,
99
108
  description: obj.description,
100
- "x-typia_jsDocTags": obj.jsDocTags,
109
+ "x-typia-jsDocTags": obj.jsDocTags,
110
+ ...(options.purpose === "ajv"
111
+ ? extraProps
112
+ : {
113
+ // swagger can't express patternProperties
114
+ "x-typia-additionalProperties":
115
+ extraProps.additionalProperties,
116
+ "x-typia-patternProperties": extraProps.patternProperties,
117
+ additionalProperties:
118
+ join(options)(components)(extraMeta),
119
+ }),
101
120
  };
102
121
  components.schemas[key] = schema;
103
122
  };
123
+
124
+ const join =
125
+ (options: ApplicationProgrammer.IOptions) =>
126
+ (components: IJsonComponents) =>
127
+ (extra: ISuperfluous): IJsonSchema | undefined => {
128
+ // LIST UP METADATA
129
+ const elements: [Metadata, IJsonSchema][] = Object.values(
130
+ extra.patternProperties || {},
131
+ );
132
+ if (extra.additionalProperties)
133
+ elements.push(extra.additionalProperties);
134
+
135
+ // SHORT RETURN
136
+ if (elements.length === 0) return undefined;
137
+ else if (elements.length === 1) return elements[0]![1]!;
138
+
139
+ // MERGE METADATA AND GENERATE VULNERABLE SCHEMA
140
+ const meta: Metadata = elements
141
+ .map((tuple) => tuple[0])
142
+ .reduce((x, y) => Metadata.merge(x, y));
143
+ return (
144
+ application_schema(options)(components)(true)(meta, {
145
+ "x-typia-required": false,
146
+ }) ?? undefined
147
+ );
148
+ };
149
+
150
+ interface ISuperfluous {
151
+ additionalProperties?: [Metadata, IJsonSchema];
152
+ patternProperties: Record<string, [Metadata, IJsonSchema]>;
153
+ }
@@ -1,10 +1,7 @@
1
1
  import { Metadata } from "../../metadata/Metadata";
2
- import { MetadataConstant } from "../../metadata/MetadataConstant";
3
2
  import { IJsonComponents } from "../../schemas/IJsonComponents";
4
3
  import { IJsonSchema } from "../../schemas/IJsonSchema";
5
4
 
6
- import { ArrayUtil } from "../../utils/ArrayUtil";
7
-
8
5
  import { ApplicationProgrammer } from "../ApplicationProgrammer";
9
6
  import { AtomicPredicator } from "../helpers/AtomicPredicator";
10
7
  import { application_array } from "./application_array";
@@ -74,7 +71,7 @@ export const application_schema =
74
71
  // ARRAY
75
72
  for (const schema of meta.arrays.values())
76
73
  union.push(
77
- application_array(options)(components)(
74
+ application_array(options)(components)()(
78
75
  schema,
79
76
  meta.nullable,
80
77
  attribute,
@@ -82,18 +79,11 @@ export const application_schema =
82
79
  );
83
80
 
84
81
  // TUPLE
85
- for (const items of meta.tuples)
86
- if (
87
- options.purpose === "ajv" &&
88
- items.every((i) => i.rest === null)
89
- )
90
- union.push(
91
- application_tuple(options)(components)(
92
- items,
93
- meta.nullable,
94
- attribute,
95
- ),
96
- );
82
+ for (const items of meta.tuples) {
83
+ const tuple: IJsonSchema.ITuple = application_tuple(options)(
84
+ components,
85
+ )(items, meta.nullable, attribute);
86
+ if (options.purpose === "ajv") union.push(tuple);
97
87
  else {
98
88
  if (items.length === 0)
99
89
  throw new Error(
@@ -102,16 +92,17 @@ export const application_schema =
102
92
 
103
93
  // SWAGGER DOES NOT SUPPORT TUPLE TYPE YET
104
94
  const merged: Metadata = items.reduce((x, y) =>
105
- merge_metadata(x, y),
95
+ Metadata.merge(x, y),
106
96
  );
107
97
  union.push(
108
- application_array(options)(components)(
98
+ application_array(options)(components)(tuple)(
109
99
  merged,
110
100
  merged?.nullable || false,
111
101
  attribute,
112
102
  ),
113
103
  );
114
104
  }
105
+ }
115
106
 
116
107
  // NATIVES
117
108
  for (const native of meta.natives)
@@ -177,57 +168,4 @@ const recursive = (
177
168
  ...attribute,
178
169
  });
179
170
 
180
- /**
181
- * @internal
182
- * @todo: not perfect
183
- */
184
- function merge_metadata(x: Metadata, y: Metadata): Metadata {
185
- const output: Metadata = Metadata.create({
186
- any: x.any || y.any,
187
- nullable: x.nullable || y.nullable,
188
- required: x.required && y.required,
189
- functional: x.functional || y.functional,
190
-
191
- resolved:
192
- x.resolved !== null && y.resolved !== null
193
- ? merge_metadata(x.resolved, y.resolved)
194
- : x.resolved || y.resolved,
195
- atomics: [...new Set([...x.atomics, ...y.atomics])],
196
- constants: [...x.constants],
197
- templates: x.templates.slice(),
198
-
199
- rest: null,
200
- arrays: x.arrays.slice(),
201
- tuples: x.tuples.slice(),
202
- objects: x.objects.slice(),
203
-
204
- natives: [...new Set([...x.natives, ...y.natives])],
205
- sets: x.sets.slice(),
206
- maps: x.maps.slice(),
207
- });
208
- for (const constant of y.constants) {
209
- const target: MetadataConstant = ArrayUtil.take(
210
- output.constants,
211
- (elem) => elem.type === constant.type,
212
- () => ({
213
- type: constant.type,
214
- values: [],
215
- }),
216
- );
217
- for (const value of constant.values)
218
- ArrayUtil.add(target.values, value);
219
- }
220
- for (const array of y.arrays)
221
- ArrayUtil.set(output.arrays, array, (elem) => elem.getName());
222
- for (const obj of y.objects)
223
- ArrayUtil.set(output.objects, obj, (elem) => elem.name);
224
-
225
- if (x.rest !== null)
226
- ArrayUtil.set(output.arrays, x.rest, (elem) => elem.getName());
227
- if (y.rest !== null)
228
- ArrayUtil.set(output.arrays, y.rest, (elem) => elem.getName());
229
-
230
- return output;
231
- }
232
-
233
171
  const NO_BIGINT = "Error on typia.application(): does not allow bigint type.";
@@ -17,8 +17,12 @@ export const application_tuple =
17
17
  attribute: IJsonSchema.IAttribute,
18
18
  ): IJsonSchema.ITuple => ({
19
19
  type: "array",
20
- items: items.map((meta) =>
21
- application_schema(options)(components)(false)(meta, attribute),
20
+ items: items.map((meta, i) =>
21
+ application_schema(options)(components)(false)(meta.rest ?? meta, {
22
+ ...attribute,
23
+ "x-typia-rest":
24
+ i === items.length - 1 ? meta.rest !== null : undefined,
25
+ }),
22
26
  ),
23
27
  nullable,
24
28
  ...attribute,
@@ -1,22 +1,22 @@
1
- import ts from "typescript";
2
-
3
- import { ExpressionFactory } from "../../factories/ExpressionFactory";
4
-
5
- import { IMetadataTag } from "../../metadata/IMetadataTag";
6
-
7
- import { check_array_length } from "./check_array_length";
8
-
9
- /**
10
- * @internal
11
- */
12
- export function check_array(
13
- input: ts.Expression,
14
- tagList: IMetadataTag[],
15
- ): ts.Expression {
16
- const conditions: ts.Expression[] = [ExpressionFactory.isArray(input)];
17
-
18
- const length: ts.Expression | null = check_array_length(input, tagList);
19
- if (length !== null) conditions.push(length);
20
-
21
- return conditions.reduce((x, y) => ts.factory.createLogicalAnd(x, y));
22
- }
1
+ import ts from "typescript";
2
+
3
+ import { ExpressionFactory } from "../../factories/ExpressionFactory";
4
+
5
+ import { IMetadataTag } from "../../metadata/IMetadataTag";
6
+
7
+ import { check_array_length } from "./check_array_length";
8
+
9
+ /**
10
+ * @internal
11
+ */
12
+ export function check_array(
13
+ input: ts.Expression,
14
+ tagList: IMetadataTag[],
15
+ ): ts.Expression {
16
+ const conditions: ts.Expression[] = [ExpressionFactory.isArray(input)];
17
+
18
+ const length: ts.Expression | null = check_array_length(input, tagList);
19
+ if (length !== null) conditions.push(length);
20
+
21
+ return conditions.reduce((x, y) => ts.factory.createLogicalAnd(x, y));
22
+ }
@@ -8,6 +8,8 @@ export interface IJsonComponents {
8
8
  export namespace IJsonComponents {
9
9
  export interface IObject {
10
10
  $id?: string;
11
+ $recursiveAnchor?: boolean;
12
+
11
13
  type: "object";
12
14
  nullable: boolean;
13
15
 
@@ -17,8 +19,8 @@ export namespace IJsonComponents {
17
19
 
18
20
  required?: string[];
19
21
  description?: string;
20
- "x-typia_jsDocTags"?: IJsDocTagInfo[];
21
-
22
- $recursiveAnchor?: boolean;
22
+ "x-typia-jsDocTags"?: IJsDocTagInfo[];
23
+ "x-typia-patternProperties"?: Record<string, IJsonSchema>;
24
+ "x-typia-additionalProperties"?: IJsonSchema;
23
25
  }
24
26
  }
@@ -53,6 +53,7 @@ export namespace IJsonSchema {
53
53
  items: IJsonSchema;
54
54
  minItems?: number;
55
55
  maxItems?: number;
56
+ "x-typia-tuple"?: ITuple;
56
57
  }
57
58
  export interface ITuple extends ISignificant<"array"> {
58
59
  items: IJsonSchema[];
@@ -86,5 +87,6 @@ export namespace IJsonSchema {
86
87
  "x-typia-metaTags"?: IMetadataTag[];
87
88
  "x-typia-jsDocTags"?: IJsDocTagInfo[];
88
89
  "x-typia-required"?: boolean;
90
+ "x-typia-rest"?: boolean;
89
91
  }
90
92
  }