@svton/cli 1.2.0 → 1.2.2

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.
Files changed (103) hide show
  1. package/dist/index.js +20 -7
  2. package/dist/index.mjs +20 -7
  3. package/features.json +339 -0
  4. package/package.json +4 -1
  5. package/templates/apps/admin/next-env.d.ts +2 -0
  6. package/templates/apps/admin/next.config.js +15 -0
  7. package/templates/apps/admin/package.json.tpl +54 -0
  8. package/templates/apps/admin/postcss.config.js +6 -0
  9. package/templates/apps/admin/src/app/globals.css +37 -0
  10. package/templates/apps/admin/src/app/layout.tsx +19 -0
  11. package/templates/apps/admin/src/app/login/page.tsx +96 -0
  12. package/templates/apps/admin/src/app/page.tsx +8 -0
  13. package/templates/apps/admin/src/app/users/page.tsx +165 -0
  14. package/templates/apps/admin/src/components/ui/switch.tsx +29 -0
  15. package/templates/apps/admin/src/hooks/useAPI.ts +130 -0
  16. package/templates/apps/admin/src/lib/api-client.ts +112 -0
  17. package/templates/apps/admin/src/lib/api-server.ts +95 -0
  18. package/templates/apps/admin/tailwind.config.js +54 -0
  19. package/templates/apps/admin/tsconfig.json +22 -0
  20. package/templates/apps/backend/.env.example +29 -0
  21. package/templates/apps/backend/nest-cli.json +8 -0
  22. package/templates/apps/backend/package.json.tpl +57 -0
  23. package/templates/apps/backend/prisma/schema.prisma +72 -0
  24. package/templates/apps/backend/prisma/seed.ts +32 -0
  25. package/templates/apps/backend/src/app.controller.ts +15 -0
  26. package/templates/apps/backend/src/app.module.ts +85 -0
  27. package/templates/apps/backend/src/app.service.ts +12 -0
  28. package/templates/apps/backend/src/auth/auth.controller.ts +31 -0
  29. package/templates/apps/backend/src/auth/auth.module.ts +27 -0
  30. package/templates/apps/backend/src/auth/auth.service.ts +89 -0
  31. package/templates/apps/backend/src/auth/jwt-auth.guard.ts +5 -0
  32. package/templates/apps/backend/src/auth/jwt.strategy.ts +27 -0
  33. package/templates/apps/backend/src/config/env.schema.ts +35 -0
  34. package/templates/apps/backend/src/main.ts +51 -0
  35. package/templates/apps/backend/src/object-storage/object-storage.controller.ts +114 -0
  36. package/templates/apps/backend/src/object-storage/object-storage.module.ts +7 -0
  37. package/templates/apps/backend/src/prisma/prisma.module.ts +9 -0
  38. package/templates/apps/backend/src/prisma/prisma.service.ts +13 -0
  39. package/templates/apps/backend/src/user/user.controller.ts +50 -0
  40. package/templates/apps/backend/src/user/user.module.ts +12 -0
  41. package/templates/apps/backend/src/user/user.service.ts +117 -0
  42. package/templates/apps/backend/tsconfig.json +23 -0
  43. package/templates/apps/mobile/babel.config.js +8 -0
  44. package/templates/apps/mobile/config/index.ts +65 -0
  45. package/templates/apps/mobile/package.json.tpl +48 -0
  46. package/templates/apps/mobile/project.config.json.tpl +17 -0
  47. package/templates/apps/mobile/src/app.config.ts +9 -0
  48. package/templates/apps/mobile/src/app.scss +4 -0
  49. package/templates/apps/mobile/src/app.ts +8 -0
  50. package/templates/apps/mobile/src/hooks/useAPI.ts +285 -0
  51. package/templates/apps/mobile/src/pages/index/index.scss +7 -0
  52. package/templates/apps/mobile/src/pages/index/index.tsx +49 -0
  53. package/templates/apps/mobile/src/services/api.ts +155 -0
  54. package/templates/apps/mobile/src/services/upload.service.ts +41 -0
  55. package/templates/apps/mobile/tsconfig.json +21 -0
  56. package/templates/configs/authz.config.ts +10 -0
  57. package/templates/configs/cache.config.ts +14 -0
  58. package/templates/configs/oauth.config.ts +20 -0
  59. package/templates/configs/payment.config.ts +44 -0
  60. package/templates/configs/queue.config.ts +21 -0
  61. package/templates/configs/rate-limit.config.ts +16 -0
  62. package/templates/configs/sms.config.ts +11 -0
  63. package/templates/configs/storage.config.ts +14 -0
  64. package/templates/examples/README.md +258 -0
  65. package/templates/examples/authz/README.md +273 -0
  66. package/templates/examples/authz/roles.guard.ts +37 -0
  67. package/templates/examples/authz/user.controller.ts +116 -0
  68. package/templates/examples/cache/README.md +82 -0
  69. package/templates/examples/cache/user.controller.ts +42 -0
  70. package/templates/examples/cache/user.service.ts +78 -0
  71. package/templates/examples/oauth/README.md +192 -0
  72. package/templates/examples/oauth/auth.controller.ts +99 -0
  73. package/templates/examples/oauth/auth.service.ts +97 -0
  74. package/templates/examples/payment/README.md +151 -0
  75. package/templates/examples/payment/order.controller.ts +56 -0
  76. package/templates/examples/payment/order.service.ts +132 -0
  77. package/templates/examples/payment/webhook.controller.ts +73 -0
  78. package/templates/examples/queue/README.md +134 -0
  79. package/templates/examples/queue/email.controller.ts +34 -0
  80. package/templates/examples/queue/email.processor.ts +68 -0
  81. package/templates/examples/queue/email.service.ts +64 -0
  82. package/templates/examples/rate-limit/README.md +249 -0
  83. package/templates/examples/rate-limit/api.controller.ts +113 -0
  84. package/templates/examples/sms/README.md +121 -0
  85. package/templates/examples/sms/sms.service.ts +69 -0
  86. package/templates/examples/sms/verification.controller.ts +100 -0
  87. package/templates/examples/storage/README.md +224 -0
  88. package/templates/examples/storage/upload.controller.ts +117 -0
  89. package/templates/examples/storage/upload.service.ts +123 -0
  90. package/templates/packages/types/package.json.tpl +16 -0
  91. package/templates/packages/types/src/api.ts +88 -0
  92. package/templates/packages/types/src/common.ts +89 -0
  93. package/templates/packages/types/src/index.ts +3 -0
  94. package/templates/packages/types/tsconfig.json +16 -0
  95. package/templates/skills/authz.skill.md +42 -0
  96. package/templates/skills/base.skill.md +57 -0
  97. package/templates/skills/cache.skill.md +88 -0
  98. package/templates/skills/oauth.skill.md +41 -0
  99. package/templates/skills/payment.skill.md +129 -0
  100. package/templates/skills/queue.skill.md +140 -0
  101. package/templates/skills/rate-limit.skill.md +38 -0
  102. package/templates/skills/sms.skill.md +39 -0
  103. package/templates/skills/storage.skill.md +42 -0
