cabloy 5.1.156 → 5.1.157
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/.cabloy-version +1 -1
- package/CHANGELOG.md +6 -0
- package/package.json +1 -1
- package/vona/packages-cli/cli/package.json +1 -1
- package/vona/packages-cli/cli-set-api/package.json +1 -1
- package/vona/packages-utils/cascade-extend/package.json +1 -1
- package/vona/packages-utils/dotenv/package.json +1 -1
- package/vona/packages-vona/vona/package.json +1 -1
- package/vona/src/suite/a-home/modules/home-user/test/role.test.ts +11 -5
- package/vona/src/suite-vendor/a-cabloy/modules/a-rbac/package.json +1 -1
- package/vona/src/suite-vendor/a-cabloy/package.json +2 -2
- package/vona/src/suite-vendor/a-vona/modules/a-orm/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/service/databaseDialectBase_.ts +104 -33
- package/vona/src/suite-vendor/a-vona/modules/a-permission/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/package.json +1 -1
- package/zova/packages-cli/cli/package.json +2 -2
- package/zova/packages-cli/cli-set-front/package.json +2 -2
- package/zova/packages-utils/zova-vite/package.json +2 -2
- package/.claude/scheduled_tasks.lock +0 -1
- package/vona/packages-utils/cascade-extend/LICENSE +0 -3
package/.cabloy-version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
5.1.
|
|
1
|
+
5.1.157
|
package/CHANGELOG.md
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vona",
|
|
3
|
-
"version": "5.1.
|
|
3
|
+
"version": "5.1.91",
|
|
4
4
|
"gitHead": "a79189b882c17af5911573896a781bbb0046d37d",
|
|
5
5
|
"description": "Vona is an intuitive, elegant and powerful Node.js framework for rapidly developing enterprise applications of any size",
|
|
6
6
|
"keywords": [
|
|
@@ -45,11 +45,13 @@ describe('role.test.ts', { concurrency: false }, () => {
|
|
|
45
45
|
it('bean:role emits only actual membership changes', async () => {
|
|
46
46
|
let userId: string | undefined;
|
|
47
47
|
let roleId: string | undefined;
|
|
48
|
-
const events: unknown[] = [];
|
|
48
|
+
const events: Array<{ userIds: unknown[]; roleIds: unknown[] }> = [];
|
|
49
49
|
const event = app.scope('a-user').event.roleMembershipChanged;
|
|
50
50
|
const originalEmit = event.emit.bind(event);
|
|
51
51
|
const emitMock = mock.method(event, 'emit', async data => {
|
|
52
|
-
|
|
52
|
+
if (data.userIds.some(item => String(item) === userId)) {
|
|
53
|
+
events.push(data);
|
|
54
|
+
}
|
|
53
55
|
return await originalEmit(data);
|
|
54
56
|
});
|
|
55
57
|
try {
|
|
@@ -143,11 +145,13 @@ describe('role.test.ts', { concurrency: false }, () => {
|
|
|
143
145
|
it('bean:role replaces a membership set with one change event', async () => {
|
|
144
146
|
let userId: string | undefined;
|
|
145
147
|
const roleIds: string[] = [];
|
|
146
|
-
const events: unknown[] = [];
|
|
148
|
+
const events: Array<{ userIds: unknown[]; roleIds: unknown[] }> = [];
|
|
147
149
|
const event = app.scope('a-user').event.roleMembershipChanged;
|
|
148
150
|
const originalEmit = event.emit.bind(event);
|
|
149
151
|
const emitMock = mock.method(event, 'emit', async data => {
|
|
150
|
-
|
|
152
|
+
if (data.userIds.some(item => String(item) === userId)) {
|
|
153
|
+
events.push(data);
|
|
154
|
+
}
|
|
151
155
|
return await originalEmit(data);
|
|
152
156
|
});
|
|
153
157
|
try {
|
|
@@ -175,7 +179,9 @@ describe('role.test.ts', { concurrency: false }, () => {
|
|
|
175
179
|
|
|
176
180
|
const result = await app.bean.role.replaceUserRoleIds(user.id, [roles[1].id, roles[2].id]);
|
|
177
181
|
assert.deepEqual(result, { addedRoleIds: [roles[2].id], removedRoleIds: [roles[0].id] });
|
|
178
|
-
assert.
|
|
182
|
+
assert.equal(events.length, 1);
|
|
183
|
+
assert.deepEqual(events[0]?.userIds, [user.id]);
|
|
184
|
+
assert.deepEqual(events[0]?.roleIds.toSorted(), [roles[0].id, roles[2].id].toSorted());
|
|
179
185
|
|
|
180
186
|
const memberships = await homeUser.model.roleUser.select({ where: { userId: user.id } });
|
|
181
187
|
assert.deepEqual(
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vona-suite-a-cabloy",
|
|
3
|
-
"version": "5.1.
|
|
3
|
+
"version": "5.1.50",
|
|
4
4
|
"gitHead": "a79189b882c17af5911573896a781bbb0046d37d",
|
|
5
5
|
"description": "",
|
|
6
6
|
"author": "",
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"dependencies": {
|
|
10
10
|
"vona-module-a-cabloy": "workspace:^",
|
|
11
11
|
"vona-module-a-markdown": "^5.0.3",
|
|
12
|
-
"vona-module-a-rbac": "^5.0.
|
|
12
|
+
"vona-module-a-rbac": "^5.0.4",
|
|
13
13
|
"vona-module-a-socket": "workspace:^",
|
|
14
14
|
"vona-module-a-ssr": "workspace:^",
|
|
15
15
|
"vona-module-a-ssrhmr": "workspace:^"
|
|
@@ -15,17 +15,17 @@ import type {
|
|
|
15
15
|
} from '../types/dialect.ts';
|
|
16
16
|
|
|
17
17
|
const BOOLEAN_COLUMN_TYPES = ['bit', 'bool', 'boolean'];
|
|
18
|
-
const
|
|
19
|
-
const
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
'
|
|
27
|
-
'
|
|
28
|
-
|
|
18
|
+
const INTEGER_COLUMN_TYPES = ['tinyint', 'smallint', 'mediumint', 'int', 'integer'];
|
|
19
|
+
const FLOAT_COLUMN_TYPES = ['float', 'double'];
|
|
20
|
+
const GENERATED_DEFAULT_PATTERN =
|
|
21
|
+
/^(?:current_timestamp\b(?:\s*\(\s*\d*\s*\))?|current_date\b|current_time\b(?:\s*\(\s*\d*\s*\))?|now\s*\(\s*\)|nextval\s*\()/i;
|
|
22
|
+
const NUMBER_LITERAL_PATTERN = /^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?$/i;
|
|
23
|
+
const QUOTED_LITERAL_PATTERN = /^'((?:''|[^'])*)'$/;
|
|
24
|
+
|
|
25
|
+
type ColumnDefaultLiteral =
|
|
26
|
+
| { kind: 'string'; value: string }
|
|
27
|
+
| { kind: 'boolean'; value: boolean }
|
|
28
|
+
| { kind: 'number'; value: string };
|
|
29
29
|
|
|
30
30
|
@Service()
|
|
31
31
|
@Virtual()
|
|
@@ -96,13 +96,13 @@ export class ServiceDatabaseDialectBase extends BeanBase {
|
|
|
96
96
|
|
|
97
97
|
coerceColumn(column: Knex.ColumnInfo): ITableColumn {
|
|
98
98
|
const result = { type: column.type } as ITableColumn;
|
|
99
|
-
result.default = this._coerceColumnValue(column.type, column.defaultValue);
|
|
100
99
|
const columnType = (column as Knex.ColumnInfo & { columnType?: string }).columnType;
|
|
101
100
|
if (columnType) result.columnType = columnType;
|
|
101
|
+
result.default = this._coerceColumnValue(result, column.defaultValue);
|
|
102
102
|
return result;
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
-
protected _isMysqlBooleanColumn(column: ITableColumn) {
|
|
105
|
+
protected _isMysqlBooleanColumn(column: Pick<ITableColumn, 'columnType'>) {
|
|
106
106
|
return column.columnType?.trim().toLowerCase() === 'tinyint(1)';
|
|
107
107
|
}
|
|
108
108
|
|
|
@@ -191,34 +191,105 @@ export class ServiceDatabaseDialectBase extends BeanBase {
|
|
|
191
191
|
return [items.map(item => item.id), builder];
|
|
192
192
|
}
|
|
193
193
|
|
|
194
|
-
protected _coerceColumnValue(
|
|
194
|
+
protected _coerceColumnValue(column: Pick<ITableColumn, 'type' | 'columnType'>, value: unknown) {
|
|
195
195
|
if (isNil(value)) return undefined;
|
|
196
|
-
if (
|
|
197
|
-
|
|
198
|
-
if (
|
|
199
|
-
this._columnTypePrefixes(type, TIMESTAMP_COLUMN_TYPE_PREFIXES) &&
|
|
200
|
-
value === 'CURRENT_TIMESTAMP'
|
|
201
|
-
) {
|
|
202
|
-
return undefined; // new Date();
|
|
196
|
+
if (typeof value !== 'string') {
|
|
197
|
+
return this._isBooleanColumn(column) ? safeBoolean(value as any) : value;
|
|
203
198
|
}
|
|
204
|
-
|
|
205
|
-
|
|
199
|
+
|
|
200
|
+
const literal = this._coerceColumnLiteral(value);
|
|
201
|
+
if (literal === undefined) return undefined;
|
|
202
|
+
if (this._isBooleanColumn(column)) return safeBoolean(literal.value);
|
|
203
|
+
if (literal.kind === 'boolean') return literal.value;
|
|
204
|
+
|
|
205
|
+
const type = this._columnTypeName(column.type);
|
|
206
|
+
if (literal.kind === 'string') return literal.value;
|
|
207
|
+
if (INTEGER_COLUMN_TYPES.includes(type)) return this._safeInteger(literal.value);
|
|
208
|
+
if (FLOAT_COLUMN_TYPES.includes(type)) return this._safeNumber(literal.value);
|
|
209
|
+
return literal.value;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
protected _isBooleanColumn(column: Pick<ITableColumn, 'type' | 'columnType'>) {
|
|
213
|
+
return (
|
|
214
|
+
this._isMysqlBooleanColumn(column) ||
|
|
215
|
+
BOOLEAN_COLUMN_TYPES.includes(this._columnTypeName(column.type))
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
protected _columnTypeName(type: string) {
|
|
220
|
+
return type.trim().toLowerCase().match(/^\w+/)?.[0] ?? '';
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
protected _coerceColumnLiteral(value: string): ColumnDefaultLiteral | undefined {
|
|
224
|
+
const literal = this._unwrapColumnDefault(value);
|
|
225
|
+
if (literal === '') return undefined;
|
|
226
|
+
const literalLower = literal.toLowerCase();
|
|
227
|
+
if (literalLower === 'null' || GENERATED_DEFAULT_PATTERN.test(literal)) return undefined;
|
|
228
|
+
const quoted = literal.match(QUOTED_LITERAL_PATTERN);
|
|
229
|
+
if (quoted) return { kind: 'string', value: quoted[1].replaceAll("''", "'") };
|
|
230
|
+
if (literalLower === 'true' || literalLower === 't') return { kind: 'boolean', value: true };
|
|
231
|
+
if (literalLower === 'false' || literalLower === 'f') return { kind: 'boolean', value: false };
|
|
232
|
+
if (NUMBER_LITERAL_PATTERN.test(literal)) return { kind: 'number', value: literal };
|
|
233
|
+
if (this._isExpressionColumnDefault(literal)) return undefined;
|
|
234
|
+
return { kind: 'string', value: literal };
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
protected _unwrapColumnDefault(value: string) {
|
|
238
|
+
let literal = value.trim();
|
|
239
|
+
while (literal.startsWith('(') && literal.endsWith(')')) {
|
|
240
|
+
const inner = literal.slice(1, -1).trim();
|
|
241
|
+
if (!inner || !this._isColumnDefaultBalanced(inner)) break;
|
|
242
|
+
literal = inner;
|
|
206
243
|
}
|
|
207
|
-
|
|
208
|
-
|
|
244
|
+
const castIndex = literal.indexOf('::');
|
|
245
|
+
if (castIndex > 0 && this._isScalarColumnDefault(literal.slice(0, castIndex).trim())) {
|
|
246
|
+
literal = literal.slice(0, castIndex).trim();
|
|
209
247
|
}
|
|
210
|
-
|
|
211
|
-
if (value.indexOf('NULL::') === 0) return undefined;
|
|
212
|
-
return value;
|
|
248
|
+
return literal;
|
|
213
249
|
}
|
|
214
250
|
|
|
215
|
-
|
|
216
|
-
|
|
251
|
+
protected _isScalarColumnDefault(value: string) {
|
|
252
|
+
return (
|
|
253
|
+
value.toLowerCase() === 'null' ||
|
|
254
|
+
QUOTED_LITERAL_PATTERN.test(value) ||
|
|
255
|
+
value.toLowerCase() === 'true' ||
|
|
256
|
+
value.toLowerCase() === 'false' ||
|
|
257
|
+
NUMBER_LITERAL_PATTERN.test(value)
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
protected _isColumnDefaultBalanced(value: string) {
|
|
262
|
+
let depth = 0;
|
|
263
|
+
let quoted = false;
|
|
264
|
+
for (let index = 0; index < value.length; index++) {
|
|
265
|
+
const char = value[index];
|
|
266
|
+
if (char === "'") {
|
|
267
|
+
if (quoted && value[index + 1] === "'") {
|
|
268
|
+
index++;
|
|
269
|
+
} else {
|
|
270
|
+
quoted = !quoted;
|
|
271
|
+
}
|
|
272
|
+
} else if (!quoted && char === '(') {
|
|
273
|
+
depth++;
|
|
274
|
+
} else if (!quoted && char === ')') {
|
|
275
|
+
if (depth === 0) return false;
|
|
276
|
+
depth--;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
return !quoted && depth === 0;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
protected _isExpressionColumnDefault(value: string) {
|
|
283
|
+
return /[()*/+\-\s]/.test(value);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
protected _safeInteger(value: string) {
|
|
217
287
|
const num = Number(value);
|
|
218
|
-
return Number.
|
|
288
|
+
return Number.isSafeInteger(num) ? num : value;
|
|
219
289
|
}
|
|
220
290
|
|
|
221
|
-
protected
|
|
222
|
-
|
|
291
|
+
protected _safeNumber(value: string) {
|
|
292
|
+
const num = Number(value);
|
|
293
|
+
return Number.isFinite(num) ? num : undefined;
|
|
223
294
|
}
|
|
224
295
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zova-cli",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.105",
|
|
4
4
|
"gitHead": "6f675a8cc46d596142c591c28a40cc4d82fcc6cc",
|
|
5
5
|
"description": "zova cli",
|
|
6
6
|
"keywords": [
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"@cabloy/process-helper": "^3.1.8",
|
|
45
45
|
"fs-extra": "^11.3.5",
|
|
46
46
|
"semver": "^7.6.2",
|
|
47
|
-
"zova-cli-set-front": "^1.2.
|
|
47
|
+
"zova-cli-set-front": "^1.2.103"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
50
|
"clean-package": "^2.2.0",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zova-cli-set-front",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.103",
|
|
4
4
|
"gitHead": "6f675a8cc46d596142c591c28a40cc4d82fcc6cc",
|
|
5
5
|
"description": "zova cli-set-front",
|
|
6
6
|
"keywords": [
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
"vite": "^8.0.14",
|
|
71
71
|
"yaml": "^2.8.3",
|
|
72
72
|
"zova-openapi": "^1.1.17",
|
|
73
|
-
"zova-vite": "^1.1.
|
|
73
|
+
"zova-vite": "^1.1.45"
|
|
74
74
|
},
|
|
75
75
|
"devDependencies": {
|
|
76
76
|
"clean-package": "^2.2.0",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zova-vite",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.45",
|
|
4
4
|
"gitHead": "09d901d17140a80ee0764211b441cda72fd94663",
|
|
5
5
|
"description": "zova vite",
|
|
6
6
|
"keywords": [
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"@babel/plugin-proposal-decorators": "^7.29.0",
|
|
41
41
|
"@babel/plugin-transform-class-properties": "^7.28.6",
|
|
42
42
|
"@babel/plugin-transform-typescript": "^7.28.6",
|
|
43
|
-
"@cabloy/dotenv": "^1.2.
|
|
43
|
+
"@cabloy/dotenv": "^1.2.9",
|
|
44
44
|
"@cabloy/extend": "^3.2.8",
|
|
45
45
|
"@cabloy/module-glob": "^5.3.18",
|
|
46
46
|
"@cabloy/module-info": "^2.0.3",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"sessionId":"834e10b3-c7de-4e46-8255-347f58ac99c9","pid":21283,"procStart":"Tue Sep 1 21:20:05 2026","acquiredAt":1788301241462}
|