koatty 3.11.9 → 3.13.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/.rollup.config.js +28 -6
- package/CHANGELOG.md +1877 -184
- package/README.md +122 -139
- package/dist/README.md +122 -139
- package/dist/index.d.ts +56 -26
- package/dist/index.js +466 -749
- package/dist/index.mjs +406 -707
- package/dist/package.json +15 -16
- package/package.json +15 -16
package/README.md
CHANGED
@@ -1,179 +1,162 @@
|
|
1
|
-
#
|
1
|
+
# Koatty 🚀
|
2
2
|
|
3
|
-
|
3
|
+
[](https://www.npmjs.com/package/koatty)
|
4
|
+
[](https://opensource.org/licenses/BSD-3-Clause)
|
4
5
|
|
5
|
-
|
6
|
+
Koa2 + Typescript + IOC = koatty. **Koatty** is a progressive Node.js framework for building efficient and scalable server-side applications. Perfect for crafting enterprise-level APIs, microservices, and full-stack applications with TypeScript excellence.
|
6
7
|
|
7
|
-
|
8
|
+
## Why Koatty? 💡
|
8
9
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
* OpenTelemetry . 💪
|
18
|
-
|
19
|
-
|
20
|
-
## Documentation
|
21
|
-
|
22
|
-
[koatty_doc_CN](https://koatty.org/) (In progress💪)
|
10
|
+
- 🚄 **High Performance**: Built on top of Koa2 with optimized architecture
|
11
|
+
- 🧩 **Full-Featured**: Supports gRPC, HTTP, WebSocket, Schedule tasks, and more
|
12
|
+
- 🧠 **TypeScript First**: Native TypeScript support with elegant OOP design
|
13
|
+
- 🌀 **Spring-like IOC Container**: Powerful dependency injection system with autowiring
|
14
|
+
- ✂️ **AOP Support**: Aspect-oriented programming with decorator-based interceptors
|
15
|
+
- 🔌 **Extensible Architecture**: Plugin system with dependency injection
|
16
|
+
- 📦 **Modern Tooling**: CLI scaffolding, testing utilities, and production-ready configs
|
17
|
+
- 🌐 **Protocol Agnostic**: Write once, deploy as HTTP/gRPC/WebSocket services
|
23
18
|
|
24
19
|
|
25
|
-
##
|
20
|
+
## New features ✨
|
26
21
|
|
27
|
-
|
28
|
-
|
22
|
+
* HTTP、HTTPS、HTTP2、gRPC、WebSocket server.✔️
|
23
|
+
* Support loading configurations based on the environment, support command-line argument parsing(process.argv), and support environment variable parsing(process.env).✔️
|
24
|
+
* `@ExceptionHandler()` Register global exception handling.✔️
|
25
|
+
* Graceful shutdown and pre-exit event.✔️
|
26
|
+
* Supports custom decorators, bound to app events for execution.✔️
|
27
|
+
* GraphQL integration is available. ✔️
|
28
|
+
* Full-stack tracing capability through OpenTelemetry . ✔️
|
29
|
+
* Middleware can be bound to controllers and their method routes.✔️
|
30
|
+
* gRPC streaming is now supported. ✔️
|
31
|
+
* Added support for Swagger OpenAPI 3.0. 💪
|
32
|
+
|
33
|
+
|
34
|
+
## Core Features ✨
|
35
|
+
|
36
|
+
### 📡 Multi-Protocol Support
|
37
|
+
```typescript
|
38
|
+
// config/config.ts
|
39
|
+
export default {
|
40
|
+
...
|
41
|
+
protocol: "grpc", // Server protocol 'http' | 'https' | 'http2' | 'grpc' | 'ws' | 'wss' | 'graphql'
|
42
|
+
...
|
43
|
+
}
|
29
44
|
```
|
30
45
|
|
31
|
-
|
46
|
+
### 💉 Dependency Injection
|
47
|
+
```typescript
|
48
|
+
@Service()
|
49
|
+
export class UserService {
|
50
|
+
async findUser(id: number) {
|
51
|
+
return { id, name: 'Koatty User' };
|
52
|
+
}
|
53
|
+
}
|
32
54
|
|
33
|
-
|
55
|
+
@Controller()
|
56
|
+
export class IndexController {
|
57
|
+
app: App;
|
58
|
+
ctx: KoattyContext;
|
59
|
+
@Config("protocol")
|
60
|
+
conf: { protocol: string };
|
61
|
+
...
|
34
62
|
|
35
|
-
|
36
|
-
|
63
|
+
@Autowired()
|
64
|
+
private userService: UserService;
|
37
65
|
|
66
|
+
async test(id: number) {
|
67
|
+
const info = await this.userService.findUser(id);
|
68
|
+
...
|
69
|
+
}
|
70
|
+
}
|
38
71
|
```
|
39
72
|
|
40
|
-
###
|
73
|
+
### ✂️ Aspect-Oriented Programming
|
74
|
+
```javascript
|
75
|
+
@Aspect()
|
76
|
+
export class LogAspect implements IAspect {
|
77
|
+
app: App;
|
41
78
|
|
42
|
-
|
43
|
-
|
79
|
+
run() {
|
80
|
+
console.log('LogAspect');
|
81
|
+
}
|
82
|
+
}
|
44
83
|
|
45
|
-
|
84
|
+
// Apply aspect to controller
|
85
|
+
@Controller()
|
86
|
+
@BeforeEach(LogAspect)
|
87
|
+
export class UserController {
|
88
|
+
...
|
89
|
+
@After(LogAspect)
|
90
|
+
test() {
|
91
|
+
...
|
92
|
+
}
|
93
|
+
}
|
46
94
|
```
|
47
95
|
|
48
|
-
###
|
49
|
-
|
96
|
+
### 🔌 Plugin System
|
97
|
+
```javascript
|
98
|
+
// plugin/logger.ts
|
99
|
+
export class LoggerPlugin implements IPlugin {
|
100
|
+
app: App;
|
101
|
+
|
102
|
+
run() {
|
103
|
+
// todo something or hook on app.event
|
104
|
+
Logger.Debug("LoggerPlugin");
|
105
|
+
return Promise.resolve();
|
106
|
+
}
|
107
|
+
}
|
50
108
|
```
|
51
|
-
npm run dev
|
52
109
|
|
53
|
-
// or
|
54
|
-
npm start
|
55
|
-
```
|
56
110
|
|
57
|
-
##
|
111
|
+
## Benchmarks 📊
|
58
112
|
|
59
|
-
|
113
|
+
| Framework | Requests/sec | Latency | Memory Usage |
|
114
|
+
| ---------- | ------------ | ------- | ------------ |
|
115
|
+
| **Koatty** | 13,321 | 1.43ms | 54MB |
|
116
|
+
| Express | 12,456 | 1.45ms | 52MB |
|
117
|
+
| NestJS | 11,892 | 1.51ms | 63MB |
|
60
118
|
|
61
|
-
|
62
|
-
import { Controller, Autowired, GetMapping, RequestBody, PathVariable,
|
63
|
-
PostMapping, RequestMapping, RequestMethod, Valid, Output } from "koatty";
|
64
|
-
import { TestDTO } from "../model/dto/TestDTO";
|
65
|
-
import { TestService } from "../service/TestService";
|
66
|
-
import { App } from "../App";
|
119
|
+
*Tested on AWS t3.micro with 100 concurrent connections*
|
67
120
|
|
68
|
-
|
69
|
-
export class IndexController {
|
70
|
-
app: App;
|
71
|
-
ctx: KoattyContext;
|
121
|
+
## Documentation 📚
|
72
122
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
*
|
79
|
-
*/
|
80
|
-
constructor(ctx: KoattyContext) {
|
81
|
-
this.ctx = ctx;
|
82
|
-
}
|
123
|
+
- [中文文档](https://koatty.org/)
|
124
|
+
- [Getting Started Guide](https://github.com/Koatty/koatty_doc/blob/master/docs/README-en.md)
|
125
|
+
- [API Reference](https://koatty.org/#/?id=api)
|
126
|
+
- [Recipes & Best Practices](https://github.com/Koatty/koatty_awesome)
|
127
|
+
- [Example](https://github.com/Koatty/koatty_demo)
|
83
128
|
|
84
|
-
@GetMapping('/')
|
85
|
-
index() {
|
86
|
-
return Output.ok("Hello, koatty!");
|
87
|
-
}
|
88
129
|
|
89
|
-
|
90
|
-
async default(@PathVariable("name") @Valid("IsNotEmpty") name: string) {
|
91
|
-
try {
|
92
|
-
const info = await this.testService.sayHello(name);
|
93
|
-
return Output.ok(this.ctx, "success", info);
|
94
|
-
} catch (err: Error) {
|
95
|
-
return Output.fail(this.ctx, err.message));
|
96
|
-
}
|
97
|
-
}
|
130
|
+
## Quick Start ⚡
|
98
131
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
return Output.ok(this.ctx, "test", params);
|
103
|
-
}
|
104
|
-
}
|
132
|
+
1. **Install CLI**:
|
133
|
+
```bash
|
134
|
+
npm install -g koatty_cli
|
105
135
|
```
|
106
136
|
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
```javascript
|
112
|
-
import request from 'supertest';
|
113
|
-
import { ExecBootStrap } from 'koatty';
|
114
|
-
import { App } from '../src/App';
|
115
|
-
|
116
|
-
describe('UT example', () => {
|
117
|
-
|
118
|
-
let app: KoattyApplication;
|
119
|
-
beforeAll(async () => {
|
120
|
-
jest.useFakeTimers();
|
121
|
-
// test env
|
122
|
-
process.env.KOATTY_ENV = 'ts-node';
|
123
|
-
app = await ExecBootStrap()(App);
|
124
|
-
// app.use(async (ctx: any) => {
|
125
|
-
// ctx.body = 'Hello, World!';
|
126
|
-
// });
|
127
|
-
});
|
128
|
-
|
129
|
-
afterAll(done => {
|
130
|
-
done();
|
131
|
-
jest.clearAllMocks();
|
132
|
-
});
|
133
|
-
|
134
|
-
it('request', async () => {
|
135
|
-
const res = await request(app.callback()).get('/');
|
136
|
-
expect(res.status).toBe(200);
|
137
|
-
});
|
138
|
-
});
|
137
|
+
2. **Create Project**:
|
138
|
+
```bash
|
139
|
+
koatty new awesome-app
|
140
|
+
```
|
139
141
|
|
142
|
+
3. **Run Development Server**:
|
143
|
+
```bash
|
144
|
+
cd awesome-app
|
145
|
+
npm run dev
|
140
146
|
```
|
141
147
|
|
142
|
-
## How to debug
|
143
148
|
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
"version": "0.2.0",
|
148
|
-
"configurations": [
|
149
|
-
{
|
150
|
-
"type": "node",
|
151
|
-
"request": "launch",
|
152
|
-
"name": "TS Program",
|
153
|
-
"args": [
|
154
|
-
"${workspaceRoot}/src/App.ts"
|
155
|
-
],
|
156
|
-
"runtimeArgs": [
|
157
|
-
"--nolazy",
|
158
|
-
"-r",
|
159
|
-
"ts-node/register"
|
160
|
-
],
|
161
|
-
"sourceMaps": true,
|
162
|
-
"cwd": "${workspaceRoot}",
|
163
|
-
"protocol": "inspector",
|
164
|
-
"outputCapture": "std",
|
165
|
-
"internalConsoleOptions": "neverOpen"
|
166
|
-
}
|
167
|
-
]
|
168
|
-
}
|
169
|
-
```
|
170
|
-
Select `TS Program` to debug run. Try to call `http://localhost:3000/` .
|
149
|
+
## Community 🌍
|
150
|
+
|
151
|
+
- [GitHub Discussions](https://github.com/Koatty/koatty/discussions)
|
171
152
|
|
172
|
-
##
|
153
|
+
## Contributors ✨
|
173
154
|
|
174
|
-
|
155
|
+
Thanks to these amazing developers:
|
175
156
|
|
176
|
-
|
157
|
+
<!-- Add contributor list here -->
|
177
158
|
|
178
159
|
|
160
|
+
## License 📄
|
179
161
|
|
162
|
+
BSD-3 © [Koatty Team](https://github.com/Koatty)
|
package/dist/README.md
CHANGED
@@ -1,179 +1,162 @@
|
|
1
|
-
#
|
1
|
+
# Koatty 🚀
|
2
2
|
|
3
|
-
|
3
|
+
[](https://www.npmjs.com/package/koatty)
|
4
|
+
[](https://opensource.org/licenses/BSD-3-Clause)
|
4
5
|
|
5
|
-
|
6
|
+
Koa2 + Typescript + IOC = koatty. **Koatty** is a progressive Node.js framework for building efficient and scalable server-side applications. Perfect for crafting enterprise-level APIs, microservices, and full-stack applications with TypeScript excellence.
|
6
7
|
|
7
|
-
|
8
|
+
## Why Koatty? 💡
|
8
9
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
* OpenTelemetry . 💪
|
18
|
-
|
19
|
-
|
20
|
-
## Documentation
|
21
|
-
|
22
|
-
[koatty_doc_CN](https://koatty.org/) (In progress💪)
|
10
|
+
- 🚄 **High Performance**: Built on top of Koa2 with optimized architecture
|
11
|
+
- 🧩 **Full-Featured**: Supports gRPC, HTTP, WebSocket, Schedule tasks, and more
|
12
|
+
- 🧠 **TypeScript First**: Native TypeScript support with elegant OOP design
|
13
|
+
- 🌀 **Spring-like IOC Container**: Powerful dependency injection system with autowiring
|
14
|
+
- ✂️ **AOP Support**: Aspect-oriented programming with decorator-based interceptors
|
15
|
+
- 🔌 **Extensible Architecture**: Plugin system with dependency injection
|
16
|
+
- 📦 **Modern Tooling**: CLI scaffolding, testing utilities, and production-ready configs
|
17
|
+
- 🌐 **Protocol Agnostic**: Write once, deploy as HTTP/gRPC/WebSocket services
|
23
18
|
|
24
19
|
|
25
|
-
##
|
20
|
+
## New features ✨
|
26
21
|
|
27
|
-
|
28
|
-
|
22
|
+
* HTTP、HTTPS、HTTP2、gRPC、WebSocket server.✔️
|
23
|
+
* Support loading configurations based on the environment, support command-line argument parsing(process.argv), and support environment variable parsing(process.env).✔️
|
24
|
+
* `@ExceptionHandler()` Register global exception handling.✔️
|
25
|
+
* Graceful shutdown and pre-exit event.✔️
|
26
|
+
* Supports custom decorators, bound to app events for execution.✔️
|
27
|
+
* GraphQL integration is available. ✔️
|
28
|
+
* Full-stack tracing capability through OpenTelemetry . ✔️
|
29
|
+
* Middleware can be bound to controllers and their method routes.✔️
|
30
|
+
* gRPC streaming is now supported. ✔️
|
31
|
+
* Added support for Swagger OpenAPI 3.0. 💪
|
32
|
+
|
33
|
+
|
34
|
+
## Core Features ✨
|
35
|
+
|
36
|
+
### 📡 Multi-Protocol Support
|
37
|
+
```typescript
|
38
|
+
// config/config.ts
|
39
|
+
export default {
|
40
|
+
...
|
41
|
+
protocol: "grpc", // Server protocol 'http' | 'https' | 'http2' | 'grpc' | 'ws' | 'wss' | 'graphql'
|
42
|
+
...
|
43
|
+
}
|
29
44
|
```
|
30
45
|
|
31
|
-
|
46
|
+
### 💉 Dependency Injection
|
47
|
+
```typescript
|
48
|
+
@Service()
|
49
|
+
export class UserService {
|
50
|
+
async findUser(id: number) {
|
51
|
+
return { id, name: 'Koatty User' };
|
52
|
+
}
|
53
|
+
}
|
32
54
|
|
33
|
-
|
55
|
+
@Controller()
|
56
|
+
export class IndexController {
|
57
|
+
app: App;
|
58
|
+
ctx: KoattyContext;
|
59
|
+
@Config("protocol")
|
60
|
+
conf: { protocol: string };
|
61
|
+
...
|
34
62
|
|
35
|
-
|
36
|
-
|
63
|
+
@Autowired()
|
64
|
+
private userService: UserService;
|
37
65
|
|
66
|
+
async test(id: number) {
|
67
|
+
const info = await this.userService.findUser(id);
|
68
|
+
...
|
69
|
+
}
|
70
|
+
}
|
38
71
|
```
|
39
72
|
|
40
|
-
###
|
73
|
+
### ✂️ Aspect-Oriented Programming
|
74
|
+
```javascript
|
75
|
+
@Aspect()
|
76
|
+
export class LogAspect implements IAspect {
|
77
|
+
app: App;
|
41
78
|
|
42
|
-
|
43
|
-
|
79
|
+
run() {
|
80
|
+
console.log('LogAspect');
|
81
|
+
}
|
82
|
+
}
|
44
83
|
|
45
|
-
|
84
|
+
// Apply aspect to controller
|
85
|
+
@Controller()
|
86
|
+
@BeforeEach(LogAspect)
|
87
|
+
export class UserController {
|
88
|
+
...
|
89
|
+
@After(LogAspect)
|
90
|
+
test() {
|
91
|
+
...
|
92
|
+
}
|
93
|
+
}
|
46
94
|
```
|
47
95
|
|
48
|
-
###
|
49
|
-
|
96
|
+
### 🔌 Plugin System
|
97
|
+
```javascript
|
98
|
+
// plugin/logger.ts
|
99
|
+
export class LoggerPlugin implements IPlugin {
|
100
|
+
app: App;
|
101
|
+
|
102
|
+
run() {
|
103
|
+
// todo something or hook on app.event
|
104
|
+
Logger.Debug("LoggerPlugin");
|
105
|
+
return Promise.resolve();
|
106
|
+
}
|
107
|
+
}
|
50
108
|
```
|
51
|
-
npm run dev
|
52
109
|
|
53
|
-
// or
|
54
|
-
npm start
|
55
|
-
```
|
56
110
|
|
57
|
-
##
|
111
|
+
## Benchmarks 📊
|
58
112
|
|
59
|
-
|
113
|
+
| Framework | Requests/sec | Latency | Memory Usage |
|
114
|
+
| ---------- | ------------ | ------- | ------------ |
|
115
|
+
| **Koatty** | 13,321 | 1.43ms | 54MB |
|
116
|
+
| Express | 12,456 | 1.45ms | 52MB |
|
117
|
+
| NestJS | 11,892 | 1.51ms | 63MB |
|
60
118
|
|
61
|
-
|
62
|
-
import { Controller, Autowired, GetMapping, RequestBody, PathVariable,
|
63
|
-
PostMapping, RequestMapping, RequestMethod, Valid, Output } from "koatty";
|
64
|
-
import { TestDTO } from "../model/dto/TestDTO";
|
65
|
-
import { TestService } from "../service/TestService";
|
66
|
-
import { App } from "../App";
|
119
|
+
*Tested on AWS t3.micro with 100 concurrent connections*
|
67
120
|
|
68
|
-
|
69
|
-
export class IndexController {
|
70
|
-
app: App;
|
71
|
-
ctx: KoattyContext;
|
121
|
+
## Documentation 📚
|
72
122
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
*
|
79
|
-
*/
|
80
|
-
constructor(ctx: KoattyContext) {
|
81
|
-
this.ctx = ctx;
|
82
|
-
}
|
123
|
+
- [中文文档](https://koatty.org/)
|
124
|
+
- [Getting Started Guide](https://github.com/Koatty/koatty_doc/blob/master/docs/README-en.md)
|
125
|
+
- [API Reference](https://koatty.org/#/?id=api)
|
126
|
+
- [Recipes & Best Practices](https://github.com/Koatty/koatty_awesome)
|
127
|
+
- [Example](https://github.com/Koatty/koatty_demo)
|
83
128
|
|
84
|
-
@GetMapping('/')
|
85
|
-
index() {
|
86
|
-
return Output.ok("Hello, koatty!");
|
87
|
-
}
|
88
129
|
|
89
|
-
|
90
|
-
async default(@PathVariable("name") @Valid("IsNotEmpty") name: string) {
|
91
|
-
try {
|
92
|
-
const info = await this.testService.sayHello(name);
|
93
|
-
return Output.ok(this.ctx, "success", info);
|
94
|
-
} catch (err: Error) {
|
95
|
-
return Output.fail(this.ctx, err.message));
|
96
|
-
}
|
97
|
-
}
|
130
|
+
## Quick Start ⚡
|
98
131
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
return Output.ok(this.ctx, "test", params);
|
103
|
-
}
|
104
|
-
}
|
132
|
+
1. **Install CLI**:
|
133
|
+
```bash
|
134
|
+
npm install -g koatty_cli
|
105
135
|
```
|
106
136
|
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
```javascript
|
112
|
-
import request from 'supertest';
|
113
|
-
import { ExecBootStrap } from 'koatty';
|
114
|
-
import { App } from '../src/App';
|
115
|
-
|
116
|
-
describe('UT example', () => {
|
117
|
-
|
118
|
-
let app: KoattyApplication;
|
119
|
-
beforeAll(async () => {
|
120
|
-
jest.useFakeTimers();
|
121
|
-
// test env
|
122
|
-
process.env.KOATTY_ENV = 'ts-node';
|
123
|
-
app = await ExecBootStrap()(App);
|
124
|
-
// app.use(async (ctx: any) => {
|
125
|
-
// ctx.body = 'Hello, World!';
|
126
|
-
// });
|
127
|
-
});
|
128
|
-
|
129
|
-
afterAll(done => {
|
130
|
-
done();
|
131
|
-
jest.clearAllMocks();
|
132
|
-
});
|
133
|
-
|
134
|
-
it('request', async () => {
|
135
|
-
const res = await request(app.callback()).get('/');
|
136
|
-
expect(res.status).toBe(200);
|
137
|
-
});
|
138
|
-
});
|
137
|
+
2. **Create Project**:
|
138
|
+
```bash
|
139
|
+
koatty new awesome-app
|
140
|
+
```
|
139
141
|
|
142
|
+
3. **Run Development Server**:
|
143
|
+
```bash
|
144
|
+
cd awesome-app
|
145
|
+
npm run dev
|
140
146
|
```
|
141
147
|
|
142
|
-
## How to debug
|
143
148
|
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
"version": "0.2.0",
|
148
|
-
"configurations": [
|
149
|
-
{
|
150
|
-
"type": "node",
|
151
|
-
"request": "launch",
|
152
|
-
"name": "TS Program",
|
153
|
-
"args": [
|
154
|
-
"${workspaceRoot}/src/App.ts"
|
155
|
-
],
|
156
|
-
"runtimeArgs": [
|
157
|
-
"--nolazy",
|
158
|
-
"-r",
|
159
|
-
"ts-node/register"
|
160
|
-
],
|
161
|
-
"sourceMaps": true,
|
162
|
-
"cwd": "${workspaceRoot}",
|
163
|
-
"protocol": "inspector",
|
164
|
-
"outputCapture": "std",
|
165
|
-
"internalConsoleOptions": "neverOpen"
|
166
|
-
}
|
167
|
-
]
|
168
|
-
}
|
169
|
-
```
|
170
|
-
Select `TS Program` to debug run. Try to call `http://localhost:3000/` .
|
149
|
+
## Community 🌍
|
150
|
+
|
151
|
+
- [GitHub Discussions](https://github.com/Koatty/koatty/discussions)
|
171
152
|
|
172
|
-
##
|
153
|
+
## Contributors ✨
|
173
154
|
|
174
|
-
|
155
|
+
Thanks to these amazing developers:
|
175
156
|
|
176
|
-
|
157
|
+
<!-- Add contributor list here -->
|
177
158
|
|
178
159
|
|
160
|
+
## License 📄
|
179
161
|
|
162
|
+
BSD-3 © [Koatty Team](https://github.com/Koatty)
|