diff --git a/docs/API.png b/docs/API.png new file mode 100644 index 000000000..9528ae3aa Binary files /dev/null and b/docs/API.png differ diff --git a/docs/MiniMIS.png b/docs/MiniMIS.png new file mode 100644 index 000000000..3da87253d Binary files /dev/null and b/docs/MiniMIS.png differ diff --git a/docs/MiniProgram.png b/docs/MiniProgram.png new file mode 100644 index 000000000..b54052ab6 Binary files /dev/null and b/docs/MiniProgram.png differ diff --git a/docs/NocoBase.png b/docs/NocoBase.png new file mode 100644 index 000000000..ae481139d Binary files /dev/null and b/docs/NocoBase.png differ diff --git a/docs/SaaS1.png b/docs/SaaS1.png new file mode 100644 index 000000000..16b53a01d Binary files /dev/null and b/docs/SaaS1.png differ diff --git a/docs/SaaS2.png b/docs/SaaS2.png new file mode 100644 index 000000000..99c8f61a6 Binary files /dev/null and b/docs/SaaS2.png differ diff --git a/docs/guide/application.md b/docs/guide/application.md new file mode 100644 index 000000000..57d719bbf --- /dev/null +++ b/docs/guide/application.md @@ -0,0 +1,43 @@ +--- +title: 应用 - Application +nav: + title: 教程 +--- + +# 进阶教程 + +NocoBase 的 Application 的继承关系 + +```ts +class Application extends Koa { + +} + +class Koa extends EventEmitter { + +} +``` + +Koa 和 EventEmitter 详细 API 可以查阅相关文档,就不多说了。这里主要列一些重点: + +实例属性: + +- app.db:数据库实例,每个 app 都有自己的 db,完全隔离。 +- app.resources,数据资源实例,为数据提供 API 操作 +- app.cli,commander 实例,提供命令行操作 +- app.context,上下文 +- app.plugins 插件 + +实例方法: + +- app.constructor() +- app.collection() +- app.resource() +- app.command() +- app.plugin() +- app.load() +- app.start() +- app.use() 添加中间件的方法,由 Koa 提供 +- app.on() 添加事件监听,由 EventEmitter 提供 +- app.emit() 触发事件,由 EventEmitter 提供 +- app.emitSync() 触发异步事件 diff --git a/docs/guide/cli.md b/docs/guide/cli.md new file mode 100644 index 000000000..800970f5d --- /dev/null +++ b/docs/guide/cli.md @@ -0,0 +1,7 @@ +--- +title: 命令行 - CLI +nav: + title: 教程 +--- + +# 命令行 - CLI diff --git a/docs/guide/client.md b/docs/guide/client.md new file mode 100644 index 000000000..02ff4ece0 --- /dev/null +++ b/docs/guide/client.md @@ -0,0 +1,7 @@ +--- +title: 客户端 - Client +nav: + title: 教程 +--- + +# 客户端 - Client \ No newline at end of file diff --git a/docs/guide/collection.md b/docs/guide/collection.md new file mode 100644 index 000000000..cd6f997c1 --- /dev/null +++ b/docs/guide/collection.md @@ -0,0 +1,7 @@ +--- +title: 数据集 - Collection +nav: + title: 教程 +--- + +# 数据集 - Collection \ No newline at end of file diff --git a/docs/guide/event.md b/docs/guide/event.md new file mode 100644 index 000000000..c824cfd91 --- /dev/null +++ b/docs/guide/event.md @@ -0,0 +1,7 @@ +--- +title: 事件 - Event +nav: + title: 教程 +--- + +# 事件 - Event diff --git a/docs/guide/installation.md b/docs/guide/installation.md new file mode 100644 index 000000000..83159f49a --- /dev/null +++ b/docs/guide/installation.md @@ -0,0 +1,7 @@ +--- +title: 安装 - Installation +nav: + title: 教程 +--- + +# 安装 - Installation diff --git a/docs/guide/middleware.md b/docs/guide/middleware.md new file mode 100644 index 000000000..6a2576f7e --- /dev/null +++ b/docs/guide/middleware.md @@ -0,0 +1,8 @@ +--- +title: 中间件 - Middleware +nav: + title: 教程 +--- + +# 中间件 - Middleware + diff --git a/docs/guide/plugin.md b/docs/guide/plugin.md new file mode 100644 index 000000000..562064787 --- /dev/null +++ b/docs/guide/plugin.md @@ -0,0 +1,8 @@ +--- +title: 插件 - Plugin +nav: + title: 教程 +--- + +# 插件 - Plugin + diff --git a/docs/guide/resource.md b/docs/guide/resource.md new file mode 100644 index 000000000..aa095aca9 --- /dev/null +++ b/docs/guide/resource.md @@ -0,0 +1,7 @@ +--- +title: 资源 & 操作 - Resource & Action +nav: + title: 教程 +--- + +# 资源 & 操作 - Resource & Action \ No newline at end of file diff --git a/docs/guide/test.md b/docs/guide/test.md new file mode 100644 index 000000000..79116e0a3 --- /dev/null +++ b/docs/guide/test.md @@ -0,0 +1,7 @@ +--- +title: 测试 - Testing +nav: + title: 教程 +--- + +# 测试 - Testing \ No newline at end of file diff --git a/docs/index.md b/docs/index.md index 1e9834489..b6ae2e753 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,22 +1,24 @@ --- -title: 基础概念 +title: 介绍 toc: menu --- # NocoBase -考虑到大家是初次接触 NocoBase,开发文档的第一篇,从宏观的角度,带大家了解 NocoBase 的基础概念。NocoBase 采用微内核的架构,框架只保留核心概念,各类功能都以插件形式扩展。 +考虑到大家是初次接触 NocoBase,开发文档的第一篇,从宏观的角度,带大家了解 NocoBase 的基础概念。NocoBase 采用微内核架构,框架只保留核心,各类功能以插件形式扩展。 + + ## 微服务 - Microservices -先来个例子一睹为快,新建一个 server.js 文件,代码如下: +首先我们创建一个应用,新建一个 app.js 文件,代码如下: ```ts -const Server = require('@nocobase/server'); +const { Application } = require('@nocobase/server'); -const server = new Server(); +const app = new Application(); -server.collection({ +app.collection({ name: 'users', fields: [ { type: 'string', name: 'username' }, @@ -24,16 +26,16 @@ server.collection({ ], }); -server.start(process.argv); +app.start(process.argv); ``` 终端运行 ```bash # 根据配置生成数据库表结构 -node server.js db sync +node app.js db sync # 启动应用 -node server.js start --port=3000 +node app.js start --port=3000 ``` 相关 users 表的 REST API 就生成了 @@ -49,7 +51,7 @@ DELETE http://localhost:3000/api/users/1 除了内置的 REST API 以外,还可以自定义其他操作,如登录、注册、注销等。 ```ts -server.registerActions({ +app.registerActions({ async login(ctx, next) {}, async register(ctx, next) {}, async logout(ctx, next) {}, @@ -81,23 +83,23 @@ POST http://localhost:3000/api/users:destroy/1 ```ts const { ClientSDK } = require('@nocobase/client'); -const client = new ClientSDK(); +const api = new ClientSDK(); -await client.resource('users').list(); -await client.resource('users').create(); -await client.resource('users').get(); -await client.resource('users').update(); -await client.resource('users').destroy(); -await client.resource('users').login(); -await client.resource('users').register(); -await client.resource('users').logout(); +await api.resource('users').list(); +await api.resource('users').create(); +await api.resource('users').get(); +await api.resource('users').update(); +await api.resource('users').destroy(); +await api.resource('users').login(); +await api.resource('users').register(); +await api.resource('users').logout(); ``` ## 数据集 - Collection -上述例子,通过 `server.collection()` 方法定义数据的 Schema,Schema 的核心为字段配置,字段类型包括: +上述例子,通过 `app.collection()` 方法定义数据的 Schema,Schema 的核心为字段配置,字段类型包括: -Attribute 属性 +属性 Attribute - Boolean 布尔型 - String 字符串 @@ -115,7 +117,7 @@ Attribute 属性 - Password 密码 - Sort 排序 -Association/Realtion 关系 +关系 Association/Realtion - HasOne 一对一 - HasMany 一对多 @@ -127,7 +129,7 @@ Association/Realtion 关系 ```ts // 用户 -server.collection({ +app.collection({ name: 'users', fields: [ { type: 'string', name: 'username', unique: true }, @@ -137,7 +139,7 @@ server.collection({ }); // 文章 -server.collection({ +app.collection({ name: 'posts', fields: [ { type: 'string', name: 'title' }, @@ -149,7 +151,7 @@ server.collection({ }); // 标签 -server.collection({ +app.collection({ name: 'tags', fields: [ { type: 'string', name: 'name' }, @@ -158,7 +160,7 @@ server.collection({ }); // 评论 -server.collection({ +app.collection({ name: 'comments', fields: [ { type: 'text', name: 'content' }, @@ -169,16 +171,16 @@ server.collection({ 存在外键关联时,也无需顾虑建表和字段的顺序,`db sync` 时会自动处理。为了方便开发,提供了一些有用的属性或方法: -- `server.db` 数据库实例 -- `server.db.getModel()` 获取 Model -- `server.db.getTable()` 获取 Schema Table +- `app.db` 数据库实例 +- `app.db.getModel()` 获取 Model +- `app.db.getTable()` 获取 Schema Table ## 资源 & 操作 - Resource & Action -不同于常规的 MVC + Router,NocoBase 的路由(Resourcer)基于资源(Resource)和操作(Action)设计,将 REST 和 RPC 结合起来,提供更为灵活且统一的 Resource Action API,Action 不局限于增删改查。资源可以通过 `server.resource` 方法定义,如: +不同于常规的 MVC + Router,NocoBase 的路由(Resourcer)基于资源(Resource)和操作(Action)设计,将 REST 和 RPC 结合起来,提供更为灵活且统一的 Resource Action API,Action 不局限于增删改查。资源可以通过 `app.resource()` 方法定义,如: ```ts -server.resource({ +app.resource({ name: 'users', actions: { list: { @@ -210,53 +212,52 @@ server.resource({ }); ``` -`server.collection()` 和 `server.resource()` 的区别? +`app.collection()` 和 `app.resource()` 的区别? -- `server.collection()` 定义数据的 Schema(结构和关系) -- `server.resource()` 定义数据的 Action(操作方法) +- `app.collection()` 定义数据的 Schema(结构和关系) +- `app.resource()` 定义数据的 Action(操作方法) 一般情况无需显式声明 collection 的 resource,因为已定义的 collection 会自动同步给 resource。 ## 事件 - Event -在操作执行前、后都放置了相关的事件监听器,可以通过 `server.db.on` 和 `server.on` 添加。区别在于: +在操作执行前、后都放置了相关事件监听器,可以通过 `app.db.on()` 和 `app.on()` 添加。区别在于: -- `server.db.on` 添加数据库的监听器 -- `server.on` 添加服务器的监听器 +- `app.db.on()` 添加数据库层面的监听器 +- `app.on()` 添加服务器应用层面的监听器 -以 `users:login` 为例,在数据库里为「查询」操作,在服务器里为「登录」操作。如果需要记录登录操作日志,需要在 `server.on` 里处理。 +以 `users:login` 为例,在数据库里为「查询」操作,在应用里为「登录」操作。也就是说,如果需要记录登录操作日志,要在 `app.on()` 里处理。 ```ts // 创建数据时,执行 User.create() 时触发 -server.db.on('users:beforeCreate', async (model) => {}); +app.db.on('users:beforeCreate', async (model) => {}); // 客户端 `POST /api/users:login` 时触发 -server.on('users:beforeLogin', async (ctx, next) => {}); +app.on('users:beforeLogin', async (ctx, next) => {}); // 客户端 `POST /api/users` 时触发 -server.on('users:beforeCreate', async (ctx, next) => {}); +app.on('users:beforeCreate', async (ctx, next) => {}); ``` ## 中间件 - Middleware -Server 基于 Koa,所有 Koa 的插件(中间件)都可以直接使用,可以通过 server.use 添加。如: +Server Application 基于 Koa,所有 Koa 的插件(中间件)都可以直接使用,可以通过 `app.use()` 添加。如: ```ts -server.use(async (ctx, next) => { - const token = ctx.get('Authorization').replace(/^Bearer\s+/gi, ''); - if (token !== '123456') { - return ctx.throw(401, 'Unauthorized'); - } - return next(); +const responseTime = require('koa-response-time'); +app.use(responseTime()); + +app.use(async (ctx, next) => { + await next(); }); ``` -弥补 koa.use 的不足,提供了更完善的 middleware 适配器 +弥补 `app.use()` 不足,加了个 `middleware()` 适配器,可以用于限定 resource 和 action。除此之外,也可以控制中间件的插入位置。 ```ts import { middleware } from '@nocobase/server'; -server.use(middleware(async (ctx, next) => {}, { +app.use(middleware(async (ctx, next) => {}, { name: 'middlewareName1', resourceNames: [], actionNames: [], @@ -267,15 +268,16 @@ server.use(middleware(async (ctx, next) => {}, { ## 命令行 - CLI -除此之外,Server 还集成了 commander,可用于 cli 场景。目前内置的有: +Application 除了可以做 HTTP Server 以外,也可以是 CLI(内置了 Commander)。目前内置的命令有: - `db sync --force` 用于配置与数据库表结构同步 - `start --port` 启动应用 +- `plugin` 插件相关 自定义: ```ts -server.command('foo').action(async () => { +app.command('foo').action(async () => { console.log('foo...'); }); ``` @@ -285,46 +287,50 @@ server.command('foo').action(async () => { 上文,讲述了核心的扩展接口,包括但不局限于: - Database/Collection - - `server.db` database 实例 - - `server.collection()` 等同于 `server.db.table()` + - `app.db` database 实例 + - `app.collection()` 等同于 `app.db.table()` - Resource/Action - - `server.resource()` 等同于 `server.resourcer.define()` - - `server.registerActions()` 等同于 `server.resourcer.registerActions()` + - `app.resource()` 等同于 `app.resourcer.define()` + - `app.registerActions()` 等同于 `app.resourcer.registerActions()` - Hook/Event - - `server.on()` 添加服务器监听器 - - `server.db.on()` 添加数据库监听器 + - `app.on()` 添加服务器监听器 + - `app.db.on()` 添加数据库监听器 - Middleware - - `server.use()` 添加中间件 + - `app.use()` 添加中间件 - CLI - - `server.cli` commander 实例 - - `server.command()` 等同于 `server.cli.command()` + - `app.cli` commander 实例 + - `app.command()` 等同于 `app.cli.command()` - Plugin - - `server.pluginManager` 插件管理器 - - `server.plugin` 等同于 `server.pluginManager.add()` + - `app.pluginManager` 插件管理器 + - `app.plugin` 等同于 `app.pluginManager.add()` -基于以上扩展接口,提供模块化、可插拔的插件,可以通过 `server.plugin()` 添加。 +基于以上扩展接口,提供了模块化、可插拔的插件,可以通过 `app.plugin()` 添加。 + +完整的插件包括安装、升级、激活、载入、禁用、卸载流程,但是并不是所有插件都要这完整的流程。比如: **最简单的插件** ```ts -server.plugin(function pluginName1() { +app.plugin(function pluginName1() { }); ``` +这种方式添加的插件会直接载入。 + **JSON 风格** -包括安装、激活、载入、禁用、卸载流程的配置 - ```ts -server.plugin({ +const plugin = app.plugin({ async install() {}, + async upgrade() {}, async activate() {}, async bootstrap() {}, async deactivate() {}, async unstall() {}, }, { - name: 'pluginName1', + activate: false, // 默认为 true,不需要启用时可以禁用。 + name: 'plugin-name1', displayName: '插件名称', version: '1.2.3', dependencies: { @@ -332,21 +338,24 @@ server.plugin({ pluginName3: '1.x', }, }); +// 通过 api 激活插件 +plugin.activate(); ``` **OOP 风格** ```ts class MyPlugin extends Plugin { - async bootstrap() {} async install() {} - async unstall() {} + async upgrade() {} + async bootstrap() {} async activate() {} async deactivate() {} + async unstall() {} } -server.plugin(MyPlugin, { - name: 'pluginName1', +app.plugin(MyPlugin, { + name: 'plugin-name1', displayName: '插件名称', version: '1.2.3', dependencies: { @@ -359,7 +368,7 @@ server.plugin(MyPlugin, { **引用独立的 Package** ```ts -server.plugin('@nocobase/plugin-action-logs'); +app.plugin('@nocobase/plugin-action-logs'); ``` 插件信息也可以直接写在 `package.json` 里 @@ -376,12 +385,6 @@ server.plugin('@nocobase/plugin-action-logs'); } ``` -通过 `server.plugin()` 添加的插件需要激活才能使用 - -```ts -await server.pluginManager.activate(['pluginName1', 'pluginName2']); -``` - **插件 CLI** ```bash @@ -454,7 +457,7 @@ describe('mock server', () => { 插件配置 ```ts -server.plugin('@nocobase/plugin-client', { +app.plugin('@nocobase/plugin-client', { // 自定义 dist 路径 dist: path.resolve(__dirname, './node_modules/@nocobase/client/app'), }); @@ -566,30 +569,30 @@ nocobase-app 默认使用 umijs 作为项目构建工具,并集成了 Server ### 小型管理信息系统 -小型管理信息系统(包括后台数据管理系统),具备完整的前后端 +小型管理信息系统,具备完整的前后端。 - + ### API 服务 -无需客户端,提供纯后端接口,也同样适用于 Open API +无客户端,提供纯后端接口。 - + ### 小程序 + 后台管理 -只需要一套数据库,但有两套用户和权限,一套是用于后台用户,一套用于小程序用户 +只需要一套数据库,但有两套用户和权限,一套用于后台用户,一套用于小程序用户。 - + ### SaaS 服务(共享用户) -每个应用有自己配套的数据库,各应用数据完全隔离。应用不需要用户和权限模块,SaaS 主站全局共享了。目前的大多数协作平台、无代码平台是这样的结构。 +每个应用有自己配套的数据库,各应用数据完全隔离。应用不需要用户和权限模块,SaaS 主站全局共享了。 - + ### SaaS 服务(独立用户) -每个应用有自己的独立用户模块和权限,每个应用可以绑定自己的域名,更适用于搭建企业应用平台。 +每个应用有自己的独立用户模块和权限,应用可以绑定自己的域名。 - + diff --git a/docs/plugins/action-logs.md b/docs/plugins/action-logs.md new file mode 100644 index 000000000..eb37defdd --- /dev/null +++ b/docs/plugins/action-logs.md @@ -0,0 +1,7 @@ +--- +title: '@nocobase/plugin-action-logs' +nav: + title: 插件 +--- + +# @nocobase/plugin-action-logs diff --git a/docs/plugins/automations.md b/docs/plugins/automations.md new file mode 100644 index 000000000..8b18267f3 --- /dev/null +++ b/docs/plugins/automations.md @@ -0,0 +1,7 @@ +--- +title: '@nocobase/plugin-automations' +nav: + title: 插件 +--- + +# @nocobase/plugin-automations diff --git a/docs/plugins/client.md b/docs/plugins/client.md new file mode 100644 index 000000000..7030d6a0b --- /dev/null +++ b/docs/plugins/client.md @@ -0,0 +1,7 @@ +--- +title: '@nocobase/plugin-client' +nav: + title: 插件 +--- + +# @nocobase/plugin-client diff --git a/docs/plugins/collections.md b/docs/plugins/collections.md new file mode 100644 index 000000000..7dc05b50c --- /dev/null +++ b/docs/plugins/collections.md @@ -0,0 +1,7 @@ +--- +title: '@nocobase/plugin-collections' +nav: + title: 插件 +--- + +# @nocobase/plugin-collections diff --git a/docs/plugins/export.md b/docs/plugins/export.md new file mode 100644 index 000000000..314633699 --- /dev/null +++ b/docs/plugins/export.md @@ -0,0 +1,7 @@ +--- +title: '@nocobase/plugin-export' +nav: + title: 插件 +--- + +# @nocobase/plugin-export diff --git a/docs/plugins/file-manager.md b/docs/plugins/file-manager.md new file mode 100644 index 000000000..6fb5fff0d --- /dev/null +++ b/docs/plugins/file-manager.md @@ -0,0 +1,7 @@ +--- +title: '@nocobase/plugin-file-manager' +nav: + title: 插件 +--- + +# @nocobase/plugin-file-manager diff --git a/docs/plugins/permissions.md b/docs/plugins/permissions.md new file mode 100644 index 000000000..33822a660 --- /dev/null +++ b/docs/plugins/permissions.md @@ -0,0 +1,7 @@ +--- +title: '@nocobase/plugin-permissions' +nav: + title: 插件 +--- + +# @nocobase/plugin-permissions diff --git a/docs/plugins/saas.md b/docs/plugins/saas.md new file mode 100644 index 000000000..c9bf69345 --- /dev/null +++ b/docs/plugins/saas.md @@ -0,0 +1,7 @@ +--- +title: '@nocobase/plugin-saas' +nav: + title: 插件 +--- + +# @nocobase/plugin-saas diff --git a/docs/plugins/system-settings.md b/docs/plugins/system-settings.md new file mode 100644 index 000000000..9d3577286 --- /dev/null +++ b/docs/plugins/system-settings.md @@ -0,0 +1,7 @@ +--- +title: '@nocobase/plugin-system-settings' +nav: + title: 插件 +--- + +# @nocobase/plugin-system-settings diff --git a/docs/plugins/users.md b/docs/plugins/users.md new file mode 100644 index 000000000..f570794e9 --- /dev/null +++ b/docs/plugins/users.md @@ -0,0 +1,7 @@ +--- +title: '@nocobase/plugin-users' +nav: + title: 插件 +--- + +# @nocobase/plugin-users diff --git a/package.json b/package.json index 54d269d2c..0534ee81c 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "@types/file-saver": "^2.0.3", "@types/jest": "^24.0.18", "@types/koa": "^2.13.1", + "@types/koa-mount": "^4.0.1", "@types/lodash": "^4.14.169", "@types/mockjs": "^1.0.3", "@types/node": "^12.6.8", diff --git a/packages/actions/src/__tests__/create.test.ts b/packages/actions/src/__tests__/create.test.ts index 39b7e03e6..edf982eea 100644 --- a/packages/actions/src/__tests__/create.test.ts +++ b/packages/actions/src/__tests__/create.test.ts @@ -16,13 +16,13 @@ describe('create', () => { }); it('create', async () => { - api.database.table({ + api.db.table({ name: 'tests', fields: [ { type: 'string', name: 'name' }, ], }); - await api.database.sync(); + await api.db.sync(); const response = await api.resource('tests').create({ values: { name: 'n1' }, }); @@ -30,21 +30,21 @@ describe('create', () => { }); it('associations', async () => { - api.database.table({ + api.db.table({ name: 'comments', fields: [ { type: 'string', name: 'content' }, ], }); - api.database.table({ + api.db.table({ name: 'posts', fields: [ { type: 'string', name: 'title' }, { type: 'hasMany', name: 'comments' }, ], }); - await api.database.sync(); - const [Post, Comment] = api.database.getModels(['posts', 'comments']); + await api.db.sync(); + const [Post, Comment] = api.db.getModels(['posts', 'comments']); const response = await api.resource('posts').create({ values: { title: 't1', diff --git a/packages/actions/src/__tests__/destroy.test.ts b/packages/actions/src/__tests__/destroy.test.ts index 8d9f4d1aa..bad44aa08 100644 --- a/packages/actions/src/__tests__/destroy.test.ts +++ b/packages/actions/src/__tests__/destroy.test.ts @@ -9,18 +9,18 @@ describe('destroy', () => { dataWrapping: false, }); registerActions(api); - api.database.table({ + api.db.table({ name: 'posts', fields: [ { type: 'string', name: 'title' }, { type: 'hasMany', name: 'comments' }, ], }); - api.database.table({ + api.db.table({ name: 'comments', fields: [{ type: 'string', name: 'content' }], }); - await api.database.sync(); + await api.db.sync(); }); afterEach(async () => { @@ -28,7 +28,7 @@ describe('destroy', () => { }); it('destroy', async () => { - const Post = api.database.getModel('posts'); + const Post = api.db.getModel('posts'); const post = await Post.create(); expect( await Post.count({ @@ -46,7 +46,7 @@ describe('destroy', () => { }); it('destroy associations', async () => { - const [Post, Comment] = api.database.getModels(['posts', 'comments']); + const [Post, Comment] = api.db.getModels(['posts', 'comments']); const post = await Post.create(); const comment = await Comment.create(); await post.updateAssociations({ diff --git a/packages/actions/src/__tests__/get.test.ts b/packages/actions/src/__tests__/get.test.ts index bfda34225..e8efffe31 100644 --- a/packages/actions/src/__tests__/get.test.ts +++ b/packages/actions/src/__tests__/get.test.ts @@ -9,20 +9,20 @@ describe('get', () => { dataWrapping: false, }); registerActions(api); - api.database.table({ + api.db.table({ name: 'posts', fields: [ { type: 'string', name: 'title' }, { type: 'hasMany', name: 'comments' }, ], }); - api.database.table({ + api.db.table({ name: 'comments', fields: [ { type: 'string', name: 'content' }, ], }); - await api.database.sync(); + await api.db.sync(); }); afterEach(async () => { @@ -30,7 +30,7 @@ describe('get', () => { }); it('get', async () => { - const Post = api.database.getModel('posts'); + const Post = api.db.getModel('posts'); const post = await Post.create({ title: 't1' }); const response = await api.resource('posts').get({ resourceKey: post.id, @@ -40,7 +40,7 @@ describe('get', () => { }); it('get associations', async () => { - const [Post, Comment] = api.database.getModels(['posts', 'comments']); + const [Post, Comment] = api.db.getModels(['posts', 'comments']); const post = await Post.create(); const comment = await Comment.create({ content: 'c2' }); await post.updateAssociations({ diff --git a/packages/actions/src/__tests__/list.test.ts b/packages/actions/src/__tests__/list.test.ts index 3a96c5964..85b2aee00 100644 --- a/packages/actions/src/__tests__/list.test.ts +++ b/packages/actions/src/__tests__/list.test.ts @@ -9,14 +9,14 @@ describe('list', () => { dataWrapping: false, }); registerActions(api); - api.database.table({ + api.db.table({ name: 'comments', fields: [ { type: 'string', name: 'content' }, { type: 'string', name: 'status', defaultValue: 'draft' }, ], }); - api.database.table({ + api.db.table({ name: 'posts', fields: [ { type: 'string', name: 'title' }, @@ -24,8 +24,8 @@ describe('list', () => { { type: 'string', name: 'status', defaultValue: 'draft' }, ], }); - await api.database.sync(); - const [Post, Comment] = api.database.getModels(['posts', 'comments']); + await api.db.sync(); + const [Post, Comment] = api.db.getModels(['posts', 'comments']); for (let index = 1; index < 4; index++) { const post = await Post.create({ title: `t${index}` }); await post.updateAssociations({ diff --git a/packages/actions/src/__tests__/sort.test.ts b/packages/actions/src/__tests__/sort.test.ts index b5307bd44..0180e77be 100644 --- a/packages/actions/src/__tests__/sort.test.ts +++ b/packages/actions/src/__tests__/sort.test.ts @@ -11,7 +11,7 @@ describe('sort', () => { dataWrapping: false, }); registerActions(api); - api.database.table({ + api.db.table({ name: 'tests', fields: [ { type: 'string', name: 'title' }, @@ -19,8 +19,8 @@ describe('sort', () => { { type: 'sort', name: 'sort2' }, ], }); - await api.database.sync(); - const Test = api.database.getModel('tests'); + await api.db.sync(); + const Test = api.db.getModel('tests'); for (let index = 1; index < 5; index++) { await Test.create({ title: `t${index}` }); } @@ -160,7 +160,7 @@ describe('sort', () => { dataWrapping: false, }); registerActions(api); - api.database.table({ + api.db.table({ name: 'tests', fields: [ { type: 'string', name: 'title' }, @@ -168,8 +168,8 @@ describe('sort', () => { { type: 'sort', name: 'sort', scope: ['state'] }, ], }); - await api.database.sync(); - const Test = api.database.getModel('tests'); + await api.db.sync(); + const Test = api.db.getModel('tests'); for (let index = 1; index < 5; index++) { await Test.create({ title: `t1${index}`, state: 1 }); } diff --git a/packages/actions/src/__tests__/update.test.ts b/packages/actions/src/__tests__/update.test.ts index 92242b499..e1723915c 100644 --- a/packages/actions/src/__tests__/update.test.ts +++ b/packages/actions/src/__tests__/update.test.ts @@ -9,20 +9,20 @@ describe('update', () => { dataWrapping: false, }); registerActions(api); - api.database.table({ + api.db.table({ name: 'posts', fields: [ { type: 'string', name: 'title' }, { type: 'hasMany', name: 'comments' }, ], }); - api.database.table({ + api.db.table({ name: 'comments', fields: [ { type: 'string', name: 'content' }, ], }); - await api.database.sync(); + await api.db.sync(); }); afterEach(async () => { @@ -30,7 +30,7 @@ describe('update', () => { }); it('update', async () => { - const Post = api.database.getModel('posts'); + const Post = api.db.getModel('posts'); const post = await Post.create(); await api.resource('posts').update({ resourceKey: post.id, @@ -45,7 +45,7 @@ describe('update', () => { }); it('update associations', async () => { - const [Post, Comment] = api.database.getModels(['posts', 'comments']); + const [Post, Comment] = api.db.getModels(['posts', 'comments']); const post = await Post.create(); const comment = await Comment.create(); await post.updateAssociations({ diff --git a/packages/api/src/index.ts b/packages/api/src/index.ts index e4fb81c4b..5702f7d9b 100644 --- a/packages/api/src/index.ts +++ b/packages/api/src/index.ts @@ -24,7 +24,7 @@ import { middlewares } from '@nocobase/server'; } await api.start(process.argv); - console.log(api.database.getTables().map(t => t.getName())); + console.log(api.db.getTables().map(t => t.getName())); console.log(`Start-up time: ${(Date.now() - start) / 1000}s`); console.log(`http://localhost:${process.env.API_PORT}/`); })(); diff --git a/packages/api/src/migrations/init.ts b/packages/api/src/migrations/init.ts index 117ed52ec..06a18aa54 100644 --- a/packages/api/src/migrations/init.ts +++ b/packages/api/src/migrations/init.ts @@ -12,7 +12,7 @@ import * as uiSchema from './ui-schema'; (async () => { await api.loadPlugins(); - const database: Database = api.database; + const database: Database = api.db; await database.sync({ // tables: ['collections', 'fields', 'actions', 'views', 'tabs'], }); diff --git a/packages/api/src/migrations/sync.ts b/packages/api/src/migrations/sync.ts index 2a469dfb7..55ac5260c 100644 --- a/packages/api/src/migrations/sync.ts +++ b/packages/api/src/migrations/sync.ts @@ -3,7 +3,7 @@ import api from '../app'; (async () => { await api.loadPlugins(); - const database: Database = api.database; + const database: Database = api.db; await database.sync({ // tables: ['collections', 'fields', 'actions', 'views', 'tabs'], }); diff --git a/packages/app/package.json b/packages/app/package.json index c36b4f2b8..677ff090a 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -29,6 +29,8 @@ "@nocobase/client": "^0.4.0-alpha.7", "@nocobase/preset-nocobase": "^0.4.0-alpha.7", "@umijs/preset-react": "1.x", + "koa-mount": "^4.0.0", + "ts-node-dev": "^1.1.8", "umi": "^3.0.0" }, "devDependencies": { diff --git a/packages/app/src/api/index.ts b/packages/app/src/api/index.ts index b3298d5be..8b102e621 100644 --- a/packages/app/src/api/index.ts +++ b/packages/app/src/api/index.ts @@ -2,6 +2,7 @@ import Server from '@nocobase/server'; import { registerActions } from '@nocobase/actions'; import dotenv from 'dotenv'; import path from 'path'; +import mount from 'koa-mount'; const start = Date.now(); console.log('starting... ', new Date().toUTCString()); @@ -56,6 +57,8 @@ if (process.argv.length < 3) { process.argv.push('start', '--port', '2000'); } +console.log(process.argv); + api.start(process.argv).then(() => { console.log(`Start-up time: ${(Date.now() - start) / 1000}s`); }); diff --git a/packages/client/src/schemas/action/index.md b/packages/client/src/schemas/action/index.md index 33e1c41b0..7158456b6 100644 --- a/packages/client/src/schemas/action/index.md +++ b/packages/client/src/schemas/action/index.md @@ -2,11 +2,10 @@ title: Action - 操作 nav: title: 组件 - path: /client + path: /schemas group: - order: 1 - title: Schemas - path: /client/schemas + title: 组件 + path: /schemas --- # Action - 操作 diff --git a/packages/database/src/__tests__/index.ts b/packages/database/src/__tests__/index.ts index 88449a989..b345032de 100644 --- a/packages/database/src/__tests__/index.ts +++ b/packages/database/src/__tests__/index.ts @@ -29,6 +29,8 @@ const config = { logging: process.env.DB_LOG_SQL === 'on' ? console.log : false }; +console.log(config); + export function getDatabase() { return new Database({ ...config, diff --git a/packages/plugin-action-logs/src/__tests__/hook.test.ts b/packages/plugin-action-logs/src/__tests__/hook.test.ts index 337ee74c1..e77d3063c 100644 --- a/packages/plugin-action-logs/src/__tests__/hook.test.ts +++ b/packages/plugin-action-logs/src/__tests__/hook.test.ts @@ -16,7 +16,7 @@ describe('hook', () => { }); registerActions(api); await api.loadPlugins(); - db = api.database; + db = api.db; db.table({ name: 'posts', logging: true, diff --git a/packages/plugin-automations/src/__tests__/automations.test.ts b/packages/plugin-automations/src/__tests__/automations.test.ts index 26ed45566..75569c62e 100644 --- a/packages/plugin-automations/src/__tests__/automations.test.ts +++ b/packages/plugin-automations/src/__tests__/automations.test.ts @@ -14,7 +14,7 @@ describe('automations', () => { beforeEach(async () => { app = await getApp(); - db = app.database; + db = app.db; Automation = db.getModel('automations') as any; Test = db.getModel('tests'); Target = db.getModel('targets'); diff --git a/packages/plugin-automations/src/__tests__/index.ts b/packages/plugin-automations/src/__tests__/index.ts index 9ec131ab0..ff38b9cb1 100644 --- a/packages/plugin-automations/src/__tests__/index.ts +++ b/packages/plugin-automations/src/__tests__/index.ts @@ -59,11 +59,11 @@ export async function getApp() { automations: plugin }); await app.loadPlugins(); - const testTables = app.database.import({ + const testTables = app.db.import({ directory: path.resolve(__dirname, './collections') }); try { - await app.database.sync(); + await app.db.sync(); } catch (err) { console.error(err); } @@ -71,15 +71,15 @@ export async function getApp() { for (const table of testTables.values()) { // TODO(bug): 由于每个用例结束后不会清理用于测试的数据表,导致再次创建和更新 // 创建和更新里面仍会再次创建 fields,导致创建相关的数据重复,数据库报错。 - await app.database.getModel('collections').import(table.getOptions(), { update: true, migrate: false }); + await app.db.getModel('collections').import(table.getOptions(), { update: true, migrate: false }); } - app.context.db = app.database; + app.context.db = app.db; app.use(bodyParser()); app.use(middleware({ prefix: '/api', resourcer: app.resourcer, - database: app.database, + database: app.db, })); return app; } diff --git a/packages/plugin-automations/src/__tests__/jobs.test.ts b/packages/plugin-automations/src/__tests__/jobs.test.ts index 64233e939..44266cfd5 100644 --- a/packages/plugin-automations/src/__tests__/jobs.test.ts +++ b/packages/plugin-automations/src/__tests__/jobs.test.ts @@ -19,7 +19,7 @@ describe('automations.jobs', () => { beforeEach(async () => { app = await getApp(); - db = app.database; + db = app.db; Job = db.getModel('automations_jobs') as any; Test = db.getModel('tests'); }); diff --git a/packages/plugin-china-region/src/server.ts b/packages/plugin-china-region/src/server.ts index 48006e59d..cfe363686 100644 --- a/packages/plugin-china-region/src/server.ts +++ b/packages/plugin-china-region/src/server.ts @@ -6,14 +6,14 @@ import Application from '@nocobase/server'; registerModels({ ChinaRegion }); export default async function (this: Application, options = {}) { - const { database } = this; + const { db } = this; - database.import({ + db.import({ directory: path.resolve(__dirname, 'collections'), }); - this.on('china-region.init', async () => { - const M = database.getModel('china_regions'); + this.on('db.init', async () => { + const M = db.getModel('china_regions'); await M.importData(); }); } diff --git a/packages/plugin-collections/src/__tests__/collections.test.ts b/packages/plugin-collections/src/__tests__/collections.test.ts index 4cdc87f57..8949dbd1c 100644 --- a/packages/plugin-collections/src/__tests__/collections.test.ts +++ b/packages/plugin-collections/src/__tests__/collections.test.ts @@ -9,11 +9,11 @@ describe('collection hooks', () => { beforeEach(async () => { app = await getApp(); - db = app.database; + db = app.db; agent = getAgent(app); }); - afterEach(() => app.database.close()); + afterEach(() => app.db.close()); it('create table', async () => { const Collection = db.getModel('collections'); diff --git a/packages/plugin-collections/src/__tests__/index.ts b/packages/plugin-collections/src/__tests__/index.ts index e0574424b..d4439acdc 100644 --- a/packages/plugin-collections/src/__tests__/index.ts +++ b/packages/plugin-collections/src/__tests__/index.ts @@ -59,22 +59,22 @@ export async function getApp() { app.resourcer.registerActionHandlers({ ...actions.associate, ...actions.common }); app.registerPlugin('collections', [plugin]); await app.loadPlugins(); - await app.database.sync(); + await app.db.sync(); // 表配置信息存到数据库里 - // const tables = app.database.getTables([]); + // const tables = app.db.getTables([]); // for (const table of tables) { - // const Collection = app.database.getModel('collections'); + // const Collection = app.db.getModel('collections'); // await Collection.import(table.getOptions(), { hooks: false }); // } app.use(async (ctx, next) => { - ctx.db = app.database; + ctx.db = app.db; await next(); }); app.use(bodyParser()); app.use(middleware({ prefix: '/api', resourcer: app.resourcer, - database: app.database, + database: app.db, })); return app; } diff --git a/packages/plugin-collections/src/server.ts b/packages/plugin-collections/src/server.ts index 532bac938..44c01a91c 100644 --- a/packages/plugin-collections/src/server.ts +++ b/packages/plugin-collections/src/server.ts @@ -6,7 +6,7 @@ import { createOrUpdate, findAll } from './actions'; import { create } from './actions/fields'; export default async function (this: Application, options = {}) { - const database = this.database; + const database = this.db; registerModels(models); @@ -14,12 +14,11 @@ export default async function (this: Application, options = {}) { directory: path.resolve(__dirname, 'collections'), }); - this.on('pluginsLoaded', async () => { - console.log('pluginsLoaded'); + this.on('afterLoadPlugins', async () => { await database.getModel('collections').load(); }); - this.on('collections.init', async () => { + this.on('db.init', async () => { const userTable = database.getTable('users'); const config = userTable.getOptions(); const Collection = database.getModel('collections'); diff --git a/packages/plugin-file-manager/src/__tests__/action.test.ts b/packages/plugin-file-manager/src/__tests__/action.test.ts index 859382483..ec183e317 100644 --- a/packages/plugin-file-manager/src/__tests__/action.test.ts +++ b/packages/plugin-file-manager/src/__tests__/action.test.ts @@ -16,9 +16,9 @@ describe('action', () => { app = await getApp(); agent = getAgent(app); api = getAPI(app); - db = app.database; + db = app.db; - const Storage = app.database.getModel('storages'); + const Storage = app.db.getModel('storages'); await Storage.create({ name: `local_${Date.now().toString(36)}`, type: STORAGE_TYPE_LOCAL, diff --git a/packages/plugin-file-manager/src/__tests__/index.ts b/packages/plugin-file-manager/src/__tests__/index.ts index 51933fb1f..b446f4f93 100644 --- a/packages/plugin-file-manager/src/__tests__/index.ts +++ b/packages/plugin-file-manager/src/__tests__/index.ts @@ -67,23 +67,23 @@ export async function getApp() { 'file-manager': [plugin] }); await app.loadPlugins(); - app.database.import({ + app.db.import({ directory: path.resolve(__dirname, './tables') }); try { - await app.database.sync(); + await app.db.sync(); } catch (error) { console.error(error); } app.use(async (ctx, next) => { - ctx.db = app.database; + ctx.db = app.db; await next(); }); app.use(bodyParser()); app.use(middleware({ prefix: '/api', resourcer: app.resourcer, - database: app.database, + database: app.db, })); return app; } diff --git a/packages/plugin-file-manager/src/server.ts b/packages/plugin-file-manager/src/server.ts index a138becf7..16748aee3 100644 --- a/packages/plugin-file-manager/src/server.ts +++ b/packages/plugin-file-manager/src/server.ts @@ -23,8 +23,9 @@ export default async function () { resourcer.registerActionHandler('upload', uploadAction); localMiddleware(this); - this.on('file-manager.init', async () => { - const Storage = database.getModel('storages'); + const Storage = database.getModel('storages'); + + this.on('db.init', async () => { await Storage.create({ title: '本地存储', name: `local`, diff --git a/packages/plugin-file-manager/src/storages/local.ts b/packages/plugin-file-manager/src/storages/local.ts index ada0b85de..da205de73 100644 --- a/packages/plugin-file-manager/src/storages/local.ts +++ b/packages/plugin-file-manager/src/storages/local.ts @@ -18,7 +18,7 @@ export function getDocumentRoot(storage): string { // TODO(optimize): 初始化的时机不应该放在中间件里 export function middleware(app) { const storages = new Map(); - const StorageModel = app.database.getModel('storages'); + const StorageModel = app.db.getModel('storages'); return app.use(async function (ctx, next) { const items = await StorageModel.findAll({ diff --git a/packages/plugin-saas/.gitignore b/packages/plugin-saas/.gitignore new file mode 100644 index 000000000..1be219988 --- /dev/null +++ b/packages/plugin-saas/.gitignore @@ -0,0 +1,4 @@ +node_modules +yarn-error.log +.env +src2 \ No newline at end of file diff --git a/packages/plugin-saas/.npmignore b/packages/plugin-saas/.npmignore new file mode 100644 index 000000000..461574b2f --- /dev/null +++ b/packages/plugin-saas/.npmignore @@ -0,0 +1,7 @@ +node_modules +*.log +docs +__tests__ +tsconfig.json +src +.fatherrc.ts \ No newline at end of file diff --git a/packages/plugin-system-settings/src/server.ts b/packages/plugin-system-settings/src/server.ts index 87f3b6ba3..4858d674a 100644 --- a/packages/plugin-system-settings/src/server.ts +++ b/packages/plugin-system-settings/src/server.ts @@ -25,7 +25,7 @@ export default async function (this: Application, options = {}) { await next(); }); - this.on('system-settings.init', async () => { + this.on('db.init', async () => { const setting = await SystemSetting.create({ title: 'NocoBase', }); diff --git a/packages/plugin-ui-router/src/__tests__/index.ts b/packages/plugin-ui-router/src/__tests__/index.ts index e0574424b..d4439acdc 100644 --- a/packages/plugin-ui-router/src/__tests__/index.ts +++ b/packages/plugin-ui-router/src/__tests__/index.ts @@ -59,22 +59,22 @@ export async function getApp() { app.resourcer.registerActionHandlers({ ...actions.associate, ...actions.common }); app.registerPlugin('collections', [plugin]); await app.loadPlugins(); - await app.database.sync(); + await app.db.sync(); // 表配置信息存到数据库里 - // const tables = app.database.getTables([]); + // const tables = app.db.getTables([]); // for (const table of tables) { - // const Collection = app.database.getModel('collections'); + // const Collection = app.db.getModel('collections'); // await Collection.import(table.getOptions(), { hooks: false }); // } app.use(async (ctx, next) => { - ctx.db = app.database; + ctx.db = app.db; await next(); }); app.use(bodyParser()); app.use(middleware({ prefix: '/api', resourcer: app.resourcer, - database: app.database, + database: app.db, })); return app; } diff --git a/packages/plugin-ui-router/src/__tests__/routes.test.ts b/packages/plugin-ui-router/src/__tests__/routes.test.ts index 789f02664..028032606 100644 --- a/packages/plugin-ui-router/src/__tests__/routes.test.ts +++ b/packages/plugin-ui-router/src/__tests__/routes.test.ts @@ -9,11 +9,11 @@ describe('routes', () => { beforeEach(async () => { app = await getApp(); - db = app.database; + db = app.db; agent = getAgent(app); }); - afterEach(() => app.database.close()); + afterEach(() => app.db.close()); // it.only('create route', async () => { // const Route = db.getModel('routes'); diff --git a/packages/plugin-ui-router/src/server.ts b/packages/plugin-ui-router/src/server.ts index 089a1e7e3..e5d147537 100644 --- a/packages/plugin-ui-router/src/server.ts +++ b/packages/plugin-ui-router/src/server.ts @@ -17,7 +17,7 @@ export default async function (this: Application, options = {}) { const Route = database.getModel('routes'); - this.on('ui-router.init', async () => { + this.on('db.init', async () => { const data = [ { type: 'redirect', diff --git a/packages/plugin-users/src/__tests__/fields.test.ts b/packages/plugin-users/src/__tests__/fields.test.ts index af6bcc744..330784c21 100644 --- a/packages/plugin-users/src/__tests__/fields.test.ts +++ b/packages/plugin-users/src/__tests__/fields.test.ts @@ -10,7 +10,7 @@ describe('user fields', () => { api = mockServer(); api.registerPlugin('users', require('../server').default); await api.loadPlugins(); - db = api.database; + db = api.db; }); afterEach(async () => { diff --git a/packages/plugin-users/src/server.ts b/packages/plugin-users/src/server.ts index 21fb2d525..cf40d8583 100644 --- a/packages/plugin-users/src/server.ts +++ b/packages/plugin-users/src/server.ts @@ -12,7 +12,7 @@ export default async function (this: Application, options = {}) { registerFields(fields); - this.on('users.init', async () => { + this.on('db.init', async () => { const User = database.getModel('users'); await User.create({ nickname: '超级管理员', diff --git a/packages/server/src/__tests__/application.test.ts b/packages/server/src/__tests__/application.test.ts index 79734facc..b12f61786 100644 --- a/packages/server/src/__tests__/application.test.ts +++ b/packages/server/src/__tests__/application.test.ts @@ -1,5 +1,9 @@ import supertest from 'supertest'; import { Application } from '../application'; +import { Plugin } from '../plugin'; + +class MyPlugin extends Plugin { +} describe('application', () => { let app: Application; @@ -42,7 +46,7 @@ describe('application', () => { }); afterEach(async () => { - return app.database.close(); + return app.db.close(); }); it('resourcer.define', async () => { @@ -63,7 +67,7 @@ describe('application', () => { }); it('db.table', async () => { - app.database.table({ + app.db.table({ name: 'tests', }); const response = await agent.get('/api/tests'); @@ -71,10 +75,10 @@ describe('application', () => { }); it('db.association', async () => { - app.database.table({ + app.db.table({ name: 'bars', }); - app.database.table({ + app.db.table({ name: 'foos', fields: [ { @@ -90,7 +94,7 @@ describe('application', () => { it('db.middleware', async () => { const index = app.middleware.findIndex(m => m.name === 'table2resource'); app.middleware.splice(index, 0, async (ctx, next) => { - app.database.table({ + app.db.table({ name: 'tests', }); await next(); @@ -102,10 +106,10 @@ describe('application', () => { it('db.middleware', async () => { const index = app.middleware.findIndex(m => m.name === 'table2resource'); app.middleware.splice(index, 0, async (ctx, next) => { - app.database.table({ + app.db.table({ name: 'bars', }); - app.database.table({ + app.db.table({ name: 'foos', fields: [ { diff --git a/packages/server/src/__tests__/dataWrapping.test.ts b/packages/server/src/__tests__/dataWrapping.test.ts index 82ab9b0be..30457bad0 100644 --- a/packages/server/src/__tests__/dataWrapping.test.ts +++ b/packages/server/src/__tests__/dataWrapping.test.ts @@ -42,7 +42,7 @@ describe('application', () => { }); afterEach(async () => { - return app.database.close(); + return app.db.close(); }); it('resourcer.define', async () => { diff --git a/packages/server/src/__tests__/plugin.test.ts b/packages/server/src/__tests__/plugin.test.ts new file mode 100644 index 000000000..26f358a86 --- /dev/null +++ b/packages/server/src/__tests__/plugin.test.ts @@ -0,0 +1,147 @@ +import supertest from 'supertest'; +import { Application } from '../application'; +import { Plugin } from '../plugin'; +import path from 'path'; +import Plugin3 from './plugins/plugin3'; + +describe('plugin', () => { + let app: Application; + + beforeEach(() => { + app = new Application({ + database: { + username: process.env.DB_USER, + password: process.env.DB_PASSWORD, + database: process.env.DB_DATABASE, + host: process.env.DB_HOST, + port: process.env.DB_PORT as any, + dialect: process.env.DB_DIALECT as any, + dialectOptions: { + charset: 'utf8mb4', + collate: 'utf8mb4_unicode_ci', + }, + }, + resourcer: { + prefix: '/api', + }, + dataWrapping: false, + }); + }); + + afterEach(async () => { + return app.db.close(); + }); + + describe('define', () => { + it('plugin name', async () => { + const plugin = app.plugin(function abc() {}); + expect(plugin).toBeInstanceOf(Plugin); + expect(plugin.getName()).toBe('abc'); + }); + it('plugin name', async () => { + const plugin = app.plugin(function abc() {}, { + name: 'plugin-name2' + }); + expect(plugin).toBeInstanceOf(Plugin); + expect(plugin.getName()).toBe('plugin-name2'); + }); + it('plugin name', async () => { + const plugin = app.plugin(function () {}, { + name: 'plugin-name3' + }); + expect(plugin).toBeInstanceOf(Plugin); + expect(plugin.getName()).toBe('plugin-name3'); + }); + it('plugin name', async () => { + class MyPlugin extends Plugin {} + const plugin = app.plugin(MyPlugin); + expect(plugin).toBeInstanceOf(MyPlugin); + expect(plugin.getName()).toBe('MyPlugin'); + }); + + it('plugin name', async () => { + const plugin = app.plugin(path.resolve(__dirname, './plugins/plugin1')); + expect(plugin).toBeInstanceOf(Plugin); + expect(plugin.getName()).toBe('abc'); + }); + + it('plugin name', async () => { + const plugin = app.plugin(path.resolve(__dirname, './plugins/plugin3')); + expect(plugin).toBeInstanceOf(Plugin3); + expect(plugin.getName()).toBe('Plugin3'); + }); + }); + + describe('load', () => { + it('plugin load', async () => { + app.plugin(function abc(this: Plugin) { + this.app.collection({ + name: 'tests', + }); + }); + await app.load(); + const Test = app.db.getModel('tests'); + expect(Test).toBeDefined(); + }); + + it('plugin load', async () => { + app.plugin({ + load() { + app.collection({ + name: 'tests', + }); + } + }); + await app.load(); + const Test = app.db.getModel('tests'); + expect(Test).toBeDefined(); + }); + + it('plugin load', async () => { + app.plugin({ + load: () => { + app.collection({ + name: 'tests', + }); + } + }); + await app.load(); + const Test = app.db.getModel('tests'); + expect(Test).toBeDefined(); + }); + + it('plugin load', async () => { + app.plugin(class MyPlugin extends Plugin { + async load() { + this.app.collection({ + name: 'tests', + }); + } + }); + await app.load(); + const Test = app.db.getModel('tests'); + expect(Test).toBeDefined(); + }); + + it('plugin load', async () => { + app.plugin(path.resolve(__dirname, './plugins/plugin1')); + await app.load(); + const Test = app.db.getModel('tests'); + expect(Test).toBeDefined(); + }); + + it('plugin load', async () => { + app.plugin(path.resolve(__dirname, './plugins/plugin2')); + await app.load(); + const Test = app.db.getModel('tests'); + expect(Test).toBeDefined(); + }); + + it('plugin load', async () => { + app.plugin(path.resolve(__dirname, './plugins/plugin3')); + await app.load(); + const Test = app.db.getModel('tests'); + expect(Test).toBeDefined(); + }); + }); +}); diff --git a/packages/server/src/__tests__/plugins/plugin1.ts b/packages/server/src/__tests__/plugins/plugin1.ts new file mode 100644 index 000000000..cea3e2030 --- /dev/null +++ b/packages/server/src/__tests__/plugins/plugin1.ts @@ -0,0 +1,7 @@ +import { Plugin } from '../../plugin'; + +export default function abc(this: Plugin) { + this.app.db.table({ + name: 'tests', + }); +} diff --git a/packages/server/src/__tests__/plugins/plugin2.ts b/packages/server/src/__tests__/plugins/plugin2.ts new file mode 100644 index 000000000..07b9d2bcd --- /dev/null +++ b/packages/server/src/__tests__/plugins/plugin2.ts @@ -0,0 +1,9 @@ +import { IPlugin } from '../../plugin'; + +export default { + load() { + this.app.db.table({ + name: 'tests', + }); + } +} as IPlugin; diff --git a/packages/server/src/__tests__/plugins/plugin3.ts b/packages/server/src/__tests__/plugins/plugin3.ts new file mode 100644 index 000000000..b11288b0c --- /dev/null +++ b/packages/server/src/__tests__/plugins/plugin3.ts @@ -0,0 +1,9 @@ +import { Plugin } from '../../plugin'; + +export default class Plugin3 extends Plugin { + async load() { + this.app.db.table({ + name: 'tests', + }); + } +} diff --git a/packages/server/src/application.ts b/packages/server/src/application.ts index adb83dc55..81dd9ab4b 100644 --- a/packages/server/src/application.ts +++ b/packages/server/src/application.ts @@ -1,10 +1,11 @@ import Koa from 'koa'; import cors from '@koa/cors'; import bodyParser from 'koa-bodyparser'; -import { Command } from 'commander'; -import Database, { DatabaseOptions } from '@nocobase/database'; -import Resourcer from '@nocobase/resourcer'; +import { Command, CommandOptions } from 'commander'; +import Database, { DatabaseOptions, TableOptions } from '@nocobase/database'; +import Resourcer, { ResourceOptions } from '@nocobase/resourcer'; import { dataWrapping, table2resource } from './middlewares'; +import { PluginType, Plugin, PluginOptions } from './plugin'; export interface ResourcerOptions { prefix?: string; @@ -19,18 +20,18 @@ export interface ApplicationOptions { } export class Application extends Koa { - public readonly database: Database; + public readonly db: Database; public readonly resourcer: Resourcer; public readonly cli: Command; - protected plugins = new Map(); + protected plugins = new Map(); constructor(options: ApplicationOptions) { super(); - this.database = new Database(options.database); + this.db = new Database(options.database); this.resourcer = new Resourcer({ ...options.resourcer }); this.cli = new Command(); @@ -48,8 +49,7 @@ export class Application extends Koa { ); this.use(async (ctx, next) => { - ctx.db = this.database; - ctx.database = this.database; + ctx.db = this.db; ctx.resourcer = this.resourcer; await next(); }); @@ -65,9 +65,21 @@ export class Application extends Koa { .command('db sync') .option('-f, --force') .action(async (...args) => { + console.log('db sync'); const cli = args.pop(); - await this.database.sync(); - await this.database.close(); + const force = cli.opts()?.force; + await this.load(); + await this.db.sync( + force + ? { + force: true, + alter: { + drop: true, + }, + } + : {}, + ); + await this.destroy(); }); this.cli @@ -75,6 +87,13 @@ export class Application extends Koa { // .option('-f, --force') .action(async (...args) => { const cli = args.pop(); + await this.load(); + await this.db.sync({ + force: true, + alter: { + drop: true, + }, + }); await this.emitAsync('db.init'); await this.destroy(); }); @@ -86,13 +105,66 @@ export class Application extends Koa { const cli = args.pop(); console.log(args); const opts = cli.opts(); - await this.loadPlugins(); + await this.load(); await this.emitAsync('server.beforeStart'); this.listen(opts.port || 3000); console.log(`http://localhost:${opts.port || 3000}/`); }); } + collection(options: TableOptions) { + return this.db.table(options); + } + + resource(options: ResourceOptions) { + return this.resourcer.define(options); + } + + command(nameAndArgs: string, opts?: CommandOptions) { + return this.cli.command(nameAndArgs, opts); + } + + plugin(plugin: PluginType, options?: PluginOptions): Plugin { + if (typeof plugin === 'string') { + plugin = require(plugin).default; + } + let instance: Plugin; + try { + // @ts-ignore + const p = new plugin(); + if (p instanceof Plugin) { + // @ts-ignore + instance = new plugin({}, { + ...options, + app: this, + }); + } else { + throw new Error('plugin must be instanceof Plugin'); + } + } catch (err) { + instance = new Plugin(plugin, { + ...options, + app: this, + }); + } + const name = instance.getName(); + if (this.plugins.has(name)) { + throw new Error(`plugin name [${name}] is repeated`); + } + this.plugins.set(name, instance); + return instance; + } + + async load() { + await this.emitAsync('plugins.beforeLoad'); + for (const [name, plugin] of this.plugins) { + await this.emitAsync(`plugins.${name}.beforeLoad`); + await plugin.load(); + await this.emitAsync(`plugins.${name}.afterLoad`); + } + await this.emitAsync('plugins.afterLoad'); + } + async emitAsync(event: string | symbol, ...args: any[]): Promise { // @ts-ignore const events = this._events; @@ -144,57 +216,58 @@ export class Application extends Koa { return true; } - registerPlugin(key: string | object, plugin?: any) { - if (typeof key === 'object') { - Object.keys(key).forEach((k) => { - this.registerPlugin(k, key[k]); - }); - } else { - const config = {}; - if (Array.isArray(plugin)) { - const [entry, options = {}] = plugin; - Object.assign(config, { entry, options }); - } else { - Object.assign(config, { entry: plugin, options: {} }); - } - this.plugins.set(key, config); - } - } + // registerPlugin(key: string | object, plugin?: any) { + // if (typeof key === 'object') { + // Object.keys(key).forEach((k) => { + // this.registerPlugin(k, key[k]); + // }); + // } else { + // const config = {}; + // if (Array.isArray(plugin)) { + // const [entry, options = {}] = plugin; + // Object.assign(config, { entry, options }); + // } else { + // Object.assign(config, { entry: plugin, options: {} }); + // } + // this.plugins.set(key, config); + // } + // } - async loadPlugins() { - await this.emitAsync('plugins.beforeLoad'); - const allPlugins = this.plugins.values(); - for (const plugin of allPlugins) { - plugin.instance = await this.loadPlugin(plugin); - } - await this.emitAsync('plugins.afterLoad'); - } + // async loadPlugins() { + // await this.emitAsync('plugins.beforeLoad'); + // const allPlugins = this.plugins.values(); + // for (const plugin of allPlugins) { + // plugin.instance = await this.loadPlugin(plugin); + // } + // await this.emitAsync('plugins.afterLoad'); + // } async start(argv = process.argv) { return this.cli.parseAsync(argv); } async destroy() { - await this.database.close() + await this.db.close(); } - protected async loadPlugin({ - entry, - options = {}, - }: { - entry: string | Function; - options: any; - }) { - let main: any; - if (typeof entry === 'function') { - main = entry; - } else if (typeof entry === 'string') { - const pathname = `${entry}/${__filename.endsWith('.ts') ? 'src' : 'lib' - }/server`; - main = require(pathname).default; - } - return main && (await main.call(this, options)); - } + // protected async loadPlugin({ + // entry, + // options = {}, + // }: { + // entry: string | Function; + // options: any; + // }) { + // let main: any; + // if (typeof entry === 'function') { + // main = entry; + // } else if (typeof entry === 'string') { + // const pathname = `${entry}/${ + // __filename.endsWith('.ts') ? 'src' : 'lib' + // }/server`; + // main = require(pathname).default; + // } + // return main && (await main.call(this, options)); + // } } export default Application; diff --git a/packages/server/src/middlewares/action-params.ts b/packages/server/src/middlewares/action-params.ts index 2d780af9c..08aac41a7 100644 --- a/packages/server/src/middlewares/action-params.ts +++ b/packages/server/src/middlewares/action-params.ts @@ -1,7 +1,7 @@ -import { actions } from '@nocobase/actions'; +import { Context, Next } from '@nocobase/actions'; export function actionParams(options: any = {}) { - return async (ctx: actions.Context, next: actions.Next) => { + return async (ctx: Context, next: Next) => { const { actionName, resourceField, resourceName, fields = {} } = ctx.action.params; const table = ctx.db.getTable(resourceField ? resourceField.options.target : resourceName); // ctx.state.developerMode = {[Op.not]: null}; diff --git a/packages/server/src/middlewares/app-dist-serve.ts b/packages/server/src/middlewares/app-dist-serve.ts index da8db08ff..230751660 100644 --- a/packages/server/src/middlewares/app-dist-serve.ts +++ b/packages/server/src/middlewares/app-dist-serve.ts @@ -1,7 +1,7 @@ import path from 'path'; import send from 'koa-send'; import serve from 'koa-static'; -import actions from '@nocobase/actions'; +import { Context, Next } from '@nocobase/actions'; export interface AppDistServeOptions { root?: string; @@ -9,7 +9,7 @@ export interface AppDistServeOptions { } export function appDistServe(options: AppDistServeOptions = {}) { - return async (ctx: actions.Context, next: actions.Next) => { + return async (ctx: Context, next: Next) => { if (!options.root || !options.useStaticServer) { return next(); } diff --git a/packages/server/src/middlewares/data-wrapping.ts b/packages/server/src/middlewares/data-wrapping.ts index 8f6253e9e..6f5f3e64c 100644 --- a/packages/server/src/middlewares/data-wrapping.ts +++ b/packages/server/src/middlewares/data-wrapping.ts @@ -1,6 +1,6 @@ -import { Action } from '@nocobase/resourcer'; +import { Context, Next } from '@nocobase/actions'; -export async function dataWrapping(ctx, next) { +export async function dataWrapping(ctx: Context, next: Next) { await next(); if (ctx.withoutDataWrapping) { return; diff --git a/packages/server/src/middlewares/demo-blacklisted-actions.ts b/packages/server/src/middlewares/demo-blacklisted-actions.ts index 253b21bfc..ecd8da0d3 100644 --- a/packages/server/src/middlewares/demo-blacklisted-actions.ts +++ b/packages/server/src/middlewares/demo-blacklisted-actions.ts @@ -1,4 +1,4 @@ -import { actions } from '@nocobase/actions'; +import { Context, Next } from '@nocobase/actions'; export interface DemoBlackListedActionsOptions { emails?: string[]; @@ -9,7 +9,7 @@ const defaultBlacklist = ['create', 'update', 'destroy', 'sort', 'upload']; export function demoBlacklistedActions(options: DemoBlackListedActionsOptions = {}) { const { emails, blacklist = defaultBlacklist } = options; - return async (ctx: actions.Context, next: actions.Next) => { + return async (ctx: Context, next: Next) => { const currentUser = ctx.state.currentUser; if (currentUser && emails.includes(currentUser.email)) { return next(); diff --git a/packages/server/src/plugin.ts b/packages/server/src/plugin.ts new file mode 100644 index 000000000..153e86a9a --- /dev/null +++ b/packages/server/src/plugin.ts @@ -0,0 +1,70 @@ +import { uid } from '@nocobase/database'; +import { Application } from './application'; + +export interface PluginOptions { + app?: Application; + name?: string; + activate?: boolean; + displayName?: string; + description?: string; + version?: string; +} + +export interface IPlugin { + install?: (this: Plugin) => void; + load?: (this: Plugin) => void; +} + +export type PluginFn = (this: Plugin) => void; + +export type PluginType = string | PluginFn | typeof Plugin | IPlugin; + +export class Plugin implements IPlugin { + options: PluginOptions = {}; + app: Application; + callbacks: IPlugin = {}; + + constructor(plugin?: PluginType, options?: PluginOptions) { + this.app = options?.app; + this.options = options || {}; + if (typeof plugin === 'function') { + if (!this.options?.name && plugin.name) { + this.options.name = plugin.name; + } + this.callbacks.load = plugin as any; + } else if ( + typeof plugin === 'object' && + plugin.constructor === {}.constructor + ) { + this.callbacks = plugin; + } + const cName = this.constructor.name; + if (this.options && !this.options?.name && cName && cName !== 'Plugin') { + this.options.name = cName; + } + } + + getName() { + return this.options.name || uid(); + } + + async activate() { + this.options.activate = true; + } + + async install() { + await this.call('install'); + } + + async call(name: string) { + if (!this.callbacks[name]) { + return; + } + const callback = this.callbacks[name].bind(this); + await callback(); + } + + async load() { + await this.call('load'); + } +} diff --git a/yarn.lock b/yarn.lock index 1424e4825..3835af857 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3145,6 +3145,17 @@ call-me-maybe "^1.0.1" glob-to-regexp "^0.3.0" +"@nocobase/plugin-pages@^0.4.0-alpha.7": + version "0.4.0-alpha.7" + resolved "https://registry.npmjs.org/@nocobase/plugin-pages/-/plugin-pages-0.4.0-alpha.7.tgz#f3998d565eaca66c60b4e8f090ff2b9743cba615" + integrity sha512-eOyvxmGuKLaEk9E5M31gA8XYLCEsg2GUYpbtwwqYfurzICxwezEKYJZ/2o4yVy4sSCCdqMMzvwpPJDR/+mmrJw== + dependencies: + "@nocobase/actions" "^0.4.0-alpha.7" + "@nocobase/database" "^0.4.0-alpha.7" + "@nocobase/resourcer" "^0.4.0-alpha.7" + "@nocobase/server" "^0.4.0-alpha.7" + crypto-random-string "^3.3.1" + "@nodelib/fs.scandir@2.1.5": version "2.1.5" resolved "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" @@ -3776,6 +3787,13 @@ dependencies: "@types/koa" "*" +"@types/koa-mount@^4.0.1": + version "4.0.1" + resolved "https://registry.npmjs.org/@types/koa-mount/-/koa-mount-4.0.1.tgz#2994be86eaa3d9dc97365e6ebfa227cee3c5f157" + integrity sha512-HNeg80CVS9Dfq8dGYqCZZCAUm7g6jPCNJ1ydqVLEJxLrjmeburpvq+lOZkE4rxBZ6O38dr3tj9IA3IfbdoI05w== + dependencies: + "@types/koa" "*" + "@types/koa@*", "@types/koa@^2.13.1": version "2.13.4" resolved "https://registry.npmjs.org/@types/koa/-/koa-2.13.4.tgz#10620b3f24a8027ef5cbae88b393d1b31205726b" @@ -4031,6 +4049,16 @@ resolved "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c" integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw== +"@types/strip-bom@^3.0.0": + version "3.0.0" + resolved "https://registry.npmjs.org/@types/strip-bom/-/strip-bom-3.0.0.tgz#14a8ec3956c2e81edb7520790aecf21c290aebd2" + integrity sha1-FKjsOVbC6B7bdSB5CuzyHCkK69I= + +"@types/strip-json-comments@0.0.30": + version "0.0.30" + resolved "https://registry.npmjs.org/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz#9aa30c04db212a9a0649d6ae6fd50accc40748a1" + integrity sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ== + "@types/superagent@*": version "4.1.12" resolved "https://registry.npmjs.org/@types/superagent/-/superagent-4.1.12.tgz#fad68c6712936892ad24cf94f2f7a07cc749fd0f" @@ -6206,7 +6234,7 @@ chokidar@^2.0.4: optionalDependencies: fsevents "^1.2.7" -chokidar@^3.0.2, chokidar@^3.2.2: +chokidar@^3.0.2, chokidar@^3.2.2, chokidar@^3.5.1: version "3.5.2" resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz#dba3976fcadb016f66fd365021d91600d01c1e75" integrity sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ== @@ -7910,6 +7938,13 @@ dva@^2.6.0-beta.20: react-router-dom "^5.1.2" redux "^4.0.1" +dynamic-dedupe@^0.3.0: + version "0.3.0" + resolved "https://registry.npmjs.org/dynamic-dedupe/-/dynamic-dedupe-0.3.0.tgz#06e44c223f5e4e94d78ef9db23a6515ce2f962a1" + integrity sha1-BuRMIj9eTpTXjvnbI6ZRXOL5YqE= + dependencies: + xtend "^4.0.0" + ecc-jsbn@~0.1.1: version "0.1.2" resolved "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" @@ -11998,11 +12033,6 @@ jstoxml@^0.2.3: array-includes "^3.1.2" object.assign "^4.1.2" -just-has@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/just-has/-/just-has-1.0.0.tgz#7b0b5b4a1513e6684063521de74622c7629111cd" - integrity sha512-VxEMStctzOh3nQWZTq0vZrQe/B9oi75rhL4lcgYm5qrTX2enp38KMiKjkefdRR2ca01jlRHi62mNjme8G/TgvQ== - katex@^0.12.0: version "0.12.0" resolved "https://registry.npmjs.org/katex/-/katex-0.12.0.tgz#2fb1c665dbd2b043edcf8a1f5c555f46beaa0cb9" @@ -13208,7 +13238,7 @@ mkdirp-promise@^5.0.1: dependencies: mkdirp "*" -mkdirp@*, mkdirp@1.x, mkdirp@^1.0.3: +mkdirp@*, mkdirp@1.x, mkdirp@^1.0.3, mkdirp@^1.0.4: version "1.0.4" resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== @@ -17042,7 +17072,7 @@ resolve@1.17.0: dependencies: path-parse "^1.0.6" -resolve@^1.10.0, resolve@^1.10.1, resolve@^1.14.2, resolve@^1.16.1, resolve@^1.17.0, resolve@^1.18.1, resolve@^1.20.0, resolve@^1.3.2, resolve@^1.4.0, resolve@^1.5.0, resolve@^1.8.1: +resolve@^1.0.0, resolve@^1.10.0, resolve@^1.10.1, resolve@^1.14.2, resolve@^1.16.1, resolve@^1.17.0, resolve@^1.18.1, resolve@^1.20.0, resolve@^1.3.2, resolve@^1.4.0, resolve@^1.5.0, resolve@^1.8.1: version "1.20.0" resolved "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== @@ -17900,6 +17930,14 @@ source-map-resolve@^0.5.0, source-map-resolve@^0.5.2: source-map-url "^0.4.0" urix "^0.1.0" +source-map-support@^0.5.12: + version "0.5.20" + resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.20.tgz#12166089f8f5e5e8c56926b377633392dd2cb6c9" + integrity sha512-n1lZZ8Ve4ksRqizaBQgxXDgKwttHDhyfQjA6YZZn8+AroHbsIz+JjwxQDxbp+7y5OYCI8t1Yk7etjD9CRd2hIw== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + source-map-support@^0.5.16, source-map-support@^0.5.17, source-map-support@^0.5.6, source-map-support@~0.5.19: version "0.5.19" resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" @@ -18376,16 +18414,16 @@ strip-indent@^3.0.0: dependencies: min-indent "^1.0.0" +strip-json-comments@^2.0.0, strip-json-comments@~2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= + strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: version "3.1.1" resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== -strip-json-comments@~2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" - integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= - strong-log-transformer@^2.0.0: version "2.1.0" resolved "https://registry.npmjs.org/strong-log-transformer/-/strong-log-transformer-2.1.0.tgz#0f5ed78d325e0421ac6f90f7f10e691d6ae3ae10" @@ -18961,6 +18999,22 @@ ts-loader@^8.0.7: micromatch "^4.0.0" semver "^7.3.4" +ts-node-dev@^1.1.8: + version "1.1.8" + resolved "https://registry.npmjs.org/ts-node-dev/-/ts-node-dev-1.1.8.tgz#95520d8ab9d45fffa854d6668e2f8f9286241066" + integrity sha512-Q/m3vEwzYwLZKmV6/0VlFxcZzVV/xcgOt+Tx/VjaaRHyiBcFlV0541yrT09QjzzCxlDZ34OzKjrFAynlmtflEg== + dependencies: + chokidar "^3.5.1" + dynamic-dedupe "^0.3.0" + minimist "^1.2.5" + mkdirp "^1.0.4" + resolve "^1.0.0" + rimraf "^2.6.1" + source-map-support "^0.5.12" + tree-kill "^1.2.2" + ts-node "^9.0.0" + tsconfig "^7.0.0" + ts-node@^10.2.1: version "10.2.1" resolved "https://registry.npmjs.org/ts-node/-/ts-node-10.2.1.tgz#4cc93bea0a7aba2179497e65bb08ddfc198b3ab5" @@ -18979,7 +19033,7 @@ ts-node@^10.2.1: make-error "^1.1.1" yn "3.1.1" -ts-node@^9.1.1: +ts-node@^9.0.0, ts-node@^9.1.1: version "9.1.1" resolved "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz#51a9a450a3e959401bda5f004a72d54b936d376d" integrity sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg== @@ -19000,6 +19054,16 @@ tsconfig-paths@^3.9.0: minimist "^1.2.0" strip-bom "^3.0.0" +tsconfig@^7.0.0: + version "7.0.0" + resolved "https://registry.npmjs.org/tsconfig/-/tsconfig-7.0.0.tgz#84538875a4dc216e5c4a5432b3a4dec3d54e91b7" + integrity sha512-vZXmzPrL+EmC4T/4rVlT2jNVMWCi/O4DIiSj3UHg1OE5kCKbk4mfrXc6dZksLgRM/TZlKnousKH9bbTazUWRRw== + dependencies: + "@types/strip-bom" "^3.0.0" + "@types/strip-json-comments" "0.0.30" + strip-bom "^3.0.0" + strip-json-comments "^2.0.0" + tslib@2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/tslib/-/tslib-2.0.1.tgz#410eb0d113e5b6356490eec749603725b021b43e"