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"