@@ -0,0 +1,114 @@
1
+ import {
2
+ Controller,
3
+ Post,
4
+ Get,
5
+ Query,
6
+ Req,
7
+ UnauthorizedException,
8
+ BadRequestException,
9
+ Logger,
10
+ } from '@nestjs/common';
11
+ import { ApiTags, ApiOperation, ApiBearerAuth } from '@nestjs/swagger';
12
+ import { Request } from 'express';
13
+ // import { InjectObjectStorage, ObjectStorageClient } from '@svton/nestjs-object-storage';
14
+
15
+ /**
16
+ * 对象存储控制器示例
17
+ * 演示如何使用 @svton/nestjs-object-storage 进行文件上传和回调验签
18
+ *
19
+ * 使用前请先:
20
+ * 1. 安装依赖:pnpm add @svton/nestjs-object-storage @svton/nestjs-object-storage-qiniu-kodo
21
+ * 2. 在 AppModule 中启用 ObjectStorageModule
22
+ * 3. 配置环境变量
23
+ */
24
+ @ApiTags('对象存储')
25
+ @Controller('object-storage')
26
+ export class ObjectStorageController {
27
+ private readonly logger = new Logger(ObjectStorageController.name);
28
+
29
+ // constructor(
30
+ // @InjectObjectStorage() private readonly storage: ObjectStorageClient,
31
+ // ) {}
32
+
33
+ /**
34
+ * 获取上传预签名 URL
35
+ * 前端使用此 URL 直接上传文件到云存储
36
+ */
37
+ @ApiBearerAuth()
38
+ @Get('presign')
39
+ @ApiOperation({ summary: '获取上传预签名 URL' })
40
+ async getPresignedUrl(
41
+ @Query('key') key: string,
42
+ @Query('contentType') contentType?: string,
43
+ ) {
44
+ if (!key) {
45
+ throw new BadRequestException('key is required');
46
+ }
47
+
48
+ // 示例代码(启用 ObjectStorageModule 后取消注释)
49
+ // const result = await this.storage.presign({
50
+ // key,
51
+ // method: 'PUT',
52
+ // expiresIn: 3600,
53
+ // contentType,
54
+ // callback: {
55
+ // url: 'https://your-domain.com/object-storage/callback',
56
+ // body: 'key=$(key)&hash=$(etag)&bucket=$(bucket)&fsize=$(fsize)',
57
+ // },
58
+ // });
59
+ // return result;
60
+
61
+ return {
62
+ message: 'ObjectStorageModule not enabled. See app.module.ts for setup instructions.',
63
+ };
64
+ }
65
+
66
+ /**
67
+ * 对象存储回调接口
68
+ * 云存储上传完成后会调用此接口进行通知
69
+ *
70
+ * 注意:需要配置 raw body 中间件才能正确验签
71
+ * 参见 main.ts 中的 rawBody 配置
72
+ */
73
+ @Post('callback')
74
+ @ApiOperation({ summary: '对象存储回调(云存储调用)' })
75
+ async handleCallback(@Req() req: Request) {
76
+ // 获取 rawBody(需要在 main.ts 中配置)
77
+ const rawBody = (req as Request & { rawBody?: Buffer }).rawBody;
78
+
79
+ if (!rawBody) {
80
+ this.logger.warn('rawBody not available. Please configure raw body middleware.');
81
+ throw new BadRequestException('rawBody not available');
82
+ }
83
+
84
+ // 示例代码(启用 ObjectStorageModule 后取消注释)
85
+ // const result = await this.storage.verifyCallback({
86
+ // method: req.method,
87
+ // path: req.path,
88
+ // query: req.query as Record<string, string | string[]>,
89
+ // headers: req.headers as Record<string, string | string[]>,
90
+ // rawBody,
91
+ // });
92
+
93
+ // if (!result.isValid) {
94
+ // this.logger.warn('Invalid callback signature');
95
+ // throw new UnauthorizedException('Invalid callback signature');
96
+ // }
97
+
98
+ // this.logger.log(`File uploaded: ${result.key}, size: ${result.size}`);
99
+
100
+ // // 在这里处理业务逻辑,例如:
101
+ // // - 保存文件记录到数据库
102
+ // // - 触发后续处理(如图片压缩、视频转码等)
103
+
104
+ // return {
105
+ // success: true,
106
+ // key: result.key,
107
+ // etag: result.etag,
108
+ // };
109
+
110
+ return {
111
+ message: 'ObjectStorageModule not enabled. See app.module.ts for setup instructions.',
112
+ };
113
+ }
114
+ }
@@ -0,0 +1,7 @@
1
+ import { Module } from '@nestjs/common';
2
+ import { ObjectStorageController } from './object-storage.controller';
3
+
4
+ @Module({
5
+ controllers: [ObjectStorageController],
6
+ })
7
+ export class ObjectStorageExampleModule {}
@@ -0,0 +1,9 @@
1
+ import { Global, Module } from '@nestjs/common';
2
+ import { PrismaService } from './prisma.service';
3
+
4
+ @Global()
5
+ @Module({
6
+ providers: [PrismaService],
7
+ exports: [PrismaService],
8
+ })
9
+ export class PrismaModule {}
@@ -0,0 +1,13 @@
1
+ import { Injectable, OnModuleInit, OnModuleDestroy } from '@nestjs/common';
2
+ import { PrismaClient } from '@prisma/client';
3
+
4
+ @Injectable()
5
+ export class PrismaService extends PrismaClient implements OnModuleInit, OnModuleDestroy {
6
+ async onModuleInit() {
7
+ await this.$connect();
8
+ }
9
+
10
+ async onModuleDestroy() {
11
+ await this.$disconnect();
12
+ }
13
+ }
@@ -0,0 +1,50 @@
1
+ import {
2
+ Controller,
3
+ Get,
4
+ Put,
5
+ Delete,
6
+ Body,
7
+ Param,
8
+ Query,
9
+ UseGuards,
10
+ ParseIntPipe,
11
+ } from '@nestjs/common';
12
+ import { ApiTags, ApiOperation, ApiBearerAuth } from '@nestjs/swagger';
13
+ import { UserService } from './user.service';
14
+ import { JwtAuthGuard } from '../auth/jwt-auth.guard';
15
+ import type { UserListParams, UpdateUserDto } from '{{ORG_NAME}}/types';
16
+
17
+ @ApiTags('用户管理')
18
+ @Controller('users')
19
+ @UseGuards(JwtAuthGuard)
20
+ @ApiBearerAuth()
21
+ export class UserController {
22
+ constructor(private userService: UserService) {}
23
+
24
+ @Get()
25
+ @ApiOperation({ summary: '获取用户列表' })
26
+ async findAll(@Query() params: UserListParams) {
27
+ return this.userService.findAll(params);
28
+ }
29
+
30
+ @Get(':id')
31
+ @ApiOperation({ summary: '获取用户详情' })
32
+ async findOne(@Param('id', ParseIntPipe) id: number) {
33
+ return this.userService.findOne(id);
34
+ }
35
+
36
+ @Put(':id')
37
+ @ApiOperation({ summary: '更新用户' })
38
+ async update(
39
+ @Param('id', ParseIntPipe) id: number,
40
+ @Body() dto: UpdateUserDto,
41
+ ) {
42
+ return this.userService.update(id, dto);
43
+ }
44
+
45
+ @Delete(':id')
46
+ @ApiOperation({ summary: '删除用户' })
47
+ async remove(@Param('id', ParseIntPipe) id: number) {
48
+ return this.userService.remove(id);
49
+ }
50
+ }
@@ -0,0 +1,12 @@
1
+ import { Module } from '@nestjs/common';
2
+ import { UserController } from './user.controller';
3
+ import { UserService } from './user.service';
4
+ import { PrismaModule } from '../prisma/prisma.module';
5
+
6
+ @Module({
7
+ imports: [PrismaModule],
8
+ controllers: [UserController],
9
+ providers: [UserService],
10
+ exports: [UserService],
11
+ })
12
+ export class UserModule {}
@@ -0,0 +1,117 @@
1
+ import { Injectable, NotFoundException } from '@nestjs/common';
2
+ import { PrismaService } from '../prisma/prisma.service';
3
+ import type { UserListParams, UpdateUserDto, PaginatedResponse, UserVo } from '{{ORG_NAME}}/types';
4
+
5
+ @Injectable()
6
+ export class UserService {
7
+ constructor(private prisma: PrismaService) {}
8
+
9
+ async findAll(params: UserListParams): Promise<PaginatedResponse<UserVo>> {
10
+ const { page = 1, pageSize = 10, keyword, role, status } = params;
11
+ const skip = (page - 1) * pageSize;
12
+
13
+ const where: any = {};
14
+ if (keyword) {
15
+ where.OR = [
16
+ { phone: { contains: keyword } },
17
+ { nickname: { contains: keyword } },
18
+ ];
19
+ }
20
+ if (role) where.role = role;
21
+ if (status !== undefined) where.status = status;
22
+
23
+ const [list, total] = await Promise.all([
24
+ this.prisma.user.findMany({
25
+ where,
26
+ skip,
27
+ take: pageSize,
28
+ orderBy: { createdAt: 'desc' },
29
+ select: {
30
+ id: true,
31
+ phone: true,
32
+ nickname: true,
33
+ avatar: true,
34
+ role: true,
35
+ status: true,
36
+ createdAt: true,
37
+ updatedAt: true,
38
+ },
39
+ }),
40
+ this.prisma.user.count({ where }),
41
+ ]);
42
+
43
+ return {
44
+ list: list.map((u) => ({
45
+ ...u,
46
+ role: u.role as any,
47
+ status: u.status as any,
48
+ createdAt: u.createdAt.toISOString(),
49
+ updatedAt: u.updatedAt.toISOString(),
50
+ })),
51
+ total,
52
+ page,
53
+ pageSize,
54
+ totalPages: Math.ceil(total / pageSize),
55
+ };
56
+ }
57
+
58
+ async findOne(id: number): Promise<UserVo> {
59
+ const user = await this.prisma.user.findUnique({
60
+ where: { id },
61
+ select: {
62
+ id: true,
63
+ phone: true,
64
+ nickname: true,
65
+ avatar: true,
66
+ role: true,
67
+ status: true,
68
+ createdAt: true,
69
+ updatedAt: true,
70
+ },
71
+ });
72
+
73
+ if (!user) {
74
+ throw new NotFoundException('用户不存在');
75
+ }
76
+
77
+ return {
78
+ ...user,
79
+ role: user.role as any,
80
+ status: user.status as any,
81
+ createdAt: user.createdAt.toISOString(),
82
+ updatedAt: user.updatedAt.toISOString(),
83
+ };
84
+ }
85
+
86
+ async update(id: number, dto: UpdateUserDto): Promise<UserVo> {
87
+ await this.findOne(id);
88
+
89
+ const user = await this.prisma.user.update({
90
+ where: { id },
91
+ data: dto,
92
+ select: {
93
+ id: true,
94
+ phone: true,
95
+ nickname: true,
96
+ avatar: true,
97
+ role: true,
98
+ status: true,
99
+ createdAt: true,
100
+ updatedAt: true,
101
+ },
102
+ });
103
+
104
+ return {
105
+ ...user,
106
+ role: user.role as any,
107
+ status: user.status as any,
108
+ createdAt: user.createdAt.toISOString(),
109
+ updatedAt: user.updatedAt.toISOString(),
110
+ };
111
+ }
112
+
113
+ async remove(id: number): Promise<void> {
114
+ await this.findOne(id);
115
+ await this.prisma.user.delete({ where: { id } });
116
+ }
117
+ }
@@ -0,0 +1,23 @@
1
+ {
2
+ "compilerOptions": {
3
+ "module": "commonjs",
4
+ "declaration": true,
5
+ "removeComments": true,
6
+ "emitDecoratorMetadata": true,
7
+ "experimentalDecorators": true,
8
+ "allowSyntheticDefaultImports": true,
9
+ "target": "ES2021",
10
+ "sourceMap": true,
11
+ "outDir": "./dist",
12
+ "baseUrl": "./",
13
+ "incremental": true,
14
+ "skipLibCheck": true,
15
+ "strictNullChecks": true,
16
+ "noImplicitAny": true,
17
+ "strictBindCallApply": true,
18
+ "forceConsistentCasingInFileNames": true,
19
+ "noFallthroughCasesInSwitch": true,
20
+ "esModuleInterop": true,
21
+ "resolveJsonModule": true
22
+ }
23
+ }
@@ -0,0 +1,8 @@
1
+ module.exports = {
2
+ presets: [
3
+ ['taro', {
4
+ framework: 'react',
5
+ ts: true,
6
+ }],
7
+ ],
8
+ };
@@ -0,0 +1,65 @@
1
+ import type { UserConfigExport } from '@tarojs/cli';
2
+
3
+ export default {
4
+ projectName: 'mobile',
5
+ date: '2024-1-1',
6
+ designWidth: 750,
7
+ deviceRatio: {
8
+ 640: 2.34 / 2,
9
+ 750: 1,
10
+ 828: 1.81 / 2,
11
+ },
12
+ sourceRoot: 'src',
13
+ outputRoot: 'dist',
14
+ plugins: [],
15
+ defineConstants: {},
16
+ copy: {
17
+ patterns: [],
18
+ options: {},
19
+ },
20
+ framework: 'react',
21
+ compiler: {
22
+ type: 'webpack5',
23
+ prebundle: {
24
+ enable: false,
25
+ },
26
+ },
27
+ mini: {
28
+ postcss: {
29
+ pxtransform: {
30
+ enable: true,
31
+ config: {},
32
+ },
33
+ url: {
34
+ enable: true,
35
+ config: {
36
+ limit: 1024,
37
+ },
38
+ },
39
+ cssModules: {
40
+ enable: false,
41
+ config: {
42
+ namingPattern: 'module',
43
+ generateScopedName: '[name]__[local]___[hash:base64:5]',
44
+ },
45
+ },
46
+ },
47
+ },
48
+ h5: {
49
+ publicPath: '/',
50
+ staticDirectory: 'static',
51
+ postcss: {
52
+ autoprefixer: {
53
+ enable: true,
54
+ config: {},
55
+ },
56
+ cssModules: {
57
+ enable: false,
58
+ config: {
59
+ namingPattern: 'module',
60
+ generateScopedName: '[name]__[local]___[hash:base64:5]',
61
+ },
62
+ },
63
+ },
64
+ },
65
+ } satisfies UserConfigExport;
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "{{ORG_NAME}}/mobile",
3
+ "version": "1.0.0",
4
+ "description": "{{PROJECT_NAME}} 移动端小程序",
5
+ "private": true,
6
+ "templateInfo": {
7
+ "name": "default",
8
+ "typescript": true,
9
+ "css": "sass"
10
+ },
11
+ "scripts": {
12
+ "build:weapp": "taro build --type weapp",
13
+ "dev": "npm run dev:weapp",
14
+ "dev:weapp": "npm run build:weapp -- --watch",
15
+ "dev:h5": "taro build --type h5 --watch",
16
+ "lint": "eslint \"src/**/*.{ts,tsx}\"",
17
+ "type-check": "tsc --noEmit"
18
+ },
19
+ "dependencies": {
20
+ "@babel/runtime": "^7.23.6",
21
+ "@svton/api-client": "^1.0.0",
22
+ "@svton/hooks": "^1.0.0",
23
+ "@svton/taro-ui": "^1.0.0",
24
+ "{{ORG_NAME}}/types": "workspace:*",
25
+ "@tarojs/components": "3.6.23",
26
+ "@tarojs/helper": "3.6.23",
27
+ "@tarojs/plugin-framework-react": "3.6.23",
28
+ "@tarojs/plugin-platform-weapp": "3.6.23",
29
+ "@tarojs/react": "3.6.23",
30
+ "@tarojs/runtime": "3.6.23",
31
+ "@tarojs/taro": "3.6.23",
32
+ "react": "^18.2.0",
33
+ "react-dom": "^18.2.0",
34
+ "zustand": "^4.4.7"
35
+ },
36
+ "devDependencies": {
37
+ "@babel/core": "^7.23.6",
38
+ "@tarojs/cli": "3.6.23",
39
+ "@tarojs/webpack5-runner": "3.6.23",
40
+ "@types/react": "^18.2.45",
41
+ "@typescript-eslint/eslint-plugin": "^6.15.0",
42
+ "@typescript-eslint/parser": "^6.15.0",
43
+ "babel-preset-taro": "3.6.23",
44
+ "eslint": "^8.56.0",
45
+ "eslint-config-taro": "3.6.23",
46
+ "typescript": "^5.3.3"
47
+ }
48
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "miniprogramRoot": "dist/",
3
+ "projectname": "{{PROJECT_NAME}}",
4
+ "description": "{{PROJECT_NAME}} 小程序",
5
+ "appid": "",
6
+ "setting": {
7
+ "urlCheck": true,
8
+ "es6": false,
9
+ "enhance": false,
10
+ "compileHotReLoad": false,
11
+ "postcss": false,
12
+ "minified": false,
13
+ "bundle": false,
14
+ "nodeModules": false
15
+ },
16
+ "compileType": "miniprogram"
17
+ }
@@ -0,0 +1,9 @@
1
+ export default defineAppConfig({
2
+ pages: ['pages/index/index'],
3
+ window: {
4
+ backgroundTextStyle: 'light',
5
+ navigationBarBackgroundColor: '#fff',
6
+ navigationBarTitleText: '首页',
7
+ navigationBarTextStyle: 'black',
8
+ },
9
+ });
@@ -0,0 +1,4 @@
1
+ page {
2
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
3
+ background-color: #f5f5f5;
4
+ }
@@ -0,0 +1,8 @@
1
+ import { PropsWithChildren } from 'react';
2
+ import './app.scss';
3
+
4
+ function App({ children }: PropsWithChildren) {
5
+ return children;
6
+ }
7
+
8
+ export default App;