mirror of
https://github.com/nocobase/nocobase
synced 2024-11-15 03:36:05 +00:00
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 <chenlinxh@gmail.com>
This commit is contained in:
parent
12b3590845
commit
05ecb25d1b
2
.gitignore
vendored
2
.gitignore
vendored
@ -20,3 +20,5 @@ uploads/
|
||||
/.idea
|
||||
|
||||
.vercel
|
||||
packages/database/src/__tests__/.db
|
||||
db.sqlite
|
25
README.md
25
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
|
||||
~~~
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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",
|
||||
|
@ -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`);
|
||||
|
@ -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"
|
||||
|
@ -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,
|
||||
|
@ -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('<project-directory>')
|
||||
.usage(`${chalk.green('<project-directory>')}`)
|
||||
.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);
|
||||
|
@ -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();
|
||||
};
|
||||
|
28
packages/create-nocobase-app/src/utils/index.ts
Normal file
28
packages/create-nocobase-app/src/utils/index.ts
Normal file
@ -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']);
|
||||
}
|
@ -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=
|
||||
|
47
packages/create-nocobase-app/templates/AppGenerator/.env.tpl
Normal file
47
packages/create-nocobase-app/templates/AppGenerator/.env.tpl
Normal file
@ -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=
|
@ -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' },
|
||||
},
|
||||
|
@ -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 }}}",
|
||||
|
@ -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}}
|
@ -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`);
|
||||
});
|
||||
|
@ -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",
|
||||
|
@ -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']] });
|
||||
|
@ -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');
|
||||
|
@ -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
|
||||
}`;
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
}
|
||||
|
@ -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);
|
||||
});
|
||||
|
@ -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: {
|
||||
|
@ -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) {
|
||||
|
@ -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<M extends Model> = typeof Model & { new(): M } & { [key: string]: any };
|
||||
export type ModelCtor<M extends Model> = typeof Model & { new (): M } & {
|
||||
[key: string]: any;
|
||||
};
|
||||
|
||||
export default Model;
|
||||
|
@ -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);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -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<string, any>();
|
||||
|
||||
@ -45,15 +43,15 @@ export function getRegisteredModel(key) {
|
||||
return key;
|
||||
}
|
||||
|
||||
export interface TableOptions extends Omit<ModelOptions<Model>, 'name' | 'modelName'> {
|
||||
|
||||
export interface TableOptions
|
||||
extends Omit<ModelOptions<Model>, '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<ModelOptions<Model>, '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<string>();
|
||||
|
||||
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<string | ModelIndexesOptions>, reinitialize: Reinitialize = true) {
|
||||
public addIndexes(
|
||||
indexes: Array<string | ModelIndexesOptions>,
|
||||
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 = [];
|
||||
|
@ -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') {
|
||||
|
65
packages/plugin-client/src/__tests__/sqlite.test.ts
Normal file
65
packages/plugin-client/src/__tests__/sqlite.test.ts
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
@ -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),
|
||||
|
@ -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),
|
||||
|
@ -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),
|
||||
|
@ -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'),
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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),
|
||||
|
@ -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),
|
||||
|
@ -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'),
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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'),
|
||||
|
@ -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'),
|
||||
|
5
packages/plugin-client/src/db/postgres/setinval.sql
Normal file
5
packages/plugin-client/src/db/postgres/setinval.sql
Normal file
@ -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);
|
@ -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;
|
||||
|
@ -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;
|
||||
});
|
||||
|
||||
|
@ -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',
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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<DefaultState, DefaultContext>(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<NewStateT = {}, NewContextT = {}>(
|
||||
@ -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);
|
||||
|
100
packages/server/src/helper.ts
Normal file
100
packages/server/src/helper.ts
Normal file
@ -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<DefaultState, DefaultContext>(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());
|
||||
}
|
305
yarn.lock
305
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==
|
||||
|
Loading…
Reference in New Issue
Block a user