From 05ecb25d1b3939ce1cbdeae8b88eeeec1c4c0e8b Mon Sep 17 00:00:00 2001 From: ChengLei Shao Date: Mon, 18 Oct 2021 12:49:37 +0800 Subject: [PATCH] feat: create nocobase app with simple & quickstart option (#87) * feat: create nocobase app with simple & quickstart option * chore: delete template file * create-nocobase-app: add env API_PORT fallback * chore: log * env default fallback * move config dir * change has yarn * chore: prettier * fix: npm running issue * database testing support sqlite * once... * chore: typo * fix: sqlite test * update readme * feat: copy .env.example to .env at create-nocobase-app * create-nocobase-app: change sqlite3 to github master * create-nocobase-app: .env template * create-nocobase-app: update .env * chore: typo * update README * chore: Application constructor * feat: sqlite demo data support * fix test * fix: application error * chore: plugin-client run sql * fix: application createCli * fix: can choose whether to register actions * chore: model compile error * fix: support sqlite * fix: demo data set index sequence on postgresql * chore: code reduce * fix: operators are compatible with sqlite * add impor demo option to init command * update env Co-authored-by: chenos --- .gitignore | 2 + README.md | 25 +- README.zh-CN.md | 27 +- package.json | 1 + packages/app/src/apis/index.ts | 16 +- packages/create-nocobase-app/package.json | 5 +- .../src/AppGenerator/AppGenerator.ts | 7 + packages/create-nocobase-app/src/cli.ts | 131 ++++++-- packages/create-nocobase-app/src/index.ts | 4 + .../create-nocobase-app/src/utils/index.ts | 28 ++ .../templates/AppGenerator/.env.example | 34 +- .../templates/AppGenerator/.env.tpl | 47 +++ .../templates/AppGenerator/.umirc.ts | 6 +- .../templates/AppGenerator/package.json.tpl | 5 +- .../AppGenerator/src/apis/config/db.ts.tpl | 36 +++ .../templates/AppGenerator/src/apis/index.ts | 42 +-- packages/database/package.json | 2 +- .../src/__tests__/fields/radio.test.ts | 75 +++-- .../src/__tests__/fields/types.test.ts | 111 ++++--- packages/database/src/__tests__/index.ts | 32 +- .../src/__tests__/model/model.test.ts | 257 ++++++++------- packages/database/src/__tests__/op.test.ts | 3 +- packages/database/src/database.ts | 2 +- packages/database/src/model.ts | 251 ++++++++------ packages/database/src/op.ts | 84 +++-- packages/database/src/table.ts | 130 ++++---- packages/database/src/utils.ts | 1 + .../src/__tests__/sqlite.test.ts | 65 ++++ .../src/db/part1/01-ui_schemas.sql | 2 +- .../src/db/part1/02-collections.sql | 2 +- .../plugin-client/src/db/part1/03-fields.sql | 2 +- .../plugin-client/src/db/part1/04-routes.sql | 2 +- .../src/db/part1/05-attachments.sql | 2 +- .../src/db/part2/06-t_2uhu4szs1kq.sql | 2 +- .../src/db/part2/07-t_fsveob6p269.sql | 2 +- .../src/db/part2/08-t_geso7fru7a9.sql | 2 +- .../src/db/part2/09-t_bphcojtl8bx.sql | 1 + .../src/db/part2/10-t_jh7a28dsfzi.sql | 1 + .../src/db/part2/11-t_p13gbi31uux.sql | 1 + .../plugin-client/src/db/part2/12-roles.sql | 1 + .../src/db/part2/13-action_permissions.sql | 2 +- .../src/db/part2/14-field_permissions.sql | 1 + .../src/db/part2/15-roles_ui_schemas.sql | 1 + .../src/db/postgres/setinval.sql | 5 + packages/plugin-client/src/server.ts | 89 +++-- .../plugin-users/src/__tests__/fields.test.ts | 4 +- packages/plugin-users/src/server.ts | 4 +- .../server/src/__tests__/application.test.ts | 25 +- packages/server/src/application.ts | 107 ++---- packages/server/src/helper.ts | 100 ++++++ yarn.lock | 305 ++++++++++++++++-- 51 files changed, 1433 insertions(+), 659 deletions(-) create mode 100644 packages/create-nocobase-app/src/utils/index.ts create mode 100644 packages/create-nocobase-app/templates/AppGenerator/.env.tpl create mode 100644 packages/create-nocobase-app/templates/AppGenerator/src/apis/config/db.ts.tpl create mode 100644 packages/plugin-client/src/__tests__/sqlite.test.ts create mode 100644 packages/plugin-client/src/db/postgres/setinval.sql create mode 100644 packages/server/src/helper.ts diff --git a/.gitignore b/.gitignore index 3d4f39edc..c84fdf1e4 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,5 @@ uploads/ /.idea .vercel +packages/database/src/__tests__/.db +db.sqlite \ No newline at end of file diff --git a/README.md b/README.md index e4b1eeb10..2cd8c2144 100644 --- a/README.md +++ b/README.md @@ -60,19 +60,34 @@ Node: Database: - PostgreSQL 10.x+ +- Sqlite 3+ Installation ---------- ### Create a project with `create-nocobase-app` +#### Quickstart ~~~shell -mkdir my-nocobase-app && cd my-nocobase-app -yarn create nocobase-app -cp .env.example .env +yarn create nocobase-app my-nocobase-app --quickstart +~~~ + +#### Start with configuration +~~~shell +# 1. create project +yarn create nocobase-app my-nocobase-app +cd my-nocobase-app + +# 2. edit configuration in .env file +vim .env + +# 3. start a database (optional) docker-compose up -d postgres -yarn install -yarn nocobase init + +# 4. create initialization data +yarn nocobase init --import-demo + +# 5. start project yarn start ~~~ diff --git a/README.zh-CN.md b/README.zh-CN.md index 18f810ba5..e30ddc5b7 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -62,23 +62,38 @@ Node: Database: - PostgreSQL 10.x+ +- Sqlite 3+ 安装 & 运行 ---------- ### 通过 `create-nocobase-app` 创建项目 +#### 快速启动方式 ~~~shell -mkdir my-nocobase-app && cd my-nocobase-app -yarn create nocobase-app -cp .env.example .env +yarn create nocobase-app my-nocobase-app --quickstart +~~~ + +#### 自定义启动方式 +~~~shell +# 1. 创建项目 +yarn create nocobase-app my-nocobase-app +cd my-nocobase-app + +# 2. 修改.env中对应的数据库配置 +vim .env + +# 3. 启动预置数据库(可选) docker-compose up -d postgres -yarn install -yarn nocobase init + +# 4. 初始化数据 +yarn nocobase init --import-demo + +# 5. 启动项目 yarn start ~~~ -浏览器内打开 http://localhost:8000 +使用浏览器打开 http://localhost:8000 ### 使用 docker compose diff --git a/package.json b/package.json index 05bddd395..023f9baf8 100644 --- a/package.json +++ b/package.json @@ -68,6 +68,7 @@ "react-router-dom": "^5.2.0", "react-test-renderer": "^17.0.2", "rimraf": "^3.0.2", + "sqlite3": "^5.0.2", "supertest": "^6.1.3", "ts-jest": "^26.5.6", "ts-node": "^9.1.1", diff --git a/packages/app/src/apis/index.ts b/packages/app/src/apis/index.ts index 611904954..e948f7322 100644 --- a/packages/app/src/apis/index.ts +++ b/packages/app/src/apis/index.ts @@ -4,7 +4,18 @@ import path from 'path'; const start = Date.now(); const api = new Server({ - database: { + database: process.env.DB_DIALECT === 'sqlite' ? { + dialect: process.env.DB_DIALECT as any, + storage: path.resolve(process.cwd(), './db.sqlite'), + logging: process.env.DB_LOG_SQL === 'on' ? console.log : false, + define: {}, + sync: { + force: false, + alter: { + drop: false, + }, + }, + } : { username: process.env.DB_USER, password: process.env.DB_PASSWORD, database: process.env.DB_DATABASE, @@ -59,7 +70,7 @@ for (const plugin of plugins) { api.plugin( require(`@nocobase/plugin-client/${libDir}/server`).default, { dist: path.resolve(process.cwd(), './dist'), - importDemo: true, + // importDemo: true, }); if (process.argv.length < 3) { @@ -67,7 +78,6 @@ if (process.argv.length < 3) { process.argv.push('start', '--port', process.env.API_PORT); } -console.log(process.argv); api.parse(process.argv).then(() => { console.log(`Start-up time: ${(Date.now() - start) / 1000}s`); diff --git a/packages/create-nocobase-app/package.json b/packages/create-nocobase-app/package.json index e9c5b1216..2dc8fd8cd 100755 --- a/packages/create-nocobase-app/package.json +++ b/packages/create-nocobase-app/package.json @@ -15,7 +15,10 @@ }, "license": "MIT", "dependencies": { - "@umijs/utils": "3.5.17" + "@umijs/utils": "3.5.17", + "commander": "^8.2.0", + "execa": "^5.1.1", + "ora": "^5.4.1" }, "bin": { "create-nocobase-app": "bin/create-nocobase-app.js" diff --git a/packages/create-nocobase-app/src/AppGenerator/AppGenerator.ts b/packages/create-nocobase-app/src/AppGenerator/AppGenerator.ts index cd5a854c3..8a4c5c4f7 100755 --- a/packages/create-nocobase-app/src/AppGenerator/AppGenerator.ts +++ b/packages/create-nocobase-app/src/AppGenerator/AppGenerator.ts @@ -2,11 +2,18 @@ import { Generator } from '@umijs/utils'; import { join } from 'path'; export default class AppGenerator extends Generator { + private tplContext = {}; + + setTplContext(context) { + this.tplContext = context; + } + async writing() { this.copyDirectory({ context: { version: require('../../package').version, conventionRoutes: this.args.conventionRoutes, + ...this.tplContext }, path: join(__dirname, '../../templates/AppGenerator'), target: this.cwd, diff --git a/packages/create-nocobase-app/src/cli.ts b/packages/create-nocobase-app/src/cli.ts index c4b3d372c..dec471a19 100755 --- a/packages/create-nocobase-app/src/cli.ts +++ b/packages/create-nocobase-app/src/cli.ts @@ -1,30 +1,107 @@ -import { chalk, yParser } from '@umijs/utils'; -import { existsSync } from 'fs'; -import { join } from 'path'; +import { chalk } from '@umijs/utils'; +import commander from 'commander'; +import path from 'path'; +import ora from 'ora'; +import { hasYarn, runInit, runInstall, runStart } from './utils'; +import execa from 'execa'; -const args = yParser(process.argv.slice(2), { - alias: { - version: ['v'], - help: ['h'], - }, - boolean: ['version'], -}); +const packageJson = require('../package.json'); -if (args.version && !args._[0]) { - args._[0] = 'version'; - const local = existsSync(join(__dirname, '../.local')) - ? chalk.cyan('@local') - : ''; - const { name, version } = require('../package.json'); - console.log(`${name}@${version}${local}`); -} else { - require('./') - .default({ - cwd: process.cwd(), - args, - }) - .catch((err: Error) => { - console.error(`Create failed, ${err.message}`); - console.error(err); +const program = new commander.Command(packageJson.name) + .version(packageJson.version) + .option('--simple', 'create nocobase app without install dependencies') + .option('--quickstart', 'create quickstart nocobase app') + .arguments('') + .usage(`${chalk.green('')}`) + .action(async (directory, options) => { + console.log( + `Creating a new Nocobase application at ${chalk.green(directory)}.`, + ); + console.log('Creating files.'); + + const fullPath = path.join(process.cwd(), directory); + + await require('./index').default({ + cwd: fullPath, + args: {}, + tplContext: options.quickstart + ? { quickstart: true } + : { quickstart: false }, }); -} + + const cmd = chalk.cyan(hasYarn() ? 'yarn' : 'npm run'); + + if (options.simple) { + console.log(); + console.log('Done. You can start by doing:'); + console.log(); + console.log(` ${chalk.cyan('cd')} ${directory}`); + console.log(` ${cmd} install`); + console.log(` ${cmd} nocobase init --import-demo`); + console.log(` ${cmd} start`); + console.log(); + return; + } + + const installPrefix = chalk.yellow('Installing dependencies:'); + const loader = ora(installPrefix).start(); + const logInstall = (chunk = '') => { + loader.text = `${installPrefix} ${chunk + .toString() + .split('\n') + .join(' ')}`; + }; + + const runner = runInstall(fullPath); + + runner?.stdout?.on('data', logInstall); + runner?.stderr?.on('data', logInstall); + + await runner; + loader.stop(); + console.log(`Dependencies installed ${chalk.green('successfully')}.`); + console.log(); + console.log(`Your application was created at ${chalk.green(directory)}.\n`); + + if (options.quickstart) { + // Using Sqlite as Database + const prefix = chalk.yellow('Nocobase init'); + const initLoader = ora(prefix).start(); + + try { + const initLog = (chunk = '') => { + initLoader.text = `${prefix} ${chunk + .toString() + .split('\n') + .join(' ')}`; + }; + + const init = runInit(fullPath); + init.stderr.on('data', initLog); + init.stdout.on('data', initLog); + await init; + initLoader.stop(); + } catch (e) { + initLoader.stop(); + console.log(); + console.log(e.message); + process.exit(1); + } + + console.log(`Running your application.`); + await execa('npm', ['run', 'start'], { + stdio: 'inherit', + cwd: fullPath, + }); + } else { + console.log(); + console.log('You can start by doing:'); + console.log(); + console.log(` ${chalk.cyan('cd')} ${directory}`); + console.log(` ${cmd} nocobase init --import-demo`); + console.log(` ${cmd} start`); + console.log(); + } + }) + .showHelpAfterError() + .parse(process.argv); diff --git a/packages/create-nocobase-app/src/index.ts b/packages/create-nocobase-app/src/index.ts index e79f1f6a5..e7c606421 100755 --- a/packages/create-nocobase-app/src/index.ts +++ b/packages/create-nocobase-app/src/index.ts @@ -4,13 +4,17 @@ import AppGenerator from './AppGenerator/AppGenerator'; export default async ({ cwd, args, + tplContext }: { cwd: string; args: yargs.Arguments; + tplContext: object }) => { const generator = new AppGenerator({ cwd, args, }); + + generator.setTplContext(tplContext); await generator.run(); }; diff --git a/packages/create-nocobase-app/src/utils/index.ts b/packages/create-nocobase-app/src/utils/index.ts new file mode 100644 index 000000000..d86cd082d --- /dev/null +++ b/packages/create-nocobase-app/src/utils/index.ts @@ -0,0 +1,28 @@ +import execa from 'execa'; + +export function hasYarn() { + return (process.env.npm_config_user_agent || '').indexOf('yarn') === 0; +} + +function runYarn(path: string, args: string[]) { + if (hasYarn()) { + return execa('yarn', args, { + cwd: path, + stdin: 'ignore', + }); + } + + return execa('npm', args, { cwd: path, stdin: 'ignore' }); +} + +export function runInstall(path) { + return runYarn(path, ['install']); +} + +export function runStart(path) { + return runYarn(path, ['run', 'start']); +} + +export function runInit(path) { + return runYarn(path, ['run', 'nocobase', 'init', '--import-demo']); +} diff --git a/packages/create-nocobase-app/templates/AppGenerator/.env.example b/packages/create-nocobase-app/templates/AppGenerator/.env.example index c46d33e18..c1891190c 100644 --- a/packages/create-nocobase-app/templates/AppGenerator/.env.example +++ b/packages/create-nocobase-app/templates/AppGenerator/.env.example @@ -1,34 +1,25 @@ ########## DOCKER COMPOSE ENV ########## -DB_POSTGRES_PORT=15432 -APP_PORT=13000 -ADMINER_PORT=18080 +ADMINER_PORT=8080 +DB_MYSQL_PORT=3306 +DB_POSTGRES_PORT=5432 +VERDACCIO_PORT=4873 +APP_PORT=13001 +API_PORT=13002 ########## NOCOBASE ENV ########## # DATABASE DB_DIALECT=postgres +DB_HOST=localhost +DB_PORT=5432 DB_DATABASE=nocobase DB_USER=nocobase DB_PASSWORD=nocobase # set to 'on' to enable log DB_LOG_SQL= -# for localhost -DB_PORT=15432 -DB_HOST=localhost - -# for docker -# DB_PORT=5432 -# DB_HOST=postgres - -# API & APP - -NOCOBASE_ENV= -API_PORT=13001 -API_URL=/api/ - # ADMIN USER (Initialization only) ADMIN_EMAIL=admin@nocobase.com @@ -41,4 +32,11 @@ STORAGE_TYPE=local # LOCAL STORAGE LOCAL_STORAGE_USE_STATIC_SERVER=true -LOCAL_STORAGE_BASE_URL=http://localhost:23000 +LOCAL_STORAGE_BASE_URL=http://localhost:13002 + +# ALI OSS STORAGE +ALI_OSS_STORAGE_BASE_URL= +ALI_OSS_REGION=oss-cn-beijing +ALI_OSS_ACCESS_KEY_ID= +ALI_OSS_ACCESS_KEY_SECRET= +ALI_OSS_BUCKET= diff --git a/packages/create-nocobase-app/templates/AppGenerator/.env.tpl b/packages/create-nocobase-app/templates/AppGenerator/.env.tpl new file mode 100644 index 000000000..fc5f471f3 --- /dev/null +++ b/packages/create-nocobase-app/templates/AppGenerator/.env.tpl @@ -0,0 +1,47 @@ +########## DOCKER COMPOSE ENV ########## +ADMINER_PORT=8080 +DB_MYSQL_PORT=3306 +DB_POSTGRES_PORT=5432 +VERDACCIO_PORT=4873 +APP_PORT=13001 +API_PORT=13002 + +########## NOCOBASE ENV ########## + +# DATABASE +{{#quickstart}} +DB_DIALECT=sqlite +DB_STORAGE=db.sqlite +{{/quickstart}} +{{^quickstart}} +DB_DIALECT=postgres +DB_HOST=localhost +DB_PORT=5432 +DB_DATABASE=nocobase +DB_USER=nocobase +DB_PASSWORD=nocobase +{{/quickstart}} + +# set to 'on' to enable log +DB_LOG_SQL= + +# ADMIN USER (Initialization only) + +ADMIN_EMAIL=admin@nocobase.com +ADMIN_PASSWORD=admin + +# STORAGE (Initialization only) + +# local or ali-oss +STORAGE_TYPE=local + +# LOCAL STORAGE +LOCAL_STORAGE_USE_STATIC_SERVER=true +LOCAL_STORAGE_BASE_URL=http://localhost:13002 + +# ALI OSS STORAGE +ALI_OSS_STORAGE_BASE_URL= +ALI_OSS_REGION=oss-cn-beijing +ALI_OSS_ACCESS_KEY_ID= +ALI_OSS_ACCESS_KEY_SECRET= +ALI_OSS_BUCKET= diff --git a/packages/create-nocobase-app/templates/AppGenerator/.umirc.ts b/packages/create-nocobase-app/templates/AppGenerator/.umirc.ts index b06bdf995..90daf5955 100644 --- a/packages/create-nocobase-app/templates/AppGenerator/.umirc.ts +++ b/packages/create-nocobase-app/templates/AppGenerator/.umirc.ts @@ -11,12 +11,12 @@ export default defineConfig({ type: 'none', }, define: { - 'process.env.API_URL': process.env.API_URL, - 'process.env.API_PORT': process.env.API_PORT, + 'process.env.API_URL': process.env.API_URL || "http://127.0.0.1", + 'process.env.API_PORT': process.env.API_PORT || "13001", }, proxy: { '/api': { - 'target': `http://localhost:${process.env.API_PORT}/`, + 'target': `http://localhost:${process.env.API_PORT || "13001"}/`, 'changeOrigin': true, 'pathRewrite': { '^/api': '/api' }, }, diff --git a/packages/create-nocobase-app/templates/AppGenerator/package.json.tpl b/packages/create-nocobase-app/templates/AppGenerator/package.json.tpl index 8ed3cc10c..e8bac2876 100644 --- a/packages/create-nocobase-app/templates/AppGenerator/package.json.tpl +++ b/packages/create-nocobase-app/templates/AppGenerator/package.json.tpl @@ -5,7 +5,7 @@ "start": "concurrently \"npm run start-server\" \"umi dev\"", "start-client": "umi dev", "start-server": "ts-node-dev -r dotenv/config --project tsconfig.apis.json ./src/apis/index.ts", - "nocobase": "ts-node-dev -r dotenv/config --project tsconfig.apis.json ./src/apis/index.ts", + "nocobase": "ts-node -r dotenv/config --project tsconfig.apis.json ./src/apis/index.ts", "serve": "node -r dotenv/config ./lib/apis/index.js", "build": "npm run build-server && npm run build-client", "build-client": "umi build", @@ -27,7 +27,8 @@ ] }, "dependencies": { - "@nocobase/plugin-action-logs": "^{{{ version }}}", + {{#quickstart}}"sqlite3": "https://github.com/mapbox/node-sqlite3/tarball/master", + {{/quickstart}}"@nocobase/plugin-action-logs": "^{{{ version }}}", "@nocobase/plugin-china-region": "^{{{ version }}}", "@nocobase/plugin-client": "^{{{ version }}}", "@nocobase/plugin-collections": "^{{{ version }}}", diff --git a/packages/create-nocobase-app/templates/AppGenerator/src/apis/config/db.ts.tpl b/packages/create-nocobase-app/templates/AppGenerator/src/apis/config/db.ts.tpl new file mode 100644 index 000000000..3a22ae050 --- /dev/null +++ b/packages/create-nocobase-app/templates/AppGenerator/src/apis/config/db.ts.tpl @@ -0,0 +1,36 @@ +import { DatabaseOptions } from '@nocobase/database'; +{{#quickstart}} +export default { + dialect: process.env.DB_DIALECT, + dialectModule: require('sqlite3'), + storage: process.env.DB_STORAGE +} as DatabaseOptions; +{{/quickstart}} +{{^quickstart}} +export default { + 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', + }, + pool: { + max: 5, + min: 0, + acquire: 60000, + idle: 10000, + }, + logging: process.env.DB_LOG_SQL === 'on' ? console.log : false, + define: {}, + sync: { + force: false, + alter: { + drop: false, + }, + }, +} as DatabaseOptions; +{{/quickstart}} diff --git a/packages/create-nocobase-app/templates/AppGenerator/src/apis/index.ts b/packages/create-nocobase-app/templates/AppGenerator/src/apis/index.ts index ef13aa210..7f08964fe 100644 --- a/packages/create-nocobase-app/templates/AppGenerator/src/apis/index.ts +++ b/packages/create-nocobase-app/templates/AppGenerator/src/apis/index.ts @@ -1,35 +1,10 @@ -import Server from '@nocobase/server'; import path from 'path'; +import Application from '@nocobase/server'; const start = Date.now(); -const api = new Server({ - 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', - }, - pool: { - max: 5, - min: 0, - acquire: 60000, - idle: 10000, - }, - logging: process.env.DB_LOG_SQL === 'on' ? console.log : false, - define: {}, - sync: { - force: false, - alter: { - drop: false, - }, - }, - }, +const api = new Application({ + database: require('./config/db').default, resourcer: { prefix: '/api', }, @@ -49,24 +24,19 @@ const plugins = [ ]; for (const plugin of plugins) { - api.plugin( - require(`${plugin}/lib/server`).default, - ); + api.plugin(require(`${plugin}/lib/server`).default); } -api.plugin( - require(`@nocobase/plugin-client/lib/server`).default, { +api.plugin(require(`@nocobase/plugin-client/lib/server`).default, { dist: path.resolve(process.cwd(), './dist'), importDemo: true, }); if (process.argv.length < 3) { // @ts-ignore - process.argv.push('start', '--port', process.env.API_PORT); + process.argv.push('start', '--port', process.env.API_PORT || '13001'); } -console.log(process.argv); - api.parse(process.argv).then(() => { console.log(`Start-up time: ${(Date.now() - start) / 1000}s`); }); diff --git a/packages/database/package.json b/packages/database/package.json index 37cac5435..ec8fb4d12 100644 --- a/packages/database/package.json +++ b/packages/database/package.json @@ -10,7 +10,7 @@ "bcrypt": "^5.0.0", "deepmerge": "^4.2.2", "glob": "^7.1.6", - "sequelize": "^6.3.3" + "sequelize": "6.7.0" }, "repository": { "type": "git", diff --git a/packages/database/src/__tests__/fields/radio.test.ts b/packages/database/src/__tests__/fields/radio.test.ts index c2b6aa5c6..7a946fd00 100644 --- a/packages/database/src/__tests__/fields/radio.test.ts +++ b/packages/database/src/__tests__/fields/radio.test.ts @@ -11,13 +11,13 @@ describe('radio', () => { fields: [ { type: 'string', - name: 'name' + name: 'name', }, { type: 'hasMany', - name: 'posts' - } - ] + name: 'posts', + }, + ], }); db.table({ @@ -38,25 +38,25 @@ describe('radio', () => { }, { type: 'radio', - name: 'pinned' + name: 'pinned', }, { type: 'radio', name: 'latest', - defaultValue: true + defaultValue: true, }, { type: 'radio', name: 'pinned_in_status', - scope: ['status'] + scope: ['status'], }, { type: 'radio', name: 'pinned_in_user', scope: ['user'], - defaultValue: true - } - ] + defaultValue: true, + }, + ], }); await db.sync({ force: true }); @@ -79,7 +79,7 @@ describe('radio', () => { expect(posts.map(({ pinned, latest }) => ({ pinned, latest }))).toEqual([ { pinned: true, latest: false }, - { pinned: false, latest: true } + { pinned: false, latest: true }, ]); }); @@ -107,7 +107,7 @@ describe('radio', () => { expect(posts.map(({ pinned, latest }) => ({ pinned, latest }))).toEqual([ { pinned: false, latest: false }, { pinned: true, latest: false }, - { pinned: false, latest: true } + { pinned: false, latest: true }, ]); }); @@ -117,25 +117,50 @@ describe('radio', () => { const Post = db.getModel('posts'); const bulkCreated = await Post.bulkCreate([ { title: 'title1', status: 'published', user_id: 1 }, - { title: 'title2', status: 'published', user_id: 2, pinned_in_status: true }, - { title: 'title3', status: 'draft', user_id: 1, pinned_in_status: true }, + { + title: 'title2', + status: 'published', + user_id: 2, + pinned_in_status: true, + }, + { + title: 'title3', + status: 'draft', + user_id: 1, + pinned_in_status: true, + }, ]); - expect(bulkCreated.map(({ pinned_in_status, pinned_in_user }) => ({ pinned_in_status, pinned_in_user }))).toEqual([ + expect( + bulkCreated.map(({ pinned_in_status, pinned_in_user }) => ({ + pinned_in_status, + pinned_in_user, + })), + ).toEqual([ { pinned_in_status: false, pinned_in_user: false }, { pinned_in_status: true, pinned_in_user: true }, - { pinned_in_status: true, pinned_in_user: true } + { pinned_in_status: true, pinned_in_user: true }, ]); - const user1Post = await users[1].createPost({ title: 'title4', status: 'draft', pinned_in_status: true }); + const user1Post = await users[1].createPost({ + title: 'title4', + status: 'draft', + pinned_in_status: true, + }); expect(user1Post.pinned_in_status).toBe(true); expect(user1Post.pinned_in_user).toBe(true); const posts = await Post.findAll({ order: [['id', 'ASC']] }); - expect(posts.map(({ title, pinned_in_status, pinned_in_user }) => ({ title, pinned_in_status, pinned_in_user }))).toMatchObject([ + expect( + posts.map(({ title, pinned_in_status, pinned_in_user }) => ({ + title, + pinned_in_status, + pinned_in_user, + })), + ).toMatchObject([ { title: 'title1', pinned_in_status: false, pinned_in_user: false }, { title: 'title2', pinned_in_status: true, pinned_in_user: false }, { title: 'title3', pinned_in_status: false, pinned_in_user: true }, - { title: 'title4', pinned_in_status: true, pinned_in_user: true } + { title: 'title4', pinned_in_status: true, pinned_in_user: true }, ]); }); }); @@ -145,13 +170,13 @@ describe('radio', () => { const Post = db.getModel('posts'); await Post.bulkCreate([ { title: 'title1', pinned: true }, - { title: 'title2' } + { title: 'title2' }, ]); const created = await Post.create({ pinned: false }); expect(created.pinned).toBe(false); - const posts = await Post.findAll({ order: [['title', 'ASC']] }); + const posts = await Post.findAll({ order: [['id', 'ASC']] }); expect(posts.map(({ pinned }) => pinned)).toEqual([true, false, false]); }); @@ -160,13 +185,13 @@ describe('radio', () => { const Post = db.getModel('posts'); await Post.bulkCreate([ { title: 'title1', pinned: true }, - { title: 'title2' } + { title: 'title2' }, ]); const created = await Post.create({ pinned: true }); expect(created.pinned).toBe(true); - const posts = await Post.findAll({ order: [['title', 'ASC']] }); + const posts = await Post.findAll({ order: [['id', 'ASC']] }); expect(posts.map(({ pinned }) => pinned)).toEqual([false, false, true]); }); @@ -176,7 +201,7 @@ describe('radio', () => { await Post.bulkCreate([ { title: 'bug1' }, { title: 'bug2' }, - { title: 'bug3' } + { title: 'bug3' }, ]); const post = await Post.findByPk(2); @@ -186,7 +211,7 @@ describe('radio', () => { }); await post.update({ - status: 'draft' + status: 'draft', }); const posts = await Post.findAll({ order: [['id', 'ASC']] }); diff --git a/packages/database/src/__tests__/fields/types.test.ts b/packages/database/src/__tests__/fields/types.test.ts index f65edda17..dd558aba7 100644 --- a/packages/database/src/__tests__/fields/types.test.ts +++ b/packages/database/src/__tests__/fields/types.test.ts @@ -33,20 +33,28 @@ describe('field types', () => { const table = db.table({ name: 'test', }); - const field = buildField({ - type: actual, - name: 'test', - }, { - sourceTable: table, - database: db, - }); + const field = buildField( + { + type: actual, + name: 'test', + }, + { + sourceTable: table, + database: db, + }, + ); + expect(field).toBeInstanceOf(expected); + if (field instanceof Column) { const { type } = field.getAttributeOptions() as any; if (actual instanceof ABSTRACT) { expect(type).toBeInstanceOf(field.getDataType()); // postgres 的 text 不限制长度,无需参数 - if (db.sequelize.getDialect() !== 'postgres' || getDataTypeKey(type) !== 'TEXT') { + if ( + db.sequelize.getDialect() !== 'postgres' || + getDataTypeKey(type) !== 'TEXT' + ) { // 非严谨比较,undefined == null expect(type).toEqual(actual); } @@ -58,7 +66,7 @@ describe('field types', () => { } } db.close(); - } + }; it('shound be boolean', () => { assertTypeInstanceOf(Boolean, 'boolean'); @@ -69,9 +77,12 @@ describe('field types', () => { assertTypeInstanceOf(Integer, 'int'); assertTypeInstanceOf(Integer, 'integer'); assertTypeInstanceOf(Integer, DataTypes.INTEGER); - assertTypeInstanceOf(Integer, DataTypes.INTEGER({ - length: 5, - })); + assertTypeInstanceOf( + Integer, + DataTypes.INTEGER({ + length: 5, + }), + ); }); it('shound be tiny integer', () => { @@ -80,9 +91,12 @@ describe('field types', () => { assertTypeInstanceOf(Integer, 'tinyinteger'); assertTypeInstanceOf(Integer, 'tinyInteger'); assertTypeInstanceOf(Integer, DataTypes.TINYINT); - assertTypeInstanceOf(Integer, DataTypes.TINYINT({ - length: 5, - })); + assertTypeInstanceOf( + Integer, + DataTypes.TINYINT({ + length: 5, + }), + ); }); it('shound be small integer', () => { @@ -91,9 +105,12 @@ describe('field types', () => { assertTypeInstanceOf(Integer, 'smallinteger'); assertTypeInstanceOf(Integer, 'smallInteger'); assertTypeInstanceOf(Integer, DataTypes.SMALLINT); - assertTypeInstanceOf(Integer, DataTypes.SMALLINT({ - length: 5, - })); + assertTypeInstanceOf( + Integer, + DataTypes.SMALLINT({ + length: 5, + }), + ); }); it('shound be medium integer', () => { @@ -102,9 +119,12 @@ describe('field types', () => { assertTypeInstanceOf(Integer, 'MediumInteger'); assertTypeInstanceOf(Integer, 'MediumInteger'); assertTypeInstanceOf(Integer, DataTypes.MEDIUMINT); - assertTypeInstanceOf(Integer, DataTypes.MEDIUMINT({ - length: 5, - })); + assertTypeInstanceOf( + Integer, + DataTypes.MEDIUMINT({ + length: 5, + }), + ); }); it('shound be big integer', () => { @@ -113,16 +133,22 @@ describe('field types', () => { assertTypeInstanceOf(Integer, 'biginteger'); assertTypeInstanceOf(Integer, 'bigInteger'); assertTypeInstanceOf(Integer, DataTypes.BIGINT); - assertTypeInstanceOf(Integer, DataTypes.BIGINT({ - length: 5, - })); + assertTypeInstanceOf( + Integer, + DataTypes.BIGINT({ + length: 5, + }), + ); }); it('shound be float', () => { assertTypeInstanceOf(Float, 'float'); - assertTypeInstanceOf(Float, DataTypes.FLOAT({ - length: 5, - })); + assertTypeInstanceOf( + Float, + DataTypes.FLOAT({ + length: 5, + }), + ); }); it('shound be double', () => { @@ -132,9 +158,12 @@ describe('field types', () => { it('shound be real', () => { assertTypeInstanceOf(Real, 'real'); - assertTypeInstanceOf(Real, DataTypes.REAL({ - length: 5, - })); + assertTypeInstanceOf( + Real, + DataTypes.REAL({ + length: 5, + }), + ); }); it('shound be decimal', () => { @@ -151,9 +180,12 @@ describe('field types', () => { it('shound be text', () => { assertTypeInstanceOf(Text, 'text'); assertTypeInstanceOf(Text, DataTypes.TEXT); - assertTypeInstanceOf(Text, DataTypes.TEXT({ - length: 'long', - })); + assertTypeInstanceOf( + Text, + DataTypes.TEXT({ + length: 'long', + }), + ); }); it('shound be time', () => { @@ -185,6 +217,9 @@ describe('field types', () => { }); it('shound be jsonb', () => { + if (process.env.DB_DIALECT === 'sqlite') { + return; + } assertTypeInstanceOf(Jsonb, 'jsonb'); assertTypeInstanceOf(Jsonb, DataTypes.JSONB); }); @@ -273,7 +308,7 @@ describe('field types', () => { name: 'password', }, ], - }) + }); db.table({ name: 'bars', tableName: 'formula_bars', @@ -281,9 +316,9 @@ describe('field types', () => { { type: 'string', name: 'col2', - } + }, ], - }) + }); await db.sync({ force: true }); }); afterEach(async () => { @@ -316,7 +351,7 @@ describe('field types', () => { await formula.updateAssociations({ bar: { col2: 'val2', - } + }, }); const f = await Formula.findOne({ where: { @@ -324,7 +359,7 @@ describe('field types', () => { }, include: { association: 'bar', - } + }, }); expect(f.reference1).toBe('val1'); expect(f.reference2).toBe('val2'); diff --git a/packages/database/src/__tests__/index.ts b/packages/database/src/__tests__/index.ts index b345032de..c1050f2ed 100644 --- a/packages/database/src/__tests__/index.ts +++ b/packages/database/src/__tests__/index.ts @@ -1,5 +1,7 @@ import Database from '../database'; import { Dialect } from 'sequelize'; +import path from 'path'; +import { uid } from '../utils'; function getTestKey() { const { id } = require.main; @@ -9,36 +11,32 @@ function getTestKey() { .replace(/src\/__tests__/g, '') .replace('.test.ts', '') .replace(/[^\w]/g, '_'); - return key + return key; } -const config = { +let config: any = { username: process.env.DB_USER, password: process.env.DB_PASSWORD, database: process.env.DB_DATABASE, host: process.env.DB_HOST, port: Number.parseInt(process.env.DB_PORT, 10), dialect: process.env.DB_DIALECT as Dialect, - define: { - hooks: { - beforeCreate(model, options) { - - }, - }, - }, - logging: process.env.DB_LOG_SQL === 'on' ? console.log : false + logging: process.env.DB_LOG_SQL === 'on' ? console.log : false, }; -console.log(config); +if (process.env.DB_DIALECT === 'sqlite') { + config = { + dialect: process.env.DB_DIALECT as Dialect, + storage: path.resolve(__dirname, `./.db/${uid()}.sqlite`), + logging: process.env.DB_LOG_SQL === 'on' ? console.log : false, + }; +} export function getDatabase() { return new Database({ ...config, sync: { force: true, - alter: { - drop: true, - } }, hooks: { beforeDefine(model, options) { @@ -46,8 +44,10 @@ export function getDatabase() { options.tableNamePrefix = `${getTestKey()}_`; // @ts-ignore options.hookModelName = options.tableName; - options.tableName = `${getTestKey()}_${options.tableName || options.name.plural}`; + options.tableName = `${getTestKey()}_${ + options.tableName || options.name.plural + }`; }, }, }); -}; +} diff --git a/packages/database/src/__tests__/model/model.test.ts b/packages/database/src/__tests__/model/model.test.ts index 4900febc5..0757a46ae 100644 --- a/packages/database/src/__tests__/model/model.test.ts +++ b/packages/database/src/__tests__/model/model.test.ts @@ -2,8 +2,6 @@ import Database from '../..'; import { getDatabase } from '..'; import Model, { ModelCtor } from '../../model'; - - let db: Database; beforeEach(async () => { @@ -39,8 +37,8 @@ describe('model', () => { scope: { title: 'title1', }, - } - ] + }, + ], }); db.table({ @@ -51,7 +49,7 @@ describe('model', () => { type: 'string', name: 'name', }, - ] + ], }); db.table({ @@ -87,7 +85,7 @@ describe('model', () => { name: 'current_user_comments', target: 'comments', }, - ] + ], }); db.table({ @@ -98,7 +96,7 @@ describe('model', () => { type: 'string', name: 'name', }, - ] + ], }); db.table({ @@ -131,8 +129,8 @@ describe('model', () => { { type: 'string', name: 'content', - } - ] + }, + ], }); db.table({ @@ -147,7 +145,7 @@ describe('model', () => { { type: 'hasMany', name: 'fields', - } + }, ], }); @@ -161,7 +159,7 @@ describe('model', () => { { type: 'string', name: 'name', - } + }, ], }); @@ -177,7 +175,7 @@ describe('model', () => { type: 'hasMany', name: 'columns', sourceKey: 'name', - } + }, ], }); @@ -192,7 +190,7 @@ describe('model', () => { { type: 'string', name: 'name', - } + }, ], }); @@ -208,7 +206,7 @@ describe('model', () => { const user = await User.create(); const post = await Post.create(); await post.updateAssociations({ - user: user.id + user: user.id, }); const authorizedPost = await Post.findByPk(post.id); @@ -219,7 +217,7 @@ describe('model', () => { const Post = db.getModel('posts'); const post = await Post.create(); await post.updateAssociations({ - user: {} + user: {}, }); const authorizedPost = await Post.findByPk(post.id); @@ -231,7 +229,7 @@ describe('model', () => { const user = await User.create(); const post = await Post.create(); await post.updateAssociations({ - user + user, }); const authorizedPost = await Post.findByPk(post.id); @@ -244,10 +242,10 @@ describe('model', () => { const user2 = await Post.create(); const post = await Post.create(); await post.updateAssociations({ - user: user1.id + user: user1.id, }); await post.updateAssociations({ - user: user2.id + user: user2.id, }); const authorizedPost = await Post.findByPk(post.id); @@ -261,26 +259,26 @@ describe('model', () => { const post = await Post.create(); const comments = await Comment.bulkCreate([{}, {}, {}, {}]); await post.updateAssociations({ - comments: comments.map(item => item.id) + comments: comments.map((item) => item.id), }); const postComments = await Comment.findAll({ where: { post_id: post.id }, - attributes: ['id'] + attributes: ['id'], }); - expect(postComments.map(item => item.id)).toEqual([1, 2, 3, 4]); + expect(postComments.map((item) => item.id)).toEqual([1, 2, 3, 4]); }); it('update with new object', async () => { const [Post, Comment] = db.getModels(['posts', 'comments']); const post = await Post.create(); await post.updateAssociations({ - comments: [{}, {}, {}, {}] + comments: [{}, {}, {}, {}], }); const postCommentIds = await Comment.findAll({ where: { post_id: post.id }, - attributes: ['id'] + attributes: ['id'], }); - expect(postCommentIds.map(item => item.id)).toEqual([1, 2, 3, 4]); + expect(postCommentIds.map((item) => item.id)).toEqual([1, 2, 3, 4]); }); it('update with new model', async () => { @@ -288,13 +286,13 @@ describe('model', () => { const post = await Post.create(); const comments = await Comment.bulkCreate([{}, {}, {}, {}]); await post.updateAssociations({ - comments + comments, }); const postCommentIds = await Comment.findAll({ where: { post_id: post.id }, - attributes: ['id'] + attributes: ['id'], }); - expect(postCommentIds.map(item => item.id)).toEqual([1, 2, 3, 4]); + expect(postCommentIds.map((item) => item.id)).toEqual([1, 2, 3, 4]); }); it('update with exist rows/primaryKeys', async () => { @@ -302,19 +300,19 @@ describe('model', () => { const post = await Post.create(); const comments = await Comment.bulkCreate([{}, {}, {}, {}]); await post.updateAssociations({ - comments + comments, }); await post.updateAssociations({ - comments + comments, }); await post.updateAssociations({ - comments: comments.map(item => item.id) + comments: comments.map((item) => item.id), }); const postCommentIds = await Comment.findAll({ where: { post_id: post.id }, - attributes: ['id'] + attributes: ['id'], }); - expect(postCommentIds.map(item => item.id)).toEqual([1, 2, 3, 4]); + expect(postCommentIds.map((item) => item.id)).toEqual([1, 2, 3, 4]); }); it('update with exist objects', async () => { @@ -322,23 +320,25 @@ describe('model', () => { const post = await Post.create(); const comments = await Comment.bulkCreate([{}, {}, {}, {}]); await post.updateAssociations({ - comments + comments, }); await post.updateAssociations({ - comments: comments.map(item => ({ + comments: comments.map((item) => ({ ...item.get(), - content: `content${item.id}` - })) + content: `content${item.id}`, + })), }); const postComments = await Comment.findAll({ where: { post_id: post.id }, - attributes: ['id', 'content'] + attributes: ['id', 'content'], }); - expect(postComments.map(({ id, content }) => ({ id, content }))).toEqual([ + expect( + postComments.map(({ id, content }) => ({ id, content })), + ).toEqual([ { id: 1, content: 'content1' }, { id: 2, content: 'content2' }, { id: 3, content: 'content3' }, - { id: 4, content: 'content4' } + { id: 4, content: 'content4' }, ]); }); @@ -348,39 +348,43 @@ describe('model', () => { const post2 = await Post.create(); const comments = await Comment.bulkCreate([{}, {}, {}, {}]); await post.updateAssociations({ - comments + comments, }); const postComments = await Comment.findAll({ - where: { post_id: post.id } + where: { post_id: post.id }, }); - expect(postComments.map(({ id, post_id }) => ({ id, post_id }))).toEqual([ + expect( + postComments.map(({ id, post_id }) => ({ id, post_id })), + ).toEqual([ { id: 1, post_id: post.id }, { id: 2, post_id: post.id }, { id: 3, post_id: post.id }, - { id: 4, post_id: post.id } + { id: 4, post_id: post.id }, ]); await post2.updateAssociations({ - comments: postComments.map(item => ({ + comments: postComments.map((item) => ({ ...item.get(), - content: `content${item.id}` - })) + content: `content${item.id}`, + })), }); const updatedComments = await Comment.findAll(); const post1CommentsCount = await Comment.count({ - where: { post_id: post.id } + where: { post_id: post.id }, }); expect(post1CommentsCount).toBe(0); const post2Comments = await Comment.findAll({ where: { post_id: post2.id }, - attributes: ['id', 'content'] + attributes: ['id', 'content'], }); - expect(post2Comments.map(({ id, content }) => ({ id, content }))).toEqual([ + expect( + post2Comments.map(({ id, content }) => ({ id, content })), + ).toEqual([ { id: 1, content: 'content1' }, { id: 2, content: 'content2' }, { id: 3, content: 'content3' }, - { id: 4, content: 'content4' } + { id: 4, content: 'content4' }, ]); }); @@ -389,17 +393,17 @@ describe('model', () => { const post = await Post.create(); const comments = await Comment.bulkCreate([{}, {}, {}, {}]); await post.updateAssociations({ - comments + comments, }); await post.updateAssociations({ comments: comments .filter(({ id }) => Boolean(id % 2)) - .concat(...[await Comment.create()]) + .concat(...[await Comment.create()]), }); const postComments = await Comment.findAll({ where: { post_id: post.id }, - attributes: ['id'] + attributes: ['id'], }); expect(postComments.map(({ id }) => id)).toEqual([1, 3, 5]); }); @@ -407,37 +411,47 @@ describe('model', () => { describe('belongsToMany', () => { it('update with primary key', async () => { - const [Post, Tag, PostTag] = db.getModels(['posts', 'tags', 'posts_tags']); + const [Post, Tag, PostTag] = db.getModels([ + 'posts', + 'tags', + 'posts_tags', + ]); const post = await Post.create(); const tags = await Tag.bulkCreate([{}, {}, {}, {}]); await post.updateAssociations({ - tags: tags.map(item => item.id) + tags: tags.map((item) => item.id), }); const tagged = await PostTag.findAll({ where: { post_id: post.id }, - attributes: ['tag_id'] + attributes: ['tag_id'], }); - expect(tagged.map(item => item.tag_id)).toEqual([1, 2, 3, 4]); + expect(tagged.map((item) => item.tag_id)).toEqual([1, 2, 3, 4]); }); it('update with exist rows/primaryKeys', async () => { - const [Post, Tag, PostTag] = db.getModels(['posts', 'tags', 'posts_tags']); + const [Post, Tag, PostTag] = db.getModels([ + 'posts', + 'tags', + 'posts_tags', + ]); const post = await Post.create(); const tags = await Tag.bulkCreate([{}, {}, {}, {}]); await post.updateAssociations({ - tags: tags.map(item => item.id) + tags: tags.map((item) => item.id), }); await post.updateAssociations({ - tags: tags.map(item => item.id) + tags: tags.map((item) => item.id), }); await post.updateAssociations({ - tags + tags, }); const tagged = await PostTag.findAll({ where: { post_id: post.id }, - attributes: ['tag_id', 'post_id'] + attributes: ['tag_id', 'post_id'], }); - expect(tagged.map(({ post_id, tag_id }) => ({ post_id, tag_id }))).toEqual([ + expect( + tagged.map(({ post_id, tag_id }) => ({ post_id, tag_id })), + ).toEqual([ { tag_id: 1, post_id: 1 }, { tag_id: 2, post_id: 1 }, { tag_id: 3, post_id: 1 }, @@ -446,35 +460,47 @@ describe('model', () => { }); it('update with exist rows/primaryKeys and new objects', async () => { - const [Post, Tag, PostTag] = db.getModels(['posts', 'tags', 'posts_tags']); + const [Post, Tag, PostTag] = db.getModels([ + 'posts', + 'tags', + 'posts_tags', + ]); const post = await Post.create(); const tags = await Tag.bulkCreate([{}, {}, {}, {}]); await post.updateAssociations({ - tags: tags.map(item => item.id) + tags: tags.map((item) => item.id), }); await post.updateAssociations({ - tags: tags.filter(({ id }) => Boolean(id % 2)).concat(await Tag.create({})) + tags: tags + .filter(({ id }) => Boolean(id % 2)) + .concat(await Tag.create({})), }); const tagged = await PostTag.findAll({ where: { post_id: post.id }, - attributes: ['tag_id'] + attributes: ['tag_id'], }); expect(tagged.map(({ tag_id }) => tag_id)).toEqual([1, 3, 5]); }); it('update other with exist rows/primaryKeys', async () => { - const [Post, Tag, PostTag] = db.getModels(['posts', 'tags', 'posts_tags']); + const [Post, Tag, PostTag] = db.getModels([ + 'posts', + 'tags', + 'posts_tags', + ]); const post = await Post.create(); const post2 = await Post.create(); const tags = await Tag.bulkCreate([{}, {}, {}, {}]); await post.updateAssociations({ - tags: tags.map(item => item.id) + tags: tags.map((item) => item.id), }); await post2.updateAssociations({ - tags + tags, }); const tagged = await PostTag.findAll(); - expect(tagged.map(({ post_id, tag_id }) => ({ post_id, tag_id }))).toEqual([ + expect( + tagged.map(({ post_id, tag_id }) => ({ post_id, tag_id })), + ).toEqual([ { tag_id: 1, post_id: 1 }, { tag_id: 2, post_id: 1 }, { tag_id: 3, post_id: 1 }, @@ -492,17 +518,20 @@ describe('model', () => { const post = await Post.create(); const tag = await Tag.create(); await post.updateAssociations({ - tags: [{ - name: 'xxx', - posts_tags: { - name: 'name134', - } - }, { - id: tag.id, - posts_tags: { - name: 'name234', - } - }], + tags: [ + { + name: 'xxx', + posts_tags: { + name: 'name134', + }, + }, + { + id: tag.id, + posts_tags: { + name: 'name234', + }, + }, + ], }); const PostTag = db.getModel('posts_tags'); const [t1, t2] = await PostTag.findAll({ @@ -518,7 +547,11 @@ describe('model', () => { describe('scope', () => { it('scope', async () => { - const [User, Post, Comment] = db.getModels(['users', 'posts', 'comments']); + const [User, Post, Comment] = db.getModels([ + 'users', + 'posts', + 'comments', + ]); const user1 = await User.create(); const user2 = await User.create(); const user3 = await User.create(); @@ -565,8 +598,8 @@ describe('model', () => { return { where: { name: name, - } - } + }, + }; }); const [User, Post] = db.getModels(['users', 'posts']); @@ -589,29 +622,22 @@ describe('model', () => { posts: post, }); await post.updateAssociations({ - tags: [ - { name: 'tag1' }, - { name: 'tag2' }, - { name: 'tag3' }, - ], + tags: [{ name: 'tag1' }, { name: 'tag2' }, { name: 'tag3' }], }); // where & include const options = Post.parseApiJson({ filter: { title: 'title112233', - user: { // belongsTo + user: { + // belongsTo name: 'name112233', }, - tags: { // belongsToMany + tags: { + // belongsToMany scopeName: 'tag3', }, }, - fields: [ - 'title', - 'tags_count', - 'tags.name', - 'user.name' - ], + fields: ['title', 'tags_count', 'tags.name', 'user.name'], sort: '-tags_count,tags.name,user.posts_count', context: { scopeName: 'tag3', @@ -631,7 +657,7 @@ describe('model', () => { // console.log(JSON.stringify(rows[0].toJSON(), null, 2)); - rows.forEach(row => { + rows.forEach((row) => { // expect(row.toJSON()).toEqual({ title: 'title112233', 'tags_count': 3, user: { name: 'name112233', posts_count: 1 } }); expect(row.get('title')).toBe('title112233'); expect(row.user.get('name')).toBe('name112233'); @@ -694,7 +720,7 @@ describe('model', () => { include: [ User.withCountAttribute({ association: 'posts', - alias: 'posts2_count' + alias: 'posts2_count', }), ], }, @@ -736,7 +762,9 @@ describe('model', () => { include: [ { association: 'user', - attributes: ['id', 'name', + attributes: [ + 'id', + 'name', User.withCountAttribute({ sourceAlias: 'user', association: 'posts', @@ -752,7 +780,7 @@ describe('model', () => { expect(post.get('tags_count')).toBe(5); expect(post.get('tags_name1_count')).toBe(1); expect(post.user.get('posts_count')).toBe(1); - post.tags.forEach(tag => { + post.tags.forEach((tag) => { expect(tag.get('posts_count')).toBe(1); }); }); @@ -904,9 +932,7 @@ describe('model', () => { name: 'examples', }); await table.updateAssociations({ - fields: [ - { name: 'name' }, - ] + fields: [{ name: 'name' }], }); const field = await Field.findOne({ where: { @@ -924,9 +950,7 @@ describe('model', () => { name: 'examples', }); await row.updateAssociations({ - columns: [ - { name: 'name' }, - ] + columns: [{ name: 'name' }], }); const column = await Column.findOne({ where: { @@ -950,7 +974,7 @@ describe('model', () => { id: field.id, name: 'nam1234', }, - ] + ], }); const f = await Field.findOne({ @@ -973,7 +997,6 @@ describe('model', () => { }); describe('blongsTo', () => { - it('shoud associated id when association is integer', async () => { const [User, Post] = db.getModels(['users', 'posts']); const user = await User.create(); @@ -1143,7 +1166,7 @@ describe('model', () => { const post = await Post.create(); await post.updateAssociations({ tags: { - name: 'tag1' + name: 'tag1', }, }); const count = await post.countTags(); @@ -1156,10 +1179,10 @@ describe('model', () => { await post.updateAssociations({ tags: [ { - name: 'tag2' + name: 'tag2', }, { - name: 'tag3' + name: 'tag3', }, ], }); @@ -1239,9 +1262,11 @@ describe('belongsToMany', () => { }); it('update with new object', async () => { await post.updateAssociations({ - tags: [{ - name: 'tag3', - }], + tags: [ + { + name: 'tag3', + }, + ], }); expect(await post.countTags()).toBe(1); }); diff --git a/packages/database/src/__tests__/op.test.ts b/packages/database/src/__tests__/op.test.ts index 0e27a4041..24abc9916 100644 --- a/packages/database/src/__tests__/op.test.ts +++ b/packages/database/src/__tests__/op.test.ts @@ -29,6 +29,7 @@ afterEach(async () => { describe('op', () => { it('test', async () => { + if ((process.env.DB_DIALECT = 'sqlite')) return; const Test = db.getModel('tests'); await Test.bulkCreate([ { @@ -42,7 +43,7 @@ describe('op', () => { }, { arr: ['dd'], - } + }, ]); const options = Test.parseApiJson({ filter: { diff --git a/packages/database/src/database.ts b/packages/database/src/database.ts index d9ad5782a..af4525669 100644 --- a/packages/database/src/database.ts +++ b/packages/database/src/database.ts @@ -168,7 +168,7 @@ type HookType = const hookType = this._getHookType(event); if (hookType) { const state = this.hookTypes.get(hookType); - console.log('sequelize.addHook', event, hookType) + this.sequelize.addHook(hookType, async (...args: any[]) => { let modelName: string; switch (state) { diff --git a/packages/database/src/model.ts b/packages/database/src/model.ts index 0d734adcf..4bbf08244 100644 --- a/packages/database/src/model.ts +++ b/packages/database/src/model.ts @@ -1,5 +1,10 @@ import { - Model as SequelizeModel, Op, Sequelize, ProjectionAlias, Utils, SaveOptions + Model as SequelizeModel, + Op, + Sequelize, + ProjectionAlias, + Utils, + SaveOptions, } from 'sequelize'; import Database from './database'; import { @@ -12,48 +17,50 @@ import { import { toInclude } from './utils'; export interface ApiJsonOptions { - /** * 字段 - * + * * 数组式: * ['col', 'association.col1', 'association_count'], - * + * * 白名单: * { * only: ['col1'], * appends: ['association_count'], * } - * + * * 黑名单: * { * except: ['col1'], * appends: ['association_count'], * } */ - fields?: string[] | { - only?: string[]; - appends?: string[]; - } | { - except?: string[]; - appends?: string[]; - }; + fields?: + | string[] + | { + only?: string[]; + appends?: string[]; + } + | { + except?: string[]; + appends?: string[]; + }; /** * 过滤 - * + * * 常规用法: * { * col1: { * $eq: 'val1' * }, * } - * + * * scope 的用法(如果 scope 与 col 同名,只会执行 scope): * { * scope1: value * } - * + * * json 数据 & 关系数据,可以用点号: * { * 'association.col1': { @@ -67,7 +74,7 @@ export interface ApiJsonOptions { * $eq: 'val1' * }, * } - * + * * json 数据 & 关系数据的查询也可以不用点号: * { * association: { @@ -81,9 +88,9 @@ export interface ApiJsonOptions { /** * 排序 - * + * * TODO - * + * * ['col1', '-col2', 'association.col1', '-association.col2'] */ sort?: any; @@ -100,7 +107,6 @@ export interface ApiJsonOptions { } export interface WithCountAttributeOptions { - /** * 关系名 */ @@ -108,9 +114,9 @@ export interface WithCountAttributeOptions { /** * SourceModel 别名 - * + * * 在 include 里使用时,需要指定,一般与 include 的 association 同名 - * + * * include: { * association: 'user', // Post.belongsTo(User) * attributes: [ @@ -143,12 +149,11 @@ export interface UpdateAssociationOptions extends SaveOptions { /** * Model 相关 - * + * * TODO: 自定义 model 时的提示问题 */ // @ts-ignore export abstract class Model extends SequelizeModel { - /** * 防止 ts 报错提示 */ @@ -156,7 +161,7 @@ export abstract class Model extends SequelizeModel { /** * 当前 Model 的 database - * + * * 与 Model.sequelize 对应,database 也用了 public static readonly */ public static database: Database; @@ -171,17 +176,25 @@ export abstract class Model extends SequelizeModel { /** * sub query 关联数据的数量 - * + * * TODO: 关联字段暂不支持主键以外的字段 - * - * @param options + * + * @param options */ - static withCountAttribute(options?: string | WithCountAttributeOptions): (string | ProjectionAlias) { + static withCountAttribute( + options?: string | WithCountAttributeOptions, + ): string | ProjectionAlias { if (typeof options === 'string') { options = { association: options }; } - const { sourceAlias, association, where = {}, alias, ...restOptions } = options; + const { + sourceAlias, + association, + where = {}, + alias, + ...restOptions + } = options; const associator = this.associations[association]; const table = this.database.getTable(this.name); const field = table.getField(association); @@ -193,17 +206,20 @@ export abstract class Model extends SequelizeModel { }; } else if (associator.associationType === 'BelongsToMany') { where[targetKey] = { - // @ts-ignore - [Op.in]: Sequelize.literal(`(${associator.through.model.selectQuery({ - attributes: [otherKey], - where: { - [foreignKey]: { - [Op.eq]: Sequelize.col(`${sourceAlias || this.name}.${sourceKey}`), + [Op.in]: Sequelize.literal( + `(${(associator as any).through.model.selectQuery({ + attributes: [otherKey], + where: { + [foreignKey]: { + [Op.eq]: Sequelize.col( + `${sourceAlias || this.name}.${sourceKey}`, + ), + }, + // @ts-ignore + ...(associator.through.scope || {}), }, - // @ts-ignore - ...(associator.through.scope || {}), - }, - })})`), + })})`, + ), }; } @@ -221,41 +237,47 @@ export abstract class Model extends SequelizeModel { attributes: [[Sequelize.literal(countLiteral), 'count']], where: { // @ts-ignore - ...where, ...(associator.scope || {}), + ...where, + ...((associator as any).scope || {}), }, - })})` + })})`, ), - alias || Utils.underscoredIf(`${association}Count`, this.options.underscored), + alias || + Utils.underscoredIf(`${association}Count`, this.options.underscored), ].filter(Boolean); - return attribute as unknown as ProjectionAlias; + return (attribute as unknown) as ProjectionAlias; } /** * 当前 Model 的 SQL - * - * @param options + * + * @param options */ static selectQuery(options = {}): string { // @ts-ignore - return this.queryGenerator.selectQuery( - this.getTableName(), - options, - this, - ).replace(/;$/, ''); + return this.queryGenerator + .selectQuery(this.getTableName(), options, this) + .replace(/;$/, ''); } static parseApiJson(options: ApiJsonOptions) { const { fields, filter, sort, context, page, perPage } = options; - const data = toInclude({ fields, filter, sort }, { - model: this, - associations: this.associations, - dialect: this.sequelize.getDialect(), - ctx: context, - database: this.database, - }); + const data = toInclude( + { fields, filter, sort }, + { + model: this, + associations: this.associations, + dialect: this.sequelize.getDialect(), + ctx: context, + database: this.database, + }, + ); if (page || perPage) { - data.limit = perPage === -1 ? MAX_LIMIT : Math.min(perPage || DEFAULT_LIMIT, MAX_LIMIT); + data.limit = + perPage === -1 + ? MAX_LIMIT + : Math.min(perPage || DEFAULT_LIMIT, MAX_LIMIT); data.offset = data.limit * (page > 0 ? page - 1 : DEFAULT_OFFSET); } if (data.attributes && data.attributes.length === 0) { @@ -269,11 +291,12 @@ export abstract class Model extends SequelizeModel { const Model = table.getModel(); const associations = table.getAssociations(); const where = {}; - scope.forEach(col => { + scope.forEach((col) => { const association = associations.get(col); - const dataKey = association && association instanceof BELONGSTO - ? association.options.foreignKey - : col; + const dataKey = + association && association instanceof BELONGSTO + ? association.options.foreignKey + : col; if (!Model.rawAttributes[dataKey]) { return; } @@ -285,7 +308,11 @@ export abstract class Model extends SequelizeModel { return where; } - async updateSingleAssociation(key: string, data: any, options: UpdateAssociationOptions = {}) { + async updateSingleAssociation( + key: string, + data: any, + options: UpdateAssociationOptions = {}, + ) { const { fields, transaction = await this.sequelize.transaction(), @@ -302,20 +329,25 @@ export abstract class Model extends SequelizeModel { return; } - if (typeof data === 'number' || typeof data === 'string' || data instanceof SequelizeModel) { + if ( + typeof data === 'number' || + typeof data === 'string' || + data instanceof SequelizeModel + ) { await this[accessors.set](data, opts); } else if (typeof data === 'object') { const Target = association.getTargetModel(); - const targetAttribute = association instanceof BELONGSTO - ? association.options.targetKey - : association.options.sourceKey; + const targetAttribute = + association instanceof BELONGSTO + ? association.options.targetKey + : association.options.sourceKey; if (data[targetAttribute]) { if (Object.keys(data).length > 0) { const target = await Target.findOne({ where: { [targetAttribute]: data[targetAttribute], }, - transaction + transaction, }); if (target) { await this[accessors.set](data[targetAttribute], opts); @@ -337,7 +369,11 @@ export abstract class Model extends SequelizeModel { } } - async updateMultipleAssociation(associationName: string, data: any, options: UpdateAssociationOptions = {}) { + async updateMultipleAssociation( + associationName: string, + data: any, + options: UpdateAssociationOptions = {}, + ) { const items = Array.isArray(data) ? data : data == null ? [] : [data]; const { @@ -371,7 +407,7 @@ export abstract class Model extends SequelizeModel { const toUpsertObjects = []; // 遍历所有值成员准备数据 - items.forEach(item => { + items.forEach((item) => { if (item instanceof SequelizeModel) { if (targetKeyIsPk) { toSetPks.add(item.getDataValue(targetPk)); @@ -381,14 +417,18 @@ export abstract class Model extends SequelizeModel { return; } if (typeof item === 'number' || typeof item === 'string') { - let targetKeyType = getDataTypeKey(Target.rawAttributes[targetKey].type).toLocaleLowerCase(); + let targetKeyType = getDataTypeKey( + Target.rawAttributes[targetKey].type, + ).toLocaleLowerCase(); if (targetKeyType === 'integer') { targetKeyType = 'number'; } // 如果传值类型与之前在 Model 上定义的 targetKey 不同,则报错。 // 不应兼容定义的 targetKey 不是 primaryKey 却传了 primaryKey 的值的情况。 if (typeof item !== targetKeyType) { - throw new Error(`target key type [${typeof item}] does not match to [${targetKeyType}]`); + throw new Error( + `target key type [${typeof item}] does not match to [${targetKeyType}]`, + ); } if (targetKeyIsPk) { toSetPks.add(item); @@ -404,31 +444,35 @@ export abstract class Model extends SequelizeModel { /* 仅传关联键处理开始 */ // 查找已存在的数据 - const byPkExistItems = toSetPks.size ? await Target.findAll({ - ...opts, - // @ts-ignore - where: { - [targetPk]: { - [Op.in]: Array.from(toSetPks) - } - }, - attributes: [targetPk] - }) : []; - byPkExistItems.forEach(item => { + const byPkExistItems = toSetPks.size + ? await Target.findAll({ + ...opts, + // @ts-ignore + where: { + [targetPk]: { + [Op.in]: Array.from(toSetPks), + }, + }, + attributes: [targetPk], + }) + : []; + byPkExistItems.forEach((item) => { toSetItems.add(item); }); - const byUkExistItems = toSetUks.size ? await Target.findAll({ - ...opts, - // @ts-ignore - where: { - [targetKey]: { - [Op.in]: Array.from(toSetUks) - } - }, - attributes: [targetPk, targetKey] - }) : []; - byUkExistItems.forEach(item => { + const byUkExistItems = toSetUks.size + ? await Target.findAll({ + ...opts, + // @ts-ignore + where: { + [targetKey]: { + [Op.in]: Array.from(toSetUks), + }, + }, + attributes: [targetPk, targetKey], + }) + : []; + byUkExistItems.forEach((item) => { toSetItems.add(item); }); /* 仅传关联键处理结束 */ @@ -458,7 +502,7 @@ export abstract class Model extends SequelizeModel { if (association instanceof BELONGSTOMANY) { belongsToManyList.push({ item, - target + target, }); } @@ -474,7 +518,6 @@ export abstract class Model extends SequelizeModel { const ThroughModel = (association as BELONGSTOMANY).getThroughModel(); const throughName = (association as BELONGSTOMANY).getThroughName(); - for (const { item, target } of belongsToManyList) { const throughValues = item[throughName]; if (throughValues && typeof throughValues === 'object') { @@ -484,7 +527,7 @@ export abstract class Model extends SequelizeModel { [foreignKey]: this.get(sourceKey), [otherKey]: target.get(targetKey), }, - transaction + transaction, }); await through.update(throughValues, opts); // TODO:有 BUG,未知 @@ -498,7 +541,11 @@ export abstract class Model extends SequelizeModel { } } - async updateAssociation(key: string, data: any, options: UpdateAssociationOptions = {}) { + async updateAssociation( + key: string, + data: any, + options: UpdateAssociationOptions = {}, + ) { const table = this.database.getTable(this.constructor.name); const association = table.getAssociations().get(key); switch (true) { @@ -513,7 +560,7 @@ export abstract class Model extends SequelizeModel { /** * 关联数据的更新 - * + * * @param data */ async updateAssociations(data: any, options: UpdateAssociationOptions = {}) { @@ -526,7 +573,7 @@ export abstract class Model extends SequelizeModel { } await this.updateAssociation(key, data[key], { ...options, - transaction + transaction, }); } @@ -544,6 +591,8 @@ export abstract class Model extends SequelizeModel { /** * ModelCtor 需要为当前 Model 的 */ -export type ModelCtor = typeof Model & { new(): M } & { [key: string]: any }; +export type ModelCtor = typeof Model & { new (): M } & { + [key: string]: any; +}; export default Model; diff --git a/packages/database/src/op.ts b/packages/database/src/op.ts index 5d77bfaab..dd2880349 100644 --- a/packages/database/src/op.ts +++ b/packages/database/src/op.ts @@ -53,36 +53,58 @@ op.set('$isFalsy', () => ({ // 字符串 // 包含:指对应字段的值包含某个子串 -op.set('$includes', (value: string) => ({ [Op.iLike]: `%${value}%` })); +op.set('$includes', (value: string, { dialect }) => ({ + [dialect === 'postgres' ? Op.iLike : Op.like]: `%${value}%`, +})); // 不包含:指对应字段的值不包含某个子串(慎用:性能问题) -op.set('$notIncludes', (value: string) => ({ [Op.notILike]: `%${value}%` })); +op.set('$notIncludes', (value: string, { dialect }) => ({ + [dialect === 'postgres' ? Op.notILike : Op.notLike]: `%${value}%`, +})); // 以之起始 -op.set('$startsWith', (value: string) => ({ [Op.iLike]: `${value}%` })); +op.set('$startsWith', (value: string, { dialect }) => ({ + [dialect === 'postgres' ? Op.iLike : Op.like]: `${value}%`, +})); // 不以之起始 -op.set('$notStartsWith', (value: string) => ({ [Op.notILike]: `${value}%` })); +op.set('$notStartsWith', (value: string, { dialect }) => ({ + [dialect === 'postgres' ? Op.notILike : Op.notLike]: `${value}%`, +})); // 以之结束 -op.set('$endsWith', (value: string) => ({ [Op.iLike]: `%${value}` })); +op.set('$endsWith', (value: string, { dialect }) => ({ + [dialect === 'postgres' ? Op.iLike : Op.like]: `%${value}`, +})); // 不以之结束 -op.set('$notEndsWith', (value: string) => ({ [Op.notILike]: `%${value}` })); +op.set('$notEndsWith', (value: string, { dialect }) => ({ + [dialect === 'postgres' ? Op.notILike : Op.notLike]: `%${value}`, +})); // 仅日期 // 在某日 -op.set('$dateOn', (value: string) => ({ [Op.and]: [{ [Op.gte]: stringToDate(value) }, { [Op.lt]: getNextDay(value) }] })); +op.set('$dateOn', (value: string) => ({ + [Op.and]: [{ [Op.gte]: stringToDate(value) }, { [Op.lt]: getNextDay(value) }], +})); // 不在某日 -op.set('$dateNotOn', (value: string) => ({ [Op.or]: [{ [Op.lt]: stringToDate(value) }, { [Op.gte]: getNextDay(value) }] })); +op.set('$dateNotOn', (value: string) => ({ + [Op.or]: [{ [Op.lt]: stringToDate(value) }, { [Op.gte]: getNextDay(value) }], +})); // 某日前 op.set('$dateBefore', (value: string) => ({ [Op.lt]: stringToDate(value) })); // 某日后 op.set('$dateAfter', (value: string) => ({ [Op.gte]: getNextDay(value) })); // 不早于(含当天) -op.set('$dateNotBefore', (value: string) => ({ [Op.gte]: stringToDate(value) })); +op.set('$dateNotBefore', (value: string) => ({ + [Op.gte]: stringToDate(value), +})); // 不晚于(含当天) op.set('$dateNotAfter', (value: string) => ({ [Op.lt]: getNextDay(value) })); // 在期间 -op.set('$dateBetween', ([from, to]: string[]) => ({ [Op.and]: [{ [Op.gte]: stringToDate(from) }, { [Op.lt]: getNextDay(to) }] })); +op.set('$dateBetween', ([from, to]: string[]) => ({ + [Op.and]: [{ [Op.gte]: stringToDate(from) }, { [Op.lt]: getNextDay(to) }], +})); // 不在期间 -op.set('$dateNotBetween', ([from, to]: string[]) => ({ [Op.or]: [{ [Op.lt]: stringToDate(from) }, { [Op.gte]: getNextDay(to) }] })); +op.set('$dateNotBetween', ([from, to]: string[]) => ({ + [Op.or]: [{ [Op.lt]: stringToDate(from) }, { [Op.gte]: getNextDay(to) }], +})); // 多选(JSON)类型 @@ -96,9 +118,13 @@ op.set('$anyOf', (values: any[], options) => { return Sequelize.literal(''); } const { field, fieldPath } = options; - const column = fieldPath.split('.').map(name => `"${name}"`).join('.'); - const sql = values.map(value => `(${column})::jsonb @> '${JSON.stringify(value)}'`).join(' OR '); - console.log(sql); + const column = fieldPath + .split('.') + .map((name) => `"${name}"`) + .join('.'); + const sql = values + .map((value) => `(${column})::jsonb @> '${JSON.stringify(value)}'`) + .join(' OR '); return Sequelize.literal(sql); }); // 包含组中所有值 @@ -113,9 +139,13 @@ op.set('$noneOf', (values: any[], options) => { return Sequelize.literal(''); } const { field, fieldPath } = options; - const column = fieldPath.split('.').map(name => `"${name}"`).join('.'); - const sql = values.map(value => `(${column})::jsonb @> '${JSON.stringify(value)}'`).join(' OR '); - console.log(sql); + const column = fieldPath + .split('.') + .map((name) => `"${name}"`) + .join('.'); + const sql = values + .map((value) => `(${column})::jsonb @> '${JSON.stringify(value)}'`) + .join(' OR '); return Sequelize.literal(`not (${sql})`); }); // 与组中值匹配 @@ -125,8 +155,13 @@ op.set('$match', (values: any[], options) => { return Sequelize.literal(''); } const { field, fieldPath } = options; - const column = fieldPath.split('.').map(name => `"${name}"`).join('.'); - const sql = `(${column})::jsonb @> '${JSON.stringify(array)}' AND (${column})::jsonb <@ '${JSON.stringify(array)}'` + const column = fieldPath + .split('.') + .map((name) => `"${name}"`) + .join('.'); + const sql = `(${column})::jsonb @> '${JSON.stringify( + array, + )}' AND (${column})::jsonb <@ '${JSON.stringify(array)}'`; return Sequelize.literal(sql); }); op.set('$notMatch', (values: any[], options) => { @@ -135,8 +170,13 @@ op.set('$notMatch', (values: any[], options) => { return Sequelize.literal(''); } const { field, fieldPath } = options; - const column = fieldPath.split('.').map(name => `"${name}"`).join('.'); - const sql = `(${column})::jsonb @> '${JSON.stringify(array)}' AND (${column})::jsonb <@ '${JSON.stringify(array)}'` + const column = fieldPath + .split('.') + .map((name) => `"${name}"`) + .join('.'); + const sql = `(${column})::jsonb @> '${JSON.stringify( + array, + )}' AND (${column})::jsonb <@ '${JSON.stringify(array)}'`; return Sequelize.literal(`not (${sql})`); // return Sequelize.literal(`(not (${sql})) AND ${column} IS NULL`); }); @@ -153,4 +193,4 @@ export default class Operator { public static register(key: string, fn: Function) { op.set(key, fn); } -}; +} diff --git a/packages/database/src/table.ts b/packages/database/src/table.ts index 47d54c59c..823ea1179 100644 --- a/packages/database/src/table.ts +++ b/packages/database/src/table.ts @@ -12,16 +12,14 @@ import { Relation, BELONGSTO, BELONGSTOMANY, - SORT + SORT, } from './fields'; import Database from './database'; import { Model, ModelCtor } from './model'; import _ from 'lodash'; import merge from 'deepmerge'; -export interface MergeOptions extends merge.Options { - -} +export interface MergeOptions extends merge.Options {} const registeredModels = new Map(); @@ -45,15 +43,15 @@ export function getRegisteredModel(key) { return key; } -export interface TableOptions extends Omit, 'name' | 'modelName'> { - +export interface TableOptions + extends Omit, 'name' | 'modelName'> { /** * 唯一标识,与 ModelOptions 的 name 有区别 - * + * * 注意:name 可用于初始化 tableName、modelName,但是 tableName 和 modelName 可能有所不同 * name 主要用于获取指定的 tables 和 models * 如果没有特殊指定 tableName 时,name、tableName、modelName 都是一个值 - * + * * TODO: name, tableName, modelName,freezeTableName,underscored,单复数等等情况还比较混乱 */ name: string; @@ -77,7 +75,7 @@ export interface TableOptions extends Omit, 'name' | 'modelN /** * 上下文,Tabel 配置需要的其他变量 */ -export interface TabelContext { +export interface TableContext { database: Database; } @@ -92,11 +90,10 @@ export type Reinitialize = boolean | 'modelOnly'; /** * 表配置 - * + * * 用于处理相关 Model 的配置 */ export class Table { - protected database: Database; protected options: TableOptions; @@ -134,19 +131,16 @@ export class Table { public relationTables = new Set(); get sortable(): boolean { - return Array.from(this.fields.values()).some(field => field instanceof SORT); + return Array.from(this.fields.values()).some( + (field) => field instanceof SORT, + ); } - constructor(options: TableOptions, context: TabelContext) { + constructor(options: TableOptions, context: TableContext) { const { database } = context; database.runHooks('beforeTableInit', options); database.emit('beforeTableInit', options); - const { - model, - fields = [], - indexes = [], - sortable, - } = options; + const { model, fields = [], indexes = [], sortable } = options; this.options = options; this.database = database; // 初始化的时候获取 @@ -183,7 +177,9 @@ export class Table { public modelInit(reinitialize: Reinitialize = false) { if (reinitialize || !this.Model) { let DefaultModel = this.defaultModel; - this.Model = DefaultModel ? (class extends DefaultModel {}) : (class extends Model { }); + this.Model = DefaultModel + ? class extends DefaultModel {} + : class extends Model {}; this.Model.database = this.database; // 关系的建立是在 model.init 之后,在配置中表字段(Column)和关系(Relation)都在 fields, // 所以需要单独提炼出 associations 字段,并在 Model.init 之后执行 Model.associate @@ -194,7 +190,10 @@ export class Table { if (this.database.isDefined(target)) { const TargetModel = this.database.getModel(target); // 如果关系表在之后才定义,未设置 targetKey 时,targetKey 默认值需要在 target model 初始化之后才能取到 - if (association instanceof BELONGSTO || association instanceof BELONGSTOMANY) { + if ( + association instanceof BELONGSTO || + association instanceof BELONGSTOMANY + ) { association.updateOptionsAfterTargetModelBeDefined(); } this.Model[type](TargetModel, association.getAssociationOptions()); @@ -202,7 +201,7 @@ export class Table { this.associating.delete(key); } } - } + }; } this.Model.init(this.getModelAttributes(), this.getModelOptions()); @@ -229,7 +228,7 @@ export class Table { } /** - * + * * @param key 获取数据表配置,也可以指定 key */ public getOptions(key?: any): TableOptions { @@ -245,12 +244,9 @@ export class Table { } public getModelOptions(): InitOptions { - const { - name, - underscored = true, - ...restOptions - } = this.options; - const hooks = _.get(this.getModel(), 'options.hooks') || this.options.hooks || {}; + const { name, underscored = true, ...restOptions } = this.options; + const hooks = + _.get(this.getModel(), 'options.hooks') || this.options.hooks || {}; return { underscored, modelName: name, @@ -294,9 +290,11 @@ export class Table { this.fields.clear(); this.associating.clear(); this.associations.clear(); - for (const key in fields) { - this.addField(fields[key], false); + + for (const filed of fields) { + this.addField(filed, false); } + this.modelInit(true); } @@ -310,9 +308,9 @@ export class Table { /** * 添加字段 - * - * @param options - * @param reinitialize + * + * @param options + * @param reinitialize */ public addField(options: FieldOptions, reinitialize: Reinitialize = true) { this.database.runHooks('beforeAddField', options, this); @@ -327,7 +325,9 @@ export class Table { if (!this.options.fields) { this.options.fields = []; } - const existIndex = this.options.fields.findIndex(field => field.name === name); + const existIndex = this.options.fields.findIndex( + (field) => field.name === name, + ); if (existIndex !== -1) { this.options.fields.splice(existIndex, 1, options); } else { @@ -344,10 +344,13 @@ export class Table { if (index === true) { this.addIndex(name, false); } else if (typeof index === 'object') { - this.addIndex({ - fields: [name], - ...index, - }, false); + this.addIndex( + { + fields: [name], + ...index, + }, + false, + ); } this.modelAttributes[name] = field.getAttributeOptions(); } @@ -359,14 +362,20 @@ export class Table { /** * 添加索引 - * - * @param indexOptions - * @param reinitialize + * + * @param indexOptions + * @param reinitialize */ - public addIndex(indexOptions: string | ModelIndexesOptions, reinitialize: Reinitialize = true) { - const options = typeof indexOptions === 'string' ? { - fields: [indexOptions], - } : indexOptions; + public addIndex( + indexOptions: string | ModelIndexesOptions, + reinitialize: Reinitialize = true, + ) { + const options = + typeof indexOptions === 'string' + ? { + fields: [indexOptions], + } + : indexOptions; // @ts-ignore const index = Utils.nameIndex(options, this.options.name); console.log(this.options, { index, options }); @@ -380,11 +389,14 @@ export class Table { /** * 批量添加索引 - * - * @param indexes - * @param reinitialize + * + * @param indexes + * @param reinitialize */ - public addIndexes(indexes: Array, reinitialize: Reinitialize = true) { + public addIndexes( + indexes: Array, + reinitialize: Reinitialize = true, + ) { for (const index in indexes) { this.addIndex(indexes[index], false); } @@ -393,20 +405,16 @@ export class Table { /** * 扩展(实验性 API) - * - * @param options + * + * @param options */ public extend(options: TableOptions, mergeOptions: MergeOptions = {}) { - const { - fields = [], - indexes = [], - model, - ...restOptions - } = options; + const { fields = [], indexes = [], model, ...restOptions } = options; if (model) { this.defaultModel = getRegisteredModel(model); } - const { arrayMerge = (target: any[], source: any[]) => source } = mergeOptions; + const { arrayMerge = (target: any[], source: any[]) => source } = + mergeOptions; this.options = merge(this.options, restOptions, { arrayMerge, ...mergeOptions, @@ -422,8 +430,8 @@ export class Table { /** * 相关表字段更新 - * - * @param options + * + * @param options */ public async sync(options: SyncOptions = {}) { const tables = []; diff --git a/packages/database/src/utils.ts b/packages/database/src/utils.ts index 17adda060..70452262b 100644 --- a/packages/database/src/utils.ts +++ b/packages/database/src/utils.ts @@ -58,6 +58,7 @@ export function toWhere(options: any, context: ToWhereContext = {}) { ctx, model, database, + dialect: database.sequelize.getDialect(), fieldPath: name ? `${name}.${prefix}` : prefix, }); if (result.constructor.name === 'Literal') { diff --git a/packages/plugin-client/src/__tests__/sqlite.test.ts b/packages/plugin-client/src/__tests__/sqlite.test.ts new file mode 100644 index 000000000..628b359d3 --- /dev/null +++ b/packages/plugin-client/src/__tests__/sqlite.test.ts @@ -0,0 +1,65 @@ +import Application from '../../../server/src/application'; +import { getInitSqls, runSql } from '../server'; + +test('import demo data', async () => { + const app = new Application({ + database: { + dialect: 'sqlite', + dialectModule: require('sqlite3'), + storage: 'db.sqlite', + logging: false, + }, + }); + + const plugins = [ + '@nocobase/plugin-ui-router', + '@nocobase/plugin-ui-schema', + '@nocobase/plugin-collections', + '@nocobase/plugin-users', + '@nocobase/plugin-action-logs', + '@nocobase/plugin-file-manager', + '@nocobase/plugin-permissions', + '@nocobase/plugin-export', + '@nocobase/plugin-system-settings', + '@nocobase/plugin-china-region', + ]; + + for (const plugin of plugins) { + const pluginPath = `${plugin}/src/server`.replace( + '@nocobase/', + '../../../', + ); + app.plugin((await import(pluginPath)).default); + } + + await app.load(); + + await app.db.sync({ force: true }); + + await app.emitAsync('db.init'); + + const database = app.db; + + const sqls = getInitSqls(); + + for (const sqlGroup of sqls.part1) { + for (const sql of sqlGroup.split(';')) { + await runSql(sql, database); + } + } + + await app.db.getModel('collections').load({ + skipExisting: true, + }); + await app.db.sync(); + + for (const sqlGroup of sqls.part2) { + for (const sql of sqlGroup.split(';')) { + try { + await runSql(sql, database); + } catch (e) { + console.error(e); + } + } + } +}); diff --git a/packages/plugin-client/src/db/part1/01-ui_schemas.sql b/packages/plugin-client/src/db/part1/01-ui_schemas.sql index 8a3846032..409f0e22c 100644 --- a/packages/plugin-client/src/db/part1/01-ui_schemas.sql +++ b/packages/plugin-client/src/db/part1/01-ui_schemas.sql @@ -1,4 +1,4 @@ -TRUNCATE "ui_schemas" RESTART IDENTITY CASCADE; +DELETE FROM "ui_schemas"; INSERT INTO "ui_schemas" ("key", "name", "title", "type", "x-component", "options", "async", "sort", "created_at", "updated_at", "parent_key") VALUES ('dtu7erjj0q8', NULL, '昵称', 'string', 'Input', '{}', '0', 1, '2021-09-02 15:07:56.984+00', '2021-09-02 15:07:56.984+00', NULL), ('5nqs5glgn5c', NULL, '邮箱', 'string', 'Input', '{"require":true}', '0', 2, '2021-09-02 15:07:57.015+00', '2021-09-02 15:07:57.015+00', NULL), diff --git a/packages/plugin-client/src/db/part1/02-collections.sql b/packages/plugin-client/src/db/part1/02-collections.sql index a7e0e4369..ebc2bbfa8 100644 --- a/packages/plugin-client/src/db/part1/02-collections.sql +++ b/packages/plugin-client/src/db/part1/02-collections.sql @@ -1,4 +1,4 @@ -TRUNCATE "collections" RESTART IDENTITY CASCADE; +DELETE FROM "collections"; INSERT INTO "collections" ("name", "logging", "title", "privilege", "sortable", "options", "sort", "created_at", "updated_at", "ui_schema_key") VALUES ('users', '1', '用户', 'undelete', '"sort"', '{"createdBy":false,"updatedBy":false}', 2, '2021-09-02 15:07:56.914+00', '2021-09-15 00:59:20.676+00', NULL), ('t_fsveob6p269', '1', '顾客', NULL, '"sort"', '{}', 5, '2021-09-12 01:05:52.722+00', '2021-09-18 04:15:23.113+00', NULL), diff --git a/packages/plugin-client/src/db/part1/03-fields.sql b/packages/plugin-client/src/db/part1/03-fields.sql index 2b9d887fd..d822d7f8f 100644 --- a/packages/plugin-client/src/db/part1/03-fields.sql +++ b/packages/plugin-client/src/db/part1/03-fields.sql @@ -1,4 +1,4 @@ -TRUNCATE "fields" RESTART IDENTITY CASCADE; +DELETE FROM "fields"; INSERT INTO "fields" ("key", "name", "interface", "data_type", "privilege", "state", "options", "sort", "created_at", "updated_at", "parent_key", "collection_name", "ui_schema_key", "reverse_key") VALUES ('aun85p76s0v', 'f_l8uuiwcnlw9', 'textarea', 'text', NULL, 1, '{}', 8, '2021-09-12 08:18:46.92+00', '2021-09-12 08:18:46.935+00', NULL, 't_2uhu4szs1kq', 'c2pallk0lge', NULL), ('sj5p2y4ac06', '70enejazumv', 'updatedAt', 'date', 'undelete', 1, '{"field":"updated_at"}', 2, '2021-09-03 08:17:30.543+00', '2021-09-03 08:17:30.555+00', NULL, 't_2uhu4szs1kq', 'lchrxtjm3d5', NULL), diff --git a/packages/plugin-client/src/db/part1/04-routes.sql b/packages/plugin-client/src/db/part1/04-routes.sql index d3e55efad..a2125c255 100644 --- a/packages/plugin-client/src/db/part1/04-routes.sql +++ b/packages/plugin-client/src/db/part1/04-routes.sql @@ -1,4 +1,4 @@ -TRUNCATE "routes" RESTART IDENTITY CASCADE; +DELETE FROM "routes"; INSERT INTO "routes" ("key", "type", "options", "sort", "created_at", "updated_at", "parent_key", "ui_schema_key") VALUES ('r_94b8nz6evyh', 'redirect', '{"from":"/","to":"/admin","exact":true}', 1, '2021-09-02 15:07:57.162+00', '2021-09-02 15:07:57.162+00', NULL, NULL), ('r_w1sa2lfk44v', 'route', '{"path":"/admin/:name(.+)?","component":"AdminLayout","title":"后台"}', 2, '2021-09-02 15:07:57.17+00', '2021-09-02 15:07:57.185+00', NULL, 'qqzzjakwkwl'), diff --git a/packages/plugin-client/src/db/part1/05-attachments.sql b/packages/plugin-client/src/db/part1/05-attachments.sql index ca00e86a8..b2bb0fa37 100644 --- a/packages/plugin-client/src/db/part1/05-attachments.sql +++ b/packages/plugin-client/src/db/part1/05-attachments.sql @@ -1,3 +1,4 @@ +DELETE FROM "attachments"; INSERT INTO "attachments" ("id", "title", "filename", "extname", "size", "mimetype", "path", "meta", "url", "created_at", "updated_at", "created_by_id", "updated_by_id", "storage_id") VALUES (2, 'Nocobase', 'dac1165de10a3373a87ae4e5da9788ab.png', '.png', NULL, 'image/png', '', '{}', 'https://nocobase.oss-cn-beijing.aliyuncs.com/dac1165de10a3373a87ae4e5da9788ab.png', '2021-09-04 01:33:38.878+00', '2021-09-04 01:33:38.878+00', NULL, NULL, 2), (3, 'grapes-276070_1280', '3451976fea63a882b5fd661aa211693e.jpg', '.jpg', NULL, 'image/jpeg', '', '{}', 'https://nocobase.oss-cn-beijing.aliyuncs.com/3451976fea63a882b5fd661aa211693e.jpg', '2021-09-04 01:34:50.388+00', '2021-09-04 01:34:50.388+00', NULL, 1, 2), @@ -27,4 +28,3 @@ INSERT INTO "attachments" ("id", "title", "filename", "extname", "size", "mimety (26, 'kaffeerad-1543158_1280', '8b94dd8cbecdbbd03e4c2fcfbd1067e7.jpg', '.jpg', NULL, 'image/jpeg', '', '{}', 'https://nocobase.oss-cn-beijing.aliyuncs.com/8b94dd8cbecdbbd03e4c2fcfbd1067e7.jpg', '2021-09-12 07:47:39.63+00', '2021-09-12 07:47:39.63+00', NULL, 1, 2), (28, 'apple-1609693_1280', '31b7dee511a8d6364fee18411763c188.jpg', '.jpg', NULL, 'image/jpeg', '', '{}', 'https://nocobase.oss-cn-beijing.aliyuncs.com/31b7dee511a8d6364fee18411763c188.jpg', '2021-09-12 07:48:41.367+00', '2021-09-12 07:48:41.367+00', NULL, 1, 2), (29, 'cappuccino-1609932_1280', '8f480c4f1bd3d292ef78fb6847669e51.jpg', '.jpg', NULL, 'image/jpeg', '', '{}', 'https://nocobase.oss-cn-beijing.aliyuncs.com/8f480c4f1bd3d292ef78fb6847669e51.jpg', '2021-09-12 07:48:41.372+00', '2021-09-12 07:48:41.372+00', NULL, 1, 2); -SELECT setval('attachments_id_seq', (SELECT MAX(id) FROM "attachments"), true); diff --git a/packages/plugin-client/src/db/part2/06-t_2uhu4szs1kq.sql b/packages/plugin-client/src/db/part2/06-t_2uhu4szs1kq.sql index 105fce2d4..f9a399b8e 100644 --- a/packages/plugin-client/src/db/part2/06-t_2uhu4szs1kq.sql +++ b/packages/plugin-client/src/db/part2/06-t_2uhu4szs1kq.sql @@ -1,3 +1,4 @@ +DELETE FROM "t_2uhu4szs1kq"; INSERT INTO "t_2uhu4szs1kq" ("id", "created_at", "updated_at", "sort", "created_by_id", "updated_by_id", "f_hwenour8ara", "f_hznqtmqljb2", "f_u4i0jrp4uo6", "f_l8uuiwcnlw9") VALUES (1, '2021-09-03 08:19:53.163+00', '2021-09-18 07:50:34.039+00', 70, 1, 1, '这是我们要做的任务这是我们要做的', '2021-09-17 00:00:00+00', 'f1g3r41rdh8', '利本毛所线表体定质花,则根物大教斯经前,飞能D发科程W目。 阶直少看员片飞飞今西取亲本就条,持层品平米今ON孤你七2。 花离定可除展通向业很,斗术真节西严特矿,导养-N群长置便。 深规马细三强低按段,事作般习就代它,完技O布D枝快。 便报动斗改克离为具影研立特,养命规么才设步局方总较共张,时什Q肃声的者起开出话。'), (2, '2021-09-12 08:01:16.467+00', '2021-09-18 07:50:34.039+00', 71, 1, 1, '批量上传附件', '2021-09-24 00:00:00+00', 'f1g3r41rdh8', '利本毛所线表体定质花,则根物大教斯经前,飞能D发科程W目。 阶直少看员片飞飞今西取亲本就条,持层品平米今ON孤你七2。 花离定可除展通向业很,斗术真节西严特矿,导养-N群长置便。 深规马细三强低按段,事作般习就代它,完技O布D枝快。 便报动斗改克离为具影研立特,养命规么才设步局方总较共张,时什Q肃声的者起开出话。'), @@ -5,4 +6,3 @@ INSERT INTO "t_2uhu4szs1kq" ("id", "created_at", "updated_at", "sort", "created_ (4, '2021-09-12 08:02:37.852+00', '2021-09-18 07:50:34.039+00', 72, 1, 1, '包派极都火题折究条', '2021-10-08 00:00:00+00', 'zfowtv6fnel', '利本毛所线表体定质花,则根物大教斯经前,飞能D发科程W目。 阶直少看员片飞飞今西取亲本就条,持层品平米今ON孤你七2。 花离定可除展通向业很,斗术真节西严特矿,导养-N群长置便。 深规马细三强低按段,事作般习就代它,完技O布D枝快。 便报动斗改克离为具影研立特,养命规么才设步局方总较共张,时什Q肃声的者起开出话。'), (6, '2021-09-17 01:35:18.379+00', '2021-09-18 07:50:34.039+00', 73, 1, 1, '达到顶峰五十五分', '2021-09-17 00:00:00+00', 'zfowtv6fnel', NULL), (3, '2021-09-12 08:02:20.501+00', '2021-09-18 07:50:34.042+00', 68, 1, 1, '往你周观青整积元路公', '2021-09-24 00:00:00+00', 'lebkfnj3d9i', '利本毛所线表体定质花,则根物大教斯经前,飞能D发科程W目。 阶直少看员片飞飞今西取亲本就条,持层品平米今ON孤你七2。 花离定可除展通向业很,斗术真节西严特矿,导养-N群长置便。 深规马细三强低按段,事作般习就代它,完技O布D枝快。 便报动斗改克离为具影研立特,养命规么才设步局方总较共张,时什Q肃声的者起开出话。'); -SELECT setval('t_2uhu4szs1kq_id_seq', (SELECT MAX(id) FROM "t_2uhu4szs1kq"), true); diff --git a/packages/plugin-client/src/db/part2/07-t_fsveob6p269.sql b/packages/plugin-client/src/db/part2/07-t_fsveob6p269.sql index 445eeb166..86abef1e8 100644 --- a/packages/plugin-client/src/db/part2/07-t_fsveob6p269.sql +++ b/packages/plugin-client/src/db/part2/07-t_fsveob6p269.sql @@ -1,3 +1,4 @@ +DELETE FROM "t_fsveob6p269"; INSERT INTO "t_fsveob6p269" ("id", "created_at", "updated_at", "sort", "created_by_id", "updated_by_id", "f_vgw4f62h16e", "f_3det6kr3vn8", "f_x9kmuovlm79", "f_bysgds7j36p", "f_kk4nspj1i28", "f_61znsfh307m") VALUES (2, '2021-09-12 07:41:10.508+00', '2021-09-12 07:41:10.508+00', 2, 1, 1, '庆贝儿', '18910347327', 'khan8z430yx', '2000-02-03 00:00:00+00', '件段两声提局则着除所法军,为后出子石了期七小南领能革,表相F声化等水坑但F连。 今劳查速布你安约定值斗,运通声资区的选心认,部样7展期辅些正记。 @@ -18,4 +19,3 @@ INSERT INTO "t_fsveob6p269" ("id", "created_at", "updated_at", "sort", "created_ (6, '2021-09-12 07:43:57.617+00', '2021-09-12 08:28:50.544+00', 6, 1, 1, '宓中仕', '13527645198', 'khan8z430yx', '1999-02-26 00:00:00+00', '压界点小名理它车机两得,原查去证行村太孟道。求真强记织管装总论形感增委越,术市点便定肃容红个而写。代往门张照布活,期头劳量人,导8新建给。造装话和很片算只类论所,全变容时过地传达,术族刷车进变了邮盯。 在给情她积音新问整史图火验往,受王多式儿花6集听壳是呆。越县管积小与权内有红压,往你周观青整积元路公,包派极都火题折究条。 张斯些况于非按并音习又极别解切参,气斗也新越几励小集及询报吧。整展研号公清文例资品会头,厂斗志照部都越何事车前,斯队两将断极S点其会。方去即白林两团规,度关这步明管王至,但低承我质建求。 组了间族你那记的格压把,走张它然无便同儿把意,七土两叫询呜没报时。她引广书率出主且金,来万须采确部还活件,较M来霸豆板枝。政个值满物种被,共据运本很极然满,须E事眼目外。器约精机王传马,立目证权适色总,公屈过史类刺。体回每在或机发己极即,指办置们选会习十己,连难4持包厕知柜。 速还提红达知器金积外委下', 'z7wq1mkwmmx'); -SELECT setval('t_fsveob6p269_id_seq', (SELECT MAX(id) FROM "t_fsveob6p269"), true); diff --git a/packages/plugin-client/src/db/part2/08-t_geso7fru7a9.sql b/packages/plugin-client/src/db/part2/08-t_geso7fru7a9.sql index 04dbaabcc..da859b79f 100644 --- a/packages/plugin-client/src/db/part2/08-t_geso7fru7a9.sql +++ b/packages/plugin-client/src/db/part2/08-t_geso7fru7a9.sql @@ -1,3 +1,4 @@ +DELETE FROM "t_geso7fru7a9"; INSERT INTO "t_geso7fru7a9" ("id", "created_at", "updated_at", "sort", "created_by_id", "updated_by_id", "f_umjewufbyhj", "f_nlgk67tpdql", "f_niymyj0no38") VALUES (1, '2021-09-12 01:24:52.21+00', '2021-09-12 07:38:07.64+00', 1, 1, 1, '2021-09-10 11:26:28+00', 'x4qnavatfai', '751834123525'), (2, '2021-09-12 07:36:07.647+00', '2021-09-12 07:38:15.812+00', 2, 1, 1, '2021-09-24 15:41:03+00', 'i2xjqmnwrsu', '751834123526'), @@ -15,4 +16,3 @@ INSERT INTO "t_geso7fru7a9" ("id", "created_at", "updated_at", "sort", "created_ (14, '2021-09-14 02:06:10.875+00', '2021-09-14 02:06:10.875+00', 14, 1, 1, '2021-09-07 10:06:06+00', 'x4qnavatfai', '751834123537'), (15, '2021-09-14 02:06:29.102+00', '2021-09-14 02:06:29.102+00', 15, 1, 1, '2021-09-25 10:06:18+00', 'x4qnavatfai', '751834123538'), (16, '2021-09-14 02:06:45.054+00', '2021-09-14 02:06:45.054+00', 16, 1, 1, '2021-09-09 10:06:41+00', 'i2xjqmnwrsu', '751834123539'); -SELECT setval('t_geso7fru7a9_id_seq', (SELECT MAX(id) FROM "t_geso7fru7a9"), true); diff --git a/packages/plugin-client/src/db/part2/09-t_bphcojtl8bx.sql b/packages/plugin-client/src/db/part2/09-t_bphcojtl8bx.sql index e25855417..bbc8187ee 100644 --- a/packages/plugin-client/src/db/part2/09-t_bphcojtl8bx.sql +++ b/packages/plugin-client/src/db/part2/09-t_bphcojtl8bx.sql @@ -1,3 +1,4 @@ +DELETE FROM "t_bphcojtl8bx"; INSERT INTO "t_bphcojtl8bx" ("created_at", "updated_at", "f_cn6a3p0wq2p", "f_4x6u1waa8r6") VALUES ('2021-09-12 01:24:52.263+00', '2021-09-12 01:24:52.263+00', 1, 1), ('2021-09-12 07:36:07.681+00', '2021-09-12 07:36:07.681+00', 1, 2), diff --git a/packages/plugin-client/src/db/part2/10-t_jh7a28dsfzi.sql b/packages/plugin-client/src/db/part2/10-t_jh7a28dsfzi.sql index 0b81e4ec7..3923aa6d0 100644 --- a/packages/plugin-client/src/db/part2/10-t_jh7a28dsfzi.sql +++ b/packages/plugin-client/src/db/part2/10-t_jh7a28dsfzi.sql @@ -1,3 +1,4 @@ +DELETE FROM "t_jh7a28dsfzi"; INSERT INTO "t_jh7a28dsfzi" ("created_at", "updated_at", "f_xg3mysbjfra", "f_gc7ppj0b7n1") VALUES ('2021-09-12 01:21:03.65+00', '2021-09-12 01:21:03.65+00', 1, 6), ('2021-09-12 01:21:03.65+00', '2021-09-12 01:21:03.65+00', 1, 5), diff --git a/packages/plugin-client/src/db/part2/11-t_p13gbi31uux.sql b/packages/plugin-client/src/db/part2/11-t_p13gbi31uux.sql index bbd76988a..7b2073332 100644 --- a/packages/plugin-client/src/db/part2/11-t_p13gbi31uux.sql +++ b/packages/plugin-client/src/db/part2/11-t_p13gbi31uux.sql @@ -1,3 +1,4 @@ +DELETE FROM "t_p13gbi31uux"; INSERT INTO "t_p13gbi31uux" ("created_at", "updated_at", "f_86s65mbw93e", "f_hwx3hgg5b5t") VALUES ('2021-09-12 01:21:03.666+00', '2021-09-12 01:21:03.666+00', 1, '11'), ('2021-09-12 01:21:03.666+00', '2021-09-12 01:21:03.666+00', 1, '1101'), diff --git a/packages/plugin-client/src/db/part2/12-roles.sql b/packages/plugin-client/src/db/part2/12-roles.sql index 170ca5521..c203402d0 100644 --- a/packages/plugin-client/src/db/part2/12-roles.sql +++ b/packages/plugin-client/src/db/part2/12-roles.sql @@ -1,3 +1,4 @@ +DELETE FROM "roles"; INSERT INTO "roles" ("name", "title", "sort", "created_at", "updated_at", "created_by_id", "updated_by_id") VALUES ('r_tfs4qtaxjcs', '管理员', 1, '2021-09-14 02:12:19.98+00', '2021-09-14 02:12:19.98+00', 1, 1), ('r_np59b00ex8z', '普通成员', 2, '2021-09-14 02:12:35.799+00', '2021-09-14 02:12:35.799+00', 1, 1); diff --git a/packages/plugin-client/src/db/part2/13-action_permissions.sql b/packages/plugin-client/src/db/part2/13-action_permissions.sql index 28178fb22..e435f4a6e 100644 --- a/packages/plugin-client/src/db/part2/13-action_permissions.sql +++ b/packages/plugin-client/src/db/part2/13-action_permissions.sql @@ -1,3 +1,4 @@ +DELETE FROM "action_permissions"; INSERT INTO "action_permissions" ("id", "action_name", "created_at", "updated_at", "collection_name", "user_id", "created_by_id", "updated_by_id", "role_name", "scope_id") VALUES (1, 'create', '2021-09-14 02:19:38.019+00', '2021-09-14 02:19:45.64+00', 't_geso7fru7a9', NULL, 1, 1, NULL, NULL), (2, 'get', '2021-09-14 02:19:38.04+00', '2021-09-14 02:19:45.64+00', 't_geso7fru7a9', NULL, 1, 1, NULL, NULL), @@ -15,4 +16,3 @@ INSERT INTO "action_permissions" ("id", "action_name", "created_at", "updated_at (10, 'get', '2021-09-14 02:19:53.274+00', '2021-09-14 02:20:00.371+00', 't_fsveob6p269', NULL, 1, 1, NULL, NULL), (11, 'update', '2021-09-14 02:19:53.284+00', '2021-09-14 02:20:00.371+00', 't_fsveob6p269', NULL, 1, 1, NULL, NULL), (12, 'destroy', '2021-09-14 02:19:53.294+00', '2021-09-14 02:20:00.371+00', 't_fsveob6p269', NULL, 1, 1, NULL, NULL); -SELECT setval('action_permissions_id_seq', (SELECT MAX(id) FROM "action_permissions"), true); diff --git a/packages/plugin-client/src/db/part2/14-field_permissions.sql b/packages/plugin-client/src/db/part2/14-field_permissions.sql index f48101efd..e0c5db477 100644 --- a/packages/plugin-client/src/db/part2/14-field_permissions.sql +++ b/packages/plugin-client/src/db/part2/14-field_permissions.sql @@ -1,3 +1,4 @@ +DELETE FROM "field_permissions"; INSERT INTO "field_permissions" ("created_at", "updated_at", "action_permission_id", "field_key") VALUES ('2021-09-14 02:19:38.034+00', '2021-09-14 02:19:38.034+00', 1, 'nv0iw8wdxmz'), ('2021-09-14 02:19:38.034+00', '2021-09-14 02:19:38.034+00', 1, 'lu1ibrb0yi2'), diff --git a/packages/plugin-client/src/db/part2/15-roles_ui_schemas.sql b/packages/plugin-client/src/db/part2/15-roles_ui_schemas.sql index 6947cfac1..6b8c3443a 100644 --- a/packages/plugin-client/src/db/part2/15-roles_ui_schemas.sql +++ b/packages/plugin-client/src/db/part2/15-roles_ui_schemas.sql @@ -1,3 +1,4 @@ +DELETE FROM "roles_ui_schemas"; INSERT INTO "roles_ui_schemas" ("created_at", "updated_at", "ui_schema_key", "role_name") VALUES ('2021-09-14 02:19:13.669+00', '2021-09-14 02:19:13.669+00', '0b73gccskc2', 'r_tfs4qtaxjcs'), ('2021-09-14 02:19:13.669+00', '2021-09-14 02:19:13.669+00', 'hall72478p5', 'r_tfs4qtaxjcs'), diff --git a/packages/plugin-client/src/db/postgres/setinval.sql b/packages/plugin-client/src/db/postgres/setinval.sql new file mode 100644 index 000000000..a5a997b0d --- /dev/null +++ b/packages/plugin-client/src/db/postgres/setinval.sql @@ -0,0 +1,5 @@ +SELECT setval('action_permissions_id_seq', (SELECT MAX(id) FROM "action_permissions"), true); +SELECT setval('attachments_id_seq', (SELECT MAX(id) FROM "attachments"), true); +SELECT setval('t_2uhu4szs1kq_id_seq', (SELECT MAX(id) FROM "t_2uhu4szs1kq"), true); +SELECT setval('t_fsveob6p269_id_seq', (SELECT MAX(id) FROM "t_fsveob6p269"), true); +SELECT setval('t_geso7fru7a9_id_seq', (SELECT MAX(id) FROM "t_geso7fru7a9"), true); diff --git a/packages/plugin-client/src/server.ts b/packages/plugin-client/src/server.ts index 91336d2ca..e0d8fcbba 100644 --- a/packages/plugin-client/src/server.ts +++ b/packages/plugin-client/src/server.ts @@ -5,20 +5,46 @@ import { PluginOptions } from '@nocobase/server'; import { readFileSync } from 'fs'; import glob from 'glob'; -function getInitSqls() { - const part1 = []; - const part2 = []; - const files1 = glob.sync(path.resolve(__dirname, './db/part1/*.sql')); - for (const file of files1) { - const sql = readFileSync(file).toString(); - part1.push(sql); +export function getInitSqls(): { + [key: string]: string[]; +} { + const dirs = ['part1', 'part2', 'postgres']; + return dirs + .map((dir) => { + return { + dir, + files: glob + .sync(path.resolve(__dirname, `./db/${dir}/*.sql`)) + .map((fileName) => readFileSync(fileName).toString()), + }; + }) + .reduce((carry, dirFiles) => { + carry[dirFiles.dir] = dirFiles.files; + return carry; + }, {}); +} + +export function runSql(sql, database) { + const trimmed = sql.trim(); + if (trimmed.length == 0) { + return; } - const files2 = glob.sync(path.resolve(__dirname, './db/part2/*.sql')); - for (const file of files2) { - const sql = readFileSync(file).toString(); - part2.push(sql); + return database.sequelize.query(trimmed, { + raw: true, + logging: false, + }); +} + +async function runSqlFile(content, database) { + for (const sqlGroup of content) { + for (const sql of sqlGroup.split(';')) { + try { + await runSql(sql, database); + } catch (e) { + console.error({ e, sql }); + } + } } - return { part1, part2 }; } export default { @@ -39,33 +65,32 @@ export default { } }); const app = this.app; - this.app.on('db.init', async () => { - if (this.options.importDemo !== true) { + const cmd = app.findCommand('init'); + if (cmd) { + cmd.option('--import-demo'); + } + this.app.on('db.init', async (opts, cli) => { + const importDemo = opts.importDemo || this.options.importDemo; + console.log({ importDemo }); + if (importDemo !== true) { return; } - const transaction = await app.db.sequelize.transaction(); const sqls = getInitSqls(); - try { - for (const sql of sqls.part1) { - await app.db.sequelize.query(sql, { transaction }); - } - await transaction.commit(); - } catch (error) { - await transaction.rollback(); - } + + const database = app.db; + + await runSqlFile(sqls.part1, database); + await app.db.getModel('collections').load({ skipExisting: true, }); await app.db.sync(); - const transaction2 = await app.db.sequelize.transaction(); - try { - for (const sql of sqls.part2) { - await app.db.sequelize.query(sql, { transaction: transaction2 }); - } - await transaction2.commit(); - } catch (error) { - await transaction2.rollback(); + + await runSqlFile(sqls.part2, database); + + if (app.db.sequelize.getDialect() == 'postgres') { + await runSqlFile(sqls.postgres, database); } }); - } + }, } as PluginOptions; diff --git a/packages/plugin-users/src/__tests__/fields.test.ts b/packages/plugin-users/src/__tests__/fields.test.ts index 59950fee8..4a22a32d0 100644 --- a/packages/plugin-users/src/__tests__/fields.test.ts +++ b/packages/plugin-users/src/__tests__/fields.test.ts @@ -8,8 +8,8 @@ describe('user fields', () => { beforeEach(async () => { api = mockServer(); - api.registerPlugin('users', require('../server').default); - await api.loadPlugins(); + api.plugin(require('../server').default); + await api.load(); db = api.db; }); diff --git a/packages/plugin-users/src/server.ts b/packages/plugin-users/src/server.ts index 10e5ece57..2e209981a 100644 --- a/packages/plugin-users/src/server.ts +++ b/packages/plugin-users/src/server.ts @@ -17,8 +17,8 @@ export default { const User = database.getModel('users'); await User.create({ nickname: '超级管理员', - email: process.env.ADMIN_EMAIL, - password: process.env.ADMIN_PASSWORD, + email: process.env.ADMIN_EMAIL || 'admin@nocobase.com', + password: process.env.ADMIN_PASSWORD || 'admin', }); }); diff --git a/packages/server/src/__tests__/application.test.ts b/packages/server/src/__tests__/application.test.ts index b12f61786..510d12253 100644 --- a/packages/server/src/__tests__/application.test.ts +++ b/packages/server/src/__tests__/application.test.ts @@ -2,8 +2,7 @@ import supertest from 'supertest'; import { Application } from '../application'; import { Plugin } from '../plugin'; -class MyPlugin extends Plugin { -} +class MyPlugin extends Plugin {} describe('application', () => { let app: Application; @@ -12,21 +11,15 @@ describe('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', - }, + dialect: 'sqlite', + dialectModule: require('sqlite3'), + storage: ':memory:', }, resourcer: { prefix: '/api', }, dataWrapping: false, + registerActions: false, }); app.resourcer.registerActionHandlers({ list: async (ctx, next) => { @@ -84,7 +77,7 @@ describe('application', () => { { type: 'hasMany', name: 'bars', - } + }, ], }); const response = await agent.get('/api/foos/1/bars'); @@ -92,7 +85,7 @@ describe('application', () => { }); it('db.middleware', async () => { - const index = app.middleware.findIndex(m => m.name === 'table2resource'); + const index = app.middleware.findIndex((m) => m.name === 'table2resource'); app.middleware.splice(index, 0, async (ctx, next) => { app.db.table({ name: 'tests', @@ -104,7 +97,7 @@ describe('application', () => { }); it('db.middleware', async () => { - const index = app.middleware.findIndex(m => m.name === 'table2resource'); + const index = app.middleware.findIndex((m) => m.name === 'table2resource'); app.middleware.splice(index, 0, async (ctx, next) => { app.db.table({ name: 'bars', @@ -115,7 +108,7 @@ describe('application', () => { { type: 'hasMany', name: 'bars', - } + }, ], }); await next(); diff --git a/packages/server/src/application.ts b/packages/server/src/application.ts index 03314a3c7..66772ac98 100644 --- a/packages/server/src/application.ts +++ b/packages/server/src/application.ts @@ -1,12 +1,15 @@ import Koa from 'koa'; -import cors from '@koa/cors'; -import bodyParser from 'koa-bodyparser'; 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'; import { registerActions } from '@nocobase/actions'; +import { + createCli, + createDatabase, + createResourcer, + registerMiddlewares, +} from './helper'; export interface ResourcerOptions { prefix?: string; @@ -18,6 +21,7 @@ export interface ApplicationOptions { bodyParser?: any; cors?: any; dataWrapping?: boolean; + registerActions?: boolean; } interface DefaultState { @@ -46,7 +50,7 @@ interface ActionsOptions { export class Application< StateT = DefaultState, - ContextT = DefaultContext, + ContextT = DefaultContext > extends Koa { public readonly db: Database; @@ -59,91 +63,14 @@ export class Application< constructor(options: ApplicationOptions) { super(); - if (options.database instanceof Database) { - this.db = options.database; - } else { - this.db = new Database(options.database); + this.db = createDatabase(options); + this.resourcer = createResourcer(options); + this.cli = createCli(this, options); + + registerMiddlewares(this, options); + if (options.registerActions !== false) { + registerActions(this); } - - this.resourcer = new Resourcer({ ...options.resourcer }); - this.cli = new Command(); - - if (options.bodyParser !== false) { - this.use( - bodyParser({ - ...options.bodyParser, - }), - ); - } - - this.use( - cors({ - exposeHeaders: ['content-disposition'], - ...options.cors, - }), - ); - - this.use(async (ctx, next) => { - ctx.db = this.db; - ctx.resourcer = this.resourcer; - await next(); - }); - - if (options.dataWrapping !== false) { - this.use(dataWrapping()); - } - - this.use(table2resource()); - this.use(this.resourcer.restApiMiddleware()); - - registerActions(this); - - this.cli - .command('db:sync') - .option('-f, --force') - .action(async (...args) => { - console.log('db sync...'); - const cli = args.pop(); - const force = cli.opts()?.force; - await this.db.sync( - force - ? { - force: true, - alter: { - drop: true, - }, - } - : {}, - ); - await this.destroy(); - }); - - this.cli - .command('init') - // .option('-f, --force') - .action(async (...args) => { - const cli = args.pop(); - await this.db.sync({ - force: true, - alter: { - drop: true, - }, - }); - await this.emitAsync('db.init'); - await this.destroy(); - }); - - this.cli - .command('start') - .option('-p, --port [port]') - .action(async (...args) => { - const cli = args.pop(); - console.log(args); - const opts = cli.opts(); - await this.emitAsync('beforeStart'); - this.listen(opts.port || 3000); - console.log(`http://localhost:${opts.port || 3000}/`); - }); } use( @@ -170,6 +97,10 @@ export class Application< return this.cli.command(nameAndArgs, opts); } + findCommand(name: string): Command { + return (this.cli as any)._findCommand(name); + } + plugin(options?: PluginType | PluginOptions, ext?: PluginOptions): Plugin { if (typeof options === 'string') { return this.plugin(require(options).default, ext); diff --git a/packages/server/src/helper.ts b/packages/server/src/helper.ts new file mode 100644 index 000000000..c1bb14978 --- /dev/null +++ b/packages/server/src/helper.ts @@ -0,0 +1,100 @@ +import { DefaultContext, DefaultState } from 'koa'; +import bodyParser from 'koa-bodyparser'; +import cors from '@koa/cors'; +import Database from '@nocobase/database'; +import Resourcer from '@nocobase/resourcer'; +import { Command } from 'commander'; +import Application, { ApplicationOptions } from './application'; +import { dataWrapping } from './middlewares/data-wrapping'; +import { table2resource } from './middlewares/table2resource'; + +export function createDatabase(options: ApplicationOptions) { + if (options.database instanceof Database) { + return options.database; + } else { + return new Database(options.database); + } +} + +export function createResourcer(options: ApplicationOptions) { + return new Resourcer({ ...options.resourcer }); +} + +export function createCli(app, options: ApplicationOptions) { + const cli = new Command(); + + cli + .command('db:sync') + .option('-f, --force') + .action(async (...args) => { + console.log('db sync...'); + const cli = args.pop(); + const force = cli.opts()?.force; + await app.db.sync( + force + ? { + force: true, + alter: { + drop: true, + }, + } + : {}, + ); + await app.destroy(); + }); + + cli + .command('init') + .option('-f, --force') + .action(async (...args) => { + await app.db.sync({ + force: true, + }); + await app.emitAsync('db.init', ...args); + await app.destroy(); + }); + cli + .command('start') + .option('-p, --port [port]') + .action(async (...args) => { + const cli = args.pop(); + const opts = cli.opts(); + await app.emitAsync('beforeStart'); + app.listen(opts.port || 3000); + console.log(`http://localhost:${opts.port || 3000}/`); + }); + return cli; +} + +export function registerMiddlewares( + app: Application, + options: ApplicationOptions, +) { + if (options.bodyParser !== false) { + app.use( + bodyParser({ + ...options.bodyParser, + }), + ); + } + + app.use( + cors({ + exposeHeaders: ['content-disposition'], + ...options.cors, + }), + ); + + app.use(async (ctx, next) => { + ctx.db = app.db; + ctx.resourcer = app.resourcer; + await next(); + }); + + if (options.dataWrapping !== false) { + app.use(dataWrapping()); + } + + app.use(table2resource()); + app.use(app.resourcer.restApiMiddleware()); +} diff --git a/yarn.lock b/yarn.lock index 0a90389e3..06ed0890f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4080,10 +4080,17 @@ "@types/prop-types" "*" "@types/react" "*" -"@types/react-dom@^16.9.8", "@types/react-dom@^17.0.0", "@types/react-dom@^17.0.3": +"@types/react-dom@^16.9.8": + version "16.9.14" + resolved "https://registry.nlark.com/@types/react-dom/download/@types/react-dom-16.9.14.tgz?cache=0&sync_timestamp=1629708935856&other_urls=https%3A%2F%2Fregistry.nlark.com%2F%40types%2Freact-dom%2Fdownload%2F%40types%2Freact-dom-16.9.14.tgz#674b8f116645fe5266b40b525777fc6bb8eb3bcd" + integrity sha1-Z0uPEWZF/lJmtAtSV3f8a7jrO80= + dependencies: + "@types/react" "^16" + +"@types/react-dom@^17.0.3": version "17.0.9" - resolved "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.9.tgz#441a981da9d7be117042e1a6fd3dac4b30f55add" - integrity sha512-wIvGxLfgpVDSAMH5utdL9Ngm5Owu0VsGmldro3ORLXV8CShrL8awVj06NuEXFQ5xyaYfdca7Sgbk/50Ri1GdPg== + resolved "https://registry.nlark.com/@types/react-dom/download/@types/react-dom-17.0.9.tgz#441a981da9d7be117042e1a6fd3dac4b30f55add" + integrity sha1-RBqYHanXvhFwQuGm/T2sSzD1Wt0= dependencies: "@types/react" "*" @@ -4156,7 +4163,7 @@ "@types/history" "*" "@types/react" "*" -"@types/react@*", "@types/react@>=16.9.11", "@types/react@^16.9.43", "@types/react@^17.0.0": +"@types/react@*", "@types/react@>=16.9.11", "@types/react@^17.0.0": version "17.0.18" resolved "https://registry.npmjs.org/@types/react/-/react-17.0.18.tgz#4109cbbd901be9582e5e39e3d77acd7b66bb7fbe" integrity sha512-YTLgu7oS5zvSqq49X5Iue5oAbVGhgPc5Au29SJC4VeE17V6gASoOxVkUDy9pXFMRFxCWCD9fLeweNFizo3UzOg== @@ -4165,6 +4172,15 @@ "@types/scheduler" "*" csstype "^3.0.2" +"@types/react@^16", "@types/react@^16.9.43": + version "16.14.17" + resolved "https://registry.npmmirror.com/@types/react/download/@types/react-16.14.17.tgz?cache=0&sync_timestamp=1634220794290&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2F%40types%2Freact%2Fdownload%2F%40types%2Freact-16.14.17.tgz#c57fcfb05efa6423f5b65fcd4a75f63f05b162bf" + integrity sha1-xX/PsF76ZCP1tl/NSnX2PwWxYr8= + dependencies: + "@types/prop-types" "*" + "@types/scheduler" "*" + csstype "^3.0.2" + "@types/resolve@1.17.1": version "1.17.1" resolved "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz#3afd6ad8967c77e4376c598a82ddd58f46ec45d6" @@ -5869,7 +5885,7 @@ balanced-match@^1.0.0: resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== -base64-js@^1.0.2: +base64-js@^1.0.2, base64-js@^1.3.1: version "1.5.1" resolved "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== @@ -5937,11 +5953,27 @@ bindings@^1.5.0: dependencies: file-uri-to-path "1.0.0" +bl@^4.1.0: + version "4.1.0" + resolved "https://registry.nlark.com/bl/download/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" + integrity sha1-RRU1JkGCvsL7vIOmKrmM8R2fezo= + dependencies: + buffer "^5.5.0" + inherits "^2.0.4" + readable-stream "^3.4.0" + blessed@0.1.81: version "0.1.81" resolved "https://registry.npmjs.org/blessed/-/blessed-0.1.81.tgz#f962d687ec2c369570ae71af843256e6d0ca1129" integrity sha1-+WLWh+wsNpVwrnGvhDJW5tDKESk= +block-stream@*: + version "0.0.9" + resolved "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz#13ebfe778a03205cfe03751481ebb4b3300c126a" + integrity sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo= + dependencies: + inherits "~2.0.0" + bluebird@^3.5.1, bluebird@^3.5.3, bluebird@^3.5.5: version "3.7.2" resolved "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" @@ -6168,6 +6200,14 @@ buffer@^4.3.0: ieee754 "^1.1.4" isarray "^1.0.0" +buffer@^5.5.0: + version "5.7.1" + resolved "https://registry.npm.taobao.org/buffer/download/buffer-5.7.1.tgz?cache=0&sync_timestamp=1606098073225&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fbuffer%2Fdownload%2Fbuffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" + integrity sha1-umLnwTEzBTWCGXFghRqPZI6Z7tA= + dependencies: + base64-js "^1.3.1" + ieee754 "^1.1.13" + builtin-modules@^3.0.0, builtin-modules@^3.1.0: version "3.2.0" resolved "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.2.0.tgz#45d5db99e7ee5e6bc4f362e008bf917ab5049887" @@ -6617,6 +6657,18 @@ cli-cursor@^2.1.0: dependencies: restore-cursor "^2.0.0" +cli-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.nlark.com/cli-cursor/download/cli-cursor-3.1.0.tgz?cache=0&sync_timestamp=1629747481175&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fcli-cursor%2Fdownload%2Fcli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" + integrity sha1-JkMFp65JDR0Dvwybp8kl0XU68wc= + dependencies: + restore-cursor "^3.1.0" + +cli-spinners@^2.5.0: + version "2.6.1" + resolved "https://registry.npmmirror.com/cli-spinners/download/cli-spinners-2.6.1.tgz#adc954ebe281c37a6319bfa401e6dd2488ffb70d" + integrity sha1-rclU6+KBw3pjGb+kAebdJIj/tw0= + cli-tableau@^2.0.0: version "2.0.1" resolved "https://registry.npmjs.org/cli-tableau/-/cli-tableau-2.0.1.tgz#baa78d83e08a2d7ab79b7dad9406f0254977053f" @@ -6864,6 +6916,11 @@ commander@^2.19.0, commander@^2.20.0: resolved "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== +commander@^8.2.0: + version "8.2.0" + resolved "https://registry.nlark.com/commander/download/commander-8.2.0.tgz#37fe2bde301d87d47a53adeff8b5915db1381ca8" + integrity sha1-N/4r3jAdh9R6U63v+LWRXbE4HKg= + commander@~2.14.1: version "2.14.1" resolved "https://registry.npmjs.org/commander/-/commander-2.14.1.tgz#2235123e37af8ca3c65df45b026dbd357b01b9aa" @@ -7339,7 +7396,7 @@ cross-spawn@^6.0.0: shebang-command "^1.2.0" which "^1.2.9" -cross-spawn@^7.0.0, cross-spawn@^7.0.2: +cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== @@ -7994,7 +8051,7 @@ detect-indent@^5.0.0: resolved "https://registry.npmjs.org/detect-indent/-/detect-indent-5.0.0.tgz#3871cc0a6a002e8c3e5b3cf7f336264675f06b9d" integrity sha1-OHHMCmoALow+Wzz38zYmRnXwa50= -detect-libc@^1.0.3: +detect-libc@^1.0.2, detect-libc@^1.0.3: version "1.0.3" resolved "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups= @@ -8990,6 +9047,21 @@ execa@^4.0.0: signal-exit "^3.0.2" strip-final-newline "^2.0.0" +execa@^5.1.1: + version "5.1.1" + resolved "https://registry.nlark.com/execa/download/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" + integrity sha1-+ArZy/Qpj3vR1MlVXCHpN0HEEd0= + dependencies: + cross-spawn "^7.0.3" + get-stream "^6.0.0" + human-signals "^2.1.0" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.1" + onetime "^5.1.2" + signal-exit "^3.0.3" + strip-final-newline "^2.0.0" + exenv@^1.2.0: version "1.2.2" resolved "https://registry.npmjs.org/exenv/-/exenv-1.2.2.tgz#2ae78e85d9894158670b03d47bec1f03bd91bb9d" @@ -9537,6 +9609,16 @@ fsevents@~2.1.2: resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz#fb738703ae8d2f9fe900c33836ddebee8b97f23e" integrity sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ== +fstream@^1.0.0, fstream@^1.0.12: + version "1.0.12" + resolved "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz#4e8ba8ee2d48be4f7d0de505455548eae5932045" + integrity sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg== + dependencies: + graceful-fs "^4.1.2" + inherits "~2.0.0" + mkdirp ">=0.5 0" + rimraf "2" + ftp@^0.3.10: version "0.3.10" resolved "https://registry.npmjs.org/ftp/-/ftp-0.3.10.tgz#9197d861ad8142f3e63d5a83bfe4c59f7330885d" @@ -9677,6 +9759,11 @@ get-stream@^5.0.0, get-stream@^5.1.0: dependencies: pump "^3.0.0" +get-stream@^6.0.0: + version "6.0.1" + resolved "https://registry.nlark.com/get-stream/download/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" + integrity sha1-omLY7vZ6ztV8KFKtYWdSakPL97c= + get-uri@3: version "3.0.2" resolved "https://registry.npmjs.org/get-uri/-/get-uri-3.0.2.tgz#f0ef1356faabc70e1f9404fa3b66b2ba9bfc725c" @@ -9808,6 +9895,18 @@ glob-to-regexp@^0.3.0: resolved "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab" integrity sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs= +glob@^7.0.3: + version "7.2.0" + resolved "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" + integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + glob@^7.0.5, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: version "7.1.7" resolved "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90" @@ -10483,6 +10582,11 @@ human-signals@^1.1.1: resolved "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3" integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw== +human-signals@^2.1.0: + version "2.1.0" + resolved "https://registry.nlark.com/human-signals/download/human-signals-2.1.0.tgz?cache=0&sync_timestamp=1624364695595&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fhuman-signals%2Fdownload%2Fhuman-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" + integrity sha1-3JH8ukLk0G5Kuu0zs+ejwC9RTqA= + humanize-ms@^1.2.0, humanize-ms@^1.2.1: version "1.2.1" resolved "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed" @@ -10516,7 +10620,7 @@ identity-obj-proxy@3.0.0: dependencies: harmony-reflect "^1.4.6" -ieee754@^1.1.4: +ieee754@^1.1.13, ieee754@^1.1.4: version "1.2.1" resolved "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== @@ -10683,7 +10787,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3: +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3: version "2.0.4" resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -11062,6 +11166,11 @@ is-installed-globally@^0.3.1: global-dirs "^2.0.1" is-path-inside "^3.0.1" +is-interactive@^1.0.0: + version "1.0.0" + resolved "https://registry.nlark.com/is-interactive/download/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" + integrity sha1-zqbmrlyHCnsKAAQHC3tYfgJSkS4= + is-module@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591" @@ -11269,6 +11378,11 @@ is-unc-path@^1.0.0: dependencies: unc-path-regex "^0.1.2" +is-unicode-supported@^0.1.0: + version "0.1.0" + resolved "https://registry.nlark.com/is-unicode-supported/download/is-unicode-supported-0.1.0.tgz?cache=0&sync_timestamp=1625294161966&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fis-unicode-supported%2Fdownload%2Fis-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" + integrity sha1-PybHaoCVk7Ur+i7LVxDtJ3m1Iqc= + is-url@1.2.4: version "1.2.4" resolved "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz#04a4df46d28c4cff3d73d01ff06abeb318a1aa52" @@ -12949,6 +13063,14 @@ log-driver@^1.2.7: resolved "https://registry.npmjs.org/log-driver/-/log-driver-1.2.7.tgz#63b95021f0702fedfa2c9bb0a24e7797d71871d8" integrity sha512-U7KCmLdqsGHBLeWqYlFA0V0Sl6P08EE1ZrmA9cxjUE0WVqT9qnyVDPz1kzpFEP0jdJuFnasWIfSd7fsaNXkpbg== +log-symbols@^4.1.0: + version "4.1.0" + resolved "https://registry.npm.taobao.org/log-symbols/download/log-symbols-4.1.0.tgz?cache=0&sync_timestamp=1618723146520&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Flog-symbols%2Fdownload%2Flog-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" + integrity sha1-P727lbRoOsn8eFER55LlWNSr1QM= + dependencies: + chalk "^4.1.0" + is-unicode-supported "^0.1.0" + long-timeout@0.1.1: version "0.1.1" resolved "https://registry.npmjs.org/long-timeout/-/long-timeout-0.1.1.tgz#9721d788b47e0bcb5a24c2e2bee1a0da55dab514" @@ -13686,7 +13808,7 @@ mkdirp@*, mkdirp@1.0.4, mkdirp@1.x, mkdirp@^1.0.3, mkdirp@^1.0.4: resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== -mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@^0.5.4, mkdirp@^0.5.5, mkdirp@~0.5.1: +"mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@^0.5.4, mkdirp@^0.5.5, mkdirp@~0.5.1: version "0.5.5" resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== @@ -13885,6 +14007,15 @@ needle@2.4.0: iconv-lite "^0.4.4" sax "^1.2.4" +needle@^2.2.1: + version "2.9.1" + resolved "https://registry.npmjs.org/needle/-/needle-2.9.1.tgz#22d1dffbe3490c2b83e301f7709b6736cd8f2684" + integrity sha512-6R9fqJ5Zcmf+uYaFgdIHmLwNldn5HbK8L5ybn7Uz+ylX/rnOsSp1AHcvQSrCaFN+qNM1wpymHqD7mVasEOlHGQ== + dependencies: + debug "^3.2.6" + iconv-lite "^0.4.4" + sax "^1.2.4" + negotiator@0.6.2: version "0.6.2" resolved "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" @@ -13923,7 +14054,7 @@ no-case@^3.0.4: lower-case "^2.0.2" tslib "^2.0.3" -node-addon-api@^3.1.0: +node-addon-api@^3.0.0, node-addon-api@^3.1.0: version "3.2.1" resolved "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz#81325e0a2117789c0128dab65e7e38f07ceba161" integrity sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A== @@ -13955,6 +14086,24 @@ node-fetch@^2.5.0, node-fetch@^2.6.1: resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== +node-gyp@3.x: + version "3.8.0" + resolved "https://registry.npmjs.org/node-gyp/-/node-gyp-3.8.0.tgz#540304261c330e80d0d5edce253a68cb3964218c" + integrity sha512-3g8lYefrRRzvGeSowdJKAKyks8oUpLEd/DyPV4eMhVlhJ0aNaZqIrNUIPuEWWTAoPqyFkfGrM67MC69baqn6vA== + dependencies: + fstream "^1.0.0" + glob "^7.0.3" + graceful-fs "^4.1.2" + mkdirp "^0.5.0" + nopt "2 || 3" + npmlog "0 || 1 || 2 || 3 || 4" + osenv "0" + request "^2.87.0" + rimraf "2" + semver "~5.3.0" + tar "^2.0.0" + which "1" + node-gyp@^5.0.2: version "5.1.1" resolved "https://registry.npmjs.org/node-gyp/-/node-gyp-5.1.1.tgz#eb915f7b631c937d282e33aed44cb7a025f62a3e" @@ -14034,6 +14183,22 @@ node-notifier@^8.0.0: uuid "^8.3.0" which "^2.0.2" +node-pre-gyp@^0.11.0: + version "0.11.0" + resolved "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.11.0.tgz#db1f33215272f692cd38f03238e3e9b47c5dd054" + integrity sha512-TwWAOZb0j7e9eGaf9esRx3ZcLaE5tQ2lvYy1pb5IAaG1a2e2Kv5Lms1Y4hpj+ciXJRofIxxlt5haeQ/2ANeE0Q== + dependencies: + detect-libc "^1.0.2" + mkdirp "^0.5.1" + needle "^2.2.1" + nopt "^4.0.1" + npm-packlist "^1.1.6" + npmlog "^4.0.2" + rc "^1.2.7" + rimraf "^2.6.1" + semver "^5.3.0" + tar "^4" + node-releases@^1.1.73: version "1.1.74" resolved "https://registry.npmjs.org/node-releases/-/node-releases-1.1.74.tgz#e5866488080ebaa70a93b91144ccde06f3c3463e" @@ -14086,6 +14251,13 @@ nodemon@^2.0.12: undefsafe "^2.0.3" update-notifier "^4.1.0" +"nopt@2 || 3": + version "3.0.6" + resolved "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" + integrity sha1-xkZdvwirzU2zWTF/eaxopkayj/k= + dependencies: + abbrev "1" + nopt@^4.0.1: version "4.0.3" resolved "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz#a375cad9d02fd921278d954c2254d5aa57e15e48" @@ -14218,7 +14390,7 @@ npm-normalize-package-bin@^1.0.0, npm-normalize-package-bin@^1.0.1: semver "^5.6.0" validate-npm-package-name "^3.0.0" -npm-packlist@^1.4.4: +npm-packlist@^1.1.6, npm-packlist@^1.4.4: version "1.4.8" resolved "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.8.tgz#56ee6cc135b9f98ad3d51c1c95da22bbb9b2ef3e" integrity sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A== @@ -14243,14 +14415,14 @@ npm-run-path@^2.0.0: dependencies: path-key "^2.0.0" -npm-run-path@^4.0.0: +npm-run-path@^4.0.0, npm-run-path@^4.0.1: version "4.0.1" resolved "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== dependencies: path-key "^3.0.0" -npmlog@^4.1.2: +"npmlog@0 || 1 || 2 || 3 || 4", npmlog@^4.0.2, npmlog@^4.1.2: version "4.1.2" resolved "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== @@ -14426,7 +14598,7 @@ onetime@^2.0.0: dependencies: mimic-fn "^1.0.0" -onetime@^5.1.0: +onetime@^5.1.0, onetime@^5.1.2: version "5.1.2" resolved "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== @@ -14462,6 +14634,21 @@ optionator@^0.9.1: type-check "^0.4.0" word-wrap "^1.2.3" +ora@^5.4.1: + version "5.4.1" + resolved "https://registry.nlark.com/ora/download/ora-5.4.1.tgz?cache=0&sync_timestamp=1631556513877&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fora%2Fdownload%2Fora-5.4.1.tgz#1b2678426af4ac4a509008e5e4ac9e9959db9e18" + integrity sha1-GyZ4Qmr0rEpQkAjl5KyemVnbnhg= + dependencies: + bl "^4.1.0" + chalk "^4.1.0" + cli-cursor "^3.1.0" + cli-spinners "^2.5.0" + is-interactive "^1.0.0" + is-unicode-supported "^0.1.0" + log-symbols "^4.1.0" + strip-ansi "^6.0.0" + wcwidth "^1.0.1" + ordered-read-streams@^1.0.0: version "1.0.1" resolved "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-1.0.1.tgz#77c0cb37c41525d64166d990ffad7ec6a0e1363e" @@ -14500,7 +14687,7 @@ os-tmpdir@^1.0.0, os-tmpdir@~1.0.2: resolved "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= -osenv@^0.1.4, osenv@^0.1.5: +osenv@0, osenv@^0.1.4, osenv@^0.1.5: version "0.1.5" resolved "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" integrity sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g== @@ -16793,7 +16980,7 @@ rc-virtual-list@^3.0.1, rc-virtual-list@^3.2.0: rc-resize-observer "^1.0.0" rc-util "^5.0.7" -rc@^1.2.8: +rc@^1.2.7, rc@^1.2.8: version "1.2.8" resolved "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== @@ -17217,7 +17404,7 @@ readable-stream@1.1.x: isarray "0.0.1" string_decoder "~0.10.x" -"readable-stream@2 || 3", readable-stream@3, readable-stream@^3.0.0, readable-stream@^3.0.2, readable-stream@^3.6.0: +"readable-stream@2 || 3", readable-stream@3, readable-stream@^3.0.0, readable-stream@^3.0.2, readable-stream@^3.4.0, readable-stream@^3.6.0: version "3.6.0" resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== @@ -17709,6 +17896,14 @@ restore-cursor@^2.0.0: onetime "^2.0.0" signal-exit "^3.0.2" +restore-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.nlark.com/restore-cursor/download/restore-cursor-3.1.0.tgz?cache=0&sync_timestamp=1629746923086&other_urls=https%3A%2F%2Fregistry.nlark.com%2Frestore-cursor%2Fdownload%2Frestore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" + integrity sha1-OfZ8VLOnpYzqUjbZXPADQjljH34= + dependencies: + onetime "^5.1.0" + signal-exit "^3.0.2" + ret@~0.1.10: version "0.1.15" resolved "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" @@ -17748,6 +17943,13 @@ right-align@^0.1.1: dependencies: align-text "^0.1.1" +rimraf@2, rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.2, rimraf@^2.6.3: + version "2.7.1" + resolved "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" + integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== + dependencies: + glob "^7.1.3" + rimraf@2.6.3: version "2.6.3" resolved "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" @@ -17755,13 +17957,6 @@ rimraf@2.6.3: dependencies: glob "^7.1.3" -rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.2, rimraf@^2.6.3: - version "2.7.1" - resolved "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" - integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== - dependencies: - glob "^7.1.3" - rimraf@^3.0.0, rimraf@^3.0.2: version "3.0.2" resolved "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" @@ -18239,6 +18434,11 @@ semver@7.x, semver@^7.2, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7. dependencies: lru-cache "^6.0.0" +semver@~5.3.0: + version "5.3.0" + resolved "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" + integrity sha1-myzl094C0XxgEq0yaqa00M9U+U8= + semver@~7.2.0: version "7.2.3" resolved "https://registry.npmjs.org/semver/-/semver-7.2.3.tgz#3641217233c6382173c76bf2c7ecd1e1c16b0d8a" @@ -18254,6 +18454,25 @@ sequelize-pool@^6.0.0: resolved "https://registry.npmjs.org/sequelize-pool/-/sequelize-pool-6.1.0.tgz#caaa0c1e324d3c2c3a399fed2c7998970925d668" integrity sha512-4YwEw3ZgK/tY/so+GfnSgXkdwIJJ1I32uZJztIEgZeAO6HMgj64OzySbWLgxj+tXhZCJnzRfkY9gINw8Ft8ZMg== +sequelize@6.7.0: + version "6.7.0" + resolved "https://registry.npmmirror.com/sequelize/download/sequelize-6.7.0.tgz#6af3149ad9b39c4bfc8b563f25e1598e2a0cf881" + integrity sha1-avMUmtmznEv8i1Y/JeFZjioM+IE= + dependencies: + debug "^4.1.1" + dottie "^2.0.0" + inflection "1.13.1" + lodash "^4.17.20" + moment "^2.26.0" + moment-timezone "^0.5.31" + retry-as-promised "^3.2.0" + semver "^7.3.2" + sequelize-pool "^6.0.0" + toposort-class "^1.0.1" + uuid "^8.1.0" + validator "^13.6.0" + wkx "^0.5.0" + sequelize@^6.3.3: version "6.6.5" resolved "https://registry.npmjs.org/sequelize/-/sequelize-6.6.5.tgz#6f618e99f3df1fc81f28709e8a3139cec3ef1f0c" @@ -18685,6 +18904,16 @@ sprintf-js@~1.0.2: resolved "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= +sqlite3@^5.0.2: + version "5.0.2" + resolved "https://registry.npmjs.org/sqlite3/-/sqlite3-5.0.2.tgz#00924adcc001c17686e0a6643b6cbbc2d3965083" + integrity sha512-1SdTNo+BVU211Xj1csWa8lV6KM0CtucDwRyA0VHl91wEH1Mgh7RxUpI4rVvG7OhHrzCSGaVyW5g8vKvlrk9DJA== + dependencies: + node-addon-api "^3.0.0" + node-pre-gyp "^0.11.0" + optionalDependencies: + node-gyp "3.x" + sqlstring@^2.3.2: version "2.3.2" resolved "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.2.tgz#cdae7169389a1375b18e885f2e60b3e460809514" @@ -19229,6 +19458,28 @@ tapable@^1.0.0: resolved "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2" integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA== +tar@^2.0.0: + version "2.2.2" + resolved "https://registry.npmjs.org/tar/-/tar-2.2.2.tgz#0ca8848562c7299b8b446ff6a4d60cdbb23edc40" + integrity sha512-FCEhQ/4rE1zYv9rYXJw/msRqsnmlje5jHP6huWeBZ704jUTy02c5AZyWujpMR1ax6mVw9NyJMfuK2CMDWVIfgA== + dependencies: + block-stream "*" + fstream "^1.0.12" + inherits "2" + +tar@^4: + version "4.4.19" + resolved "https://registry.npmjs.org/tar/-/tar-4.4.19.tgz#2e4d7263df26f2b914dee10c825ab132123742f3" + integrity sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA== + dependencies: + chownr "^1.1.4" + fs-minipass "^1.2.7" + minipass "^2.9.0" + minizlib "^1.3.3" + mkdirp "^0.5.5" + safe-buffer "^5.2.1" + yallist "^3.1.1" + tar@^4.4.10, tar@^4.4.12, tar@^4.4.8: version "4.4.17" resolved "https://registry.npmjs.org/tar/-/tar-4.4.17.tgz#44be5e3fa8353ee1d11db3b1401561223a5c3985" @@ -20599,7 +20850,7 @@ warning@^4.0.1, warning@^4.0.3: dependencies: loose-envify "^1.0.0" -wcwidth@^1.0.0: +wcwidth@^1.0.0, wcwidth@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" integrity sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g= @@ -20699,7 +20950,7 @@ which-module@^2.0.0: resolved "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= -which@^1.2.9, which@^1.3.0, which@^1.3.1: +which@1, which@^1.2.9, which@^1.3.0, which@^1.3.1: version "1.3.1" resolved "https://registry.npmjs.org/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==