@nestia/core 3.0.0 → 3.0.1-dev.20240412

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nestia/core",
3
- "version": "3.0.0",
3
+ "version": "3.0.1-dev.20240412",
4
4
  "description": "Super-fast validation decorators of NestJS",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
@@ -36,25 +36,25 @@
36
36
  },
37
37
  "homepage": "https://nestia.io",
38
38
  "dependencies": {
39
- "@nestia/fetcher": "^3.0.0",
39
+ "@nestia/fetcher": "^3.0.1-dev.20240412",
40
40
  "@nestjs/common": ">=7.0.1",
41
41
  "@nestjs/core": ">=7.0.1",
42
- "@samchon/openapi": "^0.1.2",
42
+ "@samchon/openapi": "^0.1.4",
43
43
  "detect-ts-node": "^1.0.5",
44
44
  "glob": "^7.2.0",
45
45
  "multer": "1.4.5-lts.1",
46
46
  "raw-body": "^2.0.0",
47
47
  "reflect-metadata": ">=0.1.12",
48
- "rxjs": ">=6.0.0",
49
- "typia": "^6.0.0"
48
+ "rxjs": ">=6.0.1",
49
+ "typia": "^6.0.1"
50
50
  },
51
51
  "peerDependencies": {
52
- "@nestia/fetcher": ">=3.0.0",
52
+ "@nestia/fetcher": ">=3.0.1-dev.20240412",
53
53
  "@nestjs/common": ">=7.0.1",
54
54
  "@nestjs/core": ">=7.0.1",
55
55
  "reflect-metadata": ">=0.1.12",
56
- "rxjs": ">=6.0.0",
57
- "typia": ">=6.0.0 <7.0.0"
56
+ "rxjs": ">=6.0.1",
57
+ "typia": ">=6.0.1 <7.0.0"
58
58
  },
