feat: init external data source support (#785)

Co-authored-by: sealday <sealday@gmail.com>
Reviewed-on: daoyoucloud/tachybase#785
This commit is contained in:
sealday 2024-04-20 17:41:23 +08:00
parent 99b79d87ad
commit cd9c2e1e8c
21 changed files with 875 additions and 17 deletions

View File

@ -0,0 +1,2 @@
/node_modules
/src

View File

@ -0,0 +1 @@
# @hera/plugin-external-data-source

View File

@ -0,0 +1,2 @@
export * from './dist/client';
export { default } from './dist/client';

View File

@ -0,0 +1 @@
module.exports = require('./dist/client/index.js');

View File

@ -0,0 +1,20 @@
{
"name": "@hera/plugin-external-data-source",
"version": "0.0.1",
"main": "dist/server/index.js",
"dependencies": {
"antd": "5.16.1",
"lodash": "^4.17.21",
"pg": "^8.11.3",
"react-i18next": "^11.15.1"
},
"devDependencies": {
"@types/lodash": "^4.17.0"
},
"peerDependencies": {
"@nocobase/client": "workspace:*",
"@nocobase/plugin-data-source-manager": "workspace:*",
"@nocobase/server": "workspace:*",
"@nocobase/test": "workspace:*"
}
}

View File

@ -0,0 +1,2 @@
export * from './dist/server';
export { default } from './dist/server';

View File

@ -0,0 +1 @@
module.exports = require('./dist/server/index.js');

View File

@ -0,0 +1,110 @@
import React from 'react';
import { Plugin, SchemaComponent } from '@nocobase/client';
import {} from '@nocobase/client';
import { PluginDataSourceManagerClient } from '@nocobase/plugin-data-source-manager/client';
import { Space } from 'antd';
import { generateNTemplate as tval, usePluginTranslation } from './locale';
const DataSourceSettingsForm = () => {
const { t } = usePluginTranslation();
return (
<SchemaComponent
scope={{ t }}
components={{ Space }}
schema={{
type: 'object',
properties: {
key: {
type: 'string',
title: tval('Data source name'),
required: true,
'x-decorator': 'FormItem',
'x-component': 'Input',
'x-validator': 'uid',
'x-disabled': '{{ createOnly }}',
description: tval(
'Randomly generated and can be modified. Support letters, numbers and underscores, must start with an letter.',
),
},
displayName: {
type: 'string',
title: tval('Data source display name'),
required: !0,
'x-decorator': 'FormItem',
'x-component': 'Input',
},
options: {
type: 'object',
properties: {
host: {
type: 'string',
title: tval('Host'),
required: !0,
'x-decorator': 'FormItem',
'x-component': 'Input',
default: 'localhost',
},
port: {
type: 'string',
title: tval('Port'),
required: !0,
'x-decorator': 'FormItem',
'x-component': 'Input',
default: 5432,
},
database: {
type: 'string',
title: tval('Database'),
required: !0,
'x-decorator': 'FormItem',
'x-component': 'Input',
},
username: {
type: 'string',
title: tval('Username'),
required: !0,
'x-decorator': 'FormItem',
'x-component': 'Input',
},
password: {
type: 'string',
title: tval('Password'),
'x-decorator': 'FormItem',
'x-component': 'Password',
},
tablePrefix: {
type: 'string',
title: tval('Table prefix'),
'x-decorator': 'FormItem',
'x-component': 'Input',
},
schema: {
type: 'string',
title: tval('Schema'),
'x-decorator': 'FormItem',
'x-component': 'Input',
},
},
},
enabled: {
type: 'string',
'x-content': tval('Enabled the data source'),
'x-decorator': 'FormItem',
'x-component': 'Checkbox',
default: true,
},
},
}}
/>
);
};
export class PluginExternalDataSourceClient extends Plugin {
async load() {
this.app.pm
.get(PluginDataSourceManagerClient)
.registerType('postgres', { DataSourceSettingsForm, label: tval('PostgreSQL') });
}
}
export default PluginExternalDataSourceClient;

View File

@ -0,0 +1,14 @@
import { i18n } from '@nocobase/client';
import { useTranslation } from 'react-i18next';
export const NAMESPACE = '@hera/plugin-external-data-source';
export function lang(key: string, options = {}) {
return i18n.t(key, { ...options, ns: NAMESPACE });
}
export function generateNTemplate(key: string) {
return `{{t('${key}', { ns: '${NAMESPACE}', nsMode: 'fallback' })}}`;
}
export function usePluginTranslation() {
return useTranslation(NAMESPACE);
}

View File

@ -0,0 +1,2 @@
export * from './server';
export { default } from './server';

View File

@ -0,0 +1,26 @@
{
"Data source name": "Data source name",
"Data source display name": "Data source display name",
"Host": "Host",
"Port": "Port",
"Database": "Database",
"Database connections": "Database connections",
"Test Connection": "Test Connection",
"Connection successful'": "Connection successful'",
"Display name": "Display name",
"Username": "Username",
"Password": "Password",
"Database dialaect": "Database dialaect",
"Description": "Description",
"Storage": "Storage",
"Collections": "Collections",
"Permissions": "Permissions",
"Allow adding and modifying collection": "Allow adding and modifying collection",
"Unknown field type": "Unknown field type",
"The following field types are not compatible and do not support output and display": "The following field types are not compatible and do not support output and display",
"Field database type": "Field database type",
"Postgres": "Postgres",
"Enabled the data source": "Enabled the data source",
"Table prefix": "Table prefix",
"Randomly generated and can be modified. Support letters, numbers and underscores, must start with an letter.": "Randomly generated and can be modified. Support letters, numbers and underscores, must start with an letter."
}

View File

@ -0,0 +1,26 @@
{
"Data source name": "数据源标识",
"Data source display name": "数据源名称",
"Host": "服务器地址",
"Port": "端口",
"Database": "数据库",
"Database connections": "连接第三方数据库",
"Test Connection": "测试连接",
"Connection successful'": "连接成功",
"Display name": "名称",
"Username": "用户名",
"Password": "密码",
"Database dialaect": "数据库",
"Description": "描述",
"Storage": "文件地址",
"Collections": "数据表",
"Permissions": "权限",
"Allow adding and modifying collection": "允许添加和修改数据表结构",
"Unknown field type": "未知字段类型",
"The following field types are not compatible and do not support output and display": "以下字段类型未适配,不支持输出和显示",
"Field database type": "字段数据库类型",
"Postgres":"PostgreSQL",
"Enabled the data source":"启用数据源",
"Table prefix":"表前缀",
"Randomly generated and can be modified. Support letters, numbers and underscores, must start with an letter.": "随机生成,可修改。支持英文、数字和下划线,必须以英文字母开头。"
}

View File

@ -0,0 +1 @@
export { default } from './plugin';

View File

@ -0,0 +1,28 @@
import { Plugin } from '@nocobase/server';
import { PostgresDataSource } from './postgres-data-source';
export class PluginExternalDataSourceServer extends Plugin {
async afterAdd() {}
async beforeLoad() {
this.app.dataSourceManager.factory.register('postgres', PostgresDataSource);
}
async beforeEnable() {
const plugin = this.pm.get('data-source-manager');
if (!plugin.enabled) {
throw new Error(`${this.name} plugin need data-source-manager plugin enabled`);
}
}
async load() {}
async install() {}
async afterEnable() {}
async afterDisable() {}
async remove() {}
}
export default PluginExternalDataSourceServer;

View File

@ -0,0 +1,2 @@
import { SequelizeCollectionManager } from '@nocobase/data-source-manager';
export class PostgresCollectionManager extends SequelizeCollectionManager {}

View File

@ -0,0 +1,94 @@
import { DataSource } from '@nocobase/data-source-manager';
import { Database } from '@nocobase/database';
import { PostgresCollectionManager } from './postgres-collection-manager';
import { DatabaseIntrospector } from './services/database-introspector';
import typeInterfaceMap from './services/type-interface-map';
export class PostgresDataSource extends DataSource {
static async testConnection(options) {
const database = new Database({
dialect: 'postgres',
...options,
});
try {
await database.sequelize.authenticate();
if (options.schema) {
const queryRes = await database.sequelize.query(
`
SELECT schema_name
FROM information_schema.schemata
WHERE schema_name = $1;
`,
{
bind: [options.schema],
type: 'SELECT',
},
);
// @ts-ignore
if (queryRes.length === 0) {
throw new Error(`schema ${options.schema} not exists`);
}
}
} catch (e) {
throw e;
} finally {
await database.close();
}
return true;
}
async load(options) {
// @ts-ignore
const db = this.collectionManager.db;
const introspector = new DatabaseIntrospector({
db,
typeInterfaceMap,
});
introspector.on('loadMessage', ({ message }) => {
this.emit('loadMessage', { message });
});
const { localData } = options;
const collections = await introspector.getCollections({
localData,
});
const delayFields = /* @__PURE__ */ new Map();
for (const collection of collections) {
try {
const fields = collection.fields;
for (const field of fields) {
if (field.type === 'belongsToMany') {
if (!delayFields.has(collection.name)) {
delayFields.set(collection.name, []);
}
delayFields.get(collection.name).push(field);
}
}
this.collectionManager.defineCollection({
...collection,
fields: fields.filter((field) => field.type !== 'belongsToMany'),
introspected: true,
});
} catch (e) {
db.logger.error(`load collection failed, ${e}`);
delayFields.delete(collection.name);
}
}
for (const [name, fields] of delayFields.entries()) {
try {
for (const field of fields) {
this.collectionManager.getCollection(name).setField(field.name, field);
}
} catch (e) {
db.logger.error(`load belongs to many field failed, ${e}`);
}
}
}
createCollectionManager(options) {
const database = new Database({
dialect: 'postgres',
...options,
});
return new PostgresCollectionManager({
database,
});
}
}

View File

@ -0,0 +1,252 @@
import { ViewFieldInference } from '@nocobase/database';
import { mergeOptions } from '@nocobase/plugin-data-source-manager';
import { EventEmitter } from 'events';
import _ from 'lodash';
import { showUniqueIndexes } from './utils';
export class DatabaseIntrospector extends EventEmitter {
db;
typeInterfaceMap;
constructor(options) {
super();
this.db = options.db;
this.typeInterfaceMap = options.typeInterfaceMap;
}
async getCollections(options = {}) {
let tableList = await this.db.sequelize.getQueryInterface().showAllTables();
const views = (await this.db.queryInterface.listViews()).map((view) => view.name);
tableList = tableList.concat(views);
if (this.db.options.tablePrefix) {
tableList = tableList.filter((tableName) => {
return tableName.startsWith(this.db.options.tablePrefix);
});
}
const batchSize = 5;
const results = [];
for (let i = 0; i < tableList.length; i += batchSize) {
const batch = tableList.slice(i, i + batchSize);
const batchResults = await Promise.all(
batch.map(async (tableName) => {
const tableInfo = {
tableName,
};
this.emit('loadMessage', {
message: `load table ${tableName}`,
});
const collectionOptions = this.tableInfoToCollectionOptions(tableInfo);
// @ts-ignore
const localOptions = options.localData?.[collectionOptions.name];
try {
return await this.getCollection({
tableInfo,
localOptions,
mergedOptions: views.includes(tableName) ? { view: true, template: 'view' } : {},
});
} catch (e) {
if (e.message.includes('No description found for')) {
return false;
}
this.db.logger.error(`load table ${tableName} failed, ${e.message}`);
return false;
}
}),
);
results.push(...batchResults);
}
return results.filter(Boolean);
}
async getPrimaryKeysOfTable(tableInfo) {
const sql = `
SELECT
pg_attribute.attname,
format_type(pg_attribute.atttypid, pg_attribute.atttypmod)
FROM pg_index, pg_class, pg_attribute, pg_namespace
WHERE
pg_class.oid = '${tableInfo.tableName}'::regclass AND
indrelid = pg_class.oid AND
nspname = '${tableInfo.schema || 'public'}' AND
pg_class.relnamespace = pg_namespace.oid AND
pg_attribute.attrelid = pg_class.oid AND
pg_attribute.attnum = any(pg_index.indkey)
AND indisprimary
`;
return await this.db.sequelize.query(sql, {
type: 'SELECT',
});
}
async getCollection(options) {
const { tableInfo } = options;
const columnsInfo = await this.db.sequelize.getQueryInterface().describeTable(tableInfo);
const primaryKeys = await this.getPrimaryKeysOfTable(tableInfo);
Object.keys(columnsInfo).forEach((columnName) => {
const columnInfo = columnsInfo[columnName];
const primaryKey = primaryKeys.find((key) => key.attname === columnName);
if (primaryKey) {
columnInfo.primaryKey = true;
}
});
const collectionOptions = this.tableInfoToCollectionOptions(tableInfo);
const constraints = await this.db.sequelize.query(showUniqueIndexes(tableInfo), {
type: 'SELECT',
});
try {
const fields = Object.keys(columnsInfo).map((columnName) => {
return this.columnInfoToFieldOptions(columnsInfo, columnName, constraints);
});
const unsupportedFields = fields.filter((field) => {
// @ts-ignore
return field.supported === false;
});
const supportFields = fields.filter((field) => {
// @ts-ignore
return field.supported !== false;
});
const remoteCollectionInfo = {
...collectionOptions,
...((options == null ? void 0 : options.mergedOptions) || {}),
...this.collectionOptionsByFields(supportFields),
fields: [...supportFields],
};
if (unsupportedFields.length) {
remoteCollectionInfo.unsupportedFields = unsupportedFields;
}
const finalOptions = this.mergeLocalDataIntoCollectionOptions(remoteCollectionInfo, options.localOptions);
if (finalOptions.view && !finalOptions.filterTargetKey && supportFields.find((field) => field.name === 'id')) {
finalOptions.filterTargetKey = 'id';
}
return finalOptions;
} catch (e) {
throw new Error(`table ${tableInfo.tableName} introspection error: ${e.message}`);
}
}
loadCollection(options) {
this.db.collection({
...options,
introspected: true,
});
}
loadCollections(options) {
options.collections.forEach((collection) => {
this.loadCollection(collection);
});
}
tableInfoToCollectionOptions(tableInfo) {
const tableName = tableInfo.tableName;
let name = tableName;
if (this.db.options.tablePrefix) {
name = tableName.replace(this.db.options.tablePrefix, '');
}
return {
name,
title: name,
schema: tableInfo.schema,
tableName,
};
}
collectionOptionsByFields(fields) {
const options = {
timestamps: false,
autoGenId: false,
filterTargetKey: undefined,
};
const autoIncrementField = fields.find((field) => field.autoIncrement);
if (autoIncrementField) {
options.filterTargetKey = autoIncrementField.name;
}
const primaryKeys = fields.filter((field) => field.primaryKey);
if (!options.filterTargetKey && primaryKeys.length === 1) {
options.filterTargetKey = primaryKeys[0].name;
}
const uniques = fields.filter((field) => field.unique);
if (!options.filterTargetKey && uniques.length === 1) {
options.filterTargetKey = uniques[0].name;
}
return options;
}
mergeLocalDataIntoCollectionOptions(collectionOptions, localData) {
if (!localData) {
return collectionOptions;
}
const collectionFields = collectionOptions.fields || [];
const localFieldsAsObject = _.keyBy(localData.fields, 'name');
const newFields = collectionFields.map((field) => {
const localField = localFieldsAsObject[field.name];
if (!localField) {
return field;
}
return mergeOptions(field, localField);
});
const localAssociationFields = localData.fields?.filter((field) => {
return ['belongsTo', 'belongsToMany', 'hasMany', 'hasOne'].includes(field.type);
});
if (localAssociationFields) {
newFields.push(...localAssociationFields);
}
return {
...collectionOptions,
..._.omit(localData, ['fields']),
fields: newFields,
};
}
columnInfoToFieldOptions(columnsInfo, columnName, indexes) {
const columnInfo = columnsInfo[columnName];
let fieldOptions = {
...this.columnAttribute(columnsInfo, columnName, indexes),
...ViewFieldInference.inferToFieldType({
dialect: this.db.options.dialect,
type: columnInfo.type,
name: columnName,
}),
rawType: columnInfo.type,
name: columnName,
};
const interfaceConfig = this.getDefaultInterfaceByType(columnsInfo, columnName, fieldOptions.type);
if (typeof interfaceConfig === 'string') {
// @ts-ignore
fieldOptions.interface = interfaceConfig;
} else {
fieldOptions = {
...fieldOptions,
...interfaceConfig,
};
}
if (!fieldOptions.type) {
return {
rawType: columnInfo.type,
name: columnName,
supported: false,
};
}
_.set(fieldOptions, 'uiSchema.title', columnName);
return fieldOptions;
}
getDefaultInterfaceByType(columnsInfo, columnName, type) {
const interfaceConfig = this.typeInterfaceMap[type];
if (typeof interfaceConfig === 'function') {
return interfaceConfig(columnsInfo[columnName]);
}
return interfaceConfig;
}
columnAttribute(columnsInfo, columnName, indexes) {
const columnInfo = columnsInfo[columnName];
const attr = {
type: columnInfo.type,
allowNull: columnInfo.allowNull,
primaryKey: columnInfo.primaryKey,
unique: false,
autoIncrement: false,
};
if (columnInfo.defaultValue && typeof columnInfo.defaultValue === 'string') {
const isSerial = columnInfo.defaultValue.match(/^nextval\(/);
const isUUID = columnInfo.defaultValue.match(/^uuid_generate_v4\(/);
if (isSerial || isUUID) {
attr.autoIncrement = true;
}
}
for (const index of indexes) {
if (index['is_single_column'] && index['column_name'] === columnName) {
attr.unique = true;
}
}
return attr;
}
}

View File

@ -0,0 +1,221 @@
const typeInterfaceMap = {
array: '',
belongsTo: '',
belongsToMany: '',
boolean: () => {
return {
interface: 'checkbox',
uiSchema: {
type: 'boolean',
'x-component': 'Checkbox',
},
};
},
context: '',
date: () => {
return {
interface: 'datetime',
uiSchema: {
'x-component': 'DatePicker',
'x-component-props': {
dateFormat: 'YYYY-MM-DD',
showTime: false,
},
},
};
},
hasMany: '',
hasOne: '',
json: () => {
return {
interface: 'json',
uiSchema: {
'x-component': 'Input.JSON',
'x-component-props': {
autoSize: {
minRows: 5,
// maxRows: 20,
},
},
default: null,
},
};
},
jsonb: () => {
return {
interface: 'json',
uiSchema: {
'x-component': 'Input.JSON',
'x-component-props': {
autoSize: {
minRows: 5,
// maxRows: 20,
},
},
default: null,
},
};
},
integer: () => ({
interface: 'integer',
// name,
uiSchema: {
type: 'number',
// title,
'x-component': 'InputNumber',
'x-component-props': {
stringMode: true,
step: '1',
},
'x-validator': 'integer',
},
}),
bigInt: (columnInfo) => {
return {
interface: 'integer',
uiSchema: {
'x-component': 'InputNumber',
'x-component-props': {
style: {
width: '100%',
},
},
},
};
},
float: () => {
return {
interface: 'number',
uiSchema: {
type: 'number',
// title,
'x-component': 'InputNumber',
'x-component-props': {
stringMode: true,
step: '1',
},
},
};
},
double: () => {
return {
interface: 'number',
uiSchema: {
type: 'number',
// title,
'x-component': 'InputNumber',
'x-component-props': {
stringMode: true,
step: '1',
},
},
};
},
real: () => {
return {
interface: 'number',
uiSchema: {
type: 'number',
// title,
'x-component': 'InputNumber',
'x-component-props': {
stringMode: true,
step: '1',
},
},
};
},
decimal: () => {
return {
interface: 'number',
uiSchema: {
type: 'number',
// title,
'x-component': 'InputNumber',
'x-component-props': {
stringMode: true,
step: '1',
},
},
};
},
password: () => ({
interface: 'password',
hidden: true,
// name,
uiSchema: {
type: 'string',
// title,
'x-component': 'Password',
},
}),
radio: '',
set: '',
sort: '',
string: () => {
return {
interface: 'input',
uiSchema: {
'x-component': 'Input',
'x-component-props': {
style: {
width: '100%',
},
},
},
};
},
text: () => {
return {
interface: 'textarea',
// name,
uiSchema: {
type: 'string',
'x-component': 'Input.TextArea',
},
};
},
time: () => ({
interface: 'time',
// name,
uiSchema: {
type: 'string',
'x-component': 'TimePicker',
'x-component-props': {
format: 'HH:mm:ss',
},
},
}),
uid: () => {
return {
interface: 'input',
uiSchema: {
'x-component': 'Input',
'x-component-props': {
style: {
width: '100%',
},
},
},
};
},
uuid: () => {
return {
interface: 'uuid',
uiSchema: {
'x-component': 'Input',
'x-component-props': {
style: {
width: '100%',
},
},
},
};
},
virtual: '',
point: '',
polygon: '',
lineString: '',
circle: '',
};
export default typeInterfaceMap;

View File

@ -0,0 +1,22 @@
export function showUniqueIndexes(tableInfo2) {
return `
SELECT
tc.constraint_schema,
tc.constraint_name,
tc.table_name,
kcu.column_name,
CASE WHEN (SELECT COUNT(*)
FROM information_schema.key_column_usage kcu2
WHERE kcu2.constraint_name = tc.constraint_name
AND kcu2.table_schema = tc.constraint_schema) = 1 THEN TRUE
ELSE FALSE END AS is_single_column
FROM information_schema.table_constraints AS tc
JOIN information_schema.key_column_usage AS kcu
ON tc.constraint_name = kcu.constraint_name
AND tc.table_schema = kcu.table_schema
WHERE tc.constraint_type = 'UNIQUE'
AND tc.table_name = '${tableInfo2.tableName}'
AND tc.table_schema = '${tableInfo2.schema || 'public'}'
ORDER BY tc.constraint_name, kcu.column_name;
`;
}

View File

@ -1541,6 +1541,37 @@ importers:
specifier: ^8.13.0
version: 8.15.1
packages/plugins/@hera/plugin-external-data-source:
dependencies:
'@nocobase/client':
specifier: workspace:*
version: link:../../../core/client
'@nocobase/plugin-data-source-manager':
specifier: workspace:*
version: link:../../@nocobase/plugin-data-source-manager
'@nocobase/server':
specifier: workspace:*
version: link:../../../core/server
'@nocobase/test':
specifier: workspace:*
version: link:../../../core/test
antd:
specifier: 5.16.1
version: 5.16.1(react-dom@18.2.0)(react@18.2.0)
lodash:
specifier: ^4.17.21
version: 4.17.21
pg:
specifier: ^8.11.3
version: 8.11.3
react-i18next:
specifier: ^11.15.1
version: 11.18.6(i18next@22.5.1)(react-dom@18.2.0)(react@18.2.0)
devDependencies:
'@types/lodash':
specifier: ^4.17.0
version: 4.17.0
packages/plugins/@hera/plugin-mobile:
dependencies:
'@nocobase/client':
@ -4695,7 +4726,7 @@ packages:
'@emotion/unitless': 0.7.5
classnames: 2.5.1
csstype: 3.1.3
rc-util: 5.38.1(react-dom@18.2.0)(react@18.2.0)
rc-util: 5.39.1(react-dom@18.2.0)(react@18.2.0)
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
stylis: 4.3.1
@ -4768,7 +4799,7 @@ packages:
react-dom: '>=17.0.0'
dependencies:
'@ant-design/cssinjs': 1.19.0(react-dom@18.2.0)(react@18.2.0)
'@babel/runtime': 7.24.1
'@babel/runtime': 7.24.4
'@ctrl/tinycolor': 3.6.1
antd: 5.16.1(react-dom@18.2.0)(react@18.2.0)
rc-util: 5.39.1(react-dom@18.2.0)(react@18.2.0)
@ -4785,7 +4816,7 @@ packages:
dependencies:
'@ant-design/icons': 5.3.6(react-dom@18.2.0)(react@18.2.0)
'@ant-design/pro-provider': 2.13.5(antd@5.16.1)(react-dom@18.2.0)(react@18.2.0)
'@babel/runtime': 7.24.1
'@babel/runtime': 7.24.4
antd: 5.16.1(react-dom@18.2.0)(react@18.2.0)
classnames: 2.5.1
dayjs: 1.11.10
@ -7275,7 +7306,7 @@ packages:
/@changesets/apply-release-plan@7.0.0:
resolution: {integrity: sha512-vfi69JR416qC9hWmFGSxj7N6wA5J222XNBmezSVATPWDVPIF7gkd4d8CpbEbXmRWbVrkoli3oerGS6dcL/BGsQ==}
dependencies:
'@babel/runtime': 7.24.0
'@babel/runtime': 7.24.4
'@changesets/config': 3.0.0
'@changesets/get-version-range-type': 0.4.0
'@changesets/git': 3.0.0
@ -7293,7 +7324,7 @@ packages:
/@changesets/assemble-release-plan@6.0.0:
resolution: {integrity: sha512-4QG7NuisAjisbW4hkLCmGW2lRYdPrKzro+fCtZaILX+3zdUELSvYjpL4GTv0E4aM9Mef3PuIQp89VmHJ4y2bfw==}
dependencies:
'@babel/runtime': 7.24.0
'@babel/runtime': 7.24.4
'@changesets/errors': 0.2.0
'@changesets/get-dependents-graph': 2.0.0
'@changesets/types': 6.0.0
@ -7376,7 +7407,7 @@ packages:
/@changesets/get-release-plan@4.0.0:
resolution: {integrity: sha512-9L9xCUeD/Tb6L/oKmpm8nyzsOzhdNBBbt/ZNcjynbHC07WW4E1eX8NMGC5g5SbM5z/V+MOrYsJ4lRW41GCbg3w==}
dependencies:
'@babel/runtime': 7.24.0
'@babel/runtime': 7.24.4
'@changesets/assemble-release-plan': 6.0.0
'@changesets/config': 3.0.0
'@changesets/pre': 2.0.0
@ -7392,7 +7423,7 @@ packages:
/@changesets/git@3.0.0:
resolution: {integrity: sha512-vvhnZDHe2eiBNRFHEgMiGd2CT+164dfYyrJDhwwxTVD/OW0FUD6G7+4DIx1dNwkwjHyzisxGAU96q0sVNBns0w==}
dependencies:
'@babel/runtime': 7.24.0
'@babel/runtime': 7.24.4
'@changesets/errors': 0.2.0
'@changesets/types': 6.0.0
'@manypkg/get-packages': 1.1.3
@ -7417,7 +7448,7 @@ packages:
/@changesets/pre@2.0.0:
resolution: {integrity: sha512-HLTNYX/A4jZxc+Sq8D1AMBsv+1qD6rmmJtjsCJa/9MSRybdxh0mjbTvE6JYZQ/ZiQ0mMlDOlGPXTm9KLTU3jyw==}
dependencies:
'@babel/runtime': 7.24.0
'@babel/runtime': 7.24.4
'@changesets/errors': 0.2.0
'@changesets/types': 6.0.0
'@manypkg/get-packages': 1.1.3
@ -7427,7 +7458,7 @@ packages:
/@changesets/read@0.6.0:
resolution: {integrity: sha512-ZypqX8+/im1Fm98K4YcZtmLKgjs1kDQ5zHpc2U1qdtNBmZZfo/IBiG162RoP0CUF05tvp2y4IspH11PLnPxuuw==}
dependencies:
'@babel/runtime': 7.24.0
'@babel/runtime': 7.24.4
'@changesets/git': 3.0.0
'@changesets/logger': 0.1.0
'@changesets/parse': 0.4.0
@ -7448,7 +7479,7 @@ packages:
/@changesets/write@0.3.0:
resolution: {integrity: sha512-slGLb21fxZVUYbyea+94uFiD6ntQW0M2hIKNznFizDhZPDgn2c/fv1UzzlW43RVzh1BEDuIqW6hzlJ1OflNmcw==}
dependencies:
'@babel/runtime': 7.24.0
'@babel/runtime': 7.24.4
'@changesets/types': 6.0.0
fs-extra: 7.0.1
human-id: 1.0.2
@ -7899,7 +7930,7 @@ packages:
'@types/react':
optional: true
dependencies:
'@babel/runtime': 7.24.1
'@babel/runtime': 7.24.4
'@emotion/babel-plugin': 11.11.0
'@emotion/cache': 11.11.0
'@emotion/serialize': 1.1.2
@ -9448,7 +9479,7 @@ packages:
/@manypkg/get-packages@1.1.3:
resolution: {integrity: sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A==}
dependencies:
'@babel/runtime': 7.24.0
'@babel/runtime': 7.24.4
'@changesets/types': 4.1.0
'@manypkg/find-root': 1.1.0
fs-extra: 8.1.0
@ -13164,7 +13195,7 @@ packages:
'@babel/plugin-transform-react-jsx-self': 7.24.1(@babel/core@7.24.4)
'@babel/plugin-transform-react-jsx-source': 7.24.1(@babel/core@7.24.4)
react-refresh: 0.14.0
vite: 4.5.2(@types/node@20.12.7)(less@4.1.3)
vite: 4.5.2(@types/node@20.12.2)(less@4.1.3)
transitivePeerDependencies:
- supports-color
@ -25270,7 +25301,7 @@ packages:
react: '*'
react-dom: '*'
dependencies:
'@babel/runtime': 7.24.0
'@babel/runtime': 7.24.4
'@rc-component/trigger': 1.18.3(react-dom@18.2.0)(react@18.2.0)
classnames: 2.5.1
rc-motion: 2.9.0(react-dom@18.2.0)(react@18.2.0)
@ -25880,7 +25911,7 @@ packages:
react-native:
optional: true
dependencies:
'@babel/runtime': 7.24.0
'@babel/runtime': 7.24.4
'@types/react-redux': 7.1.33
hoist-non-react-statics: 3.3.2
loose-envify: 1.4.0
@ -26190,7 +26221,7 @@ packages:
/redux@4.2.1:
resolution: {integrity: sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==}
dependencies:
'@babel/runtime': 7.24.0
'@babel/runtime': 7.24.4
/reflect-metadata@0.1.14:
resolution: {integrity: sha512-ZhYeb6nRaXCfhnndflDK8qI6ZQ/YcWZCISRAWICW9XYqMUwjZM9Z0DveWX/ABN01oxSHwVxKQmxeYZSsm0jh5A==}
@ -29754,7 +29785,6 @@ packages:
rollup: 3.29.4
optionalDependencies:
fsevents: 2.3.3
dev: false
/vite@4.5.2(@types/node@20.12.7)(less@4.1.3):
resolution: {integrity: sha512-tBCZBNSBbHQkaGyhGCDUGqeo2ph8Fstyp6FMSvTtsXeZSPpSMGlviAOav2hxVTqFcx8Hj/twtWKsMJXNY0xI8w==}
@ -29791,6 +29821,7 @@ packages:
rollup: 3.29.4
optionalDependencies:
fsevents: 2.3.3
dev: true
/vite@5.1.5(@types/node@20.12.7):
resolution: {integrity: sha512-BdN1xh0Of/oQafhU+FvopafUp6WaYenLU/NFoL5WyJL++GxkNfieKzBhM24H3HVsPQrlAqB7iJYTHabzaRed5Q==}