\ No newline at end of file
+404: This page could not be found
404
This page could not be found.
\ No newline at end of file
diff --git a/404/index.html b/404/index.html
index 9a99458b4..c0cecb22e 100644
--- a/404/index.html
+++ b/404/index.html
@@ -1 +1 @@
-404: This page could not be found
404
This page could not be found.
\ No newline at end of file
+404: This page could not be found
404
This page could not be found.
\ No newline at end of file
diff --git a/_next/static/6Un9dHYao4GBleXmq30EZ/_buildManifest.js b/_next/static/6Un9dHYao4GBleXmq30EZ/_buildManifest.js
new file mode 100644
index 000000000..9c9ece51b
--- /dev/null
+++ b/_next/static/6Un9dHYao4GBleXmq30EZ/_buildManifest.js
@@ -0,0 +1 @@
+self.__BUILD_MANIFEST=function(s,c,e,d,a,o,t,r,p,i,u){return{__rewrites:{afterFiles:[{has:void 0,source:"/api",destination:"/api/index.html"}],beforeFiles:[],fallback:[]},"/":[s,c,e,a,"static/chunks/934-74a87c2a9b139380.js","static/chunks/pages/index-a2d4a6b1be6d6284.js"],"/_error":["static/chunks/pages/_error-8447282b6bcee29e.js"],"/docs":[s,c,e,d,"static/chunks/pages/docs-cb4494be822d4e27.js"],"/docs/core/TypedBody":[s,c,"static/chunks/pages/docs/core/TypedBody-10830570d579b37e.js"],"/docs/core/TypedException":[s,c,"static/chunks/pages/docs/core/TypedException-d6af77c4068efd01.js"],"/docs/core/TypedFormData":[s,c,"static/chunks/pages/docs/core/TypedFormData-eff6705326cb1195.js"],"/docs/core/TypedHeaders":[s,c,"static/chunks/pages/docs/core/TypedHeaders-31695ffe1f39d986.js"],"/docs/core/TypedParam":[s,c,"static/chunks/pages/docs/core/TypedParam-8efcfa0b864e5220.js"],"/docs/core/TypedQuery":[s,c,"static/chunks/pages/docs/core/TypedQuery-63f669950af8dd48.js"],"/docs/core/TypedRoute":[s,c,"static/chunks/pages/docs/core/TypedRoute-bc1ed907d7e0aae5.js"],"/docs/core/WebSocketRoute":[s,c,"static/chunks/pages/docs/core/WebSocketRoute-a1f39ab44d0abb9d.js"],"/docs/e2e/benchmark":[s,c,"static/chunks/pages/docs/e2e/benchmark-4e1a5354d0f809ce.js"],"/docs/e2e/development":[s,c,"static/chunks/pages/docs/e2e/development-e5075f9a0ec231ce.js"],"/docs/e2e/why":[s,c,"static/chunks/pages/docs/e2e/why-79c3ab0fbefafad8.js"],"/docs/editor":[o,t,r,p,s,c,e,d,i,a,u,"static/chunks/pages/docs/editor-b08a9292bbdfc786.js"],"/docs/migrate":[o,t,r,p,s,c,e,d,i,a,u,"static/chunks/pages/docs/migrate-ec5fd92b2f6d7b45.js"],"/docs/pure":[s,c,"static/chunks/pages/docs/pure-738fa4012e2cc427.js"],"/docs/sdk/e2e":[s,c,"static/chunks/pages/docs/sdk/e2e-383f8411ded36413.js"],"/docs/sdk/sdk":[s,c,"static/chunks/pages/docs/sdk/sdk-a9758559f2f2cf46.js"],"/docs/sdk/simulator":[s,c,"static/chunks/pages/docs/sdk/simulator-f47ea4532add4399.js"],"/docs/sdk/swagger":[o,t,r,p,s,c,i,"static/chunks/pages/docs/sdk/swagger-966628e8e5e7670e.js"],"/docs/setup":[s,c,e,d,"static/chunks/pages/docs/setup-040bb8792eefc92a.js"],"/playground":[s,c,"static/chunks/pages/playground-12e8f8e76ded55a4.js"],sortedPages:["/","/_app","/_error","/docs","/docs/core/TypedBody","/docs/core/TypedException","/docs/core/TypedFormData","/docs/core/TypedHeaders","/docs/core/TypedParam","/docs/core/TypedQuery","/docs/core/TypedRoute","/docs/core/WebSocketRoute","/docs/e2e/benchmark","/docs/e2e/development","/docs/e2e/why","/docs/editor","/docs/migrate","/docs/pure","/docs/sdk/e2e","/docs/sdk/sdk","/docs/sdk/simulator","/docs/sdk/swagger","/docs/setup","/playground"]}}("static/css/445c4173ffc2bf04.css","static/chunks/235-fb39fc8970be3812.js","static/chunks/170-c3ebafadfcde0c90.js","static/chunks/722-c8689fd9b5c63a31.js","static/chunks/696-e2b51f3094fe003d.js","static/chunks/88682331-0a4e775cf8cebf92.js","static/chunks/d89928ee-156d471515b864ec.js","static/chunks/e0385ce4-4854b2e503bbcab1.js","static/chunks/46d2146f-5ad3af8e0f1a9bfb.js","static/chunks/860-e4e3ec67082ca8e8.js","static/chunks/764-2c01679c6460b648.js"),self.__BUILD_MANIFEST_CB&&self.__BUILD_MANIFEST_CB();
\ No newline at end of file
diff --git a/_next/static/NWuZhrNqT7c7YYivOypy3/_ssgManifest.js b/_next/static/6Un9dHYao4GBleXmq30EZ/_ssgManifest.js
similarity index 100%
rename from _next/static/NWuZhrNqT7c7YYivOypy3/_ssgManifest.js
rename to _next/static/6Un9dHYao4GBleXmq30EZ/_ssgManifest.js
diff --git a/_next/static/NWuZhrNqT7c7YYivOypy3/_buildManifest.js b/_next/static/NWuZhrNqT7c7YYivOypy3/_buildManifest.js
deleted file mode 100644
index 40c669754..000000000
--- a/_next/static/NWuZhrNqT7c7YYivOypy3/_buildManifest.js
+++ /dev/null
@@ -1 +0,0 @@
-self.__BUILD_MANIFEST=function(s,c,e,d,a,o,t,r,p,i,u){return{__rewrites:{afterFiles:[{has:void 0,source:"/api",destination:"/api/index.html"}],beforeFiles:[],fallback:[]},"/":[s,c,e,a,"static/chunks/934-74a87c2a9b139380.js","static/chunks/pages/index-bb6938e988a9acbd.js"],"/_error":["static/chunks/pages/_error-8447282b6bcee29e.js"],"/docs":[s,c,e,d,"static/chunks/pages/docs-c2514c0b514b1f9f.js"],"/docs/core/TypedBody":[s,c,"static/chunks/pages/docs/core/TypedBody-58f758be2995d538.js"],"/docs/core/TypedException":[s,c,"static/chunks/pages/docs/core/TypedException-b192f302f71b8a42.js"],"/docs/core/TypedFormData":[s,c,"static/chunks/pages/docs/core/TypedFormData-85736369f0658b2b.js"],"/docs/core/TypedHeaders":[s,c,"static/chunks/pages/docs/core/TypedHeaders-68b31f4f3722e6ba.js"],"/docs/core/TypedParam":[s,c,"static/chunks/pages/docs/core/TypedParam-1c49117f1e20cf2e.js"],"/docs/core/TypedQuery":[s,c,"static/chunks/pages/docs/core/TypedQuery-c31255d149798fc4.js"],"/docs/core/TypedRoute":[s,c,"static/chunks/pages/docs/core/TypedRoute-41870e0d3b02235b.js"],"/docs/core/WebSocketRoute":[s,c,"static/chunks/pages/docs/core/WebSocketRoute-f9c923c8f264268a.js"],"/docs/e2e/benchmark":[s,c,"static/chunks/pages/docs/e2e/benchmark-a8ddc7d345ff60be.js"],"/docs/e2e/development":[s,c,"static/chunks/pages/docs/e2e/development-6d9b36b50a7e091d.js"],"/docs/e2e/why":[s,c,"static/chunks/pages/docs/e2e/why-0c6dca28d2236ecd.js"],"/docs/editor":[o,t,r,p,s,c,e,d,i,a,u,"static/chunks/pages/docs/editor-b265acc1e0217de3.js"],"/docs/migrate":[o,t,r,p,s,c,e,d,i,a,u,"static/chunks/pages/docs/migrate-2b937bf116fa4d1d.js"],"/docs/pure":[s,c,"static/chunks/pages/docs/pure-cadea426e6470a02.js"],"/docs/sdk/e2e":[s,c,"static/chunks/pages/docs/sdk/e2e-ec897af45c529793.js"],"/docs/sdk/sdk":[s,c,"static/chunks/pages/docs/sdk/sdk-32f933097f8de0a6.js"],"/docs/sdk/simulator":[s,c,"static/chunks/pages/docs/sdk/simulator-82fd620023dbdf36.js"],"/docs/sdk/swagger":[o,t,r,p,s,c,i,"static/chunks/pages/docs/sdk/swagger-b4c42403ba1f516a.js"],"/docs/setup":[s,c,e,d,"static/chunks/pages/docs/setup-459c3e607b64752d.js"],"/playground":[s,c,"static/chunks/pages/playground-c45fb837d2895d5d.js"],sortedPages:["/","/_app","/_error","/docs","/docs/core/TypedBody","/docs/core/TypedException","/docs/core/TypedFormData","/docs/core/TypedHeaders","/docs/core/TypedParam","/docs/core/TypedQuery","/docs/core/TypedRoute","/docs/core/WebSocketRoute","/docs/e2e/benchmark","/docs/e2e/development","/docs/e2e/why","/docs/editor","/docs/migrate","/docs/pure","/docs/sdk/e2e","/docs/sdk/sdk","/docs/sdk/simulator","/docs/sdk/swagger","/docs/setup","/playground"]}}("static/css/445c4173ffc2bf04.css","static/chunks/235-fb39fc8970be3812.js","static/chunks/170-c3ebafadfcde0c90.js","static/chunks/722-c8689fd9b5c63a31.js","static/chunks/696-e2b51f3094fe003d.js","static/chunks/88682331-0a4e775cf8cebf92.js","static/chunks/d89928ee-156d471515b864ec.js","static/chunks/e0385ce4-4854b2e503bbcab1.js","static/chunks/46d2146f-5ad3af8e0f1a9bfb.js","static/chunks/860-e4e3ec67082ca8e8.js","static/chunks/764-2c01679c6460b648.js"),self.__BUILD_MANIFEST_CB&&self.__BUILD_MANIFEST_CB();
\ No newline at end of file
diff --git a/_next/static/chunks/nextra-data-en-US.json b/_next/static/chunks/nextra-data-en-US.json
index 941f4b6e5..159b23130 100644
--- a/_next/static/chunks/nextra-data-en-US.json
+++ b/_next/static/chunks/nextra-data-en-US.json
@@ -1 +1 @@
-{"/docs/core/TypedException":{"title":"Typedexception","data":{"outline#Outline":"export function TypedException(\n status: number | \"2XX\" | \"3XX\" | \"4XX\" | \"5XX\",\n description?: string,\n): MethodDecorator;\nexport function TypedException(props: {\n status: : number | \"2XX\" | \"3XX\" | \"4XX\" | \"5XX\";\n description?: string;\n example?: T;\n examples?: Record;\n}): MethodDecorator;\nException decorator of HTTP responses.TypedException is a decorator function describing HTTP exception and its type which could be occured in a controller method. For reference, this decorator function does not affect to the method's behavior, but affects to the swagger documents generation, or SDK functions when propagation mode being used.","how-to-use#How to use":"import { Controller } from \"@nestjs/common\";\nimport typia, { TypeGuardError } from \"typia\";\nimport {\n TypedBody,\n TypedException,\n TypedParam,\n TypedRoute,\n} from \"@nestia/core\";\nimport { IBbsArticle } from \"@api/lib/structures/IBbsArticle\";\nimport { IInternalServerError } from \"@api/lib/structures/IInternalServerError\";\nimport { INotFound } from \"@api/lib/structures/INotFound\";\nimport { IUnprocessibleEntity } from \"@api/lib/structures/IUnprocessibleEntity\";\n@Controller(\"exception\")\nexport class ExceptionController {\n @TypedRoute.Post(\":section/typed\")\n @TypedException({\n status: 400, \n description: \"invalid request\",\n example: {\n name: \"BadRequestException\",\n method: \"TypedBody\",\n path: \"$input.title\",\n expected: \"string\",\n value: 123,\n message: \"invalid type\",\n },\n })\n @TypedException(404, \"unable to find the matched section\")\n @TypedException(428)\n @TypedException(\"5XX\", \"internal server error\")\n public async typed(\n @TypedParam(\"section\") section: string,\n @TypedBody() input: IBbsArticle.IStore,\n ): Promise {\n section;\n input;\n return typia.random();\n }\n}\nJust call TypedException() function with target type and status code.If you want to add description or example value, you also can add it as a property.For reference, swagger allows to special pattern like 2XX, 3XX, 4XX, 5XX for status code.","swagger-example#Swagger Example":"Here is an example of swagger documents utilizing the @TypedException() decorator.\n{\n \"openapi\": \"3.1.0\",\n \"servers\": [\n {\n \"url\": \"https://github.com/samchon/nestia\",\n \"description\": \"insert your server url\"\n }\n ],\n \"info\": {\n \"version\": \"3.11.1\",\n \"title\": \"@samchon/nestia-test\",\n \"description\": \"Test program of Nestia\",\n \"license\": {\n \"name\": \"MIT\"\n }\n },\n \"paths\": {\n \"/exception/{section}/typed\": {\n \"post\": {\n \"tags\": [],\n \"parameters\": [\n {\n \"name\": \"section\",\n \"in\": \"path\",\n \"schema\": {\n \"type\": \"string\"\n },\n \"required\": true\n }\n ],\n \"requestBody\": {\n \"content\": {\n \"application/json\": {\n \"schema\": {\n \"$ref\": \"#/components/schemas/IBbsArticle.IStore\"\n }\n }\n },\n \"required\": true\n },\n \"responses\": {\n \"201\": {\n \"content\": {\n \"application/json\": {\n \"schema\": {\n \"$ref\": \"#/components/schemas/IBbsArticle\"\n }\n }\n }\n },\n \"400\": {\n \"description\": \"invalid request\",\n \"content\": {\n \"application/json\": {\n \"schema\": {\n \"$ref\": \"#/components/schemas/TypeGuardErrorany\"\n },\n \"example\": {\n \"name\": \"BadRequestException\",\n \"method\": \"TypedBody\",\n \"path\": \"$input.title\",\n \"expected\": \"string\",\n \"value\": 123,\n \"message\": \"invalid type\"\n }\n }\n }\n },\n \"404\": {\n \"description\": \"unable to find the matched section\",\n \"content\": {\n \"application/json\": {\n \"schema\": {\n \"$ref\": \"#/components/schemas/INotFound\"\n }\n }\n }\n },\n \"428\": {\n \"content\": {\n \"application/json\": {\n \"schema\": {\n \"$ref\": \"#/components/schemas/IUnprocessibleEntity\"\n }\n }\n }\n },\n \"5XX\": {\n \"description\": \"internal server error\",\n \"content\": {\n \"application/json\": {\n \"schema\": {\n \"$ref\": \"#/components/schemas/IInternalServerError\"\n }\n }\n }\n }\n }\n }\n }\n },\n \"components\": {\n \"schemas\": {\n \"IBbsArticle\": {\n \"type\": \"object\",\n \"properties\": {\n \"id\": {\n \"type\": \"string\",\n \"format\": \"uuid\"\n },\n \"created_at\": {\n \"type\": \"string\",\n \"format\": \"date-time\"\n },\n \"title\": {\n \"type\": \"string\",\n \"minLength\": 3,\n \"maxLength\": 50\n },\n \"body\": {\n \"type\": \"string\"\n },\n \"files\": {\n \"type\": \"array\",\n \"items\": {\n \"$ref\": \"#/components/schemas/IAttachmentFile\"\n }\n }\n },\n \"required\": [\n \"id\",\n \"created_at\",\n \"title\",\n \"body\",\n \"files\"\n ]\n },\n \"IAttachmentFile\": {\n \"type\": \"object\",\n \"properties\": {\n \"name\": {\n \"oneOf\": [\n {\n \"type\": \"null\"\n },\n {\n \"type\": \"string\",\n \"maxLength\": 255\n }\n ]\n },\n \"extension\": {\n \"oneOf\": [\n {\n \"type\": \"null\"\n },\n {\n \"type\": \"string\",\n \"minLength\": 1,\n \"maxLength\": 8\n }\n ]\n },\n \"url\": {\n \"type\": \"string\",\n \"format\": \"uri\"\n }\n },\n \"required\": [\n \"name\",\n \"extension\",\n \"url\"\n ]\n },\n \"IBbsArticle.IStore\": {\n \"type\": \"object\",\n \"properties\": {\n \"title\": {\n \"type\": \"string\",\n \"minLength\": 3,\n \"maxLength\": 50\n },\n \"body\": {\n \"type\": \"string\"\n },\n \"files\": {\n \"type\": \"array\",\n \"items\": {\n \"$ref\": \"#/components/schemas/IAttachmentFile\"\n }\n }\n },\n \"required\": [\n \"title\",\n \"body\",\n \"files\"\n ]\n },\n \"TypeGuardErrorany\": {\n \"type\": \"object\",\n \"properties\": {\n \"method\": {\n \"type\": \"string\"\n },\n \"path\": {\n \"type\": \"string\"\n },\n \"expected\": {\n \"type\": \"string\"\n },\n \"value\": {},\n \"fake_expected_typed_value_\": {},\n \"name\": {\n \"type\": \"string\"\n },\n \"message\": {\n \"type\": \"string\"\n },\n \"stack\": {\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"method\",\n \"expected\",\n \"value\",\n \"name\",\n \"message\"\n ]\n },\n \"INotFound\": {\n \"type\": \"object\",\n \"properties\": {\n \"schema\": {\n \"type\": \"string\"\n },\n \"table\": {\n \"type\": \"string\"\n },\n \"id\": {\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"schema\",\n \"table\",\n \"id\"\n ]\n },\n \"IUnprocessibleEntity\": {\n \"type\": \"object\",\n \"properties\": {\n \"reason\": {\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"reason\"\n ]\n },\n \"IInternalServerError\": {\n \"type\": \"object\",\n \"properties\": {\n \"name\": {\n \"type\": \"string\"\n },\n \"message\": {\n \"type\": \"string\"\n },\n \"stack\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n }\n }\n },\n \"required\": [\n \"name\",\n \"message\",\n \"stack\"\n ]\n }\n },\n \"securitySchemes\": {\n \"bearer\": {\n \"type\": \"apiKey\"\n }\n }\n },\n \"tags\": [],\n \"x-samchon-emended\": true\n}"}},"/docs/core/TypedParam":{"title":"Typedparam","data":{"outline#Outline":"export function TypedParam(name: string): ParameterDecorator;\nType safe path parameter decorator.@TypedParam() is a decorator parsing path parameter.It's almost same with original @Param() function of NestJS, however, @TypedParam() is more type safe.As @TypedParam() can anlayze source code in the compilation level, it can specify parameter type by itself. Also, while NestJS cannot distinguish nullable type and consider every parameter value as a string type, @TypedParam() can do it. Furthermore, @TypedParam() can validate special types like \"uuid\" or \"date\".Let's read below example code, and see how @TypedParam() works.\n@TypedParam() is not essential for Swagger Documents or SDK Library building.Therefore, it is not a matter to use @TypedParam() or @Param() of the original NestJS.","how-to-use#How to use":"import { TypedParam } from \"@nestia/core\";\nimport { Controller, Get } from \"@nestjs/common\";\nimport { tags } from \"typia\";\n@Controller(\"parameters\")\nexport class ParametersController {\n @Get(\"uint32/:value\")\n public async uint32(\n @TypedParam(\"value\") value: (number & tags.Type<\"uint32\">) | null,\n ): Promise<(number & tags.Type<\"uint32\">) | null> {\n return value;\n }\n @Get(\"string/:value\")\n public async string(\n @TypedParam(\"value\") value: string\n ): Promise {\n return value;\n }\n @Get(\"uuid/:value\")\n public async uuid(\n @TypedParam(\"value\") value: string & tags.Format<\"uuid\">,\n ): Promise {\n return value;\n }\n}\n\"use strict\";\nvar __decorate =\n (this && this.__decorate) ||\n function (decorators, target, key, desc) {\n var c = arguments.length,\n r =\n c < 3\n ? target\n : desc === null\n ? (desc = Object.getOwnPropertyDescriptor(target, key))\n : desc,\n d;\n if (typeof Reflect === \"object\" && typeof Reflect.decorate === \"function\")\n r = Reflect.decorate(decorators, target, key, desc);\n else\n for (var i = decorators.length - 1; i >= 0; i--)\n if ((d = decorators[i]))\n r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;\n return c > 3 && r && Object.defineProperty(target, key, r), r;\n };\nvar __metadata =\n (this && this.__metadata) ||\n function (k, v) {\n if (typeof Reflect === \"object\" && typeof Reflect.metadata === \"function\")\n return Reflect.metadata(k, v);\n };\nvar __param =\n (this && this.__param) ||\n function (paramIndex, decorator) {\n return function (target, key) {\n decorator(target, key, paramIndex);\n };\n };\nvar __awaiter =\n (this && this.__awaiter) ||\n function (thisArg, _arguments, P, generator) {\n function adopt(value) {\n return value instanceof P\n ? value\n : new P(function (resolve) {\n resolve(value);\n });\n }\n return new (P || (P = Promise))(function (resolve, reject) {\n function fulfilled(value) {\n try {\n step(generator.next(value));\n } catch (e) {\n reject(e);\n }\n }\n function rejected(value) {\n try {\n step(generator[\"throw\"](value));\n } catch (e) {\n reject(e);\n }\n }\n function step(result) {\n result.done\n ? resolve(result.value)\n : adopt(result.value).then(fulfilled, rejected);\n }\n step((generator = generator.apply(thisArg, _arguments || [])).next());\n });\n };\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.ParametersController = void 0;\nconst core_1 = require(\"@nestia/core\");\nconst common_1 = require(\"@nestjs/common\");\nlet ParametersController = class ParametersController {\n uint32(value) {\n return __awaiter(this, void 0, void 0, function* () {\n return value;\n });\n }\n string(value) {\n return __awaiter(this, void 0, void 0, function* () {\n return value;\n });\n }\n uuid(value) {\n return __awaiter(this, void 0, void 0, function* () {\n return value;\n });\n }\n};\nexports.ParametersController = ParametersController;\n__decorate(\n [\n (0, common_1.Get)(\"uint32/:value\"),\n __param(\n 0,\n (0, core_1.TypedParam)(\"value\", {\n name: '((number & Type<\"uint32\">) | null)',\n is: (input) => {\n return (\n null === input ||\n (\"number\" === typeof input &&\n Math.floor(input) === input &&\n 0 <= input &&\n input <= 4294967295)\n );\n },\n cast: (str) => (\"null\" === str ? null : Number(str)),\n }),\n ),\n __metadata(\"design:type\", Function),\n __metadata(\"design:paramtypes\", [Object]),\n __metadata(\"design:returntype\", Promise),\n ],\n ParametersController.prototype,\n \"uint32\",\n null,\n);\n__decorate(\n [\n (0, common_1.Get)(\"string/:value\"),\n __param(\n 0,\n (0, core_1.TypedParam)(\"value\", {\n name: \"string\",\n is: (input) => {\n return \"string\" === typeof input;\n },\n cast: (str) => str,\n }),\n ),\n __metadata(\"design:type\", Function),\n __metadata(\"design:paramtypes\", [String]),\n __metadata(\"design:returntype\", Promise),\n ],\n ParametersController.prototype,\n \"string\",\n null,\n);\n__decorate(\n [\n (0, common_1.Get)(\"uuid/:value\"),\n __param(\n 0,\n (0, core_1.TypedParam)(\"value\", {\n name: '(string & Format<\"uuid\">)',\n is: (input) => {\n return (\n \"string\" === typeof input &&\n /^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000)$/i.test(\n input,\n )\n );\n },\n cast: (str) => str,\n }),\n ),\n __metadata(\"design:type\", Function),\n __metadata(\"design:paramtypes\", [Object]),\n __metadata(\"design:returntype\", Promise),\n ],\n ParametersController.prototype,\n \"uuid\",\n null,\n);\nexports.ParametersController = ParametersController = __decorate(\n [(0, common_1.Controller)(\"parameters\")],\n ParametersController,\n);\nJust call @TypedParam() function on the path paremeter, that's all.If you want to special parameter type like \"uint32\" or \"uuid\", utilize type tags of typia.When wrong typed value comes, 400 bad request error would be thrown.","restriction#Restriction":"@TypedParam() allows only atomic type.\nboolean\nnumber\nstring\nbigint\nAlso, @TypedParam() allows nullable like number | null, but undefindable type is not.\nnumber | null is allowed\nstring | undefined is prohibited\nIf you violate above condition, and try to declare object or union type, compilation error would be occured:\nError on nestia.core.TypedParam(): only atomic type is allowed"}},"/docs/e2e/benchmark":{"title":"Benchmark","data":{"outline#Outline":"Benchmark your backend server with e2e test functions.If you've developed e2e test functions utilizing SDK library of @nestia/sdk, you can re-use those e2e test functions in the benchmark program supported by @nestia/benchmark. The benchmark program will run these e2e test functions in parellel and randomly to measure the performance of your backend server.If you want to pre-exprience the benchmark program utliizng the e2e test functions of @nestia/sdk, visit below playground website. Also, here is the benchmark report example generated by the benchmark program of @nestia/benchmark executed in the below playgroud link.\nπ» https://stackblitz.com/~/github.com/samchon/nestia-start\n? Number of requests to make 1024\n? Number of threads to use 4\n? Number of simultaneous requests to make 32\nββββββββββββββββββββββββββββββββββββββββ 100% | ETA: 0s | 3654/1024","main-program#Main Program":"import { DynamicBenchmarker } from \"@nestia/benchmark\";\nimport cliProgress from \"cli-progress\";\nimport fs from \"fs\";\nimport os from \"os\";\nimport { IPointer } from \"tstl\";\nimport { MyBackend } from \"../../src/MyBackend\";\nimport { MyConfiguration } from \"../../src/MyConfiguration\";\nimport { MyGlobal } from \"../../src/MyGlobal\";\nimport { ArgumentParser } from \"../helpers/ArgumentParser\";\ninterface IOptions {\n include?: string[];\n exclude?: string[];\n count: number;\n threads: number;\n simultaneous: number;\n}\nconst getOptions = () =>\n ArgumentParser.parse(async (command, prompt, action) => {\n // command.option(\"--mode \", \"target mode\");\n // command.option(\"--reset \", \"reset local DB or not\");\n command.option(\"--include \", \"include feature files\");\n command.option(\"--exclude \", \"exclude feature files\");\n command.option(\"--count \", \"number of requests to make\");\n command.option(\"--threads \", \"number of threads to use\");\n command.option(\n \"--simultaneous \",\n \"number of simultaneous requests to make\",\n );\n return action(async (options) => {\n // if (typeof options.reset === \"string\")\n // options.reset = options.reset === \"true\";\n // options.mode ??= await prompt.select(\"mode\")(\"Select mode\")([\n // \"LOCAL\",\n // \"DEV\",\n // \"REAL\",\n // ]);\n // options.reset ??= await prompt.boolean(\"reset\")(\"Reset local DB\");\n options.count = Number(\n options.count ??\n (await prompt.number(\"count\")(\"Number of requests to make\")),\n );\n options.threads = Number(\n options.threads ??\n (await prompt.number(\"threads\")(\"Number of threads to use\")),\n );\n options.simultaneous = Number(\n options.simultaneous ??\n (await prompt.number(\"simultaneous\")(\n \"Number of simultaneous requests to make\",\n )),\n );\n return options as IOptions;\n });\n });\nconst main = async (): Promise => {\n // CONFIGURATIONS\n const options: IOptions = await getOptions();\n MyGlobal.testing = true;\n // BACKEND SERVER\n const backend: MyBackend = new MyBackend();\n await backend.open();\n // DO BENCHMARK\n const prev: IPointer = { value: 0 };\n const bar: cliProgress.SingleBar = new cliProgress.SingleBar(\n {},\n cliProgress.Presets.shades_classic,\n );\n bar.start(options.count, 0);\n const report: DynamicBenchmarker.IReport = await DynamicBenchmarker.master({\n servant: `${__dirname}/servant.js`,\n count: options.count,\n threads: options.threads,\n simultaneous: options.simultaneous,\n filter: (func) =>\n (!options.include?.length ||\n (options.include ?? []).some((str) => func.includes(str))) &&\n (!options.exclude?.length ||\n (options.exclude ?? []).every((str) => !func.includes(str))),\n progress: (value: number) => {\n if (value >= 100 + prev.value) {\n bar.update(value);\n prev.value = value;\n }\n },\n stdio: \"ignore\",\n });\n bar.stop();\n // DOCUMENTATION\n try {\n await fs.promises.mkdir(`${MyConfiguration.ROOT}/docs/benchmarks`, {\n recursive: true,\n });\n } catch {}\n await fs.promises.writeFile(\n `${MyConfiguration.ROOT}/docs/benchmarks/${os\n .cpus()[0]\n .model.trim()\n .split(\"\\\\\")\n .join(\"\")\n .split(\"/\")\n .join(\"\")}.md`,\n DynamicBenchmarker.markdown(report),\n \"utf8\",\n );\n // CLOSE\n await backend.close();\n};\nmain().catch((exp) => {\n console.error(exp);\n process.exit(-1);\n});\nimport { DynamicBenchmarker } from \"@nestia/benchmark\";\nimport { MyConfiguration } from \"../../src/MyConfiguration\";\nDynamicBenchmarker.servant({\n connection: {\n host: `http://127.0.0.1:${MyConfiguration.API_PORT()}`,\n },\n location: `${__dirname}/../features`,\n parameters: (connection) => [connection],\n prefix: \"test_api_\",\n}).catch((exp) => {\n console.error(exp);\n process.exit(-1);\n});\nTo compose the benchmark program of @nestia/benchmark on your backend application, you have to create two executable TypeScript programs; the main program and the servant program.The main program is executed by user (npm run benchmark command in the playground project), and centralizes the benchmark progress. It creates multiple servant programs parallel, and aggregate the benchmark results from them. After the aggregation, it publishes the benchmark report with markdown format.The servant program is executed by the main program multiply in parallel, and actually runs the e2e test functions for benchmarking. Composing the servant program, you have to specify the directory where the e2e test functions are located. Also, composing the main program of benchmark, you also have to specify the file location of the servant program.If you want to see more benchmark program cases, visit below links:\nProject\tMain\tServant\tReport\tsamchon/nestia-start\tindex.ts\tservant.ts\tREPORT.md\tsamchon/backend\tindex.ts\tservant.ts\tREPORT.md","test-functions#Test Functions":"import { RandomGenerator, TestValidator } from \"@nestia/e2e\";\nimport { v4 } from \"uuid\";\nimport api from \"@ORGANIZATION/PROJECT-api/lib/index\";\nimport { IBbsArticle } from \"@ORGANIZATION/PROJECT-api/lib/structures/bbs/IBbsArticle\";\nexport async function test_api_bbs_article_create(\n connection: api.IConnection,\n): Promise {\n // STORE A NEW ARTICLE\n const stored: IBbsArticle = await api.functional.bbs.articles.create(\n connection,\n \"general\",\n {\n writer: RandomGenerator.name(),\n title: RandomGenerator.paragraph(3)(),\n body: RandomGenerator.content(8)()(),\n format: \"txt\",\n files: [\n {\n name: \"logo\",\n extension: \"png\",\n url: \"https://somewhere.com/logo.png\",\n },\n ],\n password: v4(),\n },\n );\n // READ THE DATA AGAIN\n const read: IBbsArticle = await api.functional.bbs.articles.at(\n connection,\n stored.section,\n stored.id,\n );\n TestValidator.equals(\"created\")(stored)(read);\n}\nimport { RandomGenerator, TestValidator } from \"@nestia/e2e\";\nimport { v4 } from \"uuid\";\nimport api from \"@ORGANIZATION/PROJECT-api/lib/index\";\nimport { IBbsArticle } from \"@ORGANIZATION/PROJECT-api/lib/structures/bbs/IBbsArticle\";\nexport async function test_api_bbs_article_update(\n connection: api.IConnection,\n): Promise {\n // STORE A NEW ARTICLE\n const password: string = v4();\n const article: IBbsArticle = await api.functional.bbs.articles.create(\n connection,\n \"general\",\n {\n writer: RandomGenerator.name(),\n title: RandomGenerator.paragraph(3)(),\n body: RandomGenerator.content(8)()(),\n format: \"txt\",\n files: [\n {\n name: \"logo\",\n extension: \"png\",\n url: \"https://somewhere.com/logo.png\",\n },\n ],\n password,\n },\n );\n // UPDATE WITH EXACT PASSWORD\n const content: IBbsArticle.ISnapshot =\n await api.functional.bbs.articles.update(\n connection,\n article.section,\n article.id,\n {\n title: RandomGenerator.paragraph(3)(),\n body: RandomGenerator.content(8)()(),\n format: \"txt\",\n files: [],\n password,\n },\n );\n article.snapshots.push(content);\n // TRY UPDATE WITH WRONG PASSWORD\n await TestValidator.error(\"update with wrong password\")(() =>\n api.functional.bbs.articles.update(\n connection,\n article.section,\n article.id,\n {\n title: RandomGenerator.paragraph(5)(),\n body: RandomGenerator.content(8)()(),\n format: \"txt\",\n files: [],\n password: v4(),\n },\n ),\n );\n}\nimport { ArrayUtil, RandomGenerator, TestValidator } from \"@nestia/e2e\";\nimport api from \"@ORGANIZATION/PROJECT-api/lib/index\";\nimport { IBbsArticle } from \"@ORGANIZATION/PROJECT-api/lib/structures/bbs/IBbsArticle\";\nimport { IPage } from \"@ORGANIZATION/PROJECT-api/lib/structures/common/IPage\";\nexport async function test_api_bbs_article_index_search(\n connection: api.IConnection,\n): Promise {\n // GENERATE 100 ARTICLES\n const section: string = \"general\";\n const articles: IBbsArticle[] = await ArrayUtil.asyncRepeat(100)(() =>\n api.functional.bbs.articles.create(connection, section, {\n writer: RandomGenerator.name(),\n title: RandomGenerator.paragraph(4)(),\n body: RandomGenerator.content(3)()(),\n format: \"txt\",\n files: [],\n password: RandomGenerator.alphabets(8),\n }),\n );\n // GET ENTIRE DATA\n const total: IPage =\n await api.functional.bbs.articles.index(connection, section, {\n limit: articles.length,\n sort: [\"-created_at\"],\n });\n // PREPARE SEARCH FUNCTION\n const search = TestValidator.search(\"BbsArticleProvider.index()\")(\n async (input: IBbsArticle.IRequest.ISearch) => {\n const page: IPage =\n await api.functional.bbs.articles.index(connection, section, {\n limit: articles.length,\n search: input,\n sort: [\"-created_at\"],\n });\n return page.data;\n },\n )(total.data, 10);\n // SEARCH TITLE\n await search({\n fields: [\"title\"],\n values: (article) => [article.title],\n request: ([title]) => ({ title }),\n filter: (article, [title]) => article.title.includes(title),\n });\n // SEARCH WRITER\n await search({\n fields: [\"writer\"],\n values: (article) => [article.writer],\n request: ([writer]) => ({ writer }),\n filter: (article, [writer]) => article.writer.includes(writer),\n });\n // SEARCH BOTH OF THEM\n await search({\n fields: [\"title\", \"writer\"],\n values: (article) => [article.title, article.writer],\n request: ([title, writer]) => ({ title, writer }),\n filter: (article, [title, writer]) =>\n article.title.includes(title) && article.writer.includes(writer),\n });\n}\nDeveloping e2e test functions are very easy. Just make e2e based test function utilizing @nestia/sdk generated SDK library, and exports the function with test_ prefixed name (If you've configured another prefix property in the benchmark main program, just follow the configuration).Also, make the function to have parameter(s) configured in the servant program of the benchmark. As above test functions are examples of playground project that has configured to have only one connection parameter, All of them have the only one parameter connection.After composing these e2e test functions, just execute the benchmark main program. In the playground project, it can be exeucted by npm run benchmark command. The benchmark program will run these e2e test functions in parallel and randomly, and measure the performance of your backend server.\ngit clone https://github.com/samchon/nestia-start\ncd nestia-start\nnpm install\nnpm run build:test\nnpm run benchmark"}},"/docs/e2e/development":{"title":"Development","data":{"outline#Outline":"Test your backend server with e2e test functions.If you've succeded to generate SDK library by @nestia/sdk, you can utilize the SDK library to implement e2e test functions. As the SDK library ensures types safety for remote API calls, you can develop much more efficient and safer test program than unit testing case.If you want to pre-exprience the test program utliizng the e2e test functions of @nestia/sdk, visit below playground website.π» https://stackblitz.com/~/github.com/samchon/nestia-start\n- test_api_bbs_article_at: 149 ms\n- test_api_bbs_article_create: 30 ms\n- test_api_bbs_article_index_search: 1,312 ms\n- test_api_bbs_article_index_sort: 1,110 ms\n- test_api_bbs_article_update: 28 m","main-program#Main Program":"import { DynamicExecutor } from \"@nestia/e2e\";\nimport api from \"@ORGANIZATION/PROJECT-api\";\nimport { MyBackend } from \"../src/MyBackend\";\nimport { MyConfiguration } from \"../src/MyConfiguration\";\nimport { MyGlobal } from \"../src/MyGlobal\";\nimport { ArgumentParser } from \"./helpers/ArgumentParser\";\ninterface IOptions {\n include?: string[];\n exclude?: string[];\n}\nconst getOptions = () =>\n ArgumentParser.parse(async (command, _prompt, action) => {\n // command.option(\"--mode \", \"target mode\");\n // command.option(\"--reset \", \"reset local DB or not\");\n command.option(\"--include \", \"include feature files\");\n command.option(\"--exclude \", \"exclude feature files\");\n return action(async (options) => {\n // if (typeof options.reset === \"string\")\n // options.reset = options.reset === \"true\";\n // options.mode ??= await prompt.select(\"mode\")(\"Select mode\")([\n // \"LOCAL\",\n // \"DEV\",\n // \"REAL\",\n // ]);\n // options.reset ??= await prompt.boolean(\"reset\")(\"Reset local DB\");\n return options as IOptions;\n });\n });\nasync function main(): Promise {\n // CONFIGURATIONS\n const options: IOptions = await getOptions();\n MyGlobal.testing = true;\n // BACKEND SERVER\n const backend: MyBackend = new MyBackend();\n await backend.open();\n //----\n // CLINET CONNECTOR\n //----\n // DO TEST\n const connection: api.IConnection = {\n host: `http://127.0.0.1:${MyConfiguration.API_PORT()}`,\n };\n const report: DynamicExecutor.IReport = await DynamicExecutor.validate({\n prefix: \"test\",\n parameters: () => [{ ...connection }],\n filter: (func) =>\n (!options.include?.length ||\n (options.include ?? []).some((str) => func.includes(str))) &&\n (!options.exclude?.length ||\n (options.exclude ?? []).every((str) => !func.includes(str))),\n })(__dirname + \"/features\");\n await backend.close();\n const failures: DynamicExecutor.IReport.IExecution[] =\n report.executions.filter((exec) => exec.error !== null);\n if (failures.length === 0) {\n console.log(\"Success\");\n console.log(\"Elapsed time\", report.time.toLocaleString(), `ms`);\n } else {\n for (const f of failures) console.log(f.error);\n process.exit(-1);\n }\n console.log(\n [\n `All: #${report.executions.length}`,\n `Success: #${report.executions.length - failures.length}`,\n `Failed: #${failures.length}`,\n ].join(\"\\n\"),\n );\n}\nmain().catch((exp) => {\n console.log(exp);\n process.exit(-1);\n});\nTo compose the test program of @nestia/e2e on your backend application, you have to create one executable TypeScript program.The main program is executed by user (npm run benchmark command in the playground project), and it runs every (or some filtered) e2e test functions located in the target directory. In above case, test/features is the directory collecting e2e test functions.If you want to see more test program cases, visit below links:\nsamchon/nestia-start\nsamchon/backend","test-functions#Test Functions":"import { RandomGenerator, TestValidator } from \"@nestia/e2e\";\nimport { v4 } from \"uuid\";\nimport api from \"@ORGANIZATION/PROJECT-api/lib/index\";\nimport { IBbsArticle } from \"@ORGANIZATION/PROJECT-api/lib/structures/bbs/IBbsArticle\";\nexport async function test_api_bbs_article_create(\n connection: api.IConnection,\n): Promise {\n // STORE A NEW ARTICLE\n const stored: IBbsArticle = await api.functional.bbs.articles.create(\n connection,\n \"general\",\n {\n writer: RandomGenerator.name(),\n title: RandomGenerator.paragraph(3)(),\n body: RandomGenerator.content(8)()(),\n format: \"txt\",\n files: [\n {\n name: \"logo\",\n extension: \"png\",\n url: \"https://somewhere.com/logo.png\",\n },\n ],\n password: v4(),\n },\n );\n // READ THE DATA AGAIN\n const read: IBbsArticle = await api.functional.bbs.articles.at(\n connection,\n stored.section,\n stored.id,\n );\n TestValidator.equals(\"created\")(stored)(read);\n}\nimport { RandomGenerator, TestValidator } from \"@nestia/e2e\";\nimport { v4 } from \"uuid\";\nimport api from \"@ORGANIZATION/PROJECT-api/lib/index\";\nimport { IBbsArticle } from \"@ORGANIZATION/PROJECT-api/lib/structures/bbs/IBbsArticle\";\nexport async function test_api_bbs_article_update(\n connection: api.IConnection,\n): Promise {\n // STORE A NEW ARTICLE\n const password: string = v4();\n const article: IBbsArticle = await api.functional.bbs.articles.create(\n connection,\n \"general\",\n {\n writer: RandomGenerator.name(),\n title: RandomGenerator.paragraph(3)(),\n body: RandomGenerator.content(8)()(),\n format: \"txt\",\n files: [\n {\n name: \"logo\",\n extension: \"png\",\n url: \"https://somewhere.com/logo.png\",\n },\n ],\n password,\n },\n );\n // UPDATE WITH EXACT PASSWORD\n const content: IBbsArticle.ISnapshot =\n await api.functional.bbs.articles.update(\n connection,\n article.section,\n article.id,\n {\n title: RandomGenerator.paragraph(3)(),\n body: RandomGenerator.content(8)()(),\n format: \"txt\",\n files: [],\n password,\n },\n );\n article.snapshots.push(content);\n // TRY UPDATE WITH WRONG PASSWORD\n await TestValidator.error(\"update with wrong password\")(() =>\n api.functional.bbs.articles.update(\n connection,\n article.section,\n article.id,\n {\n title: RandomGenerator.paragraph(5)(),\n body: RandomGenerator.content(8)()(),\n format: \"txt\",\n files: [],\n password: v4(),\n },\n ),\n );\n}\nimport { ArrayUtil, RandomGenerator, TestValidator } from \"@nestia/e2e\";\nimport api from \"@ORGANIZATION/PROJECT-api/lib/index\";\nimport { IBbsArticle } from \"@ORGANIZATION/PROJECT-api/lib/structures/bbs/IBbsArticle\";\nimport { IPage } from \"@ORGANIZATION/PROJECT-api/lib/structures/common/IPage\";\nexport async function test_api_bbs_article_index_search(\n connection: api.IConnection,\n): Promise {\n // GENERATE 100 ARTICLES\n const section: string = \"general\";\n const articles: IBbsArticle[] = await ArrayUtil.asyncRepeat(100)(() =>\n api.functional.bbs.articles.create(connection, section, {\n writer: RandomGenerator.name(),\n title: RandomGenerator.paragraph(4)(),\n body: RandomGenerator.content(3)()(),\n format: \"txt\",\n files: [],\n password: RandomGenerator.alphabets(8),\n }),\n );\n // GET ENTIRE DATA\n const total: IPage =\n await api.functional.bbs.articles.index(connection, section, {\n limit: articles.length,\n sort: [\"-created_at\"],\n });\n // PREPARE SEARCH FUNCTION\n const search = TestValidator.search(\"BbsArticleProvider.index()\")(\n async (input: IBbsArticle.IRequest.ISearch) => {\n const page: IPage =\n await api.functional.bbs.articles.index(connection, section, {\n limit: articles.length,\n search: input,\n sort: [\"-created_at\"],\n });\n return page.data;\n },\n )(total.data, 10);\n // SEARCH TITLE\n await search({\n fields: [\"title\"],\n values: (article) => [article.title],\n request: ([title]) => ({ title }),\n filter: (article, [title]) => article.title.includes(title),\n });\n // SEARCH WRITER\n await search({\n fields: [\"writer\"],\n values: (article) => [article.writer],\n request: ([writer]) => ({ writer }),\n filter: (article, [writer]) => article.writer.includes(writer),\n });\n // SEARCH BOTH OF THEM\n await search({\n fields: [\"title\", \"writer\"],\n values: (article) => [article.title, article.writer],\n request: ([title, writer]) => ({ title, writer }),\n filter: (article, [title, writer]) =>\n article.title.includes(title) && article.writer.includes(writer),\n });\n}\nDeveloping e2e test functions are very easy. Just make e2e based test function utilizing @nestia/sdk generated SDK library, and exports the function with test_ prefixed name (If you've configured another prefix property in the test main program, just follow the configuration).Also, make the function to have parameter(s) configured in the servant program of the benchmark. As above test functions are examples of playground project that has configured to have only one connection parameter, All of them have the only one parameter connection.After composing these e2e test functions, just execute the test main program. In the playground project, it can be exeucted by npm run test command. The test program will run these e2e test functions, and report if some bugs be occured.\ngit clone https://github.com/samchon/nestia-start\ncd nestia-start\nnpm install\nnpm run build:test\nnpm run test"}},"/docs/editor":{"title":"Editor","data":{"typescript-swagger-editor#TypeScript Swagger Editor":"Put your swagger.json file, then \"TypeScript Swagger Editor\" be opened.\n\"TypeScript Swagger Editor\" is a web-based TypeScript editor (of StackBlitz) for Swagger API specifications, with SDK (Software Development Kit) library generated by @nestia/migrate. With the cloud \"TypeScript Swagger Editor\", you can easily test the backend API with TypeScript code, and it is much convenient than the traditional way of using Swagger UI, due to type checking and auto-completion.Also, \"TypeScript Swagger Editor\" provides Mockup Simulator. With the simulator, you can start the frontend (or client) development even when the backend API is not ready yet. Furthermore, \"TypeScript Swagger Editor\" supports automatic e2e (end-to-end) test functions' generation, so that you can easily validate the API with the generated test code.Here are the some example projects generated by \"TypeScript Swagger Editor\". Traveling those example projects, you may understand how to utilize the \"TypeScript Swagger Editor\". Let's start the type safe API interaction development with \"TypeScript Swagger Editor\"!","how-to-use-in-local#How to use in local":"npx @nestia/migrate\n? Migration mode (Use arrow keys):\n NestJS\n > SDK\n? Swagger file location: assets/input/clickhouse.json\n? Output directory path: assets/output/clickhouse-sdk-manual\n? Mokup Simulator: true\n? E2E Test Functions: true\nRelated document: Nestia > Guide Documents > Migration from Swagger\nRun npx @nestia/migrate command in your terminal. The @nestia/migrate program will inquiry you something. After that, you can generate the SDK library in your local machine, what you've seen in the \"TypeScript Swagger Editor\".For reference, if your backend application utilizes nestia (NestJS), you don't need to build the SDK (Software Development Kit) library by converting from the Swagger Documents. The nestia will automatically generate the much advanced SDK library, just by analyzing your backend application's source code.","roadmap#Roadmap":"Currently, \"TypeScript Swagger Editor\" is utilizing the StackBlitz as the web-based TypeScript editor.However, if many developers agree with the usefulness of the SDK and are interested in \"TypeScript Swagger Editor\", I am planning to develop a new standalone web application called @nestia/editor. It will take advantages of both \"Swagger-UI\" and \"TypeScript Swagger Editor\", and provide more convenient features for the API interaction development.I don't know how popular \"TypeScript Swagger Editor\" would be at this stage, but I hope that I can proceed with the @nestia/editor project as I have created something that front (client) developers desperately need.Let's nestia together."}},"/docs":{"title":"Index","data":{"outline#Outline":"Nestia is a set of helper libraries for NestJS, supporting below features:\n@nestia/core:\nSuper-fast/easy decorators\nAdvanced WebSocket routes\n@nestia/sdk:\nSwagger generator evolved than ever\nOpenAI function calling schema generator\nSDK library generator for clients\nMockup Simulator for client developers\nAutomatic E2E test functions generator\n@nestia/e2e: Test program utilizing e2e test functions\n@nestia/benchmark: Benchmark program using e2e test functions\n@nestia/migrate: OpenAPI generator from Swagger to NestJS/SDK\n@nestia/editor: Swagger-UI with Online TypeScript Editor\nnestia: Just CLI (command line interface) tool\nOnly one line required, with pure TypeScript type\nEnhance performance 30x up\nRuntime validator is 20,000x faster than class-validator\nJSON serialization is 200x faster than class-transformer\nSoftware Development Kit\nCollection of typed fetch functions with DTO structures like tRPC\nMockup simulator means embedded backend simulator in the SDK\nsimilar with msw, but fully automated\nLeft is NestJS server code, and right is client (frontend) code utilizing SDK","sponsors#Sponsors":"Thanks for your support.Your donation would encourage nestia development."}},"/docs/migrate":{"title":"Migration from Swagger","data":{"outline#Outline":"npx @nestia/migrate\n@nestia/migrate converts swagger.json file to:\nNestJS Project\nStandalone SDK Library\nStandalone Mockup Simulator\nWhen you run npx @nestia/migrate command, @nestia/migrate will analyze your swagger.json file, and generate a NestJS project into the output direcory. If you're considering to migrate your backend project to NestJS, @nestia/migrate will be a good starting point.Of course, even if you're not considering to adapt NestJS, you can generate standalone SDK library and Mockup Simulator from the swagger.json file instead. Just run the npx @nestia/migrate command and build the SDK library and Mockup Simulator, then frontend (client) developers may get satisfied.\nLeft is NestJS server code, and right is client (frontend) code utilizing SDK","arguments#Arguments":"# GRAMMER\nnpx @nestia/migrate \n --mode [nest|sdk] \n --input [input] \n --output [output] \n --simulate [true|false]\n --e2e [true|false]\n# EXAMPLES\nnpx @nestia/migrate --mode nest --input swagger.json --output directory --simulate false --e2e false\nnpx @nestia/migrate --mode sdk --input swagger.json --output directory --simulate true --e2e true\nYou can specify migration options like above.However, don't worry anything. If you omit something, prompt inquiries will help you.\nnpx @nestia/migrate\n? Migration mode (Use arrow keys):\n > NestJS\n SDK\n? Swagger file location: assets/input/clickhouse.json\n? Output directory path: assets/output/clickhouse-sdk-manual\n? Mokup Simulator: true\n? E2E Test Functions: true","cloud-editor#Cloud Editor":"You can test @nestia/migrate on the web browser.Visit TypeScript Swagger Editor and upload your swagger.json file.Then, you can test the migrated NestJS Project, SDK library and Mockup Simulator in the web browser.\nPut your swagger.json file, then \"TypeScript Swagger Editor\" be opened."}},"/":{"title":"Index","data":{"key-features#Key Features":"","sponsors#Sponsors":"Thanks for your support.Your donation would encourage nestia development."}},"/playground":{"title":"Index","data":{}},"/docs/core/TypedBody":{"title":"Typedbody","data":{"outline#Outline":"export function TypedBody(): ParameterDecorator;\nRequest body decorator 20,000x faster, even easy to use.@TypedBody() is a decorator function parsing application/json typed request body, and validates the request body value type through typia.assert() function. If the request body is not following the promised type, 400 bad request error would be thrown.It is almost same with original @Body() function of NestJS, however, 20,000x faster.Also, @TypedBody() is much more easier to use than class-validator, because it can use pure TypeScript type. If you can't understand the word \"pure TypeScript type\", then move to below #How to use section and read the IBbsArticle.IUpdate interface type. You may understand what it means.\nIf you want other Content-Type, use one of below:\napplication/x-www-form-urlencoded: @TypedQuery.Body().\nmultipart/form-data: @TypedFormData.Body()\n@TypedBody() is not essential for Swagger Documents or SDK Library building.Therefore, it is not a matter to use @TypedBody() or @Body() of the original NestJS.","how-to-use#How to use":"import { tags } from \"typia\";\nexport interface IBbsArticle extends IBbsArticle.IStore {\n id: string & tags.Format<\"uuid\">;\n created_at: string & tags.Format<\"date-time\">;\n}\nexport namespace IBbsArticle {\n export interface IStore {\n title: string & tags.MinLength<3> & tags.MaxLength<50>;\n body: string;\n files: IAttachmentFile[];\n }\n}\nimport { tags } from \"typia\";\nexport interface IAttachmentFile {\n name: null | (string & tags.MinLength<1> & tags.MaxLength<255>);\n extension: null | (string & tags.MinLength<1> & tags.MaxLength<8>);\n url: string & tags.Format<\"url\">;\n}\nimport { TypedBody, TypedRoute } from \"@nestia/core\";\nimport { Controller } from \"@nestjs/common\";\nimport { tags } from \"typia\";\nimport { IBbsArticle } from \"./IBbsArticle\";\n@Controller(\"bbs/articles\")\nexport class BbsArticlesController {\n @TypedRoute.Post()\n public async store(\n @TypedBody() input: IBbsArticle.IStore,\n ): Promise {\n return {\n ...input,\n id: \"2b5e21d8-0e44-4482-bd3e-4540dee7f3d6\",\n created_at: \"2023-04-23T12:04:54.168Z\",\n };\n }\n}\n\"use strict\";\nvar __decorate =\n (this && this.__decorate) ||\n function (decorators, target, key, desc) {\n var c = arguments.length,\n r =\n c < 3\n ? target\n : desc === null\n ? (desc = Object.getOwnPropertyDescriptor(target, key))\n : desc,\n d;\n if (typeof Reflect === \"object\" && typeof Reflect.decorate === \"function\")\n r = Reflect.decorate(decorators, target, key, desc);\n else\n for (var i = decorators.length - 1; i >= 0; i--)\n if ((d = decorators[i]))\n r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;\n return c > 3 && r && Object.defineProperty(target, key, r), r;\n };\nvar __metadata =\n (this && this.__metadata) ||\n function (k, v) {\n if (typeof Reflect === \"object\" && typeof Reflect.metadata === \"function\")\n return Reflect.metadata(k, v);\n };\nvar __param =\n (this && this.__param) ||\n function (paramIndex, decorator) {\n return function (target, key) {\n decorator(target, key, paramIndex);\n };\n };\nvar __awaiter =\n (this && this.__awaiter) ||\n function (thisArg, _arguments, P, generator) {\n function adopt(value) {\n return value instanceof P\n ? value\n : new P(function (resolve) {\n resolve(value);\n });\n }\n return new (P || (P = Promise))(function (resolve, reject) {\n function fulfilled(value) {\n try {\n step(generator.next(value));\n } catch (e) {\n reject(e);\n }\n }\n function rejected(value) {\n try {\n step(generator[\"throw\"](value));\n } catch (e) {\n reject(e);\n }\n }\n function step(result) {\n result.done\n ? resolve(result.value)\n : adopt(result.value).then(fulfilled, rejected);\n }\n step((generator = generator.apply(thisArg, _arguments || [])).next());\n });\n };\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.BbsArticlesController = void 0;\nconst core_1 = require(\"@nestia/core\");\nconst common_1 = require(\"@nestjs/common\");\nlet BbsArticlesController = class BbsArticlesController {\n store(input) {\n return __awaiter(this, void 0, void 0, function* () {\n return Object.assign(Object.assign({}, input), {\n id: \"2b5e21d8-0e44-4482-bd3e-4540dee7f3d6\",\n created_at: \"2023-04-23T12:04:54.168Z\",\n });\n });\n }\n};\nexports.BbsArticlesController = BbsArticlesController;\n__decorate(\n [\n core_1.TypedRoute.Post({\n type: \"assert\",\n assert: (input) => {\n const assert = (input) => {\n const __is = (input) => {\n const $io0 = (input) =>\n \"string\" === typeof input.id &&\n /^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000)$/i.test(\n input.id,\n ) &&\n \"string\" === typeof input.created_at &&\n !isNaN(new Date(input.created_at).getTime()) &&\n \"string\" === typeof input.title &&\n 3 <= input.title.length &&\n input.title.length <= 50 &&\n \"string\" === typeof input.body &&\n Array.isArray(input.files) &&\n input.files.every(\n (elem) =>\n \"object\" === typeof elem && null !== elem && $io1(elem),\n );\n const $io1 = (input) =>\n (null === input.name ||\n (\"string\" === typeof input.name &&\n 1 <= input.name.length &&\n input.name.length <= 255)) &&\n (null === input.extension ||\n (\"string\" === typeof input.extension &&\n 1 <= input.extension.length &&\n input.extension.length <= 8)) &&\n \"string\" === typeof input.url &&\n /^[a-zA-Z0-9]+:\\/\\/(?:www.)?[-a-zA-Z0-9@:%._+~#=]{1,256}.[a-zA-Z0-9()]{1,6}\\b(?:[-a-zA-Z0-9()@:%_+.~#?&/=]*)$/.test(\n input.url,\n );\n return \"object\" === typeof input && null !== input && $io0(input);\n };\n if (false === __is(input))\n ((input, _path, _exceptionable = true) => {\n const $guard = core_1.TypedRoute.Post.guard;\n const $ao0 = (input, _path, _exceptionable = true) =>\n ((\"string\" === typeof input.id &&\n (/^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000)$/i.test(\n input.id,\n ) ||\n $guard(_exceptionable, {\n path: _path + \".id\",\n expected: 'string & Format<\"uuid\">',\n value: input.id,\n }))) ||\n $guard(_exceptionable, {\n path: _path + \".id\",\n expected: '(string & Format<\"uuid\">)',\n value: input.id,\n })) &&\n ((\"string\" === typeof input.created_at &&\n (!isNaN(new Date(input.created_at).getTime()) ||\n $guard(_exceptionable, {\n path: _path + \".created_at\",\n expected: 'string & Format<\"date-time\">',\n value: input.created_at,\n }))) ||\n $guard(_exceptionable, {\n path: _path + \".created_at\",\n expected: '(string & Format<\"date-time\">)',\n value: input.created_at,\n })) &&\n ((\"string\" === typeof input.title &&\n (3 <= input.title.length ||\n $guard(_exceptionable, {\n path: _path + \".title\",\n expected: \"string & MinLength<3>\",\n value: input.title,\n })) &&\n (input.title.length <= 50 ||\n $guard(_exceptionable, {\n path: _path + \".title\",\n expected: \"string & MaxLength<50>\",\n value: input.title,\n }))) ||\n $guard(_exceptionable, {\n path: _path + \".title\",\n expected: \"(string & MinLength<3> & MaxLength<50>)\",\n value: input.title,\n })) &&\n (\"string\" === typeof input.body ||\n $guard(_exceptionable, {\n path: _path + \".body\",\n expected: \"string\",\n value: input.body,\n })) &&\n (((Array.isArray(input.files) ||\n $guard(_exceptionable, {\n path: _path + \".files\",\n expected: \"Array\",\n value: input.files,\n })) &&\n input.files.every(\n (elem, _index1) =>\n (((\"object\" === typeof elem && null !== elem) ||\n $guard(_exceptionable, {\n path: _path + \".files[\" + _index1 + \"]\",\n expected: \"IAttachmentFile\",\n value: elem,\n })) &&\n $ao1(\n elem,\n _path + \".files[\" + _index1 + \"]\",\n true && _exceptionable,\n )) ||\n $guard(_exceptionable, {\n path: _path + \".files[\" + _index1 + \"]\",\n expected: \"IAttachmentFile\",\n value: elem,\n }),\n )) ||\n $guard(_exceptionable, {\n path: _path + \".files\",\n expected: \"Array\",\n value: input.files,\n }));\n const $ao1 = (input, _path, _exceptionable = true) =>\n (null === input.name ||\n (\"string\" === typeof input.name &&\n (1 <= input.name.length ||\n $guard(_exceptionable, {\n path: _path + \".name\",\n expected: \"string & MinLength<1>\",\n value: input.name,\n })) &&\n (input.name.length <= 255 ||\n $guard(_exceptionable, {\n path: _path + \".name\",\n expected: \"string & MaxLength<255>\",\n value: input.name,\n }))) ||\n $guard(_exceptionable, {\n path: _path + \".name\",\n expected:\n \"((string & MinLength<1> & MaxLength<255>) | null)\",\n value: input.name,\n })) &&\n (null === input.extension ||\n (\"string\" === typeof input.extension &&\n (1 <= input.extension.length ||\n $guard(_exceptionable, {\n path: _path + \".extension\",\n expected: \"string & MinLength<1>\",\n value: input.extension,\n })) &&\n (input.extension.length <= 8 ||\n $guard(_exceptionable, {\n path: _path + \".extension\",\n expected: \"string & MaxLength<8>\",\n value: input.extension,\n }))) ||\n $guard(_exceptionable, {\n path: _path + \".extension\",\n expected: \"((string & MinLength<1> & MaxLength<8>) | null)\",\n value: input.extension,\n })) &&\n ((\"string\" === typeof input.url &&\n (/^[a-zA-Z0-9]+:\\/\\/(?:www.)?[-a-zA-Z0-9@:%._+~#=]{1,256}.[a-zA-Z0-9()]{1,6}\\b(?:[-a-zA-Z0-9()@:%_+.~#?&/=]*)$/.test(\n input.url,\n ) ||\n $guard(_exceptionable, {\n path: _path + \".url\",\n expected: 'string & Format<\"url\">',\n value: input.url,\n }))) ||\n $guard(_exceptionable, {\n path: _path + \".url\",\n expected: '(string & Format<\"url\">)',\n value: input.url,\n }));\n return (\n (((\"object\" === typeof input && null !== input) ||\n $guard(true, {\n path: _path + \"\",\n expected: \"IBbsArticle\",\n value: input,\n })) &&\n $ao0(input, _path + \"\", true)) ||\n $guard(true, {\n path: _path + \"\",\n expected: \"IBbsArticle\",\n value: input,\n })\n );\n })(input, \"$input\", true);\n return input;\n };\n const stringify = (input) => {\n const $io1 = (input) =>\n (null === input.name ||\n (\"string\" === typeof input.name &&\n 1 <= input.name.length &&\n input.name.length <= 255)) &&\n (null === input.extension ||\n (\"string\" === typeof input.extension &&\n 1 <= input.extension.length &&\n input.extension.length <= 8)) &&\n \"string\" === typeof input.url &&\n /^[a-zA-Z0-9]+:\\/\\/(?:www.)?[-a-zA-Z0-9@:%._+~#=]{1,256}.[a-zA-Z0-9()]{1,6}\\b(?:[-a-zA-Z0-9()@:%_+.~#?&/=]*)$/.test(\n input.url,\n );\n const $string = core_1.TypedRoute.Post.string;\n const $so0 = (input) =>\n `{\"id\":${$string(input.id)},\"created_at\":${$string(\n input.created_at,\n )},\"title\":${$string(input.title)},\"body\":${$string(\n input.body,\n )},\"files\":${`[${input.files\n .map((elem) => $so1(elem))\n .join(\",\")}]`}}`;\n const $so1 = (input) =>\n `{\"name\":${\n null !== input.name ? $string(input.name) : \"null\"\n },\"extension\":${\n null !== input.extension ? $string(input.extension) : \"null\"\n },\"url\":${$string(input.url)}}`;\n return $so0(input);\n };\n return stringify(assert(input));\n },\n }),\n __param(\n 0,\n (0, core_1.TypedBody)({\n type: \"assert\",\n assert: (input) => {\n const __is = (input) => {\n const $io0 = (input) =>\n \"string\" === typeof input.title &&\n 3 <= input.title.length &&\n input.title.length <= 50 &&\n \"string\" === typeof input.body &&\n Array.isArray(input.files) &&\n input.files.every(\n (elem) =>\n \"object\" === typeof elem && null !== elem && $io1(elem),\n );\n const $io1 = (input) =>\n (null === input.name ||\n (\"string\" === typeof input.name &&\n 1 <= input.name.length &&\n input.name.length <= 255)) &&\n (null === input.extension ||\n (\"string\" === typeof input.extension &&\n 1 <= input.extension.length &&\n input.extension.length <= 8)) &&\n \"string\" === typeof input.url &&\n /^[a-zA-Z0-9]+:\\/\\/(?:www.)?[-a-zA-Z0-9@:%._+~#=]{1,256}.[a-zA-Z0-9()]{1,6}\\b(?:[-a-zA-Z0-9()@:%_+.~#?&/=]*)$/.test(\n input.url,\n );\n return \"object\" === typeof input && null !== input && $io0(input);\n };\n if (false === __is(input))\n ((input, _path, _exceptionable = true) => {\n const $guard = core_1.TypedBody.guard;\n const $ao0 = (input, _path, _exceptionable = true) =>\n ((\"string\" === typeof input.title &&\n (3 <= input.title.length ||\n $guard(_exceptionable, {\n path: _path + \".title\",\n expected: \"string & MinLength<3>\",\n value: input.title,\n })) &&\n (input.title.length <= 50 ||\n $guard(_exceptionable, {\n path: _path + \".title\",\n expected: \"string & MaxLength<50>\",\n value: input.title,\n }))) ||\n $guard(_exceptionable, {\n path: _path + \".title\",\n expected: \"(string & MinLength<3> & MaxLength<50>)\",\n value: input.title,\n })) &&\n (\"string\" === typeof input.body ||\n $guard(_exceptionable, {\n path: _path + \".body\",\n expected: \"string\",\n value: input.body,\n })) &&\n (((Array.isArray(input.files) ||\n $guard(_exceptionable, {\n path: _path + \".files\",\n expected: \"Array\",\n value: input.files,\n })) &&\n input.files.every(\n (elem, _index1) =>\n (((\"object\" === typeof elem && null !== elem) ||\n $guard(_exceptionable, {\n path: _path + \".files[\" + _index1 + \"]\",\n expected: \"IAttachmentFile\",\n value: elem,\n })) &&\n $ao1(\n elem,\n _path + \".files[\" + _index1 + \"]\",\n true && _exceptionable,\n )) ||\n $guard(_exceptionable, {\n path: _path + \".files[\" + _index1 + \"]\",\n expected: \"IAttachmentFile\",\n value: elem,\n }),\n )) ||\n $guard(_exceptionable, {\n path: _path + \".files\",\n expected: \"Array\",\n value: input.files,\n }));\n const $ao1 = (input, _path, _exceptionable = true) =>\n (null === input.name ||\n (\"string\" === typeof input.name &&\n (1 <= input.name.length ||\n $guard(_exceptionable, {\n path: _path + \".name\",\n expected: \"string & MinLength<1>\",\n value: input.name,\n })) &&\n (input.name.length <= 255 ||\n $guard(_exceptionable, {\n path: _path + \".name\",\n expected: \"string & MaxLength<255>\",\n value: input.name,\n }))) ||\n $guard(_exceptionable, {\n path: _path + \".name\",\n expected:\n \"((string & MinLength<1> & MaxLength<255>) | null)\",\n value: input.name,\n })) &&\n (null === input.extension ||\n (\"string\" === typeof input.extension &&\n (1 <= input.extension.length ||\n $guard(_exceptionable, {\n path: _path + \".extension\",\n expected: \"string & MinLength<1>\",\n value: input.extension,\n })) &&\n (input.extension.length <= 8 ||\n $guard(_exceptionable, {\n path: _path + \".extension\",\n expected: \"string & MaxLength<8>\",\n value: input.extension,\n }))) ||\n $guard(_exceptionable, {\n path: _path + \".extension\",\n expected: \"((string & MinLength<1> & MaxLength<8>) | null)\",\n value: input.extension,\n })) &&\n ((\"string\" === typeof input.url &&\n (/^[a-zA-Z0-9]+:\\/\\/(?:www.)?[-a-zA-Z0-9@:%._+~#=]{1,256}.[a-zA-Z0-9()]{1,6}\\b(?:[-a-zA-Z0-9()@:%_+.~#?&/=]*)$/.test(\n input.url,\n ) ||\n $guard(_exceptionable, {\n path: _path + \".url\",\n expected: 'string & Format<\"url\">',\n value: input.url,\n }))) ||\n $guard(_exceptionable, {\n path: _path + \".url\",\n expected: '(string & Format<\"url\">)',\n value: input.url,\n }));\n return (\n (((\"object\" === typeof input && null !== input) ||\n $guard(true, {\n path: _path + \"\",\n expected: \"IBbsArticle.IStore\",\n value: input,\n })) &&\n $ao0(input, _path + \"\", true)) ||\n $guard(true, {\n path: _path + \"\",\n expected: \"IBbsArticle.IStore\",\n value: input,\n })\n );\n })(input, \"$input\", true);\n return input;\n },\n }),\n ),\n __metadata(\"design:type\", Function),\n __metadata(\"design:paramtypes\", [Object]),\n __metadata(\"design:returntype\", Promise),\n ],\n BbsArticlesController.prototype,\n \"store\",\n null,\n);\nexports.BbsArticlesController = BbsArticlesController = __decorate(\n [(0, common_1.Controller)(\"bbs/articles\")],\n BbsArticlesController,\n);\nJust call @TypedBody() function on the request body parameter, that's all.Nestia will analyze your type (IBbsArticle.IUpdate), and writes optimal code for the target type, in the compilation level. If you click the \"Complied JavaScript File\" tab of above, you can see the optimal validation code.Such optimization is called AOT (Ahead of Time) compilation, and it is the secret of @TypedBody.","special-tags#Special Tags":"You can enhance validation logic, of @TypedBody(), through special tags.You know what? @TypedBody() utilizes typia.assert() function for request body data validation, and the typia.assert() function supports additional type checking logics through type and comment tags. For reference, \"Type Tag\" means a intersection type with atomic type and special tag type of typia like number & tags.Type<\"uint32\">, and \"Comment Tag\" means a comment starting from @ symbol following @${name} ${value} format.With those type and comment tags, you can add additional validation logics. If you want to add a custom validation logic, you also can do it. Read below Guide Docments of typia, and see the example code. You may understand how to utilize such type and comment tags, in a few minutes.\ntypia > Validators > Custom Tags\nOutline\nType Tags\nComment Tags\nCustomization\nimport typia, { tags } from \"typia\";\nexport const checkSpecialTag = typia.createIs();\ninterface SpecialTag {\n int32: number & tags.Type<\"int32\">;\n range?: number & tags.ExclusiveMinimum<19> & tags.Maximum<100>;\n minLength: string & tags.MinLength<3>;\n pattern: string & tags.Pattern<\"^[a-z]+$\">;\n date: null | (string & tags.Format<\"date\">);\n ip: string & (tags.Format<\"ipv4\"> | tags.Format<\"ipv6\">);\n uuids: Array> &\n tags.MinItems<3> &\n tags.MaxItems<100>;\n}\n\"use strict\";\nvar __importDefault =\n (this && this.__importDefault) ||\n function (mod) {\n return mod && mod.__esModule ? mod : { default: mod };\n };\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.checkSpecialTag = void 0;\nconst typia_1 = __importDefault(require(\"typia\"));\nconst checkSpecialTag = (input) => {\n const $io0 = (input) =>\n \"number\" === typeof input.int32 &&\n Math.floor(input.int32) === input.int32 &&\n -2147483648 <= input.int32 &&\n input.int32 <= 2147483647 &&\n (undefined === input.range ||\n (\"number\" === typeof input.range &&\n 19 < input.range &&\n input.range <= 100)) &&\n \"string\" === typeof input.minLength &&\n 3 <= input.minLength.length &&\n \"string\" === typeof input.pattern &&\n /^[a-z]+$/.test(input.pattern) &&\n (null === input.date ||\n (\"string\" === typeof input.date &&\n /^(d{4})-(d{2})-(d{2})$/.test(input.date))) &&\n \"string\" === typeof input.ip &&\n (/^(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.test(\n input.ip,\n ) ||\n /^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]).){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]).){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$/.test(\n input.ip,\n )) &&\n Array.isArray(input.uuids) &&\n 3 <= input.uuids.length &&\n input.uuids.length <= 100 &&\n input.uuids.every(\n (elem) =>\n \"string\" === typeof elem &&\n /^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000)$/i.test(\n elem,\n ),\n );\n return \"object\" === typeof input && null !== input && $io0(input);\n};\nexports.checkSpecialTag = checkSpecialTag;","encryptedbody#EncryptedBody":"Encrypted request body decorator function.@EncryptedBody() is a decorator function similar with @TypedBody(), but it encrypts the request body through AES-128/256-CBC algorithm like below. Therefore, it would be slower than @TypedBody(), but it guarantees the security of request body data.\nAES-128/256\nCBC mode\nPKCS #5 Padding\nBase64 Encoding\nFor reference, such encryption spec is not supported in the Swagger-UI. Instead, SDK (Software Development Kit) generated by @nestia/sdk supports it. Thus, you have to build and distribute the SDK library to the client developers when using such encryption decorators.","configuration#Configuration":"{\n \"compilerOptions\": {\n \"strict\": true,\n \"plugins\": [\n { \"transform\": \"typia/lib/transform\" },\n {\n \"transform\": \"@nestia/core/lib/transform\",\n \"validate\": \"assert\",\n // \"assert\"\n // \"is\"\n // \"validate\"\n // \"assertEquals\"\n // \"equals\"\n // \"validateEquals\"\n }\n ]\n }\n}\nChange type validation function to another one.If you configure validate property of plugin defined in the tsconfig.json file, you can change the @TypedBody() to use another validation function instead of the default typia.assert function. For example, if you want to use typia.validateEquals() function instead, then set the validate property to validateEquals.Below is the list of available validation functions.\nis or equals: check validation only, and do not reveal the reason why\nassert or assertEquals: find the 1st type error with reason\nvalidate or validateEquals: find every type errors with detailed reasons\nassertPrune or validatePrune: prune extra properties with type checking\nassertClone or validateClone: deep clone with type checking for faster pruning","benchmark#Benchmark":"Super-fast and super-safe.Nestia utilizes typia, and NestJS uses class-validator. One thing amazing is, typia is maximum 20,000x faster than class-validator of NestJS. Color of class-transformer is skyblue, and can you find the skyblue color in the below benchmark graph? It may hard to find because class-validator is extremely slow.\nMeasured on AMD R9-7940HS, Rog Flow X13\nFurthermore, while other libraries can't validate complicate union types, typia can validate every TypeScript types. However, in the class-validator case, it always be failed when any type of complicate comes. I can't understand why NestJS has adopted such slow and unstable library.Moreover, only typia can utilize pure TypeScript type, without any extra schema definition. Beside, all of other libraries require extra and duplicated schema definition, different with pure TypeScript type. Nestia is using such wonderful, super-fast and super-fast typia library.\nTypeBox requires TypeBox schema\najv requires JSON schema definition\nio-ts requires io-ts schema\nzod requires zod schema\nclass-validator requires DTO class with decorator function calls\nComponents\ttypia\tTypeBox\tajv\tio-ts\tzod\tC.V.\tEasy to use\tβ \tβ\tβ\tβ\tβ\tβ\tObject (simple)\tβ\tβ\tβ\tβ\tβ\tβ\tObject (hierarchical)\tβ\tβ\tβ\tβ\tβ\tβ\tObject (recursive)\tβ\tβ\tβ\tβ\tβ\tβ\tβ\tObject (union, implicit)\tβ \tβ\tβ\tβ\tβ\tβ\tObject (union, explicit)\tβ\tβ\tβ\tβ\tβ\tβ\tObject (additional tags)\tβ\tβ\tβ\tβ\tβ\tβ\tObject (template literal types)\tβ\tβ\tβ\tβ\tβ\tβ\tObject (dynamic properties)\tβ\tβ\tβ\tβ\tβ\tβ\tArray (rest tuple)\tβ \tβ\tβ\tβ\tβ\tβ\tArray (hierarchical)\tβ\tβ\tβ\tβ\tβ\tβ\tArray (recursive)\tβ\tβ\tβ\tβ\tβ\tβ\tArray (recursive, union)\tβ\tβ\tβ\tβ\tβ\tβ\tArray (R+U, implicit)\tβ \tβ\tβ\tβ\tβ\tβ\tUltimate Union Type\tβ \tβ\tβ\tβ\tβ\tβ\t\nC.V. means class-validator"}},"/docs/core/TypedFormData":{"title":"Typedformdata","data":{"outline#Outline":"export namespace TypedFormData {\n export function Body(): ParameterDecorator;\n}\nRequest body decorator of multipart/form-data.@TypedFormData.Body() is a request body decorator function, for the multipart/form-data content type. It is useful for file uploading with additional data, and automatically casts property type following its DTO definition, performing the type validation.As you can see from the below code, @TypedFormData.Body() function is much easier and type safer than @UploadFile() of NestJS. Also, if you're considering the SDK library generation, only @TypedFormData.Body() is supported. Therefore, I recommend you to utilize @TypedFormData.Body() instead of the @UploadFile() function.Of course, as every features of nestia does, you don't need to define any extra schema definition for the Swagger Documents generation. @nestia/sdk and @TypedFormData.Body() will do everything just by analyzing your TypeScript types and codes.\nimport { TypedFormData, TypedRoute } from \"@nestia/core\";\nimport { Controller } from \"@nestjs/common\";\n@Controller(\"bbs/articles\")\nexport class BbsArticlesController {\n @TypedRoute.Post()\n public async create(\n @TypedFormData.Body() input: IBbsArticleCreate,\n ): Promise {\n input;\n }\n}\nexport interface IBbsArticleCreate {\n title: string;\n body: string | null;\n thumbnail?: File | undefined;\n files: File[];\n tags: string[];\n}\nimport { \n Body, \n Controller, \n Post, \n UploadedFile, \n UploadedFiles, \n UseInterceptors \n} from \"@nestjs/common\";\nimport { ApiBody, ApiConsumes } from \"@nestjs/swagger\";\nimport { FileFieldsInterceptor } from \"@nestjs/platform-express\";\nimport { IsArray, IsOptional, IsString } from \"class-validator\";\n@Controller(\"bbs/articles\")\nexport class BbsArticlesController {\n @Post()\n @ApiConsumes(\"multipart/form-data\")\n @UseInterceptors(\n FileFieldsInterceptor([\n { name: \"thumbnail\", maxCount: 1 },\n { name: \"files\" },\n ]),\n )\n @ApiBody({\n schema: {\n type: \"object\",\n properties: {\n title: {\n type: \"string\",\n },\n body: {\n type: \"string\",\n nullable: true,\n },\n thumbnail: {\n type: \"string\",\n format: \"binary\",\n },\n files: {\n type: \"array\",\n items: {\n type: \"string\",\n format: \"binary\",\n },\n },\n },\n required: [\n \"id\", \n \"title\", \n \"body\", \n \"files\",\n \"tags\",\n \"created_at\",\n ],\n },\n })\n public async create(\n @Body() input: BbsArticleCreateDto,\n @UploadedFiles() binary: {\n thumbnail?: Express.Multer.File[];\n files: Express.Multer.File[];\n },\n ): Promise {\n input;\n binary;\n }\n}\nexport class BbsArticleCreateDto {\n @IsString()\n title: string;\n @IsString()\n @IsOptional()\n body: string | null;\n @IsArray({ each: true })\n @IsString()\n tags: string[];\n}","how-to-use#How to use":"export interface IBbsArticleCreate {\n title: string;\n body: string | null;\n thumbnail?: File | undefined;\n files: File[];\n tags: string[];\n}\nimport { TypedFormData, TypedRoute } from \"@nestia/core\";\nimport { Controller } from \"@nestjs/common\";\nimport { IBbsArticleCreate } from \"./IBbsArticleCreate\";\n@Controller(\"bbs/articles\")\nexport class BbsArticlesController {\n @TypedRoute.Post()\n public async create(\n @TypedFormData.Body() input: IBbsArticleCreate,\n ): Promise {\n input;\n }\n}\n\"use strict\";\nvar __decorate =\n (this && this.__decorate) ||\n function (decorators, target, key, desc) {\n var c = arguments.length,\n r =\n c < 3\n ? target\n : desc === null\n ? (desc = Object.getOwnPropertyDescriptor(target, key))\n : desc,\n d;\n if (typeof Reflect === \"object\" && typeof Reflect.decorate === \"function\")\n r = Reflect.decorate(decorators, target, key, desc);\n else\n for (var i = decorators.length - 1; i >= 0; i--)\n if ((d = decorators[i]))\n r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;\n return c > 3 && r && Object.defineProperty(target, key, r), r;\n };\nvar __metadata =\n (this && this.__metadata) ||\n function (k, v) {\n if (typeof Reflect === \"object\" && typeof Reflect.metadata === \"function\")\n return Reflect.metadata(k, v);\n };\nvar __param =\n (this && this.__param) ||\n function (paramIndex, decorator) {\n return function (target, key) {\n decorator(target, key, paramIndex);\n };\n };\nvar __awaiter =\n (this && this.__awaiter) ||\n function (thisArg, _arguments, P, generator) {\n function adopt(value) {\n return value instanceof P\n ? value\n : new P(function (resolve) {\n resolve(value);\n });\n }\n return new (P || (P = Promise))(function (resolve, reject) {\n function fulfilled(value) {\n try {\n step(generator.next(value));\n } catch (e) {\n reject(e);\n }\n }\n function rejected(value) {\n try {\n step(generator[\"throw\"](value));\n } catch (e) {\n reject(e);\n }\n }\n function step(result) {\n result.done\n ? resolve(result.value)\n : adopt(result.value).then(fulfilled, rejected);\n }\n step((generator = generator.apply(thisArg, _arguments || [])).next());\n });\n };\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.BbsArticlesController = void 0;\nconst core_1 = require(\"@nestia/core\");\nconst common_1 = require(\"@nestjs/common\");\nlet BbsArticlesController = class BbsArticlesController {\n create(input) {\n return __awaiter(this, void 0, void 0, function* () {\n input;\n });\n }\n};\nexports.BbsArticlesController = BbsArticlesController;\n__decorate(\n [\n core_1.TypedRoute.Post({\n type: \"assert\",\n assert: (input) => {\n const assert = (input) => {\n const __is = (input) => {\n return null !== input && undefined === input;\n };\n if (false === __is(input))\n ((input, _path, _exceptionable = true) => {\n const $guard = core_1.TypedRoute.Post.guard;\n return (\n (null !== input ||\n $guard(true, {\n path: _path + \"\",\n expected: \"undefined\",\n value: input,\n })) &&\n (undefined === input ||\n $guard(true, {\n path: _path + \"\",\n expected: \"undefined\",\n value: input,\n }))\n );\n })(input, \"$input\", true);\n return input;\n };\n const stringify = (input) => {\n return undefined;\n };\n return stringify(assert(input));\n },\n }),\n __param(\n 0,\n core_1.TypedFormData.Body({\n files: [\n {\n name: \"thumbnail\",\n limit: 1,\n },\n {\n name: \"files\",\n limit: null,\n },\n ],\n validator: {\n type: \"assert\",\n assert: (input) => {\n const decode = (input) => {\n var _a;\n const $string = core_1.TypedFormData.Body.string;\n const $file = core_1.TypedFormData.Body.file;\n const output = {\n title: $string(input.get(\"title\")),\n body: $string(input.get(\"body\")),\n thumbnail:\n (_a = $file(input.get(\"thumbnail\"))) !== null && _a !== void 0\n ? _a\n : undefined,\n files: input.getAll(\"files\").map((elem) => $file(elem)),\n tags: input.getAll(\"tags\").map((elem) => $string(elem)),\n };\n return output;\n };\n const assert = (input) => {\n const __is = (input) => {\n const $io0 = (input) =>\n \"string\" === typeof input.title &&\n (null === input.body || \"string\" === typeof input.body) &&\n (undefined === input.thumbnail ||\n input.thumbnail instanceof File) &&\n Array.isArray(input.files) &&\n input.files.every((elem) => elem instanceof File) &&\n Array.isArray(input.tags) &&\n input.tags.every((elem) => \"string\" === typeof elem);\n return (\n \"object\" === typeof input && null !== input && $io0(input)\n );\n };\n if (false === __is(input))\n ((input, _path, _exceptionable = true) => {\n const $guard = core_1.TypedFormData.Body.guard;\n const $ao0 = (input, _path, _exceptionable = true) =>\n (\"string\" === typeof input.title ||\n $guard(_exceptionable, {\n path: _path + \".title\",\n expected: \"string\",\n value: input.title,\n })) &&\n (null === input.body ||\n \"string\" === typeof input.body ||\n $guard(_exceptionable, {\n path: _path + \".body\",\n expected: \"(null | string)\",\n value: input.body,\n })) &&\n (undefined === input.thumbnail ||\n input.thumbnail instanceof File ||\n $guard(_exceptionable, {\n path: _path + \".thumbnail\",\n expected: \"(File | undefined)\",\n value: input.thumbnail,\n })) &&\n (((Array.isArray(input.files) ||\n $guard(_exceptionable, {\n path: _path + \".files\",\n expected: \"Array\",\n value: input.files,\n })) &&\n input.files.every(\n (elem, _index1) =>\n elem instanceof File ||\n $guard(_exceptionable, {\n path: _path + \".files[\" + _index1 + \"]\",\n expected: \"File\",\n value: elem,\n }),\n )) ||\n $guard(_exceptionable, {\n path: _path + \".files\",\n expected: \"Array\",\n value: input.files,\n })) &&\n (((Array.isArray(input.tags) ||\n $guard(_exceptionable, {\n path: _path + \".tags\",\n expected: \"Array\",\n value: input.tags,\n })) &&\n input.tags.every(\n (elem, _index2) =>\n \"string\" === typeof elem ||\n $guard(_exceptionable, {\n path: _path + \".tags[\" + _index2 + \"]\",\n expected: \"string\",\n value: elem,\n }),\n )) ||\n $guard(_exceptionable, {\n path: _path + \".tags\",\n expected: \"Array\",\n value: input.tags,\n }));\n return (\n (((\"object\" === typeof input && null !== input) ||\n $guard(true, {\n path: _path + \"\",\n expected: \"IBbsArticleCreate\",\n value: input,\n })) &&\n $ao0(input, _path + \"\", true)) ||\n $guard(true, {\n path: _path + \"\",\n expected: \"IBbsArticleCreate\",\n value: input,\n })\n );\n })(input, \"$input\", true);\n return input;\n };\n const output = decode(input);\n return assert(output);\n },\n },\n }),\n ),\n __metadata(\"design:type\", Function),\n __metadata(\"design:paramtypes\", [Object]),\n __metadata(\"design:returntype\", Promise),\n ],\n BbsArticlesController.prototype,\n \"create\",\n null,\n);\nexports.BbsArticlesController = BbsArticlesController = __decorate(\n [(0, common_1.Controller)(\"bbs/articles\")],\n BbsArticlesController,\n);\n//# sourceMappingURL=BbsArticlesController.js.map\nJust call @TypedFormData.Body() function on the request body parameter, that's all.Nestia will analyze your type (IBbsArticleCreate), and writes optimal code for the target type, in the compilation level. If you click the \"Compiled JavaScript File\" tab of above, you can see the optimal transformation and validation code.Such optimization is called AOT (Ahead of Time) compilation, and it is the secret of @TypedFormData.Body.By the way, if you're using fastify, you have to setup @fastify/multipart and configure like below when composing the NestJS application. If you don't do it, @TypedFormData.Body() will not work properly, and throw 500 internal server error when Blob or File type being utilized.\nimport multipart from \"fastify-multipart\";\nimport { NestFactory } from \"@nestjs/core\";\nimport { \n FastifyAdapter, \n NestFastifyApplication \n} from \"@nestjs/platform-fastify\";\nexport async function main() {\n const app = await NestFactory.create(\n AppModule,\n new FastifyAdapter(),\n );\n app.register(multipart);\n await app.listen(3000);\n}","special-tags#Special Tags":"You can enhance validation logic, of @TypedFormData.Body(), through comment tags.You know what? @TypedFormData.Body() utilizes typia.assert() function for form data validation, and the typia.assert() function supports additional type checking logics through comment tags. For reference, \"Type Tag\" means a intersection type with atomic type and special tag type of typia like number & tags.Type<\"uint32\">, and \"Comment Tag\" means a comment starting from @ symbol following @${name} ${value} format.With those type and comment tags, you can add additional validation logics. If you want to add a custom validation logic, you also can do it. Read below Guide Documents of typia, and see the example code. You may understand how to utilize such type and comment tags, in a few minutes.\ntypia > Validators > Custom Tags\nOutline\nType Tags\nComment Tags\nCustomization\nimport typia, { tags } from \"typia\";\nexport const checkSpecialTag = typia.createIs();\ninterface SpecialTag {\n int32: number & tags.Type<\"int32\">;\n range?: number & tags.ExclusiveMinimum<19> & tags.Maximum<100>;\n minLength: string & tags.MinLength<3>;\n pattern: string & tags.Pattern<\"^[a-z]+$\">;\n date: null | (string & tags.Format<\"date\">);\n ip: string & (tags.Format<\"ipv4\"> | tags.Format<\"ipv6\">);\n uuids: Array> &\n tags.MinItems<3> &\n tags.MaxItems<100>;\n}\n\"use strict\";\nvar __importDefault =\n (this && this.__importDefault) ||\n function (mod) {\n return mod && mod.__esModule ? mod : { default: mod };\n };\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.checkSpecialTag = void 0;\nconst typia_1 = __importDefault(require(\"typia\"));\nconst checkSpecialTag = (input) => {\n const $io0 = (input) =>\n \"number\" === typeof input.int32 &&\n Math.floor(input.int32) === input.int32 &&\n -2147483648 <= input.int32 &&\n input.int32 <= 2147483647 &&\n (undefined === input.range ||\n (\"number\" === typeof input.range &&\n 19 < input.range &&\n input.range <= 100)) &&\n \"string\" === typeof input.minLength &&\n 3 <= input.minLength.length &&\n \"string\" === typeof input.pattern &&\n /^[a-z]+$/.test(input.pattern) &&\n (null === input.date ||\n (\"string\" === typeof input.date &&\n /^(d{4})-(d{2})-(d{2})$/.test(input.date))) &&\n \"string\" === typeof input.ip &&\n (/^(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.test(\n input.ip,\n ) ||\n /^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]).){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]).){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$/.test(\n input.ip,\n )) &&\n Array.isArray(input.uuids) &&\n 3 <= input.uuids.length &&\n input.uuids.length <= 100 &&\n input.uuids.every(\n (elem) =>\n \"string\" === typeof elem &&\n /^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000)$/i.test(\n elem,\n ),\n );\n return \"object\" === typeof input && null !== input && $io0(input);\n};\nexports.checkSpecialTag = checkSpecialTag;","restriction#Restriction":"When using @TypedFormData.Body(), you've to follow such restrction.At first, type of @TypedFormData.Body() must be a pure object type. It does not allow union type. Also, nullable and undefindable types are not allowed, either. Note that, query parameter type must be a sole object type without any extra definition.At next, type of properties must be atomic, Blob, File or array of them. In the atomic type case, the atomic type allows both nullable and undefindable types. However, mixed union atomic type like string | number or \"1\" | \"2\" | 3 are not allowed. Also, the array type does not allow both nullable and undefindable types, either.\nboolean\nnumber\nbigint\nstring\nBlob\nFile\nexport interface SomeFormDataDto {\n //----\n // ATOMIC OR FILE TYPES\n //----\n // ALLOWED\n boolean: boolean;\n number: number;\n string: string;\n bigint: bigint;\n optional_number?: number;\n nullable_string: string | null;\n literal_union: \"A\" | \"B\" | \"C\" | \"D\";\n blob: Blob;\n file: File;\n // NOT ALLOWED\n mixed_union: string | number | boolean;\n mixed_literal: \"A\" | \"B\" | 3;\n //----\n // ARRAY TYPES\n //----\n // ALLOWED\n nullable_element_array: (string | null)[];\n string_array: string[];\n number_array: number[];\n literal_union_array: (\"A\" | \"B\" | \"C\")[];\n literal_tuple: [\"A\", \"B\", \"C\"];\n blobs: Blob[];\n files: File[];\n // NOT ALLOWED\n optional_element_array: (string | undefined)[];\n optional_array: string[] | undefined;\n nullable_array: string[] | null;\n union_atomic_array: (string | number)[];\n mixed_literal_array: (\"A\", \"B\", 3)[];\n mixed_tuple: [\"A\", \"B\", 3];\n}"}},"/docs/core/TypedHeaders":{"title":"Typedheaders","data":{"outline#Outline":"export function TypedHeaders(): ParameterDecorator;\nRequest headers decorator, type safe.@TypedHeaders() is a decorator function parsing request headers to a typed object. It validates the request header values through typia.assert(). If the request header values are invalid, it will throw 400 bad request exception.It is almost same with original @Headers() of NestJS, but much type safe.\n@TypedHeaders() is not essential for Swagger Documents or SDK Library building.Therefore, it is not a matter to use @TypedHeaders() or @Headers() of the original NestJS.","how-to-use#How to use":"export interface IHeaders {\n \"x-category\": \"x\" | \"y\" | \"z\";\n \"x-memo\"?: string;\n \"x-name\"?: string;\n \"x-values\": number[];\n \"x-flags\": boolean[];\n \"X-Descriptions\": string[]; // ALLOW UPPER-CASE\n}\nimport { Controller } from \"@nestjs/common\";\nimport core from \"@nestia/core\";\nimport { IHeaders } from \"@api/lib/structures/IHeaders\";\n@Controller(\"headers/:section\")\nexport class HeadersController {\n /**\n * Emplace headers.\n *\n * @param headers Headers for authentication\n * @param section Target section code\n * @returns Store article\n *\n * @author Samchon\n */\n @core.TypedRoute.Patch()\n public emplace(\n @core.TypedHeaders() headers: IHeaders,\n @core.TypedParam(\"section\", \"string\") section: string,\n ): void {\n headers;\n section;\n }\n}\n\"use strict\";\nvar __decorate =\n (this && this.__decorate) ||\n function (decorators, target, key, desc) {\n var c = arguments.length,\n r =\n c < 3\n ? target\n : desc === null\n ? (desc = Object.getOwnPropertyDescriptor(target, key))\n : desc,\n d;\n if (typeof Reflect === \"object\" && typeof Reflect.decorate === \"function\")\n r = Reflect.decorate(decorators, target, key, desc);\n else\n for (var i = decorators.length - 1; i >= 0; i--)\n if ((d = decorators[i]))\n r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;\n return c > 3 && r && Object.defineProperty(target, key, r), r;\n };\nvar __metadata =\n (this && this.__metadata) ||\n function (k, v) {\n if (typeof Reflect === \"object\" && typeof Reflect.metadata === \"function\")\n return Reflect.metadata(k, v);\n };\nvar __param =\n (this && this.__param) ||\n function (paramIndex, decorator) {\n return function (target, key) {\n decorator(target, key, paramIndex);\n };\n };\nvar __importDefault =\n (this && this.__importDefault) ||\n function (mod) {\n return mod && mod.__esModule ? mod : { default: mod };\n };\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.HeadersController = void 0;\nconst common_1 = require(\"@nestjs/common\");\nconst core_1 = __importDefault(require(\"@nestia/core\"));\nlet HeadersController = (exports.HeadersController = class HeadersController {\n /**\n * Emplace headers.\n *\n * @param headers Headers for authentication\n * @param section Target section code\n * @returns Store article\n *\n * @author Samchon\n */\n emplace(headers, section) {\n headers;\n section;\n }\n});\n__decorate(\n [\n (0, common_1.Get)(),\n __param(\n 0,\n core_1.default.TypedHeaders((input) => {\n const $number = core_1.default.TypedHeaders.number;\n const $boolean = core_1.default.TypedHeaders.boolean;\n const $string = core_1.default.TypedHeaders.string;\n const output = {\n \"x-category\": input[\"x-category\"],\n \"x-memo\": input[\"x-memo\"],\n \"x-name\": input[\"x-name\"],\n \"x-values\": input[\"x-values\"]?.split(\", \")?.map($number),\n \"x-flags\": input[\"x-flags\"]?.split(\", \")?.map($boolean),\n \"X-Descriptions\": input[\"x-descriptions\"]?.split(\", \")?.map($string),\n // AUTOMATIC UPPER-CASE CONVERTING\n };\n return ((input) => {\n const __is = (input) => {\n const $io0 = (input) =>\n (\"x\" === input[\"x-category\"] ||\n \"y\" === input[\"x-category\"] ||\n \"z\" === input[\"x-category\"]) &&\n (undefined === input[\"x-memo\"] ||\n \"string\" === typeof input[\"x-memo\"]) &&\n (undefined === input[\"x-name\"] ||\n \"string\" === typeof input[\"x-name\"]) &&\n Array.isArray(input[\"x-values\"]) &&\n input[\"x-values\"].every(\n (elem) => \"number\" === typeof elem && Number.isFinite(elem),\n ) &&\n Array.isArray(input[\"x-flags\"]) &&\n input[\"x-flags\"].every((elem) => \"boolean\" === typeof elem) &&\n Array.isArray(input[\"X-Descriptions\"]) &&\n input[\"X-Descriptions\"].every((elem) => \"string\" === typeof elem);\n return \"object\" === typeof input && null !== input && $io0(input);\n };\n if (false === __is(input))\n ((input, _path, _exceptionable = true) => {\n const $guard = core_1.default.TypedHeaders.guard;\n const $ao0 = (input, _path, _exceptionable = true) =>\n (\"x\" === input[\"x-category\"] ||\n \"y\" === input[\"x-category\"] ||\n \"z\" === input[\"x-category\"] ||\n $guard(_exceptionable, {\n path: _path + '[\"x-category\"]',\n expected: '(\"x\" | \"y\" | \"z\")',\n value: input[\"x-category\"],\n })) &&\n (undefined === input[\"x-memo\"] ||\n \"string\" === typeof input[\"x-memo\"] ||\n $guard(_exceptionable, {\n path: _path + '[\"x-memo\"]',\n expected: \"(string | undefined)\",\n value: input[\"x-memo\"],\n })) &&\n (undefined === input[\"x-name\"] ||\n \"string\" === typeof input[\"x-name\"] ||\n $guard(_exceptionable, {\n path: _path + '[\"x-name\"]',\n expected: \"(string | undefined)\",\n value: input[\"x-name\"],\n })) &&\n (((Array.isArray(input[\"x-values\"]) ||\n $guard(_exceptionable, {\n path: _path + '[\"x-values\"]',\n expected: \"Array\",\n value: input[\"x-values\"],\n })) &&\n input[\"x-values\"].every(\n (elem, _index1) =>\n (\"number\" === typeof elem && Number.isFinite(elem)) ||\n $guard(_exceptionable, {\n path: _path + '[\"x-values\"][' + _index1 + \"]\",\n expected: \"number\",\n value: elem,\n }),\n )) ||\n $guard(_exceptionable, {\n path: _path + '[\"x-values\"]',\n expected: \"Array\",\n value: input[\"x-values\"],\n })) &&\n (((Array.isArray(input[\"x-flags\"]) ||\n $guard(_exceptionable, {\n path: _path + '[\"x-flags\"]',\n expected: \"Array\",\n value: input[\"x-flags\"],\n })) &&\n input[\"x-flags\"].every(\n (elem, _index2) =>\n \"boolean\" === typeof elem ||\n $guard(_exceptionable, {\n path: _path + '[\"x-flags\"][' + _index2 + \"]\",\n expected: \"boolean\",\n value: elem,\n }),\n )) ||\n $guard(_exceptionable, {\n path: _path + '[\"x-flags\"]',\n expected: \"Array\",\n value: input[\"x-flags\"],\n })) &&\n (((Array.isArray(input[\"X-Descriptions\"]) ||\n $guard(_exceptionable, {\n path: _path + '[\"X-Descriptions\"]',\n expected: \"Array\",\n value: input[\"X-Descriptions\"],\n })) &&\n input[\"X-Descriptions\"].every(\n (elem, _index3) =>\n \"string\" === typeof elem ||\n $guard(_exceptionable, {\n path: _path + '[\"X-Descriptions\"][' + _index3 + \"]\",\n expected: \"string\",\n value: elem,\n }),\n )) ||\n $guard(_exceptionable, {\n path: _path + '[\"X-Descriptions\"]',\n expected: \"Array\",\n value: input[\"X-Descriptions\"],\n }));\n return (\n (((\"object\" === typeof input && null !== input) ||\n $guard(true, {\n path: _path + \"\",\n expected: \"IHeaders\",\n value: input,\n })) &&\n $ao0(input, _path + \"\", true)) ||\n $guard(true, {\n path: _path + \"\",\n expected: \"IHeaders\",\n value: input,\n })\n );\n })(input, \"$input\", true);\n return input;\n })(output);\n }),\n ),\n __param(1, core_1.default.TypedParam(\"section\", \"string\", false)),\n __metadata(\"design:type\", Function),\n __metadata(\"design:paramtypes\", [Object, String]),\n __metadata(\"design:returntype\", void 0),\n ],\n HeadersController.prototype,\n \"emplace\",\n null,\n);\nexports.HeadersController = HeadersController = __decorate(\n [(0, common_1.Controller)(\"headers/:section\")],\n HeadersController,\n);\nJust call @TypedHeaders() function on the request headers parameter, that's all.Nestia will analyze your type (IHeaders), and write optimal conversion and validation code for the target type, in the compilation level. If you click the \"Compiled JavaScript File\" tab of above, you can see the optimal code.Also, as you can see from the \"Compiled JavaScript File\", when upper case alphabet is used in the header key name like IHeaders[\"X-Descriptions\"], @TypedHeaders() would automatically convert to the upper case alphabet key named property from lower case key named property of raw data.Such optimization is called AOT (Ahead of Time) compilation, and it is the secret of @TypedHeaders\nBesides, the original @Headers() decorator of NestJS does not support such automatic upper case conversion. When you've define upper-cased property name in DTO, undefined value always be assigned, even if you've sent upper-cased property in the client side.","special-tags#Special Tags":"You can enhance validation logic, of @TypedHeaders(), through comment tags.You know what? @TypedHeaders() utilizes typia.assert() function for query data validation, and the typia.assert() function supports additional type checking logics through comment tags. For reference, \"Type Tag\" means a intersection type with atomic type and special tag type of typia like number & tags.Type<\"uint32\">, and \"Comment Tag\" means a comment starting from @ symbol following @${name} ${value} format.With those type and comment tags, you can add additional validation logics. If you want to add a custom validation logic, you also can do it. Read below Guide Docments of typia, and see the example code. You may understand how to utilize such type and comment tags, in a few minutes.\ntypia > Validators > Custom Tags\nOutline\nType Tags\nComment Tags\nCustomization\nimport typia, { tags } from \"typia\";\nexport const checkSpecialTag = typia.createIs();\ninterface SpecialTag {\n int32: number & tags.Type<\"int32\">;\n range?: number & tags.ExclusiveMinimum<19> & tags.Maximum<100>;\n minLength: string & tags.MinLength<3>;\n pattern: string & tags.Pattern<\"^[a-z]+$\">;\n date: null | (string & tags.Format<\"date\">);\n ip: string & (tags.Format<\"ipv4\"> | tags.Format<\"ipv6\">);\n uuids: Array> &\n tags.MinItems<3> &\n tags.MaxItems<100>;\n}\n\"use strict\";\nvar __importDefault =\n (this && this.__importDefault) ||\n function (mod) {\n return mod && mod.__esModule ? mod : { default: mod };\n };\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.checkSpecialTag = void 0;\nconst typia_1 = __importDefault(require(\"typia\"));\nconst checkSpecialTag = (input) => {\n const $io0 = (input) =>\n \"number\" === typeof input.int32 &&\n Math.floor(input.int32) === input.int32 &&\n -2147483648 <= input.int32 &&\n input.int32 <= 2147483647 &&\n (undefined === input.range ||\n (\"number\" === typeof input.range &&\n 19 < input.range &&\n input.range <= 100)) &&\n \"string\" === typeof input.minLength &&\n 3 <= input.minLength.length &&\n \"string\" === typeof input.pattern &&\n /^[a-z]+$/.test(input.pattern) &&\n (null === input.date ||\n (\"string\" === typeof input.date &&\n /^(d{4})-(d{2})-(d{2})$/.test(input.date))) &&\n \"string\" === typeof input.ip &&\n (/^(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.test(\n input.ip,\n ) ||\n /^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]).){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]).){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$/.test(\n input.ip,\n )) &&\n Array.isArray(input.uuids) &&\n 3 <= input.uuids.length &&\n input.uuids.length <= 100 &&\n input.uuids.every(\n (elem) =>\n \"string\" === typeof elem &&\n /^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000)$/i.test(\n elem,\n ),\n );\n return \"object\" === typeof input && null !== input && $io0(input);\n};\nexports.checkSpecialTag = checkSpecialTag;","restriction#Restriction":"When using @TypedHeaders(), you've to follow such restrictions.At first, type of @TypedHeaders() must be a pure object type. It does not allow union type. Also, nullable types are not allowed, either. Note that, request headers type must be a sole object type without any extra definition. Of course, the word object does not contain array type.At next, type of properties must be atomic, or array of atomic type. In the atomic type case, the atomic type allows both nullable and undefindable types. However, mixed union atomic type like string | number or \"1\" | \"2\" | 3 are not allowed. Also, the array type does not allow both nullable and undefindable types, either.\nboolean\nnumber\nbigint\nstring\nAt last, HTTP headers has special restriction on value types for specific key names. For example, Set-Cookie must be Array type, and Authorization must be an atomic type like string. Therefore, @TypedHeaders() also restricts the value type of specific key names, and it is described in below.\nOnly array type allowed:\nset-cookie\nOnly atomic type allowed:\nage\nauthorization\ncontent-length\ncontent-type\netag\nexpires\nfrom\nhost\nif-modified-since\nif-unmodified-since\nlast-modified\nlocation\nmax-forwards\nproxy-authorization\nreferer\nretry-after\nserver\nuser-agent\nexport interface SomeHeadersDto {\n //----\n // ATOMIC TYPES\n //----\n // ALLOWED\n boolean: boolean;\n number: number;\n string: string;\n bigint: bigint;\n optional_number?: number;\n nullable_string: string | null;\n literal_union: \"A\" | \"B\" | \"C\" | \"D\";\n // NOT ALLOWED\n mixed_union: string | number | boolean;\n mixed_literal: \"A\" | \"B\" | 3;\n //----\n // ARRAY TYPES\n //----\n // ALLOWED\n nullable_element_array: (string | null)[];\n string_array: string[];\n number_array: number[];\n literal_union_array: (\"A\" | \"B\" | \"C\")[];\n literal_tuple: [\"A\", \"B\", \"C\"];\n // NOT ALLOWED\n optional_element_array: (string | undefined)[];\n optional_array: string[] | undefined;\n nullable_array: string[] | null;\n union_atomic_array: (string | number)[];\n mixed_literal_array: (\"A\", \"B\", 3)[];\n mixed_tuple: [\"A\", \"B\", 3];\n //----\n // SPECIAL CASES\n //----\n // MUST BE ARRAY\n \"Set-Cookie\": string[];\n // MUST BE ATOMIC\n Accept: string;\n Authorization: string;\n // NOT ALLOWED - MUST BE ATOMIC\n referer: string[];\n age: number[];\n}"}},"/docs/core/TypedQuery":{"title":"Typedquery","data":{"outline#Outline":"export function TypedQuery(): ParameterDecorator;\nexport namespace TypedQuery {\n export function Body(): ParameterDecorator;\n export function Get(path?: string): MethodDecorator;\n export function Post(path?: string): MethodDecorator;\n export function Put(path?: string): MethodDecorator;\n export function Patch(path?: string): MethodDecorator;\n export function Delete(path?: string): MethodDecorator;\n}\nDecorators for query parameters.\nWhat the query parameters are?This is the query parameters!\nname=Samchon&age=20&sex=male\n@TypedQuery() is not essential for Swagger Documents or SDK Library building.Therefore, it is not a matter to use @TypedQuery() or @Query() of the original NestJS.","typedquery#@TypedQuery()":"export function TypedQuery(): ParameterDecorator;\nType safe URL query decorator.@TypedQuery() is a decorator parsing URL query.It's almost same with original @Query() function of NestJS, but @TypedQuery() is more stable and general.While NestJS does not support query type validation, @TypedQuery() validates the request query type and throws 400 bad request error when mismatched. Also, while NestJS does not support property type (@Query() only supports string typed properties), @TypedQuery() can define variable property types like bigint, number or boolean.\nimport { tags } from \"typia\";\nexport namespace IBbsArticle {\n export interface ISummary {\n id: string & tags.Format<\"uuid\">;\n writer: string;\n title: string & tags.MinLength<3> & tags.MaxLength<50>;\n created_at: string & tags.Format<\"date-time\">;\n }\n}\nimport { tags } from \"typia\";\nexport interface IPage {\n data: T[];\n pagination: IPage.IPagination;\n}\nexport namespace IPage {\n /**\n * Page request data\n */\n export interface IRequest {\n page?: number & tags.Type<\"uint32\">;\n limit?: number & tags.Type<\"uint32\">;\n }\n /**\n * Page information.\n */\n export interface IPagination {\n current: number & tags.Type<\"uint32\">;\n limit: number & tags.Type<\"uint32\">;\n records: number & tags.Type<\"uint32\">;\n pages: number & tags.Type<\"uint32\">;\n }\n}\nimport { TypedQuery, TypedRoute } from \"@nestia/core\";\nimport { Controller } from \"@nestjs/common\";\nimport { tags } from \"typia\";\nimport { IBbsArticle } from \"./IBbsArticle\";\nimport { IPage } from \"./IPage\";\n@Controller(\"bbs/articles\")\nexport class BbsArticlesController {\n @TypedRoute.Get()\n public async index(\n @TypedQuery() query: IPage.IRequest\n ): Promise> {\n return {\n pagination: {\n current: query.page ?? 1,\n limit: query.limit ?? 100,\n records: 0,\n pages: 0,\n },\n data: [],\n };\n }\n}\n\"use strict\";\nvar __decorate =\n (this && this.__decorate) ||\n function (decorators, target, key, desc) {\n var c = arguments.length,\n r =\n c < 3\n ? target\n : desc === null\n ? (desc = Object.getOwnPropertyDescriptor(target, key))\n : desc,\n d;\n if (typeof Reflect === \"object\" && typeof Reflect.decorate === \"function\")\n r = Reflect.decorate(decorators, target, key, desc);\n else\n for (var i = decorators.length - 1; i >= 0; i--)\n if ((d = decorators[i]))\n r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;\n return c > 3 && r && Object.defineProperty(target, key, r), r;\n };\nvar __metadata =\n (this && this.__metadata) ||\n function (k, v) {\n if (typeof Reflect === \"object\" && typeof Reflect.metadata === \"function\")\n return Reflect.metadata(k, v);\n };\nvar __param =\n (this && this.__param) ||\n function (paramIndex, decorator) {\n return function (target, key) {\n decorator(target, key, paramIndex);\n };\n };\nvar __awaiter =\n (this && this.__awaiter) ||\n function (thisArg, _arguments, P, generator) {\n function adopt(value) {\n return value instanceof P\n ? value\n : new P(function (resolve) {\n resolve(value);\n });\n }\n return new (P || (P = Promise))(function (resolve, reject) {\n function fulfilled(value) {\n try {\n step(generator.next(value));\n } catch (e) {\n reject(e);\n }\n }\n function rejected(value) {\n try {\n step(generator[\"throw\"](value));\n } catch (e) {\n reject(e);\n }\n }\n function step(result) {\n result.done\n ? resolve(result.value)\n : adopt(result.value).then(fulfilled, rejected);\n }\n step((generator = generator.apply(thisArg, _arguments || [])).next());\n });\n };\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.BbsArticlesController = void 0;\nconst core_1 = require(\"@nestia/core\");\nconst common_1 = require(\"@nestjs/common\");\nlet BbsArticlesController = class BbsArticlesController {\n index(query) {\n var _a, _b;\n return __awaiter(this, void 0, void 0, function* () {\n return {\n pagination: {\n current: (_a = query.page) !== null && _a !== void 0 ? _a : 1,\n limit: (_b = query.limit) !== null && _b !== void 0 ? _b : 100,\n records: 0,\n pages: 0,\n },\n data: [],\n };\n });\n }\n};\nexports.BbsArticlesController = BbsArticlesController;\n__decorate(\n [\n core_1.TypedRoute.Get({\n type: \"assert\",\n assert: (input) => {\n const assert = (input) => {\n const __is = (input) => {\n const $io0 = (input) =>\n Array.isArray(input.data) &&\n input.data.every(\n (elem) =>\n \"object\" === typeof elem && null !== elem && $io1(elem),\n ) &&\n \"object\" === typeof input.pagination &&\n null !== input.pagination &&\n \"number\" === typeof input.pagination.current &&\n Math.floor(input.pagination.current) ===\n input.pagination.current &&\n 0 <= input.pagination.current &&\n input.pagination.current <= 4294967295 &&\n \"number\" === typeof input.pagination.limit &&\n Math.floor(input.pagination.limit) === input.pagination.limit &&\n 0 <= input.pagination.limit &&\n input.pagination.limit <= 4294967295 &&\n \"number\" === typeof input.pagination.records &&\n Math.floor(input.pagination.records) ===\n input.pagination.records &&\n 0 <= input.pagination.records &&\n input.pagination.records <= 4294967295 &&\n \"number\" === typeof input.pagination.pages &&\n Math.floor(input.pagination.pages) === input.pagination.pages &&\n 0 <= input.pagination.pages &&\n input.pagination.pages <= 4294967295;\n const $io1 = (input) =>\n \"string\" === typeof input.id &&\n /^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000)$/i.test(\n input.id,\n ) &&\n \"string\" === typeof input.writer &&\n \"string\" === typeof input.title &&\n 3 <= input.title.length &&\n input.title.length <= 50 &&\n \"string\" === typeof input.created_at &&\n !isNaN(new Date(input.created_at).getTime());\n return \"object\" === typeof input && null !== input && $io0(input);\n };\n if (false === __is(input))\n ((input, _path, _exceptionable = true) => {\n const $guard = core_1.TypedRoute.Get.guard;\n const $ao0 = (input, _path, _exceptionable = true) =>\n (((Array.isArray(input.data) ||\n $guard(_exceptionable, {\n path: _path + \".data\",\n expected: \"Array\",\n value: input.data,\n })) &&\n input.data.every(\n (elem, _index1) =>\n (((\"object\" === typeof elem && null !== elem) ||\n $guard(_exceptionable, {\n path: _path + \".data[\" + _index1 + \"]\",\n expected: \"IBbsArticle.ISummary\",\n value: elem,\n })) &&\n $ao1(\n elem,\n _path + \".data[\" + _index1 + \"]\",\n true && _exceptionable,\n )) ||\n $guard(_exceptionable, {\n path: _path + \".data[\" + _index1 + \"]\",\n expected: \"IBbsArticle.ISummary\",\n value: elem,\n }),\n )) ||\n $guard(_exceptionable, {\n path: _path + \".data\",\n expected: \"Array\",\n value: input.data,\n })) &&\n ((((\"object\" === typeof input.pagination &&\n null !== input.pagination) ||\n $guard(_exceptionable, {\n path: _path + \".pagination\",\n expected: \"IPage.IPagination\",\n value: input.pagination,\n })) &&\n $ao2(\n input.pagination,\n _path + \".pagination\",\n true && _exceptionable,\n )) ||\n $guard(_exceptionable, {\n path: _path + \".pagination\",\n expected: \"IPage.IPagination\",\n value: input.pagination,\n }));\n const $ao1 = (input, _path, _exceptionable = true) =>\n ((\"string\" === typeof input.id &&\n (/^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000)$/i.test(\n input.id,\n ) ||\n $guard(_exceptionable, {\n path: _path + \".id\",\n expected: 'string & Format<\"uuid\">',\n value: input.id,\n }))) ||\n $guard(_exceptionable, {\n path: _path + \".id\",\n expected: '(string & Format<\"uuid\">)',\n value: input.id,\n })) &&\n (\"string\" === typeof input.writer ||\n $guard(_exceptionable, {\n path: _path + \".writer\",\n expected: \"string\",\n value: input.writer,\n })) &&\n ((\"string\" === typeof input.title &&\n (3 <= input.title.length ||\n $guard(_exceptionable, {\n path: _path + \".title\",\n expected: \"string & MinLength<3>\",\n value: input.title,\n })) &&\n (input.title.length <= 50 ||\n $guard(_exceptionable, {\n path: _path + \".title\",\n expected: \"string & MaxLength<50>\",\n value: input.title,\n }))) ||\n $guard(_exceptionable, {\n path: _path + \".title\",\n expected: \"(string & MinLength<3> & MaxLength<50>)\",\n value: input.title,\n })) &&\n ((\"string\" === typeof input.created_at &&\n (!isNaN(new Date(input.created_at).getTime()) ||\n $guard(_exceptionable, {\n path: _path + \".created_at\",\n expected: 'string & Format<\"date-time\">',\n value: input.created_at,\n }))) ||\n $guard(_exceptionable, {\n path: _path + \".created_at\",\n expected: '(string & Format<\"date-time\">)',\n value: input.created_at,\n }));\n const $ao2 = (input, _path, _exceptionable = true) =>\n ((\"number\" === typeof input.current &&\n ((Math.floor(input.current) === input.current &&\n 0 <= input.current &&\n input.current <= 4294967295) ||\n $guard(_exceptionable, {\n path: _path + \".current\",\n expected: 'number & Type<\"uint32\">',\n value: input.current,\n }))) ||\n $guard(_exceptionable, {\n path: _path + \".current\",\n expected: '(number & Type<\"uint32\">)',\n value: input.current,\n })) &&\n ((\"number\" === typeof input.limit &&\n ((Math.floor(input.limit) === input.limit &&\n 0 <= input.limit &&\n input.limit <= 4294967295) ||\n $guard(_exceptionable, {\n path: _path + \".limit\",\n expected: 'number & Type<\"uint32\">',\n value: input.limit,\n }))) ||\n $guard(_exceptionable, {\n path: _path + \".limit\",\n expected: '(number & Type<\"uint32\">)',\n value: input.limit,\n })) &&\n ((\"number\" === typeof input.records &&\n ((Math.floor(input.records) === input.records &&\n 0 <= input.records &&\n input.records <= 4294967295) ||\n $guard(_exceptionable, {\n path: _path + \".records\",\n expected: 'number & Type<\"uint32\">',\n value: input.records,\n }))) ||\n $guard(_exceptionable, {\n path: _path + \".records\",\n expected: '(number & Type<\"uint32\">)',\n value: input.records,\n })) &&\n ((\"number\" === typeof input.pages &&\n ((Math.floor(input.pages) === input.pages &&\n 0 <= input.pages &&\n input.pages <= 4294967295) ||\n $guard(_exceptionable, {\n path: _path + \".pages\",\n expected: 'number & Type<\"uint32\">',\n value: input.pages,\n }))) ||\n $guard(_exceptionable, {\n path: _path + \".pages\",\n expected: '(number & Type<\"uint32\">)',\n value: input.pages,\n }));\n return (\n (((\"object\" === typeof input && null !== input) ||\n $guard(true, {\n path: _path + \"\",\n expected: \"IPage\",\n value: input,\n })) &&\n $ao0(input, _path + \"\", true)) ||\n $guard(true, {\n path: _path + \"\",\n expected: \"IPage\",\n value: input,\n })\n );\n })(input, \"$input\", true);\n return input;\n };\n const stringify = (input) => {\n const $io1 = (input) =>\n \"string\" === typeof input.id &&\n /^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000)$/i.test(\n input.id,\n ) &&\n \"string\" === typeof input.writer &&\n \"string\" === typeof input.title &&\n 3 <= input.title.length &&\n input.title.length <= 50 &&\n \"string\" === typeof input.created_at &&\n !isNaN(new Date(input.created_at).getTime());\n const $io2 = (input) =>\n \"number\" === typeof input.current &&\n Math.floor(input.current) === input.current &&\n 0 <= input.current &&\n input.current <= 4294967295 &&\n \"number\" === typeof input.limit &&\n Math.floor(input.limit) === input.limit &&\n 0 <= input.limit &&\n input.limit <= 4294967295 &&\n \"number\" === typeof input.records &&\n Math.floor(input.records) === input.records &&\n 0 <= input.records &&\n input.records <= 4294967295 &&\n \"number\" === typeof input.pages &&\n Math.floor(input.pages) === input.pages &&\n 0 <= input.pages &&\n input.pages <= 4294967295;\n const $string = core_1.TypedRoute.Get.string;\n const $so0 = (input) =>\n `{\"data\":${`[${input.data\n .map(\n (elem) =>\n `{\"id\":${$string(elem.id)},\"writer\":${$string(\n elem.writer,\n )},\"title\":${$string(elem.title)},\"created_at\":${$string(\n elem.created_at,\n )}}`,\n )\n .join(\n \",\",\n )}]`},\"pagination\":${`{\"current\":${input.pagination.current},\"limit\":${input.pagination.limit},\"records\":${input.pagination.records},\"pages\":${input.pagination.pages}}`}}`;\n return $so0(input);\n };\n return stringify(assert(input));\n },\n }),\n __param(\n 0,\n (0, core_1.TypedQuery)((input) => {\n var _a, _b;\n const $number = core_1.TypedQuery.number;\n const output = {\n page:\n (_a = $number(input.get(\"page\"))) !== null && _a !== void 0\n ? _a\n : undefined,\n limit:\n (_b = $number(input.get(\"limit\"))) !== null && _b !== void 0\n ? _b\n : undefined,\n };\n return ((input) => {\n const __is = (input) => {\n const $io0 = (input) =>\n (undefined === input.page ||\n (\"number\" === typeof input.page &&\n Math.floor(input.page) === input.page &&\n 0 <= input.page &&\n input.page <= 4294967295)) &&\n (undefined === input.limit ||\n (\"number\" === typeof input.limit &&\n Math.floor(input.limit) === input.limit &&\n 0 <= input.limit &&\n input.limit <= 4294967295));\n return (\n \"object\" === typeof input &&\n null !== input &&\n false === Array.isArray(input) &&\n $io0(input)\n );\n };\n if (false === __is(input))\n ((input, _path, _exceptionable = true) => {\n const $guard = core_1.TypedQuery.guard;\n const $ao0 = (input, _path, _exceptionable = true) =>\n (undefined === input.page ||\n (\"number\" === typeof input.page &&\n ((Math.floor(input.page) === input.page &&\n 0 <= input.page &&\n input.page <= 4294967295) ||\n $guard(_exceptionable, {\n path: _path + \".page\",\n expected: 'number & Type<\"uint32\">',\n value: input.page,\n }))) ||\n $guard(_exceptionable, {\n path: _path + \".page\",\n expected: '((number & Type<\"uint32\">) | undefined)',\n value: input.page,\n })) &&\n (undefined === input.limit ||\n (\"number\" === typeof input.limit &&\n ((Math.floor(input.limit) === input.limit &&\n 0 <= input.limit &&\n input.limit <= 4294967295) ||\n $guard(_exceptionable, {\n path: _path + \".limit\",\n expected: 'number & Type<\"uint32\">',\n value: input.limit,\n }))) ||\n $guard(_exceptionable, {\n path: _path + \".limit\",\n expected: '((number & Type<\"uint32\">) | undefined)',\n value: input.limit,\n }));\n return (\n (((\"object\" === typeof input &&\n null !== input &&\n false === Array.isArray(input)) ||\n $guard(true, {\n path: _path + \"\",\n expected: \"IPage.IRequest\",\n value: input,\n })) &&\n $ao0(input, _path + \"\", true)) ||\n $guard(true, {\n path: _path + \"\",\n expected: \"IPage.IRequest\",\n value: input,\n })\n );\n })(input, \"$input\", true);\n return input;\n })(output);\n }),\n ),\n __metadata(\"design:type\", Function),\n __metadata(\"design:paramtypes\", [Object]),\n __metadata(\"design:returntype\", Promise),\n ],\n BbsArticlesController.prototype,\n \"index\",\n null,\n);\nexports.BbsArticlesController = BbsArticlesController = __decorate(\n [(0, common_1.Controller)(\"bbs/articles\")],\n BbsArticlesController,\n);\nJust call @TypedQuery() function on the query parameter, that's all.Nestia will analyze your type (IPage.IRequest), and writes optimal code for the target type, in the compilation level. If you click the \"Compiled JavaScript\" file tab of above and fine enhanced lines by blue lines, you can see the optimal parsing and validation code.Such optimization is called AOT (Ahead of Time) compilation, and it is the secret of @TypedQuery.","typedquerybody#TypedQuery.Body()":"export namespace TypedQuery {\n export function Body(): ParameterDecorator;\n}\nRequest body decorator of application/x-www-form-urlencoded format.If you call @TypedQuery.Body() decorator function on a specific parameter, the parameter will be parsed from the request body as application/x-www-form-urlencoded format. Otherwise, you want to declare a application/json format response body, use @TypedBody() decorator function instead.\nimport { tags } from \"typia\";\nexport interface IBbsArticle {\n id: string;\n writer: string;\n title: string & tags.MinLength<3> & tags.MaxLength<50>;\n body: string;\n created_at: string & tags.Format<\"date-time\">;\n}\nexport namespace IBbsArticle {\n export interface IStore {\n title: string & tags.MinLength<3> & tags.MaxLength<50>;\n body: string;\n }\n}\nimport { TypedQuery } from \"@nestia/core\";\nimport { Controller } from \"@nestjs/common\";\nimport { IBbsArticle } from \"@api/lib/structures/IBbsArticle\";\n@Controller(\"bbs/articles\")\nexport class BbsArticlesController {\n @TypedQuery.Post()\n public async store(\n @TypedQuery.Body() body: IBbsArticle.IStore,\n ): Promise {\n return {\n id: \"00000000-0000-0000-0000-000000000000\",\n writer: \"Samchon\",\n title: body.title,\n body: body.body,\n created_at: new Date().toISOString(),\n };\n }\n}\n\"use strict\";\nvar __decorate =\n (this && this.__decorate) ||\n function (decorators, target, key, desc) {\n var c = arguments.length,\n r =\n c < 3\n ? target\n : desc === null\n ? (desc = Object.getOwnPropertyDescriptor(target, key))\n : desc,\n d;\n if (typeof Reflect === \"object\" && typeof Reflect.decorate === \"function\")\n r = Reflect.decorate(decorators, target, key, desc);\n else\n for (var i = decorators.length - 1; i >= 0; i--)\n if ((d = decorators[i]))\n r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;\n return c > 3 && r && Object.defineProperty(target, key, r), r;\n };\nvar __metadata =\n (this && this.__metadata) ||\n function (k, v) {\n if (typeof Reflect === \"object\" && typeof Reflect.metadata === \"function\")\n return Reflect.metadata(k, v);\n };\nvar __param =\n (this && this.__param) ||\n function (paramIndex, decorator) {\n return function (target, key) {\n decorator(target, key, paramIndex);\n };\n };\nvar __awaiter =\n (this && this.__awaiter) ||\n function (thisArg, _arguments, P, generator) {\n function adopt(value) {\n return value instanceof P\n ? value\n : new P(function (resolve) {\n resolve(value);\n });\n }\n return new (P || (P = Promise))(function (resolve, reject) {\n function fulfilled(value) {\n try {\n step(generator.next(value));\n } catch (e) {\n reject(e);\n }\n }\n function rejected(value) {\n try {\n step(generator[\"throw\"](value));\n } catch (e) {\n reject(e);\n }\n }\n function step(result) {\n result.done\n ? resolve(result.value)\n : adopt(result.value).then(fulfilled, rejected);\n }\n step((generator = generator.apply(thisArg, _arguments || [])).next());\n });\n };\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.BbsArticlesController = void 0;\nconst core_1 = require(\"@nestia/core\");\nconst common_1 = require(\"@nestjs/common\");\nlet BbsArticlesController = class BbsArticlesController {\n store(body) {\n return __awaiter(this, void 0, void 0, function* () {\n return {\n id: \"00000000-0000-0000-0000-000000000000\",\n writer: \"Samchon\",\n title: body.title,\n body: body.body,\n created_at: new Date().toISOString(),\n };\n });\n }\n};\nexports.BbsArticlesController = BbsArticlesController;\n__decorate(\n [\n core_1.TypedQuery.Post({\n type: \"assert\",\n assert: (input) => {\n const assert = (input) => {\n const __is = (input) => {\n return (\n \"object\" === typeof input &&\n null !== input &&\n \"string\" === typeof input.id &&\n \"string\" === typeof input.writer &&\n \"string\" === typeof input.title &&\n 3 <= input.title.length &&\n input.title.length <= 50 &&\n \"string\" === typeof input.body &&\n \"string\" === typeof input.created_at &&\n !isNaN(new Date(input.created_at).getTime())\n );\n };\n if (false === __is(input))\n ((input, _path, _exceptionable = true) => {\n const $guard = core_1.TypedQuery.Post.guard;\n const $ao0 = (input, _path, _exceptionable = true) =>\n (\"string\" === typeof input.id ||\n $guard(_exceptionable, {\n path: _path + \".id\",\n expected: \"string\",\n value: input.id,\n })) &&\n (\"string\" === typeof input.writer ||\n $guard(_exceptionable, {\n path: _path + \".writer\",\n expected: \"string\",\n value: input.writer,\n })) &&\n ((\"string\" === typeof input.title &&\n (3 <= input.title.length ||\n $guard(_exceptionable, {\n path: _path + \".title\",\n expected: \"string & MinLength<3>\",\n value: input.title,\n })) &&\n (input.title.length <= 50 ||\n $guard(_exceptionable, {\n path: _path + \".title\",\n expected: \"string & MaxLength<50>\",\n value: input.title,\n }))) ||\n $guard(_exceptionable, {\n path: _path + \".title\",\n expected: \"(string & MinLength<3> & MaxLength<50>)\",\n value: input.title,\n })) &&\n (\"string\" === typeof input.body ||\n $guard(_exceptionable, {\n path: _path + \".body\",\n expected: \"string\",\n value: input.body,\n })) &&\n ((\"string\" === typeof input.created_at &&\n (!isNaN(new Date(input.created_at).getTime()) ||\n $guard(_exceptionable, {\n path: _path + \".created_at\",\n expected: 'string & Format<\"date-time\">',\n value: input.created_at,\n }))) ||\n $guard(_exceptionable, {\n path: _path + \".created_at\",\n expected: '(string & Format<\"date-time\">)',\n value: input.created_at,\n }));\n return (\n (((\"object\" === typeof input && null !== input) ||\n $guard(true, {\n path: _path + \"\",\n expected: \"IBbsArticle\",\n value: input,\n })) &&\n $ao0(input, _path + \"\", true)) ||\n $guard(true, {\n path: _path + \"\",\n expected: \"IBbsArticle\",\n value: input,\n })\n );\n })(input, \"$input\", true);\n return input;\n };\n const stringify = (input) => {\n const output = new URLSearchParams();\n output.append(\"id\", input.id);\n output.append(\"writer\", input.writer);\n output.append(\"title\", input.title);\n output.append(\"body\", input.body);\n output.append(\"created_at\", input.created_at);\n return output;\n };\n return stringify(assert(input));\n },\n }),\n __param(\n 0,\n core_1.TypedQuery.Body({\n type: \"assert\",\n assert: (input) => {\n const decode = (input) => {\n const $params = core_1.TypedQuery.Body.params;\n const $string = core_1.TypedQuery.Body.string;\n input = $params(input);\n const output = {\n title: $string(input.get(\"title\")),\n body: $string(input.get(\"body\")),\n };\n return output;\n };\n const assert = (input) => {\n const __is = (input) => {\n return (\n \"object\" === typeof input &&\n null !== input &&\n \"string\" === typeof input.title &&\n 3 <= input.title.length &&\n input.title.length <= 50 &&\n \"string\" === typeof input.body\n );\n };\n if (false === __is(input))\n ((input, _path, _exceptionable = true) => {\n const $guard = core_1.TypedQuery.Body.guard;\n const $ao0 = (input, _path, _exceptionable = true) =>\n ((\"string\" === typeof input.title &&\n (3 <= input.title.length ||\n $guard(_exceptionable, {\n path: _path + \".title\",\n expected: \"string & MinLength<3>\",\n value: input.title,\n })) &&\n (input.title.length <= 50 ||\n $guard(_exceptionable, {\n path: _path + \".title\",\n expected: \"string & MaxLength<50>\",\n value: input.title,\n }))) ||\n $guard(_exceptionable, {\n path: _path + \".title\",\n expected: \"(string & MinLength<3> & MaxLength<50>)\",\n value: input.title,\n })) &&\n (\"string\" === typeof input.body ||\n $guard(_exceptionable, {\n path: _path + \".body\",\n expected: \"string\",\n value: input.body,\n }));\n return (\n (((\"object\" === typeof input && null !== input) ||\n $guard(true, {\n path: _path + \"\",\n expected: \"IBbsArticle.IStore\",\n value: input,\n })) &&\n $ao0(input, _path + \"\", true)) ||\n $guard(true, {\n path: _path + \"\",\n expected: \"IBbsArticle.IStore\",\n value: input,\n })\n );\n })(input, \"$input\", true);\n return input;\n };\n const output = decode(input);\n return assert(output);\n },\n }),\n ),\n __metadata(\"design:type\", Function),\n __metadata(\"design:paramtypes\", [Object]),\n __metadata(\"design:returntype\", Promise),\n ],\n BbsArticlesController.prototype,\n \"store\",\n null,\n);\nexports.BbsArticlesController = BbsArticlesController = __decorate(\n [(0, common_1.Controller)(\"bbs/articles\")],\n BbsArticlesController,\n);","typedquerypost#TypedQuery.Post()":"export namespace TypedQuery {\n export function Get(path?: string): MethodDecorator;\n export function Post(path?: string): MethodDecorator;\n export function Put(path?: string): MethodDecorator;\n export function Patch(path?: string): MethodDecorator;\n export function Delete(path?: string): MethodDecorator;\n}\nRoute decorators of application/x-www-form-urlencoded format response body.If you call one of below decorator functions on a method, the method will return application/x-www-form-urlencoded format response body. Otherwise, you want to declare a application/json format response body, use @TypedRoute.Post() decorator function instead.\n@TypedQuery.Get()\n@TypedQuery.Post()\n@TypedQuery.Put()\n@TypedQuery.Patch()\n@TypedQuery.Delete()\nimport { tags } from \"typia\";\nexport interface IBbsArticle {\n id: string;\n writer: string;\n title: string & tags.MinLength<3> & tags.MaxLength<50>;\n body: string;\n created_at: string & tags.Format<\"date-time\">;\n}\nexport namespace IBbsArticle {\n export interface IStore {\n title: string & tags.MinLength<3> & tags.MaxLength<50>;\n body: string;\n }\n}\nimport { TypedQuery } from \"@nestia/core\";\nimport { Controller } from \"@nestjs/common\";\nimport { IBbsArticle } from \"@api/lib/structures/IBbsArticle\";\n@Controller(\"bbs/articles\")\nexport class BbsArticlesController {\n @TypedQuery.Post()\n public async store(\n @TypedQuery.Body() body: IBbsArticle.IStore,\n ): Promise {\n return {\n id: \"00000000-0000-0000-0000-000000000000\",\n writer: \"Samchon\",\n title: body.title,\n body: body.body,\n created_at: new Date().toISOString(),\n };\n }\n}\n\"use strict\";\nvar __decorate =\n (this && this.__decorate) ||\n function (decorators, target, key, desc) {\n var c = arguments.length,\n r =\n c < 3\n ? target\n : desc === null\n ? (desc = Object.getOwnPropertyDescriptor(target, key))\n : desc,\n d;\n if (typeof Reflect === \"object\" && typeof Reflect.decorate === \"function\")\n r = Reflect.decorate(decorators, target, key, desc);\n else\n for (var i = decorators.length - 1; i >= 0; i--)\n if ((d = decorators[i]))\n r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;\n return c > 3 && r && Object.defineProperty(target, key, r), r;\n };\nvar __metadata =\n (this && this.__metadata) ||\n function (k, v) {\n if (typeof Reflect === \"object\" && typeof Reflect.metadata === \"function\")\n return Reflect.metadata(k, v);\n };\nvar __param =\n (this && this.__param) ||\n function (paramIndex, decorator) {\n return function (target, key) {\n decorator(target, key, paramIndex);\n };\n };\nvar __awaiter =\n (this && this.__awaiter) ||\n function (thisArg, _arguments, P, generator) {\n function adopt(value) {\n return value instanceof P\n ? value\n : new P(function (resolve) {\n resolve(value);\n });\n }\n return new (P || (P = Promise))(function (resolve, reject) {\n function fulfilled(value) {\n try {\n step(generator.next(value));\n } catch (e) {\n reject(e);\n }\n }\n function rejected(value) {\n try {\n step(generator[\"throw\"](value));\n } catch (e) {\n reject(e);\n }\n }\n function step(result) {\n result.done\n ? resolve(result.value)\n : adopt(result.value).then(fulfilled, rejected);\n }\n step((generator = generator.apply(thisArg, _arguments || [])).next());\n });\n };\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.BbsArticlesController = void 0;\nconst core_1 = require(\"@nestia/core\");\nconst common_1 = require(\"@nestjs/common\");\nlet BbsArticlesController = class BbsArticlesController {\n store(body) {\n return __awaiter(this, void 0, void 0, function* () {\n return {\n id: \"00000000-0000-0000-0000-000000000000\",\n writer: \"Samchon\",\n title: body.title,\n body: body.body,\n created_at: new Date().toISOString(),\n };\n });\n }\n};\nexports.BbsArticlesController = BbsArticlesController;\n__decorate(\n [\n core_1.TypedQuery.Post({\n type: \"assert\",\n assert: (input) => {\n const assert = (input) => {\n const __is = (input) => {\n return (\n \"object\" === typeof input &&\n null !== input &&\n \"string\" === typeof input.id &&\n \"string\" === typeof input.writer &&\n \"string\" === typeof input.title &&\n 3 <= input.title.length &&\n input.title.length <= 50 &&\n \"string\" === typeof input.body &&\n \"string\" === typeof input.created_at &&\n !isNaN(new Date(input.created_at).getTime())\n );\n };\n if (false === __is(input))\n ((input, _path, _exceptionable = true) => {\n const $guard = core_1.TypedQuery.Post.guard;\n const $ao0 = (input, _path, _exceptionable = true) =>\n (\"string\" === typeof input.id ||\n $guard(_exceptionable, {\n path: _path + \".id\",\n expected: \"string\",\n value: input.id,\n })) &&\n (\"string\" === typeof input.writer ||\n $guard(_exceptionable, {\n path: _path + \".writer\",\n expected: \"string\",\n value: input.writer,\n })) &&\n ((\"string\" === typeof input.title &&\n (3 <= input.title.length ||\n $guard(_exceptionable, {\n path: _path + \".title\",\n expected: \"string & MinLength<3>\",\n value: input.title,\n })) &&\n (input.title.length <= 50 ||\n $guard(_exceptionable, {\n path: _path + \".title\",\n expected: \"string & MaxLength<50>\",\n value: input.title,\n }))) ||\n $guard(_exceptionable, {\n path: _path + \".title\",\n expected: \"(string & MinLength<3> & MaxLength<50>)\",\n value: input.title,\n })) &&\n (\"string\" === typeof input.body ||\n $guard(_exceptionable, {\n path: _path + \".body\",\n expected: \"string\",\n value: input.body,\n })) &&\n ((\"string\" === typeof input.created_at &&\n (!isNaN(new Date(input.created_at).getTime()) ||\n $guard(_exceptionable, {\n path: _path + \".created_at\",\n expected: 'string & Format<\"date-time\">',\n value: input.created_at,\n }))) ||\n $guard(_exceptionable, {\n path: _path + \".created_at\",\n expected: '(string & Format<\"date-time\">)',\n value: input.created_at,\n }));\n return (\n (((\"object\" === typeof input && null !== input) ||\n $guard(true, {\n path: _path + \"\",\n expected: \"IBbsArticle\",\n value: input,\n })) &&\n $ao0(input, _path + \"\", true)) ||\n $guard(true, {\n path: _path + \"\",\n expected: \"IBbsArticle\",\n value: input,\n })\n );\n })(input, \"$input\", true);\n return input;\n };\n const stringify = (input) => {\n const output = new URLSearchParams();\n output.append(\"id\", input.id);\n output.append(\"writer\", input.writer);\n output.append(\"title\", input.title);\n output.append(\"body\", input.body);\n output.append(\"created_at\", input.created_at);\n return output;\n };\n return stringify(assert(input));\n },\n }),\n __param(\n 0,\n core_1.TypedQuery.Body({\n type: \"assert\",\n assert: (input) => {\n const decode = (input) => {\n const $params = core_1.TypedQuery.Body.params;\n const $string = core_1.TypedQuery.Body.string;\n input = $params(input);\n const output = {\n title: $string(input.get(\"title\")),\n body: $string(input.get(\"body\")),\n };\n return output;\n };\n const assert = (input) => {\n const __is = (input) => {\n return (\n \"object\" === typeof input &&\n null !== input &&\n \"string\" === typeof input.title &&\n 3 <= input.title.length &&\n input.title.length <= 50 &&\n \"string\" === typeof input.body\n );\n };\n if (false === __is(input))\n ((input, _path, _exceptionable = true) => {\n const $guard = core_1.TypedQuery.Body.guard;\n const $ao0 = (input, _path, _exceptionable = true) =>\n ((\"string\" === typeof input.title &&\n (3 <= input.title.length ||\n $guard(_exceptionable, {\n path: _path + \".title\",\n expected: \"string & MinLength<3>\",\n value: input.title,\n })) &&\n (input.title.length <= 50 ||\n $guard(_exceptionable, {\n path: _path + \".title\",\n expected: \"string & MaxLength<50>\",\n value: input.title,\n }))) ||\n $guard(_exceptionable, {\n path: _path + \".title\",\n expected: \"(string & MinLength<3> & MaxLength<50>)\",\n value: input.title,\n })) &&\n (\"string\" === typeof input.body ||\n $guard(_exceptionable, {\n path: _path + \".body\",\n expected: \"string\",\n value: input.body,\n }));\n return (\n (((\"object\" === typeof input && null !== input) ||\n $guard(true, {\n path: _path + \"\",\n expected: \"IBbsArticle.IStore\",\n value: input,\n })) &&\n $ao0(input, _path + \"\", true)) ||\n $guard(true, {\n path: _path + \"\",\n expected: \"IBbsArticle.IStore\",\n value: input,\n })\n );\n })(input, \"$input\", true);\n return input;\n };\n const output = decode(input);\n return assert(output);\n },\n }),\n ),\n __metadata(\"design:type\", Function),\n __metadata(\"design:paramtypes\", [Object]),\n __metadata(\"design:returntype\", Promise),\n ],\n BbsArticlesController.prototype,\n \"store\",\n null,\n);\nexports.BbsArticlesController = BbsArticlesController = __decorate(\n [(0, common_1.Controller)(\"bbs/articles\")],\n BbsArticlesController,\n);","special-tags#Special Tags":"You can enhance validation logic, of @TypedQuery(), through comment tags.You know what? @TypedQuery() utilizes typia.assert() function for query data validation, and the typia.assert() function supports additional type checking logics through comment tags. For reference, \"Type Tag\" means a intersection type with atomic type and special tag type of typia like number & tags.Type<\"uint32\">, and \"Comment Tag\" means a comment starting from @ symbol following @${name} ${value} format.With those type and comment tags, you can add additional validation logics. If you want to add a custom validation logic, you also can do it. Read below Guide Docments of typia, and see the example code. You may understand how to utilize such type and comment tags, in a few minutes.\ntypia > Validators > Custom Tags\nOutline\nType Tags\nComment Tags\nCustomization\nimport typia, { tags } from \"typia\";\nexport const checkSpecialTag = typia.createIs();\ninterface SpecialTag {\n int32: number & tags.Type<\"int32\">;\n range?: number & tags.ExclusiveMinimum<19> & tags.Maximum<100>;\n minLength: string & tags.MinLength<3>;\n pattern: string & tags.Pattern<\"^[a-z]+$\">;\n date: null | (string & tags.Format<\"date\">);\n ip: string & (tags.Format<\"ipv4\"> | tags.Format<\"ipv6\">);\n uuids: Array> &\n tags.MinItems<3> &\n tags.MaxItems<100>;\n}\n\"use strict\";\nvar __importDefault =\n (this && this.__importDefault) ||\n function (mod) {\n return mod && mod.__esModule ? mod : { default: mod };\n };\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.checkSpecialTag = void 0;\nconst typia_1 = __importDefault(require(\"typia\"));\nconst checkSpecialTag = (input) => {\n const $io0 = (input) =>\n \"number\" === typeof input.int32 &&\n Math.floor(input.int32) === input.int32 &&\n -2147483648 <= input.int32 &&\n input.int32 <= 2147483647 &&\n (undefined === input.range ||\n (\"number\" === typeof input.range &&\n 19 < input.range &&\n input.range <= 100)) &&\n \"string\" === typeof input.minLength &&\n 3 <= input.minLength.length &&\n \"string\" === typeof input.pattern &&\n /^[a-z]+$/.test(input.pattern) &&\n (null === input.date ||\n (\"string\" === typeof input.date &&\n /^(d{4})-(d{2})-(d{2})$/.test(input.date))) &&\n \"string\" === typeof input.ip &&\n (/^(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.test(\n input.ip,\n ) ||\n /^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]).){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]).){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$/.test(\n input.ip,\n )) &&\n Array.isArray(input.uuids) &&\n 3 <= input.uuids.length &&\n input.uuids.length <= 100 &&\n input.uuids.every(\n (elem) =>\n \"string\" === typeof elem &&\n /^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000)$/i.test(\n elem,\n ),\n );\n return \"object\" === typeof input && null !== input && $io0(input);\n};\nexports.checkSpecialTag = checkSpecialTag;","restriction#Restriction":"When using @TypedQuery(), you've to follow such restrction.At first, type of @TypedQuery() must be a pure object type. It does not allow union type. Also, nullable and undefindable types are not allowed, either. Note that, query parameter type must be a sole object type without any extra definition.At next, type of properties must be atomic, or array of atomic type. In the atomic type case, the atomic type allows both nullable and undefindable types. However, mixed union atomic type like string | number or \"1\" | \"2\" | 3 are not allowed. Also, the array type does not allow both nullable and undefindable types, either.\nboolean\nnumber\nbigint\nstring\nexport interface SomeQueryDto {\n //----\n // ATOMIC TYPES\n //----\n // ALLOWED\n boolean: boolean;\n number: number;\n string: string;\n bigint: bigint;\n optional_number?: number;\n nullable_string: string | null;\n literal_union: \"A\" | \"B\" | \"C\" | \"D\";\n // NOT ALLOWED\n mixed_union: string | number | boolean;\n mixed_literal: \"A\" | \"B\" | 3;\n //----\n // ARRAY TYPES\n //----\n // ALLOWED\n nullable_element_array: (string | null)[];\n string_array: string[];\n number_array: number[];\n literal_union_array: (\"A\" | \"B\" | \"C\")[];\n literal_tuple: [\"A\", \"B\", \"C\"];\n // NOT ALLOWED\n optional_element_array: (string | undefined)[];\n optional_array: string[] | undefined;\n nullable_array: string[] | null;\n union_atomic_array: (string | number)[];\n mixed_literal_array: (\"A\", \"B\", 3)[];\n mixed_tuple: [\"A\", \"B\", 3];\n}"}},"/docs/core/TypedRoute":{"title":"Typedroute","data":{"outline#Outline":"export namespace TypedRoute {\n export function Get(path?: string): MethodDecorator;\n export function Post(path?: string): MethodDecorator;\n export function Put(path?: string): MethodDecorator;\n export function Patch(path?: string): MethodDecorator;\n export function Delete(path?: string): MethodDecorator;\n}\nRoute decorators 200x faster, even type safe and easy to use.TypedRoute is a namespaced module containing router decorators utilizing typia.assertStringify() function. Those decorators are almost same with original NestJS, but TypedRoute can boost up JSON serialization speed maximum 200x times faster than class-transformer, therefore much faster than original NestJS.Furthermore, as TypedRoute utilizes typia.assertStringify() function, it is even type safe. The typia.assertStringify() function validates response data type, via typia.assert() function, before JSON serialization. Therefore, if you try to return wrong typed value, it would be blocked with 500 internal server error.Moreover, TypedRoute is much easier than class-transformer, because it just needs only pure TypeScript type definition. If you can't understand the word \"pure TypeScript type\", then move to below #How to use section and read the IBbsArticle interface type. You may understand what it means.\nIf you want application/x-www-form-urlencoded type, use @TypedQuery.Post() instead.\n@TypedRoute.Get() is not essential for Swagger Documents or SDK Library building.Therefore, it is not a matter to use @TypedRoute.Post() or @Post() of the original NestJS.","how-to-use#How to use":"import { tags } from \"typia\";\nexport interface IBbsArticle extends IBbsArticle.IStore {\n id: string & tags.Format<\"uuid\">;\n created_at: string & tags.Format<\"date-time\">;\n}\nexport namespace IBbsArticle {\n export interface IStore {\n title: string & tags.MinLength<3> & tags.MaxLength<50>;\n body: string;\n files: IAttachmentFile[];\n }\n}\nexport interface IAttachmentFile {\n name: null | (string & tags.MinLength<1> & tags.MaxLength<255>);\n extension: null | (string & tags.MinLength<1> & tags.MaxLength<8>);\n url: string & tags.Format<\"url\">;\n}\nimport { TypedRoute } from \"@nestia/core\";\nimport { Controller } from \"@nestjs/common\";\nimport { IBbsArticle } from \"./IBbsArticle\";\n@Controller(\"bbs/articles\")\nexport class BbsArticlesController {\n /**\n * Get random article for testing.\n */\n @TypedRoute.Get(\"random\")\n public async random(): Promise {\n return {\n id: \"2b5e21d8-0e44-4482-bd3e-4540dee7f3d6\",\n title: \"Hello nestia users\",\n body: \"Just use `TypedRoute.Get()` function like this\",\n created_at: \"2023-04-23T12:04:54.168Z\",\n files: [],\n };\n }\n}\n\"use strict\";\nvar __decorate =\n (this && this.__decorate) ||\n function (decorators, target, key, desc) {\n var c = arguments.length,\n r =\n c < 3\n ? target\n : desc === null\n ? (desc = Object.getOwnPropertyDescriptor(target, key))\n : desc,\n d;\n if (typeof Reflect === \"object\" && typeof Reflect.decorate === \"function\")\n r = Reflect.decorate(decorators, target, key, desc);\n else\n for (var i = decorators.length - 1; i >= 0; i--)\n if ((d = decorators[i]))\n r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;\n return c > 3 && r && Object.defineProperty(target, key, r), r;\n };\nvar __metadata =\n (this && this.__metadata) ||\n function (k, v) {\n if (typeof Reflect === \"object\" && typeof Reflect.metadata === \"function\")\n return Reflect.metadata(k, v);\n };\nvar __awaiter =\n (this && this.__awaiter) ||\n function (thisArg, _arguments, P, generator) {\n function adopt(value) {\n return value instanceof P\n ? value\n : new P(function (resolve) {\n resolve(value);\n });\n }\n return new (P || (P = Promise))(function (resolve, reject) {\n function fulfilled(value) {\n try {\n step(generator.next(value));\n } catch (e) {\n reject(e);\n }\n }\n function rejected(value) {\n try {\n step(generator[\"throw\"](value));\n } catch (e) {\n reject(e);\n }\n }\n function step(result) {\n result.done\n ? resolve(result.value)\n : adopt(result.value).then(fulfilled, rejected);\n }\n step((generator = generator.apply(thisArg, _arguments || [])).next());\n });\n };\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.BbsArticlesController = void 0;\nconst core_1 = require(\"@nestia/core\");\nconst common_1 = require(\"@nestjs/common\");\nlet BbsArticlesController = class BbsArticlesController {\n /**\n * Get random article for testing.\n */\n random() {\n return __awaiter(this, void 0, void 0, function* () {\n return {\n id: \"2b5e21d8-0e44-4482-bd3e-4540dee7f3d6\",\n title: \"Hello nestia users\",\n body: \"Just use `TypedRoute.Get()` function like this\",\n created_at: \"2023-04-23T12:04:54.168Z\",\n files: [],\n };\n });\n }\n};\nexports.BbsArticlesController = BbsArticlesController;\n__decorate(\n [\n core_1.TypedRoute.Get(\"random\", {\n type: \"assert\",\n assert: (input) => {\n const assert = (input) => {\n const __is = (input) => {\n const $io0 = (input) =>\n \"string\" === typeof input.id &&\n /^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000)$/i.test(\n input.id,\n ) &&\n \"string\" === typeof input.created_at &&\n !isNaN(new Date(input.created_at).getTime()) &&\n \"string\" === typeof input.title &&\n 3 <= input.title.length &&\n input.title.length <= 50 &&\n \"string\" === typeof input.body &&\n Array.isArray(input.files) &&\n input.files.every(\n (elem) =>\n \"object\" === typeof elem && null !== elem && $io1(elem),\n );\n const $io1 = (input) =>\n (null === input.name ||\n (\"string\" === typeof input.name &&\n 1 <= input.name.length &&\n input.name.length <= 255)) &&\n (null === input.extension ||\n (\"string\" === typeof input.extension &&\n 1 <= input.extension.length &&\n input.extension.length <= 8)) &&\n \"string\" === typeof input.url &&\n /^[a-zA-Z0-9]+:\\/\\/(?:www.)?[-a-zA-Z0-9@:%._+~#=]{1,256}.[a-zA-Z0-9()]{1,6}\\b(?:[-a-zA-Z0-9()@:%_+.~#?&/=]*)$/.test(\n input.url,\n );\n return \"object\" === typeof input && null !== input && $io0(input);\n };\n if (false === __is(input))\n ((input, _path, _exceptionable = true) => {\n const $guard = core_1.TypedRoute.Get.guard;\n const $ao0 = (input, _path, _exceptionable = true) =>\n ((\"string\" === typeof input.id &&\n (/^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000)$/i.test(\n input.id,\n ) ||\n $guard(_exceptionable, {\n path: _path + \".id\",\n expected: 'string & Format<\"uuid\">',\n value: input.id,\n }))) ||\n $guard(_exceptionable, {\n path: _path + \".id\",\n expected: '(string & Format<\"uuid\">)',\n value: input.id,\n })) &&\n ((\"string\" === typeof input.created_at &&\n (!isNaN(new Date(input.created_at).getTime()) ||\n $guard(_exceptionable, {\n path: _path + \".created_at\",\n expected: 'string & Format<\"date-time\">',\n value: input.created_at,\n }))) ||\n $guard(_exceptionable, {\n path: _path + \".created_at\",\n expected: '(string & Format<\"date-time\">)',\n value: input.created_at,\n })) &&\n ((\"string\" === typeof input.title &&\n (3 <= input.title.length ||\n $guard(_exceptionable, {\n path: _path + \".title\",\n expected: \"string & MinLength<3>\",\n value: input.title,\n })) &&\n (input.title.length <= 50 ||\n $guard(_exceptionable, {\n path: _path + \".title\",\n expected: \"string & MaxLength<50>\",\n value: input.title,\n }))) ||\n $guard(_exceptionable, {\n path: _path + \".title\",\n expected: \"(string & MinLength<3> & MaxLength<50>)\",\n value: input.title,\n })) &&\n (\"string\" === typeof input.body ||\n $guard(_exceptionable, {\n path: _path + \".body\",\n expected: \"string\",\n value: input.body,\n })) &&\n (((Array.isArray(input.files) ||\n $guard(_exceptionable, {\n path: _path + \".files\",\n expected: \"Array\",\n value: input.files,\n })) &&\n input.files.every(\n (elem, _index1) =>\n (((\"object\" === typeof elem && null !== elem) ||\n $guard(_exceptionable, {\n path: _path + \".files[\" + _index1 + \"]\",\n expected: \"IAttachmentFile\",\n value: elem,\n })) &&\n $ao1(\n elem,\n _path + \".files[\" + _index1 + \"]\",\n true && _exceptionable,\n )) ||\n $guard(_exceptionable, {\n path: _path + \".files[\" + _index1 + \"]\",\n expected: \"IAttachmentFile\",\n value: elem,\n }),\n )) ||\n $guard(_exceptionable, {\n path: _path + \".files\",\n expected: \"Array\",\n value: input.files,\n }));\n const $ao1 = (input, _path, _exceptionable = true) =>\n (null === input.name ||\n (\"string\" === typeof input.name &&\n (1 <= input.name.length ||\n $guard(_exceptionable, {\n path: _path + \".name\",\n expected: \"string & MinLength<1>\",\n value: input.name,\n })) &&\n (input.name.length <= 255 ||\n $guard(_exceptionable, {\n path: _path + \".name\",\n expected: \"string & MaxLength<255>\",\n value: input.name,\n }))) ||\n $guard(_exceptionable, {\n path: _path + \".name\",\n expected:\n \"((string & MinLength<1> & MaxLength<255>) | null)\",\n value: input.name,\n })) &&\n (null === input.extension ||\n (\"string\" === typeof input.extension &&\n (1 <= input.extension.length ||\n $guard(_exceptionable, {\n path: _path + \".extension\",\n expected: \"string & MinLength<1>\",\n value: input.extension,\n })) &&\n (input.extension.length <= 8 ||\n $guard(_exceptionable, {\n path: _path + \".extension\",\n expected: \"string & MaxLength<8>\",\n value: input.extension,\n }))) ||\n $guard(_exceptionable, {\n path: _path + \".extension\",\n expected: \"((string & MinLength<1> & MaxLength<8>) | null)\",\n value: input.extension,\n })) &&\n ((\"string\" === typeof input.url &&\n (/^[a-zA-Z0-9]+:\\/\\/(?:www.)?[-a-zA-Z0-9@:%._+~#=]{1,256}.[a-zA-Z0-9()]{1,6}\\b(?:[-a-zA-Z0-9()@:%_+.~#?&/=]*)$/.test(\n input.url,\n ) ||\n $guard(_exceptionable, {\n path: _path + \".url\",\n expected: 'string & Format<\"url\">',\n value: input.url,\n }))) ||\n $guard(_exceptionable, {\n path: _path + \".url\",\n expected: '(string & Format<\"url\">)',\n value: input.url,\n }));\n return (\n (((\"object\" === typeof input && null !== input) ||\n $guard(true, {\n path: _path + \"\",\n expected: \"IBbsArticle\",\n value: input,\n })) &&\n $ao0(input, _path + \"\", true)) ||\n $guard(true, {\n path: _path + \"\",\n expected: \"IBbsArticle\",\n value: input,\n })\n );\n })(input, \"$input\", true);\n return input;\n };\n const stringify = (input) => {\n const $io1 = (input) =>\n (null === input.name ||\n (\"string\" === typeof input.name &&\n 1 <= input.name.length &&\n input.name.length <= 255)) &&\n (null === input.extension ||\n (\"string\" === typeof input.extension &&\n 1 <= input.extension.length &&\n input.extension.length <= 8)) &&\n \"string\" === typeof input.url &&\n /^[a-zA-Z0-9]+:\\/\\/(?:www.)?[-a-zA-Z0-9@:%._+~#=]{1,256}.[a-zA-Z0-9()]{1,6}\\b(?:[-a-zA-Z0-9()@:%_+.~#?&/=]*)$/.test(\n input.url,\n );\n const $string = core_1.TypedRoute.Get.string;\n const $so0 = (input) =>\n `{\"id\":${$string(input.id)},\"created_at\":${$string(\n input.created_at,\n )},\"title\":${$string(input.title)},\"body\":${$string(\n input.body,\n )},\"files\":${`[${input.files\n .map((elem) => $so1(elem))\n .join(\",\")}]`}}`;\n const $so1 = (input) =>\n `{\"name\":${\n null !== input.name ? $string(input.name) : \"null\"\n },\"extension\":${\n null !== input.extension ? $string(input.extension) : \"null\"\n },\"url\":${$string(input.url)}}`;\n return $so0(input);\n };\n return stringify(assert(input));\n },\n }),\n __metadata(\"design:type\", Function),\n __metadata(\"design:paramtypes\", []),\n __metadata(\"design:returntype\", Promise),\n ],\n BbsArticlesController.prototype,\n \"random\",\n null,\n);\nexports.BbsArticlesController = BbsArticlesController = __decorate(\n [(0, common_1.Controller)(\"bbs/articles\")],\n BbsArticlesController,\n);\nJust call @TypedRoute.${method}() function on the target method, that's all.Nestia will analyze your type (IBbsArticle), and writes optimal code for the target type, in the compilation level. If you click the \"Compiled JavaScript File\" tab of above, you can see the optimal validation and JSON serialization code.Such optimization is called AOT (Ahead of Time) compilation, and it is the secret of TypedRoute.","special-tags#Special Tags":"You can enhance validation logic, of TypedRoute, through comment tags.You know what? @TypedRoute.${method}() functions are using typia.assertStringify() function, that is combined with typia.assert() and typia.stringify() functions. It is the secret of @TypedRoute.${method}() functions, which can validates response body data type before JSON serialization, and throws 500 internal server error when the data type is not matched.Also, as typia.assert() function can utililze comment tags for additional validation, TypedRoute also can utillze them, too. For reference, \"Type Tag\" means a intersection type with atomic type and special tag type of typia like number & tags.Type<\"uint32\">, and \"Comment Tag\" means a comment starting from @ symbol following @${name} ${value} format.With those type and comment tags, you can add additional validation logics. If you want to add a custom validation logic, you also can do it. Read below Guide Docments of typia, and see the example code. You may understand how to utilize such type and comment tags, in a few minutes.\ntypia > Validators > Custom Tags\nOutline\nType Tags\nComment Tags\nCustomization\nimport typia, { tags } from \"typia\";\nexport const checkSpecialTag = typia.createIs();\ninterface SpecialTag {\n int32: number & tags.Type<\"int32\">;\n range?: number & tags.ExclusiveMinimum<19> & tags.Maximum<100>;\n minLength: string & tags.MinLength<3>;\n pattern: string & tags.Pattern<\"^[a-z]+$\">;\n date: null | (string & tags.Format<\"date\">);\n ip: string & (tags.Format<\"ipv4\"> | tags.Format<\"ipv6\">);\n uuids: Array> &\n tags.MinItems<3> &\n tags.MaxItems<100>;\n}\n\"use strict\";\nvar __importDefault =\n (this && this.__importDefault) ||\n function (mod) {\n return mod && mod.__esModule ? mod : { default: mod };\n };\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.checkSpecialTag = void 0;\nconst typia_1 = __importDefault(require(\"typia\"));\nconst checkSpecialTag = (input) => {\n const $io0 = (input) =>\n \"number\" === typeof input.int32 &&\n Math.floor(input.int32) === input.int32 &&\n -2147483648 <= input.int32 &&\n input.int32 <= 2147483647 &&\n (undefined === input.range ||\n (\"number\" === typeof input.range &&\n 19 < input.range &&\n input.range <= 100)) &&\n \"string\" === typeof input.minLength &&\n 3 <= input.minLength.length &&\n \"string\" === typeof input.pattern &&\n /^[a-z]+$/.test(input.pattern) &&\n (null === input.date ||\n (\"string\" === typeof input.date &&\n /^(d{4})-(d{2})-(d{2})$/.test(input.date))) &&\n \"string\" === typeof input.ip &&\n (/^(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.test(\n input.ip,\n ) ||\n /^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]).){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]).){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$/.test(\n input.ip,\n )) &&\n Array.isArray(input.uuids) &&\n 3 <= input.uuids.length &&\n input.uuids.length <= 100 &&\n input.uuids.every(\n (elem) =>\n \"string\" === typeof elem &&\n /^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000)$/i.test(\n elem,\n ),\n );\n return \"object\" === typeof input && null !== input && $io0(input);\n};\nexports.checkSpecialTag = checkSpecialTag;","encrypedroute#EncrypedRoute":"export namespace EncrypedRoute {\n export function Get(path?: string): MethodDecorator;\n export function Post(path?: string): MethodDecorator;\n export function Put(path?: string): MethodDecorator;\n export function Patch(path?: string): MethodDecorator;\n export function Delete(path?: string): MethodDecorator;\n}\nEncrypted router decorator functions.EncryptedRoute is a namespaced module similar with TypedRoute, but it encrypts response body data through AES-128/256 CBC algorithm like below. Therefore, it would be slower than TypedRoute, but it guarantees the security of response body data.\nAES-128/256\nCBC mode\nPKCS #5 Padding\nBase64 Encoding\nFor reference, such encryption spec is not supported in the Swagger-UI. Instead, SDK (Software Development Kit) generated by @nestia/sdk supports it. Thus, you have to build and distribute the SDK library to the client developers when using such encryption decorators.","configuration#Configuration":"{\n \"compilerOptions\": {\n \"strict\": true,\n \"plugins\": [\n { \"transform\": \"typia/lib/transform\" },\n {\n \"transform\": \"@nestia/core/lib/transform\",\n \"stringify\": \"assert\",\n // \"stringify\": typia.stringify without validation\n // \"assert\": typia.assertStringify\n // \"is\": typia.isStringify\n // \"validate\": typia.validateStringify\n // \"validate.log\": do not throw error, just log\n // \"validateEquals\".log: do not throw error, just log\n }\n ]\n }\n}\nChange JSON serializer to another one.If you configure stringify property of plugin defined in the tsconfig.json file, you can change the @TypedRoute module to utilize another JSON serialization function instead of the default typia.json.assertStringify() function. For example, if you change the property to \"validate\", the JSON serialization function of @TypedRoute module be changed to typia.json.validateStringify() function.Here is the list of available options.\nstringify: typia.json.stringify()\nassert: typia.json.assertStringify()\nis: typia.json.isStringify()\nvalidate: typia.json.validateStringify()\nnull: just use JSON.stringify() function without validation\nvalidate.log: do not throw error, but just log by typia.validate()\nvalidateEquals.log: do not throw error, but just log by typia.validateEquals()\nBy the way, this is not a recommended way, but you can skip the response type validation for. If you set the stringify property to null, the response type validation will be skipped and just JSON.stringify() function be used.Also, validate.log and validateEquals.log perform the validation, but do not throw 500 internal server error. When type error be detected, it seralizes response data by JSON.stringify() function, and logs the error message to the console or somewhere you've specified. It is useful when you want to know the error message, but do not want to throw 500 internal server error to the client application.\nTypedRoute.setValidateErrorLogger((err: TypedRoute.IValidateErrorLog) => {\n // you can customize the validation error logging\n console.error(err);\n});","benchmark#Benchmark":"","json#JSON":"Comparing JSON serialization speed, typia is maximum 200x faster than class-transformer.For reference, Nestia is using typia, and NestJS is using class-transformer. The other one fast-json-stringify is made and used by fastify (do not mean NestJS fastify mode, but mean pure fastify library. NestJS fastify mode still utilizes class-transformer), and it requires JSON schema definition.From above benchmark graph, you can see that class-transformer is extremely slower than others, even including built-in JSON.stringify() function. I can't understand why NestJS has adopted such slow and difficult library. The other fast-json-stringify is enough faster, but it needs extra schema definition like class-validator, therefore not easy to use.\ntypia needs only pure TypeScript type.\nclass-transformer requires DTO class with decorator function calls.\nfast-json-stringify requires JSON schema definition.\nMeasured on AMD R9-7940HS, Rog Flow X13","server#Server":"Looking at above benchmark, someone may ask:\nJSON serialization speed affects on the server performance?I think that the JSON serialization is just a tiny thing in the server side, isn't it?\nMy answer is, \"Yes, it affects on the server performance\".Most operations in NodeJS server are asynchronously executed in background thread, what are called \"event based non-blocking I/O model\". However, JSON serialization is a synchronous operation running on the main thread. Therefore, if the JSON serialization speed is slow, it makes the entire server program slow.I'll show you the benchmark result that, how JSON serizliation speed affects on the server performance.\nMeasured on AMD R9-7940HS, Rog Flow X13"}},"/docs/core/WebSocketRoute":{"title":"Websocketroute","data":{"outline#Outline":"export function WebSocketRoute(path?: string): MethodDecorator;\nexport namespace WebSocketRoute {\n export function Acceptor(): ParameterDecorator;\n export function Driver(): ParameterDecorator;\n export function Header(): ParameterDecorator;\n export function Param(field: string): ParameterDecorator;\n export function Query(): ParameterDecorator;\n}\nimport { WebSocketRoute } from \"@nestia/core\";\nimport { Controller } from \"@nestjs/common\";\nimport { Driver, WebSocketAcceptor } from \"tgrid\";\nimport { ICalculator } from \"./api/structures/ICalculator\";\nimport { IListener } from \"./api/structures/IListener\";\nimport { Calculator } from \"./providers/Calculator\";\n@Controller(\"calculate\")\nexport class CalculateController {\n /**\n * Start simple calculator.\n *\n * Start simple calculator through WebSocket.\n */\n @WebSocketRoute(\"start\")\n public async start(\n @WebSocketRoute.Acceptor()\n acceptor: WebSocketAcceptor,\n @WebSocketRoute.Driver() driver: Driver,\n ): Promise {\n await acceptor.accept(new Calculator(driver));\n }\n}\nimport { TestValidator } from \"@nestia/e2e\";\nimport api from \"@samchon/calculator-api/lib/index\";\nimport { IListener } from \"@samchon/calculator-api/lib/structures/IListener\";\nexport const test_api_calculate_start = async (\n connection: api.IConnection,\n): Promise => {\n const stack: IListener.IEvent[] = [];\n const listener: IListener = {\n on: (event) => stack.push(event),\n };\n const { connector, driver } = await api.functional.calculate.start(\n connection,\n listener,\n );\n try {\n TestValidator.equals(\"plus\")(await driver.plus(4, 2))(6);\n TestValidator.equals(\"minus\")(await driver.minus(4, 2))(2);\n TestValidator.equals(\"multiply\")(await driver.multiply(4, 2))(8);\n TestValidator.equals(\"divide\")(await driver.divide(4, 2))(2);\n TestValidator.equals(\"events\")(stack)([\n { type: \"plus\", x: 4, y: 2, z: 6 },\n { type: \"minus\", x: 4, y: 2, z: 2 },\n { type: \"multiply\", x: 4, y: 2, z: 8 },\n { type: \"divide\", x: 4, y: 2, z: 2 },\n ]);\n } catch (exp) {\n throw exp;\n } finally {\n await connector.close();\n }\n};\nWebSocket route decorators.@WebSocketRoute() is a collection of decorators for WebSocket routes.Also, supports SDK (Software Development Kit), so that you can easily develop the WebSocket client.\nReferences\nTGrid > Guide Documents > Remote Procedure Call\nTGrid > Guide Documents > Features > Components\nTGrid > Guide Documents > Features > WebSocket Protocol\nTGrid > Guide Documents > Learn from Examples > NestJS WebSocket\nπ» Playground Website","how-to-use#How to use":"","application-setup#Application Setup":"import { WebSocketAdaptor } from \"@nestia/core\";\nimport { INestApplication } from \"@nestjs/common\";\nimport { NestFactory } from \"@nestjs/core\";\nimport { CalculateModule } from \"./CalculateModule\";\nexport namespace CalculateBackend {\n export const start = async (): Promise => {\n const app: INestApplication = await NestFactory.create(CalculateModule);\n await WebSocketAdaptor.upgrade(app);\n await app.listen(3_000, \"0.0.0.0\");\n return app;\n };\n}\nAt first, you need to upgrade your NestJS application to support WebSocket protocol.Import WebSocketAdaptor class from @nestia/core, and call WebSocketAdaptor.upgrade() function with the NestJS application instance like above.If you don't upgrade it, @WebSocketRoute() decorated methods never work.","websocketroute#@WebSocketRoute()":"import { WebSocketRoute } from \"@nestia/core\";\nimport { Controller } from \"@nestjs/common\";\nimport { Driver, WebSocketAcceptor } from \"tgrid\";\nimport { ICalculator } from \"./api/structures/ICalculator\";\nimport { IListener } from \"./api/structures/IListener\";\nimport { Calculator } from \"./providers/Calculator\";\n@Controller(\"calculate\")\nexport class CalculateController {\n /**\n * Start simple calculator.\n *\n * Start simple calculator through WebSocket.\n */\n @WebSocketRoute(\"start\")\n public async start(\n @WebSocketRoute.Acceptor()\n acceptor: WebSocketAcceptor,\n @WebSocketRoute.Driver() driver: Driver,\n ): Promise {\n await acceptor.accept(new Calculator(driver));\n }\n}\nimport { Driver } from \"tgrid\";\nimport { ICalculator } from \"../api/structures/ICalculator\";\nimport { IListener } from \"../api/structures/IListener\";\nexport class Calculator implements ICalculator {\n public constructor(private readonly listener: Driver) {}\n public plus(x: number, y: number): number {\n const z: number = x + y;\n this.listener.on({ type: \"plus\", x, y, z }).catch(() => {});\n return z;\n }\n public minus(x: number, y: number): number {\n const z: number = x - y;\n this.listener.on({ type: \"minus\", x, y, z }).catch(() => {});\n return z;\n }\n public multiply(x: number, y: number): number {\n const z: number = x * y;\n this.listener.on({ type: \"multiply\", x, y, z }).catch(() => {});\n return z;\n }\n public divide(x: number, y: number): number {\n const z: number = x / y;\n this.listener.on({ type: \"divide\", x, y, z }).catch(() => {});\n return z;\n }\n}\nexport interface ICalculator {\n plus(a: number, b: number): number;\n minus(a: number, b: number): number;\n multiply(a: number, b: number): number;\n divide(a: number, b: number): number;\n}\nexport interface IListener {\n on(event: IListener.IEvent): void;\n}\nexport namespace IListener {\n export interface IEvent {\n type: string;\n x: number;\n y: number;\n z: number;\n }\n}\nimport { TestValidator } from \"@nestia/e2e\";\nimport api from \"@samchon/calculator-api/lib/index\";\nimport { IListener } from \"@samchon/calculator-api/lib/structures/IListener\";\nexport const test_api_calculate_start = async (\n connection: api.IConnection,\n): Promise => {\n const stack: IListener.IEvent[] = [];\n const listener: IListener = {\n on: (event) => stack.push(event),\n };\n const { connector, driver } = await api.functional.calculate.start(\n connection,\n listener,\n );\n try {\n TestValidator.equals(\"plus\")(await driver.plus(4, 2))(6);\n TestValidator.equals(\"minus\")(await driver.minus(4, 2))(2);\n TestValidator.equals(\"multiply\")(await driver.multiply(4, 2))(8);\n TestValidator.equals(\"divide\")(await driver.divide(4, 2))(2);\n TestValidator.equals(\"events\")(stack)([\n { type: \"plus\", x: 4, y: 2, z: 6 },\n { type: \"minus\", x: 4, y: 2, z: 2 },\n { type: \"multiply\", x: 4, y: 2, z: 8 },\n { type: \"divide\", x: 4, y: 2, z: 2 },\n ]);\n } catch (exp) {\n throw exp;\n } finally {\n await connector.close();\n }\n};\nAfter that, attach @WebSocketRoute() decorator function onto target method like above.Note that, never forget to defining the @WebSocketRoute.Acceptor() decorated parameter. It is essential for both WebSocket route method and SDK library generation. Each generic arguments of WebSocketAcceptor means like below:\nHeader: Header information received by client\nProvider: Service provider for client\nListener: Remote service provider from client\nAlso, the Driver is a type of the remote provider by client. If you call any function of the remote provider, your function call request will be sent to the remote client, and returned value would be recived from the client asynchronouly.Therefore, the Driver type converts every functions' return type to be Promise. In the client side, your Provider would be also wrapped into the Driver, so that client can call your functions asynchronously, too.","nested-decorators#Nested Decorators":"import { WebSocketRoute } from \"@nestia/core\";\nimport { Controller } from \"@nestjs/common\";\nimport { Driver, WebSocketAcceptor } from \"tgrid\";\nimport { tags } from \"typia\";\nimport { IAdvancedCalculator } from \"./api/structures/IAdvancedCalculator\";\nimport { IHeader } from \"./api/structures/IHeader\";\nimport { IListener } from \"./api/structures/IListener\";\nimport { IMemo } from \"./api/structures/IMemo\";\nimport { AdvancedCalculator } from \"./providers/AdvancedCalculator\";\n@Controller(\"calculate\")\nexport class CalculateController {\n /**\n * Start advanced calculator.\n *\n * Start advanced calculator through WebSocket with additional informations.\n *\n * @param id ID to assign\n * @param header Header information\n * @param memo Memo to archive\n */\n @WebSocketRoute(\":id/advance\")\n public async advance(\n @WebSocketRoute.Param(\"id\") id: string & tags.Format<\"uuid\">,\n @WebSocketRoute.Header() header: undefined | Partial,\n @WebSocketRoute.Query() memo: IMemo,\n @WebSocketRoute.Acceptor()\n acceptor: WebSocketAcceptor,\n ): Promise {\n if (header?.precision !== undefined && header.precision < 0)\n await acceptor.reject(1008, \"Invalid precision value\");\n else\n await acceptor.accept(\n new AdvancedCalculator(\n id,\n { precision: header?.precision ?? 2 },\n memo,\n acceptor.getDriver(),\n ),\n );\n }\n}\nimport { Driver } from \"tgrid\";\nimport { IAdvancedCalculator } from \"../api/structures/IAdvancedCalculator\";\nimport { IHeader } from \"../api/structures/IHeader\";\nimport { IListener } from \"../api/structures/IListener\";\nimport { IMemo } from \"../api/structures/IMemo\";\nexport class AdvancedCalculator implements IAdvancedCalculator {\n private round: (value: number) => number;\n public constructor(\n private readonly id: string,\n private readonly header: IHeader,\n private readonly memo: IMemo,\n private readonly listener: Driver,\n ) {\n this.round = roundPrecision(header.precision);\n }\n public getId(): string {\n return this.id;\n }\n public getPrecision(): number {\n return this.header.precision;\n }\n public getMemo(): IMemo {\n return this.memo;\n }\n public plus(x: number, y: number): number {\n const z: number = this.round(x + y);\n this.listener.on({ type: \"plus\", x, y, z }).catch(() => {});\n return z;\n }\n public minus(x: number, y: number): number {\n const z: number = this.round(x - y);\n this.listener.on({ type: \"minus\", x, y, z }).catch(() => {});\n return z;\n }\n public multiply(x: number, y: number): number {\n const z: number = this.round(x * y);\n this.listener.on({ type: \"multiply\", x, y, z }).catch(() => {});\n return z;\n }\n public divide(x: number, y: number): number {\n const z: number = this.round(x / y);\n this.listener.on({ type: \"divide\", x, y, z }).catch(() => {});\n return z;\n }\n}\nconst roundPrecision =\n (precision: number) =>\n (value: number): number => {\n const factor: number = Math.pow(10, precision);\n return Math.round(value * factor) / factor;\n };\nimport { IMemo } from \"./IMemo\";\nexport interface IAdvancedCalculator {\n plus(a: number, b: number): number;\n minus(a: number, b: number): number;\n multiply(a: number, b: number): number;\n divide(a: number, b: number): number;\n getId(): string;\n getPrecision(): number;\n getMemo(): IMemo;\n}\nexport interface IListener {\n on(event: IListener.IEvent): void;\n}\nexport namespace IListener {\n export interface IEvent {\n type: string;\n x: number;\n y: number;\n z: number;\n }\n}\nimport { TestValidator } from \"@nestia/e2e\";\nimport api from \"@samchon/calculator-api/lib/index\";\nimport { IListener } from \"@samchon/calculator-api/lib/structures/IListener\";\nimport { IMemo } from \"@samchon/calculator-api/lib/structures/IMemo\";\nimport { v4 } from \"uuid\";\nexport const test_api_calculate_advance = async (\n connection: api.IConnection,\n): Promise => {\n const stack: IListener.IEvent[] = [];\n const listener: IListener = {\n on: (event) => stack.push(event),\n };\n const id: string = v4();\n const memo: IMemo = {\n title: \"test\",\n description: null,\n time: Date.now(),\n };\n const { connector, driver } = await api.functional.calculate.advance(\n {\n ...connection,\n headers: { precision: 2 },\n },\n id,\n memo,\n listener,\n );\n try {\n TestValidator.equals(\"id\")(await driver.getId())(id);\n TestValidator.equals(\"memo\")(await driver.getMemo())(memo);\n TestValidator.equals(\"precision\")(await driver.getPrecision())(2);\n TestValidator.equals(\"plus\")(await driver.plus(1, 2))(3);\n TestValidator.equals(\"minus\")(await driver.minus(1, 2))(-1);\n TestValidator.equals(\"multiply\")(await driver.multiply(0.01, 0.02))(0);\n TestValidator.equals(\"divide\")(await driver.divide(1, 3))(0.33);\n TestValidator.equals(\"events\")(stack)([\n { type: \"plus\", x: 1, y: 2, z: 3 },\n { type: \"minus\", x: 1, y: 2, z: -1 },\n { type: \"multiply\", x: 0.01, y: 0.02, z: 0 },\n { type: \"divide\", x: 1, y: 3, z: 0.33 },\n ]);\n } catch (exp) {\n throw exp;\n } finally {\n await connector.close();\n }\n};\nIf you need additional parameters, you can use nested decorators.\n@WebSocketRoute.Acceptor(): Acceptor for the client connection\n@WebSocketRoute.Driver(): Driver for the remote provider by client\n@WebSocketRoute.Header(): Header information from the client\n@WebSocketRoute.Param(): URL path parameter\n@WebSocketRoute.Query(): URL query parameter\nFor reference, those decorators are almost same with @TypedHeaders(), @TypedParam() and @TypedQuery(). However, they can't be used in @WebSocketRoute() decorated method. Only nested decorator functions under the WebSocketRoute module are allowed.Also, if you don't want to accept the client connection, reject it through WebSocketAcceptor.close() function.","software-development-kit#Software Development Kit":"Related Document: Software Development KitWhen you configure a nestia.config.ts file and run npx nestia sdk command, @nestia/sdk will generate a SDK (Software Development Kit) library for the WebSocket route. With the SDK library, you can easily develop the WebSocket client application with TypeScript types.Also, as I've mentioned above, remote provider by WebSocket server is wrapped into the Driver type, so that the client application can call the remote provider's function asynchronously. For example, ICalculator.plus() function returned number value in the server side, but Driver returns Promise type.In the same reason, the IListener type would be wrapped into the Driver in the server side, and the listener provider would be called asynchronously in the server side through the WebSocket network communication.\nimport { TestValidator } from \"@nestia/e2e\";\nimport api from \"@samchon/calculator-api/lib/index\";\nimport { IListener } from \"@samchon/calculator-api/lib/structures/IListener\";\nexport const test_api_calculate_start = async (\n connection: api.IConnection,\n): Promise => {\n const stack: IListener.IEvent[] = [];\n const listener: IListener = {\n on: (event) => stack.push(event),\n };\n const { connector, driver } = await api.functional.calculate.start(\n connection,\n listener,\n );\n try {\n TestValidator.equals(\"plus\")(await driver.plus(4, 2))(6);\n TestValidator.equals(\"minus\")(await driver.minus(4, 2))(2);\n TestValidator.equals(\"multiply\")(await driver.multiply(4, 2))(8);\n TestValidator.equals(\"divide\")(await driver.divide(4, 2))(2);\n TestValidator.equals(\"events\")(stack)([\n { type: \"plus\", x: 4, y: 2, z: 6 },\n { type: \"minus\", x: 4, y: 2, z: 2 },\n { type: \"multiply\", x: 4, y: 2, z: 8 },\n { type: \"divide\", x: 4, y: 2, z: 2 },\n ]);\n } catch (exp) {\n throw exp;\n } finally {\n await connector.close();\n }\n};\n/**\n * @packageDocumentation\n * @module api.functional.calculate\n * @nestia Generated by Nestia - https://github.com/samchon/nestia\n */\n//================================================================\nimport type { IConnection } from \"@nestia/fetcher\";\nimport { WebSocketConnector } from \"tgrid\";\nimport type { Driver } from \"tgrid\";\nimport type { Format } from \"typia/lib/tags/Format\";\nimport type { IAdvancedCalculator } from \"../../structures/IAdvancedCalculator\";\nimport type { IHeader } from \"../../structures/IHeader\";\nimport type { IListener } from \"../../structures/IListener\";\nimport type { IMemo } from \"../../structures/IMemo\";\n/**\n * Start advanced calculator.\n *\n * Start advanced calculator through WebSocket with additional informations.\n *\n * @param id ID to assign\n * @param memo Memo to archive\n *\n * @controller CalculateController.advance\n * @path /calculate/:id/advance\n * @nestia Generated by Nestia - https://github.com/samchon/nestia\n */\nexport async function advance(\n connection: IConnection,\n id: string & Format<\"uuid\">,\n memo: advance.Query,\n provider: advance.Provider,\n): Promise {\n const connector: WebSocketConnector<\n advance.Header,\n advance.Provider,\n advance.Listener\n > = new WebSocketConnector(connection.headers ?? ({} as any), provider);\n await connector.connect(\n `${connection.host}/${advance.path(id, memo)}`\n .split(\"/\")\n .filter((str) => !!str)\n .join(\"/\"),\n );\n const driver: Driver = connector.getDriver();\n return {\n connector,\n driver,\n };\n}\nexport namespace advance {\n export type Output = {\n connector: WebSocketConnector;\n driver: Driver;\n };\n export type Header = undefined | Partial;\n export type Provider = IListener;\n export type Listener = IAdvancedCalculator;\n export type Query = IMemo;\n export const path = (id: string & Format<\"uuid\">, memo: advance.Query) => {\n const variables: URLSearchParams = new URLSearchParams();\n for (const [key, value] of Object.entries(memo as any))\n if (undefined === value) continue;\n else if (Array.isArray(value))\n value.forEach((elem: any) => variables.append(key, String(elem)));\n else variables.set(key, String(value));\n const location: string = `/calculate/${encodeURIComponent(id ?? \"null\")}/advance`;\n return 0 === variables.size\n ? location\n : `${location}?${variables.toString()}`;\n };\n}\nimport { WebSocketRoute } from \"@nestia/core\";\nimport { Controller } from \"@nestjs/common\";\nimport { Driver, WebSocketAcceptor } from \"tgrid\";\nimport { tags } from \"typia\";\nimport { IAdvancedCalculator } from \"./api/structures/IAdvancedCalculator\";\nimport { IHeader } from \"./api/structures/IHeader\";\nimport { IListener } from \"./api/structures/IListener\";\nimport { IMemo } from \"./api/structures/IMemo\";\nimport { AdvancedCalculator } from \"./providers/AdvancedCalculator\";\n@Controller(\"calculate\")\nexport class CalculateController {\n /**\n * Start advanced calculator.\n *\n * Start advanced calculator through WebSocket with additional informations.\n *\n * @param id ID to assign\n * @param header Header information\n * @param memo Memo to archive\n */\n @WebSocketRoute(\":id/advance\")\n public async advance(\n @WebSocketRoute.Param(\"id\") id: string & tags.Format<\"uuid\">,\n @WebSocketRoute.Header() header: undefined | Partial,\n @WebSocketRoute.Query() memo: IMemo,\n @WebSocketRoute.Acceptor()\n acceptor: WebSocketAcceptor,\n ): Promise {\n if (header?.precision !== undefined && header.precision < 0)\n await acceptor.reject(1008, \"Invalid precision value\");\n else\n await acceptor.accept(\n new AdvancedCalculator(\n id,\n { precision: header?.precision ?? 2 },\n memo,\n acceptor.getDriver(),\n ),\n );\n }\n}\nimport { IMemo } from \"./IMemo\";\nexport interface IAdvancedCalculator {\n plus(a: number, b: number): number;\n minus(a: number, b: number): number;\n multiply(a: number, b: number): number;\n divide(a: number, b: number): number;\n getId(): string;\n getPrecision(): number;\n getMemo(): IMemo;\n}\nexport interface IListener {\n on(event: IListener.IEvent): void;\n}\nexport namespace IListener {\n export interface IEvent {\n type: string;\n x: number;\n y: number;\n z: number;\n }\n}","restrictions#Restrictions":"","websocketacceptor#@WebSocketAcceptor()":"When defining @WebSocketRoute() decorated method, you must define the @WebSocketRoute.Acceptor() decorated parameter. It is essential for both WebSocket route method and SDK library generation, because its target type WebSocketAcceptor has significant type definitions for WebSocket communication.\nHeader: Header information received by client\nProvider: Service provider for client\nListener: Remote service provider from client\nimport { WebSocketRoute } from \"@nestia/core\";\nimport { Controller } from \"@nestjs/common\";\nimport { Driver, WebSocketAcceptor } from \"tgrid\";\nimport { ICalculator } from \"./api/structures/ICalculator\";\nimport { IListener } from \"./api/structures/IListener\";\nimport { Calculator } from \"./providers/Calculator\";\n@Controller(\"calculate\")\nexport class CalculateController {\n /**\n * Start simple calculator.\n *\n * Start simple calculator through WebSocket.\n */\n @WebSocketRoute(\"start\")\n public async start(\n @WebSocketRoute.Acceptor()\n acceptor: WebSocketAcceptor,\n @WebSocketRoute.Driver() driver: Driver,\n ): Promise {\n await acceptor.accept(new Calculator(driver));\n }\n}","websocketrouteparam#@WebSocketRoute.Param()":"@WebSocketRoute.Param() allows only atomic type.\nboolean\nnumber\nstring\nAlso, @WebSocketRoute.Param() allows nullable like number | null, but undefindable type is not.\nnumber | null is allowed\nstring | undefined is prohibited\nIf you violate above condition, and try to declare object or union type, compilation error would be occured:\nError on nestia.core.WebSocketRoute.Param(): only atomic type is allowed","websocketroutequery#@WebSocketRoute.Query()":"When using @WebSocketRoute.Query(), you've to follow such restrction.At first, type of @WebSocketRoute.Query() must be a pure object type. It does not allow union type. Also, nullable and undefindable types are not allowed, either. Note that, query parameter type must be a sole object type without any extra definition.At next, type of properties must be atomic, or array of atomic type. In the atomic type case, the atomic type allows both nullable and undefindable types. However, mixed union atomic type like string | number or \"1\" | \"2\" | 3 are not allowed. Also, the array type does not allow both nullable and undefindable types, either.\nboolean\nnumber\nbigint\nstring\nexport interface SomeQueryDto {\n //----\n // ATOMIC TYPES\n //----\n // ALLOWED\n boolean: boolean;\n number: number;\n string: string;\n bigint: bigint;\n optional_number?: number;\n nullable_string: string | null;\n literal_union: \"A\" | \"B\" | \"C\" | \"D\";\n // NOT ALLOWED\n mixed_union: string | number | boolean;\n mixed_literal: \"A\" | \"B\" | 3;\n //----\n // ARRAY TYPES\n //----\n // ALLOWED\n nullable_element_array: (string | null)[];\n string_array: string[];\n number_array: number[];\n literal_union_array: (\"A\" | \"B\" | \"C\")[];\n literal_tuple: [\"A\", \"B\", \"C\"];\n // NOT ALLOWED\n optional_element_array: (string | undefined)[];\n optional_array: string[] | undefined;\n nullable_array: string[] | null;\n union_atomic_array: (string | number)[];\n mixed_literal_array: (\"A\", \"B\", 3)[];\n mixed_tuple: [\"A\", \"B\", 3];\n}"}},"/docs/e2e/why":{"title":"Why","data":{"outline#Outline":"E2E test functions rather than unit test functions.When developing a test program for NestJS developed backend server, I recommend to adapt E2E test paradigm instead of unit test paradigm. It's because with the @nestia/sdk generated SDK library, E2E test functions can be much easier, safer and efficient for production than the traditional unit test functions.Furthermore, if you develop test functions utilizing the SDK library, you can easily switch the e2e test functions to the performance benchmark functions. Just by utilizing @nestia/e2e nd @nestia/benchmark libraries, you can easily measure your NestJS developed backend server's performance through the SDK library utilizing e2e test functions.\nE2E Test Function Example\nBenchmark Result Example\nSDK library utilizing test functions can be used in the performance benchmark program","efficient-for-production#Efficient for Production":"New era, age of E2E testing paradigm comes.In the past era, backend developers had developed test programs following the unit test paradigm. It's because traditional E2E test functions' development could not take any advantage of compiled language's type safety. As you can see from the below example code, E2E test functions had to write hard-coded fetch() function with string literals.Besides, unit test could take advantages of compiled language's type safety by importing related modules. Therefore, it was more efficient to develop test functions following the unit test paradigm. This is the reason why unit test paradigm had been loved in the past era.\nimport { TestValidator } from \"@nestia/e2e\";\nimport typia from \"typia\";\nimport { IBbsArticle } from \"@samchon/bbs-api/lib/structures/bbs/IBbsArticle\";\nexport const test_api_bbs_article_create = async (\n host: string,\n): Promise => {\n // In the traditional age, E2E test function could not take advantages \n // of type safety of the TypeScript. Instead, have to write hard-coded\n // `fetch()` function with string literals.\n const response: Response = await fetch(`${host}/bbs/articles`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n writer: \"someone\",\n password: \"1234\",\n title: \"title\",\n body: \"content\",\n format: \"md\",\n files: [],\n } satisfies IBbsArticle.ICreate),\n });\n const article: IBbsArticle = await response.json();\n typia.assert(article);\n const read: IBbsArticle = await (async () => {\n const response: Response = await fetch(`${host}/bbs/articles/${article.id}`);\n const article: IBbsArticle = await response.json();\n return typia.assert(article);\n })();\n TestValidator.equals(\"written\")(article)(read);\n};\nimport { TestValidator } from \"@nestia/e2e\";\nimport typia from \"typia\";\nimport { IBbsArticle } from \"@samchon/bbs-api/lib/structures/bbs/IBbsArticle\";\nimport { BbsArticleProvider } from \"../../../../src/providers/bbs/BbsArticleProvider\";\nexport const test_provider_bbs_article_create = async (): Promise => {\n // Besides, unit test could take advantages of type safety.\n const article: IBbsArticle = await BbsArticleProvider.write({\n writer: \"someone\",\n password: \"1234\",\n title: \"title\",\n body: \"content\",\n format: \"md\",\n files: [],\n });\n typia.assert(article);\n // Therefore, unit test had been loved in the past era\n const read: IBbsArticle = await BbsArticleProvider.at(article.id);\n typia.assert(read);\n TestValidator.equals(\"written\")(article)(read);\n};\nHowever, in the new era, e2e test functions also can take advantages of the type safety. Just import SDK library generated by @nestia/e2e, and call API funtions of it with TypeScript type hints. In this way, as both e2e test paradigm and unit test paradigm can take advantages of type safety, we have to consider which strategy is suitable for the production environment.In here article, I recommend to adapt e2e test paradigm in the below reasons.\nsuitable for CDD (Contract Driven Development)\neasy to develop and maintain\nmuch safer than unit testing due to its coverage\ncan be used for the performance benchmark\ncan guide client developers as an example code\nLook at the below example code of new era's e2e test function, and compare it with the traditional unit test function. As you can see, e2e test function actually tests the backend server's behavior by calling the real API endpoints of it. Besides, unit test can't test the backend server's actual behavior. It just validates the provider's behavior. This is the reason why new era's e2e test function is safer than unit test function.Also, such new era's e2e test function can be provided to the client or frontend developers as an example code. It guides them how to call the backend server's API endpoints through the SDK library. In this way, e2e test function can be used as a well-structured document for the client developers.\nimport { TestValidator } from \"@nestia/e2e\";\nimport typia from \"typia\";\nimport BbsApi from \"@samchon/bbs-api\";\nimport { IBbsArticle } from \"@samchon/bbs-api/lib/structures/bbs/IBbsArticle\";\nexport const test_provider_bbs_article_create = async (\n connection: BbsApi.IConnection\n): Promise => {\n // Unit test functions can't validate \n // the backend server's actual behavior.\n const article: IBbsArticle = await BbsApi.functional.bbs.articles.create(\n connection,\n {\n writer: \"someone\",\n password: \"1234\",\n title: \"title\",\n body: \"content\",\n format: \"md\",\n files: [],\n },\n );\n typia.assert(article);\n // This is the reason why I've adopted the e2e test paradigm\n const read: IBbsArticle = await BbsApi.functional.bbs.articles.at(\n connection,\n article.id,\n );\n typia.assert(read);\n TestValidator.equals(\"written\")(article)(read);\n};\nimport { TestValidator } from \"@nestia/e2e\";\nimport typia from \"typia\";\nimport BbsApi from \"@samchon/bbs-api\";\nimport { BbsArticleProvider } from \"../../../../src/providers/bbs/BbsArticleProvider\";\nexport const test_provider_bbs_article_create = async (): Promise => {\n // Unit test functions can't validate \n // the backend server's actual behavior.\n const article: IBbsArticle = await BbsArticleProvider.write({\n writer: \"someone\",\n password: \"1234\",\n title: \"title\",\n body: \"content\",\n format: \"md\",\n files: [],\n });\n typia.assert(article);\n // This is the reason why I've adopted the e2e test paradigm\n const read: IBbsArticle = await BbsArticleProvider.at(article.id);\n typia.assert(read);\n TestValidator.equals(\"written\")(article)(read);\n};\nAt last, the new era's e2e test functions can be used for the performance benchmark without any extra dedication. As the e2e test function directly calls the API endpoints of the backend server, backend server performance benchmark program can be easily developed by utilizing them.Those are the reasons why I recommend to adapt the new era's e2e test paradigm instead of the traditional unit test paradigm. Those are the reason why I am insisting that e2e test functions are efficient for production. From now on, let's see how to compose the e2e test functions for the NestJS developed backend server.\nYou can experience benchmark program utilizing e2e test functions.π» https://stackblitz.com/~/github.com/samchon/nestia-start","test-program-development#Test Program Development":"import { DynamicExecutor } from \"@nestia/e2e\";\nimport api from \"@ORGANIZATION/PROJECT-api\";\nimport { MyBackend } from \"../src/MyBackend\";\nimport { MyConfiguration } from \"../src/MyConfiguration\";\nimport { MyGlobal } from \"../src/MyGlobal\";\nimport { ArgumentParser } from \"./helpers/ArgumentParser\";\ninterface IOptions {\n include?: string[];\n exclude?: string[];\n}\nconst getOptions = () =>\n ArgumentParser.parse(async (command, _prompt, action) => {\n // command.option(\"--mode \", \"target mode\");\n // command.option(\"--reset \", \"reset local DB or not\");\n command.option(\"--include \", \"include feature files\");\n command.option(\"--exclude \", \"exclude feature files\");\n return action(async (options) => {\n // if (typeof options.reset === \"string\")\n // options.reset = options.reset === \"true\";\n // options.mode ??= await prompt.select(\"mode\")(\"Select mode\")([\n // \"LOCAL\",\n // \"DEV\",\n // \"REAL\",\n // ]);\n // options.reset ??= await prompt.boolean(\"reset\")(\"Reset local DB\");\n return options as IOptions;\n });\n });\nasync function main(): Promise {\n // CONFIGURATIONS\n const options: IOptions = await getOptions();\n MyGlobal.testing = true;\n // BACKEND SERVER\n const backend: MyBackend = new MyBackend();\n await backend.open();\n //----\n // CLINET CONNECTOR\n //----\n // DO TEST\n const connection: api.IConnection = {\n host: `http://127.0.0.1:${MyConfiguration.API_PORT()}`,\n };\n const report: DynamicExecutor.IReport = await DynamicExecutor.validate({\n prefix: \"test\",\n parameters: () => [{ ...connection }],\n filter: (func) =>\n (!options.include?.length ||\n (options.include ?? []).some((str) => func.includes(str))) &&\n (!options.exclude?.length ||\n (options.exclude ?? []).every((str) => !func.includes(str))),\n })(__dirname + \"/features\");\n await backend.close();\n const failures: DynamicExecutor.IReport.IExecution[] =\n report.executions.filter((exec) => exec.error !== null);\n if (failures.length === 0) {\n console.log(\"Success\");\n console.log(\"Elapsed time\", report.time.toLocaleString(), `ms`);\n } else {\n for (const f of failures) console.log(f.error);\n process.exit(-1);\n }\n console.log(\n [\n `All: #${report.executions.length}`,\n `Success: #${report.executions.length - failures.length}`,\n `Failed: #${failures.length}`,\n ].join(\"\\n\"),\n );\n}\nmain().catch((exp) => {\n console.log(exp);\n process.exit(-1);\n});\nimport { RandomGenerator, TestValidator } from \"@nestia/e2e\";\nimport { v4 } from \"uuid\";\nimport api from \"@ORGANIZATION/PROJECT-api/lib/index\";\nimport { IBbsArticle } from \"@ORGANIZATION/PROJECT-api/lib/structures/bbs/IBbsArticle\";\nexport async function test_api_bbs_article_create(\n connection: api.IConnection,\n): Promise {\n // STORE A NEW ARTICLE\n const stored: IBbsArticle = await api.functional.bbs.articles.create(\n connection,\n \"general\",\n {\n writer: RandomGenerator.name(),\n title: RandomGenerator.paragraph(3)(),\n body: RandomGenerator.content(8)()(),\n format: \"txt\",\n files: [\n {\n name: \"logo\",\n extension: \"png\",\n url: \"https://somewhere.com/logo.png\",\n },\n ],\n password: v4(),\n },\n );\n // READ THE DATA AGAIN\n const read: IBbsArticle = await api.functional.bbs.articles.at(\n connection,\n stored.section,\n stored.id,\n );\n TestValidator.equals(\"created\")(stored)(read);\n}\nimport { ArrayUtil, RandomGenerator, TestValidator } from \"@nestia/e2e\";\nimport api from \"@ORGANIZATION/PROJECT-api/lib/index\";\nimport { IBbsArticle } from \"@ORGANIZATION/PROJECT-api/lib/structures/bbs/IBbsArticle\";\nimport { IPage } from \"@ORGANIZATION/PROJECT-api/lib/structures/common/IPage\";\nexport async function test_api_bbs_article_index_search(\n connection: api.IConnection,\n): Promise {\n // GENERATE 100 ARTICLES\n const section: string = \"general\";\n const articles: IBbsArticle[] = await ArrayUtil.asyncRepeat(100)(() =>\n api.functional.bbs.articles.create(connection, section, {\n writer: RandomGenerator.name(),\n title: RandomGenerator.paragraph(4)(),\n body: RandomGenerator.content(3)()(),\n format: \"txt\",\n files: [],\n password: RandomGenerator.alphabets(8),\n }),\n );\n // GET ENTIRE DATA\n const total: IPage =\n await api.functional.bbs.articles.index(connection, section, {\n limit: articles.length,\n sort: [\"-created_at\"],\n });\n // PREPARE SEARCH FUNCTION\n const search = TestValidator.search(\"BbsArticleProvider.index()\")(\n async (input: IBbsArticle.IRequest.ISearch) => {\n const page: IPage =\n await api.functional.bbs.articles.index(connection, section, {\n limit: articles.length,\n search: input,\n sort: [\"-created_at\"],\n });\n return page.data;\n },\n )(total.data, 10);\n // SEARCH TITLE\n await search({\n fields: [\"title\"],\n values: (article) => [article.title],\n request: ([title]) => ({ title }),\n filter: (article, [title]) => article.title.includes(title),\n });\n // SEARCH WRITER\n await search({\n fields: [\"writer\"],\n values: (article) => [article.writer],\n request: ([writer]) => ({ writer }),\n filter: (article, [writer]) => article.writer.includes(writer),\n });\n // SEARCH BOTH OF THEM\n await search({\n fields: [\"title\", \"writer\"],\n values: (article) => [article.title, article.writer],\n request: ([title, writer]) => ({ title, writer }),\n filter: (article, [title, writer]) =>\n article.title.includes(title) && article.writer.includes(writer),\n });\n}\nJust make test functions utillizing SDK library, and export those functions with test_ prefixed names. And then if compose the main program of the test, all of the test functions would be automatically mounted and executed whenever you run the main test program.If you want to experience the test program earlier, visit below playground website.π» https://stackblitz.com/~/github.com/samchon/nestia-start\n- test_api_bbs_article_at: 149 ms\n- test_api_bbs_article_create: 30 ms\n- test_api_bbs_article_index_search: 1,312 ms\n- test_api_bbs_article_index_sort: 1,110 ms\n- test_api_bbs_article_update: 28 m","performance-benchmark#Performance Benchmark":"import { DynamicBenchmarker } from \"@nestia/benchmark\";\nimport cliProgress from \"cli-progress\";\nimport fs from \"fs\";\nimport os from \"os\";\nimport { IPointer } from \"tstl\";\nimport { MyBackend } from \"../../src/MyBackend\";\nimport { MyConfiguration } from \"../../src/MyConfiguration\";\nimport { MyGlobal } from \"../../src/MyGlobal\";\nimport { ArgumentParser } from \"../helpers/ArgumentParser\";\ninterface IOptions {\n include?: string[];\n exclude?: string[];\n count: number;\n threads: number;\n simultaneous: number;\n}\nconst getOptions = () =>\n ArgumentParser.parse(async (command, prompt, action) => {\n // command.option(\"--mode \", \"target mode\");\n // command.option(\"--reset \", \"reset local DB or not\");\n command.option(\"--include \", \"include feature files\");\n command.option(\"--exclude \", \"exclude feature files\");\n command.option(\"--count \", \"number of requests to make\");\n command.option(\"--threads \", \"number of threads to use\");\n command.option(\n \"--simultaneous \",\n \"number of simultaneous requests to make\",\n );\n return action(async (options) => {\n // if (typeof options.reset === \"string\")\n // options.reset = options.reset === \"true\";\n // options.mode ??= await prompt.select(\"mode\")(\"Select mode\")([\n // \"LOCAL\",\n // \"DEV\",\n // \"REAL\",\n // ]);\n // options.reset ??= await prompt.boolean(\"reset\")(\"Reset local DB\");\n options.count = Number(\n options.count ??\n (await prompt.number(\"count\")(\"Number of requests to make\")),\n );\n options.threads = Number(\n options.threads ??\n (await prompt.number(\"threads\")(\"Number of threads to use\")),\n );\n options.simultaneous = Number(\n options.simultaneous ??\n (await prompt.number(\"simultaneous\")(\n \"Number of simultaneous requests to make\",\n )),\n );\n return options as IOptions;\n });\n });\nconst main = async (): Promise => {\n // CONFIGURATIONS\n const options: IOptions = await getOptions();\n MyGlobal.testing = true;\n // BACKEND SERVER\n const backend: MyBackend = new MyBackend();\n await backend.open();\n // DO BENCHMARK\n const prev: IPointer = { value: 0 };\n const bar: cliProgress.SingleBar = new cliProgress.SingleBar(\n {},\n cliProgress.Presets.shades_classic,\n );\n bar.start(options.count, 0);\n const report: DynamicBenchmarker.IReport = await DynamicBenchmarker.master({\n servant: `${__dirname}/servant.js`,\n count: options.count,\n threads: options.threads,\n simultaneous: options.simultaneous,\n filter: (func) =>\n (!options.include?.length ||\n (options.include ?? []).some((str) => func.includes(str))) &&\n (!options.exclude?.length ||\n (options.exclude ?? []).every((str) => !func.includes(str))),\n progress: (value: number) => {\n if (value >= 100 + prev.value) {\n bar.update(value);\n prev.value = value;\n }\n },\n stdio: \"ignore\",\n });\n bar.stop();\n // DOCUMENTATION\n try {\n await fs.promises.mkdir(`${MyConfiguration.ROOT}/docs/benchmarks`, {\n recursive: true,\n });\n } catch {}\n await fs.promises.writeFile(\n `${MyConfiguration.ROOT}/docs/benchmarks/${os\n .cpus()[0]\n .model.trim()\n .split(\"\\\\\")\n .join(\"\")\n .split(\"/\")\n .join(\"\")}.md`,\n DynamicBenchmarker.markdown(report),\n \"utf8\",\n );\n // CLOSE\n await backend.close();\n};\nmain().catch((exp) => {\n console.error(exp);\n process.exit(-1);\n});\nimport { DynamicBenchmarker } from \"@nestia/benchmark\";\nimport { MyConfiguration } from \"../../src/MyConfiguration\";\nDynamicBenchmarker.servant({\n connection: {\n host: `http://127.0.0.1:${MyConfiguration.API_PORT()}`,\n },\n location: `${__dirname}/../features`,\n parameters: (connection) => [connection],\n prefix: \"test_api_\",\n}).catch((exp) => {\n console.error(exp);\n process.exit(-1);\n});\nimport { RandomGenerator, TestValidator } from \"@nestia/e2e\";\nimport { v4 } from \"uuid\";\nimport api from \"@ORGANIZATION/PROJECT-api/lib/index\";\nimport { IBbsArticle } from \"@ORGANIZATION/PROJECT-api/lib/structures/bbs/IBbsArticle\";\nexport async function test_api_bbs_article_create(\n connection: api.IConnection,\n): Promise {\n // STORE A NEW ARTICLE\n const stored: IBbsArticle = await api.functional.bbs.articles.create(\n connection,\n \"general\",\n {\n writer: RandomGenerator.name(),\n title: RandomGenerator.paragraph(3)(),\n body: RandomGenerator.content(8)()(),\n format: \"txt\",\n files: [\n {\n name: \"logo\",\n extension: \"png\",\n url: \"https://somewhere.com/logo.png\",\n },\n ],\n password: v4(),\n },\n );\n // READ THE DATA AGAIN\n const read: IBbsArticle = await api.functional.bbs.articles.at(\n connection,\n stored.section,\n stored.id,\n );\n TestValidator.equals(\"created\")(stored)(read);\n}\nimport { ArrayUtil, RandomGenerator, TestValidator } from \"@nestia/e2e\";\nimport api from \"@ORGANIZATION/PROJECT-api/lib/index\";\nimport { IBbsArticle } from \"@ORGANIZATION/PROJECT-api/lib/structures/bbs/IBbsArticle\";\nimport { IPage } from \"@ORGANIZATION/PROJECT-api/lib/structures/common/IPage\";\nexport async function test_api_bbs_article_index_search(\n connection: api.IConnection,\n): Promise {\n // GENERATE 100 ARTICLES\n const section: string = \"general\";\n const articles: IBbsArticle[] = await ArrayUtil.asyncRepeat(100)(() =>\n api.functional.bbs.articles.create(connection, section, {\n writer: RandomGenerator.name(),\n title: RandomGenerator.paragraph(4)(),\n body: RandomGenerator.content(3)()(),\n format: \"txt\",\n files: [],\n password: RandomGenerator.alphabets(8),\n }),\n );\n // GET ENTIRE DATA\n const total: IPage =\n await api.functional.bbs.articles.index(connection, section, {\n limit: articles.length,\n sort: [\"-created_at\"],\n });\n // PREPARE SEARCH FUNCTION\n const search = TestValidator.search(\"BbsArticleProvider.index()\")(\n async (input: IBbsArticle.IRequest.ISearch) => {\n const page: IPage =\n await api.functional.bbs.articles.index(connection, section, {\n limit: articles.length,\n search: input,\n sort: [\"-created_at\"],\n });\n return page.data;\n },\n )(total.data, 10);\n // SEARCH TITLE\n await search({\n fields: [\"title\"],\n values: (article) => [article.title],\n request: ([title]) => ({ title }),\n filter: (article, [title]) => article.title.includes(title),\n });\n // SEARCH WRITER\n await search({\n fields: [\"writer\"],\n values: (article) => [article.writer],\n request: ([writer]) => ({ writer }),\n filter: (article, [writer]) => article.writer.includes(writer),\n });\n // SEARCH BOTH OF THEM\n await search({\n fields: [\"title\", \"writer\"],\n values: (article) => [article.title, article.writer],\n request: ([title, writer]) => ({ title, writer }),\n filter: (article, [title, writer]) =>\n article.title.includes(title) && article.writer.includes(writer),\n });\n}\nYou can re-use test functions for the performance benchmark program.Just compose the benchmark's main and servant programs like above pointing the test functions' directory, then the benchmark program would utilize those test functions for the backend server performance measurement. The benchmark program will make multiple worker threads, and let them to make requests to the backend server simultaneously through the test functions.If you want to experience the benchmark program earlier, visit below playground website.π» https://stackblitz.com/~/github.com/samchon/nestia-start\n? Number of requests to make 1024\n? Number of threads to use 4\n? Number of simultaneous requests to make 32\nββββββββββββββββββββββββββββββββββββββββ 100% | ETA: 0s | 3654/1024"}},"/docs/sdk/e2e":{"title":"E2e","data":{"outline#Outline":"import { INestiaConfig } from \"@nestia/sdk\";\nimport { NestFactory } from \"@nestjs/core\";\n// import { FastifyAdapter } from \"@nestjs/platform-fastify\";\nimport { YourModule } from \"./src/YourModule\";\nconst NESTIA_CONFIG: INestiaConfig = {\n input: async () => {\n const app = await NestFactory.create(YourModule);\n // const app = await NestFactory.create(YourModule, new FastifyAdapter());\n // app.setGlobalPrefix(\"api\");\n // app.enableVersioning({\n // type: VersioningType.URI,\n // prefix: \"v\",\n // })\n return app;\n },\n output: \"src/api\",\n distribute: \"packages/api\",\n e2e: \"test\",\n};\nexport default NESTIA_CONFIG;\nnpx nestia e2e\nnpx nestia e2e --config nestia.config.ts --project tsconfig.json\nConfigure nestia.config.ts file and run npx nestia e2e command.Then, @nestia/sdk will analyze your NestJS backend server code, and generate both SDK (Software Development Kit) library for client developers. Also, E2E test functions, utilizing the SDK library, will be automatically generated for correspnding to every API functions.Here is an example of generated E2E test functions:\nimport typia, { Primitive } from \"typia\";\nimport api from \"../../../../src/api\";\nimport type { IBbsArticle } from \"../../../../src/api/structures/IBbsArticle\";\nexport const test_api_body_store = async (\n connection: api.IConnection,\n): Promise => {\n const output = await api.functional.body.store(\n connection,\n typia.random>(),\n );\n typia.assert(output);\n};\nLeft is server code, and right is e2e test code utilizing SDK library","configuration#Configuration":"import { INestiaConfig } from \"@nestia/sdk\";\nimport { NestFactory } from \"@nestjs/core\";\n// import { FastifyAdapter } from \"@nestjs/platform-fastify\";\nimport { YourModule } from \"./src/YourModule\";\nconst NESTIA_CONFIG: INestiaConfig = {\n input: async () => {\n const app = await NestFactory.create(YourModule);\n // const app = await NestFactory.create(YourModule, new FastifyAdapter());\n // app.setGlobalPrefix(\"api\");\n // app.enableVersioning({\n // type: VersioningType.URI,\n // prefix: \"v\",\n // })\n return app;\n },\n output: \"src/api\",\n distribute: \"packages/api\",\n e2e: \"test\",\n};\nexport default NESTIA_CONFIG;\nimport type { INestApplication } from \"@nestjs/common\";\nimport { OpenApi } from \"@samchon/openapi\";\n/**\n * Definition for the `nestia.config.ts` file.\n *\n * @author Jeongho Nam - https://github.com/samchon\n */\nexport interface INestiaConfig {\n /**\n * Accessor of controller classes.\n *\n * You can specify it within two ways\n *\n * - Asynchronous function returning `INestApplication` instance\n * - Specify the path or directory of controller class files\n */\n input:\n | (() => Promise)\n | INestiaConfig.IInput\n | string[]\n | string;\n /**\n * Output directory that SDK would be placed in.\n *\n * If not configured, you can't build the SDK library.\n */\n output?: string;\n /**\n * Building `swagger.json` is also possible.\n *\n * If not specified, you can't build the `swagger.json`.\n */\n swagger?: INestiaConfig.ISwaggerConfig;\n /**\n * Target directory that SDK distribution files would be placed in.\n *\n * If you configure this property and runs `npx nestia sdk` command,\n * distribution environments for the SDK library would be generated.\n *\n * After the SDK library generation, move to the `distribute` directory,\n * and runs `npm publish` command, then you can share SDK library with\n * other client (frontend) developers.\n *\n * Recommend to use `\"packages/api\"` value.\n */\n distribute?: string;\n /**\n * Allow simulation mode.\n *\n * If you configure this property to be `true`, the SDK library would be contain\n * simulation mode. In the simulation mode, the SDK library would not communicate\n * with the real backend server, but just returns random mock-up data\n * with requestion data validation.\n *\n * For reference, random mock-up data would be generated by `typia.random()`\n * function.\n *\n * @default false\n */\n simulate?: boolean;\n /**\n * Target directory that e2e test functions would be placed in.\n *\n * If you configure this property and runs `npx nestia e2e` command,\n * `@nestia/sdk` will analyze your NestJS backend server code, and\n * generates e2e test functions for every API endpoints.\n *\n * If not configured, you can't run `npx nestia e2e` command.\n */\n e2e?: string;\n /**\n * Whether to use propagation mode or not.\n *\n * If being configured, interaction functions of the SDK library would\n * perform the propagation mode. The propagation mode means that never\n * throwing exception even when status code is not 200 (or 201), but just\n * returning the {@link IPropagation} typed instance, which can specify its body\n * type through discriminated union determined by status code.\n *\n * @default false\n */\n propagate?: boolean;\n /**\n * Whether to clone DTO structures or not.\n *\n * If being configured, all of DTOs used in the backend server would be cloned\n * into the `structures` directory, and the SDK library would be refer to the\n * cloned DTOs instead of the original.\n *\n * @default false\n */\n clone?: boolean;\n /**\n * Whether to wrap DTO by primitive type.\n *\n * If you don't configure this property as `false`, all of DTOs in the\n * SDK library would be automatically wrapped by {@link Primitive} type.\n *\n * For refenrece, if a DTO type be capsuled by the {@link Primitive} type,\n * all of methods in the DTO type would be automatically erased. Also, if\n * the DTO has a `toJSON()` method, the DTO type would be automatically\n * converted to return type of the `toJSON()` method.\n *\n * @default true\n */\n primitive?: boolean;\n /**\n * Whether to assert parameter types or not.\n *\n * If you configure this property to be `true`, all of the function\n * parameters of SDK library would be checked through\n * [`typia.assert()` function](https://typia.io/docs/validators/assert/).\n *\n * This option would make your SDK library compilation time a little bit slower,\n * but would enahcne the type safety even in the runtime level.\n *\n * @default false\n */\n assert?: boolean;\n /**\n * Whether to optimize JSON string conversion 10x faster or not.\n *\n * If you configure this property to be `true`, the SDK library would utilize the\n * [`typia.assertStringify() function`](https://github.com/samchon/typia#enhanced-json)\n * to boost up JSON serialization speed and ensure type safety.\n *\n * This option would make your SDK library compilation time a little bit slower,\n * but would enhance JSON serialization speed 10x faster. Also, it can ensure type\n * safety even in the rumtime level.\n *\n * @default false\n */\n json?: boolean;\n}\nexport namespace INestiaConfig {\n /**\n * List of files or directories to include or exclude to specifying the NestJS\n * controllers.\n */\n export interface IInput {\n /**\n * List of files or directories containing the NestJS controller classes.\n */\n include: string[];\n /**\n * List of files or directories to be excluded.\n */\n exclude?: string[];\n }\n /**\n * Building `swagger.json` is also possible.\n */\n export interface ISwaggerConfig {\n /**\n * Output path of the `swagger.json`.\n *\n * If you've configured only directory, the file name would be the `swagger.json`.\n * Otherwise you've configured the full path with file name and extension, the\n * `swagger.json` file would be renamed to it.\n */\n output: string;\n /**\n * OpenAPI version.\n *\n * If you configure this property to be `2.0` or `3.0`, the newly generated\n * `swagger.json` file would follow the specified OpenAPI version. The newly\n * generated `swagger.json` file would be downgraded from the OpenAPI v3.1\n * specification by {@link OpenApi.downgrade} method.\n *\n * @default 3.1\n */\n openapi?: \"2.0\" | \"3.0\" | \"3.1\";\n /**\n * API information.\n *\n * If omitted, `package.json` content would be used instead.\n */\n info?: Partial;\n /**\n * List of server addresses.\n */\n servers?: OpenApi.IServer[];\n /**\n * Security schemes.\n *\n * When generating `swagger.json` file through `nestia`, if your controllers or\n * theirs methods have a security key which is not enrolled in here property,\n * it would be an error.\n */\n security?: Record;\n /**\n * List of tag names with description.\n *\n * It is possible to omit this property or skip some tag name even if\n * the tag name is used in the API routes. In that case, the tag name\n * would be used without description.\n *\n * Of course, if you've written a comment like `@tag {name} {descrition}`,\n * you can entirely replace this property specification.\n */\n tags?: OpenApi.IDocument.ITag[];\n /**\n * Decompose query DTO.\n *\n * If you configure this property to be `true`, the query DTO would be decomposed\n * into individual query parameters per each property. Otherwise you set it to be\n * `false`, the query DTO would be one object type which contains all of query\n * parameters.\n *\n * @default false\n */\n decompose?: boolean;\n /**\n * Operation ID generator.\n *\n * @param props Properties of the API endpoint.\n * @returns Operation ID.\n */\n operationId?(props: {\n class: string;\n function: string;\n method: \"HEAD\" | \"GET\" | \"POST\" | \"PUT\" | \"PATCH\" | \"DELETE\";\n path: string;\n }): string;\n }\n}\nMake nestia.config.ts file and run npx nestia e2e command.At first, create nestia.config.ts file through npx nestia init command. It would be placed on the top level directory of your NestJS backend project. For reference, tsconfig.json file also must be placed in the top level directory, too. After creation, configure the nestia.config.ts file referencing above example code and type definition.At least, you've to configure those three properties:\ninput: Accessor of controller classes\noutput: Path of output directory for SDK library\ne2e: Path of output directory for E2E test functions\nWhen you've completed above configuration, just run npx nestia e2e command. Then, SDK library would be generated into the $config.output directory, and E2E test functions would be generated into the $config.e2e directory, following your nestia.config.ts option.By the way, nestia.config.ts supports alternative options specifying the target controller classes instead of using the Module instance. If your backend application server does not have special configuration like setGlobalPrefix, enableVersioning and RouterModule, it is okay to specifying the target controller classes just by writing their file path like below.\nimport { INestiaConfig } from \"@nestia/sdk\";\nimport { NestFactory } from \"@nestjs/core\";\nimport { YourModule } from \"./src/YourModule\";\nconst NESTIA_CONFIG: INestiaConfig = {\n input: async () => {\n const app = await NestFactory.create(YourModule);\n // app.setGlobalPrefix(\"api\");\n // app.enableVersioning({\n // type: VersioningType.URI,\n // prefix: \"v\",\n // })\n return app;\n },\n output: \"src/api\",\n distribute: \"packages/api\",\n e2e: \"test\",\n};\nexport default NESTIA_CONFIG;\nimport { INestiaConfig } from \"@nestia/sdk\";\nimport { NestFactory } from \"@nestjs/core\";\nimport { FastifyAdapter } from \"@nestjs/platform-fastify\";\nimport { YourModule } from \"./src/YourModule\";\nconst NESTIA_CONFIG: INestiaConfig = {\n input: async () => {\n const app = await NestFactory.create(YourModule, new FastifyAdapter());\n // app.setGlobalPrefix(\"api\");\n // app.enableVersioning({\n // type: VersioningType.URI,\n // prefix: \"v\",\n // })\n return app;\n },\n output: \"src/api\",\n distribute: \"packages/api\",\n e2e: \"test\",\n};\nexport default NESTIA_CONFIG;\nimport { INestiaConfig } from \"@nestia/sdk\";\nconst NESTIA_CONFIG: INestiaConfig = {\n input: [\"src/controllers\", \"src/fake/controllers\", \"src/test/controllers\"],\n output: \"src/api\",\n distribute: \"packages/api\",\n e2e: \"test\",\n};\nexport default NESTIA_CONFIG;\nimport { INestiaConfig } from \"@nestia/sdk\";\nconst NESTIA_CONFIG: INestiaConfig = {\n input: \"src/**/*.controller.ts\",\n output: \"src/api\",\n distribute: \"packages/api\",\n e2e: \"test\",\n};\nexport default NESTIA_CONFIG;\nimport { INestiaConfig } from \"@nestia/sdk\";\nconst NESTIA_CONFIG: INestiaConfig = {\n input: {\n include: [\"src/controllers\"],\n exclude: [\"src/**/*.fake.ts\"],\n },\n output: \"src/api\",\n distribute: \"packages/api\",\n e2e: \"test\",\n};\nexport default NESTIA_CONFIG;","customization#Customization":"import core from \"@nestia/core\";\nimport { DynamicExecutor } from \"@nestia/e2e\";\nimport { INestApplication } from \"@nestjs/common\";\nimport { NestFactory } from \"@nestjs/core\";\nasync function main(): Promise {\n const server: INestApplication = await NestFactory.create(\n await core.DynamicModule.mount({\n // follows your nestia.config.ts setting\n controllers: {\n input: [\"src/controllers\"],\n exclude: [\"src/**/*.fake.ts\"],\n },\n }),\n );\n await server.listen(37_000);\n const report: DynamicExecutor.IReport = await DynamicExecutor.validate({\n prefix: \"test\",\n parameters: () => [\n {\n host: \"http://127.0.0.1:37000\",\n },\n ],\n })(`${__dirname}/features`);\n await server.close();\n const exceptions: Error[] = report.executions\n .filter((exec) => exec.error !== null)\n .map((exec) => exec.error!);\n if (exceptions.length === 0) {\n console.log(\"Success\");\n console.log(\"Elapsed time\", report.time.toLocaleString(), `ms`);\n } else {\n for (const exp of exceptions) console.log(exp);\n console.log(\"Failed\");\n console.log(\"Elapsed time\", report.time.toLocaleString(), `ms`);\n process.exit(-1);\n }\n}\nmain().catch((exp) => {\n console.log(exp);\n process.exit(-1);\n});\nNothing be specified, customize by yourself.When you generate e2e test functions through npx nestia e2e command, such index.ts file would be placed into the top level directory of test program. As you can see, the initial e2e test program only opens your NestJS backend server only with path of controllers with port number 37,000.However, it may not fully meet your requirements. For example, you may connect to a database server, and also need to configure DI (Dependency Injection) classes, too. You've to configure those things by yourself. @nestia/sdk can analyzes your backend server in the compilation level, but unable to reproduce such customizations.\nimport { Controller } from \"@nestjs/common\";\nimport typia, { tags } from \"typia\";\nimport core from \"@nestia/core\";\nimport { IBbsArticle } from \"@api/lib/structures/IBbsArticle\";\n@Controller(\"bbs/articles/:section\")\nexport class BbsArticlesController {\n /**\n * Store a new article.\n *\n * @param section Section code\n * @param input Content to store\n * @returns Newly archived article\n */\n @core.TypedRoute.Post()\n public async store(\n @core.TypedParam(\"section\") section: string,\n @core.TypedBody() input: IBbsArticle.IStore,\n ): Promise {\n return {\n ...typia.random(),\n section,\n ...input,\n };\n }\n /**\n * Update an article.\n *\n * @param section Section code\n * @param id Target article ID\n * @param input Content to update\n * @returns Updated content\n */\n @core.TypedRoute.Put(\":id\")\n public async update(\n @core.TypedParam(\"section\") section: string,\n @core.TypedParam(\"id\") id: string & tags.Format<\"uuid\">,\n @core.TypedBody() input: IBbsArticle.IStore,\n ): Promise {\n return {\n ...typia.random(),\n id,\n section,\n ...input,\n };\n }\n}\n/**\n * @packageDocumentation\n * @module api.functional.bbs.articles\n * @nestia Generated by Nestia - https://github.com/samchon/nestia\n */\n//================================================================\nimport type { IConnection, Primitive } from \"@nestia/fetcher\";\nimport { PlainFetcher } from \"@nestia/fetcher/lib/PlainFetcher\";\nimport type { Format } from \"typia/lib/tags/Format\";\nimport type { IBbsArticle } from \"../../../structures/IBbsArticle\";\n/**\n * Store a new article.\n *\n * @param section Section code\n * @param input Content to store\n * @returns Newly archived article\n *\n * @controller [object Object]\n * @path POST /bbs/articles/:section\n * @nestia Generated by Nestia - https://github.com/samchon/nestia\n */\nexport async function store(\n connection: IConnection,\n section: string,\n input: store.Input,\n): Promise {\n return PlainFetcher.fetch(\n {\n ...connection,\n headers: {\n ...connection.headers,\n \"Content-Type\": \"application/json\",\n },\n },\n {\n ...store.METADATA,\n path: store.path(section),\n } as const,\n input,\n );\n}\nexport namespace store {\n export type Input = Primitive;\n export type Output = Primitive;\n export const METADATA = {\n method: \"POST\",\n path: \"/bbs/articles/:section\",\n request: {\n type: \"application/json\",\n encrypted: false,\n },\n response: {\n type: \"application/json\",\n encrypted: false,\n },\n status: null,\n } as const;\n export const path = (section: string): string => {\n return `/bbs/articles/${encodeURIComponent(section ?? \"null\")}`;\n };\n}\n/**\n * Update an article.\n *\n * @param section Section code\n * @param id Target article ID\n * @param input Content to update\n * @returns Updated content\n *\n * @controller [object Object]\n * @path PUT /bbs/articles/:section/:id\n * @nestia Generated by Nestia - https://github.com/samchon/nestia\n */\nexport async function update(\n connection: IConnection,\n section: string,\n id: string & Format<\"uuid\">,\n input: update.Input,\n): Promise {\n return PlainFetcher.fetch(\n {\n ...connection,\n headers: {\n ...connection.headers,\n \"Content-Type\": \"application/json\",\n },\n },\n {\n ...update.METADATA,\n path: update.path(section, id),\n } as const,\n input,\n );\n}\nexport namespace update {\n export type Input = Primitive;\n export type Output = Primitive;\n export const METADATA = {\n method: \"PUT\",\n path: \"/bbs/articles/:section/:id\",\n request: {\n type: \"application/json\",\n encrypted: false,\n },\n response: {\n type: \"application/json\",\n encrypted: false,\n },\n status: null,\n } as const;\n export const path = (\n section: string,\n id: string & Format<\"uuid\">,\n ): string => {\n return `/bbs/articles/${encodeURIComponent(\n section ?? \"null\",\n )}/${encodeURIComponent(id ?? \"null\")}`;\n };\n}\nimport type { IConnection, Primitive } from \"@nestia/fetcher\";\nimport { PlainFetcher } from \"@nestia/fetcher/lib/PlainFetcher\";\nimport type { Format } from \"typia/lib/tags/Format\";\nimport api from \"../../../../src/api\";\nimport type { IBbsArticle } from \"../../../../src/api/structures/IBbsArticle\";\nexport const test_api_bbs_articles_update = async (\n connection: api.IConnection,\n): Promise => {\n const output = await api.functional.bbs.articles.update(\n connection,\n typia.random(),\n typia.random>(),\n typia.random>(),\n );\n typia.assert(output);\n};\nYou also need to customize each e2e test functions.When you run npx nestia e2e command, every e2e functions would be placed into $config.e2e/features/api/automated directory. Also, automatically generated e2e test functions are composing parameters through typia.random() function.If your NestJS backend server development has not been completed, and your API functions are in the mock-up level, such random parameter composition would not be problem. Otherwise your API functions are enoughly completed, such random parameter composition may occur logic error.Therefore, you also need to customize automatically generated e2e test functions. Move each e2e test files from the $config.e2e/features/api/automated directory to somewhere else, and customize those e2e test functions to be suitable for your domain logics."}},"/docs/pure":{"title":"Pure TypeScript","data":{"outline#Outline":"nestia can use pure TypeScript type.You know what? NestJS needs triple duplicated DTO schema definitions. The 1st is defining TypeScript type, the 2nd and 3rd are calling decorator functions of class-validator and @nestjs/swagger. It's not only annoying, but also error-prone. If you take any mistake on the 2nd or 3rd, it can't be detected by TypeScript compiler. It will be detected only at runtime. Another words, it is not type safe.Besides, nestia needs only pure TypeScript type. You don't need to define any extra schema like class-validator or @nestjs/swagger. Just define pure TypeScript type only (especially recommend to use interface type), then nestia will do all the rest.","demonstration#Demonstration":"If you're confusing how DTO of NestJS and nestia are different, just see example codes below.At first, look at the first (Triple duplicated NestJS DTO) tab, and find the BbsArticle.files property, enhanced by blue coloured blocks. Looking at the files property, how do you feel? Just defining an array object type, you've to call 7 decorator functions. If you take any mistake when using the decorator like omitting isArray property, it would be a critical runtime error.Besides, nestia needs only one line. Click the second (Pure Nestia DTO) tab, and find the IAttachmentFile.files property. Only one line being used, and IBbsArticle and IAttachment types are not even class, but just interface types. Comparing it to the first tab, how do you feel? Isn't it more simple and readable?This is the power of nestia, with pure TypeScript type.\nimport { ApiProperty } from \"@nestjs/swagger\";\nimport {\n ArrayNotEmpty,\n Format,\n IsArray,\n IsObject,\n IsOptional,\n IsString,\n Match,\n MaxLength,\n Type,\n ValidateNested,\n} from \"class-validator\";\nexport class BbsArticle {\n @ApiProperty({\n format: \"uuid\",\n })\n @IsString()\n id!: string;\n // DUPLICATED SCHEMA DEFINITION\n // - duplicated function call + property type\n // - have to specify `isArray` and `nullable` props by yourself\n @ApiProperty({\n type: () => AttachmentFile,\n nullable: true,\n isArray: true,\n description: \"List of attached files.\",\n })\n @Type(() => AttachmentFile)\n @IsArray()\n @IsOptional()\n @IsObject({ each: true })\n @ValidateNested({ each: true })\n files!: AttachmentFile[] | null;\n @ApiProperty({\n type: \"string\",\n nullable: true,\n minLength: 5,\n maxLength: 100,\n description: \"Title of the article.\",\n })\n @IsOptional()\n @IsString()\n title!: string | null;\n @ApiProperty({\n description: \"Main content body of the article.\",\n })\n @IsString()\n body!: string;\n @ApiProperty({\n format: \"date-time\",\n description: \"Creation time of article\",\n })\n @IsString()\n created_at!: string;\n}\nexport class AttachmentFile {\n @ApiProperty({\n type: \"string\",\n maxLength: 255,\n pattern: \"^[a-zA-Z0-9-_]+$\",\n description: \"File name.\",\n })\n @Matches(/^[a-z0-9]+$/)\n @MaxLength(255)\n @IsString()\n name!: string | null;\n @ApiProperty({\n type: \"string\",\n nullable: true,\n maxLength: 255,\n pattern: \"^[a-zA-Z0-9-_]+$\",\n description: \"File extension.\",\n })\n @Matches(/^[a-z0-9]+$/)\n @MaxLength(8)\n @IsOptional()\n @IsString()\n extension!: string | null;\n @ApiProperty({\n format: \"url\",\n description: \"URL of the file.\",\n })\n @Format(\"uri\")\n @IsString()\n url!: string;\n}\nimport { tags } from \"typia\";\nexport interface IBbsArticle {\n /**\n * Primary Key.\n */\n id: string & tags.Format<\"uuid\">;\n /**\n * List of attached files.\n */\n files: null | IAttachmentFile[];\n /**\n * Title of the article.\n */\n title: null | (string & tags.MinLength<5> & tags.MaxLength<100>);\n /**\n * Main content body of the article.\n */\n body: string;\n /**\n * Creation time of article.\n */\n created_at: string & tags.Format<\"date-time\">;\n}\nexport interface IAttachmentFile {\n /**\n * File name.\n */\n name: string & tags.Pattern<\"^[a-z0-9]+$\"> & tags.MaxLength<255>;\n /**\n * File extension.\n */\n extension: null | (string & tags.Pattern<\"^[a-z0-9]+$\"> & tags.MaxLength<8>);\n /**\n * URL of the file.\n */\n url: string & tags.Format<\"uri\">;\n}","aot-compilation#AOT Compilation":"Someone may be suspicious of the phrase \"Pure TypeScript Type\".\n\"As you know, TypeScript types do not have any tangible instance when compiled to JS.However, with only these fictitious TypeScript types, how can nestia validates types at runtime? How nestia builds swagger documents or SDK library with only these types? Are these things really possible without extra schema definition like class-validator or @nestjs/swagger?\"\nMy answer is: \"Yes, it is possible due to nestia analyzes your server code, and performs AOT compilation\".When compiling, nestia travels your NestJS server codes, and analyzes DTO definitions. And then, nestia writes optimal code to the compiled JavaScript file. In the @TypedBody() case, nestia transforms it to optimal validation code for the IBbsArticle.IStore type. Also, nestia transforms @TypedRoute.Post() function to optimal JSON serialization code for the IBbsArticle type.Such compile time optimization is called AOT (Ahead of Time) compilation. And this is the secret why nestia can do everything with only pure TypeScript type. Read below example codes, and just look how JavaScript file being compiled. Then you may understand why nestia is much easier, and futhermore much faster.\nRuntime validator is 20,000x faster than class-validator\nJSON serialization is 200x faster than class-transformer\nimport { tags } from \"typia\";\nexport interface IBbsArticle extends IBbsArticle.ICreate {\n id: string & tags.Format<\"uuid\">;\n created_at: string & tags.Format<\"date-time\">;\n}\nexport namespace IBbsArticle {\n export interface ICreate {\n title: string & tags.MinLength<3> & tags.MaxLength<50>;\n body: string;\n files: IAttachmentFile[];\n }\n}\nexport interface IAttachmentFile {\n name: null | (string & tags.MinLength<1> & tags.MaxLength<255>);\n extension: null | (string & tags.MinLength<1> & tags.MaxLength<8>);\n url: string | (string & tags.Format<\"url\">);\n}\nimport { TypedBody, TypedRoute } from \"@nestia/core\";\nimport { Controller } from \"@nestjs/common\";\nimport { IBbsArticle } from \"./IBbsArticle\";\n@Controller(\"bbs/articles\")\nexport class BbsArticlesController {\n @TypedRoute.Post() // 200x faster JSON serialization\n public async store(\n // 20,000x faster validation\n @TypedBody() input: IBbsArticle.ICreate,\n ): Promise {\n return {\n ...input,\n id: \"2b5e21d8-0e44-4482-bd3e-4540dee7f3d6\",\n created_at: \"2023-04-23T12:04:54.168Z\",\n };\n }\n}\n\"use strict\";\n\"use strict\";\nvar __decorate =\n (this && this.__decorate) ||\n function (decorators, target, key, desc) {\n var c = arguments.length,\n r =\n c < 3\n ? target\n : desc === null\n ? (desc = Object.getOwnPropertyDescriptor(target, key))\n : desc,\n d;\n if (typeof Reflect === \"object\" && typeof Reflect.decorate === \"function\")\n r = Reflect.decorate(decorators, target, key, desc);\n else\n for (var i = decorators.length - 1; i >= 0; i--)\n if ((d = decorators[i]))\n r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;\n return c > 3 && r && Object.defineProperty(target, key, r), r;\n };\nvar __metadata =\n (this && this.__metadata) ||\n function (k, v) {\n if (typeof Reflect === \"object\" && typeof Reflect.metadata === \"function\")\n return Reflect.metadata(k, v);\n };\nvar __param =\n (this && this.__param) ||\n function (paramIndex, decorator) {\n return function (target, key) {\n decorator(target, key, paramIndex);\n };\n };\nvar __awaiter =\n (this && this.__awaiter) ||\n function (thisArg, _arguments, P, generator) {\n function adopt(value) {\n return value instanceof P\n ? value\n : new P(function (resolve) {\n resolve(value);\n });\n }\n return new (P || (P = Promise))(function (resolve, reject) {\n function fulfilled(value) {\n try {\n step(generator.next(value));\n } catch (e) {\n reject(e);\n }\n }\n function rejected(value) {\n try {\n step(generator[\"throw\"](value));\n } catch (e) {\n reject(e);\n }\n }\n function step(result) {\n result.done\n ? resolve(result.value)\n : adopt(result.value).then(fulfilled, rejected);\n }\n step((generator = generator.apply(thisArg, _arguments || [])).next());\n });\n };\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.BbsArticlesController = void 0;\nconst core_1 = require(\"@nestia/core\");\nconst common_1 = require(\"@nestjs/common\");\nlet BbsArticlesController = class BbsArticlesController {\n store(\n // 20,000x faster validation\n input,\n ) {\n return __awaiter(this, void 0, void 0, function* () {\n return Object.assign(Object.assign({}, input), {\n id: \"2b5e21d8-0e44-4482-bd3e-4540dee7f3d6\",\n created_at: \"2023-04-23T12:04:54.168Z\",\n });\n });\n }\n};\nexports.BbsArticlesController = BbsArticlesController;\n__decorate(\n [\n core_1.TypedRoute.Post({\n type: \"assert\",\n assert: (input) => {\n const assert = (input) => {\n const __is = (input) => {\n const $io0 = (input) =>\n \"string\" === typeof input.id &&\n /^(?:urn:uuid:)?[0-9a-f]{8}-(?:[0-9a-f]{4}-){3}[0-9a-f]{12}$/i.test(\n input.id,\n ) &&\n \"string\" === typeof input.created_at &&\n !isNaN(new Date(input.created_at).getTime()) &&\n \"string\" === typeof input.title &&\n 3 <= input.title.length &&\n input.title.length <= 50 &&\n \"string\" === typeof input.body &&\n Array.isArray(input.files) &&\n input.files.every(\n (elem) =>\n \"object\" === typeof elem && null !== elem && $io1(elem),\n );\n const $io1 = (input) =>\n (null === input.name ||\n (\"string\" === typeof input.name &&\n 1 <= input.name.length &&\n input.name.length <= 255)) &&\n (null === input.extension ||\n (\"string\" === typeof input.extension &&\n 1 <= input.extension.length &&\n input.extension.length <= 8)) &&\n \"string\" === typeof input.url &&\n /^(?:https?|ftp):\\/\\/(?:\\S+(?::\\S*)?@)?(?:(?!(?:10|127)(?:\\.\\d{1,3}){3})(?!(?:169\\.254|192\\.168)(?:\\.\\d{1,3}){2})(?!172\\.(?:1[6-9]|2\\d|3[0-1])(?:\\.\\d{1,3}){2})(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|(?:(?:[a-z0-9\\u{00a1}-\\u{ffff}]+-)*[a-z0-9\\u{00a1}-\\u{ffff}]+)(?:\\.(?:[a-z0-9\\u{00a1}-\\u{ffff}]+-)*[a-z0-9\\u{00a1}-\\u{ffff}]+)*(?:\\.(?:[a-z\\u{00a1}-\\u{ffff}]{2,})))(?::\\d{2,5})?(?:\\/[^\\s]*)?$/iu.test(\n input.url,\n );\n return \"object\" === typeof input && null !== input && $io0(input);\n };\n if (false === __is(input))\n ((input, _path, _exceptionable = true) => {\n const $guard = core_1.TypedRoute.Post.guard;\n const $ao0 = (input, _path, _exceptionable = true) =>\n ((\"string\" === typeof input.id &&\n (/^(?:urn:uuid:)?[0-9a-f]{8}-(?:[0-9a-f]{4}-){3}[0-9a-f]{12}$/i.test(\n input.id,\n ) ||\n $guard(_exceptionable, {\n path: _path + \".id\",\n expected: 'string & Format<\"uuid\">',\n value: input.id,\n }))) ||\n $guard(_exceptionable, {\n path: _path + \".id\",\n expected: '(string & Format<\"uuid\">)',\n value: input.id,\n })) &&\n ((\"string\" === typeof input.created_at &&\n (!isNaN(new Date(input.created_at).getTime()) ||\n $guard(_exceptionable, {\n path: _path + \".created_at\",\n expected: 'string & Format<\"date-time\">',\n value: input.created_at,\n }))) ||\n $guard(_exceptionable, {\n path: _path + \".created_at\",\n expected: '(string & Format<\"date-time\">)',\n value: input.created_at,\n })) &&\n ((\"string\" === typeof input.title &&\n (3 <= input.title.length ||\n $guard(_exceptionable, {\n path: _path + \".title\",\n expected: \"string & MinLength<3>\",\n value: input.title,\n })) &&\n (input.title.length <= 50 ||\n $guard(_exceptionable, {\n path: _path + \".title\",\n expected: \"string & MaxLength<50>\",\n value: input.title,\n }))) ||\n $guard(_exceptionable, {\n path: _path + \".title\",\n expected: \"(string & MinLength<3> & MaxLength<50>)\",\n value: input.title,\n })) &&\n (\"string\" === typeof input.body ||\n $guard(_exceptionable, {\n path: _path + \".body\",\n expected: \"string\",\n value: input.body,\n })) &&\n (((Array.isArray(input.files) ||\n $guard(_exceptionable, {\n path: _path + \".files\",\n expected: \"Array\",\n value: input.files,\n })) &&\n input.files.every(\n (elem, _index1) =>\n (((\"object\" === typeof elem && null !== elem) ||\n $guard(_exceptionable, {\n path: _path + \".files[\" + _index1 + \"]\",\n expected: \"IAttachmentFile\",\n value: elem,\n })) &&\n $ao1(\n elem,\n _path + \".files[\" + _index1 + \"]\",\n true && _exceptionable,\n )) ||\n $guard(_exceptionable, {\n path: _path + \".files[\" + _index1 + \"]\",\n expected: \"IAttachmentFile\",\n value: elem,\n }),\n )) ||\n $guard(_exceptionable, {\n path: _path + \".files\",\n expected: \"Array\",\n value: input.files,\n }));\n const $ao1 = (input, _path, _exceptionable = true) =>\n (null === input.name ||\n (\"string\" === typeof input.name &&\n (1 <= input.name.length ||\n $guard(_exceptionable, {\n path: _path + \".name\",\n expected: \"string & MinLength<1>\",\n value: input.name,\n })) &&\n (input.name.length <= 255 ||\n $guard(_exceptionable, {\n path: _path + \".name\",\n expected: \"string & MaxLength<255>\",\n value: input.name,\n }))) ||\n $guard(_exceptionable, {\n path: _path + \".name\",\n expected:\n \"((string & MinLength<1> & MaxLength<255>) | null)\",\n value: input.name,\n })) &&\n (null === input.extension ||\n (\"string\" === typeof input.extension &&\n (1 <= input.extension.length ||\n $guard(_exceptionable, {\n path: _path + \".extension\",\n expected: \"string & MinLength<1>\",\n value: input.extension,\n })) &&\n (input.extension.length <= 8 ||\n $guard(_exceptionable, {\n path: _path + \".extension\",\n expected: \"string & MaxLength<8>\",\n value: input.extension,\n }))) ||\n $guard(_exceptionable, {\n path: _path + \".extension\",\n expected: \"((string & MinLength<1> & MaxLength<8>) | null)\",\n value: input.extension,\n })) &&\n ((\"string\" === typeof input.url &&\n (/^(?:https?|ftp):\\/\\/(?:\\S+(?::\\S*)?@)?(?:(?!(?:10|127)(?:\\.\\d{1,3}){3})(?!(?:169\\.254|192\\.168)(?:\\.\\d{1,3}){2})(?!172\\.(?:1[6-9]|2\\d|3[0-1])(?:\\.\\d{1,3}){2})(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|(?:(?:[a-z0-9\\u{00a1}-\\u{ffff}]+-)*[a-z0-9\\u{00a1}-\\u{ffff}]+)(?:\\.(?:[a-z0-9\\u{00a1}-\\u{ffff}]+-)*[a-z0-9\\u{00a1}-\\u{ffff}]+)*(?:\\.(?:[a-z\\u{00a1}-\\u{ffff}]{2,})))(?::\\d{2,5})?(?:\\/[^\\s]*)?$/iu.test(\n input.url,\n ) ||\n $guard(_exceptionable, {\n path: _path + \".url\",\n expected: 'string & Format<\"url\">',\n value: input.url,\n }))) ||\n $guard(_exceptionable, {\n path: _path + \".url\",\n expected: '(string & Format<\"url\">)',\n value: input.url,\n }));\n return (\n (((\"object\" === typeof input && null !== input) ||\n $guard(true, {\n path: _path + \"\",\n expected: \"IBbsArticle\",\n value: input,\n })) &&\n $ao0(input, _path + \"\", true)) ||\n $guard(true, {\n path: _path + \"\",\n expected: \"IBbsArticle\",\n value: input,\n })\n );\n })(input, \"$input\", true);\n return input;\n };\n const stringify = (input) => {\n const $io1 = (input) =>\n (null === input.name ||\n (\"string\" === typeof input.name &&\n 1 <= input.name.length &&\n input.name.length <= 255)) &&\n (null === input.extension ||\n (\"string\" === typeof input.extension &&\n 1 <= input.extension.length &&\n input.extension.length <= 8)) &&\n \"string\" === typeof input.url &&\n /^(?:https?|ftp):\\/\\/(?:\\S+(?::\\S*)?@)?(?:(?!(?:10|127)(?:\\.\\d{1,3}){3})(?!(?:169\\.254|192\\.168)(?:\\.\\d{1,3}){2})(?!172\\.(?:1[6-9]|2\\d|3[0-1])(?:\\.\\d{1,3}){2})(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|(?:(?:[a-z0-9\\u{00a1}-\\u{ffff}]+-)*[a-z0-9\\u{00a1}-\\u{ffff}]+)(?:\\.(?:[a-z0-9\\u{00a1}-\\u{ffff}]+-)*[a-z0-9\\u{00a1}-\\u{ffff}]+)*(?:\\.(?:[a-z\\u{00a1}-\\u{ffff}]{2,})))(?::\\d{2,5})?(?:\\/[^\\s]*)?$/iu.test(\n input.url,\n );\n const $string = core_1.TypedRoute.Post.string;\n const $so0 = (input) =>\n `{\"id\":${$string(input.id)},\"created_at\":${$string(\n input.created_at,\n )},\"title\":${$string(input.title)},\"body\":${$string(\n input.body,\n )},\"files\":${`[${input.files\n .map((elem) => $so1(elem))\n .join(\",\")}]`}}`;\n const $so1 = (input) =>\n `{\"name\":${\n null !== input.name ? $string(input.name) : \"null\"\n },\"extension\":${\n null !== input.extension ? $string(input.extension) : \"null\"\n },\"url\":${$string(input.url)}}`;\n return $so0(input);\n };\n return stringify(assert(input));\n },\n }), // 200x faster JSON serialization\n __param(\n 0,\n (0, core_1.TypedBody)({\n type: \"assert\",\n assert: (input) => {\n const __is = (input) => {\n const $io0 = (input) =>\n \"string\" === typeof input.title &&\n 3 <= input.title.length &&\n input.title.length <= 50 &&\n \"string\" === typeof input.body &&\n Array.isArray(input.files) &&\n input.files.every(\n (elem) =>\n \"object\" === typeof elem && null !== elem && $io1(elem),\n );\n const $io1 = (input) =>\n (null === input.name ||\n (\"string\" === typeof input.name &&\n 1 <= input.name.length &&\n input.name.length <= 255)) &&\n (null === input.extension ||\n (\"string\" === typeof input.extension &&\n 1 <= input.extension.length &&\n input.extension.length <= 8)) &&\n \"string\" === typeof input.url &&\n /^(?:https?|ftp):\\/\\/(?:\\S+(?::\\S*)?@)?(?:(?!(?:10|127)(?:\\.\\d{1,3}){3})(?!(?:169\\.254|192\\.168)(?:\\.\\d{1,3}){2})(?!172\\.(?:1[6-9]|2\\d|3[0-1])(?:\\.\\d{1,3}){2})(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|(?:(?:[a-z0-9\\u{00a1}-\\u{ffff}]+-)*[a-z0-9\\u{00a1}-\\u{ffff}]+)(?:\\.(?:[a-z0-9\\u{00a1}-\\u{ffff}]+-)*[a-z0-9\\u{00a1}-\\u{ffff}]+)*(?:\\.(?:[a-z\\u{00a1}-\\u{ffff}]{2,})))(?::\\d{2,5})?(?:\\/[^\\s]*)?$/iu.test(\n input.url,\n );\n return \"object\" === typeof input && null !== input && $io0(input);\n };\n if (false === __is(input))\n ((input, _path, _exceptionable = true) => {\n const $guard = core_1.TypedBody.guard;\n const $ao0 = (input, _path, _exceptionable = true) =>\n ((\"string\" === typeof input.title &&\n (3 <= input.title.length ||\n $guard(_exceptionable, {\n path: _path + \".title\",\n expected: \"string & MinLength<3>\",\n value: input.title,\n })) &&\n (input.title.length <= 50 ||\n $guard(_exceptionable, {\n path: _path + \".title\",\n expected: \"string & MaxLength<50>\",\n value: input.title,\n }))) ||\n $guard(_exceptionable, {\n path: _path + \".title\",\n expected: \"(string & MinLength<3> & MaxLength<50>)\",\n value: input.title,\n })) &&\n (\"string\" === typeof input.body ||\n $guard(_exceptionable, {\n path: _path + \".body\",\n expected: \"string\",\n value: input.body,\n })) &&\n (((Array.isArray(input.files) ||\n $guard(_exceptionable, {\n path: _path + \".files\",\n expected: \"Array\",\n value: input.files,\n })) &&\n input.files.every(\n (elem, _index1) =>\n (((\"object\" === typeof elem && null !== elem) ||\n $guard(_exceptionable, {\n path: _path + \".files[\" + _index1 + \"]\",\n expected: \"IAttachmentFile\",\n value: elem,\n })) &&\n $ao1(\n elem,\n _path + \".files[\" + _index1 + \"]\",\n true && _exceptionable,\n )) ||\n $guard(_exceptionable, {\n path: _path + \".files[\" + _index1 + \"]\",\n expected: \"IAttachmentFile\",\n value: elem,\n }),\n )) ||\n $guard(_exceptionable, {\n path: _path + \".files\",\n expected: \"Array\",\n value: input.files,\n }));\n const $ao1 = (input, _path, _exceptionable = true) =>\n (null === input.name ||\n (\"string\" === typeof input.name &&\n (1 <= input.name.length ||\n $guard(_exceptionable, {\n path: _path + \".name\",\n expected: \"string & MinLength<1>\",\n value: input.name,\n })) &&\n (input.name.length <= 255 ||\n $guard(_exceptionable, {\n path: _path + \".name\",\n expected: \"string & MaxLength<255>\",\n value: input.name,\n }))) ||\n $guard(_exceptionable, {\n path: _path + \".name\",\n expected:\n \"((string & MinLength<1> & MaxLength<255>) | null)\",\n value: input.name,\n })) &&\n (null === input.extension ||\n (\"string\" === typeof input.extension &&\n (1 <= input.extension.length ||\n $guard(_exceptionable, {\n path: _path + \".extension\",\n expected: \"string & MinLength<1>\",\n value: input.extension,\n })) &&\n (input.extension.length <= 8 ||\n $guard(_exceptionable, {\n path: _path + \".extension\",\n expected: \"string & MaxLength<8>\",\n value: input.extension,\n }))) ||\n $guard(_exceptionable, {\n path: _path + \".extension\",\n expected: \"((string & MinLength<1> & MaxLength<8>) | null)\",\n value: input.extension,\n })) &&\n ((\"string\" === typeof input.url &&\n (/^(?:https?|ftp):\\/\\/(?:\\S+(?::\\S*)?@)?(?:(?!(?:10|127)(?:\\.\\d{1,3}){3})(?!(?:169\\.254|192\\.168)(?:\\.\\d{1,3}){2})(?!172\\.(?:1[6-9]|2\\d|3[0-1])(?:\\.\\d{1,3}){2})(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|(?:(?:[a-z0-9\\u{00a1}-\\u{ffff}]+-)*[a-z0-9\\u{00a1}-\\u{ffff}]+)(?:\\.(?:[a-z0-9\\u{00a1}-\\u{ffff}]+-)*[a-z0-9\\u{00a1}-\\u{ffff}]+)*(?:\\.(?:[a-z\\u{00a1}-\\u{ffff}]{2,})))(?::\\d{2,5})?(?:\\/[^\\s]*)?$/iu.test(\n input.url,\n ) ||\n $guard(_exceptionable, {\n path: _path + \".url\",\n expected: 'string & Format<\"url\">',\n value: input.url,\n }))) ||\n $guard(_exceptionable, {\n path: _path + \".url\",\n expected: '(string & Format<\"url\">)',\n value: input.url,\n }));\n return (\n (((\"object\" === typeof input && null !== input) ||\n $guard(true, {\n path: _path + \"\",\n expected: \"IBbsArticle.ICreate\",\n value: input,\n })) &&\n $ao0(input, _path + \"\", true)) ||\n $guard(true, {\n path: _path + \"\",\n expected: \"IBbsArticle.ICreate\",\n value: input,\n })\n );\n })(input, \"$input\", true);\n return input;\n },\n }),\n ),\n __metadata(\"design:type\", Function),\n __metadata(\"design:paramtypes\", [Object]),\n __metadata(\"design:returntype\", Promise),\n ],\n BbsArticlesController.prototype,\n \"store\",\n null,\n);\nexports.BbsArticlesController = BbsArticlesController = __decorate(\n [(0, common_1.Controller)(\"bbs/articles\")],\n BbsArticlesController,\n);\n//# sourceMappingURL=BbsArticlesController.js.map\nMeasured on AMD R9-7940HS, Rog Flow X13"}},"/docs/sdk/sdk":{"title":"Sdk","data":{"outline#Outline":"import { INestiaConfig } from \"@nestia/sdk\";\nimport { NestFactory } from \"@nestjs/core\";\n// import { FastifyAdapter } from \"@nestjs/platform-fastify\";\nimport { YourModule } from \"./src/YourModule\";\nconst NESTIA_CONFIG: INestiaConfig = {\n input: async () => {\n const app = await NestFactory.create(YourModule);\n // const app = await NestFactory.create(YourModule, new FastifyAdapter());\n // app.setGlobalPrefix(\"api\");\n // app.enableVersioning({\n // type: VersioningType.URI,\n // prefix: \"v\",\n // })\n return app;\n },\n output: \"src/api\",\n distribute: \"packages/api\",\n};\nexport default NESTIA_CONFIG;\nnpx nestia sdk\n/**\n * Update an article.\n *\n * @param section Section code\n * @param id Target article ID\n * @param input Content to update\n * @returns Updated content\n *\n * @controller BbsArticlesController.update()\n * @path PUT /bbs/:section/articles/:id\n * @nestia Generated by Nestia - https://github.com/samchon/nestia\n */\nexport async function update(\n connection: IConnection,\n section: string,\n id: string & Format<\"uuid\">,\n input: update.Input,\n): Promise {\n return PlainFetcher.fetch(\n {\n ...connection,\n headers: {\n ...connection.headers,\n \"Content-Type\": \"application/json\",\n },\n },\n {\n ...update.METADATA,\n path: update.path(section, id),\n } as const,\n input,\n );\n}\nexport namespace update {\n export type Input = Primitive;\n export type Output = Primitive;\n export const METADATA = {\n method: \"PUT\",\n path: \"/bbs/articles/:section/:id\",\n request: {\n type: \"application/json\",\n encrypted: false,\n },\n response: {\n type: \"application/json\",\n encrypted: false,\n },\n status: null,\n } as const;\n export const path = (\n section: string,\n id: string & Format<\"uuid\">,\n ): string => {\n return `/bbs/articles/${encodeURIComponent(\n section ?? \"null\",\n )}/${encodeURIComponent(id ?? \"null\")}`;\n };\n}\nCollection of typed fetch functions with DTO structures.Configure nestia.config.ts file and run npx nestia sdk command.Then, @nestia/sdk will analyze your NestJS backend server code, and generate SDK (Software Development Kit) library composed with. The newly generated SDK library would be composed with DTO and fetch functions with type definitions following your NestJS server.With the SDK library composed with RPC (Remote Procedure Call) functions, you can easily develop e2e test program. Also, frontend developers can utilize the SDK library to interact with your NestJS backend server, much safely and conveniently.If you can't imagine how the SDK library works, then look at the gif image of below. Left side is the NestJS backend server program, and right side is the Frontend program interacting with your server. Isn't it look like much more convenient and safer than before Swagger Documents case?\nLeft is NestJS server code, and right is client (frontend) code utilizing SDK","configuration#Configuration":"","application-module#Application Module":"import { INestiaConfig } from \"@nestia/sdk\";\nimport { NestFactory } from \"@nestjs/core\";\n// import { FastifyAdapter } from \"@nestjs/platform-fastify\";\nimport { YourModule } from \"./src/YourModule\";\nconst NESTIA_CONFIG: INestiaConfig = {\n input: async () => {\n const app = await NestFactory.create(YourModule);\n // const app = await NestFactory.create(YourModule, new FastifyAdapter());\n // app.setGlobalPrefix(\"api\");\n // app.enableVersioning({\n // type: VersioningType.URI,\n // prefix: \"v\",\n // })\n return app;\n },\n output: \"src/api\",\n distribute: \"packages/api\",\n};\nexport default NESTIA_CONFIG;\nimport type { INestApplication } from \"@nestjs/common\";\nimport type { OpenApi } from \"@samchon/openapi\";\n/**\n * Definition for the `nestia.config.ts` file.\n *\n * @author Jeongho Nam - https://github.com/samchon\n */\nexport interface INestiaConfig {\n /**\n * Accessor of controller classes.\n *\n * You can specify it within two ways\n *\n * - Asynchronous function returning `INestApplication` instance\n * - Specify the path or directory of controller class files\n */\n input:\n | (() => Promise)\n | INestiaConfig.IInput\n | string[]\n | string;\n /**\n * Output directory that SDK would be placed in.\n *\n * If not configured, you can't build the SDK library.\n */\n output?: string;\n /**\n * Building `swagger.json` is also possible.\n *\n * If not specified, you can't build the `swagger.json`.\n */\n swagger?: INestiaConfig.ISwaggerConfig;\n /**\n * Target directory that SDK distribution files would be placed in.\n *\n * If you configure this property and runs `npx nestia sdk` command,\n * distribution environments for the SDK library would be generated.\n *\n * After the SDK library generation, move to the `distribute` directory,\n * and runs `npm publish` command, then you can share SDK library with\n * other client (frontend) developers.\n *\n * Recommend to use `\"packages/api\"` value.\n */\n distribute?: string;\n /**\n * Allow simulation mode.\n *\n * If you configure this property to be `true`, the SDK library would be contain\n * simulation mode. In the simulation mode, the SDK library would not communicate\n * with the real backend server, but just returns random mock-up data\n * with requestion data validation.\n *\n * For reference, random mock-up data would be generated by `typia.random()`\n * function.\n *\n * @default false\n */\n simulate?: boolean;\n /**\n * Target directory that e2e test functions would be placed in.\n *\n * If you configure this property and runs `npx nestia e2e` command,\n * `@nestia/sdk` will analyze your NestJS backend server code, and\n * generates e2e test functions for every API endpoints.\n *\n * If not configured, you can't run `npx nestia e2e` command.\n */\n e2e?: string;\n /**\n * Whether to use propagation mode or not.\n *\n * If being configured, interaction functions of the SDK library would\n * perform the propagation mode. The propagation mode means that never\n * throwing exception even when status code is not 200 (or 201), but just\n * returning the {@link IPropagation} typed instance, which can specify its body\n * type through discriminated union determined by status code.\n *\n * @default false\n */\n propagate?: boolean;\n /**\n * Whether to clone DTO structures or not.\n *\n * If being configured, all of DTOs used in the backend server would be cloned\n * into the `structures` directory, and the SDK library would be refer to the\n * cloned DTOs instead of the original.\n *\n * @default false\n */\n clone?: boolean;\n /**\n * Whether to wrap DTO by primitive type.\n *\n * If you don't configure this property as `false`, all of DTOs in the\n * SDK library would be automatically wrapped by {@link Primitive} type.\n *\n * For refenrece, if a DTO type be capsuled by the {@link Primitive} type,\n * all of methods in the DTO type would be automatically erased. Also, if\n * the DTO has a `toJSON()` method, the DTO type would be automatically\n * converted to return type of the `toJSON()` method.\n *\n * @default true\n */\n primitive?: boolean;\n /**\n * Whether to assert parameter types or not.\n *\n * If you configure this property to be `true`, all of the function\n * parameters of SDK library would be checked through\n * [`typia.assert()` function](https://typia.io/docs/validators/assert/).\n *\n * This option would make your SDK library compilation time a little bit slower,\n * but would enahcne the type safety even in the runtime level.\n *\n * @default false\n */\n assert?: boolean;\n /**\n * Whether to optimize JSON string conversion 10x faster or not.\n *\n * If you configure this property to be `true`, the SDK library would utilize the\n * [`typia.assertStringify() function`](https://github.com/samchon/typia#enhanced-json)\n * to boost up JSON serialization speed and ensure type safety.\n *\n * This option would make your SDK library compilation time a little bit slower,\n * but would enhance JSON serialization speed 10x faster. Also, it can ensure type\n * safety even in the rumtime level.\n *\n * @default false\n */\n json?: boolean;\n}\nexport namespace INestiaConfig {\n /**\n * List of files or directories to include or exclude to specifying the NestJS\n * controllers.\n */\n export interface IInput {\n /**\n * List of files or directories containing the NestJS controller classes.\n */\n include: string[];\n /**\n * List of files or directories to be excluded.\n */\n exclude?: string[];\n }\n /**\n * Building `swagger.json` is also possible.\n */\n export interface ISwaggerConfig {\n /**\n * Output path of the `swagger.json`.\n *\n * If you've configured only directory, the file name would be the `swagger.json`.\n * Otherwise you've configured the full path with file name and extension, the\n * `swagger.json` file would be renamed to it.\n */\n output: string;\n /**\n * OpenAPI version.\n *\n * If you configure this property to be `2.0` or `3.0`, the newly generated\n * `swagger.json` file would follow the specified OpenAPI version. The newly\n * generated `swagger.json` file would be downgraded from the OpenAPI v3.1\n * specification by {@link OpenApi.downgrade} method.\n *\n * @default 3.1\n */\n openapi?: \"2.0\" | \"3.0\" | \"3.1\";\n /**\n * API information.\n *\n * If omitted, `package.json` content would be used instead.\n */\n info?: Partial;\n /**\n * List of server addresses.\n */\n servers?: OpenApi.IServer[];\n /**\n * Security schemes.\n *\n * When generating `swagger.json` file through `nestia`, if your controllers or\n * theirs methods have a security key which is not enrolled in here property,\n * it would be an error.\n */\n security?: Record;\n /**\n * List of tag names with description.\n *\n * It is possible to omit this property or skip some tag name even if\n * the tag name is used in the API routes. In that case, the tag name\n * would be used without description.\n *\n * Of course, if you've written a comment like `@tag {name} {descrition}`,\n * you can entirely replace this property specification.\n */\n tags?: OpenApi.IDocument.ITag[];\n /**\n * Decompose query DTO.\n *\n * If you configure this property to be `true`, the query DTO would be decomposed\n * into individual query parameters per each property. Otherwise you set it to be\n * `false`, the query DTO would be one object type which contains all of query\n * parameters.\n *\n * @default false\n */\n decompose?: boolean;\n /**\n * Operation ID generator.\n *\n * @param props Properties of the API endpoint.\n * @returns Operation ID.\n */\n operationId?(props: {\n class: string;\n function: string;\n method: \"HEAD\" | \"GET\" | \"POST\" | \"PUT\" | \"PATCH\" | \"DELETE\";\n path: string;\n }): string;\n }\n}\nMake nestia.config.ts file and run npx nestia sdk command.At first, create nestia.config.ts file through npx nestia init command. It would be placed on the top level directory of your NestJS backend project. For reference, tsconfig.json file also must be placed in the top level directory, too. After creation, configure the nestia.config.ts file referencing above example code and type definition.At least, you've to configure those two properties:\ninput: Accessor of controller classes\noutput: Path of output directory for SDK library\nWhen you've completed above configuration, just run npx nestia sdk command. Then, SDK library would be newly generated, and placed into the $config.output directory following your nestia.config.ts configuration.","clone-mode#Clone Mode":"import { INestiaConfig } from \"@nestia/sdk\";\nimport { NestFactory } from \"@nestjs/core\";\n// import { FastifyAdapter } from \"@nestjs/platform-fastify\";\n \nimport { YourModule } from \"./src/YourModule\";\n \nconst NESTIA_CONFIG: INestiaConfig = {\n input: async () => {\n const app = await NestFactory.create(YourModule);\n // const app = await NestFactory.create(YourModule, new FastifyAdapter());\n // app.setGlobalPrefix(\"api\");\n // app.enableVersioning({\n // type: VersioningType.URI,\n // prefix: \"v\",\n // })\n return app;\n },\n output: \"src/api\",\n clone: true,\n distribute: \"packages/api\",\n};\nexport default NESTIA_CONFIG;\nimport core from \"@nestia/core\";\nimport { Controller } from \"@nestjs/common\";\nimport typia, { tags } from \"typia\";\n@Controller(\"bbs/articles/:section\")\nexport class TypedBodyControlleer {\n @core.TypedRoute.Get()\n public async index(\n @core.TypedParam(\"section\") section: string,\n @core.TypedQuery() query: IPage.IRequest,\n ): Promise> {\n const limit: number = query.limit ?? 100;\n const current: number = query.page ?? 1;\n const records: number = limit * (current + 3) + 5;\n return {\n pagination: {\n current,\n limit,\n records,\n pages: Math.ceil(records / limit),\n },\n data: new Array(limit).fill(\"\").map(() => ({\n ...typia.random(),\n section,\n })),\n };\n }\n}\ninterface IPage {\n data: T[];\n pagination: IPage.IPagination;\n}\nnamespace IPage {\n export interface IRequest {\n page?: null | (number & tags.Type<\"uint32\">);\n limit?: null | (number & tags.Type<\"uint32\">);\n }\n export interface IPagination {\n current: number & tags.Type<\"uint32\">;\n limit: number & tags.Type<\"uint32\">;\n records: number & tags.Type<\"uint32\">;\n pages: number & tags.Type<\"uint32\">;\n }\n}\nnamespace IBbsArticle {\n export interface ISummary {\n id: string & tags.Format<\"uuid\">;\n section: string;\n writer: string;\n title: string & tags.MinLength<3> & tags.MaxLength<50>;\n created_at: string & tags.Format<\"date-time\">;\n /**\n * @format date-time\n */\n updated_at: string;\n }\n}\nexport namespace IPageIBbsArticle {\n export type ISummary = {\n data: IBbsArticle.ISummary[];\n pagination: IPage.IPagination;\n };\n}\nexport namespace IPage {\n export type IRequest = {\n page?: null | undefined | (number & Type<\"uint32\">);\n limit?: null | undefined | (number & Type<\"uint32\">);\n };\n export type IPagination = {\n current: number & Type<\"uint32\">;\n limit: number & Type<\"uint32\">;\n records: number & Type<\"uint32\">;\n pages: number & Type<\"uint32\">;\n };\n}\nexport namespace IBbsArticle {\n export type ISummary = {\n id: string & Format<\"uuid\">;\n section: string;\n writer: string;\n title: string & MinLength<3> & MaxLength<50>;\n created_at: string & Format<\"date-time\">;\n updated_at: string & Format<\"date-time\">;\n };\n}\nIf you configure clone property to be true in the nestia.config.ts file, all of DTO structures used in the backend server would be cloned into the structures directory, and the SDK library would be refer to the cloned DTO structures instead of the original.This clone mode is useful when you'd not separated DTO structures from the ORM models. When you're using TypeORM or Prisma, and returning the ORM generated instance directly in the controller without independent DTO structure definition, your SDK library requires the TypeORM or Prisma dependency install. By the dependency, client (frontend) developers may install the ORM library that they never need.In that case, it would better to remove the dependency by using this clone mode.","propagation-mode#Propagation Mode":"import { INestiaConfig } from \"@nestia/sdk\";\nimport { NestFactory } from \"@nestjs/core\";\n// import { FastifyAdapter } from \"@nestjs/platform-fastify\";\nimport { YourModule } from \"./src/YourModule\";\nconst config: INestiaConfig = {\n input: async () => {\n const app = await NestFactory.create(YourModule);\n // const app = await NestFactory.create(YourModule, new FastifyAdapter());\n // app.setGlobalPrefix(\"api\");\n // app.enableVersioning({\n // type: VersioningType.URI,\n // prefix: \"v\",\n // })\n return app;\n },\n output: \"src/api\",\n propagate: true,\n distribute: \"packages/api\",\n};\nexport default config;\nimport { Primitive } from \"./Primitive\";\n/**\n * Propagation type.\n *\n * `IPropagation` is a type gathering all possible status codes and their body\n * data types as a discriminated union type. You can specify the status code and\n * its body data type just by using conditional statement like below.\n *\n * ```typescript\n * type Output = IPropagation<{\n * 200: ISeller.IAuthorized;\n * 400: TypeGuardError.IProps;\n * >};\n *\n * const output: Output = await sdk.sellers.authenticate.join(input);\n * if (output.success) {\n * // automatically casted to \"ISeller.IAuthorized\" type\n * const authorized: ISeller.IAuthorized = output.data;\n * } else if (output.status === 400) {\n * // automatically casted to \"TypeGuardError.IProps\" type\n * const error: TypeGuardError.IProps = output.data;\n * } else {\n * // unknown type when out of pre-defined status codes\n * const result: unknown = output.data;\n * }\n * ```\n *\n * For reference, this `IPropagation` type is utilized by SDK library generated by\n * `@nestia/sdk`, when you've configured {@link INestiaConfig.propagate} to be `true`.\n * In that case, SDK functions generated by `@nestia/sdk` no more returns response DTO\n * typed data directly, but returns this `IPropagation` typed object instead.\n *\n * @template StatusMap Map of status code and its body data type.\n * @template Success Default success status code.\n * @author Jeongho Nam - https://github.com/samchon\n */\nexport type IPropagation<\n StatusMap extends {\n [P in IPropagation.Status]?: any;\n },\n Success extends number = 200 | 201,\n> =\n | {\n [P in keyof StatusMap]: IPropagation.IBranch<\n P extends Success ? true : false,\n P,\n StatusMap[P]\n >;\n }[keyof StatusMap]\n | IPropagation.IBranch;\nexport namespace IPropagation {\n /**\n * Type of configurable status codes.\n *\n * The special characters like `2XX`, `3XX`, `4XX`, `5XX` are meaning the range\n * of status codes. If `5XX` is specified, it means the status code is in the\n * range of `500` to `599`.\n */\n export type Status = number | \"2XX\" | \"3XX\" | \"4XX\" | \"5XX\";\n /**\n * Branch type of propagation.\n *\n * `IPropagation.IBranch` is a branch type composing `IPropagation` type,\n * which is gathering all possible status codes and their body data types\n * as a union type.\n */\n export interface IBranch {\n success: Success;\n status: StatusValue extends \"2XX\" | \"3XX\" | \"4XX\" | \"5XX\"\n ? StatusRange\n : StatusValue extends number\n ? StatusValue\n : never;\n data: Primitive;\n headers: Record;\n }\n /**\n * Range of status codes by the first digit.\n */\n export type StatusRange = T extends 0\n ? IntRange<200, 299>\n : T extends 3\n ? IntRange<300, 399>\n : T extends 4\n ? IntRange<400, 499>\n : IntRange<500, 599>;\n type IntRange = Exclude<\n Enumerate,\n Enumerate\n >;\n type Enumerate<\n N extends number,\n Acc extends number[] = [],\n > = Acc[\"length\"] extends N\n ? Acc[number]\n : Enumerate;\n}\nReturns IPropagation typed instance instead of throwing exception.When you configure propagate property of nestia.config.ts file, all of SDK functions generated by @nestia/sdk will perform propagation mode. The propagation mode means that never throwing exception (HttpError) even when response status code is not 200 (or 201), but just returning the IPropagation typed object, which can specify its body data type through discriminated union determined by status code.Looking at below code tabs one by one, then you may understand exactly, what the propagation mode is. As you can see from below example code, @TypedException() decorator function can be utilized to define the failure type with specific status code. Also, if returned status code is out of pre-defined, the IPropagation.data type would be automatically casted to unknown type.\nimport core from \"@nestia/core\";\nimport { Controller } from \"@nestjs/common\";\nimport typia, { tags } from \"typia\";\nimport { IBbsArticle } from \"@api/lib/structures/IBbsArticle\";\n@Controller(\"bbs/articles/:section\")\nexport class BbsArticlesController {\n /**\n * Update an article.\n *\n * @param section Section code\n * @param id Target article ID\n * @param input Content to update\n * @returns Updated content\n */\n @core.TypedException(400)\n @core.TypedRoute.Put(\":id\")\n public async update(\n @core.TypedParam(\"section\") section: string,\n @core.TypedParam(\"id\") id: string & tags.Format<\"uuid\">,\n @core.TypedBody() input: IBbsArticle.IStore,\n ): Promise {\n return {\n ...typia.random(),\n id,\n section,\n ...input,\n };\n }\n}\n/**\n * Update an article.\n *\n * @param section Section code\n * @param id Target article ID\n * @param input Content to update\n * @returns Updated content\n *\n * @controller BbsArticlesController.update()\n * @path PUT /bbs/:section/articles/:id\n * @nestia Generated by Nestia - https://github.com/samchon/nestia\n */\nexport async function update(\n connection: IConnection,\n section: string,\n id: string & Format<\"uuid\">,\n input: update.Input,\n): Promise {\n return PlainFetcher.propagate(\n {\n ...connection,\n headers: {\n ...connection.headers,\n \"Content-Type\": \"application/json\",\n },\n },\n {\n ...update.METADATA,\n path: update.path(section, id),\n } as const,\n input,\n );\n}\nexport namespace update {\n export type Input = Primitive;\n export type Output = IPropagation<\n {\n 200: IBbsArticle;\n 400: TypeGuardError.IProps;\n },\n 200\n >;\n export const METADATA = {\n method: \"PUT\",\n path: \"/bbs/articles/:section/:id\",\n request: {\n type: \"application/json\",\n encrypted: false,\n },\n response: {\n type: \"application/json\",\n encrypted: false,\n },\n status: null,\n } as const;\n export const path = (\n section: string,\n id: string & Format<\"uuid\">,\n ): string => {\n return `/bbs/articles/${encodeURIComponent(\n section ?? \"null\",\n )}/${encodeURIComponent(id ?? \"null\")}`;\n };\n}\nconst output = await api.functional.bbs.articles.update(section, id, input);\nif (output.success) {\n // automatically casted to \"IBbsArticle\" when status 200 (success)\n const article: IBbsArticle = output.data;\n} else if (output.status === 400) {\n // casted to \"TypeGuardError.IProps\" when status 400\n const error: TypeGuardError.IProps = output.data;\n} else {\n // casted to \"unknown\" when out of pre-defined status codes\n const result: unknown = output.data;\n}","cli-arguments#CLI Arguments":"npx nestia sdk\nnpx nestia sdk --config nestia2.config.ts\nnpx nestia sdk --project tsconfig2.json\nnpx nestia sdk --config nestia3.config.ts --project tsconfig3.tsconfig.json\nIf you have a special configuration file that its file name is not nestia.config.ts or the configuration file is not placed on the root directory of the project, you can specify it with --config option like npx nestia sdk --config another.config.ts.Also, if you have a special tsconfig.json file or the project file is not located in the root directory of the project, you can specify it with --project argument like npx nestia sdk --project another.tsconfig.json, too.","comment-tags#Comment Tags":"","hiding#Hiding":"If you want to hide some API endpoints from the SDK library, write a comment tag to the controller method.\n@deprepcated: warning from IDE\n@internal: hide from d.ts files\n@ignore: actually ignore, so that not even generated\nAt first, @deprecated does not hide target API from the SDK library, but just mark as deprecated. In that case, IDE will warn to the SDK users. In such reason, @deprecated comment tag is useful for legacy API endpoints that would be removed in the future.The second @internal tag also does not hide from the SDK library, but it would be disappeared from d.ts files, so that client developers can't identify the API. Therefore, @internal tag is useful for some API endpoints that would be used only in the backend server for testing or debugging purpose.The last @ignore tag is the most powerful one. If you write @ignore tag to the controller method, the SDK library would never generate the API endpoint at all. In that case, client developers can't use the API endpoint even if they know the API endpoint path and method. It is useful for some endpoints that are not supported in the SDK library.\nimport { Controller } from \"@nestjs/common\";\nimport { IBbsArticle } from \"@api/lib/structures/IBbsArticle\";\n@Controller(\"bbs/articles\")\nexport class BbsArticlesController {\n /**\n * Store an article.\n *\n * @param input Content to store\n * @returns Newly archived article\n * @deprecated\n */\n public async create(\n @TypedBody() input: IBbsArticle.IStore,\n ): Promise {\n ...\n }\n /**\n * @internal\n */\n public async update(\n @TypedParam(\"id\") id: string & tags.Format<\"uuid\">,\n @TypedBody() input: IBbsArticle.IUpdate,\n ): Promise {\n ...\n }\n /**\n * @ignore\n */\n public async erase(\n @TypedParam(\"id\") id: string & tags.Format<\"uuid\">,\n ): Promise {\n ...\n }\n}\n/**\n * @packageDocumentation\n * @module api.functional.bbs.articles\n * @nestia Generated by Nestia - https://github.com/samchon/nestia\n */\n//================================================================\nimport type { IConnection, Primitive } from \"@nestia/fetcher\";\nimport { PlainFetcher } from \"@nestia/fetcher/lib/PlainFetcher\";\nimport type { Format } from \"typia/lib/tags/Format\";\nimport type { IBbsArticle } from \"../../../structures/IBbsArticle\";\n/**\n * Store an article.\n *\n * @param input Content to store\n * @returns Newly archived article\n * @deprecated\n *\n * @controller BbsArticlesController.store\n * @path POST /bbs/articles\n * @nestia Generated by Nestia - https://github.com/samchon/nestia\n */\nexport async function store(\n connection: IConnection,\n input: store.Input,\n): Promise {\n return PlainFetcher.fetch(\n {\n ...connection,\n headers: {\n ...connection.headers,\n \"Content-Type\": \"application/json\",\n },\n },\n {\n ...store.METADATA,\n path: store.path(),\n } as const,\n input,\n );\n}\nexport namespace store {\n export type Input = Primitive;\n export type Output = Primitive;\n export const METADATA = {\n method: \"POST\",\n path: \"/bbs/articles\",\n request: {\n type: \"application/json\",\n encrypted: false,\n },\n response: {\n type: \"application/json\",\n encrypted: false,\n },\n status: null,\n } as const;\n export const path = (): string => {\n return `/bbs/articles`;\n };\n}\n/**\n *\n * @internal\n *\n * @controller BbsArticlesController.update\n * @path PUT /bbs/articles/:id\n * @nestia Generated by Nestia - https://github.com/samchon/nestia\n */\nexport async function update(\n connection: IConnection,\n id: string & Format<\"uuid\">,\n input: update.Input,\n): Promise {\n return PlainFetcher.fetch(\n {\n ...connection,\n headers: {\n ...connection.headers,\n \"Content-Type\": \"application/json\",\n },\n },\n {\n ...update.METADATA,\n path: update.path(id),\n } as const,\n input,\n );\n}\nexport namespace update {\n export type Input = Primitive>;\n export const METADATA = {\n method: \"PUT\",\n path: \"/bbs/articles/:id\",\n request: {\n type: \"application/json\",\n encrypted: false,\n },\n response: {\n type: \"application/json\",\n encrypted: false,\n },\n status: null,\n } as const;\n export const path = (id: string & Format<\"uuid\">): string => {\n return `/bbs/articles/${encodeURIComponent(id ?? \"null\")}`;\n };\n}","headers#Headers":"Also, SDK library of @nestia/sdk supports special comment tags configuring client headers.\n@setHeader\n@assignHeaders\nAt first, @setHeader {accessor} configures only one header property. It reads special value of response body data with the accessor, and configures the special value to client header with last accessor key. In the below example case, key of authorization.token would be token, and key of authorization.timeout would be timeout.The other one @assignHeaders overwrites every property values to the client headers, with special instance of response body data with accessor. In the below example case, every properties in IShoppingCustomer.IActivated[\"authorization\"] would be assigned to the clinet headers.If you're confused, read example codes of below, clicking each tabs.\nimport { Controller } from \"@nestjs/common\";\n@Controller(\"shoppings/consumers/authenticate\")\nexport class ShoppingConsumerAuthenticateController {\n /**\n * @setHeader authorization.token token\n * @setHeader authorization.timeout timeout\n */\n @TypedRoute.Post(\"join\")\n public join(\n @TypedBody() input: IShoppingConsumer.IJoin\n ): Promise;\n /**\n * @assignHeaders authorization\n */\n @TypedRoute.Post(\"login\")\n public login(\n @TypedBody() inpu: IShoppingConsumer.ILogin\n ): Promise;\n}\n/**\n * @packageDocumentation\n * @module api.functional.shoppings.consumers.authenticate\n * @nestia Generated by Nestia - https://github.com/samchon/nestia \n */\n//================================================================\nimport type { IConnection, Primitive } from \"@nestia/fetcher\";\nimport { PlainFetcher } from \"@nestia/fetcher/lib/PlainFetcher\";\nimport type { IShoppingConsumer } from \"../../../../structures/IShoppingConsumer\";\nexport async function join(\n connection: IConnection,\n input: IShoppingConsumer.IJoin,\n): Promise {\n const output: IShoppingConsumer.IActivated = await Fetcher.fetch(\n connection,\n join.ENCRYPTED,\n join.METHOD,\n join.path(),\n input,\n );\n // configure header(s)\n connection.headers ??= {};\n connection.headers.token = output.authorization.token;\n connection.headers.timeout = output.authorization.timeout;\n return output;\n}\nexport namespace join {\n ...\n}\nexport async function login(\n connection: IConnection,\n input: IShoppingConsumer.IJoin,\n): Promise {\n const output: IShoppingConsumer.IActivated = await Fetcher.fetch(\n connection,\n login.ENCRYPTED,\n login.METHOD,\n login.path(),\n input,\n );\n // configure header(s)\n connection.headers ??= {};\n Object.assign(connection.headers, output.authorization);\n return output;\n}\nexport namespace login {\n ...\n}","distribution#Distribution":"import { INestiaConfig } from \"@nestia/sdk\";\nimport { NestFactory } from \"@nestjs/core\";\n// import { FastifyAdapter } from \"@nestjs/platform-fastify\";\nimport { YourModule } from \"./src/YourModule\";\nconst NESTIA_CONFIG: INestiaConfig = {\n input: async () => {\n const app = await NestFactory.create(YourModule);\n // const app = await NestFactory.create(YourModule, new FastifyAdapter());\n // app.setGlobalPrefix(\"api\");\n // app.enableVersioning({\n // type: VersioningType.URI,\n // prefix: \"v\",\n // })\n return app;\n },\n output: \"src/api\",\n distribute: \"packages/api\",\n};\nexport default NESTIA_CONFIG;\nThe best to way to distributing SDK library is just publishing as an NPM module.Configure distribute property of nestia.config.ts file, and run npx nestia sdk command. After that, distribution environments would be automatically composed with SDK library generation. At last, move to the packages/api directory, and run npm run deploy command for publishing.From now on, client developers can use the SDK library just by using npm install command.\ncd packages/api\nnpm run deploy\nOf course, before publishing the NPM module, you've to customize some configurations like package name. Initial name of the distribution envirionments is @ORGANIZATION/PROJECT-api, but you must change the package name of yours, isn't it?Also, if your SDK library utilize special alias paths, you also need to customize tsconfig.json file, too. Reading below example package.json and tsconfig.json files generated by nestia, consider which features to customize.\n{\n \"name\": \"@ORGANIZATION/PROJECT-api\",\n \"version\": \"0.1.0\",\n \"description\": \"SDK library generated by Nestia\",\n \"main\": \"lib/index.js\",\n \"typings\": \"lib/index.d.ts\",\n \"scripts\": {\n \"build\": \"npm run build:sdk && npm run compile\",\n \"build:sdk\": \"rimraf ../../src/api/functional && cd ../.. && npx nestia sdk && cd packages/api\",\n \"compile\": \"rimraf lib && tsc\",\n \"deploy\": \"npm run build && npm publish\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/samchon/nestia\"\n },\n \"author\": \"Jeongho Nam\",\n \"license\": \"MIT\",\n \"bugs\": {\n \"url\": \"https://github.com/samchon/nestia/issues\"\n },\n \"homepage\": \"https://nestia.io\",\n \"devDependencies\": {\n \"rimraf\": \"^5.0.0\",\n \"typescript\": \"^5.4.2\",\n \"ts-patch\": \"^3.1.0\"\n },\n \"dependencies\": {\n \"@nestia/fetcher\": \"^3.4.1\",\n \"typia\": \"^6.4.0\"\n },\n \"files\": [\n \"lib\",\n \"package.json\",\n \"README.md\"\n ]\n}\n{\n \"compilerOptions\": {\n \"target\": \"ES5\",\n \"lib\": [\n \"DOM\",\n \"ES2015\"\n ],\n \"module\": \"commonjs\",\n \"declaration\": true,\n \"sourceMap\": true,\n \"outDir\": \"./lib\",\n \"downlevelIteration\": true,\n \"newLine\": \"lf\",\n \"esModuleInterop\": true,\n \"forceConsistentCasingInFileNames\": true,\n \"strict\": true,\n \"skipLibCheck\": true\n },\n \"include\": [\n \"../../src/api\"\n ]\n}"}},"/docs/sdk/simulator":{"title":"Simulator","data":{"outline#Outline":"import { INestiaConfig } from \"@nestia/sdk\";\nimport { NestFactory } from \"@nestjs/core\";\n// import { FastifyAdapter } from \"@nestjs/platform-fastify\";\nimport { YourModule } from \"./src/YourModule\";\nconst NESTIA_CONFIG: INestiaConfig = {\n input: async () => {\n const app = await NestFactory.create(YourModule);\n // const app = await NestFactory.create(YourModule, new FastifyAdapter());\n // app.setGlobalPrefix(\"api\");\n // app.enableVersioning({\n // type: VersioningType.URI,\n // prefix: \"v\",\n // })\n return app;\n },\n output: \"src/api\",\n simulate: true, // supports simulation mode\n distribute: \"packages/api\",\n e2e: \"test\",\n};\nexport default NESTIA_CONFIG;\nnpx nestia sdk\nnpx nestia sdk --config nestia.config.ts --project tsconfig.json\n/**\n * Update an article.\n *\n * @param section Section code\n * @param id Target article ID\n * @param input Content to update\n * @returns Updated content\n *\n * @controller BbsArticlesController.update()\n * @path PUT /bbs/:section/articles/:id\n * @nestia Generated by Nestia - https://github.com/samchon/nestia\n */\nexport async function update(\n connection: IConnection,\n section: string,\n id: string & Format<\"uuid\">,\n input: update.Input,\n): Promise {\n return !!connection.simulate\n ? update.simulate(connection, section, id, input)\n : PlainFetcher.fetch(\n {\n ...connection,\n headers: {\n ...connection.headers,\n \"Content-Type\": \"application/json\",\n },\n },\n {\n ...update.METADATA,\n path: update.path(section, id),\n } as const,\n input,\n );\n}\nexport namespace update {\n export type Input = Primitive;\n export type Output = Primitive;\n export const METADATA = {\n method: \"PUT\",\n path: \"/bbs/articles/:section/:id\",\n request: {\n type: \"application/json\",\n encrypted: false,\n },\n response: {\n type: \"application/json\",\n encrypted: false,\n },\n status: null,\n } as const;\n export const path = (\n section: string,\n id: string & Format<\"uuid\">,\n ): string => {\n return `/bbs/${encodeURIComponent(\n section ?? \"null\",\n )}/articles/${encodeURIComponent(id ?? \"null\")}`;\n };\n export const random = (g?: Partial): Output =>\n typia.random