59
59
  "devDependencies": {
60
60
  "@fastify/multipart": "^8.1.0",
@@ -1,115 +1,115 @@
1
- import { OpenApi } from "@samchon/openapi";
2
-
3
- /**
4
- * Swagger customization decorator.
5
- *
6
- * `SwaggerCustomizer` is a method decorator function which can used for
7
- * customizing the swagger data with `npx nestia swagger` command. Furthermore,
8
- * it is possible to add plugin properties starting with `x-` characters.
9
- *
10
- * In other words, this decorator function does not affect to the runtime,
11
- * but only for the swagger data customization.
12
- *
13
- * @param closure Callback function which can customize the swagger data
14
- * @returns Method decorator
15
- * @author Jeongho Nam - https://github.com/samchon
16
- */
17
- export function SwaggerCustomizer(
18
- closure: (props: SwaggerCustomizer.IProps) => unknown,
19
- ): MethodDecorator {
20
- return function SwaggerCustomizer(
21
- target: Object,
22
- propertyKey: string | symbol,
23
- descriptor: TypedPropertyDescriptor<any>,
24
- ) {
25
- const array: Array<(props: SwaggerCustomizer.IProps) => unknown> = (() => {
26
- if (Reflect.hasMetadata("nestia/SwaggerCustomizer", target, propertyKey))
27
- return Reflect.getMetadata(
28
- "nestia/SwaggerCustomizer",
29
- target,
30
- propertyKey,
31
- );
32
- const array: Array<(props: SwaggerCustomizer.IProps) => unknown> = [];
33
- Reflect.defineMetadata(
34
- "nestia/SwaggerCustomizer",
35
- array,
36
- target,
37
- propertyKey,
38
- );
39
- return array;
40
- })();
41
- array.push(closure);
42
- return descriptor;
43
- };
44
- }
45
- export namespace SwaggerCustomizer {
46
- /**
47
- * Properties for the `SwaggerCustomizer` decorator.
48
- *
49
- * `SwaggerCustomizer.IProps` is a type for the `closure` parameter of the
50
- * `SwaggerCustomizer` decorator. It's a callback function which can customize
51
- * the swagger data.
52
- */
53
- export interface IProps {
54
- /**
55
- * Swagger data.
56
- */
57
- swagger: OpenApi.IDocument;
58
-
59
- /**
60
- * Method of the route.
61
- */
62
- method: string;
63
-
64
- /**
65
- * Path of the route.
66
- */
67
- path: string;
68
-
69
- /**
70
- * Route data.
71
- */
72
- route: OpenApi.IOperation;
73
-
74
- /**
75
- * Get neighbor endpoint data through the controller method.
76
- *
77
- * @param func Controller method to find the neighbor endpoint
78
- * @returns Neighbor endpoint data
79
- */
80
- at(func: Function): ISwaggerEndpoint | undefined;
81
-
82
- /**
83
- * Get neighbor route data.
84
- *
85
- * @param accessor Accessor for getting neighbor route data
86
- * @returns Neighbor route data
87
- */
88
- get(accessor: IAccessor): OpenApi.IOperation | undefined;
89
- }
90
-
91
- /**
92
- * Accessor for getting neighbor route data.
93
- */
94
- export interface IAccessor {
95
- /**
96
- * Path of the neighbor route.
97
- */
98
- path: string;
99
-
100
- /**
101
- * Method of the neighbor route.
102
- */
103
- method: string;
104
- }
105
-
106
- /**
107
- * Endpoint info of the route.
108
- */
109
- export interface ISwaggerEndpoint extends IAccessor {
110
- /**
111
- * Route data.
112
- */
113
- route: OpenApi.IOperation;
114
- }
115
- }
1
+ import { OpenApi } from "@samchon/openapi";
2
+
3
+ /**
4
+ * Swagger customization decorator.
5
+ *
6
+ * `SwaggerCustomizer` is a method decorator function which can used for
7
+ * customizing the swagger data with `npx nestia swagger` command. Furthermore,
8
+ * it is possible to add plugin properties starting with `x-` characters.
9
+ *
10
+ * In other words, this decorator function does not affect to the runtime,
11
+ * but only for the swagger data customization.
12
+ *
13
+ * @param closure Callback function which can customize the swagger data
14
+ * @returns Method decorator
15
+ * @author Jeongho Nam - https://github.com/samchon
16
+ */
17
+ export function SwaggerCustomizer(
18
+ closure: (props: SwaggerCustomizer.IProps) => unknown,
19
+ ): MethodDecorator {
20
+ return function SwaggerCustomizer(
21
+ target: Object,
22
+ propertyKey: string | symbol,
23
+ descriptor: TypedPropertyDescriptor<any>,
24
+ ) {
25
+ const array: Array<(props: SwaggerCustomizer.IProps) => unknown> = (() => {
26
+ if (Reflect.hasMetadata("nestia/SwaggerCustomizer", target, propertyKey))
27
+ return Reflect.getMetadata(
28
+ "nestia/SwaggerCustomizer",
29
+ target,
30
+ propertyKey,
31
+ );
32
+ const array: Array<(props: SwaggerCustomizer.IProps) => unknown> = [];
33
+ Reflect.defineMetadata(
34
+ "nestia/SwaggerCustomizer",
35
+ array,
36
+ target,
37
+ propertyKey,
38
+ );
39
+ return array;
40
+ })();
41
+ array.push(closure);
42
+ return descriptor;
43
+ };
44
+ }
45
+ export namespace SwaggerCustomizer {
46
+ /**
47
+ * Properties for the `SwaggerCustomizer` decorator.
48
+ *
49
+ * `SwaggerCustomizer.IProps` is a type for the `closure` parameter of the
50
+ * `SwaggerCustomizer` decorator. It's a callback function which can customize
51
+ * the swagger data.
52
+ */
53
+ export interface IProps {
54
+ /**
55
+ * Swagger data.
56
+ */
57
+ swagger: OpenApi.IDocument;
58
+
59
+ /**
60
+ * Method of the route.
61
+ */
62
+ method: string;
63
+
64
+ /**
65
+ * Path of the route.
66
+ */
67
+ path: string;
68
+
69
+ /**
70
+ * Route data.
71
+ */
72
+ route: OpenApi.IOperation;
73
+
74
+ /**
75
+ * Get neighbor endpoint data through the controller method.
76
+ *
77
+ * @param func Controller method to find the neighbor endpoint
78
+ * @returns Neighbor endpoint data
79
+ */
80
+ at(func: Function): ISwaggerEndpoint | undefined;
81
+
82
+ /**
83
+ * Get neighbor route data.
84
+ *
85
+ * @param accessor Accessor for getting neighbor route data
86
+ * @returns Neighbor route data
87
+ */
88
+ get(accessor: IAccessor): OpenApi.IOperation | undefined;
89
+ }
90
+
91
+ /**
92
+ * Accessor for getting neighbor route data.
93
+ */
94
+ export interface IAccessor {
95
+ /**
96
+ * Path of the neighbor route.
97
+ */
98
+ path: string;
99
+
100
+ /**
101
+ * Method of the neighbor route.
102
+ */
103
+ method: string;
104
+ }
105
+
106
+ /**
107
+ * Endpoint info of the route.
108
+ */
109
+ export interface ISwaggerEndpoint extends IAccessor {
110
+ /**
111
+ * Route data.
112
+ */
113
+ route: OpenApi.IOperation;
114
+ }
115
+ }
@@ -1,108 +1,108 @@
1
- import ts from "typescript";
2
- import { LiteralFactory } from "typia/lib/factories/LiteralFactory";
3
- import { MetadataCollection } from "typia/lib/factories/MetadataCollection";
4
- import { MetadataFactory } from "typia/lib/factories/MetadataFactory";
5
- import { HttpAssertFormDataProgrammer } from "typia/lib/programmers/http/HttpAssertFormDataProgrammer";
6
- import { HttpFormDataProgrammer } from "typia/lib/programmers/http/HttpFormDataProgrammer";
7
- import { HttpIsFormDataProgrammer } from "typia/lib/programmers/http/HttpIsFormDataProgrammer";
8
- import { HttpValidateFormDataProgrammer } from "typia/lib/programmers/http/HttpValidateFormDataProgrammer";
9
- import { Metadata } from "typia/lib/schemas/metadata/Metadata";
10
- import { IProject } from "typia/lib/transformers/IProject";
11
- import { TransformerError } from "typia/lib/transformers/TransformerError";
12
-
13
- import { INestiaTransformProject } from "../options/INestiaTransformProject";
14
- import { IRequestFormDataProps } from "../options/IRequestFormDataProps";
15
-
16
- export namespace TypedFormDataBodyProgrammer {
17
- export const generate =
18
- (project: INestiaTransformProject) =>
19
- (modulo: ts.LeftHandSideExpression) =>
20
- (type: ts.Type): ts.ObjectLiteralExpression => {
21
- // VALIDATE TYPE
22
- const collection: MetadataCollection = new MetadataCollection();
23
- const result = MetadataFactory.analyze(
24
- project.checker,
25
- project.context,
26
- )({
27
- escape: false,
28
- constant: true,
29
- absorb: true,
30
- validate: HttpFormDataProgrammer.validate,
31
- })(collection)(type);
32
- if (result.success === false)
33
- throw TransformerError.from("nestia.core.TypedFormData.Body")(
34
- result.errors,
35
- );
36
-
37
- const files: IRequestFormDataProps.IFile[] =
38
- result.data.objects[0].properties
39
- .filter(
40
- (p) =>
41
- isFile(p.value) ||
42
- p.value.arrays.some((a) => isFile(a.type.value)),
43
- )
44
- .map((p) => ({
45
- name: p.key.constants[0].values[0].value as string,
46
- limit: !!p.value.natives.length ? 1 : null,
47
- }));
48
-
49
- // GENERATE VALIDATION PLAN
50
- const parameter =
51
- (key: IRequestFormDataProps<any>["validator"]["type"]) =>
52
- (
53
- programmer: (
54
- project: IProject,
55
- ) => (
56
- modulo: ts.LeftHandSideExpression,
57
- ) => (type: ts.Type) => ts.ArrowFunction,
58
- ) =>
59
- ts.factory.createObjectLiteralExpression(
60
- [
61
- ts.factory.createPropertyAssignment(
62
- ts.factory.createIdentifier("files"),
63
- LiteralFactory.generate(files),
64
- ),
65
- ts.factory.createPropertyAssignment(
66
- ts.factory.createIdentifier("validator"),
67
- ts.factory.createObjectLiteralExpression(
68
- [
69
- ts.factory.createPropertyAssignment(
70
- ts.factory.createIdentifier("type"),
71
- ts.factory.createStringLiteral(key),
72
- ),
73
- ts.factory.createPropertyAssignment(
74
- ts.factory.createIdentifier(key),
75
- programmer({
76
- ...project,
77
- options: {
78
- numeric: false,
79
- finite: false,
80
- functional: false,
81
- },
82
- })(modulo)(type),
83
- ),
84
- ],
85
- true,
86
- ),
87
- ),
88
- ],
89
- true,
90
- );
91
-
92
- // RETURNS
93
- const category = project.options.validate;
94
- if (category === "is" || category === "equals")
95
- return parameter("is")(HttpIsFormDataProgrammer.write);
96
- else if (
97
- category === "validate" ||
98
- category === "validateEquals" ||
99
- category === "validateClone" ||
100
- category === "validatePrune"
101
- )
102
- return parameter("validate")(HttpValidateFormDataProgrammer.write);
103
- return parameter("assert")(HttpAssertFormDataProgrammer.write);
104
- };
105
- }
106
-
107
- const isFile = (meta: Metadata) =>
108
- meta.natives.some((n) => n === "File" || n === "Blob");
1
+ import ts from "typescript";
2
+ import { LiteralFactory } from "typia/lib/factories/LiteralFactory";
3
+ import { MetadataCollection } from "typia/lib/factories/MetadataCollection";
4
+ import { MetadataFactory } from "typia/lib/factories/MetadataFactory";
5
+ import { HttpAssertFormDataProgrammer } from "typia/lib/programmers/http/HttpAssertFormDataProgrammer";
6
+ import { HttpFormDataProgrammer } from "typia/lib/programmers/http/HttpFormDataProgrammer";
7
+ import { HttpIsFormDataProgrammer } from "typia/lib/programmers/http/HttpIsFormDataProgrammer";
8
+ import { HttpValidateFormDataProgrammer } from "typia/lib/programmers/http/HttpValidateFormDataProgrammer";
9
+ import { Metadata } from "typia/lib/schemas/metadata/Metadata";
10
+ import { IProject } from "typia/lib/transformers/IProject";
11
+ import { TransformerError } from "typia/lib/transformers/TransformerError";
12
+
13
+ import { INestiaTransformProject } from "../options/INestiaTransformProject";
14
+ import { IRequestFormDataProps } from "../options/IRequestFormDataProps";
15
+
16
+ export namespace TypedFormDataBodyProgrammer {
17
+ export const generate =
18
+ (project: INestiaTransformProject) =>
19
+ (modulo: ts.LeftHandSideExpression) =>
20
+ (type: ts.Type): ts.ObjectLiteralExpression => {
21
+ // VALIDATE TYPE
22
+ const collection: MetadataCollection = new MetadataCollection();
23
+ const result = MetadataFactory.analyze(
24
+ project.checker,
25
+ project.context,
26
+ )({
27
+ escape: false,
28
+ constant: true,
29
+ absorb: true,
30
+ validate: HttpFormDataProgrammer.validate,
31
+ })(collection)(type);
32
+ if (result.success === false)
33
+ throw TransformerError.from("nestia.core.TypedFormData.Body")(
34
+ result.errors,
35
+ );
36
+
37
+ const files: IRequestFormDataProps.IFile[] =
38
+ result.data.objects[0].properties
39
+ .filter(
40
+ (p) =>
41
+ isFile(p.value) ||
42
+ p.value.arrays.some((a) => isFile(a.type.value)),
43
+ )
44
+ .map((p) => ({
45
+ name: p.key.constants[0].values[0].value as string,
46
+ limit: !!p.value.natives.length ? 1 : null,
47
+ }));
48
+
49
+ // GENERATE VALIDATION PLAN
50
+ const parameter =
51
+ (key: IRequestFormDataProps<any>["validator"]["type"]) =>
52
+ (
53
+ programmer: (
54
+ project: IProject,
55
+ ) => (
56
+ modulo: ts.LeftHandSideExpression,
57
+ ) => (type: ts.Type) => ts.ArrowFunction,
58
+ ) =>
59
+ ts.factory.createObjectLiteralExpression(
60
+ [
61
+ ts.factory.createPropertyAssignment(
62
+ ts.factory.createIdentifier("files"),
63
+ LiteralFactory.generate(files),
64
+ ),
65
+ ts.factory.createPropertyAssignment(
66
+ ts.factory.createIdentifier("validator"),
67
+ ts.factory.createObjectLiteralExpression(
68
+ [
69
+ ts.factory.createPropertyAssignment(
70
+ ts.factory.createIdentifier("type"),
71
+ ts.factory.createStringLiteral(key),
72
+ ),
73
+ ts.factory.createPropertyAssignment(
74
+ ts.factory.createIdentifier(key),
75
+ programmer({
76
+ ...project,
77
+ options: {
78
+ numeric: false,
79
+ finite: false,
80
+ functional: false,
81
+ },
82
+ })(modulo)(type),
83
+ ),
84
+ ],
85
+ true,
86
+ ),
87
+ ),
88
+ ],
89
+ true,
90
+ );
91
+
92
+ // RETURNS
93
+ const category = project.options.validate;
94
+ if (category === "is" || category === "equals")
95
+ return parameter("is")(HttpIsFormDataProgrammer.write);
96
+ else if (
97
+ category === "validate" ||
98
+ category === "validateEquals" ||
99
+ category === "validateClone" ||
100
+ category === "validatePrune"
101
+ )
102
+ return parameter("validate")(HttpValidateFormDataProgrammer.write);
103
+ return parameter("assert")(HttpAssertFormDataProgrammer.write);
104
+ };
105
+ }
106
+
107
+ const isFile = (meta: Metadata) =>
108
+ meta.natives.some((n) => n === "File" || n === "Blob");
@@ -1,96 +1,96 @@
1
- import ts from "typescript";
2
- import { IdentifierFactory } from "typia/lib/factories/IdentifierFactory";
3
- import { MetadataCollection } from "typia/lib/factories/MetadataCollection";
4
- import { MetadataFactory } from "typia/lib/factories/MetadataFactory";
5
- import { StatementFactory } from "typia/lib/factories/StatementFactory";
6
- import { FunctionImporter } from "typia/lib/programmers/helpers/FunctionImporeter";
7
- import { HttpQueryProgrammer } from "typia/lib/programmers/http/HttpQueryProgrammer";
8
- import { Metadata } from "typia/lib/schemas/metadata/Metadata";
9
- import { MetadataObject } from "typia/lib/schemas/metadata/MetadataObject";
10
- import { IProject } from "typia/lib/transformers/IProject";
11
- import { TransformerError } from "typia/lib/transformers/TransformerError";
12
-
13
- export namespace HttpQuerifyProgrammer {
14
- export const write =
15
- (project: IProject) =>
16
- (modulo: ts.LeftHandSideExpression) =>
17
- (type: ts.Type): ts.ArrowFunction => {
18
- // GET OBJECT TYPE
19
- const importer: FunctionImporter = new FunctionImporter(modulo.getText());
20
- const collection: MetadataCollection = new MetadataCollection();
21
- const result = MetadataFactory.analyze(project.checker)({
22
- escape: false,
23
- constant: true,
24
- absorb: true,
25
- validate: HttpQueryProgrammer.validate,
26
- })(collection)(type);
27
- if (result.success === false)
28
- throw TransformerError.from(
29
- `nestia.core.TypedQuery.${importer.method}`,
30
- )(result.errors);
31
-
32
- const object: MetadataObject = result.data.objects[0]!;
33
- return ts.factory.createArrowFunction(
34
- undefined,
35
- undefined,
36
- [IdentifierFactory.parameter("input")],
37
- undefined,
38
- undefined,
39
- ts.factory.createBlock(
40
- [
41
- ...importer.declare(modulo),
42
- StatementFactory.constant(
43
- "output",
44
- ts.factory.createNewExpression(
45
- ts.factory.createIdentifier("URLSearchParams"),
46
- undefined,
47
- [],
48
- ),
49
- ),
50
- ...object.properties.map((p) =>
51
- ts.factory.createExpressionStatement(
52
- decode(p.key.constants[0]!.values[0].value as string)(p.value),
53
- ),
54
- ),
55
- ts.factory.createReturnStatement(
56
- ts.factory.createIdentifier("output"),
57
- ),
58
- ],
59
- true,
60
- ),
61
- );
62
- };
63
-
64
- const decode =
65
- (key: string) =>
66
- (value: Metadata): ts.CallExpression =>
67
- !!value.arrays.length
68
- ? ts.factory.createCallExpression(
69
- IdentifierFactory.access(
70
- IdentifierFactory.access(ts.factory.createIdentifier("input"))(
71
- key,
72
- ),
73
- )("forEach"),
74
- undefined,
75
- [
76
- ts.factory.createArrowFunction(
77
- undefined,
78
- undefined,
79
- [IdentifierFactory.parameter("elem")],
80
- undefined,
81
- undefined,
82
- append(key)(ts.factory.createIdentifier("elem")),
83
- ),
84
- ],
85
- )
86
- : append(key)(
87
- IdentifierFactory.access(ts.factory.createIdentifier("input"))(key),
88
- );
89
-
90
- const append = (key: string) => (elem: ts.Expression) =>
91
- ts.factory.createCallExpression(
92
- IdentifierFactory.access(ts.factory.createIdentifier("output"))("append"),
93
- undefined,
94
- [ts.factory.createStringLiteral(key), elem],
95
- );
96
- }
1
+ import ts from "typescript";
2
+ import { IdentifierFactory } from "typia/lib/factories/IdentifierFactory";
3
+ import { MetadataCollection } from "typia/lib/factories/MetadataCollection";
4
+ import { MetadataFactory } from "typia/lib/factories/MetadataFactory";
5
+ import { StatementFactory } from "typia/lib/factories/StatementFactory";
6
+ import { FunctionImporter } from "typia/lib/programmers/helpers/FunctionImporeter";
7
+ import { HttpQueryProgrammer } from "typia/lib/programmers/http/HttpQueryProgrammer";
8
+ import { Metadata } from "typia/lib/schemas/metadata/Metadata";
9
+ import { MetadataObject } from "typia/lib/schemas/metadata/MetadataObject";
10
+ import { IProject } from "typia/lib/transformers/IProject";
11
+ import { TransformerError } from "typia/lib/transformers/TransformerError";
12
+
13
+ export namespace HttpQuerifyProgrammer {
14
+ export const write =
15
+ (project: IProject) =>
16
+ (modulo: ts.LeftHandSideExpression) =>
17
+ (type: ts.Type): ts.ArrowFunction => {
18
+ // GET OBJECT TYPE
19
+ const importer: FunctionImporter = new FunctionImporter(modulo.getText());
20
+ const collection: MetadataCollection = new MetadataCollection();
21
+ const result = MetadataFactory.analyze(project.checker)({
22
+ escape: false,
23
+ constant: true,
24
+ absorb: true,
25
+ validate: HttpQueryProgrammer.validate,
26
+ })(collection)(type);
27
+ if (result.success === false)
28
+ throw TransformerError.from(
29
+ `nestia.core.TypedQuery.${importer.method}`,
30
+ )(result.errors);
31
+
32
+ const object: MetadataObject = result.data.objects[0]!;
33
+ return ts.factory.createArrowFunction(
34
+ undefined,
35
+ undefined,
36
+ [IdentifierFactory.parameter("input")],
37
+ undefined,
38
+ undefined,
39
+ ts.factory.createBlock(
40
+ [
41
+ ...importer.declare(modulo),
42
+ StatementFactory.constant(
43
+ "output",
44
+ ts.factory.createNewExpression(
45
+ ts.factory.createIdentifier("URLSearchParams"),
46
+ undefined,
47
+ [],
48
+ ),
49
+ ),
50
+ ...object.properties.map((p) =>
51
+ ts.factory.createExpressionStatement(
52
+ decode(p.key.constants[0]!.values[0].value as string)(p.value),
53
+ ),
54
+ ),
55
+ ts.factory.createReturnStatement(
56
+ ts.factory.createIdentifier("output"),
57
+ ),
58
+ ],
59
+ true,
60
+ ),
61
+ );
62
+ };
63
+
64
+ const decode =
65
+ (key: string) =>
66
+ (value: Metadata): ts.CallExpression =>
67
+ !!value.arrays.length
68
+ ? ts.factory.createCallExpression(
69
+ IdentifierFactory.access(
70
+ IdentifierFactory.access(ts.factory.createIdentifier("input"))(
71
+ key,
72
+ ),
73
+ )("forEach"),
74
+ undefined,
75
+ [
76
+ ts.factory.createArrowFunction(
77
+ undefined,
78
+ undefined,
79
+ [IdentifierFactory.parameter("elem")],
80
+ undefined,
81
+ undefined,
82
+ append(key)(ts.factory.createIdentifier("elem")),
83
+ ),
84
+ ],
85
+ )
86
+ : append(key)(
87
+ IdentifierFactory.access(ts.factory.createIdentifier("input"))(key),
88
+ );
89
+
90
+ const append = (key: string) => (elem: ts.Expression) =>
91
+ ts.factory.createCallExpression(
92
+ IdentifierFactory.access(ts.factory.createIdentifier("output"))("append"),
93
+ undefined,
94
+ [ts.factory.createStringLiteral(key), elem],
95
+ );
96
+ }