nestia 0.4.1 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/FUNDING.yml +3 -0
- package/README.md +115 -43
- package/package.json +7 -5
- package/src/IConfiguration.ts +16 -0
- package/src/NestiaApplication.ts +13 -24
- package/src/analyses/SourceFinder.ts +59 -19
- package/src/bin/nestia.ts +63 -89
- package/src/bundle/HttpError.ts +1 -33
- package/src/bundle/IConnection.ts +1 -26
- package/src/bundle/Primitive.ts +1 -113
- package/src/bundle/__internal/AesPkcs5.ts +1 -51
- package/src/bundle/__internal/Fetcher.ts +1 -126
- package/src/executable/sdk.ts +89 -0
- package/src/generates/FileGenerator.ts +20 -14
- package/src/generates/FunctionGenerator.ts +5 -5
- package/src/utils/ImportDictionary.ts +5 -13
- package/tsconfig.json +2 -6
- package/src/utils/Terminal.ts +0 -19
package/README.md
CHANGED
|
@@ -7,8 +7,14 @@ Automatic SDK generator for the NestJS.
|
|
|
7
7
|
[](https://github.com/samchon/nestia/actions?query=workflow%3Abuild)
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
|
+
# INSTALL NESTIA
|
|
10
11
|
npm install --save-dev nestia
|
|
12
|
+
|
|
13
|
+
# WHEN ALL OF THE CONTROLLRES ARE GATHERED INTO A DIRECTORY
|
|
11
14
|
npx nestia sdk "src/controller" --out "src/api"
|
|
15
|
+
|
|
16
|
+
# REGULAR NESTJS PATTERN
|
|
17
|
+
npx nestia sdk "src/**/*.controller.ts" --out "src/api"
|
|
12
18
|
```
|
|
13
19
|
|
|
14
20
|
Don't write any `swagger` comment. Just deliver the SDK.
|
|
@@ -60,54 +66,69 @@ Just type the `npm install --save-dev nestia` command in your NestJS backend pro
|
|
|
60
66
|
```bash
|
|
61
67
|
npx nestia sdk <source_controller_directory> --out <output_sdk_directory>
|
|
62
68
|
|
|
69
|
+
npx nestia sdk "src/**/*.controller.ts" --out "src/api"
|
|
63
70
|
npx nestia sdk "src/controllers" --out "src/api"
|
|
64
|
-
npx nestia sdk "src/controllers/consumers" "src/controllers/sellers" --out "src/api
|
|
71
|
+
npx nestia sdk "src/controllers/consumers" "src/controllers/sellers" --out "src/api"
|
|
72
|
+
npx nestia sdk "src/controllers" --exclude "src/**/Fake*.ts" --out "src/api"
|
|
65
73
|
```
|
|
66
74
|
|
|
67
75
|
To generate a SDK library through the **Nestia** is very easy.
|
|
68
76
|
|
|
69
|
-
Just type the `nestia sdk <input> --out <output>` command in the console.
|
|
77
|
+
Just type the `nestia sdk <input> --out <output>` command in the console. When there're multiple source directories containing the NestJS controller classes, type all of them separating by a `space` word. If you want to exclude some directories or files from the SDK generation, the `--exclude` option would be useful.
|
|
70
78
|
|
|
71
79
|
Also, when generating a SDK using the cli options, `compilerOptions` would follow the `tsconfig.json`, that is configured for the backend server. If no `tsconfig.json` file exists in your project, the configuration would be default option (`ES5` with `strict` mode). If you want to use different `compilerOptions` with the `tsconfig.json`, you should configure the [nestia.config.ts](#nestiaconfigts).
|
|
72
80
|
|
|
73
|
-
```bash
|
|
74
|
-
npx nestia install
|
|
75
|
-
```
|
|
76
|
-
|
|
77
81
|
### Dependencies
|
|
78
|
-
SDK library generated by the **Nestia**
|
|
79
|
-
|
|
80
|
-
When you type the `nestia install` command in the console, those dependencies would be automatically installed and enrolled to the `dependencies` and `devDependencies` fields in the `package.json`
|
|
82
|
+
SDK library generated by the **Nestia** requires the [nestia-fetcher](https://github.com/samchon/nestia-fetcher) module.
|
|
81
83
|
|
|
82
|
-
|
|
83
|
-
- [node-fetch](https://github.com/node-fetch/node-fetch)
|
|
84
|
+
Therefore, when you publish an SDK library generated by this **Nestia**, you have to write the [nestia-fetcher](https://github.com/samchon/nestia-fetcher) module into the `dependencies` property of the `package.json` file like below. You also can configure the `dependencies` property of the `package.json` file by typing the `npm install --save nestia-fetcher` command in the console, too.
|
|
84
85
|
|
|
86
|
+
```json
|
|
87
|
+
{
|
|
88
|
+
"name": "payments-server-api",
|
|
89
|
+
"dependencies": {
|
|
90
|
+
"nestia-fetcher": "^1.0.2"
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
```
|
|
85
94
|
|
|
86
95
|
|
|
87
96
|
|
|
88
97
|
## Advanced
|
|
89
98
|
### `nestia.config.ts`
|
|
90
99
|
```typescript
|
|
91
|
-
export
|
|
100
|
+
export interface IConfiguration
|
|
92
101
|
{
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
102
|
+
/**
|
|
103
|
+
* List of files or directories containing the NestJS controller classes.
|
|
104
|
+
*/
|
|
105
|
+
input: string | string[] | IConfiguration.IInput;
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Output directory that SDK would be placed in.
|
|
109
|
+
*/
|
|
110
|
+
output: string;
|
|
99
111
|
|
|
112
|
+
/**
|
|
113
|
+
* Compiler options for the TypeScript.
|
|
114
|
+
*
|
|
115
|
+
* If omitted, the configuration would follow the `tsconfig.json`.
|
|
116
|
+
*/
|
|
117
|
+
compilerOptions?: tsc.CompilerOptions
|
|
118
|
+
}
|
|
119
|
+
export namespace IConfiguration
|
|
120
|
+
{
|
|
121
|
+
export interface IInput
|
|
122
|
+
{
|
|
100
123
|
/**
|
|
101
|
-
*
|
|
124
|
+
* List of files or directories containing the NestJS controller classes.
|
|
102
125
|
*/
|
|
103
|
-
|
|
126
|
+
include: string[];
|
|
104
127
|
|
|
105
128
|
/**
|
|
106
|
-
*
|
|
107
|
-
*
|
|
108
|
-
* If omitted, the configuration would follow the `tsconfig.json`.
|
|
129
|
+
* List of files or directories to be excluded.
|
|
109
130
|
*/
|
|
110
|
-
|
|
131
|
+
exclude: string[];
|
|
111
132
|
}
|
|
112
133
|
}
|
|
113
134
|
```
|
|
@@ -118,11 +139,24 @@ Write below content as the `nestia.config.ts` file and place it onto the root di
|
|
|
118
139
|
|
|
119
140
|
```typescript
|
|
120
141
|
export = {
|
|
121
|
-
input: "src/controllers
|
|
142
|
+
input: "src/controllers",
|
|
122
143
|
output: "src/api"
|
|
123
144
|
};
|
|
124
145
|
```
|
|
125
146
|
|
|
147
|
+
> Alternative options for the regular NestJS project:
|
|
148
|
+
>
|
|
149
|
+
> ```typescript
|
|
150
|
+
> export = {
|
|
151
|
+
> input: "src/**/*.controller.ts",
|
|
152
|
+
> /* input: {
|
|
153
|
+
> include: ["src/controllers/*.controller.ts"],
|
|
154
|
+
> exclude: ["src/controllers/fake_*.controller.ts"]
|
|
155
|
+
> },*/
|
|
156
|
+
> output: "src/api"
|
|
157
|
+
> }
|
|
158
|
+
> ```
|
|
159
|
+
|
|
126
160
|
|
|
127
161
|
|
|
128
162
|
### Recommended Structures
|
|
@@ -213,18 +247,15 @@ export function store
|
|
|
213
247
|
connection: IConnection,
|
|
214
248
|
section: string,
|
|
215
249
|
saleId: number,
|
|
216
|
-
input: store.Input
|
|
250
|
+
input: Primitive<store.Input>
|
|
217
251
|
): Promise<store.Output>
|
|
218
252
|
{
|
|
219
253
|
return Fetcher.fetch
|
|
220
254
|
(
|
|
221
255
|
connection,
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
},
|
|
226
|
-
"POST",
|
|
227
|
-
`/consumers/${section}/sales/${saleId}/questions/`,
|
|
256
|
+
store.ENCRYPTED,
|
|
257
|
+
store.METHOD,
|
|
258
|
+
store.path(section, saleId),
|
|
228
259
|
input
|
|
229
260
|
);
|
|
230
261
|
}
|
|
@@ -232,6 +263,18 @@ export namespace store
|
|
|
232
263
|
{
|
|
233
264
|
export type Input = Primitive<ISaleInquiry.IStore>;
|
|
234
265
|
export type Output = Primitive<ISaleInquiry<ISaleArticle.IContent>>;
|
|
266
|
+
|
|
267
|
+
export const METHOD = "POST" as const;
|
|
268
|
+
export const PATH: string = "/consumers/:section/sales/:saleId/questions";
|
|
269
|
+
export const ENCRYPTED: Fetcher.IEncrypted = {
|
|
270
|
+
request: true,
|
|
271
|
+
response: true,
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
export function path(section: string, saleId: number): string
|
|
275
|
+
{
|
|
276
|
+
return `/consumers/${section}/sales/${saleId}/questions`;
|
|
277
|
+
}
|
|
235
278
|
}
|
|
236
279
|
```
|
|
237
280
|
|
|
@@ -239,26 +282,55 @@ export namespace store
|
|
|
239
282
|
|
|
240
283
|
|
|
241
284
|
## Appendix
|
|
242
|
-
###
|
|
243
|
-
https://github.com/samchon/
|
|
285
|
+
### Template Project
|
|
286
|
+
https://github.com/samchon/backend
|
|
244
287
|
|
|
245
|
-
[
|
|
288
|
+
I support template backend project using this **Nestia*** library, [backend](https://github.com/samchon/backend).
|
|
246
289
|
|
|
247
|
-
|
|
290
|
+
Also, reading the README content of the [backend](https://github.com/samchon/backend) template repository, you can find lots of example backend projects who've been generated from the [backend](https://github.com/samchon/backend). Furthermore, the example projects guide how to generate SDK library from the **Nestia** and how to distribute the SDK library thorugh the NPM module.
|
|
248
291
|
|
|
249
|
-
|
|
250
|
-
samchon.github@gmail.com
|
|
292
|
+
Therefore, if you're planning to compose your own backend project using this **Nestia**, I recommend you to create the repository and learn from the [backend](https://github.com/samchon/backend) template project.
|
|
251
293
|
|
|
252
|
-
|
|
294
|
+
### Nestia-Helper
|
|
295
|
+
https://github.com/samchon/nestia-helper
|
|
253
296
|
|
|
254
|
-
|
|
297
|
+
Helper library of the `NestJS` with **Nestia**.
|
|
255
298
|
|
|
256
|
-
|
|
299
|
+
[nestia-helper](https://github.com/samchon/nestia-helper) is a type of helper library for `Nestia` by enhancing decorator functions. Also, all of the decorator functions provided by this [nestia-helper](https://github.com/samchon/nestia-helper) are all fully compatible with the **Nestia**, who can generate SDK library by analyzing NestJS controller classes in the compilation level.
|
|
300
|
+
|
|
301
|
+
Of course, this [nestia-helper](https://github.com/samchon/nestia-helper) is not essential for utilizing the `NestJS` and **Nestia**. You can generate SDK library of your NestJS developed backend server without this [nestia-helper](https://github.com/samchon/nestia-helper). However, as decorator functions of this [nestia-helper](https://github.com/samchon/nestia-helper) is enough strong, I recommend you to adapt this [nestia-helper](https://github.com/samchon/nestia-helper) when using `NestJS` and **Nestia**.
|
|
302
|
+
|
|
303
|
+
- Supported decorator functions
|
|
304
|
+
- [EncryptedController](https://github.com/samchon/nestia-helper#encryptedcontroller), [EncryptedModule](https://github.com/samchon/nestia-helper#encryptedmodule)
|
|
305
|
+
- [TypedRoute](https://github.com/samchon/nestia-helper#typedroute), [EncryptedRoute](https://github.com/samchon/nestia-helper#encryptedroute)
|
|
306
|
+
- [TypedParam](https://github.com/samchon/nestia-helper#typedparam), [EncryptedBody](https://github.com/samchon/nestia-helper#encryptedbody), [PlainBody](https://github.com/samchon/nestia-helper#plainbody)
|
|
307
|
+
- [ExceptionManager](https://github.com/samchon/nestia-helper#exceptionmanager)
|
|
308
|
+
|
|
309
|
+
### Safe-TypeORM
|
|
310
|
+
https://github.com/samchon/safe-typeorm
|
|
311
|
+
|
|
312
|
+
[safe-typeorm](https://github.com/samchon/safe-typeorm) is another library that what I've developed, helping `TypeORM` in the compilation level and optimizes DB performance automatically without any extra dedication.
|
|
313
|
+
|
|
314
|
+
Therefore, this **Nestia** makes you to be much convenient in the API interaction level and safe-typeorm helps you to be much convenient in the DB interaction level. With those **Nestia** and [safe-typeorm](https://github.com/samchon/safe-typeorm), let's implement the backend server much easily and conveniently.
|
|
315
|
+
|
|
316
|
+
- When writing [**SQL query**](https://github.com/samchon/safe-typeorm#safe-query-builder),
|
|
317
|
+
- Errors would be detected in the **compilation** level
|
|
318
|
+
- **Auto Completion** would be provided
|
|
319
|
+
- **Type Hint** would be supported
|
|
320
|
+
- You can implement [**App-join**](https://github.com/samchon/safe-typeorm#app-join-builder) very conveniently
|
|
321
|
+
- When [**SELECT**ing for **JSON** conversion](https://github.com/samchon/safe-typeorm#json-select-builder)
|
|
322
|
+
- [**App-Join**](https://github.com/samchon/safe-typeorm#app-join-builder) with the related entities would be automatically done
|
|
323
|
+
- Exact JSON **type** would be automatically **deduced**
|
|
324
|
+
- The **performance** would be **automatically tuned**
|
|
325
|
+
- When [**INSERT**](https://github.com/samchon/safe-typeorm#insert-collection)ing records
|
|
326
|
+
- Sequence of tables would be automatically sorted by analyzing dependencies
|
|
327
|
+
- The **performance** would be **automatically tuned**
|
|
328
|
+
|
|
329
|
+

|
|
257
330
|
|
|
258
331
|
### Archidraw
|
|
259
332
|
https://www.archisketch.com/
|
|
260
333
|
|
|
261
334
|
I have special thanks to the Archidraw, where I'm working for.
|
|
262
335
|
|
|
263
|
-
The Archidraw is a great IT company developing 3D interior editor and lots of solutions based on the 3D assets. Also, the Archidraw is the first company who had adopted this **Nestia** on their commercial backend project, even this **Nestia** was in the alpha level.
|
|
264
|
-
|
|
336
|
+
The Archidraw is a great IT company developing 3D interior editor and lots of solutions based on the 3D assets. Also, the Archidraw is the first company who had adopted this **Nestia** on their commercial backend project, even this **Nestia** was in the alpha level.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nestia",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Automatic SDK and Document generator for the NestJS",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"bin": {
|
|
@@ -30,17 +30,19 @@
|
|
|
30
30
|
"homepage": "https://github.com/samchon/nestia#readme",
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@types/cli": "^0.11.19",
|
|
33
|
+
"@types/glob": "^7.2.0",
|
|
33
34
|
"@types/node": "^14.14.22",
|
|
34
35
|
"@types/reflect-metadata": "^0.1.0",
|
|
35
36
|
"cli": "^1.0.1",
|
|
36
37
|
"del": "^6.0.0",
|
|
38
|
+
"glob": "^7.2.0",
|
|
37
39
|
"ts-node": "^9.1.1",
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
"typescript": "^4.3
|
|
40
|
+
"tsconfig-paths": "^3.14.1",
|
|
41
|
+
"tstl": "^2.5.3",
|
|
42
|
+
"typescript": "^4.6.3"
|
|
41
43
|
},
|
|
42
44
|
"devDependencies": {
|
|
43
|
-
"
|
|
45
|
+
"nestia-helper": "^1.2.0-dev.20220411",
|
|
44
46
|
"rimraf": "^3.0.2"
|
|
45
47
|
}
|
|
46
48
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type tsc from "typescript";
|
|
2
|
+
|
|
3
|
+
export interface IConfiguration
|
|
4
|
+
{
|
|
5
|
+
input: string | string[] | IConfiguration.IInput;
|
|
6
|
+
output: string;
|
|
7
|
+
compilerOptions?: tsc.CompilerOptions;
|
|
8
|
+
}
|
|
9
|
+
export namespace IConfiguration
|
|
10
|
+
{
|
|
11
|
+
export interface IInput
|
|
12
|
+
{
|
|
13
|
+
include: string[];
|
|
14
|
+
exclude?: string[];
|
|
15
|
+
}
|
|
16
|
+
}
|
package/src/NestiaApplication.ts
CHANGED
|
@@ -9,16 +9,17 @@ import { ReflectAnalyzer } from "./analyses/ReflectAnalyzer";
|
|
|
9
9
|
import { SourceFinder } from "./analyses/SourceFinder";
|
|
10
10
|
import { SdkGenerator } from "./generates/SdkGenerator";
|
|
11
11
|
|
|
12
|
+
import { IConfiguration } from "./IConfiguration";
|
|
12
13
|
import { IController } from "./structures/IController";
|
|
13
14
|
import { IRoute } from "./structures/IRoute";
|
|
14
15
|
import { ArrayUtil } from "./utils/ArrayUtil";
|
|
15
16
|
|
|
16
17
|
export class NestiaApplication
|
|
17
18
|
{
|
|
18
|
-
private readonly config_:
|
|
19
|
+
private readonly config_: IConfiguration;
|
|
19
20
|
private readonly bundle_checker_: Singleton<Promise<(str: string) => boolean>>;
|
|
20
21
|
|
|
21
|
-
public constructor(config:
|
|
22
|
+
public constructor(config: IConfiguration)
|
|
22
23
|
{
|
|
23
24
|
this.config_ = config;
|
|
24
25
|
this.bundle_checker_ = new Singleton(async () =>
|
|
@@ -47,18 +48,16 @@ export class NestiaApplication
|
|
|
47
48
|
public async generate(): Promise<void>
|
|
48
49
|
{
|
|
49
50
|
// LOAD CONTROLLER FILES
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
fileList.push(...filtered);
|
|
61
|
-
}
|
|
51
|
+
const input: IConfiguration.IInput = this.config_.input instanceof Array
|
|
52
|
+
? { include: this.config_.input }
|
|
53
|
+
: typeof this.config_.input === "string"
|
|
54
|
+
? { include: [ this.config_.input ] }
|
|
55
|
+
: this.config_.input;
|
|
56
|
+
const fileList: string[] = await ArrayUtil.asyncFilter
|
|
57
|
+
(
|
|
58
|
+
await SourceFinder.find(input),
|
|
59
|
+
file => this.is_not_excluded(file)
|
|
60
|
+
);
|
|
62
61
|
|
|
63
62
|
// ANALYZE REFLECTS
|
|
64
63
|
const unique: WeakSet<any> = new WeakSet();
|
|
@@ -94,14 +93,4 @@ export class NestiaApplication
|
|
|
94
93
|
return file.indexOf(`${this.config_.output}${path.sep}functional`) === -1
|
|
95
94
|
&& (await this.bundle_checker_.get())(file) === false;
|
|
96
95
|
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
export namespace NestiaApplication
|
|
100
|
-
{
|
|
101
|
-
export interface IConfiguration
|
|
102
|
-
{
|
|
103
|
-
input: string | string[];
|
|
104
|
-
output: string;
|
|
105
|
-
compilerOptions?: tsc.CompilerOptions;
|
|
106
|
-
}
|
|
107
96
|
}
|
|
@@ -1,33 +1,73 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import glob from "glob";
|
|
3
|
+
import path from "path";
|
|
4
|
+
|
|
5
|
+
import { IConfiguration } from "../IConfiguration";
|
|
3
6
|
|
|
4
7
|
export namespace SourceFinder
|
|
5
8
|
{
|
|
6
|
-
export async function find(
|
|
9
|
+
export async function find(input: IConfiguration.IInput): Promise<string[]>
|
|
7
10
|
{
|
|
8
|
-
const
|
|
9
|
-
await
|
|
11
|
+
const dict: Set<string> = new Set();
|
|
12
|
+
await decode(input.include, str => dict.add(str));
|
|
13
|
+
if (input.exclude)
|
|
14
|
+
await decode(input.exclude, str => dict.delete(str));
|
|
15
|
+
|
|
16
|
+
return [...dict];
|
|
17
|
+
}
|
|
10
18
|
|
|
11
|
-
|
|
19
|
+
async function decode
|
|
20
|
+
(
|
|
21
|
+
input: string[],
|
|
22
|
+
closure: (location: string) => void,
|
|
23
|
+
): Promise<void>
|
|
24
|
+
{
|
|
25
|
+
for (const pattern of input)
|
|
26
|
+
for (const location of await _Glob(path.resolve(pattern)))
|
|
27
|
+
{
|
|
28
|
+
const stats: fs.Stats = await fs.promises.stat(location);
|
|
29
|
+
if (stats.isDirectory() === true)
|
|
30
|
+
await iterate(closure, location);
|
|
31
|
+
else if (stats.isFile() && _Is_ts_file(location))
|
|
32
|
+
closure(location);
|
|
33
|
+
}
|
|
12
34
|
}
|
|
13
35
|
|
|
14
|
-
async function
|
|
36
|
+
async function iterate
|
|
37
|
+
(
|
|
38
|
+
closure: (location: string) => void,
|
|
39
|
+
location: string
|
|
40
|
+
): Promise<void>
|
|
15
41
|
{
|
|
16
|
-
const
|
|
17
|
-
for (const file of
|
|
42
|
+
const directory: string[] = await fs.promises.readdir(location);
|
|
43
|
+
for (const file of directory)
|
|
18
44
|
{
|
|
19
|
-
const
|
|
20
|
-
const stats: fs.Stats = await fs.promises.
|
|
45
|
+
const next: string = path.resolve(`${location}/${file}`);
|
|
46
|
+
const stats: fs.Stats = await fs.promises.stat(next);
|
|
21
47
|
|
|
22
48
|
if (stats.isDirectory() === true)
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
}
|
|
27
|
-
else if (file.substr(-3) !== ".ts" || file.substr(-5) === ".d.ts")
|
|
28
|
-
continue;
|
|
29
|
-
|
|
30
|
-
output.push(current);
|
|
49
|
+
await iterate(closure, next);
|
|
50
|
+
else if (stats.isFile() && _Is_ts_file(file))
|
|
51
|
+
closure(next);
|
|
31
52
|
}
|
|
32
53
|
}
|
|
54
|
+
|
|
55
|
+
function _Glob(pattern: string): Promise<string[]>
|
|
56
|
+
{
|
|
57
|
+
return new Promise((resolve, reject) =>
|
|
58
|
+
{
|
|
59
|
+
glob(pattern, (err, matches) =>
|
|
60
|
+
{
|
|
61
|
+
if (err)
|
|
62
|
+
reject(err);
|
|
63
|
+
else
|
|
64
|
+
resolve(matches.map(str => path.resolve(str)));
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function _Is_ts_file(file: string): boolean
|
|
70
|
+
{
|
|
71
|
+
return file.substr(-3) === ".ts" && file.substr(-5) !== ".d.ts";
|
|
72
|
+
}
|
|
33
73
|
}
|
package/src/bin/nestia.ts
CHANGED
|
@@ -1,88 +1,82 @@
|
|
|
1
|
-
#!/usr/bin/env ts-node
|
|
1
|
+
#!/usr/bin/env ts-node
|
|
2
2
|
|
|
3
|
-
import
|
|
3
|
+
import cp from "child_process";
|
|
4
4
|
import fs from "fs";
|
|
5
|
-
import path from "path";
|
|
6
5
|
import tsc from "typescript";
|
|
7
6
|
|
|
8
|
-
import { NestiaApplication } from "../NestiaApplication";
|
|
9
|
-
|
|
10
|
-
import { Terminal } from "../utils/Terminal";
|
|
11
7
|
import { stripJsonComments } from "../utils/stripJsonComments";
|
|
12
8
|
|
|
13
|
-
|
|
9
|
+
function install(): void
|
|
14
10
|
{
|
|
15
|
-
|
|
11
|
+
const command: string = "npm install --save nestia-fetcher";
|
|
12
|
+
cp.execSync(command, { stdio: "inherit" });
|
|
16
13
|
}
|
|
17
14
|
|
|
18
|
-
|
|
15
|
+
function sdk(alias: boolean): void
|
|
19
16
|
{
|
|
20
|
-
|
|
17
|
+
const parameters: string[] = [
|
|
18
|
+
alias ? "npx ts-node -r tsconfig-paths/register" : "npx ts-node",
|
|
19
|
+
__dirname + "/../executable/sdk",
|
|
20
|
+
...process.argv.slice(3)
|
|
21
|
+
];
|
|
22
|
+
const command: string = parameters.join(" ");
|
|
23
|
+
cp.execSync(command, { stdio: "inherit" });
|
|
24
|
+
}
|
|
21
25
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
if (fs.existsSync("
|
|
26
|
+
async function tsconfig(task: (alias: boolean) => void): Promise<void>
|
|
27
|
+
{
|
|
28
|
+
// NO TSCONFIG.JSON?
|
|
29
|
+
if (fs.existsSync("tsconfig.json") === false)
|
|
26
30
|
{
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
input = config.input instanceof Array ? config.input : [config.input];
|
|
30
|
-
command.out = config.output;
|
|
31
|
+
task(false);
|
|
32
|
+
return;
|
|
31
33
|
}
|
|
32
|
-
|
|
33
|
-
//----
|
|
34
|
-
// VALIDATIONS
|
|
35
|
-
//----
|
|
36
|
-
// CHECK OUTPUT
|
|
37
|
-
if (command.out === null)
|
|
38
|
-
throw new Error(`Output directory is not specified. Add the "--out <output_directory>" option.`);
|
|
39
34
|
|
|
40
|
-
|
|
41
|
-
const
|
|
42
|
-
const
|
|
35
|
+
const content: string = await fs.promises.readFile("tsconfig.json", "utf8");
|
|
36
|
+
const json: any = JSON.parse(stripJsonComments(content));
|
|
37
|
+
const options: tsc.CompilerOptions = json.compilerOptions;
|
|
43
38
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
// CHECK INPUTS
|
|
48
|
-
for (const path of input)
|
|
39
|
+
// NO ALIAS PATHS
|
|
40
|
+
if (!options.paths || !Object.entries(options.paths).length)
|
|
49
41
|
{
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
throw new Error(`Target "${path}" is not a directory.`);
|
|
42
|
+
task(false);
|
|
43
|
+
return;
|
|
53
44
|
}
|
|
54
45
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
//----
|
|
58
|
-
if (fs.existsSync("tsconfig.json") === true)
|
|
59
|
-
{
|
|
60
|
-
const content: string = await fs.promises.readFile("tsconfig.json", "utf8");
|
|
61
|
-
const options: tsc.CompilerOptions = JSON.parse(stripJsonComments(content)).compilerOptions;
|
|
46
|
+
let closer: null | (() => Promise<void>) = null;
|
|
47
|
+
let error: Error | null = null;
|
|
62
48
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
49
|
+
if (!options.baseUrl)
|
|
50
|
+
{
|
|
51
|
+
options.baseUrl = "./";
|
|
52
|
+
await fs.promises.writeFile
|
|
53
|
+
(
|
|
54
|
+
"tsconfig.json",
|
|
55
|
+
JSON.stringify(json, null, 2),
|
|
56
|
+
"utf8"
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
closer = () => fs.promises.writeFile
|
|
60
|
+
(
|
|
61
|
+
"tsconfig.json",
|
|
62
|
+
content,
|
|
63
|
+
"utf8"
|
|
64
|
+
);
|
|
66
65
|
}
|
|
67
66
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
// CALL THE APP.GENERATE()
|
|
71
|
-
const app: NestiaApplication = new NestiaApplication({
|
|
72
|
-
output: command.out,
|
|
73
|
-
input,
|
|
74
|
-
compilerOptions,
|
|
75
|
-
});
|
|
76
|
-
await app.generate();
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
async function install(): Promise<void>
|
|
80
|
-
{
|
|
81
|
-
for (const module of ["@types/node", "node-fetch"])
|
|
67
|
+
try
|
|
82
68
|
{
|
|
83
|
-
|
|
84
|
-
await Terminal.execute(`npm install --save-dev ${module}`);
|
|
69
|
+
task(true);
|
|
85
70
|
}
|
|
71
|
+
catch (exp)
|
|
72
|
+
{
|
|
73
|
+
error = exp as Error;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (closer)
|
|
77
|
+
await closer();
|
|
78
|
+
if (error)
|
|
79
|
+
throw error;
|
|
86
80
|
}
|
|
87
81
|
|
|
88
82
|
async function main(): Promise<void>
|
|
@@ -90,32 +84,12 @@ async function main(): Promise<void>
|
|
|
90
84
|
if (process.argv[2] === "install")
|
|
91
85
|
await install();
|
|
92
86
|
else if (process.argv[2] === "sdk")
|
|
93
|
-
|
|
94
|
-
const command: ICommand = cli.parse({
|
|
95
|
-
out: ["o", "Output path of the SDK files", "string", null],
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
try
|
|
99
|
-
{
|
|
100
|
-
const inputs: string[] = [];
|
|
101
|
-
for (const arg of process.argv.slice(3))
|
|
102
|
-
{
|
|
103
|
-
if (arg[0] === "-")
|
|
104
|
-
break;
|
|
105
|
-
inputs.push(arg);
|
|
106
|
-
}
|
|
107
|
-
await sdk(inputs, command);
|
|
108
|
-
}
|
|
109
|
-
catch (exp)
|
|
110
|
-
{
|
|
111
|
-
console.log(exp);
|
|
112
|
-
process.exit(-1);
|
|
113
|
-
}
|
|
114
|
-
}
|
|
87
|
+
await tsconfig(sdk);
|
|
115
88
|
else
|
|
116
|
-
|
|
117
|
-
console.log(`nestia supports only two commands; install and sdk, however you typed ${process.argv[2]}`);
|
|
118
|
-
process.exit(-1);
|
|
119
|
-
}
|
|
89
|
+
throw new Error(`nestia supports only two commands; install and sdk, however you typed ${process.argv[2]}`);
|
|
120
90
|
}
|
|
121
|
-
main()
|
|
91
|
+
main().catch(exp =>
|
|
92
|
+
{
|
|
93
|
+
console.log(exp.message);
|
|
94
|
+
process.exit(-1);
|
|
95
|
+
});
|
package/src/bundle/HttpError.ts
CHANGED
|
@@ -1,33 +1 @@
|
|
|
1
|
-
|
|
2
|
-
* @packageDocumentation
|
|
3
|
-
* @module api
|
|
4
|
-
*/
|
|
5
|
-
//================================================================
|
|
6
|
-
/**
|
|
7
|
-
* HTTP Error from the backend server.
|
|
8
|
-
*
|
|
9
|
-
* @author Jeongho Nam - https://github.com/samchon
|
|
10
|
-
*/
|
|
11
|
-
export class HttpError extends Error
|
|
12
|
-
{
|
|
13
|
-
public readonly method: string;
|
|
14
|
-
public readonly path: string;
|
|
15
|
-
public readonly status: number;
|
|
16
|
-
|
|
17
|
-
public constructor(method: string, path: string, status: number, message: string)
|
|
18
|
-
{
|
|
19
|
-
super(message);
|
|
20
|
-
|
|
21
|
-
// INHERITANCE POLYFILL
|
|
22
|
-
const proto: HttpError = new.target.prototype;
|
|
23
|
-
if (Object.setPrototypeOf)
|
|
24
|
-
Object.setPrototypeOf(this, proto);
|
|
25
|
-
else
|
|
26
|
-
(this as any).__proto__ = proto;
|
|
27
|
-
|
|
28
|
-
// ASSIGN MEMBERS
|
|
29
|
-
this.method = method;
|
|
30
|
-
this.path = path;
|
|
31
|
-
this.status = status;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
1
|
+
export { HttpError } from "nestia-fetcher";
|
|
@@ -1,26 +1 @@
|
|
|
1
|
-
|
|
2
|
-
* @packageDocumentation
|
|
3
|
-
* @module api
|
|
4
|
-
*/
|
|
5
|
-
//================================================================
|
|
6
|
-
export interface IConnection
|
|
7
|
-
{
|
|
8
|
-
host: string;
|
|
9
|
-
headers?: Record<string, string>;
|
|
10
|
-
encryption?: IConnection.IEncyptionPassword | IConnection.EncryptionClosure;
|
|
11
|
-
path?: (path: string) => string;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export namespace IConnection
|
|
15
|
-
{
|
|
16
|
-
export interface IEncyptionPassword
|
|
17
|
-
{
|
|
18
|
-
key: string;
|
|
19
|
-
iv: string;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export interface EncryptionClosure
|
|
23
|
-
{
|
|
24
|
-
(content: string, isEncode: boolean): IEncyptionPassword;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
1
|
+
export { IConnection } from "nestia-fetcher";
|
package/src/bundle/Primitive.ts
CHANGED
|
@@ -1,113 +1 @@
|
|
|
1
|
-
|
|
2
|
-
* @packageDocumentation
|
|
3
|
-
* @module api
|
|
4
|
-
*/
|
|
5
|
-
//================================================================
|
|
6
|
-
/**
|
|
7
|
-
* 객체의 원시 타입.
|
|
8
|
-
*
|
|
9
|
-
* `Primitive` 는 대상 인스턴스의 모든 메서드를 제거하여, 그 타입을 pritimive object 의 형태로
|
|
10
|
-
* 바꾸어주는 TMP (Type Meta Programming) 타입이다.
|
|
11
|
-
*
|
|
12
|
-
* @template Instance 대상 인스턴스
|
|
13
|
-
* @author Samchon
|
|
14
|
-
*/
|
|
15
|
-
export type Primitive<Instance> = value_of<Instance> extends object
|
|
16
|
-
? Instance extends object
|
|
17
|
-
? Instance extends IJsonable<infer Raw>
|
|
18
|
-
? value_of<Raw> extends object
|
|
19
|
-
? Raw extends object
|
|
20
|
-
? PrimitiveObject<Raw> // object would be primitified
|
|
21
|
-
: never // cannot be
|
|
22
|
-
: value_of<Raw> // atomic value
|
|
23
|
-
: PrimitiveObject<Instance> // object would be primitified
|
|
24
|
-
: never // cannot be
|
|
25
|
-
: value_of<Instance>;
|
|
26
|
-
|
|
27
|
-
export namespace Primitive
|
|
28
|
-
{
|
|
29
|
-
/**
|
|
30
|
-
* Primitive object 하드 카피.
|
|
31
|
-
*
|
|
32
|
-
* `Primitive.clone()` 은 파라미터 인스턴스를 원시 오브젝트 형태로 hard copy 하는 함수이다.
|
|
33
|
-
*
|
|
34
|
-
* @param instance 복사 대상 인스턴스
|
|
35
|
-
* @return 복사된 객체
|
|
36
|
-
*/
|
|
37
|
-
export function clone<Instance>(instance: Instance): Primitive<Instance>
|
|
38
|
-
{
|
|
39
|
-
return JSON.parse(JSON.stringify(instance));
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* @todo
|
|
44
|
-
*/
|
|
45
|
-
export function equal_to<Instance>(x: Instance, y: Instance): boolean
|
|
46
|
-
{
|
|
47
|
-
return JSON.stringify(x) === JSON.stringify(y) || recursive_equal_to(x, y);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
type PrimitiveObject<Instance extends object> = Instance extends Array<infer T>
|
|
52
|
-
? Primitive<T>[]
|
|
53
|
-
:
|
|
54
|
-
{
|
|
55
|
-
[P in keyof Instance]: Instance[P] extends Function
|
|
56
|
-
? never
|
|
57
|
-
: Primitive<Instance[P]>
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
type value_of<Instance> =
|
|
61
|
-
is_value_of<Instance, Boolean> extends true ? boolean
|
|
62
|
-
: is_value_of<Instance, Number> extends true ? number
|
|
63
|
-
: is_value_of<Instance, String> extends true ? string
|
|
64
|
-
: Instance;
|
|
65
|
-
|
|
66
|
-
type is_value_of<Instance, Object extends IValueOf<any>> =
|
|
67
|
-
Instance extends Object
|
|
68
|
-
? Object extends IValueOf<infer Primitive>
|
|
69
|
-
? Instance extends Primitive
|
|
70
|
-
? false
|
|
71
|
-
: true // not Primitive, but Object
|
|
72
|
-
: false // cannot be
|
|
73
|
-
: false;
|
|
74
|
-
|
|
75
|
-
interface IValueOf<T>
|
|
76
|
-
{
|
|
77
|
-
valueOf(): T;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
interface IJsonable<T>
|
|
81
|
-
{
|
|
82
|
-
toJSON(): T;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
function object_equal_to<T extends object>(x: T, y: T): boolean
|
|
86
|
-
{
|
|
87
|
-
for (const key in x)
|
|
88
|
-
if (recursive_equal_to(x[key], y[key]) === false)
|
|
89
|
-
return false;
|
|
90
|
-
return true;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
function array_equal_to<T>(x: T[], y: T[]): boolean
|
|
94
|
-
{
|
|
95
|
-
if (x.length !== y.length)
|
|
96
|
-
return false;
|
|
97
|
-
|
|
98
|
-
return x.every((value, index) => recursive_equal_to(value, y[index]));
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
function recursive_equal_to<T>(x: T, y: T): boolean
|
|
102
|
-
{
|
|
103
|
-
const type = typeof x;
|
|
104
|
-
if (type !== typeof y)
|
|
105
|
-
return false;
|
|
106
|
-
else if (type === "object")
|
|
107
|
-
if (x instanceof Array)
|
|
108
|
-
return array_equal_to(x, y as typeof x);
|
|
109
|
-
else
|
|
110
|
-
return object_equal_to(<any>x as object, <any>y as object);
|
|
111
|
-
else
|
|
112
|
-
return x === y;
|
|
113
|
-
}
|
|
1
|
+
export { Primitive } from "nestia-fetcher";
|
|
@@ -1,51 +1 @@
|
|
|
1
|
-
|
|
2
|
-
* @packageDocumentation
|
|
3
|
-
* @module api.__internal
|
|
4
|
-
*/
|
|
5
|
-
//================================================================
|
|
6
|
-
import * as crypto from "crypto";
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Utility class for AES Encryption.
|
|
10
|
-
*
|
|
11
|
-
* - AES-128/256
|
|
12
|
-
* - CBC mode
|
|
13
|
-
* - PKCS#5 Padding
|
|
14
|
-
* - Base64 Encoding
|
|
15
|
-
*
|
|
16
|
-
* @author Jeongho Nam - https://github.com/samchon
|
|
17
|
-
*/
|
|
18
|
-
export namespace AesPkcs5
|
|
19
|
-
{
|
|
20
|
-
/**
|
|
21
|
-
* Encode data
|
|
22
|
-
*
|
|
23
|
-
* @param data Target data
|
|
24
|
-
* @param key Key value of the encryption.
|
|
25
|
-
* @param iv Initializer Vector for the encryption
|
|
26
|
-
* @return Encoded data
|
|
27
|
-
*/
|
|
28
|
-
export function encode(data: string, key: string, iv: string): string
|
|
29
|
-
{
|
|
30
|
-
const bytes: number = key.length * 8;
|
|
31
|
-
const cipher: crypto.Cipher = crypto.createCipheriv(`AES-${bytes}-CBC`, key, iv);
|
|
32
|
-
|
|
33
|
-
return cipher.update(data, "utf8", "base64") + cipher.final("base64");
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Decode data.
|
|
38
|
-
*
|
|
39
|
-
* @param data Target data
|
|
40
|
-
* @param key Key value of the decryption.
|
|
41
|
-
* @param iv Initializer Vector for the decryption
|
|
42
|
-
* @return Decoded data.
|
|
43
|
-
*/
|
|
44
|
-
export function decode(data: string, key: string, iv: string): string
|
|
45
|
-
{
|
|
46
|
-
const bytes: number = key.length * 8;
|
|
47
|
-
const decipher: crypto.Decipher = crypto.createDecipheriv(`AES-${bytes}-CBC`, key, iv);
|
|
48
|
-
|
|
49
|
-
return decipher.update(data, "base64", "utf8") + decipher.final("utf8");
|
|
50
|
-
}
|
|
51
|
-
}
|
|
1
|
+
export { AesPkcs5 } from "nestia-fetcher";
|
|
@@ -1,126 +1 @@
|
|
|
1
|
-
|
|
2
|
-
* @packageDocumentation
|
|
3
|
-
* @module api.__internal
|
|
4
|
-
*/
|
|
5
|
-
//================================================================
|
|
6
|
-
import { AesPkcs5 } from "./AesPkcs5";
|
|
7
|
-
import { HttpError } from "../HttpError";
|
|
8
|
-
import { IConnection } from "../IConnection";
|
|
9
|
-
import { Primitive } from "../Primitive";
|
|
10
|
-
|
|
11
|
-
// POLYFILL FOR NODE
|
|
12
|
-
if (typeof global === "object"
|
|
13
|
-
&& typeof global.process === "object"
|
|
14
|
-
&& typeof global.process.versions === "object"
|
|
15
|
-
&& typeof global.process.versions.node !== undefined)
|
|
16
|
-
(global as any).fetch = require("node-fetch");
|
|
17
|
-
|
|
18
|
-
export class Fetcher
|
|
19
|
-
{
|
|
20
|
-
public static fetch<Output>(connection: IConnection, config: Fetcher.IConfig, method: "GET" | "DELETE", path: string): Promise<Primitive<Output>>;
|
|
21
|
-
public static fetch<Input, Output>(connection: IConnection, config: Fetcher.IConfig, method: "POST" | "PUT" | "PATCH", path: string, input: Input): Promise<Primitive<Output>>;
|
|
22
|
-
|
|
23
|
-
public static async fetch<Output>
|
|
24
|
-
(
|
|
25
|
-
connection: IConnection,
|
|
26
|
-
config: Fetcher.IConfig,
|
|
27
|
-
method: string,
|
|
28
|
-
path: string,
|
|
29
|
-
input?: object
|
|
30
|
-
): Promise<Primitive<Output>>
|
|
31
|
-
{
|
|
32
|
-
if (config.input_encrypted === true || config.output_encrypted === true)
|
|
33
|
-
if (connection.encryption === undefined)
|
|
34
|
-
throw new Error("Error on nestia.Fetcher.encrypt(): the encryption password has not been configured.");
|
|
35
|
-
|
|
36
|
-
//----
|
|
37
|
-
// REQUEST MESSSAGE
|
|
38
|
-
//----
|
|
39
|
-
// METHOD & HEADERS
|
|
40
|
-
const init: RequestInit = {
|
|
41
|
-
method,
|
|
42
|
-
headers: config.input_encrypted === false && input !== undefined && typeof input === "object"
|
|
43
|
-
? {
|
|
44
|
-
...connection.headers,
|
|
45
|
-
"Content-Type": "application/json"
|
|
46
|
-
}
|
|
47
|
-
: connection.headers
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
// REQUEST BODY (WITH ENCRYPTION)
|
|
51
|
-
if (input !== undefined)
|
|
52
|
-
{
|
|
53
|
-
let content: string = JSON.stringify(input);
|
|
54
|
-
if (config.input_encrypted === true)
|
|
55
|
-
{
|
|
56
|
-
const password: IConnection.IEncyptionPassword = connection.encryption instanceof Function
|
|
57
|
-
? connection.encryption!(content, true)
|
|
58
|
-
: connection.encryption!;
|
|
59
|
-
content = AesPkcs5.encode(content, password.key, password.iv);
|
|
60
|
-
}
|
|
61
|
-
init.body = content;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
//----
|
|
65
|
-
// RESPONSE MESSAGE
|
|
66
|
-
//----
|
|
67
|
-
// URL SPECIFICATION
|
|
68
|
-
if (connection.host[connection.host.length - 1] !== "/" && path[0] !== "/")
|
|
69
|
-
path = "/" + path;
|
|
70
|
-
if (connection.path)
|
|
71
|
-
path = connection.path(path);
|
|
72
|
-
|
|
73
|
-
const url: URL = new URL(`${connection.host}${path}`);
|
|
74
|
-
|
|
75
|
-
// DO FETCH
|
|
76
|
-
const response: Response = await fetch(url.href, init);
|
|
77
|
-
let content: string = await response.text();
|
|
78
|
-
|
|
79
|
-
if (!content)
|
|
80
|
-
return undefined!;
|
|
81
|
-
|
|
82
|
-
// CHECK THE STATUS CODE
|
|
83
|
-
if (response.status !== 200 && response.status !== 201)
|
|
84
|
-
throw new HttpError(method, path, response.status, content);
|
|
85
|
-
|
|
86
|
-
// FINALIZATION (WITH DECODING)
|
|
87
|
-
if (config.output_encrypted === true)
|
|
88
|
-
{
|
|
89
|
-
const password: IConnection.IEncyptionPassword = connection.encryption instanceof Function
|
|
90
|
-
? connection.encryption!(content, false)
|
|
91
|
-
: connection.encryption!;
|
|
92
|
-
content = AesPkcs5.decode(content, password.key, password.iv);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
//----
|
|
96
|
-
// OUTPUT
|
|
97
|
-
//----
|
|
98
|
-
let ret: { __set_headers__: Record<string, any> } & Primitive<Output> = content as any;
|
|
99
|
-
try
|
|
100
|
-
{
|
|
101
|
-
// PARSE RESPONSE BODY
|
|
102
|
-
ret = JSON.parse(ret as any);
|
|
103
|
-
|
|
104
|
-
// FIND __SET_HEADERS__ FIELD
|
|
105
|
-
if (ret.__set_headers__ !== undefined && typeof ret.__set_headers__ === "object")
|
|
106
|
-
{
|
|
107
|
-
if (connection.headers === undefined)
|
|
108
|
-
connection.headers = {};
|
|
109
|
-
Object.assign(connection.headers, ret.__set_headers__);
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
catch {}
|
|
113
|
-
|
|
114
|
-
// RETURNS
|
|
115
|
-
return ret;
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
export namespace Fetcher
|
|
120
|
-
{
|
|
121
|
-
export interface IConfig
|
|
122
|
-
{
|
|
123
|
-
input_encrypted?: boolean;
|
|
124
|
-
output_encrypted: boolean;
|
|
125
|
-
}
|
|
126
|
-
}
|
|
1
|
+
export { Fetcher } from "nestia-fetcher";
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import cli from "cli";
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import tsc from "typescript";
|
|
5
|
+
import { Primitive } from "nestia-fetcher";
|
|
6
|
+
|
|
7
|
+
import { IConfiguration } from "../IConfiguration";
|
|
8
|
+
import { NestiaApplication } from "../NestiaApplication";
|
|
9
|
+
import { stripJsonComments } from "../utils/stripJsonComments";
|
|
10
|
+
|
|
11
|
+
interface ICommand
|
|
12
|
+
{
|
|
13
|
+
exclude: string | null;
|
|
14
|
+
out: string | null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async function sdk(include: string[], command: ICommand): Promise<void>
|
|
18
|
+
{
|
|
19
|
+
// CONFIGURATION
|
|
20
|
+
let config: IConfiguration;
|
|
21
|
+
if (fs.existsSync("nestia.config.ts") === true)
|
|
22
|
+
config = Primitive.clone
|
|
23
|
+
(
|
|
24
|
+
await import(path.resolve("nestia.config.ts"))
|
|
25
|
+
);
|
|
26
|
+
else
|
|
27
|
+
{
|
|
28
|
+
if (command.out === null)
|
|
29
|
+
throw new Error(`Output directory is not specified. Add the "--out <output_directory>" option.`);
|
|
30
|
+
config = {
|
|
31
|
+
input: {
|
|
32
|
+
include,
|
|
33
|
+
exclude: command.exclude
|
|
34
|
+
? [command.exclude]
|
|
35
|
+
: undefined
|
|
36
|
+
},
|
|
37
|
+
output: command.out
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// VALIDATE OUTPUT DIRECTORY
|
|
42
|
+
const parentPath: string = path.resolve(config.output + "/..");
|
|
43
|
+
const parentStats: fs.Stats = await fs.promises.stat(parentPath);
|
|
44
|
+
|
|
45
|
+
if (parentStats.isDirectory() === false)
|
|
46
|
+
throw new Error(`Unable to find parent directory of the output path: "${parentPath}".`);
|
|
47
|
+
|
|
48
|
+
// GENERATION
|
|
49
|
+
if (fs.existsSync("tsconfig.json") === true)
|
|
50
|
+
{
|
|
51
|
+
const content: string = await fs.promises.readFile("tsconfig.json", "utf8");
|
|
52
|
+
const options: tsc.CompilerOptions = JSON.parse(stripJsonComments(content)).compilerOptions;
|
|
53
|
+
|
|
54
|
+
config.compilerOptions = {
|
|
55
|
+
...options,
|
|
56
|
+
...(config.compilerOptions || {})
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// CALL THE APP.GENERATE()
|
|
61
|
+
const app: NestiaApplication = new NestiaApplication(config);
|
|
62
|
+
await app.generate();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async function main(): Promise<void>
|
|
66
|
+
{
|
|
67
|
+
const command: ICommand = cli.parse({
|
|
68
|
+
exclude: ["e", "Something to exclude", "string", null],
|
|
69
|
+
out: ["o", "Output path of the SDK files", "string", null],
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
try
|
|
73
|
+
{
|
|
74
|
+
const inputs: string[] = [];
|
|
75
|
+
for (const arg of process.argv.slice(2))
|
|
76
|
+
{
|
|
77
|
+
if (arg[0] === "-")
|
|
78
|
+
break;
|
|
79
|
+
inputs.push(arg);
|
|
80
|
+
}
|
|
81
|
+
await sdk(inputs, command);
|
|
82
|
+
}
|
|
83
|
+
catch (exp)
|
|
84
|
+
{
|
|
85
|
+
console.log(exp);
|
|
86
|
+
process.exit(-1);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
main();
|
|
@@ -2,7 +2,6 @@ import * as fs from "fs";
|
|
|
2
2
|
import { HashMap } from "tstl/container/HashMap";
|
|
3
3
|
|
|
4
4
|
import { IRoute } from "../structures/IRoute";
|
|
5
|
-
import { DirectoryUtil } from "../utils/DirectoryUtil";
|
|
6
5
|
import { ImportDictionary } from "../utils/ImportDictionary";
|
|
7
6
|
import { FunctionGenerator } from "./FunctionGenerator";
|
|
8
7
|
|
|
@@ -21,14 +20,8 @@ export namespace FileGenerator
|
|
|
21
20
|
// RELOCATE FOR ONLY ONE CONTROLLER METHOD IN AN URL CASE
|
|
22
21
|
relocate(root);
|
|
23
22
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
defaultImportDict.emplace(`${outDir}/__internal/Fetcher.ts`, true, "Fetcher");
|
|
27
|
-
defaultImportDict.emplace(`${outDir}/Primitive.ts`, true, "Primitive");
|
|
28
|
-
defaultImportDict.emplace(`${outDir}/IConnection.ts`, false, "IConnection");
|
|
29
|
-
|
|
30
|
-
await DirectoryUtil.remove(outDir + "/functional");
|
|
31
|
-
await iterate(defaultImportDict, outDir + "/functional", root);
|
|
23
|
+
// ITERATE FILES
|
|
24
|
+
await iterate(outDir + "/functional", root);
|
|
32
25
|
}
|
|
33
26
|
|
|
34
27
|
function emplace(directory: Directory, route: IRoute): void
|
|
@@ -70,7 +63,7 @@ export namespace FileGenerator
|
|
|
70
63
|
/* ---------------------------------------------------------
|
|
71
64
|
FILE ITERATOR
|
|
72
65
|
--------------------------------------------------------- */
|
|
73
|
-
async function iterate(
|
|
66
|
+
async function iterate(outDir: string, directory: Directory): Promise<void>
|
|
74
67
|
{
|
|
75
68
|
// CREATE A NEW DIRECTORY
|
|
76
69
|
try
|
|
@@ -83,7 +76,7 @@ export namespace FileGenerator
|
|
|
83
76
|
let content: string = "";
|
|
84
77
|
for (const it of directory.directories)
|
|
85
78
|
{
|
|
86
|
-
await iterate(
|
|
79
|
+
await iterate(`${outDir}/${it.first}`, it.second);
|
|
87
80
|
content += `export * as ${it.first} from "./${it.first}";\n`;
|
|
88
81
|
}
|
|
89
82
|
content += "\n";
|
|
@@ -100,10 +93,23 @@ export namespace FileGenerator
|
|
|
100
93
|
|
|
101
94
|
// FINALIZE THE CONTENT
|
|
102
95
|
if (directory.routes.length !== 0)
|
|
103
|
-
content =
|
|
104
|
-
+
|
|
96
|
+
content = ""
|
|
97
|
+
+ `import { AesPkcs5, Fetcher, Primitive } from "nestia-fetcher";\n`
|
|
98
|
+
+ `import type { IConnection } from "nestia-fetcher";\n`
|
|
99
|
+
+
|
|
100
|
+
(
|
|
101
|
+
importDict.empty()
|
|
102
|
+
? ""
|
|
103
|
+
: "\n" + importDict.toScript(outDir) + "\n"
|
|
104
|
+
)
|
|
105
105
|
+ content + "\n\n"
|
|
106
|
-
+
|
|
106
|
+
+ "//---------------------------------------------------------\n"
|
|
107
|
+
+ "// TO PREVENT THE UNUSED VARIABLE ERROR\n"
|
|
108
|
+
+ "//---------------------------------------------------------\n"
|
|
109
|
+
+ "AesPkcs5;\n"
|
|
110
|
+
+ "Fetcher;\n"
|
|
111
|
+
+ "Primitive;";
|
|
112
|
+
|
|
107
113
|
content = "/**\n"
|
|
108
114
|
+ " * @packageDocumentation\n"
|
|
109
115
|
+ ` * @module ${directory.module}\n`
|
|
@@ -27,7 +27,7 @@ export namespace FunctionGenerator
|
|
|
27
27
|
const fetchArguments: string[] =
|
|
28
28
|
[
|
|
29
29
|
"connection",
|
|
30
|
-
`${route.name}.
|
|
30
|
+
`${route.name}.ENCRYPTED`,
|
|
31
31
|
`${route.name}.METHOD`,
|
|
32
32
|
`${route.name}.path(${parameters.map(p => p.name).join(", ")})`
|
|
33
33
|
];
|
|
@@ -158,15 +158,15 @@ export namespace FunctionGenerator
|
|
|
158
158
|
+
|
|
159
159
|
(
|
|
160
160
|
types.length !== 0
|
|
161
|
-
? types.map(tuple => ` export type ${tuple.first} = Primitive<${tuple.second}>;`).join("\n") + "\n
|
|
161
|
+
? types.map(tuple => ` export type ${tuple.first} = Primitive<${tuple.second}>;`).join("\n") + "\n"
|
|
162
162
|
: ""
|
|
163
163
|
)
|
|
164
164
|
+ "\n"
|
|
165
165
|
+ ` export const METHOD = "${route.method}" as const;\n`
|
|
166
166
|
+ ` export const PATH: string = "${route.path}";\n`
|
|
167
|
-
+ ` export const
|
|
168
|
-
+ `
|
|
169
|
-
+ `
|
|
167
|
+
+ ` export const ENCRYPTED: Fetcher.IEncrypted = {\n`
|
|
168
|
+
+ ` request: ${input !== undefined && input.encrypted},\n`
|
|
169
|
+
+ ` response: ${route.encrypted},\n`
|
|
170
170
|
+ ` };\n`
|
|
171
171
|
+ "\n"
|
|
172
172
|
+ ` export function path(${parameters.map(param => `${param.name}: ${param.type}`).join(", ")}): string\n`
|
|
@@ -8,6 +8,11 @@ export class ImportDictionary
|
|
|
8
8
|
{
|
|
9
9
|
private readonly dict_: HashMap<string, Pair<boolean, HashSet<string>>> = new HashMap();
|
|
10
10
|
|
|
11
|
+
public empty(): boolean
|
|
12
|
+
{
|
|
13
|
+
return this.dict_.empty();
|
|
14
|
+
}
|
|
15
|
+
|
|
11
16
|
public emplace(file: string, realistic: boolean, instance: string): void
|
|
12
17
|
{
|
|
13
18
|
if (file.substr(-5) === ".d.ts")
|
|
@@ -36,17 +41,4 @@ export class ImportDictionary
|
|
|
36
41
|
}
|
|
37
42
|
return statements.join("\n");
|
|
38
43
|
}
|
|
39
|
-
|
|
40
|
-
public listUp(): string
|
|
41
|
-
{
|
|
42
|
-
let content: string = ""
|
|
43
|
-
+ "//---------------------------------------------------------\n"
|
|
44
|
-
+ "// TO PREVENT THE UNUSED VARIABLE ERROR\n"
|
|
45
|
-
+ "//---------------------------------------------------------\n";
|
|
46
|
-
for (const it of this.dict_)
|
|
47
|
-
if (it.second.first === true)
|
|
48
|
-
for (const instance of it.second.second)
|
|
49
|
-
content += instance + ";\n";
|
|
50
|
-
return content;
|
|
51
|
-
}
|
|
52
44
|
}
|
package/tsconfig.json
CHANGED
|
@@ -68,15 +68,11 @@
|
|
|
68
68
|
/* Experimental Options */
|
|
69
69
|
"experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
|
|
70
70
|
"emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
|
|
71
|
+
"stripInternal": true,
|
|
71
72
|
|
|
72
73
|
/* Advanced Options */
|
|
73
74
|
"skipLibCheck": true, /* Skip type checking of declaration files. */
|
|
74
75
|
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
|
|
75
76
|
},
|
|
76
|
-
"include": [
|
|
77
|
-
"src",
|
|
78
|
-
"test/default/src",
|
|
79
|
-
"test/nestia.config.ts/src",
|
|
80
|
-
// "test/tsconfig.json/src"
|
|
81
|
-
]
|
|
77
|
+
"include": ["src"]
|
|
82
78
|
}
|
package/src/utils/Terminal.ts
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import * as cp from "child_process";
|
|
2
|
-
import { Pair } from "tstl/utility/Pair";
|
|
3
|
-
|
|
4
|
-
export namespace Terminal
|
|
5
|
-
{
|
|
6
|
-
export function execute(...commands: string[]): Promise<Pair<string, string>>
|
|
7
|
-
{
|
|
8
|
-
return new Promise((resolve, reject) =>
|
|
9
|
-
{
|
|
10
|
-
cp.exec(commands.join(" && "), (error: Error | null, stdout: string, stderr: string) =>
|
|
11
|
-
{
|
|
12
|
-
if (error)
|
|
13
|
-
reject(error);
|
|
14
|
-
else
|
|
15
|
-
resolve(new Pair(stdout, stderr));
|
|
16
|
-
});
|
|
17
|
-
});
|
|
18
|
-
}
|
|
19
|
-
}
|