@nocobase/database 1.9.0-beta.8 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/collection.d.ts +4 -0
- package/lib/collection.js +51 -0
- package/lib/database.d.ts +2 -1
- package/lib/database.js +27 -0
- package/lib/dialects/mysql-dialect.d.ts +2 -1
- package/lib/dialects/mysql-dialect.js +5 -2
- package/lib/fields/field.d.ts +25 -2
- package/lib/fields/index.d.ts +3 -1
- package/lib/fields/index.js +3 -1
- package/lib/fields/number-field.d.ts +1 -1
- package/lib/fields/snowflake-id-field.d.ts +29 -0
- package/lib/fields/snowflake-id-field.js +79 -0
- package/lib/fields/string-field.d.ts +1 -1
- package/lib/index.d.ts +1 -0
- package/lib/index.js +3 -0
- package/lib/inherited-sync-runner.js +5 -0
- package/lib/operators/in.d.ts +10 -0
- package/lib/operators/in.js +40 -0
- package/lib/operators/index.js +2 -0
- package/lib/relation-repository/belongs-to-many-repository.js +4 -0
- package/lib/relation-repository/relation-repository.js +1 -0
- package/lib/repository.d.ts +1 -0
- package/lib/repository.js +5 -0
- package/lib/sync-runner.js +3 -0
- package/lib/update-associations.js +20 -0
- package/lib/utils/field-validation.d.ts +14 -0
- package/lib/utils/field-validation.js +85 -0
- package/package.json +5 -4
package/lib/collection.d.ts
CHANGED
|
@@ -88,6 +88,10 @@ export declare class Collection<TModelAttributes extends {} = any, TCreationAttr
|
|
|
88
88
|
get db(): Database;
|
|
89
89
|
get treeParentField(): BelongsToField | null;
|
|
90
90
|
get treeChildrenField(): HasManyField | null;
|
|
91
|
+
validate(options: {
|
|
92
|
+
values: Record<string, any> | Record<string, any>[];
|
|
93
|
+
operation: 'create' | 'update';
|
|
94
|
+
}): void;
|
|
91
95
|
isMultiFilterTargetKey(): boolean;
|
|
92
96
|
tableName(): any;
|
|
93
97
|
/**
|
package/lib/collection.js
CHANGED
|
@@ -53,9 +53,12 @@ var import_events = require("events");
|
|
|
53
53
|
var import_lodash = __toESM(require("lodash"));
|
|
54
54
|
var import_safe_json_stringify = __toESM(require("safe-json-stringify"));
|
|
55
55
|
var import_sequelize = require("sequelize");
|
|
56
|
+
var import_fields = require("./fields");
|
|
56
57
|
var import_model = require("./model");
|
|
57
58
|
var import_repository = require("./repository");
|
|
58
59
|
var import_utils = require("./utils");
|
|
60
|
+
var import_field_validation = require("./utils/field-validation");
|
|
61
|
+
var import_joi = __toESM(require("joi"));
|
|
59
62
|
function EnsureAtomicity(target, propertyKey, descriptor) {
|
|
60
63
|
const originalMethod = descriptor.value;
|
|
61
64
|
descriptor.value = function(...args) {
|
|
@@ -152,6 +155,54 @@ const _Collection = class _Collection extends import_events.EventEmitter {
|
|
|
152
155
|
}
|
|
153
156
|
}
|
|
154
157
|
}
|
|
158
|
+
validate(options) {
|
|
159
|
+
const { values: updateValues, operation } = options;
|
|
160
|
+
if (!updateValues) {
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
const values = Array.isArray(updateValues) ? updateValues : [updateValues];
|
|
164
|
+
const helper = /* @__PURE__ */ __name((field, value) => {
|
|
165
|
+
var _a, _b;
|
|
166
|
+
const val = value[field.name];
|
|
167
|
+
if (!field.options.validation) {
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
if (field instanceof import_fields.RelationField) {
|
|
171
|
+
const rules = ((_b = (_a = field.options) == null ? void 0 : _a.validation) == null ? void 0 : _b.rules) || [];
|
|
172
|
+
const required = rules.some((rule) => rule.name === "required");
|
|
173
|
+
if (required) {
|
|
174
|
+
const { error: error2 } = import_joi.default.any().empty(null).required().label(`${this.name}.${field.name}`).validate(val);
|
|
175
|
+
if (error2) {
|
|
176
|
+
throw error2;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
const joiSchema = (0, import_field_validation.buildJoiSchema)(field.options.validation, {
|
|
182
|
+
label: `${this.name}.${field.name}`,
|
|
183
|
+
value: val
|
|
184
|
+
});
|
|
185
|
+
const { error } = joiSchema.validate(val);
|
|
186
|
+
if (error) {
|
|
187
|
+
throw error;
|
|
188
|
+
}
|
|
189
|
+
}, "helper");
|
|
190
|
+
for (const value of values) {
|
|
191
|
+
if (operation === "create") {
|
|
192
|
+
for (const [, field] of this.fields) {
|
|
193
|
+
helper(field, value);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
if (operation === "update") {
|
|
197
|
+
for (const key of Object.keys(value)) {
|
|
198
|
+
const field = this.getField(key);
|
|
199
|
+
if (field) {
|
|
200
|
+
helper(field, value);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
155
206
|
isMultiFilterTargetKey() {
|
|
156
207
|
return Array.isArray(this.filterTargetKey) && this.filterTargetKey.length > 1;
|
|
157
208
|
}
|
package/lib/database.d.ts
CHANGED
|
@@ -118,7 +118,7 @@ export declare class Database extends EventEmitter implements AsyncEmitter {
|
|
|
118
118
|
/**
|
|
119
119
|
* @internal
|
|
120
120
|
*/
|
|
121
|
-
sequelizeOptions(options:
|
|
121
|
+
sequelizeOptions(options: DatabaseOptions): IDatabaseOptions;
|
|
122
122
|
/**
|
|
123
123
|
* @internal
|
|
124
124
|
*/
|
|
@@ -128,6 +128,7 @@ export declare class Database extends EventEmitter implements AsyncEmitter {
|
|
|
128
128
|
inDialect(...dialect: string[]): boolean;
|
|
129
129
|
isMySQLCompatibleDialect(): boolean;
|
|
130
130
|
isPostgresCompatibleDialect(): boolean;
|
|
131
|
+
wrapSequelizeRunForMySQL(): void;
|
|
131
132
|
/**
|
|
132
133
|
* Add collection to database
|
|
133
134
|
* @param options
|
package/lib/database.js
CHANGED
|
@@ -176,6 +176,9 @@ const _Database = class _Database extends import_events.EventEmitter {
|
|
|
176
176
|
);
|
|
177
177
|
const sequelizeOptions = this.sequelizeOptions(this.options);
|
|
178
178
|
this.sequelize = new import_sequelize.Sequelize(sequelizeOptions);
|
|
179
|
+
if (options.dialect === "mysql") {
|
|
180
|
+
this.wrapSequelizeRunForMySQL();
|
|
181
|
+
}
|
|
179
182
|
this.queryInterface = (0, import_query_interface_builder.default)(this);
|
|
180
183
|
this.collections = /* @__PURE__ */ new Map();
|
|
181
184
|
this.modelHook = new import_model_hook.ModelHook(this);
|
|
@@ -397,6 +400,30 @@ const _Database = class _Database extends import_events.EventEmitter {
|
|
|
397
400
|
isPostgresCompatibleDialect() {
|
|
398
401
|
return this.inDialect("postgres");
|
|
399
402
|
}
|
|
403
|
+
/*
|
|
404
|
+
* https://github.com/sidorares/node-mysql2/issues/1239#issuecomment-766867699
|
|
405
|
+
* https://github.com/sidorares/node-mysql2/pull/1407#issuecomment-1325789581
|
|
406
|
+
* > I'm starting to think simple "always send (parameter.toString()) as VAR_STRING" unless the type is explicitly specified by user" might be actually the best behaviour
|
|
407
|
+
*/
|
|
408
|
+
wrapSequelizeRunForMySQL() {
|
|
409
|
+
const that = this;
|
|
410
|
+
const run = this.sequelize.dialect.Query.prototype.run;
|
|
411
|
+
this.sequelize.dialect.Query.prototype.run = function(sql, parameters) {
|
|
412
|
+
if (!/^update\s+/i.test(sql.trim()) || !(parameters == null ? void 0 : parameters.length)) {
|
|
413
|
+
return run.apply(this, [sql, parameters]);
|
|
414
|
+
}
|
|
415
|
+
try {
|
|
416
|
+
parameters.forEach((p, index) => {
|
|
417
|
+
if (typeof p === "number") {
|
|
418
|
+
parameters[index] = p.toString();
|
|
419
|
+
}
|
|
420
|
+
});
|
|
421
|
+
} catch (err) {
|
|
422
|
+
that.logger.error(err);
|
|
423
|
+
}
|
|
424
|
+
return run.apply(this, [sql, parameters]);
|
|
425
|
+
};
|
|
426
|
+
}
|
|
400
427
|
/**
|
|
401
428
|
* Add collection to database
|
|
402
429
|
* @param options
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
8
|
*/
|
|
9
9
|
import { BaseDialect } from './base-dialect';
|
|
10
|
+
import { DatabaseOptions } from '../database';
|
|
10
11
|
export declare class MysqlDialect extends BaseDialect {
|
|
11
12
|
static dialectName: string;
|
|
12
13
|
getVersionGuard(): {
|
|
@@ -14,5 +15,5 @@ export declare class MysqlDialect extends BaseDialect {
|
|
|
14
15
|
get: (v: string) => string;
|
|
15
16
|
version: string;
|
|
16
17
|
};
|
|
17
|
-
getSequelizeOptions(options:
|
|
18
|
+
getSequelizeOptions(options: DatabaseOptions): import("../database").IDatabaseOptions;
|
|
18
19
|
}
|
|
@@ -32,7 +32,6 @@ __export(mysql_dialect_exports, {
|
|
|
32
32
|
MysqlDialect: () => MysqlDialect
|
|
33
33
|
});
|
|
34
34
|
module.exports = __toCommonJS(mysql_dialect_exports);
|
|
35
|
-
var import_utils = require("@nocobase/utils");
|
|
36
35
|
var import_base_dialect = require("./base-dialect");
|
|
37
36
|
const _MysqlDialect = class _MysqlDialect extends import_base_dialect.BaseDialect {
|
|
38
37
|
getVersionGuard() {
|
|
@@ -46,7 +45,11 @@ const _MysqlDialect = class _MysqlDialect extends import_base_dialect.BaseDialec
|
|
|
46
45
|
};
|
|
47
46
|
}
|
|
48
47
|
getSequelizeOptions(options) {
|
|
49
|
-
|
|
48
|
+
const dialectOptions = {
|
|
49
|
+
...options.dialectOptions || {},
|
|
50
|
+
multipleStatements: true
|
|
51
|
+
};
|
|
52
|
+
options.dialectOptions = dialectOptions;
|
|
50
53
|
return options;
|
|
51
54
|
}
|
|
52
55
|
};
|
package/lib/fields/field.d.ts
CHANGED
|
@@ -10,17 +10,39 @@ import { DataType, ModelAttributeColumnOptions, ModelIndexesOptions, SyncOptions
|
|
|
10
10
|
import { Collection } from '../collection';
|
|
11
11
|
import { Database } from '../database';
|
|
12
12
|
import { ModelEventTypes } from '../types';
|
|
13
|
+
import { BasicType, BooleanSchema, NumberSchema, ObjectSchema, StringSchema } from 'joi';
|
|
13
14
|
export interface FieldContext {
|
|
14
15
|
database: Database;
|
|
15
16
|
collection: Collection;
|
|
16
17
|
}
|
|
17
|
-
|
|
18
|
+
type RuleSchemaMap = {
|
|
19
|
+
string: StringSchema;
|
|
20
|
+
boolean: BooleanSchema;
|
|
21
|
+
number: NumberSchema;
|
|
22
|
+
object: ObjectSchema;
|
|
23
|
+
};
|
|
24
|
+
export type FieldValidationRuleName<T extends BasicType> = T extends keyof RuleSchemaMap ? keyof RuleSchemaMap[T] : never;
|
|
25
|
+
export interface FieldValidationRule<T extends BasicType> {
|
|
26
|
+
key: string;
|
|
27
|
+
name: FieldValidationRuleName<T>;
|
|
28
|
+
args?: {
|
|
29
|
+
[key: string]: any;
|
|
30
|
+
};
|
|
31
|
+
paramsType?: 'object';
|
|
32
|
+
}
|
|
33
|
+
export interface ValidationOptions<T extends BasicType = BasicType> {
|
|
34
|
+
type: T;
|
|
35
|
+
rules: FieldValidationRule<T>[];
|
|
36
|
+
[key: string]: any;
|
|
37
|
+
}
|
|
38
|
+
export interface BaseFieldOptions<T extends BasicType = BasicType> {
|
|
18
39
|
name?: string;
|
|
19
40
|
hidden?: boolean;
|
|
20
41
|
translation?: boolean;
|
|
42
|
+
validation?: ValidationOptions<T>;
|
|
21
43
|
[key: string]: any;
|
|
22
44
|
}
|
|
23
|
-
export interface BaseColumnFieldOptions extends BaseFieldOptions
|
|
45
|
+
export interface BaseColumnFieldOptions<T extends BasicType = BasicType> extends BaseFieldOptions<T>, Omit<ModelAttributeColumnOptions, 'type'> {
|
|
24
46
|
dataType?: DataType;
|
|
25
47
|
index?: boolean | ModelIndexesOptions;
|
|
26
48
|
}
|
|
@@ -50,3 +72,4 @@ export declare abstract class Field {
|
|
|
50
72
|
additionalSequelizeOptions(): {};
|
|
51
73
|
typeToString(): any;
|
|
52
74
|
}
|
|
75
|
+
export {};
|
package/lib/fields/index.d.ts
CHANGED
|
@@ -32,6 +32,7 @@ import { UnixTimestampFieldOptions } from './unix-timestamp-field';
|
|
|
32
32
|
import { DateOnlyFieldOptions } from './date-only-field';
|
|
33
33
|
import { DatetimeNoTzFieldOptions } from './datetime-no-tz-field';
|
|
34
34
|
import { DatetimeTzFieldOptions } from './datetime-tz-field';
|
|
35
|
+
import { SnowflakeIdFieldOptions } from './snowflake-id-field';
|
|
35
36
|
export * from './array-field';
|
|
36
37
|
export * from './belongs-to-field';
|
|
37
38
|
export * from './belongs-to-many-field';
|
|
@@ -60,4 +61,5 @@ export * from './virtual-field';
|
|
|
60
61
|
export * from './nanoid-field';
|
|
61
62
|
export * from './encryption-field';
|
|
62
63
|
export * from './unix-timestamp-field';
|
|
63
|
-
export
|
|
64
|
+
export * from './snowflake-id-field';
|
|
65
|
+
export type FieldOptions = BaseFieldOptions | StringFieldOptions | IntegerFieldOptions | FloatFieldOptions | DecimalFieldOptions | DoubleFieldOptions | RealFieldOptions | JsonFieldOptions | JsonbFieldOptions | BooleanFieldOptions | RadioFieldOptions | TextFieldOptions | VirtualFieldOptions | ArrayFieldOptions | SetFieldOptions | TimeFieldOptions | DateFieldOptions | DatetimeTzFieldOptions | DatetimeNoTzFieldOptions | DateOnlyFieldOptions | UnixTimestampFieldOptions | UidFieldOptions | UUIDFieldOptions | NanoidFieldOptions | PasswordFieldOptions | ContextFieldOptions | BelongsToFieldOptions | HasOneFieldOptions | HasManyFieldOptions | BelongsToManyFieldOptions | EncryptionField | SnowflakeIdFieldOptions;
|
package/lib/fields/index.js
CHANGED
|
@@ -51,6 +51,7 @@ __reExport(fields_exports, require("./virtual-field"), module.exports);
|
|
|
51
51
|
__reExport(fields_exports, require("./nanoid-field"), module.exports);
|
|
52
52
|
__reExport(fields_exports, require("./encryption-field"), module.exports);
|
|
53
53
|
__reExport(fields_exports, require("./unix-timestamp-field"), module.exports);
|
|
54
|
+
__reExport(fields_exports, require("./snowflake-id-field"), module.exports);
|
|
54
55
|
// Annotate the CommonJS export names for ESM import in node:
|
|
55
56
|
0 && (module.exports = {
|
|
56
57
|
...require("./array-field"),
|
|
@@ -80,5 +81,6 @@ __reExport(fields_exports, require("./unix-timestamp-field"), module.exports);
|
|
|
80
81
|
...require("./virtual-field"),
|
|
81
82
|
...require("./nanoid-field"),
|
|
82
83
|
...require("./encryption-field"),
|
|
83
|
-
...require("./unix-timestamp-field")
|
|
84
|
+
...require("./unix-timestamp-field"),
|
|
85
|
+
...require("./snowflake-id-field")
|
|
84
86
|
});
|
|
@@ -13,7 +13,7 @@ export declare abstract class NumberField extends Field {
|
|
|
13
13
|
export declare class IntegerField extends NumberField {
|
|
14
14
|
get dataType(): DataTypes.IntegerDataTypeConstructor;
|
|
15
15
|
}
|
|
16
|
-
export interface IntegerFieldOptions extends BaseColumnFieldOptions {
|
|
16
|
+
export interface IntegerFieldOptions extends BaseColumnFieldOptions<'number'> {
|
|
17
17
|
type: 'integer';
|
|
18
18
|
}
|
|
19
19
|
export declare class BigIntField extends NumberField {
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
import { DataTypes } from 'sequelize';
|
|
10
|
+
import { BaseColumnFieldOptions, Field } from './field';
|
|
11
|
+
interface Application {
|
|
12
|
+
snowflakeIdGenerator: {
|
|
13
|
+
generate(): number | BigInt;
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
export declare class SnowflakeIdField extends Field {
|
|
17
|
+
private static app;
|
|
18
|
+
static setApp(app: Application): void;
|
|
19
|
+
get dataType(): DataTypes.BigIntDataTypeConstructor;
|
|
20
|
+
private setId;
|
|
21
|
+
init(): void;
|
|
22
|
+
bind(): void;
|
|
23
|
+
unbind(): void;
|
|
24
|
+
}
|
|
25
|
+
export interface SnowflakeIdFieldOptions extends BaseColumnFieldOptions {
|
|
26
|
+
type: 'snowflakeId';
|
|
27
|
+
epoch?: number;
|
|
28
|
+
}
|
|
29
|
+
export {};
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
var __defProp = Object.defineProperty;
|
|
11
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
12
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
13
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
14
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
15
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
16
|
+
var __export = (target, all) => {
|
|
17
|
+
for (var name in all)
|
|
18
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
19
|
+
};
|
|
20
|
+
var __copyProps = (to, from, except, desc) => {
|
|
21
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
22
|
+
for (let key of __getOwnPropNames(from))
|
|
23
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
24
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
25
|
+
}
|
|
26
|
+
return to;
|
|
27
|
+
};
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
30
|
+
var snowflake_id_field_exports = {};
|
|
31
|
+
__export(snowflake_id_field_exports, {
|
|
32
|
+
SnowflakeIdField: () => SnowflakeIdField
|
|
33
|
+
});
|
|
34
|
+
module.exports = __toCommonJS(snowflake_id_field_exports);
|
|
35
|
+
var import_sequelize = require("sequelize");
|
|
36
|
+
var import_field = require("./field");
|
|
37
|
+
const _SnowflakeIdField = class _SnowflakeIdField extends import_field.Field {
|
|
38
|
+
static setApp(app) {
|
|
39
|
+
this.app = app;
|
|
40
|
+
}
|
|
41
|
+
get dataType() {
|
|
42
|
+
return import_sequelize.DataTypes.BIGINT;
|
|
43
|
+
}
|
|
44
|
+
setId(name, instance) {
|
|
45
|
+
const value = instance.get(name);
|
|
46
|
+
if (!value) {
|
|
47
|
+
const generator = this.constructor.app.snowflakeIdGenerator;
|
|
48
|
+
instance.set(name, generator.generate());
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
init() {
|
|
52
|
+
const { name } = this.options;
|
|
53
|
+
this.listener = (instance) => this.setId(name, instance);
|
|
54
|
+
this.bulkListener = async (instances) => {
|
|
55
|
+
for (const instance of instances) {
|
|
56
|
+
this.setId(name, instance);
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
bind() {
|
|
61
|
+
super.bind();
|
|
62
|
+
this.on("beforeValidate", this.listener);
|
|
63
|
+
this.on("beforeSave", this.listener);
|
|
64
|
+
this.on("beforeBulkCreate", this.bulkListener);
|
|
65
|
+
}
|
|
66
|
+
unbind() {
|
|
67
|
+
super.unbind();
|
|
68
|
+
this.off("beforeValidate", this.listener);
|
|
69
|
+
this.off("beforeSave", this.listener);
|
|
70
|
+
this.off("beforeBulkCreate", this.bulkListener);
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
__name(_SnowflakeIdField, "SnowflakeIdField");
|
|
74
|
+
__publicField(_SnowflakeIdField, "app");
|
|
75
|
+
let SnowflakeIdField = _SnowflakeIdField;
|
|
76
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
77
|
+
0 && (module.exports = {
|
|
78
|
+
SnowflakeIdField
|
|
79
|
+
});
|
|
@@ -14,7 +14,7 @@ export declare class StringField extends Field {
|
|
|
14
14
|
set(value: any): void;
|
|
15
15
|
};
|
|
16
16
|
}
|
|
17
|
-
export interface StringFieldOptions extends BaseColumnFieldOptions {
|
|
17
|
+
export interface StringFieldOptions extends BaseColumnFieldOptions<'string'> {
|
|
18
18
|
type: 'string';
|
|
19
19
|
length?: number;
|
|
20
20
|
trim?: boolean;
|
package/lib/index.d.ts
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
8
|
*/
|
|
9
9
|
export { BaseError, BelongsToGetAssociationMixin, DataTypes, fn, HasManyCountAssociationsMixin, HasManyCreateAssociationMixin, HasManyGetAssociationsMixin, literal, ModelStatic, Op, SyncOptions, Transaction, UniqueConstraintError, ValidationError, ValidationErrorItem, where, } from 'sequelize';
|
|
10
|
+
export { ValidationError as JoiValidationError } from 'joi';
|
|
10
11
|
export * from './belongs-to-array/belongs-to-array-repository';
|
|
11
12
|
export * from './collection';
|
|
12
13
|
export * from './collection-group-manager';
|
package/lib/index.js
CHANGED
|
@@ -44,6 +44,7 @@ __export(src_exports, {
|
|
|
44
44
|
HasManyCountAssociationsMixin: () => import_sequelize.HasManyCountAssociationsMixin,
|
|
45
45
|
HasManyCreateAssociationMixin: () => import_sequelize.HasManyCreateAssociationMixin,
|
|
46
46
|
HasManyGetAssociationsMixin: () => import_sequelize.HasManyGetAssociationsMixin,
|
|
47
|
+
JoiValidationError: () => import_joi.ValidationError,
|
|
47
48
|
ModelStatic: () => import_sequelize.ModelStatic,
|
|
48
49
|
Op: () => import_sequelize.Op,
|
|
49
50
|
SQLParserTypes: () => import_sql_parser.SQLParserTypes,
|
|
@@ -65,6 +66,7 @@ __export(src_exports, {
|
|
|
65
66
|
});
|
|
66
67
|
module.exports = __toCommonJS(src_exports);
|
|
67
68
|
var import_sequelize = require("sequelize");
|
|
69
|
+
var import_joi = require("joi");
|
|
68
70
|
__reExport(src_exports, require("./belongs-to-array/belongs-to-array-repository"), module.exports);
|
|
69
71
|
__reExport(src_exports, require("./collection"), module.exports);
|
|
70
72
|
__reExport(src_exports, require("./collection-group-manager"), module.exports);
|
|
@@ -110,6 +112,7 @@ var import_filter_include = require("./utils/filter-include");
|
|
|
110
112
|
HasManyCountAssociationsMixin,
|
|
111
113
|
HasManyCreateAssociationMixin,
|
|
112
114
|
HasManyGetAssociationsMixin,
|
|
115
|
+
JoiValidationError,
|
|
113
116
|
ModelStatic,
|
|
114
117
|
Op,
|
|
115
118
|
SQLParserTypes,
|
|
@@ -77,6 +77,11 @@ const _InheritedSyncRunner = class _InheritedSyncRunner {
|
|
|
77
77
|
let maxSequenceName;
|
|
78
78
|
if (childAttributes.id && childAttributes.id.autoIncrement) {
|
|
79
79
|
for (const parent of parents) {
|
|
80
|
+
const attributes2 = parent.model.getAttributes();
|
|
81
|
+
const parentIdField = attributes2["id"];
|
|
82
|
+
if (!(parentIdField == null ? void 0 : parentIdField.autoIncrement)) {
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
80
85
|
const sequenceNameResult = await queryInterface.sequelize.query(
|
|
81
86
|
`SELECT column_default
|
|
82
87
|
FROM information_schema.columns
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
declare const _default: Record<string, any>;
|
|
10
|
+
export default _default;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
var __defProp = Object.defineProperty;
|
|
11
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
12
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
13
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
14
|
+
var __export = (target, all) => {
|
|
15
|
+
for (var name in all)
|
|
16
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
17
|
+
};
|
|
18
|
+
var __copyProps = (to, from, except, desc) => {
|
|
19
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
20
|
+
for (let key of __getOwnPropNames(from))
|
|
21
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
22
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
23
|
+
}
|
|
24
|
+
return to;
|
|
25
|
+
};
|
|
26
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
27
|
+
var in_exports = {};
|
|
28
|
+
__export(in_exports, {
|
|
29
|
+
default: () => in_default
|
|
30
|
+
});
|
|
31
|
+
module.exports = __toCommonJS(in_exports);
|
|
32
|
+
var import_lodash = require("lodash");
|
|
33
|
+
var import_sequelize = require("sequelize");
|
|
34
|
+
var in_default = {
|
|
35
|
+
$in(val, ctx) {
|
|
36
|
+
return {
|
|
37
|
+
[import_sequelize.Op.in]: val == null ? [] : (0, import_lodash.castArray)(val)
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
};
|
package/lib/operators/index.js
CHANGED
|
@@ -46,6 +46,7 @@ var import_empty = __toESM(require("./empty"));
|
|
|
46
46
|
var import_string = __toESM(require("./string"));
|
|
47
47
|
var import_eq = __toESM(require("./eq"));
|
|
48
48
|
var import_ne = __toESM(require("./ne"));
|
|
49
|
+
var import_in = __toESM(require("./in"));
|
|
49
50
|
var import_notIn = __toESM(require("./notIn"));
|
|
50
51
|
var import_boolean = __toESM(require("./boolean"));
|
|
51
52
|
var import_child_collection = __toESM(require("./child-collection"));
|
|
@@ -57,6 +58,7 @@ var operators_default = {
|
|
|
57
58
|
...import_string.default,
|
|
58
59
|
...import_eq.default,
|
|
59
60
|
...import_ne.default,
|
|
61
|
+
...import_in.default,
|
|
60
62
|
...import_notIn.default,
|
|
61
63
|
...import_boolean.default,
|
|
62
64
|
...import_child_collection.default
|
|
@@ -86,6 +86,10 @@ const _BelongsToManyRepository = class _BelongsToManyRepository extends import_m
|
|
|
86
86
|
through: values[this.throughName()],
|
|
87
87
|
transaction: transaction2
|
|
88
88
|
};
|
|
89
|
+
this.collection.validate({
|
|
90
|
+
values,
|
|
91
|
+
operation: "create"
|
|
92
|
+
});
|
|
89
93
|
const instance = await sourceModel[createAccessor](values, createOptions);
|
|
90
94
|
await (0, import_update_associations.updateAssociations)(instance, values, { ...options, transaction: transaction2 });
|
|
91
95
|
return instance;
|
|
@@ -178,6 +178,7 @@ const _RelationRepository = class _RelationRepository {
|
|
|
178
178
|
const values = options.values;
|
|
179
179
|
const transaction2 = await this.getTransaction(options);
|
|
180
180
|
const sourceModel = await this.getSourceModel(transaction2);
|
|
181
|
+
this.collection.validate({ values, operation: "create" });
|
|
181
182
|
const instance = await sourceModel[createAccessor](guard.sanitize(options.values), { ...options, transaction: transaction2 });
|
|
182
183
|
await (0, import_update_associations.updateAssociations)(instance, values, { ...options, transaction: transaction2 });
|
|
183
184
|
if (options.hooks !== false) {
|
package/lib/repository.d.ts
CHANGED
|
@@ -209,6 +209,7 @@ export declare class Repository<TModelAttributes extends {} = any, TCreationAttr
|
|
|
209
209
|
*/
|
|
210
210
|
firstOrCreate(options: FirstOrCreateOptions): Promise<any>;
|
|
211
211
|
updateOrCreate(options: FirstOrCreateOptions): Promise<any>;
|
|
212
|
+
private validate;
|
|
212
213
|
/**
|
|
213
214
|
* Save instance to database
|
|
214
215
|
*
|
package/lib/repository.js
CHANGED
|
@@ -436,6 +436,9 @@ const _Repository = class _Repository {
|
|
|
436
436
|
}
|
|
437
437
|
return this.create({ values, transaction: transaction2, context, ...rest });
|
|
438
438
|
}
|
|
439
|
+
validate(options) {
|
|
440
|
+
this.collection.validate(options);
|
|
441
|
+
}
|
|
439
442
|
async create(options) {
|
|
440
443
|
if (Array.isArray(options.values)) {
|
|
441
444
|
return this.createMany({
|
|
@@ -450,6 +453,7 @@ const _Repository = class _Repository {
|
|
|
450
453
|
underscored: this.collection.options.underscored
|
|
451
454
|
});
|
|
452
455
|
const values = this.model.callSetters(guard.sanitize(options.values || {}), options);
|
|
456
|
+
this.validate({ values, operation: "create" });
|
|
453
457
|
const instance = await this.model.create(values, {
|
|
454
458
|
...options,
|
|
455
459
|
transaction: transaction2
|
|
@@ -494,6 +498,7 @@ const _Repository = class _Repository {
|
|
|
494
498
|
const transaction2 = await this.getTransaction(options);
|
|
495
499
|
const guard = import_update_guard.UpdateGuard.fromOptions(this.model, { ...options, underscored: this.collection.options.underscored });
|
|
496
500
|
const values = this.model.callSetters(guard.sanitize(options.values || {}), options);
|
|
501
|
+
this.validate({ values, operation: "update" });
|
|
497
502
|
if (options.individualHooks === false) {
|
|
498
503
|
const { model: Model2 } = this.collection;
|
|
499
504
|
const primaryKeyField = Model2.primaryKeyField || Model2.primaryKeyAttribute;
|
package/lib/sync-runner.js
CHANGED
|
@@ -162,6 +162,9 @@ const _SyncRunner = class _SyncRunner {
|
|
|
162
162
|
}, "isJSONColumn");
|
|
163
163
|
for (const columnName in columns) {
|
|
164
164
|
const column = columns[columnName];
|
|
165
|
+
if (isJSONColumn(column) && this.database.inDialect("mysql")) {
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
165
168
|
const isPrimaryKey = /* @__PURE__ */ __name(() => {
|
|
166
169
|
const attribute = this.findAttributeByColumnName(columnName);
|
|
167
170
|
return attribute && attribute.primaryKey || column.primaryKey;
|
|
@@ -79,6 +79,10 @@ async function updateModelByValues(instance, values, options) {
|
|
|
79
79
|
guard.setAssociationKeysToBeUpdate(options.updateAssociationValues);
|
|
80
80
|
values = guard.sanitize(values);
|
|
81
81
|
}
|
|
82
|
+
instance.constructor.collection.validate({
|
|
83
|
+
values,
|
|
84
|
+
operation: "update"
|
|
85
|
+
});
|
|
82
86
|
await instance.update(values, options);
|
|
83
87
|
await updateAssociations(instance, values, options);
|
|
84
88
|
}
|
|
@@ -266,6 +270,10 @@ async function updateSingleAssociation(model, key, value, options = {}) {
|
|
|
266
270
|
return true;
|
|
267
271
|
}
|
|
268
272
|
}
|
|
273
|
+
association.target.collection.validate({
|
|
274
|
+
values: value,
|
|
275
|
+
operation: "create"
|
|
276
|
+
});
|
|
269
277
|
const instance = await model[createAccessor](value, { context, transaction });
|
|
270
278
|
await updateAssociations(instance, value, {
|
|
271
279
|
...options,
|
|
@@ -356,6 +364,10 @@ async function updateMultipleAssociation(model, key, value, options = {}) {
|
|
|
356
364
|
throw new Error(`${targetKey} field value is empty`);
|
|
357
365
|
}
|
|
358
366
|
if ((0, import_utils.isUndefinedOrNull)(item[targetKey])) {
|
|
367
|
+
association.target.collection.validate({
|
|
368
|
+
values: item,
|
|
369
|
+
operation: "create"
|
|
370
|
+
});
|
|
359
371
|
const instance = await model[createAccessor](item, accessorOptions);
|
|
360
372
|
await updateAssociations(instance, item, {
|
|
361
373
|
...options,
|
|
@@ -373,6 +385,10 @@ async function updateMultipleAssociation(model, key, value, options = {}) {
|
|
|
373
385
|
transaction
|
|
374
386
|
});
|
|
375
387
|
if (!instance) {
|
|
388
|
+
association.target.collection.validate({
|
|
389
|
+
values: item,
|
|
390
|
+
operation: "create"
|
|
391
|
+
});
|
|
376
392
|
instance = await model[createAccessor](item, accessorOptions);
|
|
377
393
|
await updateAssociations(instance, item, {
|
|
378
394
|
...options,
|
|
@@ -392,6 +408,10 @@ async function updateMultipleAssociation(model, key, value, options = {}) {
|
|
|
392
408
|
if (association.associationType === "HasMany") {
|
|
393
409
|
delete item[association.foreignKey];
|
|
394
410
|
}
|
|
411
|
+
association.target.collection.validate({
|
|
412
|
+
values: item,
|
|
413
|
+
operation: "update"
|
|
414
|
+
});
|
|
395
415
|
await instance.update(item, { ...options, transaction });
|
|
396
416
|
}
|
|
397
417
|
await updateAssociations(instance, item, {
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
import { AnySchema } from 'joi';
|
|
10
|
+
import { ValidationOptions } from '../fields';
|
|
11
|
+
export declare function buildJoiSchema(validation: ValidationOptions, options: {
|
|
12
|
+
label?: string;
|
|
13
|
+
value: string;
|
|
14
|
+
}): AnySchema;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
var __create = Object.create;
|
|
11
|
+
var __defProp = Object.defineProperty;
|
|
12
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
13
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
14
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
15
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
16
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
17
|
+
var __export = (target, all) => {
|
|
18
|
+
for (var name in all)
|
|
19
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
20
|
+
};
|
|
21
|
+
var __copyProps = (to, from, except, desc) => {
|
|
22
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
23
|
+
for (let key of __getOwnPropNames(from))
|
|
24
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
25
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
26
|
+
}
|
|
27
|
+
return to;
|
|
28
|
+
};
|
|
29
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
30
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
31
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
32
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
33
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
34
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
35
|
+
mod
|
|
36
|
+
));
|
|
37
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
38
|
+
var field_validation_exports = {};
|
|
39
|
+
__export(field_validation_exports, {
|
|
40
|
+
buildJoiSchema: () => buildJoiSchema
|
|
41
|
+
});
|
|
42
|
+
module.exports = __toCommonJS(field_validation_exports);
|
|
43
|
+
var import_joi = __toESM(require("joi"));
|
|
44
|
+
var import_lodash = __toESM(require("lodash"));
|
|
45
|
+
function buildJoiSchema(validation, options) {
|
|
46
|
+
const { type, rules } = validation;
|
|
47
|
+
const { label, value } = options;
|
|
48
|
+
if (!type || typeof type !== "string" || !(type in import_joi.default)) {
|
|
49
|
+
throw new Error(`Invalid validation type: "${type}". Type must be a valid Joi schema type.`);
|
|
50
|
+
}
|
|
51
|
+
let schema = import_joi.default[type]();
|
|
52
|
+
const isRequired = rules.some((rule) => rule.name === "required");
|
|
53
|
+
if (isRequired) {
|
|
54
|
+
schema = schema.empty(null);
|
|
55
|
+
} else {
|
|
56
|
+
schema = schema.allow(null, "");
|
|
57
|
+
if ([null, ""].includes(value)) return schema;
|
|
58
|
+
}
|
|
59
|
+
if (rules) {
|
|
60
|
+
rules.forEach((rule) => {
|
|
61
|
+
if (!import_lodash.default.isEmpty(rule.args)) {
|
|
62
|
+
if (rule.name === "pattern" && !import_lodash.default.isRegExp(rule.args.regex)) {
|
|
63
|
+
const lastSlash = rule.args.regex.lastIndexOf("/");
|
|
64
|
+
const isRegExpStr = rule.args.regex.startsWith("/") && lastSlash > 0;
|
|
65
|
+
if (isRegExpStr) {
|
|
66
|
+
rule.args.regex = rule.args.regex.slice(1, lastSlash);
|
|
67
|
+
}
|
|
68
|
+
rule.args.regex = new RegExp(rule.args.regex);
|
|
69
|
+
}
|
|
70
|
+
schema = rule.paramsType === "object" ? schema[rule.name](rule.args) : schema[rule.name](...Object.values(rule.args));
|
|
71
|
+
} else {
|
|
72
|
+
schema = schema[rule.name]();
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
if (label) {
|
|
77
|
+
schema = schema.label(label);
|
|
78
|
+
}
|
|
79
|
+
return schema;
|
|
80
|
+
}
|
|
81
|
+
__name(buildJoiSchema, "buildJoiSchema");
|
|
82
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
83
|
+
0 && (module.exports = {
|
|
84
|
+
buildJoiSchema
|
|
85
|
+
});
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nocobase/database",
|
|
3
|
-
"version": "1.9.0
|
|
3
|
+
"version": "1.9.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "./lib/index.js",
|
|
6
6
|
"types": "./lib/index.d.ts",
|
|
7
7
|
"license": "AGPL-3.0",
|
|
8
8
|
"dependencies": {
|
|
9
|
-
"@nocobase/logger": "1.9.0
|
|
10
|
-
"@nocobase/utils": "1.9.0
|
|
9
|
+
"@nocobase/logger": "1.9.0",
|
|
10
|
+
"@nocobase/utils": "1.9.0",
|
|
11
11
|
"async-mutex": "^0.3.2",
|
|
12
12
|
"chalk": "^4.1.1",
|
|
13
13
|
"cron-parser": "4.4.0",
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
"flat": "^5.0.2",
|
|
19
19
|
"glob": "^7.1.6",
|
|
20
20
|
"graphlib": "^2.1.8",
|
|
21
|
+
"joi": "^17.13.3",
|
|
21
22
|
"lodash": "^4.17.21",
|
|
22
23
|
"mathjs": "^10.6.1",
|
|
23
24
|
"nanoid": "^3.3.11",
|
|
@@ -38,5 +39,5 @@
|
|
|
38
39
|
"url": "git+https://github.com/nocobase/nocobase.git",
|
|
39
40
|
"directory": "packages/database"
|
|
40
41
|
},
|
|
41
|
-
"gitHead": "
|
|
42
|
+
"gitHead": "d92ce9096c2b3b5a8f205ecc0e1dedd64d7ee48b"
|
|
42
43
|
}
|