@ooneex/cli 0.1.0 → 0.3.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/README.md +379 -0
- package/dist/index.js +2 -2
- package/dist/index.js.map +4 -4
- package/package.json +8 -27
package/README.md
CHANGED
|
@@ -1 +1,380 @@
|
|
|
1
1
|
# @ooneex/cli
|
|
2
|
+
|
|
3
|
+
A powerful command-line interface for scaffolding Ooneex applications, modules, controllers, services, and more. This package provides an interactive CLI tool with code generation capabilities to accelerate development workflows.
|
|
4
|
+
|
|
5
|
+

|
|
6
|
+

|
|
7
|
+

|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
✅ **Application Scaffolding** - Generate complete Ooneex applications with best practices
|
|
12
|
+
|
|
13
|
+
✅ **Module Generation** - Create feature modules with controllers, entities, and services
|
|
14
|
+
|
|
15
|
+
✅ **Controller Generation** - Generate HTTP and WebSocket controllers with route definitions
|
|
16
|
+
|
|
17
|
+
✅ **Service Generation** - Create service classes with dependency injection
|
|
18
|
+
|
|
19
|
+
✅ **Entity Generation** - Generate TypeORM entities with common fields
|
|
20
|
+
|
|
21
|
+
✅ **CRUD Generation** - Scaffold complete CRUD operations in one command
|
|
22
|
+
|
|
23
|
+
✅ **Interactive Prompts** - User-friendly prompts for customization
|
|
24
|
+
|
|
25
|
+
✅ **Docker Support** - Generate Docker configuration files
|
|
26
|
+
|
|
27
|
+
✅ **Migration & Seeds** - Create database migrations and seed files
|
|
28
|
+
|
|
29
|
+
## Installation
|
|
30
|
+
|
|
31
|
+
### Bun (Global)
|
|
32
|
+
```bash
|
|
33
|
+
bun add -g @ooneex/cli
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Bun (Local)
|
|
37
|
+
```bash
|
|
38
|
+
bun add -D @ooneex/cli
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### pnpm
|
|
42
|
+
```bash
|
|
43
|
+
pnpm add -D @ooneex/cli
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Yarn
|
|
47
|
+
```bash
|
|
48
|
+
yarn add -D @ooneex/cli
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### npm
|
|
52
|
+
```bash
|
|
53
|
+
npm install -D @ooneex/cli
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Usage
|
|
57
|
+
|
|
58
|
+
### Creating a New Application
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
ooneex make:app
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
This command will interactively prompt you for:
|
|
65
|
+
- Application name
|
|
66
|
+
- Destination path
|
|
67
|
+
|
|
68
|
+
The generated application includes:
|
|
69
|
+
- Configured `package.json` with all necessary dependencies
|
|
70
|
+
- TypeScript configuration
|
|
71
|
+
- Biome linting setup
|
|
72
|
+
- Nx workspace configuration
|
|
73
|
+
- Git configuration with `.gitignore`
|
|
74
|
+
- Initial app module
|
|
75
|
+
|
|
76
|
+
### Generating a Module
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
ooneex make:module
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Creates a new feature module with:
|
|
83
|
+
- Module structure
|
|
84
|
+
- Controllers directory
|
|
85
|
+
- Services directory
|
|
86
|
+
- Entities directory
|
|
87
|
+
- Repository files
|
|
88
|
+
- Test scaffolding
|
|
89
|
+
|
|
90
|
+
### Generating a Controller
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
ooneex make:controller
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Interactive prompts will ask for:
|
|
97
|
+
- Controller name
|
|
98
|
+
- Socket or HTTP controller
|
|
99
|
+
- Route namespace (api, admin, public, etc.)
|
|
100
|
+
- Resource name
|
|
101
|
+
- Route action
|
|
102
|
+
- Route path
|
|
103
|
+
- HTTP method (for HTTP controllers)
|
|
104
|
+
|
|
105
|
+
**Example output:**
|
|
106
|
+
```
|
|
107
|
+
✔ src/controllers/UserListController.ts created successfully
|
|
108
|
+
✔ src/types/routes/api.users.list.ts created successfully
|
|
109
|
+
✔ tests/controllers/UserListController.spec.ts created successfully
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Generating a Service
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
ooneex make:service
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Creates a service class with:
|
|
119
|
+
- Dependency injection decorator
|
|
120
|
+
- Interface implementation
|
|
121
|
+
- Test file
|
|
122
|
+
|
|
123
|
+
### Generating an Entity
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
ooneex make:entity
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Creates a TypeORM entity with:
|
|
130
|
+
- Base entity fields (id, timestamps)
|
|
131
|
+
- Primary column configuration
|
|
132
|
+
- Repository integration
|
|
133
|
+
|
|
134
|
+
### Generating CRUD Operations
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
ooneex make:crud
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Scaffolds complete CRUD operations including:
|
|
141
|
+
- List controller
|
|
142
|
+
- Show controller
|
|
143
|
+
- Create controller
|
|
144
|
+
- Update controller
|
|
145
|
+
- Delete controller
|
|
146
|
+
- Associated route types
|
|
147
|
+
- Test files
|
|
148
|
+
|
|
149
|
+
### Additional Commands
|
|
150
|
+
|
|
151
|
+
| Command | Description |
|
|
152
|
+
|---------|-------------|
|
|
153
|
+
| `make:ai` | Generate AI service integration |
|
|
154
|
+
| `make:analytics` | Generate analytics service |
|
|
155
|
+
| `make:cache` | Generate cache service |
|
|
156
|
+
| `make:cron` | Generate cron job class |
|
|
157
|
+
| `make:database` | Generate database configuration |
|
|
158
|
+
| `make:docker` | Generate Docker files |
|
|
159
|
+
| `make:logger` | Generate logger service |
|
|
160
|
+
| `make:mailer` | Generate mailer service |
|
|
161
|
+
| `make:middleware` | Generate middleware class |
|
|
162
|
+
| `make:migration` | Generate database migration |
|
|
163
|
+
| `make:permission` | Generate permission class |
|
|
164
|
+
| `make:pubsub` | Generate pub/sub handler |
|
|
165
|
+
| `make:repository` | Generate repository class |
|
|
166
|
+
| `make:seed` | Generate database seeder |
|
|
167
|
+
| `make:storage` | Generate storage service |
|
|
168
|
+
| `make:translation` | Generate translation files |
|
|
169
|
+
|
|
170
|
+
## API Reference
|
|
171
|
+
|
|
172
|
+
### Interfaces
|
|
173
|
+
|
|
174
|
+
#### `ICommand<Options>`
|
|
175
|
+
|
|
176
|
+
Interface for creating custom CLI commands.
|
|
177
|
+
|
|
178
|
+
```typescript
|
|
179
|
+
interface ICommand<Options extends Record<string, unknown> = Record<string, unknown>> {
|
|
180
|
+
run: (options: Options) => Promise<void> | void;
|
|
181
|
+
getName: () => string;
|
|
182
|
+
getDescription: () => string;
|
|
183
|
+
}
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### Types
|
|
187
|
+
|
|
188
|
+
#### `CommandClassType`
|
|
189
|
+
|
|
190
|
+
```typescript
|
|
191
|
+
type CommandClassType = new (...args: any[]) => ICommand;
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### Decorators
|
|
195
|
+
|
|
196
|
+
#### `@decorator.command()`
|
|
197
|
+
|
|
198
|
+
Decorator to register a command with the CLI container.
|
|
199
|
+
|
|
200
|
+
```typescript
|
|
201
|
+
import { decorator } from '@ooneex/cli';
|
|
202
|
+
import type { ICommand } from '@ooneex/cli';
|
|
203
|
+
|
|
204
|
+
@decorator.command()
|
|
205
|
+
class MyCustomCommand implements ICommand {
|
|
206
|
+
public getName(): string {
|
|
207
|
+
return 'my:command';
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
public getDescription(): string {
|
|
211
|
+
return 'My custom command description';
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
public async run(options: Record<string, unknown>): Promise<void> {
|
|
215
|
+
// Command implementation
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
## Advanced Usage
|
|
221
|
+
|
|
222
|
+
### Creating Custom Commands
|
|
223
|
+
|
|
224
|
+
```typescript
|
|
225
|
+
import { decorator } from '@ooneex/cli';
|
|
226
|
+
import type { ICommand } from '@ooneex/cli';
|
|
227
|
+
import { TerminalLogger } from '@ooneex/logger';
|
|
228
|
+
|
|
229
|
+
type MyCommandOptions = {
|
|
230
|
+
name?: string;
|
|
231
|
+
verbose?: boolean;
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
@decorator.command()
|
|
235
|
+
class MyCustomCommand implements ICommand<MyCommandOptions> {
|
|
236
|
+
private readonly logger = new TerminalLogger();
|
|
237
|
+
|
|
238
|
+
public getName(): string {
|
|
239
|
+
return 'custom:generate';
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
public getDescription(): string {
|
|
243
|
+
return 'Generate custom files for the project';
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
public async run(options: MyCommandOptions): Promise<void> {
|
|
247
|
+
const { name, verbose } = options;
|
|
248
|
+
|
|
249
|
+
if (verbose) {
|
|
250
|
+
this.logger.info('Running in verbose mode');
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// Custom generation logic
|
|
254
|
+
await Bun.write('output.ts', 'export const example = true;');
|
|
255
|
+
|
|
256
|
+
this.logger.success('File generated successfully', undefined, {
|
|
257
|
+
showTimestamp: false,
|
|
258
|
+
showArrow: false,
|
|
259
|
+
useSymbol: true,
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
### Using with Arguments
|
|
266
|
+
|
|
267
|
+
```bash
|
|
268
|
+
ooneex make:controller --name UserList
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
### Programmatic Usage
|
|
272
|
+
|
|
273
|
+
```typescript
|
|
274
|
+
import { getCommand } from '@ooneex/cli';
|
|
275
|
+
|
|
276
|
+
const command = getCommand('make:controller');
|
|
277
|
+
|
|
278
|
+
if (command) {
|
|
279
|
+
await command.run({
|
|
280
|
+
name: 'UserList',
|
|
281
|
+
isSocket: false,
|
|
282
|
+
route: {
|
|
283
|
+
name: 'api.users.list',
|
|
284
|
+
path: '/api/users',
|
|
285
|
+
method: 'GET'
|
|
286
|
+
}
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
### Generated Application Structure
|
|
292
|
+
|
|
293
|
+
When running `make:app`, the following structure is created:
|
|
294
|
+
|
|
295
|
+
```
|
|
296
|
+
my-app/
|
|
297
|
+
├── modules/
|
|
298
|
+
│ └── app/
|
|
299
|
+
│ ├── src/
|
|
300
|
+
│ │ ├── controllers/
|
|
301
|
+
│ │ ├── entities/
|
|
302
|
+
│ │ ├── services/
|
|
303
|
+
│ │ └── index.ts
|
|
304
|
+
│ ├── tests/
|
|
305
|
+
│ └── package.json
|
|
306
|
+
├── .commitlintrc.ts
|
|
307
|
+
├── .gitignore
|
|
308
|
+
├── biome.jsonc
|
|
309
|
+
├── bunfig.toml
|
|
310
|
+
├── nx.json
|
|
311
|
+
├── package.json
|
|
312
|
+
└── tsconfig.json
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
### Generated Module Structure
|
|
316
|
+
|
|
317
|
+
```
|
|
318
|
+
modules/
|
|
319
|
+
└── user/
|
|
320
|
+
├── src/
|
|
321
|
+
│ ├── controllers/
|
|
322
|
+
│ ├── entities/
|
|
323
|
+
│ │ └── UserEntity.ts
|
|
324
|
+
│ ├── services/
|
|
325
|
+
│ │ └── UserService.ts
|
|
326
|
+
│ ├── types/
|
|
327
|
+
│ │ └── routes/
|
|
328
|
+
│ └── index.ts
|
|
329
|
+
├── tests/
|
|
330
|
+
│ ├── controllers/
|
|
331
|
+
│ ├── entities/
|
|
332
|
+
│ └── services/
|
|
333
|
+
├── package.json
|
|
334
|
+
└── bunup.config.ts
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
## Error Handling
|
|
338
|
+
|
|
339
|
+
The CLI provides informative error messages when something goes wrong:
|
|
340
|
+
|
|
341
|
+
```typescript
|
|
342
|
+
import { CommandException } from '@ooneex/cli';
|
|
343
|
+
|
|
344
|
+
// Errors are automatically caught and displayed
|
|
345
|
+
// with proper formatting via TerminalLogger
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
## Environment
|
|
349
|
+
|
|
350
|
+
The CLI respects the following environment variables:
|
|
351
|
+
|
|
352
|
+
| Variable | Description |
|
|
353
|
+
|----------|-------------|
|
|
354
|
+
| `CWD` | Custom working directory for file generation |
|
|
355
|
+
|
|
356
|
+
## License
|
|
357
|
+
|
|
358
|
+
This project is licensed under the MIT License - see the [LICENSE](./LICENSE) file for details.
|
|
359
|
+
|
|
360
|
+
## Contributing
|
|
361
|
+
|
|
362
|
+
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
|
|
363
|
+
|
|
364
|
+
### Development Setup
|
|
365
|
+
|
|
366
|
+
1. Clone the repository
|
|
367
|
+
2. Install dependencies: `bun install`
|
|
368
|
+
3. Run tests: `bun run test`
|
|
369
|
+
4. Build the project: `bun run build`
|
|
370
|
+
|
|
371
|
+
### Guidelines
|
|
372
|
+
|
|
373
|
+
- Write tests for new features
|
|
374
|
+
- Follow the existing code style
|
|
375
|
+
- Update documentation for API changes
|
|
376
|
+
- Ensure all tests pass before submitting PR
|
|
377
|
+
|
|
378
|
+
---
|
|
379
|
+
|
|
380
|
+
Made with ❤️ by the Ooneex team
|
package/dist/index.js
CHANGED
|
@@ -923,7 +923,7 @@ describe("{{NAME}}Module", () => {
|
|
|
923
923
|
"include": ["src/**/*.ts", "src/**/*.tsx", "tests/**/*.ts", "tests/**/*.tsx"],
|
|
924
924
|
"exclude": ["node_modules", "dist"]
|
|
925
925
|
}
|
|
926
|
-
`;class T4{getName(){return"make:module"}getDescription(){return"Generate a new module"}async run(Y){let{cwd:X=process.cwd(),silent:$=!1,skipBin:Z=!1,skipMigrations:H=!1,skipSeeds:W=!1,bunupPackages:G="external"}=Y,{name:J}=Y;if(!J)J=await w0({message:"Enter module name"});let L=O0(J).replace(/Module$/,""),A=BX(L),E=gY(X,"modules",A),z=gY(E,"bin"),B=gY(E,"src"),D=gY(E,"tests"),U=Es.replace(/{{NAME}}/g,L),S=Ls.replace(/{{NAME}}/g,L),I=zs.replace(/{{NAME}}/g,A),R=Ds.replace(/{{NAME}}/g,L),F=Js.replace('packages: "external"',`packages: "${G}"`);if(await Bun.write(gY(E,"bunup.config.ts"),F),!Z)await Bun.write(gY(z,"migration","up.ts"),As),await Bun.write(gY(z,"seed","run.ts"),Bs);if(await Bun.write(gY(B,`${L}Module.ts`),U),await Bun.write(gY(B,"index.ts"),S),!H)await Bun.write(gY(B,"migrations","migrations.ts"),"");if(!W)await Bun.write(gY(B,"seeds","seeds.ts"),"");if(await Bun.write(gY(E,"package.json"),I),await Bun.write(gY(E,"tsconfig.json"),Us),await Bun.write(gY(D,`${L}Module.spec.ts`),R),!$)new R0().success(`modules/${A} created successfully`,void 0,{showTimestamp:!1,showArrow:!1,useSymbol:!0})}}T4=I0([P0.command()],T4);class tH{getName(){return"make:app"}getDescription(){return"Generate a new application"}async run(Y){let{name:X,destination:$}=Y;if(!X)X=await w0({message:"Enter application name"});let Z=BX(X);if(!$)$=await au({message:"Enter destination path",initial:Z});let H=Ws.replace(/{{NAME}}/g,Z);await Bun.write(A6($,".commitlintrc.ts"),eu),await Bun.write(A6($,".gitignore"),Ys),await Bun.write(A6($,"biome.jsonc"),Xs),await Bun.write(A6($,"bunfig.toml"),$s),await Bun.write(A6($,"nx.json"),Hs),await Bun.write(A6($,"package.json"),H),await Bun.write(A6($,"tsconfig.json"),Gs),await new T4().run({name:"app",cwd:$,silent:!0,skipBin:!0,skipMigrations:!0,skipSeeds:!0,bunupPackages:"bundle"}),await Bun.write(A6($,"modules","app","src","index.ts"),Zs),await dV0`cd ${$} && bun add ${["@ooneex/analytics","@ooneex/app","@ooneex/app-env","@ooneex/cache","@ooneex/database","@ooneex/logger","@ooneex/mailer","@ooneex/module","@ooneex/rate-limit","@ooneex/storage","reflect-metadata"]} && bun add ${["@ooneex/cli","@biomejs/biome","@commitlint/cli","@commitlint/config-conventional","@commitlint/prompt-cli","@nx/js","@nx/workspace","@swc-node/register","@swc/core","@swc/helpers","@types/bun","@types/node","@typescript/native-preview","bunup","husky","lint-staged","nx","typescript","undici-types"]} -D`,new R0().success(`${Z} created successfully at ${$}`,void 0,{showTimestamp:!1,showArrow:!1,useSymbol:!0})}}tH=I0([P0.command()],tH);import{join as E6}from"path";var Vs=`import { describe, expect, test } from "bun:test";
|
|
926
|
+
`;class T4{getName(){return"make:module"}getDescription(){return"Generate a new module"}async run(Y){let{cwd:X=process.cwd(),silent:$=!1,skipBin:Z=!1,skipMigrations:H=!1,skipSeeds:W=!1,bunupPackages:G="external"}=Y,{name:J}=Y;if(!J)J=await w0({message:"Enter module name"});let L=O0(J).replace(/Module$/,""),A=BX(L),E=gY(X,"modules",A),z=gY(E,"bin"),B=gY(E,"src"),D=gY(E,"tests"),U=Es.replace(/{{NAME}}/g,L),S=Ls.replace(/{{NAME}}/g,L),I=zs.replace(/{{NAME}}/g,A),R=Ds.replace(/{{NAME}}/g,L),F=Js.replace('packages: "external"',`packages: "${G}"`);if(await Bun.write(gY(E,"bunup.config.ts"),F),!Z)await Bun.write(gY(z,"migration","up.ts"),As),await Bun.write(gY(z,"seed","run.ts"),Bs);if(await Bun.write(gY(B,`${L}Module.ts`),U),await Bun.write(gY(B,"index.ts"),S),!H)await Bun.write(gY(B,"migrations","migrations.ts"),"");if(!W)await Bun.write(gY(B,"seeds","seeds.ts"),"");if(await Bun.write(gY(E,"package.json"),I),await Bun.write(gY(E,"tsconfig.json"),Us),await Bun.write(gY(D,`${L}Module.spec.ts`),R),!$)new R0().success(`modules/${A} created successfully`,void 0,{showTimestamp:!1,showArrow:!1,useSymbol:!0})}}T4=I0([P0.command()],T4);class tH{getName(){return"make:app"}getDescription(){return"Generate a new application"}async run(Y){let{name:X,destination:$}=Y;if(!X)X=await w0({message:"Enter application name"});let Z=BX(X);if(!$)$=await au({message:"Enter destination path",initial:Z});let H=Ws.replace(/{{NAME}}/g,Z);await Bun.write(A6($,".commitlintrc.ts"),eu),await Bun.write(A6($,".gitignore"),Ys),await Bun.write(A6($,"biome.jsonc"),Xs),await Bun.write(A6($,"bunfig.toml"),$s),await Bun.write(A6($,"nx.json"),Hs),await Bun.write(A6($,"package.json"),H),await Bun.write(A6($,"tsconfig.json"),Gs),await new T4().run({name:"app",cwd:$,silent:!0,skipBin:!0,skipMigrations:!0,skipSeeds:!0,bunupPackages:"bundle"}),await Bun.write(A6($,"modules","app","src","index.ts"),Zs),await dV0`cd ${$} && bun add ${["@ooneex/routing@0.2.0","@ooneex/analytics","@ooneex/app","@ooneex/app-env","@ooneex/cache","@ooneex/database","@ooneex/logger","@ooneex/mailer","@ooneex/module","@ooneex/rate-limit","@ooneex/storage","reflect-metadata"]} && bun add ${["@ooneex/cli","@biomejs/biome","@commitlint/cli","@commitlint/config-conventional","@commitlint/prompt-cli","@nx/js","@nx/workspace","@swc-node/register","@swc/core","@swc/helpers","@types/bun","@types/node","@typescript/native-preview","bunup","husky","lint-staged","nx","typescript","undici-types"]} -D`,new R0().success(`${Z} created successfully at ${$}`,void 0,{showTimestamp:!1,showArrow:!1,useSymbol:!0})}}tH=I0([P0.command()],tH);import{join as E6}from"path";var Vs=`import { describe, expect, test } from "bun:test";
|
|
927
927
|
import { {{NAME}}CacheAdapter } from "@/cache/{{NAME}}CacheAdapter";
|
|
928
928
|
|
|
929
929
|
describe("{{NAME}}CacheAdapter", () => {
|
|
@@ -2558,4 +2558,4 @@ export default defineConfig({
|
|
|
2558
2558
|
`),process.exit(1);var ni=jp(oi);if(!ni)pE.info(`No commands found
|
|
2559
2559
|
`),process.exit(1);try{await ni.run(FF0)}catch(Y){pE.error(Y,void 0,{showArrow:!1,showTimestamp:!1,showLevel:!1}),process.exit(1)}
|
|
2560
2560
|
|
|
2561
|
-
//# debugId=
|
|
2561
|
+
//# debugId=AC9A96DEDE6CB9FC64756E2164756E21
|